composer.json 0000666 00000001375 13533704400 0007277 0 ustar 00 {
"name": "nette/utils",
"description": "Nette Utility Classes",
"homepage": "http://nette.org",
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [
{
"name": "David Grudl",
"homepage": "http://davidgrudl.com"
},
{
"name": "Nette Community",
"homepage": "http://nette.org/contributors"
}
],
"require": {
"php": ">=5.3.1"
},
"require-dev": {
"nette/tester": "~1.0"
},
"suggest": {
"ext-iconv": "to use Strings::webalize() and toAscii()",
"ext-intl": "for script transliteration in Strings::webalize() and toAscii()",
"ext-mbstring": "to use Strings::lower() etc...",
"ext-gd": "to use Image"
},
"conflict": {
"nette/nette": "<2.2"
},
"autoload": {
"classmap": ["src/"]
},
"minimum-stability": "dev"
}
contributing.md 0000666 00000002317 13533704400 0007603 0 ustar 00 How to contribute & use the issue tracker
=========================================
The issue tracker is the preferred channel for bug reports, features requests
and submitting pull requests, but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use
[Nette forum](http://forum.nette.org) or [Stack Overflow](http://stackoverflow.com)).
* Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
* Use the GitHub **issue search** — check if the issue has already been
reported.
A good **bug report** shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report.
**Feature requests** are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature.
Nette welcomes **pull requests**. If you'd like to contribute, please take a moment
to [read the guidelines](http://nette.org/en/contributing) in order to make
the contribution process easy and effective for everyone involved.
Thanks!
license.md 0000666 00000005243 13533704400 0006517 0 ustar 00 Licenses
========
Good news! You may use Nette Framework under the terms of either
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
The BSD License is recommended for most projects. It is easy to understand and it
places almost no restrictions on what you can do with the framework. If the GPL
fits better to your project, you can use the framework under this license.
You don't have to notify anyone which license you are using. You can freely
use Nette Framework in commercial projects as long as the copyright header
remains intact.
Please be advised that the name "Nette Framework" is a protected trademark and its
usage has some limitations. So please do not use word "Nette" in the name of your
project or top-level domain, and choose a name that stands on its own merits.
If your stuff is good, it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2004, 2014 David Grudl (http://davidgrudl.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of "Nette Framework" nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall the copyright owner or contributors be liable for
any direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and on
any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
GNU General Public License
--------------------------
GPL licenses are very very long, so instead of including them here we offer
you URLs with full text:
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)
readme.md 0000666 00000013555 13533704400 0006337 0 ustar 00 Nette Utility Classes
=====================
[![Downloads this Month](https://img.shields.io/packagist/dm/nette/utils.svg)](https://packagist.org/packages/nette/utils)
[![Build Status](https://travis-ci.org/nette/utils.svg?branch=v2.3)](https://travis-ci.org/nette/utils)
Nette\Object: Strict classes
----------------------------
PHP gives a huge freedom to developers, which makes it a perfect language for making mistakes. But you can stop this bad behavior and start writing applications without hardly discoverable mistakes. Do you wonder how? It's really simple -- you just need to have stricter rules.
Can you find an error in this example?
```php
class Circle
{
public $radius;
public function getArea()
{
return $this->radius * $this->radius * M_PI;
}
}
$circle = new Circle;
$circle->raduis = 10;
echo $circle->getArea(); // 10² * π ≈ 314
```
On the first look it seems that code will print out 314; but it returns 0. How is this even possible? Accidentaly, `$circle->radius` was mistyped to `raduis`. Just a small typo, which will give you a hard time correcting it, because PHP does not say a thing when something is wrong. Not even a Warning or Notice error message. Because PHP does not think it is an error.
The mentioned mistake could be corrected immediately, if class `Circle` would be descendant of [api:Nette\Object]:
```php
class Circle extends Nette\Object
{
...
```
Whereas the former code executed successfully (although it contained an error), the latter did not:
![](http://files.nette.org/git/doc-2.1/debugger-circle.png)
Class `Nette\Object` made `Circle` more strict and threw an exception when you tried to access an undeclared property. And `Tracy\Debugger` displayed error message about it. Line of code with fatal typo is now highlighted and error message has meaningful description: *Cannot write to an undeclared property Circle::$raduis*. Programmer can now fix the mistake he might have otherwise missed and which could be a real pain to find later.
One of many remarkable abilities of `Nette\Object` is throwing exceptions when accessing undeclared members.
```php
$circle = new Circle;
echo $circle->undeclared; // throws Nette\MemberAccessException
$circle->undeclared = 1; // throws Nette\MemberAccessException
$circle->unknownMethod(); // throws Nette\MemberAccessException
```
But it has much more to offer!
Properties, getters a setters
-----------------------------
In modern object oriented languages *property* describes members of class, which look like variables but are represented by methods. When reading or assigning values to those "variables", methods are called instead (so-called getters and setters). It is really useful feature, which allows us to control the access to these variables. Using this we can validate inputs or postpone the computation of values of these variables to the time when it is actually accessed.
Any class that is a descendant of `Nette\Object` acquires the ability to imitate properties. Only thing you need to do is to keep simple convention:
- Getter and setter have to be *public* methods.
- Getter's name is `getXyz()` or `isXyz()`, setter's is `setXyz()`
- Getter and setter are optional, so it is possible to have *read-only* or *write-only* properties
- Names of properties are case-sensitive (first letter being an exception)
We will make use of properties in the class Circle to make sure variable `$radius` contains only non-negative numbers:
```php
class Circle extends Nette\Object
{
private $radius; // not public anymore!
public function getRadius()
{
return $this->radius;
}
public function setRadius($radius)
{
// sanitizing value before saving it
$this->radius = max(0.0, (float) $radius);
}
public function getArea()
{
return $this->radius * $this->radius * M_PI;
}
public function isVisible()
{
return $this->radius > 0;
}
}
$circle = new Circle;
// the classic way using method calls
$circle->setRadius(10); // sets circle's radius
echo $circle->getArea(); // gets circle's area
// the alternative way using properties
$circle->radius = 10; // calls setRadius()
echo $circle->area; // calls getArea()
echo $circle->visible; // calls $circle->isVisible()
```
Properties are mostly a syntactic sugar to beautify the code and make programmer's life easier. You do not have to use them, if you don't want to.
Events
------
Now we are going to create functions, which will be called when border radius changes. Let's call it `change` event and those functions event handlers:
```php
class Circle extends Nette\Object
{
/** @var array */
public $onChange;
public function setRadius($radius)
{
// call events in onChange
$this->onChange($this, $this->radius, $radius);
$this->radius = max(0.0, (float) $radius);
}
}
$circle = new Circle;
// adding an event handler
$circle->onChange[] = function($circle, $oldValue, $newValue) {
echo 'there was a change!';
};
$circle->setRadius(10);
```
There is another syntactic sugar in `setRadius`'s code. Instead of iteration on `$onChange` array and calling each method one by one with unreliable (does not report if callback has any errors) function [php:call_user_func], you just have to write simple `onChange(...)` and given parameters will be handed over to the handlers.
Extension methods
-----------------
Do you need to add a new method to an existing object or class at runtime? **Extension methods** is just what you need.
```php
// declaration of future method Circle::getCircumference()
Circle::extensionMethod('getCircumference', function (Circle $that) {
return $that->radius * 2 * M_PI;
});
$circle = new Circle;
$circle->radius = 10;
echo $circle->getCircumference(); // ≈ 62.8
```
Extensions methods can also take parameters. They don't break encapsulation, because they only have access to the public members of the class. You can also connect them with interfaces, therefore every class implementing that interface will have that method available.
src/Iterators/CachingIterator.php 0000666 00000010677 13533704400 0013104 0 ustar 00 getIterator();
} while ($iterator instanceof \IteratorAggregate);
} elseif ($iterator instanceof \Traversable) {
if (!$iterator instanceof \Iterator) {
$iterator = new \IteratorIterator($iterator);
}
} else {
throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', __CLASS__, is_object($iterator) ? get_class($iterator) : gettype($iterator)));
}
parent::__construct($iterator, 0);
}
/**
* Is the current element the first one?
* @param int grid width
* @return bool
*/
public function isFirst($width = NULL)
{
return $this->counter === 1 || ($width && $this->counter !== 0 && (($this->counter - 1) % $width) === 0);
}
/**
* Is the current element the last one?
* @param int grid width
* @return bool
*/
public function isLast($width = NULL)
{
return !$this->hasNext() || ($width && ($this->counter % $width) === 0);
}
/**
* Is the iterator empty?
* @return bool
*/
public function isEmpty()
{
return $this->counter === 0;
}
/**
* Is the counter odd?
* @return bool
*/
public function isOdd()
{
return $this->counter % 2 === 1;
}
/**
* Is the counter even?
* @return bool
*/
public function isEven()
{
return $this->counter % 2 === 0;
}
/**
* Returns the counter.
* @return int
*/
public function getCounter()
{
return $this->counter;
}
/**
* Returns the count of elements.
* @return int
*/
public function count()
{
$inner = $this->getInnerIterator();
if ($inner instanceof \Countable) {
return $inner->count();
} else {
throw new Nette\NotSupportedException('Iterator is not countable.');
}
}
/**
* Forwards to the next element.
* @return void
*/
public function next()
{
parent::next();
if (parent::valid()) {
$this->counter++;
}
}
/**
* Rewinds the Iterator.
* @return void
*/
public function rewind()
{
parent::rewind();
$this->counter = parent::valid() ? 1 : 0;
}
/**
* Returns the next key.
* @return mixed
*/
public function getNextKey()
{
return $this->getInnerIterator()->key();
}
/**
* Returns the next element.
* @return mixed
*/
public function getNextValue()
{
return $this->getInnerIterator()->current();
}
/********************* Nette\Object behaviour ****************d*g**/
/**
* Call to undefined method.
* @param string method name
* @param array arguments
* @return mixed
* @throws Nette\MemberAccessException
*/
public function __call($name, $args)
{
return ObjectMixin::call($this, $name, $args);
}
/**
* Returns property value. Do not call directly.
* @param string property name
* @return mixed property value
* @throws Nette\MemberAccessException if the property is not defined.
*/
public function &__get($name)
{
return ObjectMixin::get($this, $name);
}
/**
* Sets value of a property. Do not call directly.
* @param string property name
* @param mixed property value
* @return void
* @throws Nette\MemberAccessException if the property is not defined or is read-only
*/
public function __set($name, $value)
{
ObjectMixin::set($this, $name, $value);
}
/**
* Is property defined?
* @param string property name
* @return bool
*/
public function __isset($name)
{
return ObjectMixin::has($this, $name);
}
/**
* Access to undeclared property.
* @param string property name
* @return void
* @throws Nette\MemberAccessException
*/
public function __unset($name)
{
ObjectMixin::remove($this, $name);
}
}
src/Iterators/Filter.php 0000666 00000001117 13533704400 0011250 0 ustar 00 callback = Nette\Utils\Callback::check($callback);
}
public function accept()
{
return call_user_func($this->callback, $this->current(), $this->key(), $this);
}
}
src/Iterators/Mapper.php 0000666 00000001144 13533704400 0011247 0 ustar 00 callback = Nette\Utils\Callback::check($callback);
}
public function current()
{
return call_user_func($this->callback, parent::current(), parent::key());
}
}
src/Iterators/RecursiveFilter.php 0000666 00000001163 13533704400 0013141 0 ustar 00 getInnerIterator()->hasChildren();
}
public function getChildren()
{
return new static($this->getInnerIterator()->getChildren(), $this->callback);
}
}
src/Utils/ArrayHash.php 0000666 00000003125 13533704400 0011032 0 ustar 00 $value) {
if ($recursive && is_array($value)) {
$obj->$key = static::from($value, TRUE);
} else {
$obj->$key = $value;
}
}
return $obj;
}
/**
* Returns an iterator over all items.
* @return \RecursiveArrayIterator
*/
public function getIterator()
{
return new \RecursiveArrayIterator($this);
}
/**
* Returns items count.
* @return int
*/
public function count()
{
return count((array) $this);
}
/**
* Replaces or appends a item.
* @return void
*/
public function offsetSet($key, $value)
{
if (!is_scalar($key)) { // prevents NULL
throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', gettype($key)));
}
$this->$key = $value;
}
/**
* Returns a item.
* @return mixed
*/
public function offsetGet($key)
{
return $this->$key;
}
/**
* Determines whether a item exists.
* @return bool
*/
public function offsetExists($key)
{
return isset($this->$key);
}
/**
* Removes the element from this list.
* @return void
*/
public function offsetUnset($key)
{
unset($this->$key);
}
}
src/Utils/ArrayList.php 0000666 00000003703 13533704400 0011064 0 ustar 00 list);
}
/**
* Returns items count.
* @return int
*/
public function count()
{
return count($this->list);
}
/**
* Replaces or appends a item.
* @param int|NULL
* @param mixed
* @return void
* @throws Nette\OutOfRangeException
*/
public function offsetSet($index, $value)
{
if ($index === NULL) {
$this->list[] = $value;
} elseif ($index < 0 || $index >= count($this->list)) {
throw new Nette\OutOfRangeException('Offset invalid or out of range');
} else {
$this->list[(int) $index] = $value;
}
}
/**
* Returns a item.
* @param int
* @return mixed
* @throws Nette\OutOfRangeException
*/
public function offsetGet($index)
{
if ($index < 0 || $index >= count($this->list)) {
throw new Nette\OutOfRangeException('Offset invalid or out of range');
}
return $this->list[(int) $index];
}
/**
* Determines whether a item exists.
* @param int
* @return bool
*/
public function offsetExists($index)
{
return $index >= 0 && $index < count($this->list);
}
/**
* Removes the element at the specified position in this list.
* @param int
* @return void
* @throws Nette\OutOfRangeException
*/
public function offsetUnset($index)
{
if ($index < 0 || $index >= count($this->list)) {
throw new Nette\OutOfRangeException('Offset invalid or out of range');
}
array_splice($this->list, (int) $index, 1);
}
}
src/Utils/Arrays.php 0000666 00000013450 13533704400 0010413 0 ustar 00 $v) {
if (is_array($v) && is_array($arr2[$k])) {
$res[$k] = self::mergeTree($v, $arr2[$k]);
}
}
return $res;
}
/**
* Searches the array for a given key and returns the offset if successful.
* @return int|FALSE offset if it is found, FALSE otherwise
*/
public static function searchKey($arr, $key)
{
$foo = array($key => NULL);
return array_search(key($foo), array_keys($arr), TRUE);
}
/**
* Inserts new array before item specified by key.
* @return void
*/
public static function insertBefore(array & $arr, $key, array $inserted)
{
$offset = self::searchKey($arr, $key);
$arr = array_slice($arr, 0, $offset, TRUE) + $inserted + array_slice($arr, $offset, count($arr), TRUE);
}
/**
* Inserts new array after item specified by key.
* @return void
*/
public static function insertAfter(array & $arr, $key, array $inserted)
{
$offset = self::searchKey($arr, $key);
$offset = $offset === FALSE ? count($arr) : $offset + 1;
$arr = array_slice($arr, 0, $offset, TRUE) + $inserted + array_slice($arr, $offset, count($arr), TRUE);
}
/**
* Renames key in array.
* @return void
*/
public static function renameKey(array & $arr, $oldKey, $newKey)
{
$offset = self::searchKey($arr, $oldKey);
if ($offset !== FALSE) {
$keys = array_keys($arr);
$keys[$offset] = $newKey;
$arr = array_combine($keys, $arr);
}
}
/**
* Returns array entries that match the pattern.
* @return array
*/
public static function grep(array $arr, $pattern, $flags = 0)
{
return Strings::pcre('preg_grep', array($pattern, $arr, $flags));
}
/**
* Returns flattened array.
* @return array
*/
public static function flatten(array $arr, $preserveKeys = FALSE)
{
$res = array();
$cb = $preserveKeys
? function ($v, $k) use (& $res) { $res[$k] = $v; }
: function ($v) use (& $res) { $res[] = $v; };
array_walk_recursive($arr, $cb);
return $res;
}
/**
* Finds whether a variable is a zero-based integer indexed array.
* @return bool
*/
public static function isList($value)
{
return is_array($value) && (!$value || array_keys($value) === range(0, count($value) - 1));
}
/**
* Reformats table to associative tree. Path looks like 'field|field[]field->field=field'.
* @return array|\stdClass
*/
public static function associate(array $arr, $path)
{
$parts = is_array($path)
? $path
: preg_split('#(\[\]|->|=|\|)#', $path, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
if (!$parts || $parts[0] === '=' || $parts[0] === '|' || $parts === array('->')) {
throw new Nette\InvalidArgumentException("Invalid path '$path'.");
}
$res = $parts[0] === '->' ? new \stdClass : array();
foreach ($arr as $rowOrig) {
$row = (array) $rowOrig;
$x = & $res;
for ($i = 0; $i < count($parts); $i++) {
$part = $parts[$i];
if ($part === '[]') {
$x = & $x[];
} elseif ($part === '=') {
if (isset($parts[++$i])) {
$x = $row[$parts[$i]];
$row = NULL;
}
} elseif ($part === '->') {
if (isset($parts[++$i])) {
$x = & $x->{$row[$parts[$i]]};
} else {
$row = is_object($rowOrig) ? $rowOrig : (object) $row;
}
} elseif ($part !== '|') {
$x = & $x[(string) $row[$part]];
}
}
if ($x === NULL) {
$x = $row;
}
}
return $res;
}
/**
* Normalizes to associative array.
* @return array
*/
public static function normalize(array $arr, $filling = NULL)
{
$res = array();
foreach ($arr as $k => $v) {
$res[is_int($k) ? $v : $k] = is_int($k) ? $filling : $v;
}
return $res;
}
/**
* Picks element from the array by key and return its value.
* @param array
* @param string|int array key
* @param mixed
* @return mixed
* @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
*/
public static function pick(array & $arr, $key, $default = NULL)
{
if (array_key_exists($key, $arr)) {
$value = $arr[$key];
unset($arr[$key]);
return $value;
} elseif (func_num_args() < 3) {
throw new Nette\InvalidArgumentException("Missing item '$key'.");
} else {
return $default;
}
}
}
src/Utils/Callback.php 0000666 00000011361 13533704400 0010645 0 ustar 00 = 50400) {
if (is_string($callable) && function_exists($callable)) {
$r = new \ReflectionFunction($callable);
return $r->getClosure();
} elseif (is_array($callable) && method_exists($callable[0], $callable[1])) {
$r = new \ReflectionMethod($callable[0], $callable[1]);
return $r->getClosure($callable[0]);
}
}
self::check($callable);
$_callable_ = $callable;
return function () use ($_callable_) {
return call_user_func_array($_callable_, func_get_args());
};
}
/**
* Invokes callback.
* @return mixed
*/
public static function invoke($callable)
{
self::check($callable);
return call_user_func_array($callable, array_slice(func_get_args(), 1));
}
/**
* Invokes callback with an array of parameters.
* @return mixed
*/
public static function invokeArgs($callable, array $args = array())
{
self::check($callable);
return call_user_func_array($callable, $args);
}
/**
* Invokes internal PHP function with own error handler.
* @param string
* @return mixed
*/
public static function invokeSafe($function, array $args, $onError)
{
$prev = set_error_handler(function ($severity, $message, $file, $line, $context = NULL, $stack = NULL) use ($onError, & $prev, $function) {
if ($file === '' && defined('HHVM_VERSION')) { // https://github.com/facebook/hhvm/issues/4625
$file = $stack[1]['file'];
}
if ($file === __FILE__ && $onError(str_replace("$function(): ", '', $message), $severity) !== FALSE) {
return;
} elseif ($prev) {
return call_user_func_array($prev, func_get_args());
}
return FALSE;
});
try {
$res = call_user_func_array($function, $args);
restore_error_handler();
return $res;
} catch (\Exception $e) {
restore_error_handler();
throw $e;
}
}
/**
* @return callable
*/
public static function check($callable, $syntax = FALSE)
{
if (!is_callable($callable, $syntax)) {
throw new Nette\InvalidArgumentException($syntax
? 'Given value is not a callable type.'
: sprintf("Callback '%s' is not callable.", self::toString($callable))
);
}
return $callable;
}
/**
* @return string
*/
public static function toString($callable)
{
if ($callable instanceof \Closure) {
$inner = self::unwrap($callable);
return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
} elseif (is_string($callable) && $callable[0] === "\0") {
return '{lambda}';
} else {
is_callable($callable, TRUE, $textual);
return $textual;
}
}
/**
* @return \ReflectionMethod|\ReflectionFunction
*/
public static function toReflection($callable)
{
if ($callable instanceof \Closure) {
$callable = self::unwrap($callable);
} elseif ($callable instanceof Nette\Callback) {
$callable = $callable->getNative();
}
$class = class_exists('Nette\Reflection\Method') ? 'Nette\Reflection\Method' : 'ReflectionMethod';
if (is_string($callable) && strpos($callable, '::')) {
return new $class($callable);
} elseif (is_array($callable)) {
return new $class($callable[0], $callable[1]);
} elseif (is_object($callable) && !$callable instanceof \Closure) {
return new $class($callable, '__invoke');
} else {
$class = class_exists('Nette\Reflection\GlobalFunction') ? 'Nette\Reflection\GlobalFunction' : 'ReflectionFunction';
return new $class($callable);
}
}
/**
* @return bool
*/
public static function isStatic($callable)
{
return is_array($callable) ? is_string($callable[0]) : is_string($callable);
}
/**
* Unwraps closure created by self::closure()
* @internal
* @return callable
*/
public static function unwrap(\Closure $closure)
{
$r = new \ReflectionFunction($closure);
if (substr($r->getName(), -1) === '}') {
$vars = $r->getStaticVariables();
return isset($vars['_callable_']) ? $vars['_callable_'] : $closure;
} elseif ($obj = $r->getClosureThis()) {
return array($obj, $r->getName());
} elseif ($class = $r->getClosureScopeClass()) {
return array($class->getName(), $r->getName());
} else {
return $r->getName();
}
}
}
src/Utils/DateTime.php 0000666 00000004722 13533704400 0010650 0 ustar 00 format('Y-m-d H:i:s'), $time->getTimezone());
} elseif (is_numeric($time)) {
if ($time <= self::YEAR) {
$time += time();
}
$tmp = new static('@' . $time);
return $tmp->setTimeZone(new \DateTimeZone(date_default_timezone_get()));
} else { // textual or NULL
return new static($time);
}
}
/**
* @return string
*/
public function __toString()
{
return $this->format('Y-m-d H:i:s');
}
/**
* @param string
* @return self
*/
public function modifyClone($modify = '')
{
$dolly = clone $this;
return $modify ? $dolly->modify($modify) : $dolly;
}
/**
* @param int
* @return self
*/
public function setTimestamp($timestamp)
{
$zone = $this->getTimezone();
$this->__construct('@' . $timestamp);
return $this->setTimeZone($zone);
}
/**
* @return int|string
*/
public function getTimestamp()
{
$ts = $this->format('U');
return is_float($tmp = $ts * 1) ? $ts : $tmp;
}
/**
* Returns new DateTime object formatted according to the specified format.
* @param string The format the $time parameter should be in
* @param string String representing the time
* @param string|\DateTimeZone desired timezone (default timezone is used if NULL is passed)
* @return self|FALSE
*/
public static function createFromFormat($format, $time, $timezone = NULL)
{
if ($timezone === NULL) {
$timezone = new \DateTimeZone(date_default_timezone_get());
} elseif (is_string($timezone)) {
$timezone = new \DateTimeZone($timezone);
} elseif (!$timezone instanceof \DateTimeZone) {
throw new Nette\InvalidArgumentException('Invalid timezone given');
}
$date = parent::createFromFormat($format, $time, $timezone);
return $date ? static::from($date) : FALSE;
}
}
src/Utils/FileSystem.php 0000666 00000007315 13533704400 0011241 0 ustar 00 isDir()) {
static::createDir($dest . '/' . $iterator->getSubPathName());
} else {
static::copy($item, $dest . '/' . $iterator->getSubPathName());
}
}
} else {
static::createDir(dirname($dest));
if (@stream_copy_to_stream(fopen($source, 'r'), fopen($dest, 'w')) === FALSE) { // @ is escalated to exception
throw new Nette\IOException("Unable to copy file '$source' to '$dest'.");
}
}
}
/**
* Deletes a file or directory.
* @return void
* @throws Nette\IOException
*/
public static function delete($path)
{
if (is_file($path) || is_link($path)) {
$func = DIRECTORY_SEPARATOR === '\\' && is_dir($path) ? 'rmdir' : 'unlink';
if (!@$func($path)) { // @ is escalated to exception
throw new Nette\IOException("Unable to delete '$path'.");
}
} elseif (is_dir($path)) {
foreach (new \FilesystemIterator($path) as $item) {
static::delete($item);
}
if (!@rmdir($path)) { // @ is escalated to exception
throw new Nette\IOException("Unable to delete directory '$path'.");
}
}
}
/**
* Renames a file or directory.
* @return void
* @throws Nette\IOException
* @throws Nette\InvalidStateException if the target file or directory already exist
*/
public static function rename($name, $newName, $overwrite = TRUE)
{
if (!$overwrite && file_exists($newName)) {
throw new Nette\InvalidStateException("File or directory '$newName' already exists.");
} elseif (!file_exists($name)) {
throw new Nette\IOException("File or directory '$name' not found.");
} else {
static::createDir(dirname($newName));
static::delete($newName);
if (!@rename($name, $newName)) { // @ is escalated to exception
throw new Nette\IOException("Unable to rename file or directory '$name' to '$newName'.");
}
}
}
/**
* Writes a string to a file.
* @return void
* @throws Nette\IOException
*/
public static function write($file, $content, $mode = 0666)
{
static::createDir(dirname($file));
if (@file_put_contents($file, $content) === FALSE) { // @ is escalated to exception
throw new Nette\IOException("Unable to write file '$file'.");
}
if ($mode !== NULL && !@chmod($file, $mode)) { // @ is escalated to exception
throw new Nette\IOException("Unable to chmod file '$file'.");
}
}
/**
* Is path absolute?
* @return bool
*/
public static function isAbsolute($path)
{
return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
}
}
src/Utils/Html.php 0000666 00000027643 13533704400 0010067 0 ustar 00
* $el = Html::el('a')->href($link)->setText('Nette');
* $el->class = 'myclass';
* echo $el;
*
* echo $el->startTag(), $el->endTag();
*
*/
class Html extends Nette\Object implements \ArrayAccess, \Countable, \IteratorAggregate, IHtmlString
{
/** @var string element's name */
private $name;
/** @var bool is element empty? */
private $isEmpty;
/** @var array element's attributes */
public $attrs = array();
/** @var array of Html | string nodes */
protected $children = array();
/** @var bool use XHTML syntax? */
public static $xhtml = FALSE;
/** @var array empty (void) elements */
public static $emptyElements = array(
'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1,
'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1,
'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1,
);
/**
* Static factory.
* @param string element name (or NULL)
* @param array|string element's attributes or plain text content
* @return self
*/
public static function el($name = NULL, $attrs = NULL)
{
$el = new static;
$parts = explode(' ', $name, 2);
$el->setName($parts[0]);
if (is_array($attrs)) {
$el->attrs = $attrs;
} elseif ($attrs !== NULL) {
$el->setText($attrs);
}
if (isset($parts[1])) {
foreach (Strings::matchAll($parts[1] . ' ', '#([a-z0-9:-]+)(?:=(["\'])?(.*?)(?(2)\\2|\s))?#i') as $m) {
$el->attrs[$m[1]] = isset($m[3]) ? $m[3] : TRUE;
}
}
return $el;
}
/**
* Changes element's name.
* @param string
* @param bool Is element empty?
* @return self
* @throws Nette\InvalidArgumentException
*/
public function setName($name, $isEmpty = NULL)
{
if ($name !== NULL && !is_string($name)) {
throw new Nette\InvalidArgumentException(sprintf('Name must be string or NULL, %s given.', gettype($name)));
}
$this->name = $name;
$this->isEmpty = $isEmpty === NULL ? isset(static::$emptyElements[$name]) : (bool) $isEmpty;
return $this;
}
/**
* Returns element's name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Is element empty?
* @return bool
*/
public function isEmpty()
{
return $this->isEmpty;
}
/**
* Sets multiple attributes.
* @param array
* @return self
*/
public function addAttributes(array $attrs)
{
$this->attrs = array_merge($this->attrs, $attrs);
return $this;
}
/**
* Overloaded setter for element's attribute.
* @param string HTML attribute name
* @param mixed HTML attribute value
* @return void
*/
public function __set($name, $value)
{
$this->attrs[$name] = $value;
}
/**
* Overloaded getter for element's attribute.
* @param string HTML attribute name
* @return mixed HTML attribute value
*/
public function &__get($name)
{
return $this->attrs[$name];
}
/**
* Overloaded tester for element's attribute.
* @param string HTML attribute name
* @return bool
*/
public function __isset($name)
{
return isset($this->attrs[$name]);
}
/**
* Overloaded unsetter for element's attribute.
* @param string HTML attribute name
* @return void
*/
public function __unset($name)
{
unset($this->attrs[$name]);
}
/**
* Overloaded setter for element's attribute.
* @param string HTML attribute name
* @param array (string) HTML attribute value or pair?
* @return self
*/
public function __call($m, $args)
{
$p = substr($m, 0, 3);
if ($p === 'get' || $p === 'set' || $p === 'add') {
$m = substr($m, 3);
$m[0] = $m[0] | "\x20";
if ($p === 'get') {
return isset($this->attrs[$m]) ? $this->attrs[$m] : NULL;
} elseif ($p === 'add') {
$args[] = TRUE;
}
}
if (count($args) === 0) { // invalid
} elseif (count($args) === 1) { // set
$this->attrs[$m] = $args[0];
} elseif ((string) $args[0] === '') {
$tmp = & $this->attrs[$m]; // appending empty value? -> ignore, but ensure it exists
} elseif (!isset($this->attrs[$m]) || is_array($this->attrs[$m])) { // needs array
$this->attrs[$m][$args[0]] = $args[1];
} else {
$this->attrs[$m] = array($this->attrs[$m], $args[0] => $args[1]);
}
return $this;
}
/**
* Special setter for element's attribute.
* @param string path
* @param array query
* @return self
*/
public function href($path, $query = NULL)
{
if ($query) {
$query = http_build_query($query, NULL, '&');
if ($query !== '') {
$path .= '?' . $query;
}
}
$this->attrs['href'] = $path;
return $this;
}
/**
* Setter for data-* attributes. Booleans are converted to 'true' resp. 'false'.
* @return self
*/
public function data($name, $value = NULL)
{
if (func_num_args() === 1) {
$this->attrs['data'] = $name;
} else {
$this->attrs["data-$name"] = is_bool($value) ? json_encode($value) : $value;
}
return $this;
}
/**
* Sets element's HTML content.
* @param string raw HTML string
* @return self
* @throws Nette\InvalidArgumentException
*/
public function setHtml($html)
{
if (is_array($html)) {
throw new Nette\InvalidArgumentException(sprintf('Textual content must be a scalar, %s given.', gettype($html)));
}
$this->removeChildren();
$this->children[] = (string) $html;
return $this;
}
/**
* Returns element's HTML content.
* @return string
*/
public function getHtml()
{
$s = '';
foreach ($this->children as $child) {
if (is_object($child)) {
$s .= $child->render();
} else {
$s .= $child;
}
}
return $s;
}
/**
* Sets element's textual content.
* @param string
* @return self
* @throws Nette\InvalidArgumentException
*/
public function setText($text)
{
if (!is_array($text) && !$text instanceof self) {
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
}
return $this->setHtml($text);
}
/**
* Returns element's textual content.
* @return string
*/
public function getText()
{
return html_entity_decode(strip_tags($this->getHtml()), ENT_QUOTES, 'UTF-8');
}
/**
* Adds new element's child.
* @param Html|string Html node or raw HTML string
* @return self
*/
public function add($child)
{
return $this->insert(NULL, $child);
}
/**
* Creates and adds a new Html child.
* @param string elements's name
* @param array|string element's attributes or raw HTML string
* @return self created element
*/
public function create($name, $attrs = NULL)
{
$this->insert(NULL, $child = static::el($name, $attrs));
return $child;
}
/**
* Inserts child node.
* @param int|NULL position of NULL for appending
* @param Html|string Html node or raw HTML string
* @param bool
* @return self
* @throws Nette\InvalidArgumentException
*/
public function insert($index, $child, $replace = FALSE)
{
if ($child instanceof self || is_scalar($child)) {
if ($index === NULL) { // append
$this->children[] = $child;
} else { // insert or replace
array_splice($this->children, (int) $index, $replace ? 1 : 0, array($child));
}
} else {
throw new Nette\InvalidArgumentException(sprintf('Child node must be scalar or Html object, %s given.', is_object($child) ? get_class($child) : gettype($child)));
}
return $this;
}
/**
* Inserts (replaces) child node (\ArrayAccess implementation).
* @param int|NULL position of NULL for appending
* @param Html|string Html node or raw HTML string
* @return void
*/
public function offsetSet($index, $child)
{
$this->insert($index, $child, TRUE);
}
/**
* Returns child node (\ArrayAccess implementation).
* @param int
* @return self|string
*/
public function offsetGet($index)
{
return $this->children[$index];
}
/**
* Exists child node? (\ArrayAccess implementation).
* @param int
* @return bool
*/
public function offsetExists($index)
{
return isset($this->children[$index]);
}
/**
* Removes child node (\ArrayAccess implementation).
* @param int
* @return void
*/
public function offsetUnset($index)
{
if (isset($this->children[$index])) {
array_splice($this->children, (int) $index, 1);
}
}
/**
* Returns children count.
* @return int
*/
public function count()
{
return count($this->children);
}
/**
* Removed all children.
* @return void
*/
public function removeChildren()
{
$this->children = array();
}
/**
* Iterates over a elements.
* @return \ArrayIterator
*/
public function getIterator()
{
return new \ArrayIterator($this->children);
}
/**
* Returns all of children.
* @return array
*/
public function getChildren()
{
return $this->children;
}
/**
* Renders element's start tag, content and end tag.
* @param int
* @return string
*/
public function render($indent = NULL)
{
$s = $this->startTag();
if (!$this->isEmpty) {
// add content
if ($indent !== NULL) {
$indent++;
}
foreach ($this->children as $child) {
if (is_object($child)) {
$s .= $child->render($indent);
} else {
$s .= $child;
}
}
// add end tag
$s .= $this->endTag();
}
if ($indent !== NULL) {
return "\n" . str_repeat("\t", $indent - 1) . $s . "\n" . str_repeat("\t", max(0, $indent - 2));
}
return $s;
}
public function __toString()
{
return $this->render();
}
/**
* Returns element's start tag.
* @return string
*/
public function startTag()
{
if ($this->name) {
return '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>');
} else {
return '';
}
}
/**
* Returns element's end tag.
* @return string
*/
public function endTag()
{
return $this->name && !$this->isEmpty ? '' . $this->name . '>' : '';
}
/**
* Returns element's attributes.
* @return string
* @internal
*/
public function attributes()
{
if (!is_array($this->attrs)) {
return '';
}
$s = '';
foreach ($this->attrs as $key => $value) {
if ($value === NULL || $value === FALSE) {
continue;
} elseif ($value === TRUE) {
if (static::$xhtml) {
$s .= ' ' . $key . '="' . $key . '"';
} else {
$s .= ' ' . $key;
}
continue;
} elseif (is_array($value)) {
if ($key === 'data') { // deprecated
foreach ($value as $k => $v) {
if ($v !== NULL && $v !== FALSE) {
if (is_array($v)) {
$v = Json::encode($v);
}
$q = strpos($v, '"') === FALSE ? '"' : "'";
$s .= ' data-' . $k . '='
. $q . str_replace(array('&', $q), array('&', $q === '"' ? '"' : '''), $v)
. (strpos($v, '`') !== FALSE && strpbrk($v, ' <>"\'') === FALSE ? ' ' : '')
. $q;
}
}
continue;
} elseif (strncmp($key, 'data-', 5) === 0) {
$value = Json::encode($value);
} else {
$tmp = NULL;
foreach ($value as $k => $v) {
if ($v != NULL) { // intentionally ==, skip NULLs & empty string
// composite 'style' vs. 'others'
$tmp[] = $v === TRUE ? $k : (is_string($k) ? $k . ':' . $v : $v);
}
}
if ($tmp === NULL) {
continue;
}
$value = implode($key === 'style' || !strncmp($key, 'on', 2) ? ';' : ' ', $tmp);
}
} elseif (is_float($value)) {
$value = rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
} else {
$value = (string) $value;
}
$q = strpos($value, '"') === FALSE ? '"' : "'";
$s .= ' ' . $key . '='
. $q . str_replace(array('&', $q), array('&', $q === '"' ? '"' : '''), $value)
. (strpos($value, '`') !== FALSE && strpbrk($value, ' <>"\'') === FALSE ? ' ' : '')
. $q;
}
$s = str_replace('@', '@', $s);
return $s;
}
/**
* Clones all children too.
*/
public function __clone()
{
foreach ($this->children as $key => $value) {
if (is_object($value)) {
$this->children[$key] = clone $value;
}
}
}
}
src/Utils/IHtmlString.php 0000666 00000000402 13533704400 0011347 0 ustar 00
* $image = Image::fromFile('nette.jpg');
* $image->resize(150, 100);
* $image->sharpen();
* $image->send();
*
*
* @method void alphaBlending(bool $on)
* @method void antialias(bool $on)
* @method void arc($x, $y, $w, $h, $start, $end, $color)
* @method void char(int $font, $x, $y, string $char, $color)
* @method void charUp(int $font, $x, $y, string $char, $color)
* @method int colorAllocate($red, $green, $blue)
* @method int colorAllocateAlpha($red, $green, $blue, $alpha)
* @method int colorAt($x, $y)
* @method int colorClosest($red, $green, $blue)
* @method int colorClosestAlpha($red, $green, $blue, $alpha)
* @method int colorClosestHWB($red, $green, $blue)
* @method void colorDeallocate($color)
* @method int colorExact($red, $green, $blue)
* @method int colorExactAlpha($red, $green, $blue, $alpha)
* @method void colorMatch(Image $image2)
* @method int colorResolve($red, $green, $blue)
* @method int colorResolveAlpha($red, $green, $blue, $alpha)
* @method void colorSet($index, $red, $green, $blue)
* @method array colorsForIndex($index)
* @method int colorsTotal()
* @method int colorTransparent($color = NULL)
* @method void convolution(array $matrix, float $div, float $offset)
* @method void copy(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH)
* @method void copyMerge(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
* @method void copyMergeGray(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
* @method void copyResampled(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
* @method void copyResized(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
* @method void dashedLine($x1, $y1, $x2, $y2, $color)
* @method void ellipse($cx, $cy, $w, $h, $color)
* @method void fill($x, $y, $color)
* @method void filledArc($cx, $cy, $w, $h, $s, $e, $color, $style)
* @method void filledEllipse($cx, $cy, $w, $h, $color)
* @method void filledPolygon(array $points, $numPoints, $color)
* @method void filledRectangle($x1, $y1, $x2, $y2, $color)
* @method void fillToBorder($x, $y, $border, $color)
* @method void filter($filtertype)
* @method array ftText($size, $angle, $x, $y, $col, string $fontFile, string $text, array $extrainfo = NULL)
* @method void gammaCorrect(float $inputgamma, float $outputgamma)
* @method int interlace($interlace = NULL)
* @method bool isTrueColor()
* @method void layerEffect($effect)
* @method void line($x1, $y1, $x2, $y2, $color)
* @method void paletteCopy(Image $source)
* @method void polygon(array $points, $numPoints, $color)
* @method array psText(string $text, $font, $size, $color, $backgroundColor, $x, $y, $space = NULL, $tightness = NULL, float $angle = NULL, $antialiasSteps = NULL)
* @method void rectangle($x1, $y1, $x2, $y2, $col)
* @method Image rotate(float $angle, $backgroundColor)
* @method void saveAlpha(bool $saveflag)
* @method void setBrush(Image $brush)
* @method void setPixel($x, $y, $color)
* @method void setStyle(array $style)
* @method void setThickness($thickness)
* @method void setTile(Image $tile)
* @method void string($font, $x, $y, string $s, $col)
* @method void stringUp($font, $x, $y, string $s, $col)
* @method void trueColorToPalette(bool $dither, $ncolors)
* @method array ttfText($size, $angle, $x, $y, $color, string $fontfile, string $text)
* @property-read int $width
* @property-read int $height
* @property-read resource $imageResource
*/
class Image extends Nette\Object
{
/** {@link resize()} only shrinks images */
const SHRINK_ONLY = 1;
/** {@link resize()} will ignore aspect ratio */
const STRETCH = 2;
/** {@link resize()} fits in given area so its dimensions are less than or equal to the required dimensions */
const FIT = 0;
/** {@link resize()} fills given area so its dimensions are greater than or equal to the required dimensions */
const FILL = 4;
/** {@link resize()} fills given area exactly */
const EXACT = 8;
/** image types */
const JPEG = IMAGETYPE_JPEG,
PNG = IMAGETYPE_PNG,
GIF = IMAGETYPE_GIF;
const EMPTY_GIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
/** @deprecated */
const ENLARGE = 0;
/** @var resource */
private $image;
/**
* Returns RGB color.
* @param int red 0..255
* @param int green 0..255
* @param int blue 0..255
* @param int transparency 0..127
* @return array
*/
public static function rgb($red, $green, $blue, $transparency = 0)
{
return array(
'red' => max(0, min(255, (int) $red)),
'green' => max(0, min(255, (int) $green)),
'blue' => max(0, min(255, (int) $blue)),
'alpha' => max(0, min(127, (int) $transparency)),
);
}
/**
* Opens image from file.
* @param string
* @param mixed detected image format
* @throws Nette\NotSupportedException if gd extension is not loaded
* @throws UnknownImageFileException if file not found or file type is not known
* @return self
*/
public static function fromFile($file, & $format = NULL)
{
if (!extension_loaded('gd')) {
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
}
static $funcs = array(
self::JPEG => 'imagecreatefromjpeg',
self::PNG => 'imagecreatefrompng',
self::GIF => 'imagecreatefromgif',
);
$info = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
$format = $info[2];
if (!isset($funcs[$format])) {
throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found.");
}
return new static(Callback::invokeSafe($funcs[$format], array($file), function ($message) {
throw new ImageException($message);
}));
}
/**
* @deprecated
*/
public static function getFormatFromString($s)
{
trigger_error(__METHOD__ . '() is deprecated; use finfo_buffer() instead.', E_USER_DEPRECATED);
$types = array('image/jpeg' => self::JPEG, 'image/gif' => self::GIF, 'image/png' => self::PNG);
$type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $s);
return isset($types[$type]) ? $types[$type] : NULL;
}
/**
* Create a new image from the image stream in the string.
* @param string
* @param mixed detected image format
* @return self
* @throws ImageException
*/
public static function fromString($s, & $format = NULL)
{
if (!extension_loaded('gd')) {
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
}
if (func_num_args() > 1) {
$format = @static::getFormatFromString($s); // @ suppress trigger_error
}
return new static(Callback::invokeSafe('imagecreatefromstring', array($s), function ($message) {
throw new ImageException($message);
}));
}
/**
* Creates blank image.
* @param int
* @param int
* @param array
* @return self
*/
public static function fromBlank($width, $height, $color = NULL)
{
if (!extension_loaded('gd')) {
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
}
$width = (int) $width;
$height = (int) $height;
if ($width < 1 || $height < 1) {
throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.');
}
$image = imagecreatetruecolor($width, $height);
if (is_array($color)) {
$color += array('alpha' => 0);
$color = imagecolorallocatealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
imagealphablending($image, FALSE);
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
imagealphablending($image, TRUE);
}
return new static($image);
}
/**
* Wraps GD image.
* @param resource
*/
public function __construct($image)
{
$this->setImageResource($image);
imagesavealpha($image, TRUE);
}
/**
* Returns image width.
* @return int
*/
public function getWidth()
{
return imagesx($this->image);
}
/**
* Returns image height.
* @return int
*/
public function getHeight()
{
return imagesy($this->image);
}
/**
* Sets image resource.
* @param resource
* @return self
*/
protected function setImageResource($image)
{
if (!is_resource($image) || get_resource_type($image) !== 'gd') {
throw new Nette\InvalidArgumentException('Image is not valid.');
}
$this->image = $image;
return $this;
}
/**
* Returns image GD resource.
* @return resource
*/
public function getImageResource()
{
return $this->image;
}
/**
* Resizes image.
* @param mixed width in pixels or percent
* @param mixed height in pixels or percent
* @param int flags
* @return self
*/
public function resize($width, $height, $flags = self::FIT)
{
if ($flags & self::EXACT) {
return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height);
}
list($newWidth, $newHeight) = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags);
if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize
$newImage = static::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
imagecopyresampled(
$newImage, $this->image,
0, 0, 0, 0,
$newWidth, $newHeight, $this->getWidth(), $this->getHeight()
);
$this->image = $newImage;
}
if ($width < 0 || $height < 0) { // flip is processed in two steps for better quality
$newImage = static::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
imagecopyresampled(
$newImage, $this->image,
0, 0, $width < 0 ? $newWidth - 1 : 0, $height < 0 ? $newHeight - 1 : 0,
$newWidth, $newHeight, $width < 0 ? -$newWidth : $newWidth, $height < 0 ? -$newHeight : $newHeight
);
$this->image = $newImage;
}
return $this;
}
/**
* Calculates dimensions of resized image.
* @param mixed source width
* @param mixed source height
* @param mixed width in pixels or percent
* @param mixed height in pixels or percent
* @param int flags
* @return array
*/
public static function calculateSize($srcWidth, $srcHeight, $newWidth, $newHeight, $flags = self::FIT)
{
if (substr($newWidth, -1) === '%') {
$newWidth = round($srcWidth / 100 * abs($newWidth));
$percents = TRUE;
} else {
$newWidth = (int) abs($newWidth);
}
if (substr($newHeight, -1) === '%') {
$newHeight = round($srcHeight / 100 * abs($newHeight));
$flags |= empty($percents) ? 0 : self::STRETCH;
} else {
$newHeight = (int) abs($newHeight);
}
if ($flags & self::STRETCH) { // non-proportional
if (empty($newWidth) || empty($newHeight)) {
throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.');
}
if ($flags & self::SHRINK_ONLY) {
$newWidth = round($srcWidth * min(1, $newWidth / $srcWidth));
$newHeight = round($srcHeight * min(1, $newHeight / $srcHeight));
}
} else { // proportional
if (empty($newWidth) && empty($newHeight)) {
throw new Nette\InvalidArgumentException('At least width or height must be specified.');
}
$scale = array();
if ($newWidth > 0) { // fit width
$scale[] = $newWidth / $srcWidth;
}
if ($newHeight > 0) { // fit height
$scale[] = $newHeight / $srcHeight;
}
if ($flags & self::FILL) {
$scale = array(max($scale));
}
if ($flags & self::SHRINK_ONLY) {
$scale[] = 1;
}
$scale = min($scale);
$newWidth = round($srcWidth * $scale);
$newHeight = round($srcHeight * $scale);
}
return array(max((int) $newWidth, 1), max((int) $newHeight, 1));
}
/**
* Crops image.
* @param mixed x-offset in pixels or percent
* @param mixed y-offset in pixels or percent
* @param mixed width in pixels or percent
* @param mixed height in pixels or percent
* @return self
*/
public function crop($left, $top, $width, $height)
{
list($left, $top, $width, $height) = static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height);
$newImage = static::fromBlank($width, $height, self::RGB(0, 0, 0, 127))->getImageResource();
imagecopy($newImage, $this->image, 0, 0, $left, $top, $width, $height);
$this->image = $newImage;
return $this;
}
/**
* Calculates dimensions of cutout in image.
* @param mixed source width
* @param mixed source height
* @param mixed x-offset in pixels or percent
* @param mixed y-offset in pixels or percent
* @param mixed width in pixels or percent
* @param mixed height in pixels or percent
* @return array
*/
public static function calculateCutout($srcWidth, $srcHeight, $left, $top, $newWidth, $newHeight)
{
if (substr($newWidth, -1) === '%') {
$newWidth = round($srcWidth / 100 * $newWidth);
}
if (substr($newHeight, -1) === '%') {
$newHeight = round($srcHeight / 100 * $newHeight);
}
if (substr($left, -1) === '%') {
$left = round(($srcWidth - $newWidth) / 100 * $left);
}
if (substr($top, -1) === '%') {
$top = round(($srcHeight - $newHeight) / 100 * $top);
}
if ($left < 0) {
$newWidth += $left;
$left = 0;
}
if ($top < 0) {
$newHeight += $top;
$top = 0;
}
$newWidth = min((int) $newWidth, $srcWidth - $left);
$newHeight = min((int) $newHeight, $srcHeight - $top);
return array($left, $top, $newWidth, $newHeight);
}
/**
* Sharpen image.
* @return self
*/
public function sharpen()
{
imageconvolution($this->image, array( // my magic numbers ;)
array(-1, -1, -1),
array(-1, 24, -1),
array(-1, -1, -1),
), 16, 0);
return $this;
}
/**
* Puts another image into this image.
* @param Image
* @param mixed x-coordinate in pixels or percent
* @param mixed y-coordinate in pixels or percent
* @param int opacity 0..100
* @return self
*/
public function place(Image $image, $left = 0, $top = 0, $opacity = 100)
{
$opacity = max(0, min(100, (int) $opacity));
if (substr($left, -1) === '%') {
$left = round(($this->getWidth() - $image->getWidth()) / 100 * $left);
}
if (substr($top, -1) === '%') {
$top = round(($this->getHeight() - $image->getHeight()) / 100 * $top);
}
if ($opacity === 100) {
imagecopy(
$this->image, $image->getImageResource(),
$left, $top, 0, 0, $image->getWidth(), $image->getHeight()
);
} elseif ($opacity != 0) {
$cutting = imagecreatetruecolor($image->getWidth(), $image->getHeight());
imagecopy(
$cutting, $this->image,
0, 0, $left, $top, $image->getWidth(), $image->getHeight()
);
imagecopy(
$cutting, $image->getImageResource(),
0, 0, 0, 0, $image->getWidth(), $image->getHeight()
);
imagecopymerge(
$this->image, $cutting,
$left, $top, 0, 0, $image->getWidth(), $image->getHeight(),
$opacity
);
}
return $this;
}
/**
* Saves image to the file.
* @param string filename
* @param int quality 0..100 (for JPEG and PNG)
* @param int optional image type
* @return bool TRUE on success or FALSE on failure.
*/
public function save($file = NULL, $quality = NULL, $type = NULL)
{
if ($type === NULL) {
switch (strtolower(pathinfo($file, PATHINFO_EXTENSION))) {
case 'jpg':
case 'jpeg':
$type = self::JPEG;
break;
case 'png':
$type = self::PNG;
break;
case 'gif':
$type = self::GIF;
}
}
switch ($type) {
case self::JPEG:
$quality = $quality === NULL ? 85 : max(0, min(100, (int) $quality));
return imagejpeg($this->image, $file, $quality);
case self::PNG:
$quality = $quality === NULL ? 9 : max(0, min(9, (int) $quality));
return imagepng($this->image, $file, $quality);
case self::GIF:
return imagegif($this->image, $file);
default:
throw new Nette\InvalidArgumentException('Unsupported image type \'$type\'.');
}
}
/**
* Outputs image to string.
* @param int image type
* @param int quality 0..100 (for JPEG and PNG)
* @return string
*/
public function toString($type = self::JPEG, $quality = NULL)
{
ob_start();
$this->save(NULL, $quality, $type);
return ob_get_clean();
}
/**
* Outputs image to string.
* @return string
*/
public function __toString()
{
try {
return $this->toString();
} catch (\Exception $e) {
if (func_num_args()) {
throw $e;
}
trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
}
}
/**
* Outputs image to browser.
* @param int image type
* @param int quality 0..100 (for JPEG and PNG)
* @return bool TRUE on success or FALSE on failure.
*/
public function send($type = self::JPEG, $quality = NULL)
{
if ($type !== self::GIF && $type !== self::PNG && $type !== self::JPEG) {
throw new Nette\InvalidArgumentException('Unsupported image type \'$type\'.');
}
header('Content-Type: ' . image_type_to_mime_type($type));
return $this->save(NULL, $quality, $type);
}
/**
* Call to undefined method.
*
* @param string method name
* @param array arguments
* @return mixed
* @throws Nette\MemberAccessException
*/
public function __call($name, $args)
{
$function = 'image' . $name;
if (function_exists($function)) {
foreach ($args as $key => $value) {
if ($value instanceof self) {
$args[$key] = $value->getImageResource();
} elseif (is_array($value) && isset($value['red'])) { // rgb
$args[$key] = imagecolorallocatealpha(
$this->image,
$value['red'], $value['green'], $value['blue'], $value['alpha']
);
}
}
array_unshift($args, $this->image);
$res = call_user_func_array($function, $args);
return is_resource($res) && get_resource_type($res) === 'gd' ? $this->setImageResource($res) : $res;
}
return parent::__call($name, $args);
}
public function __clone()
{
ob_start();
imagegd2($this->image);
$this->setImageResource(imagecreatefromstring(ob_get_clean()));
}
}
src/Utils/Json.php 0000666 00000005651 13533704400 0010067 0 ustar 00 'The maximum stack depth has been exceeded',
JSON_ERROR_STATE_MISMATCH => 'Syntax error, malformed JSON',
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
5 /*JSON_ERROR_UTF8*/ => 'Invalid UTF-8 sequence', // exists since 5.3.3, but is returned since 5.3.1
);
/**
* Static class - cannot be instantiated.
*/
final public function __construct()
{
throw new Nette\StaticClassException;
}
/**
* Returns the JSON representation of a value.
* @param mixed
* @param int accepts Json::PRETTY
* @return string
*/
public static function encode($value, $options = 0)
{
$flags = PHP_VERSION_ID >= 50400 ? (JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | ($options & self::PRETTY ? JSON_PRETTY_PRINT : 0)) : 0;
if (PHP_VERSION_ID < 50500) {
$json = Callback::invokeSafe('json_encode', array($value, $flags), function ($message) { // needed to receive 'recursion detected' error
throw new JsonException($message);
});
} else {
$json = json_encode($value, $flags);
}
if ($error = json_last_error()) {
$message = isset(static::$messages[$error]) ? static::$messages[$error]
: (PHP_VERSION_ID >= 50500 ? json_last_error_msg() : 'Unknown error');
throw new JsonException($message, $error);
}
$json = str_replace(array("\xe2\x80\xa8", "\xe2\x80\xa9"), array('\u2028', '\u2029'), $json);
return $json;
}
/**
* Decodes a JSON string.
* @param string
* @param int accepts Json::FORCE_ARRAY
* @return mixed
*/
public static function decode($json, $options = 0)
{
$json = (string) $json;
if (!preg_match('##u', $json)) {
throw new JsonException('Invalid UTF-8 sequence', 5); // workaround for PHP < 5.3.3 & PECL JSON-C
}
$forceArray = (bool) ($options & self::FORCE_ARRAY);
if (!$forceArray && preg_match('#(?<=[^\\\\]")\\\\u0000(?:[^"\\\\]|\\\\.)*+"\s*+:#', $json)) { // workaround for json_decode fatal error when object key starts with \u0000
throw new JsonException(static::$messages[JSON_ERROR_CTRL_CHAR]);
}
$args = array($json, $forceArray, 512);
if (PHP_VERSION_ID >= 50400 && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) { // not implemented in PECL JSON-C 1.3.2 for 64bit systems
$args[] = JSON_BIGINT_AS_STRING;
}
$value = call_user_func_array('json_decode', $args);
if ($value === NULL && $json !== '' && strcasecmp($json, 'null')) { // '' is not clearing json_last_error
$error = json_last_error();
throw new JsonException(isset(static::$messages[$error]) ? static::$messages[$error] : 'Unknown error', $error);
}
return $value;
}
}
src/Utils/Object.php 0000666 00000010156 13533704400 0010360 0 ustar 00
* $val = $obj->label; // equivalent to $val = $obj->getLabel();
* $obj->label = 'Nette'; // equivalent to $obj->setLabel('Nette');
*
* Property names are case-sensitive, and they are written in the camelCaps
* or PascalCaps.
*
* Event functionality is provided by declaration of property named 'on{Something}'
* Multiple handlers are allowed.
*
* public $onClick; // declaration in class
* $this->onClick[] = 'callback'; // attaching event handler
* if (!empty($this->onClick)) ... // are there any handlers?
* $this->onClick($sender, $arg); // raises the event with arguments
*
*
* Adding method to class (i.e. to all instances) works similar to JavaScript
* prototype property. The syntax for adding a new method is:
*
* MyClass::extensionMethod('newMethod', function (MyClass $obj, $arg, ...) { ... });
* $obj = new MyClass;
* $obj->newMethod($x);
*
*
* @property-read Nette\Reflection\ClassType|\ReflectionClass $reflection
*/
abstract class Object
{
/**
* Access to reflection.
* @return Nette\Reflection\ClassType|\ReflectionClass
*/
public static function getReflection()
{
$class = class_exists('Nette\Reflection\ClassType') ? 'Nette\Reflection\ClassType' : 'ReflectionClass';
return new $class(get_called_class());
}
/**
* Call to undefined method.
* @param string method name
* @param array arguments
* @return mixed
* @throws MemberAccessException
*/
public function __call($name, $args)
{
return ObjectMixin::call($this, $name, $args);
}
/**
* Call to undefined static method.
* @param string method name (in lower case!)
* @param array arguments
* @return mixed
* @throws MemberAccessException
*/
public static function __callStatic($name, $args)
{
return ObjectMixin::callStatic(get_called_class(), $name, $args);
}
/**
* Adding method to class.
* @param string method name
* @param callable
* @return mixed
*/
public static function extensionMethod($name, $callback = NULL)
{
if (strpos($name, '::') === FALSE) {
$class = get_called_class();
} else {
list($class, $name) = explode('::', $name);
$rc = new \ReflectionClass($class);
$class = $rc->getName();
}
if ($callback === NULL) {
return ObjectMixin::getExtensionMethod($class, $name);
} else {
ObjectMixin::setExtensionMethod($class, $name, $callback);
}
}
/**
* Returns property value. Do not call directly.
* @param string property name
* @return mixed property value
* @throws MemberAccessException if the property is not defined.
*/
public function &__get($name)
{
return ObjectMixin::get($this, $name);
}
/**
* Sets value of a property. Do not call directly.
* @param string property name
* @param mixed property value
* @return void
* @throws MemberAccessException if the property is not defined or is read-only
*/
public function __set($name, $value)
{
ObjectMixin::set($this, $name, $value);
}
/**
* Is property defined?
* @param string property name
* @return bool
*/
public function __isset($name)
{
return ObjectMixin::has($this, $name);
}
/**
* Access to undeclared property.
* @param string property name
* @return void
* @throws MemberAccessException
*/
public function __unset($name)
{
ObjectMixin::remove($this, $name);
}
}
src/Utils/ObjectMixin.php 0000666 00000024203 13533704400 0011363 0 ustar 00 0 | bool | array) used by getMethods() */
private static $methods;
/** @var array (name => 'event' | TRUE) used by hasProperty() */
private static $props;
/** @var array (name => array(type => callback)) used by get|setExtensionMethod() */
private static $extMethods;
/**
* Static class - cannot be instantiated.
*/
final public function __construct()
{
throw new Nette\StaticClassException;
}
/**
* __call() implementation.
* @param object
* @param string
* @param array
* @return mixed
* @throws MemberAccessException
*/
public static function call($_this, $name, $args)
{
$class = get_class($_this);
$isProp = self::hasProperty($class, $name);
if ($name === '') {
throw new MemberAccessException("Call to class '$class' method without name.");
} elseif ($isProp && $_this->$name instanceof \Closure) { // closure in property
return call_user_func_array($_this->$name, $args);
} elseif ($isProp === 'event') { // calling event handlers
if (is_array($_this->$name) || $_this->$name instanceof \Traversable) {
foreach ($_this->$name as $handler) {
Callback::invokeArgs($handler, $args);
}
} elseif ($_this->$name !== NULL) {
throw new Nette\UnexpectedValueException("Property $class::$$name must be array or NULL, " . gettype($_this->$name) . ' given.');
}
} elseif (($methods = & self::getMethods($class)) && isset($methods[$name]) && is_array($methods[$name])) { // magic @methods
list($op, $rp, $type) = $methods[$name];
if (count($args) !== ($op === 'get' ? 0 : 1)) {
throw new Nette\InvalidArgumentException("$class::$name() expects " . ($op === 'get' ? 'no' : '1') . ' argument, ' . count($args) . ' given.');
} elseif ($type && $args && !self::checkType($args[0], $type)) {
throw new Nette\InvalidArgumentException("Argument passed to $class::$name() must be $type, " . gettype($args[0]) . ' given.');
}
if ($op === 'get') {
return $rp->getValue($_this);
} elseif ($op === 'set') {
$rp->setValue($_this, $args[0]);
} elseif ($op === 'add') {
$val = $rp->getValue($_this);
$val[] = $args[0];
$rp->setValue($_this, $val);
}
return $_this;
} elseif ($cb = self::getExtensionMethod($class, $name)) { // extension methods
array_unshift($args, $_this);
return Callback::invokeArgs($cb, $args);
} else {
if (method_exists($class, $name)) { // called parent::$name()
$class = 'parent';
}
throw new MemberAccessException("Call to undefined method $class::$name().");
}
}
/**
* __callStatic() implementation.
* @param string
* @param string
* @param array
* @return void
* @throws MemberAccessException
*/
public static function callStatic($class, $method, $args)
{
throw new MemberAccessException("Call to undefined static method $class::$method().");
}
/**
* __get() implementation.
* @param object
* @param string property name
* @return mixed property value
* @throws MemberAccessException if the property is not defined.
*/
public static function & get($_this, $name)
{
$class = get_class($_this);
$uname = ucfirst($name);
$methods = & self::getMethods($class);
if ($name === '') {
throw new MemberAccessException("Cannot read a class '$class' property without name.");
} elseif (isset($methods[$m = 'get' . $uname]) || isset($methods[$m = 'is' . $uname])) { // property getter
if ($methods[$m] === 0) {
$rm = new \ReflectionMethod($class, $m);
$methods[$m] = $rm->returnsReference();
}
if ($methods[$m] === TRUE) {
return $_this->$m();
} else {
$val = $_this->$m();
return $val;
}
} elseif (isset($methods[$name])) { // public method as closure getter
$val = Callback::closure($_this, $name);
return $val;
} else { // strict class
$type = isset($methods['set' . $uname]) ? 'a write-only' : 'an undeclared';
throw new MemberAccessException("Cannot read $type property $class::\$$name.");
}
}
/**
* __set() implementation.
* @param object
* @param string property name
* @param mixed property value
* @return void
* @throws MemberAccessException if the property is not defined or is read-only
*/
public static function set($_this, $name, $value)
{
$class = get_class($_this);
$uname = ucfirst($name);
$methods = & self::getMethods($class);
if ($name === '') {
throw new MemberAccessException("Cannot write to a class '$class' property without name.");
} elseif (self::hasProperty($class, $name)) { // unsetted property
$_this->$name = $value;
} elseif (isset($methods[$m = 'set' . $uname])) { // property setter
$_this->$m($value);
} else { // strict class
$type = isset($methods['get' . $uname]) || isset($methods['is' . $uname])
? 'a read-only' : 'an undeclared';
throw new MemberAccessException("Cannot write to $type property $class::\$$name.");
}
}
/**
* __unset() implementation.
* @param object
* @param string property name
* @return void
* @throws MemberAccessException
*/
public static function remove($_this, $name)
{
$class = get_class($_this);
if (!self::hasProperty($class, $name)) { // strict class
throw new MemberAccessException("Cannot unset the property $class::\$$name.");
}
}
/**
* __isset() implementation.
* @param object
* @param string property name
* @return bool
*/
public static function has($_this, $name)
{
$name = ucfirst($name);
$methods = & self::getMethods(get_class($_this));
return $name !== '' && (isset($methods['get' . $name]) || isset($methods['is' . $name]));
}
/**
* Checks if the public non-static property exists.
* @return mixed
*/
private static function hasProperty($class, $name)
{
$prop = & self::$props[$class][$name];
if ($prop === NULL) {
$prop = FALSE;
try {
$rp = new \ReflectionProperty($class, $name);
if ($rp->isPublic() && !$rp->isStatic()) {
$prop = $name >= 'onA' && $name < 'on_' ? 'event' : TRUE;
}
} catch (\ReflectionException $e) {
}
}
return $prop;
}
/**
* Returns array of public (static, non-static and magic) methods.
* @return array
*/
private static function & getMethods($class)
{
if (!isset(self::$methods[$class])) {
self::$methods[$class] = array_fill_keys(get_class_methods($class), 0) + self::getMagicMethods($class);
if ($parent = get_parent_class($class)) {
self::$methods[$class] += self::getMethods($parent);
}
}
return self::$methods[$class];
}
/**
* Returns array of magic methods defined by annotation @method.
* @return array
*/
public static function getMagicMethods($class)
{
$rc = new \ReflectionClass($class);
preg_match_all('~^
[ \t*]* @method [ \t]+
(?: [^\s(]+ [ \t]+ )?
(set|get|is|add) ([A-Z]\w*) [ \t]*
(?: \( [ \t]* ([^)$\s]+) )?
()~mx', $rc->getDocComment(), $matches, PREG_SET_ORDER);
$methods = array();
foreach ($matches as $m) {
list(, $op, $prop, $type) = $m;
$name = $op . $prop;
$prop = strtolower($prop[0]) . substr($prop, 1) . ($op === 'add' ? 's' : '');
if ($rc->hasProperty($prop) && ($rp = $rc->getProperty($prop)) && !$rp->isStatic()) {
$rp->setAccessible(TRUE);
if ($op === 'get' || $op === 'is') {
$type = NULL;
$op = 'get';
} elseif (!$type && preg_match('#@var[ \t]+(\S+)' . ($op === 'add' ? '\[\]#' : '#'), $rp->getDocComment(), $m)) {
$type = $m[1];
}
if ($rc->inNamespace() && preg_match('#^[A-Z]\w+(\[|\||\z)#', $type)) {
$type = $rc->getNamespaceName() . '\\' . $type;
}
$methods[$name] = array($op, $rp, $type);
}
}
return $methods;
}
/**
* Finds whether a variable is of expected type and do non-data-loss conversion.
* @return bool
* @internal
*/
public static function checkType(& $val, $type)
{
if (strpos($type, '|') !== FALSE) {
$found = NULL;
foreach (explode('|', $type) as $type) {
$tmp = $val;
if (self::checkType($tmp, $type)) {
if ($val === $tmp) {
return TRUE;
}
$found[] = $tmp;
}
}
if ($found) {
$val = $found[0];
return TRUE;
}
return FALSE;
} elseif (substr($type, -2) === '[]') {
if (!is_array($val)) {
return FALSE;
}
$type = substr($type, 0, -2);
$res = array();
foreach ($val as $k => $v) {
if (!self::checkType($v, $type)) {
return FALSE;
}
$res[$k] = $v;
}
$val = $res;
return TRUE;
}
switch (strtolower($type)) {
case NULL:
case 'mixed':
return TRUE;
case 'bool':
case 'boolean':
return ($val === NULL || is_scalar($val)) && settype($val, 'bool');
case 'string':
return ($val === NULL || is_scalar($val) || (is_object($val) && method_exists($val, '__toString'))) && settype($val, 'string');
case 'int':
case 'integer':
return ($val === NULL || is_bool($val) || is_numeric($val)) && ((float) (int) $val === (float) $val) && settype($val, 'int');
case 'float':
return ($val === NULL || is_bool($val) || is_numeric($val)) && settype($val, 'float');
case 'scalar':
case 'array':
case 'object':
case 'callable':
case 'resource':
case 'null':
return call_user_func("is_$type", $val);
default:
return $val instanceof $type;
}
}
/**
* Adds a method to class.
* @param string
* @param string
* @param mixed callable
* @return void
*/
public static function setExtensionMethod($class, $name, $callback)
{
$name = strtolower($name);
self::$extMethods[$name][$class] = Callback::check($callback);
self::$extMethods[$name][''] = NULL;
}
/**
* Returns extension method.
* @param string
* @param string
* @return mixed
*/
public static function getExtensionMethod($class, $name)
{
$list = & self::$extMethods[strtolower($name)];
$cache = & $list[''][$class];
if (isset($cache)) {
return $cache;
}
foreach (array($class) + class_parents($class) + class_implements($class) as $cl) {
if (isset($list[$cl])) {
return $cache = $list[$cl];
}
}
return $cache = FALSE;
}
}
src/Utils/Paginator.php 0000666 00000007664 13533704400 0011110 0 ustar 00 page = (int) $page;
return $this;
}
/**
* Returns current page number.
* @return int
*/
public function getPage()
{
return $this->base + $this->getPageIndex();
}
/**
* Returns first page number.
* @return int
*/
public function getFirstPage()
{
return $this->base;
}
/**
* Returns last page number.
* @return int|NULL
*/
public function getLastPage()
{
return $this->itemCount === NULL ? NULL : $this->base + max(0, $this->getPageCount() - 1);
}
/**
* Sets first page (base) number.
* @param int
* @return self
*/
public function setBase($base)
{
$this->base = (int) $base;
return $this;
}
/**
* Returns first page (base) number.
* @return int
*/
public function getBase()
{
return $this->base;
}
/**
* Returns zero-based page number.
* @return int
*/
protected function getPageIndex()
{
$index = max(0, $this->page - $this->base);
return $this->itemCount === NULL ? $index : min($index, max(0, $this->getPageCount() - 1));
}
/**
* Is the current page the first one?
* @return bool
*/
public function isFirst()
{
return $this->getPageIndex() === 0;
}
/**
* Is the current page the last one?
* @return bool
*/
public function isLast()
{
return $this->itemCount === NULL ? FALSE : $this->getPageIndex() >= $this->getPageCount() - 1;
}
/**
* Returns the total number of pages.
* @return int|NULL
*/
public function getPageCount()
{
return $this->itemCount === NULL ? NULL : (int) ceil($this->itemCount / $this->itemsPerPage);
}
/**
* Sets the number of items to display on a single page.
* @param int
* @return self
*/
public function setItemsPerPage($itemsPerPage)
{
$this->itemsPerPage = max(1, (int) $itemsPerPage);
return $this;
}
/**
* Returns the number of items to display on a single page.
* @return int
*/
public function getItemsPerPage()
{
return $this->itemsPerPage;
}
/**
* Sets the total number of items.
* @param int (or NULL as infinity)
* @return self
*/
public function setItemCount($itemCount)
{
$this->itemCount = ($itemCount === FALSE || $itemCount === NULL) ? NULL : max(0, (int) $itemCount);
return $this;
}
/**
* Returns the total number of items.
* @return int|NULL
*/
public function getItemCount()
{
return $this->itemCount;
}
/**
* Returns the absolute index of the first item on current page.
* @return int
*/
public function getOffset()
{
return $this->getPageIndex() * $this->itemsPerPage;
}
/**
* Returns the absolute index of the first item on current page in countdown paging.
* @return int|NULL
*/
public function getCountdownOffset()
{
return $this->itemCount === NULL
? NULL
: max(0, $this->itemCount - ($this->getPageIndex() + 1) * $this->itemsPerPage);
}
/**
* Returns the number of items on current page.
* @return int|NULL
*/
public function getLength()
{
return $this->itemCount === NULL
? $this->itemsPerPage
: min($this->itemsPerPage, $this->itemCount - $this->getPageIndex() * $this->itemsPerPage);
}
}
src/Utils/Random.php 0000666 00000003172 13533704400 0010372 0 ustar 00 = 50400 || !defined('PHP_WINDOWS_VERSION_BUILD')) // slow in PHP 5.3 & Windows
) {
$rand3 = openssl_random_pseudo_bytes($length);
}
if (empty($rand3) && function_exists('mcrypt_create_iv') && (PHP_VERSION_ID >= 50307 || !$windows)) { // PHP bug #52523
$rand3 = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
}
if (empty($rand3) && !$windows && @is_readable('/dev/urandom')) {
$rand3 = file_get_contents('/dev/urandom', FALSE, NULL, -1, $length);
}
if (empty($rand3)) {
static $cache;
$rand3 = $cache ?: $cache = md5(serialize($_SERVER), TRUE);
}
$s = '';
for ($i = 0; $i < $length; $i++) {
if ($i % 5 === 0) {
list($rand, $rand2) = explode(' ', microtime());
$rand += lcg_value();
}
$rand *= $chLen;
$s .= $charlist[($rand + $rand2 + ord($rand3[$i % strlen($rand3)])) % $chLen];
$rand -= (int) $rand;
}
return $s;
}
}
src/Utils/Strings.php 0000666 00000036052 13533704400 0010606 0 ustar 00 = 0xD800 && $code <= 0xDFFF) || $code > 0x10FFFF) {
throw new Nette\InvalidArgumentException('Code point must be in range 0x0 to 0xD7FF or 0xE000 to 0x10FFFF.');
}
return iconv('UTF-32BE', 'UTF-8//IGNORE', pack('N', $code));
}
/**
* Starts the $haystack string with the prefix $needle?
* @param string
* @param string
* @return bool
*/
public static function startsWith($haystack, $needle)
{
return strncmp($haystack, $needle, strlen($needle)) === 0;
}
/**
* Ends the $haystack string with the suffix $needle?
* @param string
* @param string
* @return bool
*/
public static function endsWith($haystack, $needle)
{
return strlen($needle) === 0 || substr($haystack, -strlen($needle)) === $needle;
}
/**
* Does $haystack contain $needle?
* @param string
* @param string
* @return bool
*/
public static function contains($haystack, $needle)
{
return strpos($haystack, $needle) !== FALSE;
}
/**
* Returns a part of UTF-8 string.
* @param string
* @param int in characters (code points)
* @param int in characters (code points)
* @return string
*/
public static function substring($s, $start, $length = NULL)
{
if (function_exists('mb_substr')) {
if ($length === NULL && PHP_VERSION_ID < 50408) {
$length = self::length($s);
}
return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster
} elseif ($length === NULL) {
$length = self::length($s);
} elseif ($start < 0 && $length < 0) {
$start += self::length($s); // unifies iconv_substr behavior with mb_substr
}
return iconv_substr($s, $start, $length, 'UTF-8');
}
/**
* Removes special controls characters and normalizes line endings and spaces.
* @param string UTF-8 encoding
* @return string
*/
public static function normalize($s)
{
$s = self::normalizeNewLines($s);
// remove control characters; leave \t + \n
$s = preg_replace('#[\x00-\x08\x0B-\x1F\x7F-\x9F]+#u', '', $s);
// right trim
$s = preg_replace('#[\t ]+$#m', '', $s);
// leading and trailing blank lines
$s = trim($s, "\n");
return $s;
}
/**
* Standardize line endings to unix-like.
* @param string UTF-8 encoding or 8-bit
* @return string
*/
public static function normalizeNewLines($s)
{
return str_replace(array("\r\n", "\r"), "\n", $s);
}
/**
* Converts to ASCII.
* @param string UTF-8 encoding
* @return string ASCII
*/
public static function toAscii($s)
{
$s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
$s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
$s = str_replace(
array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
);
if (class_exists('Transliterator') && $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII')) {
$s = $transliterator->transliterate($s);
}
if (ICONV_IMPL === 'glibc') {
$s = str_replace(
array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
);
$s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
$s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
. "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
. "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
. "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
. "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
$s = preg_replace('#[^\x00-\x7F]++#', '', $s);
} else {
$s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
}
$s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}
/**
* Converts to web safe characters [a-z0-9-] text.
* @param string UTF-8 encoding
* @param string allowed characters
* @param bool
* @return string
*/
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
$s = self::toAscii($s);
if ($lower) {
$s = strtolower($s);
}
$s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
$s = trim($s, '-');
return $s;
}
/**
* Truncates string to maximal length.
* @param string UTF-8 encoding
* @param int
* @param string UTF-8 encoding
* @return string
*/
public static function truncate($s, $maxLen, $append = "\xE2\x80\xA6")
{
if (self::length($s) > $maxLen) {
$maxLen = $maxLen - self::length($append);
if ($maxLen < 1) {
return $append;
} elseif ($matches = self::match($s, '#^.{1,'.$maxLen.'}(?=[\s\x00-/:-@\[-`{-~])#us')) {
return $matches[0] . $append;
} else {
return self::substring($s, 0, $maxLen) . $append;
}
}
return $s;
}
/**
* Indents the content from the left.
* @param string UTF-8 encoding or 8-bit
* @param int
* @param string
* @return string
*/
public static function indent($s, $level = 1, $chars = "\t")
{
if ($level > 0) {
$s = self::replace($s, '#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level));
}
return $s;
}
/**
* Convert to lower case.
* @param string UTF-8 encoding
* @return string
*/
public static function lower($s)
{
return mb_strtolower($s, 'UTF-8');
}
/**
* Convert first character to lower case.
* @param string UTF-8 encoding
* @return string
*/
public static function firstLower($s)
{
return self::lower(self::substring($s, 0, 1)) . self::substring($s, 1);
}
/**
* Convert to upper case.
* @param string UTF-8 encoding
* @return string
*/
public static function upper($s)
{
return mb_strtoupper($s, 'UTF-8');
}
/**
* Convert first character to upper case.
* @param string UTF-8 encoding
* @return string
*/
public static function firstUpper($s)
{
return self::upper(self::substring($s, 0, 1)) . self::substring($s, 1);
}
/**
* Capitalize string.
* @param string UTF-8 encoding
* @return string
*/
public static function capitalize($s)
{
return mb_convert_case($s, MB_CASE_TITLE, 'UTF-8');
}
/**
* Case-insensitive compares UTF-8 strings.
* @param string
* @param string
* @param int
* @return bool
*/
public static function compare($left, $right, $len = NULL)
{
if ($len < 0) {
$left = self::substring($left, $len, -$len);
$right = self::substring($right, $len, -$len);
} elseif ($len !== NULL) {
$left = self::substring($left, 0, $len);
$right = self::substring($right, 0, $len);
}
return self::lower($left) === self::lower($right);
}
/**
* Finds the length of common prefix of strings.
* @param string|array
* @return string
*/
public static function findPrefix($strings)
{
if (!is_array($strings)) {
$strings = func_get_args();
}
$first = array_shift($strings);
for ($i = 0; $i < strlen($first); $i++) {
foreach ($strings as $s) {
if (!isset($s[$i]) || $first[$i] !== $s[$i]) {
while ($i && $first[$i - 1] >= "\x80" && $first[$i] >= "\x80" && $first[$i] < "\xC0") {
$i--;
}
return substr($first, 0, $i);
}
}
}
return $first;
}
/**
* Returns number of characters (not bytes) in UTF-8 string.
* That is the number of Unicode code points which may differ from the number of graphemes.
* @param string
* @return int
*/
public static function length($s)
{
return function_exists('mb_strlen') ? mb_strlen($s, 'UTF-8') : strlen(utf8_decode($s));
}
/**
* Strips whitespace.
* @param string UTF-8 encoding
* @param string
* @return string
*/
public static function trim($s, $charlist = " \t\n\r\0\x0B\xC2\xA0")
{
$charlist = preg_quote($charlist, '#');
return self::replace($s, '#^['.$charlist.']+|['.$charlist.']+\z#u', '');
}
/**
* Pad a string to a certain length with another string.
* @param string UTF-8 encoding
* @param int
* @param string
* @return string
*/
public static function padLeft($s, $length, $pad = ' ')
{
$length = max(0, $length - self::length($s));
$padLen = self::length($pad);
return str_repeat($pad, $length / $padLen) . self::substring($pad, 0, $length % $padLen) . $s;
}
/**
* Pad a string to a certain length with another string.
* @param string UTF-8 encoding
* @param int
* @param string
* @return string
*/
public static function padRight($s, $length, $pad = ' ')
{
$length = max(0, $length - self::length($s));
$padLen = self::length($pad);
return $s . str_repeat($pad, $length / $padLen) . self::substring($pad, 0, $length % $padLen);
}
/**
* Reverse string.
* @param string UTF-8 encoding
* @return string
*/
public static function reverse($s)
{
return @iconv('UTF-32LE', 'UTF-8', strrev(@iconv('UTF-8', 'UTF-32BE', $s)));
}
/**
* Use Nette\Utils\Random::generate
* @deprecated
*/
public static function random($length = 10, $charlist = '0-9a-z')
{
return Random::generate($length, $charlist);
}
/**
* Returns part of $haystack before $nth occurence of $needle.
* @param string
* @param string
* @param int negative value means searching from the end
* @return string|FALSE returns FALSE if the needle was not found
*/
public static function before($haystack, $needle, $nth = 1)
{
$pos = self::pos($haystack, $needle, $nth);
return $pos === FALSE
? FALSE
: substr($haystack, 0, $pos);
}
/**
* Returns part of $haystack after $nth occurence of $needle.
* @param string
* @param string
* @param int negative value means searching from the end
* @return string|FALSE returns FALSE if the needle was not found
*/
public static function after($haystack, $needle, $nth = 1)
{
$pos = self::pos($haystack, $needle, $nth);
return $pos === FALSE
? FALSE
: (string) substr($haystack, $pos + strlen($needle));
}
/**
* Returns position of $nth occurence of $needle in $haystack.
* @return int|FALSE offset in bytes or FALSE if the needle was not found
*/
private static function pos($haystack, $needle, $nth = 1)
{
if (!$nth) {
return FALSE;
} elseif ($nth > 0) {
if (strlen($needle) === 0) {
return 0;
}
$pos = 0;
while (FALSE !== ($pos = strpos($haystack, $needle, $pos)) && --$nth) {
$pos++;
}
} else {
$len = strlen($haystack);
if (strlen($needle) === 0) {
return $len;
}
$pos = $len - 1;
while (FALSE !== ($pos = strrpos($haystack, $needle, $pos - $len)) && ++$nth) {
$pos--;
}
}
return $pos;
}
/**
* Splits string by a regular expression.
* @param string
* @param string
* @param int
* @return array
*/
public static function split($subject, $pattern, $flags = 0)
{
return self::pcre('preg_split', array($pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE));
}
/**
* Performs a regular expression match.
* @param string
* @param string
* @param int can be PREG_OFFSET_CAPTURE (returned in bytes)
* @param int offset in bytes
* @return mixed
*/
public static function match($subject, $pattern, $flags = 0, $offset = 0)
{
if ($offset > strlen($subject)) {
return NULL;
}
return self::pcre('preg_match', array($pattern, $subject, & $m, $flags, $offset))
? $m
: NULL;
}
/**
* Performs a global regular expression match.
* @param string
* @param string
* @param int can be PREG_OFFSET_CAPTURE (returned in bytes); PREG_SET_ORDER is default
* @param int offset in bytes
* @return array
*/
public static function matchAll($subject, $pattern, $flags = 0, $offset = 0)
{
if ($offset > strlen($subject)) {
return array();
}
self::pcre('preg_match_all', array(
$pattern, $subject, & $m,
($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER),
$offset,
));
return $m;
}
/**
* Perform a regular expression search and replace.
* @param string
* @param string|array
* @param string|callable
* @param int
* @return string
*/
public static function replace($subject, $pattern, $replacement = NULL, $limit = -1)
{
if (is_object($replacement) || is_array($replacement)) {
if ($replacement instanceof Nette\Callback) {
$replacement = $replacement->getNative();
}
if (!is_callable($replacement, FALSE, $textual)) {
throw new Nette\InvalidStateException("Callback '$textual' is not callable.");
}
return self::pcre('preg_replace_callback', array($pattern, $replacement, $subject, $limit));
} elseif ($replacement === NULL && is_array($pattern)) {
$replacement = array_values($pattern);
$pattern = array_keys($pattern);
}
return self::pcre('preg_replace', array($pattern, $replacement, $subject, $limit));
}
/** @internal */
public static function pcre($func, $args)
{
static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
);
$res = Callback::invokeSafe($func, $args, function ($message) use ($args) {
// compile-time error, not detectable by preg_last_error
throw new RegexpException($message . ' in pattern: ' . implode(' or ', (array) $args[0]));
});
if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars
&& ($res === NULL || !in_array($func, array('preg_filter', 'preg_replace_callback', 'preg_replace')))
) {
throw new RegexpException((isset($messages[$code]) ? $messages[$code] : 'Unknown error')
. ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code);
}
return $res;
}
}
src/Utils/Validators.php 0000666 00000016663 13533704400 0011273 0 ustar 00 'is_bool',
'boolean' => 'is_bool',
'int' => 'is_int',
'integer' => 'is_int',
'float' => 'is_float',
'number' => NULL, // is_int || is_float,
'numeric' => array(__CLASS__, 'isNumeric'),
'numericint' => array(__CLASS__, 'isNumericInt'),
'string' => 'is_string',
'unicode' => array(__CLASS__, 'isUnicode'),
'array' => 'is_array',
'list' => array('Nette\Utils\Arrays', 'isList'),
'object' => 'is_object',
'resource' => 'is_resource',
'scalar' => 'is_scalar',
'callable' => array(__CLASS__, 'isCallable'),
'null' => 'is_null',
'email' => array(__CLASS__, 'isEmail'),
'url' => array(__CLASS__, 'isUrl'),
'uri' => array(__CLASS__, 'isUri'),
'none' => array(__CLASS__, 'isNone'),
'type' => array(__CLASS__, 'isType'),
'identifier' => array(__CLASS__, 'isPhpIdentifier'),
'pattern' => NULL,
'alnum' => 'ctype_alnum',
'alpha' => 'ctype_alpha',
'digit' => 'ctype_digit',
'lower' => 'ctype_lower',
'upper' => 'ctype_upper',
'space' => 'ctype_space',
'xdigit' => 'ctype_xdigit',
);
protected static $counters = array(
'string' => 'strlen',
'unicode' => array('Nette\Utils\Strings', 'length'),
'array' => 'count',
'list' => 'count',
'alnum' => 'strlen',
'alpha' => 'strlen',
'digit' => 'strlen',
'lower' => 'strlen',
'space' => 'strlen',
'upper' => 'strlen',
'xdigit' => 'strlen',
);
/**
* Throws exception if a variable is of unexpected type.
* @param mixed
* @param string expected types separated by pipe
* @param string label
* @return void
*/
public static function assert($value, $expected, $label = 'variable')
{
if (!static::is($value, $expected)) {
$expected = str_replace(array('|', ':'), array(' or ', ' in range '), $expected);
if (is_array($value)) {
$type = 'array(' . count($value) . ')';
} elseif (is_object($value)) {
$type = 'object ' . get_class($value);
} elseif (is_string($value) && strlen($value) < 40) {
$type = "string '$value'";
} else {
$type = gettype($value);
}
throw new AssertionException("The $label expects to be $expected, $type given.");
}
}
/**
* Throws exception if an array field is missing or of unexpected type.
* @param array
* @param string item
* @param string expected types separated by pipe
* @param string
* @return void
*/
public static function assertField($arr, $field, $expected = NULL, $label = "item '%' in array")
{
self::assert($arr, 'array', 'first argument');
if (!array_key_exists($field, $arr)) {
throw new AssertionException('Missing ' . str_replace('%', $field, $label) . '.');
} elseif ($expected) {
static::assert($arr[$field], $expected, str_replace('%', $field, $label));
}
}
/**
* Finds whether a variable is of expected type.
* @param mixed
* @param string expected types separated by pipe with optional ranges
* @return bool
*/
public static function is($value, $expected)
{
foreach (explode('|', $expected) as $item) {
list($type) = $item = explode(':', $item, 2);
if (isset(static::$validators[$type])) {
if (!call_user_func(static::$validators[$type], $value)) {
continue;
}
} elseif ($type === 'number') {
if (!is_int($value) && !is_float($value)) {
continue;
}
} elseif ($type === 'pattern') {
if (preg_match('|^' . (isset($item[1]) ? $item[1] : '') . '\z|', $value)) {
return TRUE;
}
continue;
} elseif (!$value instanceof $type) {
continue;
}
if (isset($item[1])) {
if (isset(static::$counters[$type])) {
$value = call_user_func(static::$counters[$type], $value);
}
$range = explode('..', $item[1]);
if (!isset($range[1])) {
$range[1] = $range[0];
}
if (($range[0] !== '' && $value < $range[0]) || ($range[1] !== '' && $value > $range[1])) {
continue;
}
}
return TRUE;
}
return FALSE;
}
/**
* Finds whether a value is an integer.
* @return bool
*/
public static function isNumericInt($value)
{
return is_int($value) || is_string($value) && preg_match('#^-?[0-9]+\z#', $value);
}
/**
* Finds whether a string is a floating point number in decimal base.
* @return bool
*/
public static function isNumeric($value)
{
return is_float($value) || is_int($value) || is_string($value) && preg_match('#^-?[0-9]*[.]?[0-9]+\z#', $value);
}
/**
* Finds whether a value is a syntactically correct callback.
* @return bool
*/
public static function isCallable($value)
{
return $value && is_callable($value, TRUE);
}
/**
* Finds whether a value is an UTF-8 encoded string.
* @param string
* @return bool
*/
public static function isUnicode($value)
{
return is_string($value) && preg_match('##u', $value);
}
/**
* Finds whether a value is "falsy".
* @return bool
*/
public static function isNone($value)
{
return $value == NULL; // intentionally ==
}
/**
* Finds whether a variable is a zero-based integer indexed array.
* @param array
* @return bool
*/
public static function isList($value)
{
return Arrays::isList($value);
}
/**
* Is a value in specified range?
* @param mixed
* @param array min and max value pair
* @return bool
*/
public static function isInRange($value, $range)
{
return (!isset($range[0]) || $range[0] === '' || $value >= $range[0])
&& (!isset($range[1]) || $range[1] === '' || $value <= $range[1]);
}
/**
* Finds whether a string is a valid email address.
* @param string
* @return bool
*/
public static function isEmail($value)
{
$atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part
$localPart = "(?:\"(?:[ !\\x23-\\x5B\\x5D-\\x7E]*|\\\\[ -~])+\"|$atom+(?:\\.$atom+)*)"; // quoted or unquoted
$alpha = "a-z\x80-\xFF"; // superset of IDN
$domain = "[0-9$alpha](?:[-0-9$alpha]{0,61}[0-9$alpha])?"; // RFC 1034 one domain component
$topDomain = "[$alpha](?:[-0-9$alpha]{0,17}[$alpha])?";
return (bool) preg_match("(^$localPart@(?:$domain\\.)+$topDomain\\z)i", $value);
}
/**
* Finds whether a string is a valid http(s) URL.
* @param string
* @return bool
*/
public static function isUrl($value)
{
$alpha = "a-z\x80-\xFF";
$subDomain = "[-_0-9$alpha]";
$domain = "[0-9$alpha](?:[-0-9$alpha]{0,61}[0-9$alpha])?";
$topDomain = "[$alpha](?:[-0-9$alpha]{0,17}[$alpha])?";
$domainName = "(?:(?:$subDomain+\\.)*?$domain\\.)?$topDomain";
return (bool) preg_match("(^https?://(?:$domainName|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\[[0-9a-f:]{3,39}\])(:\\d{1,5})?(/\\S*)?\\z)i", $value);
}
/**
* Finds whether a string is a valid URI according to RFC 1738.
* @param string
* @return bool
*/
public static function isUri($value)
{
return (bool) preg_match('#^[a-z\d+\.-]+:\S+\z#i', $value);
}
/**
* Checks whether the input is a class, interface or trait.
* @param string
* @return bool
*/
public static function isType($type)
{
return class_exists($type) || interface_exists($type) || (PHP_VERSION_ID >= 50400 && trait_exists($type));
}
/**
* Checks whether the input is a valid PHP identifier.
* @return bool
*/
public static function isPhpIdentifier($value)
{
return is_string($value) && preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*\z#', $value);
}
}
src/Utils/exceptions.php 0000666 00000005634 13533704400 0011340 0 ustar 00