New contact form with friendlycaptcha
This commit is contained in:
10
vendor/nette/utils/.phpstorm.meta.php
vendored
10
vendor/nette/utils/.phpstorm.meta.php
vendored
@@ -9,11 +9,5 @@ override(\Nette\Utils\Arrays::getRef(0), elementType(0));
|
||||
override(\Nette\Utils\Arrays::grep(0), type(0));
|
||||
override(\Nette\Utils\Arrays::toObject(0), type(1));
|
||||
|
||||
expectedArguments(\Nette\Utils\Arrays::grep(), 2, PREG_GREP_INVERT);
|
||||
expectedArguments(\Nette\Utils\Image::resize(), 2, \Nette\Utils\Image::SHRINK_ONLY, \Nette\Utils\Image::STRETCH, \Nette\Utils\Image::FIT, \Nette\Utils\Image::FILL, \Nette\Utils\Image::EXACT);
|
||||
expectedArguments(\Nette\Utils\Image::calculateSize(), 4, \Nette\Utils\Image::SHRINK_ONLY, \Nette\Utils\Image::STRETCH, \Nette\Utils\Image::FIT, \Nette\Utils\Image::FILL, \Nette\Utils\Image::EXACT);
|
||||
expectedArguments(\Nette\Utils\Json::encode(), 1, \Nette\Utils\Json::PRETTY);
|
||||
expectedArguments(\Nette\Utils\Json::decode(), 1, \Nette\Utils\Json::FORCE_ARRAY);
|
||||
expectedArguments(\Nette\Utils\Strings::split(), 2, \PREG_SPLIT_NO_EMPTY | \PREG_OFFSET_CAPTURE);
|
||||
expectedArguments(\Nette\Utils\Strings::match(), 2, \PREG_OFFSET_CAPTURE | \PREG_UNMATCHED_AS_NULL);
|
||||
expectedArguments(\Nette\Utils\Strings::matchAll(), 2, \PREG_OFFSET_CAPTURE | \PREG_UNMATCHED_AS_NULL | \PREG_PATTERN_ORDER);
|
||||
expectedArguments(\Nette\Utils\Image::resize(), 2, \Nette\Utils\Image::ShrinkOnly, \Nette\Utils\Image::Stretch, \Nette\Utils\Image::OrSmaller, \Nette\Utils\Image::OrBigger, \Nette\Utils\Image::Cover);
|
||||
expectedArguments(\Nette\Utils\Image::calculateSize(), 4, \Nette\Utils\Image::ShrinkOnly, \Nette\Utils\Image::Stretch, \Nette\Utils\Image::OrSmaller, \Nette\Utils\Image::OrBigger, \Nette\Utils\Image::Cover);
|
||||
|
||||
20
vendor/nette/utils/composer.json
vendored
20
vendor/nette/utils/composer.json
vendored
@@ -15,27 +15,31 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2 <8.3"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
"tracy/tracy": "^2.3",
|
||||
"phpstan/phpstan": "^1.0"
|
||||
"nette/tester": "^2.5",
|
||||
"tracy/tracy": "^2.9",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"jetbrains/phpstorm-attributes": "^1.2"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/di": "<3.0.6"
|
||||
"nette/finder": "<3",
|
||||
"nette/schema": "<1.2.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
|
||||
"ext-json": "to use Nette\\Utils\\Json",
|
||||
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
|
||||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
"ext-xml": "to use Strings::length() etc. when mbstring is not available",
|
||||
"ext-gd": "to use Image",
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
"classmap": ["src/"],
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"scripts": {
|
||||
@@ -44,7 +48,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
vendor/nette/utils/contributing.md
vendored
33
vendor/nette/utils/contributing.md
vendored
@@ -1,33 +0,0 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
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.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
14
vendor/nette/utils/ncs.php
vendored
14
vendor/nette/utils/ncs.php
vendored
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Rules for Nette Coding Standard
|
||||
* https://github.com/nette/coding-standard
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
// use function in Arrays.php, Callback.php, Html.php, Strings.php
|
||||
'single_import_per_statement' => false,
|
||||
'ordered_imports' => false,
|
||||
];
|
||||
18
vendor/nette/utils/ncs.xml
vendored
18
vendor/nette/utils/ncs.xml
vendored
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Custom" namespace="Nette">
|
||||
<rule ref="$presets/php72.xml"/>
|
||||
|
||||
<!-- bug in SlevomatCodingStandard -->
|
||||
<rule ref="SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
|
||||
<!-- bug in FunctionSpacingSniff -->
|
||||
<exclude-pattern>./tests/Utils/Reflection.getDeclaringMethod.alias.phpt</exclude-pattern>
|
||||
<exclude-pattern>./tests/Utils/Reflection.getDeclaringMethod.insteadof.phpt</exclude-pattern>
|
||||
|
||||
<!-- use function in Arrays.php, Callback.php, Html.php, Strings.php -->
|
||||
<rule ref="SlevomatCodingStandard.Namespaces.MultipleUsesPerLine.MultipleUsesPerLine">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
</ruleset>
|
||||
45
vendor/nette/utils/readme.md
vendored
45
vendor/nette/utils/readme.md
vendored
@@ -1,5 +1,4 @@
|
||||
Nette Utility Classes
|
||||
=====================
|
||||
[](https://doc.nette.org/en/utils)
|
||||
|
||||
[](https://packagist.org/packages/nette/utils)
|
||||
[](https://github.com/nette/utils/actions)
|
||||
@@ -11,24 +10,27 @@ Nette Utility Classes
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In package nette/utils you will find a set of [useful classes](https://doc.nette.org/utils) for everyday use:
|
||||
In package nette/utils you will find a set of useful classes for everyday use:
|
||||
|
||||
- [Arrays](https://doc.nette.org/arrays) - manipulate arrays
|
||||
- [Callback](https://doc.nette.org/callback) - PHP callbacks
|
||||
- [Date and Time](https://doc.nette.org/datetime) - modify times and dates
|
||||
- [Filesystem](https://doc.nette.org/filesystem) - copying, renaming, …
|
||||
- [Helper Functions](https://doc.nette.org/helpers)
|
||||
- [HTML elements](https://doc.nette.org/html-elements) - generate HTML
|
||||
- [Images](https://doc.nette.org/images) - crop, resize, rotate images
|
||||
- [JSON](https://doc.nette.org/json) - encoding and decoding
|
||||
- [Generating Random Strings](https://doc.nette.org/random)
|
||||
- [Paginator](https://doc.nette.org/paginator) - pagination math
|
||||
- [PHP Reflection](https://doc.nette.org/reflection)
|
||||
- [Strings](https://doc.nette.org/strings) - useful text functions
|
||||
- [SmartObject](https://doc.nette.org/smartobject) - PHP object enhancements
|
||||
- [Validation](https://doc.nette.org/validators) - validate inputs
|
||||
- [Type](https://doc.nette.org/type) - PHP data type
|
||||
✅ [Arrays](https://doc.nette.org/utils/arrays)<br>
|
||||
✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks<br>
|
||||
✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …<br>
|
||||
✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories<br>
|
||||
✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers<br>
|
||||
✅ [Helper Functions](https://doc.nette.org/utils/helpers)<br>
|
||||
✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML<br>
|
||||
✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images<br>
|
||||
✅ [Iterables](https://doc.nette.org/utils/iterables) <br>
|
||||
✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding<br>
|
||||
✅ [Generating Random Strings](https://doc.nette.org/utils/random)<br>
|
||||
✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math<br>
|
||||
✅ [PHP Reflection](https://doc.nette.org/utils/reflection)<br>
|
||||
✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions<br>
|
||||
✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements<br>
|
||||
✅ [Type](https://doc.nette.org/utils/type) - PHP data type<br>
|
||||
✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs<br>
|
||||
|
||||
<!---->
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -39,10 +41,9 @@ The recommended way to install is via Composer:
|
||||
composer require nette/utils
|
||||
```
|
||||
|
||||
- Nette Utils 3.2 is compatible with PHP 7.2 to 8.2
|
||||
- Nette Utils 3.1 is compatible with PHP 7.1 to 8.0
|
||||
- Nette Utils 3.0 is compatible with PHP 7.1 to 8.0
|
||||
- Nette Utils 2.5 is compatible with PHP 5.6 to 8.0
|
||||
Nette Utils 4.1 is compatible with PHP 8.2 to 8.5.
|
||||
|
||||
<!---->
|
||||
|
||||
[Support Me](https://github.com/sponsors/dg)
|
||||
--------------------------------------------
|
||||
|
||||
@@ -28,30 +28,15 @@ class CachingIterator extends \CachingIterator implements \Countable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var int */
|
||||
private $counter = 0;
|
||||
private int $counter = 0;
|
||||
|
||||
|
||||
public function __construct($iterator)
|
||||
public function __construct(iterable|\stdClass $iterable)
|
||||
{
|
||||
if (is_array($iterator) || $iterator instanceof \stdClass) {
|
||||
$iterator = new \ArrayIterator($iterator);
|
||||
|
||||
} elseif ($iterator instanceof \IteratorAggregate) {
|
||||
do {
|
||||
$iterator = $iterator->getIterator();
|
||||
} while ($iterator instanceof \IteratorAggregate);
|
||||
|
||||
assert($iterator instanceof \Iterator);
|
||||
|
||||
} elseif ($iterator instanceof \Iterator) {
|
||||
} elseif ($iterator instanceof \Traversable) {
|
||||
$iterator = new \IteratorIterator($iterator);
|
||||
} else {
|
||||
throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, is_object($iterator) ? get_class($iterator) : gettype($iterator)));
|
||||
}
|
||||
|
||||
parent::__construct($iterator, 0);
|
||||
$iterable = $iterable instanceof \stdClass
|
||||
? new \ArrayIterator((array) $iterable)
|
||||
: Nette\Utils\Iterables::toIterator($iterable);
|
||||
parent::__construct($iterable, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,9 +133,8 @@ class CachingIterator extends \CachingIterator implements \Countable
|
||||
|
||||
/**
|
||||
* Returns the next key.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextKey()
|
||||
public function getNextKey(): mixed
|
||||
{
|
||||
return $this->getInnerIterator()->key();
|
||||
}
|
||||
@@ -158,9 +142,8 @@ class CachingIterator extends \CachingIterator implements \Countable
|
||||
|
||||
/**
|
||||
* Returns the next element.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextValue()
|
||||
public function getNextValue(): mixed
|
||||
{
|
||||
return $this->getInnerIterator()->current();
|
||||
}
|
||||
|
||||
6
vendor/nette/utils/src/Iterators/Mapper.php
vendored
6
vendor/nette/utils/src/Iterators/Mapper.php
vendored
@@ -10,9 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Iterators;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Applies the callback to the elements of the inner iterator.
|
||||
* @deprecated use Nette\Utils\Iterables::map()
|
||||
*/
|
||||
class Mapper extends \IteratorIterator
|
||||
{
|
||||
@@ -27,8 +26,7 @@ class Mapper extends \IteratorIterator
|
||||
}
|
||||
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
public function current(): mixed
|
||||
{
|
||||
return ($this->callback)(parent::current(), parent::key());
|
||||
}
|
||||
|
||||
16
vendor/nette/utils/src/SmartObject.php
vendored
16
vendor/nette/utils/src/SmartObject.php
vendored
@@ -22,6 +22,7 @@ use Nette\Utils\ObjectHelpers;
|
||||
trait SmartObject
|
||||
{
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
@@ -35,11 +36,13 @@ trait SmartObject
|
||||
$handler(...$args);
|
||||
}
|
||||
} elseif ($handlers !== null) {
|
||||
throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . gettype($handlers) . ' given.');
|
||||
throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . get_debug_type($handlers) . ' given.');
|
||||
}
|
||||
} else {
|
||||
ObjectHelpers::strictCall($class, $name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ObjectHelpers::strictCall($class, $name);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,11 +90,9 @@ trait SmartObject
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws MemberAccessException if the property is not defined or is read-only
|
||||
*/
|
||||
public function __set(string $name, $value)
|
||||
public function __set(string $name, mixed $value): void
|
||||
{
|
||||
$class = static::class;
|
||||
|
||||
@@ -121,10 +122,9 @@ trait SmartObject
|
||||
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public function __unset(string $name)
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
$class = static::class;
|
||||
if (!ObjectHelpers::hasProperty($class, $name)) {
|
||||
|
||||
17
vendor/nette/utils/src/StaticClass.php
vendored
17
vendor/nette/utils/src/StaticClass.php
vendored
@@ -16,22 +16,9 @@ namespace Nette;
|
||||
trait StaticClass
|
||||
{
|
||||
/**
|
||||
* @return never
|
||||
* @throws \Error
|
||||
* Class is static and cannot be instantiated.
|
||||
*/
|
||||
final public function __construct()
|
||||
private function __construct()
|
||||
{
|
||||
throw new \Error('Class ' . static::class . ' is static and cannot be instantiated.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to undefined static method.
|
||||
* @return void
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public static function __callStatic(string $name, array $args)
|
||||
{
|
||||
Utils\ObjectHelpers::strictStaticCall(static::class, $name);
|
||||
}
|
||||
}
|
||||
|
||||
4
vendor/nette/utils/src/Translator.php
vendored
4
vendor/nette/utils/src/Translator.php
vendored
@@ -17,10 +17,8 @@ interface Translator
|
||||
{
|
||||
/**
|
||||
* Translates the given string.
|
||||
* @param mixed $message
|
||||
* @param mixed ...$parameters
|
||||
*/
|
||||
function translate($message, ...$parameters): string;
|
||||
function translate(string|\Stringable $message, mixed ...$parameters): string|\Stringable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
35
vendor/nette/utils/src/Utils/ArrayHash.php
vendored
35
vendor/nette/utils/src/Utils/ArrayHash.php
vendored
@@ -10,25 +10,27 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function count, is_array, is_scalar, sprintf;
|
||||
|
||||
|
||||
/**
|
||||
* Provides objects to work as array.
|
||||
* @template T
|
||||
* @implements \IteratorAggregate<array-key, T>
|
||||
* @implements \ArrayAccess<array-key, T>
|
||||
*/
|
||||
class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Transforms array to ArrayHash.
|
||||
* @param array<T> $array
|
||||
* @return static
|
||||
*/
|
||||
public static function from(array $array, bool $recursive = true)
|
||||
public static function from(array $array, bool $recursive = true): static
|
||||
{
|
||||
$obj = new static;
|
||||
foreach ($array as $key => $value) {
|
||||
$obj->$key = $recursive && is_array($value)
|
||||
? static::from($value, true)
|
||||
? static::from($value)
|
||||
: $value;
|
||||
}
|
||||
|
||||
@@ -38,11 +40,13 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
/**
|
||||
* Returns an iterator over all items.
|
||||
* @return \RecursiveArrayIterator<array-key, T>
|
||||
* @return \Iterator<array-key, T>
|
||||
*/
|
||||
public function getIterator(): \RecursiveArrayIterator
|
||||
public function &getIterator(): \Iterator
|
||||
{
|
||||
return new \RecursiveArrayIterator((array) $this);
|
||||
foreach ((array) $this as $key => $foo) {
|
||||
yield $key => $this->$key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,14 +60,14 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
|
||||
/**
|
||||
* Replaces or appends a item.
|
||||
* @param string|int $key
|
||||
* Replaces or appends an item.
|
||||
* @param array-key $key
|
||||
* @param T $value
|
||||
*/
|
||||
public function offsetSet($key, $value): void
|
||||
{
|
||||
if (!is_scalar($key)) { // prevents null
|
||||
throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', gettype($key)));
|
||||
throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', get_debug_type($key)));
|
||||
}
|
||||
|
||||
$this->$key = $value;
|
||||
@@ -71,20 +75,19 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
|
||||
/**
|
||||
* Returns a item.
|
||||
* @param string|int $key
|
||||
* Returns an item.
|
||||
* @param array-key $key
|
||||
* @return T
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
public function offsetGet($key): mixed
|
||||
{
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a item exists.
|
||||
* @param string|int $key
|
||||
* Determines whether an item exists.
|
||||
* @param array-key $key
|
||||
*/
|
||||
public function offsetExists($key): bool
|
||||
{
|
||||
@@ -94,7 +97,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
/**
|
||||
* Removes the element from this list.
|
||||
* @param string|int $key
|
||||
* @param array-key $key
|
||||
*/
|
||||
public function offsetUnset($key): void
|
||||
{
|
||||
|
||||
34
vendor/nette/utils/src/Utils/ArrayList.php
vendored
34
vendor/nette/utils/src/Utils/ArrayList.php
vendored
@@ -10,26 +10,25 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_slice, array_splice, count, is_int;
|
||||
|
||||
|
||||
/**
|
||||
* Provides the base class for a generic list (items can be accessed by index).
|
||||
* @template T
|
||||
* @implements \IteratorAggregate<int, T>
|
||||
* @implements \ArrayAccess<int, T>
|
||||
*/
|
||||
class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var mixed[] */
|
||||
private $list = [];
|
||||
private array $list = [];
|
||||
|
||||
|
||||
/**
|
||||
* Transforms array to ArrayList.
|
||||
* @param array<T> $array
|
||||
* @return static
|
||||
* @param list<T> $array
|
||||
*/
|
||||
public static function from(array $array)
|
||||
public static function from(array $array): static
|
||||
{
|
||||
if (!Arrays::isList($array)) {
|
||||
throw new Nette\InvalidArgumentException('Array is not valid list.');
|
||||
@@ -43,11 +42,13 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
/**
|
||||
* Returns an iterator over all items.
|
||||
* @return \ArrayIterator<int, T>
|
||||
* @return \Iterator<int, T>
|
||||
*/
|
||||
public function getIterator(): \ArrayIterator
|
||||
public function &getIterator(): \Iterator
|
||||
{
|
||||
return new \ArrayIterator($this->list);
|
||||
foreach ($this->list as &$item) {
|
||||
yield $item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +62,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Replaces or appends a item.
|
||||
* Replaces or appends an item.
|
||||
* @param int|null $index
|
||||
* @param T $value
|
||||
* @throws Nette\OutOfRangeException
|
||||
@@ -81,13 +82,12 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Returns a item.
|
||||
* Returns an item.
|
||||
* @param int $index
|
||||
* @return T
|
||||
* @throws Nette\OutOfRangeException
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($index)
|
||||
public function offsetGet($index): mixed
|
||||
{
|
||||
if (!is_int($index) || $index < 0 || $index >= count($this->list)) {
|
||||
throw new Nette\OutOfRangeException('Offset invalid or out of range');
|
||||
@@ -98,7 +98,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a item exists.
|
||||
* Determines whether an item exists.
|
||||
* @param int $index
|
||||
*/
|
||||
public function offsetExists($index): bool
|
||||
@@ -123,10 +123,10 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Prepends a item.
|
||||
* Prepends an item.
|
||||
* @param T $value
|
||||
*/
|
||||
public function prepend($value): void
|
||||
public function prepend(mixed $value): void
|
||||
{
|
||||
$first = array_slice($this->list, 0, 1);
|
||||
$this->offsetSet(0, $value);
|
||||
|
||||
221
vendor/nette/utils/src/Utils/Arrays.php
vendored
221
vendor/nette/utils/src/Utils/Arrays.php
vendored
@@ -9,8 +9,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function is_array, is_int, is_object, count;
|
||||
use function array_combine, array_intersect_key, array_is_list, array_key_exists, array_key_first, array_key_last, array_keys, array_reverse, array_search, array_slice, array_walk_recursive, count, func_num_args, in_array, is_array, is_int, is_object, key, preg_split;
|
||||
use const PREG_GREP_INVERT, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,7 +31,7 @@ class Arrays
|
||||
* @return ?T
|
||||
* @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
|
||||
*/
|
||||
public static function get(array $array, $key, $default = null)
|
||||
public static function get(array $array, string|int|array $key, mixed $default = null): mixed
|
||||
{
|
||||
foreach (is_array($key) ? $key : [$key] as $k) {
|
||||
if (is_array($array) && array_key_exists($k, $array)) {
|
||||
@@ -55,7 +57,7 @@ class Arrays
|
||||
* @return ?T
|
||||
* @throws Nette\InvalidArgumentException if traversed item is not an array
|
||||
*/
|
||||
public static function &getRef(array &$array, $key)
|
||||
public static function &getRef(array &$array, string|int|array $key): mixed
|
||||
{
|
||||
foreach (is_array($key) ? $key : [$key] as $k) {
|
||||
if (is_array($array) || $array === null) {
|
||||
@@ -94,12 +96,10 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Returns zero-indexed position of given array key. Returns null if key is not found.
|
||||
* @param array-key $key
|
||||
* @return int|null offset if it is found, null otherwise
|
||||
*/
|
||||
public static function getKeyOffset(array $array, $key): ?int
|
||||
public static function getKeyOffset(array $array, string|int $key): ?int
|
||||
{
|
||||
return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), true));
|
||||
return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), strict: true));
|
||||
}
|
||||
|
||||
|
||||
@@ -114,75 +114,118 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Tests an array for the presence of value.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function contains(array $array, $value): bool
|
||||
public static function contains(array $array, mixed $value): bool
|
||||
{
|
||||
return in_array($value, $array, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first item from the array or null if array is empty.
|
||||
* @template T
|
||||
* @param array<T> $array
|
||||
* @return ?T
|
||||
* Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?V
|
||||
*/
|
||||
public static function first(array $array)
|
||||
public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed
|
||||
{
|
||||
return count($array) ? reset($array) : null;
|
||||
$key = self::firstKey($array, $predicate);
|
||||
return $key === null
|
||||
? ($else ? $else() : null)
|
||||
: $array[$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the last item from the array or null if array is empty.
|
||||
* @template T
|
||||
* @param array<T> $array
|
||||
* @return ?T
|
||||
* Returns the last item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?V
|
||||
*/
|
||||
public static function last(array $array)
|
||||
public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed
|
||||
{
|
||||
return count($array) ? end($array) : null;
|
||||
$key = self::lastKey($array, $predicate);
|
||||
return $key === null
|
||||
? ($else ? $else() : null)
|
||||
: $array[$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key of first item (matching the specified predicate if given) or null if there is no such item.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?K
|
||||
*/
|
||||
public static function firstKey(array $array, ?callable $predicate = null): int|string|null
|
||||
{
|
||||
if (!$predicate) {
|
||||
return array_key_first($array);
|
||||
}
|
||||
foreach ($array as $k => $v) {
|
||||
if ($predicate($v, $k, $array)) {
|
||||
return $k;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key of last item (matching the specified predicate if given) or null if there is no such item.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?K
|
||||
*/
|
||||
public static function lastKey(array $array, ?callable $predicate = null): int|string|null
|
||||
{
|
||||
return $predicate
|
||||
? self::firstKey(array_reverse($array, preserve_keys: true), $predicate)
|
||||
: array_key_last($array);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the contents of the $inserted array into the $array immediately after the $key.
|
||||
* If $key is null (or does not exist), it is inserted at the beginning.
|
||||
* @param array-key|null $key
|
||||
*/
|
||||
public static function insertBefore(array &$array, $key, array $inserted): void
|
||||
public static function insertBefore(array &$array, string|int|null $key, array $inserted): void
|
||||
{
|
||||
$offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key);
|
||||
$array = array_slice($array, 0, $offset, true)
|
||||
$array = array_slice($array, 0, $offset, preserve_keys: true)
|
||||
+ $inserted
|
||||
+ array_slice($array, $offset, count($array), true);
|
||||
+ array_slice($array, $offset, count($array), preserve_keys: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the contents of the $inserted array into the $array before the $key.
|
||||
* If $key is null (or does not exist), it is inserted at the end.
|
||||
* @param array-key|null $key
|
||||
*/
|
||||
public static function insertAfter(array &$array, $key, array $inserted): void
|
||||
public static function insertAfter(array &$array, string|int|null $key, array $inserted): void
|
||||
{
|
||||
if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) {
|
||||
$offset = count($array) - 1;
|
||||
}
|
||||
|
||||
$array = array_slice($array, 0, $offset + 1, true)
|
||||
$array = array_slice($array, 0, $offset + 1, preserve_keys: true)
|
||||
+ $inserted
|
||||
+ array_slice($array, $offset + 1, count($array), true);
|
||||
+ array_slice($array, $offset + 1, count($array), preserve_keys: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renames key in array.
|
||||
* @param array-key $oldKey
|
||||
* @param array-key $newKey
|
||||
*/
|
||||
public static function renameKey(array &$array, $oldKey, $newKey): bool
|
||||
public static function renameKey(array &$array, string|int $oldKey, string|int $newKey): bool
|
||||
{
|
||||
$offset = self::getKeyOffset($array, $oldKey);
|
||||
if ($offset === null) {
|
||||
@@ -203,8 +246,14 @@ class Arrays
|
||||
* @param string[] $array
|
||||
* @return string[]
|
||||
*/
|
||||
public static function grep(array $array, string $pattern, int $flags = 0): array
|
||||
public static function grep(
|
||||
array $array,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $invert = false,
|
||||
): array
|
||||
{
|
||||
$flags = $invert ? PREG_GREP_INVERT : 0;
|
||||
return Strings::pcre('preg_grep', [$pattern, $array, $flags]);
|
||||
}
|
||||
|
||||
@@ -225,23 +274,19 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list.
|
||||
* @param mixed $value
|
||||
* @return ($value is list ? true : false)
|
||||
*/
|
||||
public static function isList($value): bool
|
||||
public static function isList(mixed $value): bool
|
||||
{
|
||||
return is_array($value) && (PHP_VERSION_ID < 80100
|
||||
? !$value || array_keys($value) === range(0, count($value) - 1)
|
||||
: array_is_list($value)
|
||||
);
|
||||
return is_array($value) && array_is_list($value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reformats table to associative tree. Path looks like 'field|field[]field->field=field'.
|
||||
* @param string|string[] $path
|
||||
* @return array|\stdClass
|
||||
*/
|
||||
public static function associate(array $array, $path)
|
||||
public static function associate(array $array, $path): array|\stdClass
|
||||
{
|
||||
$parts = is_array($path)
|
||||
? $path
|
||||
@@ -293,9 +338,8 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling.
|
||||
* @param mixed $filling
|
||||
*/
|
||||
public static function normalize(array $array, $filling = null): array
|
||||
public static function normalize(array $array, mixed $filling = null): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
@@ -311,12 +355,11 @@ class Arrays
|
||||
* or returns $default, if provided.
|
||||
* @template T
|
||||
* @param array<T> $array
|
||||
* @param array-key $key
|
||||
* @param ?T $default
|
||||
* @return ?T
|
||||
* @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
|
||||
*/
|
||||
public static function pick(array &$array, $key, $default = null)
|
||||
public static function pick(array &$array, string|int $key, mixed $default = null): mixed
|
||||
{
|
||||
if (array_key_exists($key, $array)) {
|
||||
$value = $array[$key];
|
||||
@@ -333,13 +376,16 @@ class Arrays
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether at least one element in the array passes the test implemented by the
|
||||
* provided callback with signature `function ($value, $key, array $array): bool`.
|
||||
* Tests whether at least one element in the array passes the test implemented by the provided function.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): bool $predicate
|
||||
*/
|
||||
public static function some(iterable $array, callable $callback): bool
|
||||
public static function some(iterable $array, callable $predicate): bool
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if ($callback($v, $k, $array)) {
|
||||
if ($predicate($v, $k, $array)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -349,13 +395,16 @@ class Arrays
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether all elements in the array pass the test implemented by the provided function,
|
||||
* which has the signature `function ($value, $key, array $array): bool`.
|
||||
* Tests whether all elements in the array pass the test implemented by the provided function.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): bool $predicate
|
||||
*/
|
||||
public static function every(iterable $array, callable $callback): bool
|
||||
public static function every(iterable $array, callable $predicate): bool
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if (!$callback($v, $k, $array)) {
|
||||
if (!$predicate($v, $k, $array)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -365,14 +414,64 @@ class Arrays
|
||||
|
||||
|
||||
/**
|
||||
* Calls $callback on all elements in the array and returns the array of return values.
|
||||
* The callback has the signature `function ($value, $key, array $array): bool`.
|
||||
* Returns a new array containing all key-value pairs matching the given $predicate.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): bool $predicate
|
||||
* @return array<K, V>
|
||||
*/
|
||||
public static function map(iterable $array, callable $callback): array
|
||||
public static function filter(array $array, callable $predicate): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$res[$k] = $callback($v, $k, $array);
|
||||
if ($predicate($v, $k, $array)) {
|
||||
$res[$k] = $v;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array containing the original keys and results of applying the given transform function to each element.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @template R
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): R $transformer
|
||||
* @return array<K, R>
|
||||
*/
|
||||
public static function map(iterable $array, callable $transformer): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$res[$k] = $transformer($v, $k, $array);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array containing new keys and values generated by applying the given transform function to each element.
|
||||
* If the function returns null, the element is skipped.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @template ResK of int|string
|
||||
* @template ResV
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): ?array{ResK, ResV} $transformer
|
||||
* @return array<ResK, ResV>
|
||||
*/
|
||||
public static function mapWithKeys(array $array, callable $transformer): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$pair = $transformer($v, $k, $array);
|
||||
if ($pair) {
|
||||
$res[$pair[0]] = $pair[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
@@ -415,7 +514,7 @@ class Arrays
|
||||
* @param T $object
|
||||
* @return T
|
||||
*/
|
||||
public static function toObject(iterable $array, $object)
|
||||
public static function toObject(iterable $array, object $object): object
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
$object->$k = $v;
|
||||
@@ -427,12 +526,10 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Converts value to array key.
|
||||
* @param mixed $value
|
||||
* @return array-key
|
||||
*/
|
||||
public static function toKey($value)
|
||||
public static function toKey(mixed $value): int|string
|
||||
{
|
||||
return key([$value => null]);
|
||||
return key(@[$value => null]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
81
vendor/nette/utils/src/Utils/Callback.php
vendored
81
vendor/nette/utils/src/Utils/Callback.php
vendored
@@ -10,7 +10,7 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array, is_object, is_string;
|
||||
use function explode, func_get_args, ini_get, is_array, is_callable, is_object, is_string, preg_replace, restore_error_handler, set_error_handler, sprintf, str_contains, str_ends_with;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,52 +20,10 @@ final class Callback
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/**
|
||||
* @param string|object|callable $callable class, object, callable
|
||||
* @deprecated use Closure::fromCallable()
|
||||
*/
|
||||
public static function closure($callable, ?string $method = null): \Closure
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated, use Closure::fromCallable().', E_USER_DEPRECATED);
|
||||
try {
|
||||
return \Closure::fromCallable($method === null ? $callable : [$callable, $method]);
|
||||
} catch (\TypeError $e) {
|
||||
throw new Nette\InvalidArgumentException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes callback.
|
||||
* @return mixed
|
||||
* @deprecated
|
||||
*/
|
||||
public static function invoke($callable, ...$args)
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated, use native invoking.', E_USER_DEPRECATED);
|
||||
self::check($callable);
|
||||
return $callable(...$args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes callback with an array of parameters.
|
||||
* @return mixed
|
||||
* @deprecated
|
||||
*/
|
||||
public static function invokeArgs($callable, array $args = [])
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated, use native invoking.', E_USER_DEPRECATED);
|
||||
self::check($callable);
|
||||
return $callable(...$args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes internal PHP function with own error handler.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function invokeSafe(string $function, array $args, callable $onError)
|
||||
public static function invokeSafe(string $function, array $args, callable $onError): mixed
|
||||
{
|
||||
$prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool {
|
||||
if ($file === __FILE__) {
|
||||
@@ -92,17 +50,16 @@ final class Callback
|
||||
/**
|
||||
* Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies
|
||||
* that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists.
|
||||
* @param mixed $callable
|
||||
* @return callable
|
||||
* @throws Nette\InvalidArgumentException
|
||||
*/
|
||||
public static function check($callable, bool $syntax = false)
|
||||
public static function check(mixed $callable, bool $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))
|
||||
: sprintf("Callback '%s' is not callable.", self::toString($callable)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,15 +69,12 @@ final class Callback
|
||||
|
||||
/**
|
||||
* Converts PHP callback to textual form. Class or method may not exists.
|
||||
* @param mixed $callable
|
||||
*/
|
||||
public static function toString($callable): string
|
||||
public static function toString(mixed $callable): string
|
||||
{
|
||||
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(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
|
||||
return $textual;
|
||||
@@ -131,21 +85,20 @@ final class Callback
|
||||
/**
|
||||
* Returns reflection for method or function used in PHP callback.
|
||||
* @param callable $callable type check is escalated to ReflectionException
|
||||
* @return \ReflectionMethod|\ReflectionFunction
|
||||
* @throws \ReflectionException if callback is not valid
|
||||
*/
|
||||
public static function toReflection($callable): \ReflectionFunctionAbstract
|
||||
public static function toReflection($callable): \ReflectionMethod|\ReflectionFunction
|
||||
{
|
||||
if ($callable instanceof \Closure) {
|
||||
$callable = self::unwrap($callable);
|
||||
}
|
||||
|
||||
if (is_string($callable) && strpos($callable, '::')) {
|
||||
return new \ReflectionMethod($callable);
|
||||
if (is_string($callable) && str_contains($callable, '::')) {
|
||||
return new ReflectionMethod(...explode('::', $callable, 2));
|
||||
} elseif (is_array($callable)) {
|
||||
return new \ReflectionMethod($callable[0], $callable[1]);
|
||||
return new ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif (is_object($callable) && !$callable instanceof \Closure) {
|
||||
return new \ReflectionMethod($callable, '__invoke');
|
||||
return new ReflectionMethod($callable, '__invoke');
|
||||
} else {
|
||||
return new \ReflectionFunction($callable);
|
||||
}
|
||||
@@ -157,25 +110,25 @@ final class Callback
|
||||
*/
|
||||
public static function isStatic(callable $callable): bool
|
||||
{
|
||||
return is_array($callable) ? is_string($callable[0]) : is_string($callable);
|
||||
return is_string(is_array($callable) ? $callable[0] : $callable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unwraps closure created by Closure::fromCallable().
|
||||
* @return callable|array
|
||||
*/
|
||||
public static function unwrap(\Closure $closure)
|
||||
public static function unwrap(\Closure $closure): callable|array
|
||||
{
|
||||
$r = new \ReflectionFunction($closure);
|
||||
if (substr($r->name, -1) === '}') {
|
||||
$class = $r->getClosureScopeClass()?->name;
|
||||
if (str_ends_with($r->name, '}')) {
|
||||
return $closure;
|
||||
|
||||
} elseif ($obj = $r->getClosureThis()) {
|
||||
} elseif (($obj = $r->getClosureThis()) && $obj::class === $class) {
|
||||
return [$obj, $r->name];
|
||||
|
||||
} elseif ($class = $r->getClosureScopeClass()) {
|
||||
return [$class->name, $r->name];
|
||||
} elseif ($class) {
|
||||
return [$class, $r->name];
|
||||
|
||||
} else {
|
||||
return $r->name;
|
||||
|
||||
154
vendor/nette/utils/src/Utils/DateTime.php
vendored
154
vendor/nette/utils/src/Utils/DateTime.php
vendored
@@ -9,7 +9,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_merge, checkdate, implode, is_numeric, is_string, preg_replace_callback, sprintf, time, trim;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,8 +17,6 @@ use Nette;
|
||||
*/
|
||||
class DateTime extends \DateTime implements \JsonSerializable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** minute in seconds */
|
||||
public const MINUTE = 60;
|
||||
|
||||
@@ -32,29 +30,27 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
public const WEEK = 7 * self::DAY;
|
||||
|
||||
/** average month in seconds */
|
||||
public const MONTH = 2629800;
|
||||
public const MONTH = 2_629_800;
|
||||
|
||||
/** average year in seconds */
|
||||
public const YEAR = 31557600;
|
||||
public const YEAR = 31_557_600;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a DateTime object from a string, UNIX timestamp, or other DateTimeInterface object.
|
||||
* @param string|int|\DateTimeInterface $time
|
||||
* @return static
|
||||
* @throws \Exception if the date and time are not valid.
|
||||
*/
|
||||
public static function from($time)
|
||||
public static function from(string|int|\DateTimeInterface|null $time): static
|
||||
{
|
||||
if ($time instanceof \DateTimeInterface) {
|
||||
return new static($time->format('Y-m-d H:i:s.u'), $time->getTimezone());
|
||||
return static::createFromInterface($time);
|
||||
|
||||
} elseif (is_numeric($time)) {
|
||||
if ($time <= self::YEAR) {
|
||||
$time += time();
|
||||
}
|
||||
|
||||
return (new static('@' . $time))->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
return (new static)->setTimestamp((int) $time);
|
||||
|
||||
} else { // textual or null
|
||||
return new static((string) $time);
|
||||
@@ -64,8 +60,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
|
||||
/**
|
||||
* Creates DateTime object.
|
||||
* @return static
|
||||
* @throws Nette\InvalidArgumentException if the date and time are not valid.
|
||||
* @throws \Exception if the date and time are not valid.
|
||||
*/
|
||||
public static function fromParts(
|
||||
int $year,
|
||||
@@ -73,50 +68,108 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
int $day,
|
||||
int $hour = 0,
|
||||
int $minute = 0,
|
||||
float $second = 0.0
|
||||
) {
|
||||
$s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second);
|
||||
if (
|
||||
!checkdate($month, $day, $year)
|
||||
|| $hour < 0
|
||||
|| $hour > 23
|
||||
|| $minute < 0
|
||||
|| $minute > 59
|
||||
|| $second < 0
|
||||
|| $second >= 60
|
||||
) {
|
||||
throw new Nette\InvalidArgumentException("Invalid date '$s'");
|
||||
}
|
||||
|
||||
return new static($s);
|
||||
float $second = 0.0,
|
||||
): static
|
||||
{
|
||||
$sec = (int) floor($second);
|
||||
return (new static(''))
|
||||
->setDate($year, $month, $day)
|
||||
->setTime($hour, $minute, $sec, (int) round(($second - $sec) * 1e6));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns new DateTime object formatted according to the specified format.
|
||||
* @param string $format The format the $time parameter should be in
|
||||
* @param string $time
|
||||
* @param string|\DateTimeZone $timezone (default timezone is used if null is passed)
|
||||
* @return static|false
|
||||
* Returns a new DateTime object formatted according to the specified format.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public static function createFromFormat($format, $time, $timezone = null)
|
||||
public static function createFromFormat(
|
||||
string $format,
|
||||
string $datetime,
|
||||
string|\DateTimeZone|null $timezone = null,
|
||||
): static|false
|
||||
{
|
||||
if ($timezone === null) {
|
||||
$timezone = new \DateTimeZone(date_default_timezone_get());
|
||||
|
||||
} elseif (is_string($timezone)) {
|
||||
if (is_string($timezone)) {
|
||||
$timezone = new \DateTimeZone($timezone);
|
||||
|
||||
} elseif (!$timezone instanceof \DateTimeZone) {
|
||||
throw new Nette\InvalidArgumentException('Invalid timezone given');
|
||||
}
|
||||
|
||||
$date = parent::createFromFormat($format, $time, $timezone);
|
||||
$date = parent::createFromFormat($format, $datetime, $timezone);
|
||||
return $date ? static::from($date) : false;
|
||||
}
|
||||
|
||||
|
||||
public function __construct(string $datetime = 'now', ?\DateTimeZone $timezone = null)
|
||||
{
|
||||
$this->apply($datetime, $timezone, true);
|
||||
}
|
||||
|
||||
|
||||
public function modify(string $modifier): static
|
||||
{
|
||||
$this->apply($modifier);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function setDate(int $year, int $month, int $day): static
|
||||
{
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
throw new \Exception(sprintf('The date %04d-%02d-%02d is not valid.', $year, $month, $day));
|
||||
}
|
||||
return parent::setDate($year, $month, $day);
|
||||
}
|
||||
|
||||
|
||||
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static
|
||||
{
|
||||
if (
|
||||
$hour < 0 || $hour > 23
|
||||
|| $minute < 0 || $minute > 59
|
||||
|| $second < 0 || $second >= 60
|
||||
|| $microsecond < 0 || $microsecond >= 1_000_000
|
||||
) {
|
||||
throw new \Exception(sprintf('The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000));
|
||||
}
|
||||
return parent::setTime($hour, $minute, $second, $microsecond);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a relative time string (e.g. '10 minut') to seconds.
|
||||
*/
|
||||
public static function relativeToSeconds(string $relativeTime): int
|
||||
{
|
||||
return (new self('@0 ' . $relativeTime))
|
||||
->getTimestamp();
|
||||
}
|
||||
|
||||
|
||||
private function apply(string $datetime, $timezone = null, bool $ctr = false): void
|
||||
{
|
||||
$relPart = '';
|
||||
$absPart = preg_replace_callback(
|
||||
'/[+-]?\s*\d+\s+((microsecond|millisecond|[mµu]sec)s?|[mµ]s|sec(ond)?s?|min(ute)?s?|hours?)(\s+ago)?\b/iu',
|
||||
function ($m) use (&$relPart) {
|
||||
$relPart .= $m[0] . ' ';
|
||||
return '';
|
||||
},
|
||||
$datetime,
|
||||
);
|
||||
|
||||
if ($ctr) {
|
||||
parent::__construct($absPart, $timezone);
|
||||
$this->handleErrors($datetime);
|
||||
} elseif (trim($absPart)) {
|
||||
parent::modify($absPart) && $this->handleErrors($datetime);
|
||||
}
|
||||
|
||||
if ($relPart) {
|
||||
$timezone ??= $this->getTimezone();
|
||||
$this->setTimezone(new \DateTimeZone('UTC'));
|
||||
parent::modify($relPart) && $this->handleErrors($datetime);
|
||||
$this->setTimezone($timezone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON representation in ISO 8601 (used by JavaScript).
|
||||
*/
|
||||
@@ -136,12 +189,21 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
|
||||
|
||||
/**
|
||||
* Creates a copy with a modified time.
|
||||
* @return static
|
||||
* You'd better use: (clone $dt)->modify(...)
|
||||
*/
|
||||
public function modifyClone(string $modify = '')
|
||||
public function modifyClone(string $modify = ''): static
|
||||
{
|
||||
$dolly = clone $this;
|
||||
return $modify ? $dolly->modify($modify) : $dolly;
|
||||
}
|
||||
|
||||
|
||||
private function handleErrors(string $value): void
|
||||
{
|
||||
$errors = self::getLastErrors();
|
||||
$errors = array_merge($errors['errors'] ?? [], $errors['warnings'] ?? []);
|
||||
if ($errors) {
|
||||
throw new \Exception(implode(', ', $errors) . " '$value'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
134
vendor/nette/utils/src/Utils/FileSystem.php
vendored
134
vendor/nette/utils/src/Utils/FileSystem.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_pop, chmod, decoct, dirname, end, fclose, file_exists, file_get_contents, file_put_contents, fopen, implode, is_dir, is_file, is_link, mkdir, preg_match, preg_split, realpath, rename, rmdir, rtrim, sprintf, str_replace, stream_copy_to_stream, stream_is_local, strtr;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,27 +19,25 @@ use Nette;
|
||||
*/
|
||||
final class FileSystem
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/**
|
||||
* Creates a directory if it doesn't exist.
|
||||
* Creates a directory if it does not exist, including parent directories.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function createDir(string $dir, int $mode = 0777): void
|
||||
public static function createDir(string $dir, int $mode = 0o777): void
|
||||
{
|
||||
if (!is_dir($dir) && !@mkdir($dir, $mode, true) && !is_dir($dir)) { // @ - dir may already exist
|
||||
if (!is_dir($dir) && !@mkdir($dir, $mode, recursive: true) && !is_dir($dir)) { // @ - dir may already exist
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to create directory '%s' with mode %s. %s",
|
||||
self::normalizePath($dir),
|
||||
decoct($mode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copies a file or a directory. Overwrites existing files and directories by default.
|
||||
* Copies a file or an entire directory. Overwrites existing files and directories by default.
|
||||
* @throws Nette\IOException on error occurred
|
||||
* @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists
|
||||
*/
|
||||
@@ -64,16 +64,12 @@ final class FileSystem
|
||||
}
|
||||
} else {
|
||||
static::createDir(dirname($target));
|
||||
if (
|
||||
($s = @fopen($origin, 'rb'))
|
||||
&& ($d = @fopen($target, 'wb'))
|
||||
&& @stream_copy_to_stream($s, $d) === false
|
||||
) { // @ is escalated to exception
|
||||
if (@stream_copy_to_stream(static::open($origin, 'rb'), static::open($target, 'wb')) === false) { // @ is escalated to exception
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to copy file '%s' to '%s'. %s",
|
||||
self::normalizePath($origin),
|
||||
self::normalizePath($target),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -81,7 +77,26 @@ final class FileSystem
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a file or directory if exists.
|
||||
* Opens file and returns resource.
|
||||
* @return resource
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function open(string $path, string $mode)
|
||||
{
|
||||
$f = @fopen($path, $mode); // @ is escalated to exception
|
||||
if (!$f) {
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to open file '%s'. %s",
|
||||
self::normalizePath($path),
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
return $f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function delete(string $path): void
|
||||
@@ -92,7 +107,7 @@ final class FileSystem
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to delete '%s'. %s",
|
||||
self::normalizePath($path),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
} elseif (is_dir($path)) {
|
||||
@@ -104,7 +119,7 @@ final class FileSystem
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to delete directory '%s'. %s",
|
||||
self::normalizePath($path),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -135,7 +150,7 @@ final class FileSystem
|
||||
"Unable to rename file or directory '%s' to '%s'. %s",
|
||||
self::normalizePath($origin),
|
||||
self::normalizePath($target),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -153,7 +168,7 @@ final class FileSystem
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to read file '%s'. %s",
|
||||
self::normalizePath($file),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -161,18 +176,49 @@ final class FileSystem
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the file content line by line. Because it reads continuously as we iterate over the lines,
|
||||
* it is possible to read files larger than the available memory.
|
||||
* @return \Generator<int, string>
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function readLines(string $file, bool $stripNewLines = true): \Generator
|
||||
{
|
||||
return (function ($f) use ($file, $stripNewLines) {
|
||||
$counter = 0;
|
||||
do {
|
||||
$line = Callback::invokeSafe('fgets', [$f], fn($error) => throw new Nette\IOException(sprintf(
|
||||
"Unable to read file '%s'. %s",
|
||||
self::normalizePath($file),
|
||||
$error,
|
||||
)));
|
||||
if ($line === false) {
|
||||
fclose($f);
|
||||
break;
|
||||
}
|
||||
if ($stripNewLines) {
|
||||
$line = rtrim($line, "\r\n");
|
||||
}
|
||||
|
||||
yield $counter++ => $line;
|
||||
|
||||
} while (true);
|
||||
})(static::open($file, 'r'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the string to a file.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function write(string $file, string $content, ?int $mode = 0666): void
|
||||
public static function write(string $file, string $content, ?int $mode = 0o666): void
|
||||
{
|
||||
static::createDir(dirname($file));
|
||||
if (@file_put_contents($file, $content) === false) { // @ is escalated to exception
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to write file '%s'. %s",
|
||||
self::normalizePath($file),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -181,17 +227,18 @@ final class FileSystem
|
||||
"Unable to chmod file '%s' to mode %s. %s",
|
||||
self::normalizePath($file),
|
||||
decoct($mode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixes permissions to a specific file or directory. Directories can be fixed recursively.
|
||||
* Sets file permissions to `$fileMode` or directory permissions to `$dirMode`.
|
||||
* Recursively traverses and sets permissions on the entire contents of the directory as well.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void
|
||||
public static function makeWritable(string $path, int $dirMode = 0o777, int $fileMode = 0o666): void
|
||||
{
|
||||
if (is_file($path)) {
|
||||
if (!@chmod($path, $fileMode)) { // @ is escalated to exception
|
||||
@@ -199,7 +246,7 @@ final class FileSystem
|
||||
"Unable to chmod file '%s' to mode %s. %s",
|
||||
self::normalizePath($path),
|
||||
decoct($fileMode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
} elseif (is_dir($path)) {
|
||||
@@ -212,7 +259,7 @@ final class FileSystem
|
||||
"Unable to chmod directory '%s' to mode %s. %s",
|
||||
self::normalizePath($path),
|
||||
decoct($dirMode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
@@ -226,7 +273,7 @@ final class FileSystem
|
||||
*/
|
||||
public static function isAbsolute(string $path): bool
|
||||
{
|
||||
return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
|
||||
return (bool) preg_match('#([a-z]:)?[/\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +282,7 @@ final class FileSystem
|
||||
*/
|
||||
public static function normalizePath(string $path): string
|
||||
{
|
||||
$parts = $path === '' ? [] : preg_split('~[/\\\\]+~', $path);
|
||||
$parts = $path === '' ? [] : preg_split('~[/\\\]+~', $path);
|
||||
$res = [];
|
||||
foreach ($parts as $part) {
|
||||
if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') {
|
||||
@@ -258,4 +305,37 @@ final class FileSystem
|
||||
{
|
||||
return self::normalizePath(implode('/', $paths));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a path against a base path. If the path is absolute, returns it directly, if it's relative, joins it with the base path.
|
||||
*/
|
||||
public static function resolvePath(string $basePath, string $path): string
|
||||
{
|
||||
return match (true) {
|
||||
self::isAbsolute($path) => self::platformSlashes($path),
|
||||
$path === '' => self::platformSlashes($basePath),
|
||||
default => self::joinPaths($basePath, $path),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts backslashes to slashes.
|
||||
*/
|
||||
public static function unixSlashes(string $path): string
|
||||
{
|
||||
return strtr($path, '\\', '/');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts slashes to platform-specific directory separators.
|
||||
*/
|
||||
public static function platformSlashes(string $path): string
|
||||
{
|
||||
return DIRECTORY_SEPARATOR === '/'
|
||||
? strtr($path, '\\', '/')
|
||||
: str_replace(':\\\\', '://', strtr($path, '/', '\\')); // protocol://
|
||||
}
|
||||
}
|
||||
|
||||
1
vendor/nette/utils/src/Utils/Floats.php
vendored
1
vendor/nette/utils/src/Utils/Floats.php
vendored
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function abs, is_finite, is_nan, max, round;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
48
vendor/nette/utils/src/Utils/Helpers.php
vendored
48
vendor/nette/utils/src/Utils/Helpers.php
vendored
@@ -10,16 +10,21 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_unique, ini_get, levenshtein, max, min, ob_end_clean, ob_get_clean, ob_start, preg_replace, strlen;
|
||||
use const PHP_OS_FAMILY;
|
||||
|
||||
|
||||
class Helpers
|
||||
{
|
||||
public const IsWindows = PHP_OS_FAMILY === 'Windows';
|
||||
|
||||
|
||||
/**
|
||||
* Executes a callback and returns the captured output as a string.
|
||||
*/
|
||||
public static function capture(callable $func): string
|
||||
{
|
||||
ob_start(function () {});
|
||||
ob_start(fn() => '');
|
||||
try {
|
||||
$func();
|
||||
return ob_get_clean();
|
||||
@@ -45,10 +50,8 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Converts false to null, does not change other values.
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public static function falseToNull($value)
|
||||
public static function falseToNull(mixed $value): mixed
|
||||
{
|
||||
return $value === false ? null : $value;
|
||||
}
|
||||
@@ -56,12 +59,8 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Returns value clamped to the inclusive range of min and max.
|
||||
* @param int|float $value
|
||||
* @param int|float $min
|
||||
* @param int|float $max
|
||||
* @return int|float
|
||||
*/
|
||||
public static function clamp($value, $min, $max)
|
||||
public static function clamp(int|float $value, int|float $min, int|float $max): int|float
|
||||
{
|
||||
if ($min > $max) {
|
||||
throw new Nette\InvalidArgumentException("Minimum ($min) is not less than maximum ($max).");
|
||||
@@ -88,4 +87,35 @@ class Helpers
|
||||
|
||||
return $best;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two values in the same way that PHP does. Recognizes operators: >, >=, <, <=, =, ==, ===, !=, !==, <>
|
||||
*/
|
||||
public static function compare(mixed $left, string $operator, mixed $right): bool
|
||||
{
|
||||
return match ($operator) {
|
||||
'>' => $left > $right,
|
||||
'>=' => $left >= $right,
|
||||
'<' => $left < $right,
|
||||
'<=' => $left <= $right,
|
||||
'=', '==' => $left == $right,
|
||||
'===' => $left === $right,
|
||||
'!=', '<>' => $left != $right,
|
||||
'!==' => $left !== $right,
|
||||
default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits a class name into namespace and short class name.
|
||||
* @return array{string, string}
|
||||
*/
|
||||
public static function splitClassName(string $name): array
|
||||
{
|
||||
return ($pos = strrpos($name, '\\')) === false
|
||||
? ['', $name]
|
||||
: [substr($name, 0, $pos), substr($name, $pos + 1)];
|
||||
}
|
||||
}
|
||||
|
||||
128
vendor/nette/utils/src/Utils/Html.php
vendored
128
vendor/nette/utils/src/Utils/Html.php
vendored
@@ -9,9 +9,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Nette\HtmlStringable;
|
||||
use function is_array, is_float, is_object, is_string;
|
||||
use function array_merge, array_splice, count, explode, func_num_args, html_entity_decode, htmlspecialchars, http_build_query, implode, is_array, is_bool, is_float, is_object, is_string, json_encode, max, number_format, rtrim, str_contains, str_repeat, str_replace, strip_tags, strncmp, strpbrk, substr;
|
||||
use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
|
||||
|
||||
|
||||
/**
|
||||
@@ -233,37 +233,30 @@ use function is_array, is_float, is_object, is_string;
|
||||
*/
|
||||
class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array<string, mixed> element's attributes */
|
||||
public $attrs = [];
|
||||
public array $attrs = [];
|
||||
|
||||
/** @var bool use XHTML syntax? */
|
||||
public static $xhtml = false;
|
||||
|
||||
/** @var array<string, int> void elements */
|
||||
public static $emptyElements = [
|
||||
/** void elements */
|
||||
public static array $emptyElements = [
|
||||
'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,
|
||||
];
|
||||
|
||||
/** @var array<int, HtmlStringable|string> nodes */
|
||||
protected $children = [];
|
||||
protected array $children = [];
|
||||
|
||||
/** @var string element's name */
|
||||
private $name;
|
||||
/** element's name */
|
||||
private string $name = '';
|
||||
|
||||
/** @var bool is element empty? */
|
||||
private $isEmpty;
|
||||
private bool $isEmpty = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new HTML element.
|
||||
* @param array|string $attrs element's attributes or plain text content
|
||||
* @return static
|
||||
*/
|
||||
public static function el(?string $name = null, $attrs = null)
|
||||
public static function el(?string $name = null, array|string|null $attrs = null): static
|
||||
{
|
||||
$el = new static;
|
||||
$parts = explode(' ', (string) $name, 2);
|
||||
@@ -289,7 +282,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
/**
|
||||
* Returns an object representing HTML text.
|
||||
*/
|
||||
public static function fromHtml(string $html): self
|
||||
public static function fromHtml(string $html): static
|
||||
{
|
||||
return (new static)->setHtml($html);
|
||||
}
|
||||
@@ -298,7 +291,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
/**
|
||||
* Returns an object representing plain text.
|
||||
*/
|
||||
public static function fromText(string $text): self
|
||||
public static function fromText(string $text): static
|
||||
{
|
||||
return (new static)->setText($text);
|
||||
}
|
||||
@@ -333,9 +326,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Changes element's name.
|
||||
* @return static
|
||||
*/
|
||||
final public function setName(string $name, ?bool $isEmpty = null)
|
||||
final public function setName(string $name, ?bool $isEmpty = null): static
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->isEmpty = $isEmpty ?? isset(static::$emptyElements[$name]);
|
||||
@@ -363,9 +355,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets multiple attributes.
|
||||
* @return static
|
||||
*/
|
||||
public function addAttributes(array $attrs)
|
||||
public function addAttributes(array $attrs): static
|
||||
{
|
||||
$this->attrs = array_merge($this->attrs, $attrs);
|
||||
return $this;
|
||||
@@ -374,11 +365,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Appends value to element's attribute.
|
||||
* @param mixed $value
|
||||
* @param mixed $option
|
||||
* @return static
|
||||
*/
|
||||
public function appendAttribute(string $name, $value, $option = true)
|
||||
public function appendAttribute(string $name, mixed $value, mixed $option = true): static
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$prev = isset($this->attrs[$name]) ? (array) $this->attrs[$name] : [];
|
||||
@@ -400,10 +388,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets element's attribute.
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function setAttribute(string $name, $value)
|
||||
public function setAttribute(string $name, mixed $value): static
|
||||
{
|
||||
$this->attrs[$name] = $value;
|
||||
return $this;
|
||||
@@ -412,9 +398,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Returns element's attribute.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute(string $name)
|
||||
public function getAttribute(string $name): mixed
|
||||
{
|
||||
return $this->attrs[$name] ?? null;
|
||||
}
|
||||
@@ -422,9 +407,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Unsets element's attribute.
|
||||
* @return static
|
||||
*/
|
||||
public function removeAttribute(string $name)
|
||||
public function removeAttribute(string $name): static
|
||||
{
|
||||
unset($this->attrs[$name]);
|
||||
return $this;
|
||||
@@ -433,9 +417,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Unsets element's attributes.
|
||||
* @return static
|
||||
*/
|
||||
public function removeAttributes(array $attributes)
|
||||
public function removeAttributes(array $attributes): static
|
||||
{
|
||||
foreach ($attributes as $name) {
|
||||
unset($this->attrs[$name]);
|
||||
@@ -447,9 +430,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Overloaded setter for element's attribute.
|
||||
* @param mixed $value
|
||||
*/
|
||||
final public function __set(string $name, $value): void
|
||||
final public function __set(string $name, mixed $value): void
|
||||
{
|
||||
$this->attrs[$name] = $value;
|
||||
}
|
||||
@@ -457,9 +439,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Overloaded getter for element's attribute.
|
||||
* @return mixed
|
||||
*/
|
||||
final public function &__get(string $name)
|
||||
final public function &__get(string $name): mixed
|
||||
{
|
||||
return $this->attrs[$name];
|
||||
}
|
||||
@@ -485,9 +466,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Overloaded setter for element's attribute.
|
||||
* @return mixed
|
||||
*/
|
||||
final public function __call(string $m, array $args)
|
||||
final public function __call(string $m, array $args): mixed
|
||||
{
|
||||
$p = substr($m, 0, 3);
|
||||
if ($p === 'get' || $p === 'set' || $p === 'add') {
|
||||
@@ -516,9 +496,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Special setter for element's attribute.
|
||||
* @return static
|
||||
*/
|
||||
final public function href(string $path, ?array $query = null)
|
||||
final public function href(string $path, array $query = []): static
|
||||
{
|
||||
if ($query) {
|
||||
$query = http_build_query($query, '', '&');
|
||||
@@ -534,10 +513,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Setter for data-* attributes. Booleans are converted to 'true' resp. 'false'.
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function data(string $name, $value = null)
|
||||
public function data(string $name, mixed $value = null): static
|
||||
{
|
||||
if (func_num_args() === 1) {
|
||||
$this->attrs['data'] = $name;
|
||||
@@ -553,10 +530,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets element's HTML content.
|
||||
* @param HtmlStringable|string $html
|
||||
* @return static
|
||||
*/
|
||||
final public function setHtml($html)
|
||||
final public function setHtml(mixed $html): static
|
||||
{
|
||||
$this->children = [(string) $html];
|
||||
return $this;
|
||||
@@ -574,10 +549,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets element's textual content.
|
||||
* @param HtmlStringable|string|int|float $text
|
||||
* @return static
|
||||
*/
|
||||
final public function setText($text)
|
||||
final public function setText(mixed $text): static
|
||||
{
|
||||
if (!$text instanceof HtmlStringable) {
|
||||
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
|
||||
@@ -599,10 +572,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Adds new element's child.
|
||||
* @param HtmlStringable|string $child Html node or raw HTML string
|
||||
* @return static
|
||||
*/
|
||||
final public function addHtml($child)
|
||||
final public function addHtml(HtmlStringable|string $child): static
|
||||
{
|
||||
return $this->insert(null, $child);
|
||||
}
|
||||
@@ -610,10 +581,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Appends plain-text string to element content.
|
||||
* @param HtmlStringable|string|int|float $text
|
||||
* @return static
|
||||
*/
|
||||
public function addText($text)
|
||||
public function addText(\Stringable|string|int|null $text): static
|
||||
{
|
||||
if (!$text instanceof HtmlStringable) {
|
||||
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
|
||||
@@ -625,10 +594,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Creates and adds a new Html child.
|
||||
* @param array|string $attrs element's attributes or raw HTML string
|
||||
* @return static created element
|
||||
*/
|
||||
final public function create(string $name, $attrs = null)
|
||||
final public function create(string $name, array|string|null $attrs = null): static
|
||||
{
|
||||
$this->insert(null, $child = static::el($name, $attrs));
|
||||
return $child;
|
||||
@@ -637,10 +604,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Inserts child node.
|
||||
* @param HtmlStringable|string $child Html node or raw HTML string
|
||||
* @return static
|
||||
*/
|
||||
public function insert(?int $index, $child, bool $replace = false)
|
||||
public function insert(?int $index, HtmlStringable|string $child, bool $replace = false): static
|
||||
{
|
||||
$child = $child instanceof self ? $child : (string) $child;
|
||||
if ($index === null) { // append
|
||||
@@ -661,17 +626,15 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
*/
|
||||
final public function offsetSet($index, $child): void
|
||||
{
|
||||
$this->insert($index, $child, true);
|
||||
$this->insert($index, $child, replace: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns child node (\ArrayAccess implementation).
|
||||
* @param int $index
|
||||
* @return HtmlStringable|string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
final public function offsetGet($index)
|
||||
final public function offsetGet($index): HtmlStringable|string
|
||||
{
|
||||
return $this->children[$index];
|
||||
}
|
||||
@@ -771,16 +734,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
final public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (\Throwable $e) {
|
||||
if (PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
trigger_error('Exception in ' . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
|
||||
return '';
|
||||
}
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
|
||||
@@ -790,7 +744,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
final public function startTag(): string
|
||||
{
|
||||
return $this->name
|
||||
? '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>')
|
||||
? '<' . $this->name . $this->attributes() . '>'
|
||||
: '';
|
||||
}
|
||||
|
||||
@@ -821,11 +775,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
continue;
|
||||
|
||||
} elseif ($value === true) {
|
||||
if (static::$xhtml) {
|
||||
$s .= ' ' . $key . '="' . $key . '"';
|
||||
} else {
|
||||
$s .= ' ' . $key;
|
||||
}
|
||||
$s .= ' ' . $key;
|
||||
|
||||
continue;
|
||||
|
||||
@@ -857,14 +807,14 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
$value = (string) $value;
|
||||
}
|
||||
|
||||
$q = strpos($value, '"') === false ? '"' : "'";
|
||||
$q = str_contains($value, '"') ? "'" : '"';
|
||||
$s .= ' ' . $key . '=' . $q
|
||||
. str_replace(
|
||||
['&', $q, '<'],
|
||||
['&', $q === '"' ? '"' : ''', self::$xhtml ? '<' : '<'],
|
||||
$value
|
||||
['&', $q === '"' ? '"' : ''', '<'],
|
||||
$value,
|
||||
)
|
||||
. (strpos($value, '`') !== false && strpbrk($value, ' <>"\'') === false ? ' ' : '')
|
||||
. (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '')
|
||||
. $q;
|
||||
}
|
||||
|
||||
|
||||
527
vendor/nette/utils/src/Utils/Image.php
vendored
527
vendor/nette/utils/src/Utils/Image.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array, is_int, is_string;
|
||||
use const IMG_BMP, IMG_FLIP_BOTH, IMG_FLIP_HORIZONTAL, IMG_FLIP_VERTICAL, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP, PATHINFO_EXTENSION;
|
||||
|
||||
|
||||
/**
|
||||
@@ -22,116 +24,128 @@ use Nette;
|
||||
* $image->send();
|
||||
* </code>
|
||||
*
|
||||
* @method Image affine(array $affine, array $clip = null)
|
||||
* @method array affineMatrixConcat(array $m1, array $m2)
|
||||
* @method array affineMatrixGet(int $type, mixed $options = null)
|
||||
* @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 Image affine(array $affine, ?array $clip = null)
|
||||
* @method void alphaBlending(bool $enable)
|
||||
* @method void antialias(bool $enable)
|
||||
* @method void arc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color)
|
||||
* @method int colorAllocate(int $red, int $green, int $blue)
|
||||
* @method int colorAllocateAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method int colorAt(int $x, int $y)
|
||||
* @method int colorClosest(int $red, int $green, int $blue)
|
||||
* @method int colorClosestAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method int colorClosestHWB(int $red, int $green, int $blue)
|
||||
* @method void colorDeallocate(int $color)
|
||||
* @method int colorExact(int $red, int $green, int $blue)
|
||||
* @method int colorExactAlpha(int $red, int $green, int $blue, int $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 colorResolve(int $red, int $green, int $blue)
|
||||
* @method int colorResolveAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method void colorSet(int $index, int $red, int $green, int $blue, int $alpha = 0)
|
||||
* @method array colorsForIndex(int $color)
|
||||
* @method int colorsTotal()
|
||||
* @method int colorTransparent($color = null)
|
||||
* @method int colorTransparent(?int $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 Image cropAuto(int $mode = -1, float $threshold = .5, int $color = -1)
|
||||
* @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 void copy(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH)
|
||||
* @method void copyMerge(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct)
|
||||
* @method void copyMergeGray(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct)
|
||||
* @method void copyResampled(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH)
|
||||
* @method void copyResized(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH)
|
||||
* @method Image cropAuto(int $mode = IMG_CROP_DEFAULT, float $threshold = .5, ?ImageColor $color = null)
|
||||
* @method void ellipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color)
|
||||
* @method void fill(int $x, int $y, ImageColor $color)
|
||||
* @method void filledArc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color, int $style)
|
||||
* @method void filledEllipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color)
|
||||
* @method void filledPolygon(array $points, ImageColor $color)
|
||||
* @method void filledRectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color)
|
||||
* @method void fillToBorder(int $x, int $y, ImageColor $borderColor, ImageColor $color)
|
||||
* @method void filter(int $filter, ...$args)
|
||||
* @method void flip(int $mode)
|
||||
* @method array ftText($size, $angle, $x, $y, $col, string $fontFile, string $text, array $extrainfo = null)
|
||||
* @method array ftText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontFile, string $text, array $options = [])
|
||||
* @method void gammaCorrect(float $inputgamma, float $outputgamma)
|
||||
* @method array getClip()
|
||||
* @method int interlace($interlace = null)
|
||||
* @method int getInterpolation()
|
||||
* @method int interlace(?bool $enable = null)
|
||||
* @method bool isTrueColor()
|
||||
* @method void layerEffect($effect)
|
||||
* @method void line($x1, $y1, $x2, $y2, $color)
|
||||
* @method void openPolygon(array $points, int $num_points, int $color)
|
||||
* @method void layerEffect(int $effect)
|
||||
* @method void line(int $x1, int $y1, int $x2, int $y2, ImageColor $color)
|
||||
* @method void openPolygon(array $points, ImageColor $color)
|
||||
* @method void paletteCopy(Image $source)
|
||||
* @method void paletteToTrueColor()
|
||||
* @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 mixed resolution(int $res_x = null, int $res_y = null)
|
||||
* @method Image rotate(float $angle, $backgroundColor)
|
||||
* @method void saveAlpha(bool $saveflag)
|
||||
* @method void polygon(array $points, ImageColor $color)
|
||||
* @method void rectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color)
|
||||
* @method mixed resolution(?int $resolutionX = null, ?int $resolutionY = null)
|
||||
* @method Image rotate(float $angle, ImageColor $backgroundColor)
|
||||
* @method void saveAlpha(bool $enable)
|
||||
* @method Image scale(int $newWidth, int $newHeight = -1, int $mode = IMG_BILINEAR_FIXED)
|
||||
* @method void setBrush(Image $brush)
|
||||
* @method void setClip(int $x1, int $y1, int $x2, int $y2)
|
||||
* @method void setInterpolation(int $method = IMG_BILINEAR_FIXED)
|
||||
* @method void setPixel($x, $y, $color)
|
||||
* @method void setPixel(int $x, int $y, ImageColor $color)
|
||||
* @method void setStyle(array $style)
|
||||
* @method void setThickness($thickness)
|
||||
* @method void setThickness(int $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|\GdImage $imageResource
|
||||
* @method void trueColorToPalette(bool $dither, int $ncolors)
|
||||
* @method array ttfText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontfile, string $text, array $options = [])
|
||||
* @property-read positive-int $width
|
||||
* @property-read positive-int $height
|
||||
* @property-read \GdImage $imageResource
|
||||
*/
|
||||
class Image
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** {@link resize()} only shrinks images */
|
||||
public const SHRINK_ONLY = 0b0001;
|
||||
/** Prevent from getting resized to a bigger size than the original */
|
||||
public const ShrinkOnly = 0b0001;
|
||||
|
||||
/** {@link resize()} will ignore aspect ratio */
|
||||
public const STRETCH = 0b0010;
|
||||
/** Resizes to a specified width and height without keeping aspect ratio */
|
||||
public const Stretch = 0b0010;
|
||||
|
||||
/** {@link resize()} fits in given area so its dimensions are less than or equal to the required dimensions */
|
||||
public const FIT = 0b0000;
|
||||
/** Resizes to fit into a specified width and height and preserves aspect ratio */
|
||||
public const OrSmaller = 0b0000;
|
||||
|
||||
/** {@link resize()} fills given area so its dimensions are greater than or equal to the required dimensions */
|
||||
public const FILL = 0b0100;
|
||||
/** Resizes while bounding the smaller dimension to the specified width or height and preserves aspect ratio */
|
||||
public const OrBigger = 0b0100;
|
||||
|
||||
/** {@link resize()} fills given area exactly */
|
||||
public const EXACT = 0b1000;
|
||||
/** Resizes to the smallest possible size to completely cover specified width and height and reserves aspect ratio */
|
||||
public const Cover = 0b1000;
|
||||
|
||||
/** @deprecated use Image::ShrinkOnly */
|
||||
public const SHRINK_ONLY = self::ShrinkOnly;
|
||||
|
||||
/** @deprecated use Image::Stretch */
|
||||
public const STRETCH = self::Stretch;
|
||||
|
||||
/** @deprecated use Image::OrSmaller */
|
||||
public const FIT = self::OrSmaller;
|
||||
|
||||
/** @deprecated use Image::OrBigger */
|
||||
public const FILL = self::OrBigger;
|
||||
|
||||
/** @deprecated use Image::Cover */
|
||||
public const EXACT = self::Cover;
|
||||
|
||||
/** @deprecated use Image::EmptyGIF */
|
||||
public const EMPTY_GIF = self::EmptyGIF;
|
||||
|
||||
/** image types */
|
||||
public const
|
||||
JPEG = IMAGETYPE_JPEG,
|
||||
PNG = IMAGETYPE_PNG,
|
||||
GIF = IMAGETYPE_GIF,
|
||||
WEBP = IMAGETYPE_WEBP,
|
||||
AVIF = 19, // IMAGETYPE_AVIF,
|
||||
BMP = IMAGETYPE_BMP;
|
||||
JPEG = ImageType::JPEG,
|
||||
PNG = ImageType::PNG,
|
||||
GIF = ImageType::GIF,
|
||||
WEBP = ImageType::WEBP,
|
||||
AVIF = ImageType::AVIF,
|
||||
BMP = ImageType::BMP;
|
||||
|
||||
public 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;";
|
||||
public const EmptyGIF = "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;";
|
||||
|
||||
private const Formats = [self::JPEG => 'jpeg', self::PNG => 'png', self::GIF => 'gif', self::WEBP => 'webp', self::AVIF => 'avif', self::BMP => 'bmp'];
|
||||
private const Formats = [ImageType::JPEG => 'jpeg', ImageType::PNG => 'png', ImageType::GIF => 'gif', ImageType::WEBP => 'webp', ImageType::AVIF => 'avif', ImageType::BMP => 'bmp'];
|
||||
|
||||
/** @var resource|\GdImage */
|
||||
private $image;
|
||||
private \GdImage $image;
|
||||
|
||||
|
||||
/**
|
||||
* Returns RGB color (0..255) and transparency (0..127).
|
||||
* @deprecated use ImageColor::rgb()
|
||||
*/
|
||||
public static function rgb(int $red, int $green, int $blue, int $transparency = 0): array
|
||||
{
|
||||
@@ -148,14 +162,10 @@ class Image
|
||||
* Reads an image from a file and returns its type in $type.
|
||||
* @throws Nette\NotSupportedException if gd extension is not loaded
|
||||
* @throws UnknownImageFileException if file not found or file type is not known
|
||||
* @return static
|
||||
*/
|
||||
public static function fromFile(string $file, ?int &$type = null)
|
||||
public static function fromFile(string $file, ?int &$type = null): static
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
|
||||
self::ensureExtension();
|
||||
$type = self::detectTypeFromFile($file);
|
||||
if (!$type) {
|
||||
throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found.");
|
||||
@@ -167,16 +177,12 @@ class Image
|
||||
|
||||
/**
|
||||
* Reads an image from a string and returns its type in $type.
|
||||
* @return static
|
||||
* @throws Nette\NotSupportedException if gd extension is not loaded
|
||||
* @throws ImageException
|
||||
*/
|
||||
public static function fromString(string $s, ?int &$type = null)
|
||||
public static function fromString(string $s, ?int &$type = null): static
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
|
||||
self::ensureExtension();
|
||||
$type = self::detectTypeFromString($s);
|
||||
if (!$type) {
|
||||
throw new UnknownImageFileException('Unknown type of image.');
|
||||
@@ -186,7 +192,7 @@ class Image
|
||||
}
|
||||
|
||||
|
||||
private static function invokeSafe(string $func, string $arg, string $message, string $callee): self
|
||||
private static function invokeSafe(string $func, string $arg, string $message, string $callee): static
|
||||
{
|
||||
$errors = [];
|
||||
$res = Callback::invokeSafe($func, [$arg], function (string $message) use (&$errors): void {
|
||||
@@ -205,54 +211,54 @@ class Image
|
||||
|
||||
/**
|
||||
* Creates a new true color image of the given dimensions. The default color is black.
|
||||
* @return static
|
||||
* @param positive-int $width
|
||||
* @param positive-int $height
|
||||
* @throws Nette\NotSupportedException if gd extension is not loaded
|
||||
*/
|
||||
public static function fromBlank(int $width, int $height, ?array $color = null)
|
||||
public static function fromBlank(int $width, int $height, ImageColor|array|null $color = null): static
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
|
||||
self::ensureExtension();
|
||||
if ($width < 1 || $height < 1) {
|
||||
throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.');
|
||||
}
|
||||
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
$image = new static(imagecreatetruecolor($width, $height));
|
||||
if ($color) {
|
||||
$color += ['alpha' => 0];
|
||||
$color = imagecolorresolvealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
|
||||
imagealphablending($image, false);
|
||||
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
|
||||
imagealphablending($image, true);
|
||||
$image->alphablending(false);
|
||||
$image->filledrectangle(0, 0, $width - 1, $height - 1, $color);
|
||||
$image->alphablending(true);
|
||||
}
|
||||
|
||||
return new static($image);
|
||||
return $image;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of image from file.
|
||||
* @return ImageType::*|null
|
||||
*/
|
||||
public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int
|
||||
{
|
||||
[$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
|
||||
return isset(self::Formats[$type]) ? $type : null;
|
||||
[$width, $height, $type] = Helpers::falseToNull(@getimagesize($file)); // @ - files smaller than 12 bytes causes read error
|
||||
return $type && isset(self::Formats[$type]) ? $type : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of image from string.
|
||||
* @return ImageType::*|null
|
||||
*/
|
||||
public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int
|
||||
{
|
||||
[$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error
|
||||
return isset(self::Formats[$type]) ? $type : null;
|
||||
[$width, $height, $type] = Helpers::falseToNull(@getimagesizefromstring($s)); // @ - strings smaller than 12 bytes causes read error
|
||||
return $type && isset(self::Formats[$type]) ? $type : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the file extension for the given `Image::XXX` constant.
|
||||
* Returns the file extension for the given image type.
|
||||
* @param ImageType::* $type
|
||||
* @return value-of<self::Formats>
|
||||
*/
|
||||
public static function typeToExtension(int $type): string
|
||||
{
|
||||
@@ -265,11 +271,12 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Returns the `Image::XXX` constant for given file extension.
|
||||
* Returns the image type for given file extension.
|
||||
* @return ImageType::*
|
||||
*/
|
||||
public static function extensionToType(string $extension): int
|
||||
{
|
||||
$extensions = array_flip(self::Formats) + ['jpg' => self::JPEG];
|
||||
$extensions = array_flip(self::Formats) + ['jpg' => ImageType::JPEG];
|
||||
$extension = strtolower($extension);
|
||||
if (!isset($extensions[$extension])) {
|
||||
throw new Nette\InvalidArgumentException("Unsupported file extension '$extension'.");
|
||||
@@ -280,7 +287,8 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Returns the mime type for the given `Image::XXX` constant.
|
||||
* Returns the mime type for the given image type.
|
||||
* @param ImageType::* $type
|
||||
*/
|
||||
public static function typeToMimeType(int $type): string
|
||||
{
|
||||
@@ -289,10 +297,43 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Wraps GD image.
|
||||
* @param resource|\GdImage $image
|
||||
* @param ImageType::* $type
|
||||
*/
|
||||
public function __construct($image)
|
||||
public static function isTypeSupported(int $type): bool
|
||||
{
|
||||
self::ensureExtension();
|
||||
return (bool) (imagetypes() & match ($type) {
|
||||
ImageType::JPEG => IMG_JPG,
|
||||
ImageType::PNG => IMG_PNG,
|
||||
ImageType::GIF => IMG_GIF,
|
||||
ImageType::WEBP => IMG_WEBP,
|
||||
ImageType::AVIF => 256, // IMG_AVIF,
|
||||
ImageType::BMP => IMG_BMP,
|
||||
default => 0,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/** @return ImageType[] */
|
||||
public static function getSupportedTypes(): array
|
||||
{
|
||||
self::ensureExtension();
|
||||
$flag = imagetypes();
|
||||
return array_filter([
|
||||
$flag & IMG_GIF ? ImageType::GIF : null,
|
||||
$flag & IMG_JPG ? ImageType::JPEG : null,
|
||||
$flag & IMG_PNG ? ImageType::PNG : null,
|
||||
$flag & IMG_WEBP ? ImageType::WEBP : null,
|
||||
$flag & 256 ? ImageType::AVIF : null, // IMG_AVIF
|
||||
$flag & IMG_BMP ? ImageType::BMP : null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps GD image.
|
||||
*/
|
||||
public function __construct(\GdImage $image)
|
||||
{
|
||||
$this->setImageResource($image);
|
||||
imagesavealpha($image, true);
|
||||
@@ -301,6 +342,7 @@ class Image
|
||||
|
||||
/**
|
||||
* Returns image width.
|
||||
* @return positive-int
|
||||
*/
|
||||
public function getWidth(): int
|
||||
{
|
||||
@@ -310,6 +352,7 @@ class Image
|
||||
|
||||
/**
|
||||
* Returns image height.
|
||||
* @return positive-int
|
||||
*/
|
||||
public function getHeight(): int
|
||||
{
|
||||
@@ -319,15 +362,9 @@ class Image
|
||||
|
||||
/**
|
||||
* Sets image resource.
|
||||
* @param resource|\GdImage $image
|
||||
* @return static
|
||||
*/
|
||||
protected function setImageResource($image)
|
||||
protected function setImageResource(\GdImage $image): static
|
||||
{
|
||||
if (!$image instanceof \GdImage && !(is_resource($image) && get_resource_type($image) === 'gd')) {
|
||||
throw new Nette\InvalidArgumentException('Image is not valid.');
|
||||
}
|
||||
|
||||
$this->image = $image;
|
||||
return $this;
|
||||
}
|
||||
@@ -335,30 +372,27 @@ class Image
|
||||
|
||||
/**
|
||||
* Returns image GD resource.
|
||||
* @return resource|\GdImage
|
||||
*/
|
||||
public function getImageResource()
|
||||
public function getImageResource(): \GdImage
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scales an image.
|
||||
* @param int|string|null $width in pixels or percent
|
||||
* @param int|string|null $height in pixels or percent
|
||||
* @return static
|
||||
* Scales an image. Width and height accept pixels or percent.
|
||||
* @param int-mask-of<self::OrSmaller|self::OrBigger|self::Stretch|self::Cover|self::ShrinkOnly> $mode
|
||||
*/
|
||||
public function resize($width, $height, int $flags = self::FIT)
|
||||
public function resize(int|string|null $width, int|string|null $height, int $mode = self::OrSmaller): static
|
||||
{
|
||||
if ($flags & self::EXACT) {
|
||||
return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height);
|
||||
if ($mode & self::Cover) {
|
||||
return $this->resize($width, $height, self::OrBigger)->crop('50%', '50%', $width, $height);
|
||||
}
|
||||
|
||||
[$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags);
|
||||
[$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $mode);
|
||||
|
||||
if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize
|
||||
$newImage = static::fromBlank($newWidth, $newHeight, self::rgb(0, 0, 0, 127))->getImageResource();
|
||||
$newImage = static::fromBlank($newWidth, $newHeight, ImageColor::rgb(0, 0, 0, 0))->getImageResource();
|
||||
imagecopyresampled(
|
||||
$newImage,
|
||||
$this->image,
|
||||
@@ -369,7 +403,7 @@ class Image
|
||||
$newWidth,
|
||||
$newHeight,
|
||||
$this->getWidth(),
|
||||
$this->getHeight()
|
||||
$this->getHeight(),
|
||||
);
|
||||
$this->image = $newImage;
|
||||
}
|
||||
@@ -383,17 +417,17 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Calculates dimensions of resized image.
|
||||
* @param int|string|null $newWidth in pixels or percent
|
||||
* @param int|string|null $newHeight in pixels or percent
|
||||
* Calculates dimensions of resized image. Width and height accept pixels or percent.
|
||||
* @param int-mask-of<self::OrSmaller|self::OrBigger|self::Stretch|self::Cover|self::ShrinkOnly> $mode
|
||||
*/
|
||||
public static function calculateSize(
|
||||
int $srcWidth,
|
||||
int $srcHeight,
|
||||
$newWidth,
|
||||
$newHeight,
|
||||
int $flags = self::FIT
|
||||
): array {
|
||||
int $mode = self::OrSmaller,
|
||||
): array
|
||||
{
|
||||
if ($newWidth === null) {
|
||||
} elseif (self::isPercent($newWidth)) {
|
||||
$newWidth = (int) round($srcWidth / 100 * abs($newWidth));
|
||||
@@ -405,19 +439,19 @@ class Image
|
||||
if ($newHeight === null) {
|
||||
} elseif (self::isPercent($newHeight)) {
|
||||
$newHeight = (int) round($srcHeight / 100 * abs($newHeight));
|
||||
$flags |= empty($percents) ? 0 : self::STRETCH;
|
||||
$mode |= empty($percents) ? 0 : self::Stretch;
|
||||
} else {
|
||||
$newHeight = abs($newHeight);
|
||||
}
|
||||
|
||||
if ($flags & self::STRETCH) { // non-proportional
|
||||
if ($mode & self::Stretch) { // non-proportional
|
||||
if (!$newWidth || !$newHeight) {
|
||||
throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.');
|
||||
}
|
||||
|
||||
if ($flags & self::SHRINK_ONLY) {
|
||||
$newWidth = (int) round($srcWidth * min(1, $newWidth / $srcWidth));
|
||||
$newHeight = (int) round($srcHeight * min(1, $newHeight / $srcHeight));
|
||||
if ($mode & self::ShrinkOnly) {
|
||||
$newWidth = min($srcWidth, $newWidth);
|
||||
$newHeight = min($srcHeight, $newHeight);
|
||||
}
|
||||
} else { // proportional
|
||||
if (!$newWidth && !$newHeight) {
|
||||
@@ -433,11 +467,11 @@ class Image
|
||||
$scale[] = $newHeight / $srcHeight;
|
||||
}
|
||||
|
||||
if ($flags & self::FILL) {
|
||||
if ($mode & self::OrBigger) {
|
||||
$scale = [max($scale)];
|
||||
}
|
||||
|
||||
if ($flags & self::SHRINK_ONLY) {
|
||||
if ($mode & self::ShrinkOnly) {
|
||||
$scale[] = 1;
|
||||
}
|
||||
|
||||
@@ -451,14 +485,9 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Crops image.
|
||||
* @param int|string $left in pixels or percent
|
||||
* @param int|string $top in pixels or percent
|
||||
* @param int|string $width in pixels or percent
|
||||
* @param int|string $height in pixels or percent
|
||||
* @return static
|
||||
* Crops image. Arguments accepts pixels or percent.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height)
|
||||
public function crop(int|string $left, int|string $top, int|string $width, int|string $height): static
|
||||
{
|
||||
[$r['x'], $r['y'], $r['width'], $r['height']]
|
||||
= static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height);
|
||||
@@ -466,7 +495,7 @@ class Image
|
||||
$this->image = imagecrop($this->image, $r);
|
||||
imagesavealpha($this->image, true);
|
||||
} else {
|
||||
$newImage = static::fromBlank($r['width'], $r['height'], self::RGB(0, 0, 0, 127))->getImageResource();
|
||||
$newImage = static::fromBlank($r['width'], $r['height'], ImageColor::rgb(0, 0, 0, 0))->getImageResource();
|
||||
imagecopy($newImage, $this->image, 0, 0, $r['x'], $r['y'], $r['width'], $r['height']);
|
||||
$this->image = $newImage;
|
||||
}
|
||||
@@ -476,13 +505,16 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Calculates dimensions of cutout in image.
|
||||
* @param int|string $left in pixels or percent
|
||||
* @param int|string $top in pixels or percent
|
||||
* @param int|string $newWidth in pixels or percent
|
||||
* @param int|string $newHeight in pixels or percent
|
||||
* Calculates dimensions of cutout in image. Arguments accepts pixels or percent.
|
||||
*/
|
||||
public static function calculateCutout(int $srcWidth, int $srcHeight, $left, $top, $newWidth, $newHeight): array
|
||||
public static function calculateCutout(
|
||||
int $srcWidth,
|
||||
int $srcHeight,
|
||||
int|string $left,
|
||||
int|string $top,
|
||||
int|string $newWidth,
|
||||
int|string $newHeight,
|
||||
): array
|
||||
{
|
||||
if (self::isPercent($newWidth)) {
|
||||
$newWidth = (int) round($srcWidth / 100 * $newWidth);
|
||||
@@ -518,9 +550,8 @@ class Image
|
||||
|
||||
/**
|
||||
* Sharpens image a little bit.
|
||||
* @return static
|
||||
*/
|
||||
public function sharpen()
|
||||
public function sharpen(): static
|
||||
{
|
||||
imageconvolution($this->image, [ // my magic numbers ;)
|
||||
[-1, -1, -1],
|
||||
@@ -532,13 +563,10 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Puts another image into this image.
|
||||
* @param int|string $left in pixels or percent
|
||||
* @param int|string $top in pixels or percent
|
||||
* @param int $opacity 0..100
|
||||
* @return static
|
||||
* Puts another image into this image. Left and top accepts pixels or percent.
|
||||
* @param int<0, 100> $opacity 0..100
|
||||
*/
|
||||
public function place(self $image, $left = 0, $top = 0, int $opacity = 100)
|
||||
public function place(self $image, int|string $left = 0, int|string $top = 0, int $opacity = 100): static
|
||||
{
|
||||
$opacity = max(0, min(100, $opacity));
|
||||
if ($opacity === 0) {
|
||||
@@ -590,29 +618,75 @@ class Image
|
||||
0,
|
||||
0,
|
||||
$width,
|
||||
$height
|
||||
$height,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the bounding box for a TrueType text. Returns keys left, top, width and height.
|
||||
*/
|
||||
public static function calculateTextBox(
|
||||
string $text,
|
||||
string $fontFile,
|
||||
float $size,
|
||||
float $angle = 0,
|
||||
array $options = [],
|
||||
): array
|
||||
{
|
||||
self::ensureExtension();
|
||||
$box = imagettfbbox($size, $angle, $fontFile, $text, $options);
|
||||
return [
|
||||
'left' => $minX = min([$box[0], $box[2], $box[4], $box[6]]),
|
||||
'top' => $minY = min([$box[1], $box[3], $box[5], $box[7]]),
|
||||
'width' => max([$box[0], $box[2], $box[4], $box[6]]) - $minX + 1,
|
||||
'height' => max([$box[1], $box[3], $box[5], $box[7]]) - $minY + 1,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw a rectangle.
|
||||
*/
|
||||
public function rectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void
|
||||
{
|
||||
if ($width !== 0 && $height !== 0) {
|
||||
$this->rectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw a filled rectangle.
|
||||
*/
|
||||
public function filledRectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void
|
||||
{
|
||||
if ($width !== 0 && $height !== 0) {
|
||||
$this->filledRectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves image to the file. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9).
|
||||
* @param ImageType::*|null $type
|
||||
* @throws ImageException
|
||||
*/
|
||||
public function save(string $file, ?int $quality = null, ?int $type = null): void
|
||||
{
|
||||
$type = $type ?? self::extensionToType(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$type ??= self::extensionToType(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$this->output($type, $quality, $file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs image to string. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9).
|
||||
* @param ImageType::* $type
|
||||
*/
|
||||
public function toString(int $type = self::JPEG, ?int $quality = null): string
|
||||
public function toString(int $type = ImageType::JPEG, ?int $quality = null): string
|
||||
{
|
||||
return Helpers::capture(function () use ($type, $quality) {
|
||||
return Helpers::capture(function () use ($type, $quality): void {
|
||||
$this->output($type, $quality);
|
||||
});
|
||||
}
|
||||
@@ -623,24 +697,16 @@ class Image
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return $this->toString();
|
||||
} catch (\Throwable $e) {
|
||||
if (func_num_args() || PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
trigger_error('Exception in ' . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
|
||||
return '';
|
||||
}
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs image to browser. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9).
|
||||
* @param ImageType::* $type
|
||||
* @throws ImageException
|
||||
*/
|
||||
public function send(int $type = self::JPEG, ?int $quality = null): void
|
||||
public function send(int $type = ImageType::JPEG, ?int $quality = null): void
|
||||
{
|
||||
header('Content-Type: ' . self::typeToMimeType($type));
|
||||
$this->output($type, $quality);
|
||||
@@ -649,55 +715,40 @@ class Image
|
||||
|
||||
/**
|
||||
* Outputs image to browser or file.
|
||||
* @param ImageType::* $type
|
||||
* @throws ImageException
|
||||
*/
|
||||
private function output(int $type, ?int $quality, ?string $file = null): void
|
||||
{
|
||||
switch ($type) {
|
||||
case self::JPEG:
|
||||
$quality = $quality === null ? 85 : max(0, min(100, $quality));
|
||||
$success = @imagejpeg($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
[$defQuality, $min, $max] = match ($type) {
|
||||
ImageType::JPEG => [85, 0, 100],
|
||||
ImageType::PNG => [9, 0, 9],
|
||||
ImageType::GIF => [null, null, null],
|
||||
ImageType::WEBP => [80, 0, 100],
|
||||
ImageType::AVIF => [30, 0, 100],
|
||||
ImageType::BMP => [null, null, null],
|
||||
default => throw new Nette\InvalidArgumentException("Unsupported image type '$type'."),
|
||||
};
|
||||
|
||||
case self::PNG:
|
||||
$quality = $quality === null ? 9 : max(0, min(9, $quality));
|
||||
$success = @imagepng($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::GIF:
|
||||
$success = @imagegif($this->image, $file); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::WEBP:
|
||||
$quality = $quality === null ? 80 : max(0, min(100, $quality));
|
||||
$success = @imagewebp($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::AVIF:
|
||||
$quality = $quality === null ? 30 : max(0, min(100, $quality));
|
||||
$success = @imageavif($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::BMP:
|
||||
$success = @imagebmp($this->image, $file); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Nette\InvalidArgumentException("Unsupported image type '$type'.");
|
||||
$args = [$this->image, $file];
|
||||
if ($defQuality !== null) {
|
||||
$args[] = $quality === null ? $defQuality : max($min, min($max, $quality));
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
throw new ImageException(Helpers::getLastError() ?: 'Unknown error');
|
||||
}
|
||||
Callback::invokeSafe('image' . self::Formats[$type], $args, function (string $message) use ($file): void {
|
||||
if ($file !== null) {
|
||||
@unlink($file);
|
||||
}
|
||||
throw new ImageException($message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to undefined method.
|
||||
* @return mixed
|
||||
* @throws Nette\MemberAccessException
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
public function __call(string $name, array $args): mixed
|
||||
{
|
||||
$function = 'image' . $name;
|
||||
if (!function_exists($function)) {
|
||||
@@ -708,25 +759,13 @@ class Image
|
||||
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']
|
||||
) ?: imagecolorresolvealpha(
|
||||
$this->image,
|
||||
$value['red'],
|
||||
$value['green'],
|
||||
$value['blue'],
|
||||
$value['alpha']
|
||||
);
|
||||
} elseif ($value instanceof ImageColor || (is_array($value) && isset($value['red']))) {
|
||||
$args[$key] = $this->resolveColor($value);
|
||||
}
|
||||
}
|
||||
|
||||
$res = $function($this->image, ...$args);
|
||||
return $res instanceof \GdImage || (is_resource($res) && get_resource_type($res) === 'gd')
|
||||
return $res instanceof \GdImage
|
||||
? $this->setImageResource($res)
|
||||
: $res;
|
||||
}
|
||||
@@ -734,18 +773,15 @@ class Image
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
ob_start(function () {});
|
||||
ob_start(fn() => '');
|
||||
imagepng($this->image, null, 0);
|
||||
$this->setImageResource(imagecreatefromstring(ob_get_clean()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int|string $num in pixels or percent
|
||||
*/
|
||||
private static function isPercent(&$num): bool
|
||||
private static function isPercent(int|string &$num): bool
|
||||
{
|
||||
if (is_string($num) && substr($num, -1) === '%') {
|
||||
if (is_string($num) && str_ends_with($num, '%')) {
|
||||
$num = (float) substr($num, 0, -1);
|
||||
return true;
|
||||
} elseif (is_int($num) || $num === (string) (int) $num) {
|
||||
@@ -760,8 +796,23 @@ class Image
|
||||
/**
|
||||
* Prevents serialization.
|
||||
*/
|
||||
public function __sleep(): array
|
||||
public function __serialize(): array
|
||||
{
|
||||
throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.');
|
||||
}
|
||||
|
||||
|
||||
public function resolveColor(ImageColor|array $color): int
|
||||
{
|
||||
$color = $color instanceof ImageColor ? $color->toRGBA() : array_values($color);
|
||||
return imagecolorallocatealpha($this->image, ...$color) ?: imagecolorresolvealpha($this->image, ...$color);
|
||||
}
|
||||
|
||||
|
||||
private static function ensureExtension(): void
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
vendor/nette/utils/src/Utils/Json.php
vendored
45
vendor/nette/utils/src/Utils/Json.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function defined, is_int, json_decode, json_encode, json_last_error, json_last_error_msg;
|
||||
use const JSON_BIGINT_AS_STRING, JSON_FORCE_OBJECT, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_OBJECT_AS_ARRAY, JSON_PRESERVE_ZERO_FRACTION, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,22 +21,39 @@ final class Json
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/** @deprecated use Json::decode(..., forceArrays: true) */
|
||||
public const FORCE_ARRAY = JSON_OBJECT_AS_ARRAY;
|
||||
|
||||
/** @deprecated use Json::encode(..., pretty: true) */
|
||||
public const PRETTY = JSON_PRETTY_PRINT;
|
||||
|
||||
/** @deprecated use Json::encode(..., asciiSafe: true) */
|
||||
public const ESCAPE_UNICODE = 1 << 19;
|
||||
|
||||
|
||||
/**
|
||||
* Converts value to JSON format. The flag can be Json::PRETTY, which formats JSON for easier reading and clarity,
|
||||
* and Json::ESCAPE_UNICODE for ASCII output.
|
||||
* @param mixed $value
|
||||
* Converts value to JSON format. Use $pretty for easier reading and clarity, $asciiSafe for ASCII output
|
||||
* and $htmlSafe for HTML escaping, $forceObjects enforces the encoding of non-associateve arrays as objects.
|
||||
* @throws JsonException
|
||||
*/
|
||||
public static function encode($value, int $flags = 0): string
|
||||
public static function encode(
|
||||
mixed $value,
|
||||
bool|int $pretty = false,
|
||||
bool $asciiSafe = false,
|
||||
bool $htmlSafe = false,
|
||||
bool $forceObjects = false,
|
||||
): string
|
||||
{
|
||||
$flags = ($flags & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE)
|
||||
| JSON_UNESCAPED_SLASHES
|
||||
| ($flags & ~self::ESCAPE_UNICODE)
|
||||
if (is_int($pretty)) { // back compatibility
|
||||
$flags = ($pretty & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE) | ($pretty & ~self::ESCAPE_UNICODE);
|
||||
} else {
|
||||
$flags = ($asciiSafe ? 0 : JSON_UNESCAPED_UNICODE)
|
||||
| ($pretty ? JSON_PRETTY_PRINT : 0)
|
||||
| ($forceObjects ? JSON_FORCE_OBJECT : 0)
|
||||
| ($htmlSafe ? JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG : 0);
|
||||
}
|
||||
|
||||
$flags |= JSON_UNESCAPED_SLASHES
|
||||
| (defined('JSON_PRESERVE_ZERO_FRACTION') ? JSON_PRESERVE_ZERO_FRACTION : 0); // since PHP 5.6.6 & PECL JSON-C 1.3.7
|
||||
|
||||
$json = json_encode($value, $flags);
|
||||
@@ -47,13 +66,17 @@ final class Json
|
||||
|
||||
|
||||
/**
|
||||
* Parses JSON to PHP value. The flag can be Json::FORCE_ARRAY, which forces an array instead of an object as the return value.
|
||||
* @return mixed
|
||||
* Parses JSON to PHP value. The $forceArrays enforces the decoding of objects as arrays.
|
||||
* @throws JsonException
|
||||
*/
|
||||
public static function decode(string $json, int $flags = 0)
|
||||
public static function decode(string $json, bool|int $forceArrays = false): mixed
|
||||
{
|
||||
$value = json_decode($json, null, 512, $flags | JSON_BIGINT_AS_STRING);
|
||||
$flags = is_int($forceArrays) // back compatibility
|
||||
? $forceArrays
|
||||
: ($forceArrays ? JSON_OBJECT_AS_ARRAY : 0);
|
||||
$flags |= JSON_BIGINT_AS_STRING;
|
||||
|
||||
$value = json_decode($json, flags: $flags);
|
||||
if ($error = json_last_error()) {
|
||||
throw new JsonException(json_last_error_msg(), $error);
|
||||
}
|
||||
|
||||
25
vendor/nette/utils/src/Utils/ObjectHelpers.php
vendored
25
vendor/nette/utils/src/Utils/ObjectHelpers.php
vendored
@@ -11,10 +11,13 @@ namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Nette\MemberAccessException;
|
||||
use function array_filter, array_merge, array_pop, array_unique, get_class_methods, get_parent_class, implode, is_a, levenshtein, method_exists, preg_match_all, preg_replace, strlen, ucfirst;
|
||||
use const PREG_SET_ORDER, SORT_REGULAR;
|
||||
|
||||
|
||||
/**
|
||||
* Nette\SmartObject helpers.
|
||||
* @internal
|
||||
*/
|
||||
final class ObjectHelpers
|
||||
{
|
||||
@@ -28,8 +31,8 @@ final class ObjectHelpers
|
||||
{
|
||||
$rc = new \ReflectionClass($class);
|
||||
$hint = self::getSuggestion(array_merge(
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); }),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'),
|
||||
), $name);
|
||||
throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
|
||||
}
|
||||
@@ -43,8 +46,8 @@ final class ObjectHelpers
|
||||
{
|
||||
$rc = new \ReflectionClass($class);
|
||||
$hint = self::getSuggestion(array_merge(
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); }),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'),
|
||||
), $name);
|
||||
throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
|
||||
}
|
||||
@@ -76,7 +79,7 @@ final class ObjectHelpers
|
||||
$hint = self::getSuggestion(array_merge(
|
||||
get_class_methods($class),
|
||||
self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:static[ \t]+)?(?:\S+[ \t]+)??(\w+)\(~m'),
|
||||
$additionalMethods
|
||||
$additionalMethods,
|
||||
), $method);
|
||||
throw new MemberAccessException("Call to undefined method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.'));
|
||||
}
|
||||
@@ -107,8 +110,8 @@ final class ObjectHelpers
|
||||
|
||||
} else {
|
||||
$hint = self::getSuggestion(
|
||||
array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), function ($m) { return $m->isStatic(); }),
|
||||
$method
|
||||
array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), fn($m) => $m->isStatic()),
|
||||
$method,
|
||||
);
|
||||
throw new MemberAccessException("Call to undefined static method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.'));
|
||||
}
|
||||
@@ -133,7 +136,7 @@ final class ObjectHelpers
|
||||
'~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx',
|
||||
(string) $rc->getDocComment(),
|
||||
$matches,
|
||||
PREG_SET_ORDER
|
||||
PREG_SET_ORDER,
|
||||
);
|
||||
|
||||
$props = [];
|
||||
@@ -199,16 +202,16 @@ final class ObjectHelpers
|
||||
}
|
||||
} while ($rc = $rc->getParentClass());
|
||||
|
||||
return preg_match_all($pattern, implode($doc), $m) ? $m[1] : [];
|
||||
return preg_match_all($pattern, implode('', $doc), $m) ? $m[1] : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the public non-static property exists.
|
||||
* @return bool|string returns 'event' if the property exists and has event like name
|
||||
* Returns 'event' if the property exists and has event like name
|
||||
* @internal
|
||||
*/
|
||||
public static function hasProperty(string $class, string $name)
|
||||
public static function hasProperty(string $class, string $name): bool|string
|
||||
{
|
||||
static $cache;
|
||||
$prop = &$cache[$class][$name];
|
||||
|
||||
41
vendor/nette/utils/src/Utils/ObjectMixin.php
vendored
41
vendor/nette/utils/src/Utils/ObjectMixin.php
vendored
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Nette\Object behaviour mixin.
|
||||
* @deprecated
|
||||
*/
|
||||
final class ObjectMixin
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/** @deprecated use ObjectHelpers::getSuggestion() */
|
||||
public static function getSuggestion(array $possibilities, string $value): ?string
|
||||
{
|
||||
trigger_error(__METHOD__ . '() has been renamed to Nette\Utils\ObjectHelpers::getSuggestion()', E_USER_DEPRECATED);
|
||||
return ObjectHelpers::getSuggestion($possibilities, $value);
|
||||
}
|
||||
|
||||
|
||||
public static function setExtensionMethod(): void
|
||||
{
|
||||
trigger_error('Class Nette\Utils\ObjectMixin is deprecated', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
|
||||
public static function getExtensionMethod(): void
|
||||
{
|
||||
trigger_error('Class Nette\Utils\ObjectMixin is deprecated', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
51
vendor/nette/utils/src/Utils/Paginator.php
vendored
51
vendor/nette/utils/src/Utils/Paginator.php
vendored
@@ -18,40 +18,37 @@ use Nette;
|
||||
* @property int $page
|
||||
* @property-read int $firstPage
|
||||
* @property-read int|null $lastPage
|
||||
* @property-read int $firstItemOnPage
|
||||
* @property-read int $lastItemOnPage
|
||||
* @property-read int<0,max> $firstItemOnPage
|
||||
* @property-read int<0,max> $lastItemOnPage
|
||||
* @property int $base
|
||||
* @property-read bool $first
|
||||
* @property-read bool $last
|
||||
* @property-read int|null $pageCount
|
||||
* @property int $itemsPerPage
|
||||
* @property int|null $itemCount
|
||||
* @property-read int $offset
|
||||
* @property-read int|null $countdownOffset
|
||||
* @property-read int $length
|
||||
* @property-read int<0,max>|null $pageCount
|
||||
* @property positive-int $itemsPerPage
|
||||
* @property int<0,max>|null $itemCount
|
||||
* @property-read int<0,max> $offset
|
||||
* @property-read int<0,max>|null $countdownOffset
|
||||
* @property-read int<0,max> $length
|
||||
*/
|
||||
class Paginator
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var int */
|
||||
private $base = 1;
|
||||
private int $base = 1;
|
||||
|
||||
/** @var int */
|
||||
private $itemsPerPage = 1;
|
||||
/** @var positive-int */
|
||||
private int $itemsPerPage = 1;
|
||||
|
||||
/** @var int */
|
||||
private $page = 1;
|
||||
private int $page = 1;
|
||||
|
||||
/** @var int|null */
|
||||
private $itemCount;
|
||||
/** @var int<0, max>|null */
|
||||
private ?int $itemCount = null;
|
||||
|
||||
|
||||
/**
|
||||
* Sets current page number.
|
||||
* @return static
|
||||
*/
|
||||
public function setPage(int $page)
|
||||
public function setPage(int $page): static
|
||||
{
|
||||
$this->page = $page;
|
||||
return $this;
|
||||
@@ -89,6 +86,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the sequence number of the first element on the page
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getFirstItemOnPage(): int
|
||||
{
|
||||
@@ -100,6 +98,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the sequence number of the last element on the page
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getLastItemOnPage(): int
|
||||
{
|
||||
@@ -109,9 +108,8 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Sets first page (base) number.
|
||||
* @return static
|
||||
*/
|
||||
public function setBase(int $base)
|
||||
public function setBase(int $base): static
|
||||
{
|
||||
$this->base = $base;
|
||||
return $this;
|
||||
@@ -129,6 +127,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns zero-based page number.
|
||||
* @return int<0, max>
|
||||
*/
|
||||
protected function getPageIndex(): int
|
||||
{
|
||||
@@ -161,6 +160,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the total number of pages.
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getPageCount(): ?int
|
||||
{
|
||||
@@ -172,9 +172,8 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Sets the number of items to display on a single page.
|
||||
* @return static
|
||||
*/
|
||||
public function setItemsPerPage(int $itemsPerPage)
|
||||
public function setItemsPerPage(int $itemsPerPage): static
|
||||
{
|
||||
$this->itemsPerPage = max(1, $itemsPerPage);
|
||||
return $this;
|
||||
@@ -183,6 +182,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the number of items to display on a single page.
|
||||
* @return positive-int
|
||||
*/
|
||||
public function getItemsPerPage(): int
|
||||
{
|
||||
@@ -192,9 +192,8 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Sets the total number of items.
|
||||
* @return static
|
||||
*/
|
||||
public function setItemCount(?int $itemCount = null)
|
||||
public function setItemCount(?int $itemCount = null): static
|
||||
{
|
||||
$this->itemCount = $itemCount === null ? null : max(0, $itemCount);
|
||||
return $this;
|
||||
@@ -203,6 +202,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the total number of items.
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getItemCount(): ?int
|
||||
{
|
||||
@@ -212,6 +212,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the absolute index of the first item on current page.
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getOffset(): int
|
||||
{
|
||||
@@ -221,6 +222,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the absolute index of the first item on current page in countdown paging.
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getCountdownOffset(): ?int
|
||||
{
|
||||
@@ -232,6 +234,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the number of items on current page.
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getLength(): int
|
||||
{
|
||||
|
||||
14
vendor/nette/utils/src/Utils/Random.php
vendored
14
vendor/nette/utils/src/Utils/Random.php
vendored
@@ -10,6 +10,9 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Random\Randomizer;
|
||||
use function strlen;
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,15 +28,20 @@ final class Random
|
||||
*/
|
||||
public static function generate(int $length = 10, string $charlist = '0-9a-z'): string
|
||||
{
|
||||
$charlist = count_chars(preg_replace_callback('#.-.#', function (array $m): string {
|
||||
return implode('', range($m[0][0], $m[0][2]));
|
||||
}, $charlist), 3);
|
||||
$charlist = preg_replace_callback(
|
||||
'#.-.#',
|
||||
fn(array $m): string => implode('', range($m[0][0], $m[0][2])),
|
||||
$charlist,
|
||||
);
|
||||
$charlist = count_chars($charlist, mode: 3);
|
||||
$chLen = strlen($charlist);
|
||||
|
||||
if ($length < 1) {
|
||||
throw new Nette\InvalidArgumentException('Length must be greater than zero.');
|
||||
} elseif ($chLen < 2) {
|
||||
throw new Nette\InvalidArgumentException('Character list must contain at least two chars.');
|
||||
} elseif (PHP_VERSION_ID >= 80300) {
|
||||
return (new Randomizer)->getBytesFromString($charlist, $length);
|
||||
}
|
||||
|
||||
$res = '';
|
||||
|
||||
156
vendor/nette/utils/src/Utils/Reflection.php
vendored
156
vendor/nette/utils/src/Utils/Reflection.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function constant, current, defined, end, explode, file_get_contents, implode, ltrim, next, ord, strrchr, strtolower, substr;
|
||||
use const T_AS, T_CLASS, T_COMMENT, T_CURLY_OPEN, T_DOC_COMMENT, T_DOLLAR_OPEN_CURLY_BRACES, T_ENUM, T_INTERFACE, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAMESPACE, T_NS_SEPARATOR, T_STRING, T_TRAIT, T_USE, T_WHITESPACE, TOKEN_PARSE;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,125 +21,21 @@ final class Reflection
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
private const BuiltinTypes = [
|
||||
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
|
||||
'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1,
|
||||
'never' => 1,
|
||||
];
|
||||
|
||||
private const ClassKeywords = [
|
||||
'self' => 1, 'parent' => 1, 'static' => 1,
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is PHP built-in type. Otherwise, it is the class name.
|
||||
*/
|
||||
/** @deprecated use Nette\Utils\Validators::isBuiltinType() */
|
||||
public static function isBuiltinType(string $type): bool
|
||||
{
|
||||
return isset(self::BuiltinTypes[strtolower($type)]);
|
||||
return Validators::isBuiltinType($type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is special class name self/parent/static.
|
||||
*/
|
||||
#[\Deprecated('use Nette\Utils\Validators::isClassKeyword()')]
|
||||
public static function isClassKeyword(string $name): bool
|
||||
{
|
||||
return isset(self::ClassKeywords[strtolower($name)]);
|
||||
return Validators::isClassKeyword($name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of return value of given function or method and normalizes `self`, `static`, and `parent` to actual class names.
|
||||
* If the function does not have a return type, it returns null.
|
||||
* If the function has union or intersection type, it throws Nette\InvalidStateException.
|
||||
*/
|
||||
public static function getReturnType(\ReflectionFunctionAbstract $func): ?string
|
||||
{
|
||||
$type = $func->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $func instanceof \ReflectionMethod ? $func->getTentativeReturnType() : null);
|
||||
return self::getType($func, $type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getReturnTypes(\ReflectionFunctionAbstract $func): array
|
||||
{
|
||||
$type = Type::fromReflection($func);
|
||||
return $type ? $type->getNames() : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of given parameter and normalizes `self` and `parent` to the actual class names.
|
||||
* If the parameter does not have a type, it returns null.
|
||||
* If the parameter has union or intersection type, it throws Nette\InvalidStateException.
|
||||
*/
|
||||
public static function getParameterType(\ReflectionParameter $param): ?string
|
||||
{
|
||||
return self::getType($param, $param->getType());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getParameterTypes(\ReflectionParameter $param): array
|
||||
{
|
||||
$type = Type::fromReflection($param);
|
||||
return $type ? $type->getNames() : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of given property and normalizes `self` and `parent` to the actual class names.
|
||||
* If the property does not have a type, it returns null.
|
||||
* If the property has union or intersection type, it throws Nette\InvalidStateException.
|
||||
*/
|
||||
public static function getPropertyType(\ReflectionProperty $prop): ?string
|
||||
{
|
||||
return self::getType($prop, PHP_VERSION_ID >= 70400 ? $prop->getType() : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getPropertyTypes(\ReflectionProperty $prop): array
|
||||
{
|
||||
$type = Type::fromReflection($prop);
|
||||
return $type ? $type->getNames() : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \ReflectionFunction|\ReflectionMethod|\ReflectionParameter|\ReflectionProperty $reflection
|
||||
*/
|
||||
private static function getType($reflection, ?\ReflectionType $type): ?string
|
||||
{
|
||||
if ($type === null) {
|
||||
return null;
|
||||
|
||||
} elseif ($type instanceof \ReflectionNamedType) {
|
||||
return Type::resolve($type->getName(), $reflection);
|
||||
|
||||
} elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
|
||||
throw new Nette\InvalidStateException('The ' . self::toString($reflection) . ' is not expected to have a union or intersection type.');
|
||||
|
||||
} else {
|
||||
throw new Nette\InvalidStateException('Unexpected type of ' . self::toString($reflection));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default value of parameter. If it is a constant, it returns its value.
|
||||
* @return mixed
|
||||
* @throws \ReflectionException If the parameter does not have a default value or the constant cannot be resolved
|
||||
*/
|
||||
public static function getParameterDefaultValue(\ReflectionParameter $param)
|
||||
public static function getParameterDefaultValue(\ReflectionParameter $param): mixed
|
||||
{
|
||||
if ($param->isDefaultValueConstant()) {
|
||||
$const = $orig = $param->getDefaultValueConstantName();
|
||||
@@ -203,7 +101,7 @@ final class Reflection
|
||||
|
||||
$hash = [$method->getFileName(), $method->getStartLine(), $method->getEndLine()];
|
||||
if (($alias = $decl->getTraitAliases()[$method->name] ?? null)
|
||||
&& ($m = new \ReflectionMethod($alias))
|
||||
&& ($m = new \ReflectionMethod(...explode('::', $alias, 2)))
|
||||
&& $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()]
|
||||
) {
|
||||
return self::getMethodDeclaringMethod($m);
|
||||
@@ -228,7 +126,7 @@ final class Reflection
|
||||
public static function areCommentsAvailable(): bool
|
||||
{
|
||||
static $res;
|
||||
return $res ?? $res = (bool) (new \ReflectionMethod(__METHOD__))->getDocComment();
|
||||
return $res ?? $res = (bool) (new \ReflectionMethod(self::class, __FUNCTION__))->getDocComment();
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +137,7 @@ final class Reflection
|
||||
} elseif ($ref instanceof \ReflectionMethod) {
|
||||
return $ref->getDeclaringClass()->name . '::' . $ref->name . '()';
|
||||
} elseif ($ref instanceof \ReflectionFunction) {
|
||||
return $ref->name . '()';
|
||||
return $ref->isAnonymous() ? '{closure}()' : $ref->name . '()';
|
||||
} elseif ($ref instanceof \ReflectionProperty) {
|
||||
return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name;
|
||||
} elseif ($ref instanceof \ReflectionParameter) {
|
||||
@@ -261,7 +159,7 @@ final class Reflection
|
||||
if (empty($name)) {
|
||||
throw new Nette\InvalidArgumentException('Class name must not be empty.');
|
||||
|
||||
} elseif (isset(self::BuiltinTypes[$lower])) {
|
||||
} elseif (Validators::isBuiltinType($lower)) {
|
||||
return $lower;
|
||||
|
||||
} elseif ($lower === 'self' || $lower === 'static') {
|
||||
@@ -291,7 +189,7 @@ final class Reflection
|
||||
}
|
||||
|
||||
|
||||
/** @return array of [alias => class] */
|
||||
/** @return array<string, class-string> of [alias => class] */
|
||||
public static function getUseStatements(\ReflectionClass $class): array
|
||||
{
|
||||
if ($class->isAnonymous()) {
|
||||
@@ -318,22 +216,21 @@ final class Reflection
|
||||
private static function parseUseStatements(string $code, ?string $forClass = null): array
|
||||
{
|
||||
try {
|
||||
$tokens = token_get_all($code, TOKEN_PARSE);
|
||||
$tokens = \PhpToken::tokenize($code, TOKEN_PARSE);
|
||||
} catch (\ParseError $e) {
|
||||
trigger_error($e->getMessage(), E_USER_NOTICE);
|
||||
$tokens = [];
|
||||
}
|
||||
|
||||
$namespace = $class = $classLevel = $level = null;
|
||||
$namespace = $class = null;
|
||||
$classLevel = $level = 0;
|
||||
$res = $uses = [];
|
||||
|
||||
$nameTokens = PHP_VERSION_ID < 80000
|
||||
? [T_STRING, T_NS_SEPARATOR]
|
||||
: [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED];
|
||||
$nameTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED];
|
||||
|
||||
while ($token = current($tokens)) {
|
||||
next($tokens);
|
||||
switch (is_array($token) ? $token[0] : $token) {
|
||||
switch ($token->id) {
|
||||
case T_NAMESPACE:
|
||||
$namespace = ltrim(self::fetch($tokens, $nameTokens) . '\\', '\\');
|
||||
$uses = [];
|
||||
@@ -342,9 +239,7 @@ final class Reflection
|
||||
case T_CLASS:
|
||||
case T_INTERFACE:
|
||||
case T_TRAIT:
|
||||
case PHP_VERSION_ID < 80100
|
||||
? T_CLASS
|
||||
: T_ENUM:
|
||||
case T_ENUM:
|
||||
if ($name = self::fetch($tokens, T_STRING)) {
|
||||
$class = $namespace . $name;
|
||||
$classLevel = $level + 1;
|
||||
@@ -389,13 +284,13 @@ final class Reflection
|
||||
|
||||
case T_CURLY_OPEN:
|
||||
case T_DOLLAR_OPEN_CURLY_BRACES:
|
||||
case '{':
|
||||
case ord('{'):
|
||||
$level++;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
case ord('}'):
|
||||
if ($level === $classLevel) {
|
||||
$class = $classLevel = null;
|
||||
$class = $classLevel = 0;
|
||||
}
|
||||
|
||||
$level--;
|
||||
@@ -406,14 +301,13 @@ final class Reflection
|
||||
}
|
||||
|
||||
|
||||
private static function fetch(array &$tokens, $take): ?string
|
||||
private static function fetch(array &$tokens, string|int|array $take): ?string
|
||||
{
|
||||
$res = null;
|
||||
while ($token = current($tokens)) {
|
||||
[$token, $s] = is_array($token) ? $token : [$token, $token];
|
||||
if (in_array($token, (array) $take, true)) {
|
||||
$res .= $s;
|
||||
} elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], true)) {
|
||||
if ($token->is($take)) {
|
||||
$res .= $token->text;
|
||||
} elseif (!$token->is([T_DOC_COMMENT, T_WHITESPACE, T_COMMENT])) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
292
vendor/nette/utils/src/Utils/Strings.php
vendored
292
vendor/nette/utils/src/Utils/Strings.php
vendored
@@ -9,8 +9,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function is_array, is_object, strlen;
|
||||
use function array_keys, array_map, array_shift, array_values, bin2hex, class_exists, defined, extension_loaded, function_exists, htmlspecialchars, htmlspecialchars_decode, iconv, iconv_strlen, iconv_substr, implode, in_array, is_array, is_callable, is_int, is_object, is_string, key, max, mb_convert_case, mb_strlen, mb_strtolower, mb_strtoupper, mb_substr, pack, preg_last_error, preg_last_error_msg, preg_quote, preg_replace, str_contains, str_ends_with, str_repeat, str_replace, str_starts_with, strlen, strpos, strrev, strrpos, strtolower, strtoupper, strtr, substr, trim, unpack, utf8_decode;
|
||||
use const ENT_IGNORE, ENT_NOQUOTES, ICONV_IMPL, MB_CASE_TITLE, PHP_EOL, PREG_OFFSET_CAPTURE, PREG_PATTERN_ORDER, PREG_SET_ORDER, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY, PREG_SPLIT_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,11 +22,14 @@ class Strings
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
public const TRIM_CHARACTERS = " \t\n\r\0\x0B\u{A0}";
|
||||
public const TrimCharacters = " \t\n\r\0\x0B\u{A0}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{200B}\u{2028}\u{3000}";
|
||||
|
||||
#[\Deprecated('use Strings::TrimCharacters')]
|
||||
public const TRIM_CHARACTERS = self::TrimCharacters;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the string is valid in UTF-8 encoding.
|
||||
* @deprecated use Nette\Utils\Validators::isUnicode()
|
||||
*/
|
||||
public static function checkEncoding(string $s): bool
|
||||
{
|
||||
@@ -59,29 +64,47 @@ class Strings
|
||||
|
||||
|
||||
/**
|
||||
* Starts the $haystack string with the prefix $needle?
|
||||
* Returns a code point of specific character in UTF-8 (number in range 0x0000..D7FF or 0xE000..10FFFF).
|
||||
*/
|
||||
public static function ord(string $c): int
|
||||
{
|
||||
if (!extension_loaded('iconv')) {
|
||||
throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.');
|
||||
}
|
||||
|
||||
$tmp = iconv('UTF-8', 'UTF-32BE//IGNORE', $c);
|
||||
if (!$tmp) {
|
||||
throw new Nette\InvalidArgumentException('Invalid UTF-8 character "' . ($c === '' ? '' : '\x' . strtoupper(bin2hex($c))) . '".');
|
||||
}
|
||||
|
||||
return unpack('N', $tmp)[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated use str_starts_with()
|
||||
*/
|
||||
public static function startsWith(string $haystack, string $needle): bool
|
||||
{
|
||||
return strncmp($haystack, $needle, strlen($needle)) === 0;
|
||||
return str_starts_with($haystack, $needle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ends the $haystack string with the suffix $needle?
|
||||
* @deprecated use str_ends_with()
|
||||
*/
|
||||
public static function endsWith(string $haystack, string $needle): bool
|
||||
{
|
||||
return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
|
||||
return str_ends_with($haystack, $needle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does $haystack contain $needle?
|
||||
* @deprecated use str_contains()
|
||||
*/
|
||||
public static function contains(string $haystack, string $needle): bool
|
||||
{
|
||||
return strpos($haystack, $needle) !== false;
|
||||
return str_contains($haystack, $needle);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +139,7 @@ class Strings
|
||||
$s = $n;
|
||||
}
|
||||
|
||||
$s = self::normalizeNewLines($s);
|
||||
$s = self::unixNewLines($s);
|
||||
|
||||
// remove control characters; leave \t + \n
|
||||
$s = self::pcre('preg_replace', ['#[\x00-\x08\x0B-\x1F\x7F-\x9F]+#u', '', $s]);
|
||||
@@ -131,12 +154,30 @@ class Strings
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Standardize line endings to unix-like.
|
||||
*/
|
||||
/** @deprecated use Strings::unixNewLines() */
|
||||
public static function normalizeNewLines(string $s): string
|
||||
{
|
||||
return str_replace(["\r\n", "\r"], "\n", $s);
|
||||
return self::unixNewLines($s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts line endings to \n used on Unix-like systems.
|
||||
* Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator.
|
||||
*/
|
||||
public static function unixNewLines(string $s): string
|
||||
{
|
||||
return preg_replace("~\r\n?|\u{2028}|\u{2029}~", "\n", $s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts line endings to platform-specific, i.e. \r\n on Windows and \n elsewhere.
|
||||
* Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator.
|
||||
*/
|
||||
public static function platformNewLines(string $s): string
|
||||
{
|
||||
return preg_replace("~\r\n?|\n|\u{2028}|\u{2029}~", PHP_EOL, $s);
|
||||
}
|
||||
|
||||
|
||||
@@ -145,17 +186,12 @@ class Strings
|
||||
*/
|
||||
public static function toAscii(string $s): string
|
||||
{
|
||||
$iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
|
||||
static $transliterator = null;
|
||||
if ($transliterator === null) {
|
||||
if (class_exists('Transliterator', false)) {
|
||||
$transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
|
||||
} else {
|
||||
trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE);
|
||||
$transliterator = false;
|
||||
}
|
||||
if (!extension_loaded('intl')) {
|
||||
throw new Nette\NotSupportedException(__METHOD__ . '() requires INTL extension that is not loaded.');
|
||||
}
|
||||
|
||||
$iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
|
||||
|
||||
// remove control characters and check UTF-8 validity
|
||||
$s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]);
|
||||
|
||||
@@ -165,39 +201,15 @@ class Strings
|
||||
$s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔
|
||||
}
|
||||
|
||||
if ($transliterator) {
|
||||
$s = $transliterator->transliterate($s);
|
||||
// use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
|
||||
if ($iconv === 'glibc') {
|
||||
$s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
$s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
|
||||
} elseif ($iconv === 'libiconv') {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
} else { // null or 'unknown' (#216)
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
|
||||
}
|
||||
} elseif ($iconv === 'glibc' || $iconv === 'libiconv') {
|
||||
// temporarily hide these characters to distinguish them from the garbage that iconv creates
|
||||
$s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
|
||||
if ($iconv === 'glibc') {
|
||||
// glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved
|
||||
$s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s);
|
||||
$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 = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]);
|
||||
} else {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
}
|
||||
|
||||
// remove garbage that iconv creates during transliteration (eg Ý -> Y')
|
||||
$s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s);
|
||||
// restore temporarily hidden characters
|
||||
$s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
|
||||
} else {
|
||||
$s = \Transliterator::create('Any-Latin; Latin-ASCII')->transliterate($s);
|
||||
// use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
|
||||
if ($iconv === 'glibc') {
|
||||
$s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
$s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
|
||||
} elseif ($iconv === 'libiconv') {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
} else { // null or 'unknown' (#216)
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
|
||||
}
|
||||
|
||||
@@ -357,16 +369,18 @@ class Strings
|
||||
*/
|
||||
public static function length(string $s): int
|
||||
{
|
||||
return function_exists('mb_strlen')
|
||||
? mb_strlen($s, 'UTF-8')
|
||||
: strlen(utf8_decode($s));
|
||||
return match (true) {
|
||||
extension_loaded('mbstring') => mb_strlen($s, 'UTF-8'),
|
||||
extension_loaded('iconv') => iconv_strlen($s, 'UTF-8'),
|
||||
default => strlen(@utf8_decode($s)), // deprecated
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string.
|
||||
*/
|
||||
public static function trim(string $s, string $charlist = self::TRIM_CHARACTERS): string
|
||||
public static function trim(string $s, string $charlist = self::TrimCharacters): string
|
||||
{
|
||||
$charlist = preg_quote($charlist, '#');
|
||||
return self::replace($s, '#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', '');
|
||||
@@ -375,6 +389,7 @@ class Strings
|
||||
|
||||
/**
|
||||
* Pads a UTF-8 string to given length by prepending the $pad string to the beginning.
|
||||
* @param non-empty-string $pad
|
||||
*/
|
||||
public static function padLeft(string $s, int $length, string $pad = ' '): string
|
||||
{
|
||||
@@ -386,6 +401,7 @@ class Strings
|
||||
|
||||
/**
|
||||
* Pads UTF-8 string to given length by appending the $pad string to the end.
|
||||
* @param non-empty-string $pad
|
||||
*/
|
||||
public static function padRight(string $s, int $length, string $pad = ' '): string
|
||||
{
|
||||
@@ -482,73 +498,187 @@ class Strings
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string into array by the regular expression. Parenthesized expression in the delimiter are captured.
|
||||
* Parameter $flags can be any combination of PREG_SPLIT_NO_EMPTY and PREG_OFFSET_CAPTURE flags.
|
||||
* Divides the string into arrays according to the regular expression. Expressions in parentheses will be captured and returned as well.
|
||||
*/
|
||||
public static function split(string $subject, string $pattern, int $flags = 0): array
|
||||
public static function split(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $captureOffset = false,
|
||||
bool $skipEmpty = false,
|
||||
int $limit = -1,
|
||||
bool $utf8 = false,
|
||||
): array
|
||||
{
|
||||
return self::pcre('preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]);
|
||||
$flags = is_int($captureOffset) // back compatibility
|
||||
? $captureOffset
|
||||
: ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0);
|
||||
|
||||
$pattern .= $utf8 ? 'u' : '';
|
||||
$m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]);
|
||||
return $utf8 && $captureOffset
|
||||
? self::bytesToChars($subject, [$m])[0]
|
||||
: $m;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if given string matches a regular expression pattern and returns an array with first found match and each subpattern.
|
||||
* Parameter $flags can be any combination of PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL flags.
|
||||
* Searches the string for the part matching the regular expression and returns
|
||||
* an array with the found expression and individual subexpressions, or `null`.
|
||||
*/
|
||||
public static function match(string $subject, string $pattern, int $flags = 0, int $offset = 0): ?array
|
||||
public static function match(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $captureOffset = false,
|
||||
int $offset = 0,
|
||||
bool $unmatchedAsNull = false,
|
||||
bool $utf8 = false,
|
||||
): ?array
|
||||
{
|
||||
if ($offset > strlen($subject)) {
|
||||
return null;
|
||||
$flags = is_int($captureOffset) // back compatibility
|
||||
? $captureOffset
|
||||
: ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
|
||||
|
||||
if ($utf8) {
|
||||
$offset = strlen(self::substring($subject, 0, $offset));
|
||||
$pattern .= 'u';
|
||||
}
|
||||
|
||||
return self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])
|
||||
? $m
|
||||
: null;
|
||||
if ($offset > strlen($subject)) {
|
||||
return null;
|
||||
} elseif (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) {
|
||||
return null;
|
||||
} elseif ($utf8 && $captureOffset) {
|
||||
return self::bytesToChars($subject, [$m])[0];
|
||||
} else {
|
||||
return $m;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all occurrences matching regular expression pattern and returns a two-dimensional array. Result is array of matches (ie uses by default PREG_SET_ORDER).
|
||||
* Parameter $flags can be any combination of PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL and PREG_PATTERN_ORDER flags.
|
||||
* Searches the string for all occurrences matching the regular expression and
|
||||
* returns an array of arrays containing the found expression and each subexpression.
|
||||
* @return ($lazy is true ? \Generator<int, array> : array[])
|
||||
*/
|
||||
public static function matchAll(string $subject, string $pattern, int $flags = 0, int $offset = 0): array
|
||||
public static function matchAll(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $captureOffset = false,
|
||||
int $offset = 0,
|
||||
bool $unmatchedAsNull = false,
|
||||
bool $patternOrder = false,
|
||||
bool $utf8 = false,
|
||||
bool $lazy = false,
|
||||
): array|\Generator
|
||||
{
|
||||
if ($utf8) {
|
||||
$offset = strlen(self::substring($subject, 0, $offset));
|
||||
$pattern .= 'u';
|
||||
}
|
||||
|
||||
if ($lazy) {
|
||||
$flags = PREG_OFFSET_CAPTURE | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
|
||||
return (function () use ($utf8, $captureOffset, $flags, $subject, $pattern, $offset) {
|
||||
$counter = 0;
|
||||
while (
|
||||
$offset <= strlen($subject) - ($counter ? 1 : 0)
|
||||
&& self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])
|
||||
) {
|
||||
$offset = $m[0][1] + max(1, strlen($m[0][0]));
|
||||
if (!$captureOffset) {
|
||||
$m = array_map(fn($item) => $item[0], $m);
|
||||
} elseif ($utf8) {
|
||||
$m = self::bytesToChars($subject, [$m])[0];
|
||||
}
|
||||
yield $counter++ => $m;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
if ($offset > strlen($subject)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flags = is_int($captureOffset) // back compatibility
|
||||
? $captureOffset
|
||||
: ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0);
|
||||
|
||||
self::pcre('preg_match_all', [
|
||||
$pattern, $subject, &$m,
|
||||
($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER),
|
||||
$offset,
|
||||
]);
|
||||
return $m;
|
||||
return $utf8 && $captureOffset
|
||||
? self::bytesToChars($subject, $m)
|
||||
: $m;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`.
|
||||
* @param string|array $pattern
|
||||
* @param string|callable $replacement
|
||||
*/
|
||||
public static function replace(string $subject, $pattern, $replacement = '', int $limit = -1): string
|
||||
public static function replace(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string|array $pattern,
|
||||
string|callable $replacement = '',
|
||||
int $limit = -1,
|
||||
bool $captureOffset = false,
|
||||
bool $unmatchedAsNull = false,
|
||||
bool $utf8 = false,
|
||||
): string
|
||||
{
|
||||
if (is_object($replacement) || is_array($replacement)) {
|
||||
if (!is_callable($replacement, false, $textual)) {
|
||||
throw new Nette\InvalidStateException("Callback '$textual' is not callable.");
|
||||
}
|
||||
|
||||
return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit]);
|
||||
$flags = ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
|
||||
if ($utf8) {
|
||||
$pattern .= 'u';
|
||||
if ($captureOffset) {
|
||||
$replacement = fn($m) => $replacement(self::bytesToChars($subject, [$m])[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]);
|
||||
|
||||
} elseif (is_array($pattern) && is_string(key($pattern))) {
|
||||
$replacement = array_values($pattern);
|
||||
$pattern = array_keys($pattern);
|
||||
}
|
||||
|
||||
if ($utf8) {
|
||||
$pattern = array_map(fn($item) => $item . 'u', (array) $pattern);
|
||||
}
|
||||
|
||||
return self::pcre('preg_replace', [$pattern, $replacement, $subject, $limit]);
|
||||
}
|
||||
|
||||
|
||||
private static function bytesToChars(string $s, array $groups): array
|
||||
{
|
||||
$lastBytes = $lastChars = 0;
|
||||
foreach ($groups as &$matches) {
|
||||
foreach ($matches as &$match) {
|
||||
if ($match[1] > $lastBytes) {
|
||||
$lastChars += self::length(substr($s, $lastBytes, $match[1] - $lastBytes));
|
||||
} elseif ($match[1] < $lastBytes) {
|
||||
$lastChars -= self::length(substr($s, $match[1], $lastBytes - $match[1]));
|
||||
}
|
||||
|
||||
$lastBytes = $match[1];
|
||||
$match[1] = $lastChars;
|
||||
}
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
public static function pcre(string $func, array $args)
|
||||
{
|
||||
@@ -560,7 +690,7 @@ class Strings
|
||||
if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars
|
||||
&& ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true))
|
||||
) {
|
||||
throw new RegexpException((RegexpException::MESSAGES[$code] ?? 'Unknown error')
|
||||
throw new RegexpException(preg_last_error_msg()
|
||||
. ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code);
|
||||
}
|
||||
|
||||
|
||||
232
vendor/nette/utils/src/Utils/Type.php
vendored
232
vendor/nette/utils/src/Utils/Type.php
vendored
@@ -10,58 +10,52 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_map, array_search, array_splice, count, explode, implode, is_a, is_resource, is_string, strcasecmp, strtolower, substr, trim;
|
||||
|
||||
|
||||
/**
|
||||
* PHP type reflection.
|
||||
*/
|
||||
final class Type
|
||||
final readonly class Type
|
||||
{
|
||||
/** @var array */
|
||||
private $types;
|
||||
|
||||
/** @var bool */
|
||||
private $single;
|
||||
|
||||
/** @var string |, & */
|
||||
private $kind;
|
||||
/** @var array<int, string|self> */
|
||||
private array $types;
|
||||
private bool $simple;
|
||||
private string $kind; // | &
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Type object based on reflection. Resolves self, static and parent to the actual class name.
|
||||
* If the subject has no type, it returns null.
|
||||
* @param \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection
|
||||
*/
|
||||
public static function fromReflection($reflection): ?self
|
||||
public static function fromReflection(
|
||||
\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection,
|
||||
): ?self
|
||||
{
|
||||
if ($reflection instanceof \ReflectionProperty && PHP_VERSION_ID < 70400) {
|
||||
return null;
|
||||
} elseif ($reflection instanceof \ReflectionMethod) {
|
||||
$type = $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 ? $reflection->getTentativeReturnType() : null);
|
||||
} else {
|
||||
$type = $reflection instanceof \ReflectionFunctionAbstract
|
||||
? $reflection->getReturnType()
|
||||
: $reflection->getType();
|
||||
}
|
||||
$type = $reflection instanceof \ReflectionFunctionAbstract
|
||||
? $reflection->getReturnType() ?? ($reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
|
||||
: $reflection->getType();
|
||||
|
||||
if ($type === null) {
|
||||
return null;
|
||||
return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null;
|
||||
}
|
||||
|
||||
} elseif ($type instanceof \ReflectionNamedType) {
|
||||
$name = self::resolve($type->getName(), $reflection);
|
||||
return new self($type->allowsNull() && $type->getName() !== 'mixed' ? [$name, 'null'] : [$name]);
|
||||
|
||||
private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject): self|string
|
||||
{
|
||||
if ($type instanceof \ReflectionNamedType) {
|
||||
$name = self::resolve($type->getName(), $of);
|
||||
return $asObject
|
||||
? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name])
|
||||
: $name;
|
||||
|
||||
} elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
|
||||
return new self(
|
||||
array_map(
|
||||
function ($t) use ($reflection) { return self::resolve($t->getName(), $reflection); },
|
||||
$type->getTypes()
|
||||
),
|
||||
$type instanceof \ReflectionUnionType ? '|' : '&'
|
||||
array_map(fn($t) => self::fromReflectionType($t, $of, asObject: false), $type->getTypes()),
|
||||
$type instanceof \ReflectionUnionType ? '|' : '&',
|
||||
);
|
||||
|
||||
} else {
|
||||
throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($reflection));
|
||||
throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,37 +65,60 @@ final class Type
|
||||
*/
|
||||
public static function fromString(string $type): self
|
||||
{
|
||||
if (!preg_match('#(?:
|
||||
\?([\w\\\\]+)|
|
||||
[\w\\\\]+ (?: (&[\w\\\\]+)* | (\|[\w\\\\]+)* )
|
||||
)()$#xAD', $type, $m)) {
|
||||
if (!Validators::isTypeDeclaration($type)) {
|
||||
throw new Nette\InvalidArgumentException("Invalid type '$type'.");
|
||||
}
|
||||
|
||||
[, $nType, $iType] = $m;
|
||||
if ($nType) {
|
||||
return new self([$nType, 'null']);
|
||||
} elseif ($iType) {
|
||||
return new self(explode('&', $type), '&');
|
||||
} else {
|
||||
return new self(explode('|', $type));
|
||||
if ($type[0] === '?') {
|
||||
return new self([substr($type, 1), 'null']);
|
||||
}
|
||||
|
||||
$unions = [];
|
||||
foreach (explode('|', $type) as $part) {
|
||||
$part = explode('&', trim($part, '()'));
|
||||
$unions[] = count($part) === 1 ? $part[0] : new self($part, '&');
|
||||
}
|
||||
|
||||
return count($unions) === 1 && $unions[0] instanceof self
|
||||
? $unions[0]
|
||||
: new self($unions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Type object based on the actual type of value.
|
||||
*/
|
||||
public static function fromValue(mixed $value): self
|
||||
{
|
||||
$type = get_debug_type($value);
|
||||
if (is_resource($value)) {
|
||||
$type = 'mixed';
|
||||
} elseif (str_ends_with($type, '@anonymous')) {
|
||||
$parent = substr($type, 0, -10);
|
||||
$type = $parent === 'class' ? 'object' : $parent;
|
||||
}
|
||||
|
||||
return new self([$type]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves 'self', 'static' and 'parent' to the actual class name.
|
||||
* @param \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection
|
||||
*/
|
||||
public static function resolve(string $type, $reflection): string
|
||||
public static function resolve(
|
||||
string $type,
|
||||
\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of,
|
||||
): string
|
||||
{
|
||||
$lower = strtolower($type);
|
||||
if ($reflection instanceof \ReflectionFunction) {
|
||||
if ($of instanceof \ReflectionFunction) {
|
||||
return $type;
|
||||
} elseif ($lower === 'self' || $lower === 'static') {
|
||||
return $reflection->getDeclaringClass()->name;
|
||||
} elseif ($lower === 'parent' && $reflection->getDeclaringClass()->getParentClass()) {
|
||||
return $reflection->getDeclaringClass()->getParentClass()->name;
|
||||
} elseif ($lower === 'self') {
|
||||
return $of->getDeclaringClass()->name;
|
||||
} elseif ($lower === 'static') {
|
||||
return ($of instanceof ReflectionMethod ? $of->getOriginalClass() : $of->getDeclaringClass())->name;
|
||||
} elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) {
|
||||
return $of->getDeclaringClass()->getParentClass()->name;
|
||||
} else {
|
||||
return $type;
|
||||
}
|
||||
@@ -110,31 +127,57 @@ final class Type
|
||||
|
||||
private function __construct(array $types, string $kind = '|')
|
||||
{
|
||||
if ($types[0] === 'null') { // null as last
|
||||
array_push($types, array_shift($types));
|
||||
$o = array_search('null', $types, strict: true);
|
||||
if ($o !== false) { // null as last
|
||||
array_splice($types, $o, 1);
|
||||
$types[] = 'null';
|
||||
}
|
||||
|
||||
$this->types = $types;
|
||||
$this->single = ($types[1] ?? 'null') === 'null';
|
||||
$this->simple = is_string($types[0]) && ($types[1] ?? 'null') === 'null';
|
||||
$this->kind = count($types) > 1 ? $kind : '';
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->single
|
||||
? (count($this->types) > 1 ? '?' : '') . $this->types[0]
|
||||
: implode($this->kind, $this->types);
|
||||
$multi = count($this->types) > 1;
|
||||
if ($this->simple) {
|
||||
return ($multi ? '?' : '') . $this->types[0];
|
||||
}
|
||||
|
||||
$res = [];
|
||||
foreach ($this->types as $type) {
|
||||
$res[] = $type instanceof self && $multi ? "($type)" : $type;
|
||||
}
|
||||
return implode($this->kind, $res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a type that accepts both the current type and the given type.
|
||||
*/
|
||||
public function with(string|self $type): self
|
||||
{
|
||||
$type = is_string($type) ? self::fromString($type) : $type;
|
||||
return match (true) {
|
||||
$this->allows($type) => $this,
|
||||
$type->allows($this) => $type,
|
||||
default => new self(array_unique(
|
||||
array_merge($this->isIntersection() ? [$this] : $this->types, $type->isIntersection() ? [$type] : $type->types),
|
||||
SORT_REGULAR,
|
||||
), '|'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the array of subtypes that make up the compound type as strings.
|
||||
* @return string[]
|
||||
* @return array<int, string|string[]>
|
||||
*/
|
||||
public function getNames(): array
|
||||
{
|
||||
return $this->types;
|
||||
return array_map(fn($t) => $t instanceof self ? $t->getNames() : $t, $this->types);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,16 +187,16 @@ final class Type
|
||||
*/
|
||||
public function getTypes(): array
|
||||
{
|
||||
return array_map(function ($name) { return self::fromString($name); }, $this->types);
|
||||
return array_map(fn($t) => $t instanceof self ? $t : new self([$t]), $this->types);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type name for single types, otherwise null.
|
||||
* Returns the type name for simple types, otherwise null.
|
||||
*/
|
||||
public function getSingleName(): ?string
|
||||
{
|
||||
return $this->single
|
||||
return $this->simple
|
||||
? $this->types[0]
|
||||
: null;
|
||||
}
|
||||
@@ -178,29 +221,36 @@ final class Type
|
||||
|
||||
|
||||
/**
|
||||
* Returns true whether it is a single type. Simple nullable types are also considered to be single types.
|
||||
* Returns true whether it is a simple type. Single nullable types are also considered to be simple types.
|
||||
*/
|
||||
public function isSimple(): bool
|
||||
{
|
||||
return $this->simple;
|
||||
}
|
||||
|
||||
|
||||
#[\Deprecated('use isSimple()')]
|
||||
public function isSingle(): bool
|
||||
{
|
||||
return $this->single;
|
||||
return $this->simple;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true whether the type is both a single and a PHP built-in type.
|
||||
* Returns true whether the type is both a simple and a PHP built-in type.
|
||||
*/
|
||||
public function isBuiltin(): bool
|
||||
{
|
||||
return $this->single && Reflection::isBuiltinType($this->types[0]);
|
||||
return $this->simple && Validators::isBuiltinType($this->types[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true whether the type is both a single and a class name.
|
||||
* Returns true whether the type is both a simple and a class name.
|
||||
*/
|
||||
public function isClass(): bool
|
||||
{
|
||||
return $this->single && !Reflection::isBuiltinType($this->types[0]);
|
||||
return $this->simple && !Validators::isBuiltinType($this->types[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,44 +259,44 @@ final class Type
|
||||
*/
|
||||
public function isClassKeyword(): bool
|
||||
{
|
||||
return $this->single && Reflection::isClassKeyword($this->types[0]);
|
||||
return $this->simple && Validators::isClassKeyword($this->types[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter.
|
||||
*/
|
||||
public function allows(string $type): bool
|
||||
public function allows(string|self $type): bool
|
||||
{
|
||||
if ($this->types === ['mixed']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$type = self::fromString($type);
|
||||
$type = is_string($type) ? self::fromString($type) : $type;
|
||||
return $type->isUnion()
|
||||
? Arrays::every($type->types, fn($t) => $this->allowsAny($t instanceof self ? $t->types : [$t]))
|
||||
: $this->allowsAny($type->types);
|
||||
}
|
||||
|
||||
if ($this->isIntersection()) {
|
||||
if (!$type->isIntersection()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays::every($this->types, function ($currentType) use ($type) {
|
||||
$builtin = Reflection::isBuiltinType($currentType);
|
||||
return Arrays::some($type->types, function ($testedType) use ($currentType, $builtin) {
|
||||
return $builtin
|
||||
? strcasecmp($currentType, $testedType) === 0
|
||||
: is_a($testedType, $currentType, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
private function allowsAny(array $givenTypes): bool
|
||||
{
|
||||
return $this->isUnion()
|
||||
? Arrays::some($this->types, fn($t) => $this->allowsAll($t instanceof self ? $t->types : [$t], $givenTypes))
|
||||
: $this->allowsAll($this->types, $givenTypes);
|
||||
}
|
||||
|
||||
$method = $type->isIntersection() ? 'some' : 'every';
|
||||
return Arrays::$method($type->types, function ($testedType) {
|
||||
$builtin = Reflection::isBuiltinType($testedType);
|
||||
return Arrays::some($this->types, function ($currentType) use ($testedType, $builtin) {
|
||||
return $builtin
|
||||
? strcasecmp($currentType, $testedType) === 0
|
||||
: is_a($testedType, $currentType, true);
|
||||
});
|
||||
});
|
||||
|
||||
private function allowsAll(array $ourTypes, array $givenTypes): bool
|
||||
{
|
||||
return Arrays::every(
|
||||
$ourTypes,
|
||||
fn($ourType) => Arrays::some(
|
||||
$givenTypes,
|
||||
fn($givenType) => Validators::isBuiltinType($ourType)
|
||||
? strcasecmp($ourType, $givenType) === 0
|
||||
: is_a($givenType, $ourType, allow_string: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
134
vendor/nette/utils/src/Utils/Validators.php
vendored
134
vendor/nette/utils/src/Utils/Validators.php
vendored
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_key_exists, class_exists, explode, gettype, interface_exists, is_callable, is_float, is_int, is_iterable, is_numeric, is_object, is_string, preg_match, str_ends_with, str_replace, str_starts_with, strlen, strtolower, substr, trait_exists, var_export;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,6 +20,12 @@ class Validators
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
private const BuiltinTypes = [
|
||||
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
|
||||
'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1,
|
||||
'never' => 1, 'true' => 1,
|
||||
];
|
||||
|
||||
/** @var array<string,?callable> */
|
||||
protected static $validators = [
|
||||
// PHP types
|
||||
@@ -87,19 +94,18 @@ class Validators
|
||||
|
||||
/**
|
||||
* Verifies that the value is of expected types separated by pipe.
|
||||
* @param mixed $value
|
||||
* @throws AssertionException
|
||||
*/
|
||||
public static function assert($value, string $expected, string $label = 'variable'): void
|
||||
public static function assert(mixed $value, string $expected, string $label = 'variable'): void
|
||||
{
|
||||
if (!static::is($value, $expected)) {
|
||||
$expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
|
||||
$translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null'];
|
||||
$type = $translate[gettype($value)] ?? gettype($value);
|
||||
if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) {
|
||||
$type .= ' ' . var_export($value, true);
|
||||
$type .= ' ' . var_export($value, return: true);
|
||||
} elseif (is_object($value)) {
|
||||
$type .= ' ' . get_class($value);
|
||||
$type .= ' ' . $value::class;
|
||||
}
|
||||
|
||||
throw new AssertionException("The $label expects to be $expected, $type given.");
|
||||
@@ -110,15 +116,15 @@ class Validators
|
||||
/**
|
||||
* Verifies that element $key in array is of expected types separated by pipe.
|
||||
* @param mixed[] $array
|
||||
* @param int|string $key
|
||||
* @throws AssertionException
|
||||
*/
|
||||
public static function assertField(
|
||||
array $array,
|
||||
$key,
|
||||
?string $expected = null,
|
||||
string $label = "item '%' in array"
|
||||
): void {
|
||||
string $label = "item '%' in array",
|
||||
): void
|
||||
{
|
||||
if (!array_key_exists($key, $array)) {
|
||||
throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.');
|
||||
|
||||
@@ -130,18 +136,17 @@ class Validators
|
||||
|
||||
/**
|
||||
* Verifies that the value is of expected types separated by pipe.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function is($value, string $expected): bool
|
||||
public static function is(mixed $value, string $expected): bool
|
||||
{
|
||||
foreach (explode('|', $expected) as $item) {
|
||||
if (substr($item, -2) === '[]') {
|
||||
if (str_ends_with($item, '[]')) {
|
||||
if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
} elseif (substr($item, 0, 1) === '?') {
|
||||
} elseif (str_starts_with($item, '?')) {
|
||||
$item = substr($item, 1);
|
||||
if ($value === null) {
|
||||
return true;
|
||||
@@ -208,9 +213,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is an integer or a float.
|
||||
* @param mixed $value
|
||||
* @return ($value is int|float ? true : false)
|
||||
*/
|
||||
public static function isNumber($value): bool
|
||||
public static function isNumber(mixed $value): bool
|
||||
{
|
||||
return is_int($value) || is_float($value);
|
||||
}
|
||||
@@ -218,9 +223,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is an integer or a integer written in a string.
|
||||
* @param mixed $value
|
||||
* @return ($value is non-empty-string ? bool : ($value is int ? true : false))
|
||||
*/
|
||||
public static function isNumericInt($value): bool
|
||||
public static function isNumericInt(mixed $value): bool
|
||||
{
|
||||
return is_int($value) || (is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value));
|
||||
}
|
||||
@@ -228,9 +233,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is a number or a number written in a string.
|
||||
* @param mixed $value
|
||||
* @return ($value is non-empty-string ? bool : ($value is int|float ? true : false))
|
||||
*/
|
||||
public static function isNumeric($value): bool
|
||||
public static function isNumeric(mixed $value): bool
|
||||
{
|
||||
return is_float($value) || is_int($value) || (is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value));
|
||||
}
|
||||
@@ -238,19 +243,17 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is a syntactically correct callback.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isCallable($value): bool
|
||||
public static function isCallable(mixed $value): bool
|
||||
{
|
||||
return $value && is_callable($value, true);
|
||||
return $value && is_callable($value, syntax_only: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the value is a valid UTF-8 string.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isUnicode($value): bool
|
||||
public static function isUnicode(mixed $value): bool
|
||||
{
|
||||
return is_string($value) && preg_match('##u', $value);
|
||||
}
|
||||
@@ -258,9 +261,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is 0, '', false or null.
|
||||
* @param mixed $value
|
||||
* @return ($value is 0|''|false|null ? true : false)
|
||||
*/
|
||||
public static function isNone($value): bool
|
||||
public static function isNone(mixed $value): bool
|
||||
{
|
||||
return $value == null; // intentionally ==
|
||||
}
|
||||
@@ -275,10 +278,10 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if a variable is a zero-based integer indexed array.
|
||||
* @param mixed $value
|
||||
* @deprecated use Nette\Utils\Arrays::isList
|
||||
* @return ($value is list ? true : false)
|
||||
*/
|
||||
public static function isList($value): bool
|
||||
public static function isList(mixed $value): bool
|
||||
{
|
||||
return Arrays::isList($value);
|
||||
}
|
||||
@@ -287,9 +290,8 @@ class Validators
|
||||
/**
|
||||
* Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null).
|
||||
* Numbers, strings and DateTime objects can be compared.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isInRange($value, array $range): bool
|
||||
public static function isInRange(mixed $value, array $range): bool
|
||||
{
|
||||
if ($value === null || !(isset($range[0]) || isset($range[1]))) {
|
||||
return false;
|
||||
@@ -320,14 +322,13 @@ class Validators
|
||||
$atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part
|
||||
$alpha = "a-z\x80-\xFF"; // superset of IDN
|
||||
return (bool) preg_match(<<<XX
|
||||
(^
|
||||
("([ !#-[\\]-~]*|\\\\[ -~])+"|$atom+(\\.$atom+)*) # quoted or unquoted
|
||||
@
|
||||
([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
$)Dix
|
||||
XX
|
||||
, $value);
|
||||
(^(?n)
|
||||
("([ !#-[\\]-~]*|\\\\[ -~])+"|$atom+(\\.$atom+)*) # quoted or unquoted
|
||||
@
|
||||
([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
$)Dix
|
||||
XX, $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,20 +339,19 @@ XX
|
||||
{
|
||||
$alpha = "a-z\x80-\xFF";
|
||||
return (bool) preg_match(<<<XX
|
||||
(^
|
||||
https?://(
|
||||
(([-_0-9$alpha]+\\.)* # subdomain
|
||||
[0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
|
||||
|\\[[0-9a-f:]{3,39}\\] # IPv6
|
||||
)(:\\d{1,5})? # port
|
||||
(/\\S*)? # path
|
||||
(\\?\\S*)? # query
|
||||
(\\#\\S*)? # fragment
|
||||
$)Dix
|
||||
XX
|
||||
, $value);
|
||||
(^(?n)
|
||||
https?://(
|
||||
(([-_0-9$alpha]+\\.)* # subdomain
|
||||
[0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
|
||||
|\\[[0-9a-f:]{3,39}\\] # IPv6
|
||||
)(:\\d{1,5})? # port
|
||||
(/\\S*)? # path
|
||||
(\\?\\S*)? # query
|
||||
(\\#\\S*)? # fragment
|
||||
$)Dix
|
||||
XX, $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -366,6 +366,7 @@ XX
|
||||
|
||||
/**
|
||||
* Checks whether the input is a class, interface or trait.
|
||||
* @deprecated
|
||||
*/
|
||||
public static function isType(string $type): bool
|
||||
{
|
||||
@@ -380,4 +381,37 @@ XX
|
||||
{
|
||||
return preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $value) === 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is PHP built-in type. Otherwise, it is the class name.
|
||||
*/
|
||||
public static function isBuiltinType(string $type): bool
|
||||
{
|
||||
return isset(self::BuiltinTypes[strtolower($type)]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is special class name self/parent/static.
|
||||
*/
|
||||
public static function isClassKeyword(string $name): bool
|
||||
{
|
||||
return (bool) preg_match('#^(self|parent|static)$#Di', $name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the given type declaration is syntactically valid.
|
||||
*/
|
||||
public static function isTypeDeclaration(string $type): bool
|
||||
{
|
||||
return (bool) preg_match(<<<'XX'
|
||||
~((?n)
|
||||
\?? (?<type> \\? (?<name> [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) |
|
||||
(?<intersection> (?&type) (& (?&type))+ ) |
|
||||
(?<upart> (?&type) | \( (?&intersection) \) ) (\| (?&upart))+
|
||||
)$~xAD
|
||||
XX, $type);
|
||||
}
|
||||
}
|
||||
|
||||
20
vendor/nette/utils/src/Utils/exceptions.php
vendored
20
vendor/nette/utils/src/Utils/exceptions.php
vendored
@@ -11,7 +11,7 @@ namespace Nette\Utils;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an image error occurs.
|
||||
* An error occurred while working with the image.
|
||||
*/
|
||||
class ImageException extends \Exception
|
||||
{
|
||||
@@ -19,7 +19,7 @@ class ImageException extends \Exception
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates invalid image file.
|
||||
* The image file is invalid or in an unsupported format.
|
||||
*/
|
||||
class UnknownImageFileException extends ImageException
|
||||
{
|
||||
@@ -27,31 +27,23 @@ class UnknownImageFileException extends ImageException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of JSON encoding/decoding.
|
||||
* JSON encoding or decoding failed.
|
||||
*/
|
||||
class JsonException extends \Exception
|
||||
class JsonException extends \JsonException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of the last Regexp execution.
|
||||
* Regular expression pattern or execution failed.
|
||||
*/
|
||||
class RegexpException extends \Exception
|
||||
{
|
||||
public const MESSAGES = [
|
||||
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',
|
||||
PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point',
|
||||
6 => 'Failed due to limited JIT stack space', // PREG_JIT_STACKLIMIT_ERROR
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates assertion error.
|
||||
* Type validation failed. The value doesn't match the expected type constraints.
|
||||
*/
|
||||
class AssertionException extends \Exception
|
||||
{
|
||||
|
||||
35
vendor/nette/utils/src/exceptions.php
vendored
35
vendor/nette/utils/src/exceptions.php
vendored
@@ -11,8 +11,7 @@ namespace Nette;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when the value of an argument is
|
||||
* outside the allowable range of values as defined by the invoked method.
|
||||
* The value is outside the allowed range.
|
||||
*/
|
||||
class ArgumentOutOfRangeException extends \InvalidArgumentException
|
||||
{
|
||||
@@ -20,8 +19,7 @@ class ArgumentOutOfRangeException extends \InvalidArgumentException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a method call is invalid for the object's
|
||||
* current state, method has been invoked at an illegal or inappropriate time.
|
||||
* The object is in a state that does not allow the requested operation.
|
||||
*/
|
||||
class InvalidStateException extends \RuntimeException
|
||||
{
|
||||
@@ -29,7 +27,7 @@ class InvalidStateException extends \RuntimeException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a requested method or operation is not implemented.
|
||||
* The requested feature is not implemented.
|
||||
*/
|
||||
class NotImplementedException extends \LogicException
|
||||
{
|
||||
@@ -37,8 +35,7 @@ class NotImplementedException extends \LogicException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an invoked method is not supported. For scenarios where
|
||||
* it is sometimes possible to perform the requested operation, see InvalidStateException.
|
||||
* The requested operation is not supported.
|
||||
*/
|
||||
class NotSupportedException extends \LogicException
|
||||
{
|
||||
@@ -46,7 +43,7 @@ class NotSupportedException extends \LogicException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a requested method or operation is deprecated.
|
||||
* The requested feature is deprecated and no longer available.
|
||||
*/
|
||||
class DeprecatedException extends NotSupportedException
|
||||
{
|
||||
@@ -54,7 +51,7 @@ class DeprecatedException extends NotSupportedException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when accessing a class member (property or method) fails.
|
||||
* Cannot access the requested class property or method.
|
||||
*/
|
||||
class MemberAccessException extends \Error
|
||||
{
|
||||
@@ -62,7 +59,7 @@ class MemberAccessException extends \Error
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an I/O error occurs.
|
||||
* Failed to read from or write to a file or stream.
|
||||
*/
|
||||
class IOException extends \RuntimeException
|
||||
{
|
||||
@@ -70,7 +67,7 @@ class IOException extends \RuntimeException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when accessing a file that does not exist on disk.
|
||||
* The requested file does not exist.
|
||||
*/
|
||||
class FileNotFoundException extends IOException
|
||||
{
|
||||
@@ -78,7 +75,7 @@ class FileNotFoundException extends IOException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when part of a file or directory cannot be found.
|
||||
* The requested directory does not exist.
|
||||
*/
|
||||
class DirectoryNotFoundException extends IOException
|
||||
{
|
||||
@@ -86,7 +83,7 @@ class DirectoryNotFoundException extends IOException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an argument does not match with the expected value.
|
||||
* The provided argument has invalid type or format.
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
@@ -94,7 +91,7 @@ class InvalidArgumentException extends \InvalidArgumentException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an illegal index was requested.
|
||||
* The requested array or collection index does not exist.
|
||||
*/
|
||||
class OutOfRangeException extends \OutOfRangeException
|
||||
{
|
||||
@@ -102,8 +99,16 @@ class OutOfRangeException extends \OutOfRangeException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a value (typically returned by function) does not match with the expected value.
|
||||
* The returned value has unexpected type or format.
|
||||
*/
|
||||
class UnexpectedValueException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Houston, we have a problem.
|
||||
*/
|
||||
class ShouldNotHappenException extends \LogicException
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user