From 36c02e8c95b364ca4f32d89df7c8cad12f90e6fb Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 15:30:47 +0100 Subject: [PATCH 01/24] opened 4.0-dev --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1ca7380fa..87571ae20 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ }, "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.0-dev" } } } From 4206c7fa45eda86e3e3b4998b34af8f0f6f3cad4 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 17:40:17 +0100 Subject: [PATCH 02/24] requires PHP 8.0 --- .github/workflows/coding-style.yml | 2 +- .github/workflows/static-analysis.yml | 2 +- .github/workflows/tests.yml | 4 ++-- composer.json | 2 +- ncs.xml | 2 +- readme.md | 1 + 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/coding-style.yml b/.github/workflows/coding-style.yml index 003ba9265..16e97d13c 100644 --- a/.github/workflows/coding-style.yml +++ b/.github/workflows/coding-style.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 coverage: none - run: composer create-project nette/code-checker temp/code-checker ^3 --no-progress diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 362037155..94b3ec657 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 coverage: none - run: composer install --no-progress --prefer-dist diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e50a2dbe0..8a9a78044 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: os: [ubuntu-latest, windows-latest] - php: ['7.2', '7.3', '7.4', '8.0', '8.1'] + php: ['8.0', '8.1'] fail-fast: false @@ -37,7 +37,7 @@ jobs: - uses: actions/checkout@v2 - uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 extensions: iconv, json, mbstring, xml, gd, intl, tokenizer coverage: none diff --git a/composer.json b/composer.json index 87571ae20..7a6563d20 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } ], "require": { - "php": ">=7.2 <8.2" + "php": ">=8.0 <8.2" }, "require-dev": { "nette/tester": "~2.0", diff --git a/ncs.xml b/ncs.xml index 17880ae31..7eacc1b07 100644 --- a/ncs.xml +++ b/ncs.xml @@ -1,6 +1,6 @@ - + diff --git a/readme.md b/readme.md index 81ed0e919..2508385a3 100644 --- a/readme.md +++ b/readme.md @@ -39,6 +39,7 @@ The recommended way to install is via Composer: composer require nette/utils ``` +- Nette Utils 4.0 is compatible with PHP 8.0 to 8.1 - Nette Utils 3.2 is compatible with PHP 7.2 to 8.1 - 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 From df067883ed09b1a4ca3f9a4150fc38e0a59d5bcc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 16:43:25 +0100 Subject: [PATCH 03/24] composer: updated dependencies --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 7a6563d20..9b37dad91 100644 --- a/composer.json +++ b/composer.json @@ -18,8 +18,8 @@ "php": ">=8.0 <8.2" }, "require-dev": { - "nette/tester": "~2.0", - "tracy/tracy": "^2.3", + "nette/tester": "^2.4", + "tracy/tracy": "^2.8", "phpstan/phpstan": "^1.0" }, "conflict": { From e8055fd37b6eda88bfd65f687e3c2e0ff7ee5b16 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 20 Sep 2021 12:47:36 +0200 Subject: [PATCH 04/24] removed support for PHP 7 --- src/Utils/Callback.php | 2 - src/Utils/Html.php | 11 +- src/Utils/Image.php | 11 +- src/Utils/Reflection.php | 6 +- src/Utils/Type.php | 4 +- .../Utils/Reflection.getParameterType.80.phpt | 70 --------- tests/Utils/Reflection.getParameterType.phpt | 19 ++- .../Utils/Reflection.getPropertyType.74.phpt | 46 ------ .../Utils/Reflection.getPropertyType.80.phpt | 62 -------- tests/Utils/Reflection.getPropertyType.phpt | 40 ++++- tests/Utils/Reflection.getReturnType.80.phpt | 137 ------------------ tests/Utils/Reflection.getReturnType.phpt | 40 +++++ tests/Utils/SmartObject.events.74.phpt | 26 ---- tests/Utils/SmartObject.events.phpt | 7 + tests/Utils/SmartObject.undeclaredMethod.phpt | 18 +-- 15 files changed, 114 insertions(+), 385 deletions(-) delete mode 100644 tests/Utils/Reflection.getParameterType.80.phpt delete mode 100644 tests/Utils/Reflection.getPropertyType.74.phpt delete mode 100644 tests/Utils/Reflection.getPropertyType.80.phpt delete mode 100644 tests/Utils/Reflection.getReturnType.80.phpt delete mode 100644 tests/Utils/SmartObject.events.74.phpt diff --git a/src/Utils/Callback.php b/src/Utils/Callback.php index 83b6e268d..d73016d00 100644 --- a/src/Utils/Callback.php +++ b/src/Utils/Callback.php @@ -119,8 +119,6 @@ public static function toString($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; diff --git a/src/Utils/Html.php b/src/Utils/Html.php index 0e1f6436e..cf2cddc29 100644 --- a/src/Utils/Html.php +++ b/src/Utils/Html.php @@ -771,16 +771,7 @@ final public function render(?int $indent = null): string 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(); } diff --git a/src/Utils/Image.php b/src/Utils/Image.php index eab99987b..42bb696ec 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -617,16 +617,7 @@ public function toString(int $type = self::JPEG, ?int $quality = null): string */ 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(); } diff --git a/src/Utils/Reflection.php b/src/Utils/Reflection.php index 8487bed4a..d79b0fbae 100644 --- a/src/Utils/Reflection.php +++ b/src/Utils/Reflection.php @@ -98,7 +98,7 @@ public static function getParameterTypes(\ReflectionParameter $param): array */ public static function getPropertyType(\ReflectionProperty $prop): ?string { - return self::getType($prop, PHP_VERSION_ID >= 70400 ? $prop->getType() : null); + return self::getType($prop, $prop->getType()); } @@ -327,9 +327,7 @@ private static function parseUseStatements(string $code, ?string $forClass = nul $namespace = $class = $classLevel = $level = null; $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); diff --git a/src/Utils/Type.php b/src/Utils/Type.php index 2d61e5a00..e00f8f535 100644 --- a/src/Utils/Type.php +++ b/src/Utils/Type.php @@ -34,9 +34,7 @@ final class Type */ public static function fromReflection($reflection): ?self { - if ($reflection instanceof \ReflectionProperty && PHP_VERSION_ID < 70400) { - return null; - } elseif ($reflection instanceof \ReflectionMethod) { + if ($reflection instanceof \ReflectionMethod) { $type = $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 ? $reflection->getTentativeReturnType() : null); } else { $type = $reflection instanceof \ReflectionFunctionAbstract diff --git a/tests/Utils/Reflection.getParameterType.80.phpt b/tests/Utils/Reflection.getParameterType.80.phpt deleted file mode 100644 index 57261ac51..000000000 --- a/tests/Utils/Reflection.getParameterType.80.phpt +++ /dev/null @@ -1,70 +0,0 @@ -getParameters(); - -Assert::same('Undeclared', Reflection::getParameterType($params[0])); -Assert::same('Test\B', Reflection::getParameterType($params[1])); -Assert::same('array', Reflection::getParameterType($params[2])); -Assert::same('callable', Reflection::getParameterType($params[3])); -Assert::same('A', Reflection::getParameterType($params[4])); -Assert::null(Reflection::getParameterType($params[5])); -Assert::same('Test\B', Reflection::getParameterType($params[6])); -Assert::same(['Test\B', 'null'], Reflection::getParameterTypes($params[6])); -Assert::same('mixed', Reflection::getParameterType($params[7])); -Assert::same(['mixed'], Reflection::getParameterTypes($params[7])); -Assert::same(['A', 'array'], Reflection::getParameterTypes($params[8])); -Assert::same(['A', 'array', 'null'], Reflection::getParameterTypes($params[9])); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[8]); -}, Nette\InvalidStateException::class, 'The $union in A::method() is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[9]); -}, Nette\InvalidStateException::class, 'The $nullableUnion in A::method() is not expected to have a union or intersection type.'); - - -$method = new ReflectionMethod('AExt', 'methodExt'); -$params = $method->getParameters(); - -Assert::same('A', Reflection::getParameterType($params[0])); diff --git a/tests/Utils/Reflection.getParameterType.phpt b/tests/Utils/Reflection.getParameterType.phpt index f80468ae8..9ffa52501 100644 --- a/tests/Utils/Reflection.getParameterType.phpt +++ b/tests/Utils/Reflection.getParameterType.phpt @@ -23,7 +23,10 @@ class A callable $callable, self $self, $none, - ?B $nullable + ?B $nullable, + mixed $mixed, + array|self $union, + array|self|null $nullableUnion, ) { } } @@ -45,6 +48,20 @@ Assert::same('callable', Reflection::getParameterType($params[3])); Assert::same('A', Reflection::getParameterType($params[4])); Assert::null(Reflection::getParameterType($params[5])); Assert::same('Test\B', Reflection::getParameterType($params[6])); +Assert::same(['Test\B', 'null'], Reflection::getParameterTypes($params[6])); +Assert::same('mixed', Reflection::getParameterType($params[7])); +Assert::same(['mixed'], Reflection::getParameterTypes($params[7])); +Assert::same(['A', 'array'], Reflection::getParameterTypes($params[8])); +Assert::same(['A', 'array', 'null'], Reflection::getParameterTypes($params[9])); + +Assert::exception(function () use ($params) { + Reflection::getParameterType($params[8]); +}, Nette\InvalidStateException::class, 'The $union in A::method() is not expected to have a union or intersection type.'); + +Assert::exception(function () use ($params) { + Reflection::getParameterType($params[9]); +}, Nette\InvalidStateException::class, 'The $nullableUnion in A::method() is not expected to have a union or intersection type.'); + $method = new ReflectionMethod('AExt', 'methodExt'); $params = $method->getParameters(); diff --git a/tests/Utils/Reflection.getPropertyType.74.phpt b/tests/Utils/Reflection.getPropertyType.74.phpt deleted file mode 100644 index b822be995..000000000 --- a/tests/Utils/Reflection.getPropertyType.74.phpt +++ /dev/null @@ -1,46 +0,0 @@ -getProperties(); - -Assert::same('Undeclared', Reflection::getPropertyType($props[0])); -Assert::same('Test\B', Reflection::getPropertyType($props[1])); -Assert::same('array', Reflection::getPropertyType($props[2])); -Assert::same('A', Reflection::getPropertyType($props[3])); -Assert::null(Reflection::getPropertyType($props[4])); -Assert::same('Test\B', Reflection::getPropertyType($props[5])); - -$class = new ReflectionClass('AExt'); -$props = $class->getProperties(); - -Assert::same('A', Reflection::getPropertyType($props[0])); diff --git a/tests/Utils/Reflection.getPropertyType.80.phpt b/tests/Utils/Reflection.getPropertyType.80.phpt deleted file mode 100644 index 00378ce66..000000000 --- a/tests/Utils/Reflection.getPropertyType.80.phpt +++ /dev/null @@ -1,62 +0,0 @@ -getProperties(); - -Assert::same('Undeclared', Reflection::getPropertyType($props[0])); -Assert::same('Test\B', Reflection::getPropertyType($props[1])); -Assert::same('array', Reflection::getPropertyType($props[2])); -Assert::same('A', Reflection::getPropertyType($props[3])); -Assert::null(Reflection::getPropertyType($props[4])); -Assert::same('Test\B', Reflection::getPropertyType($props[5])); -Assert::same(['Test\B', 'null'], Reflection::getPropertyTypes($props[5])); -Assert::same('mixed', Reflection::getPropertyType($props[6])); -Assert::same(['mixed'], Reflection::getPropertyTypes($props[6])); -Assert::same(['A', 'array'], Reflection::getPropertyTypes($props[7])); -Assert::same(['A', 'array', 'null'], Reflection::getPropertyTypes($props[8])); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[7]); -}, Nette\InvalidStateException::class, 'The A::$union is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[8]); -}, Nette\InvalidStateException::class, 'The A::$nullableUnion is not expected to have a union or intersection type.'); - -$class = new ReflectionClass('AExt'); -$props = $class->getProperties(); - -Assert::same('A', Reflection::getPropertyType($props[0])); diff --git a/tests/Utils/Reflection.getPropertyType.phpt b/tests/Utils/Reflection.getPropertyType.phpt index 8b15eb85e..e79e0d107 100644 --- a/tests/Utils/Reflection.getPropertyType.phpt +++ b/tests/Utils/Reflection.getPropertyType.phpt @@ -7,6 +7,7 @@ declare(strict_types=1); use Nette\Utils\Reflection; +use Test\B; // for testing purposes use Tester\Assert; @@ -15,11 +16,46 @@ require __DIR__ . '/../bootstrap.php'; class A { + public Undeclared $undeclared; + public B $b; + public array $array; + public self $self; public $none; + public ?B $nullable; + public mixed $mixed; + public array|self $union; + public array|self|null $nullableUnion; +} + +class AExt extends A +{ + public parent $parent; } $class = new ReflectionClass('A'); $props = $class->getProperties(); -Assert::null(Reflection::getPropertyType($props[0])); -Assert::same([], Reflection::getPropertyTypes($props[0])); +Assert::same('Undeclared', Reflection::getPropertyType($props[0])); +Assert::same('Test\B', Reflection::getPropertyType($props[1])); +Assert::same('array', Reflection::getPropertyType($props[2])); +Assert::same('A', Reflection::getPropertyType($props[3])); +Assert::null(Reflection::getPropertyType($props[4])); +Assert::same('Test\B', Reflection::getPropertyType($props[5])); +Assert::same(['Test\B', 'null'], Reflection::getPropertyTypes($props[5])); +Assert::same('mixed', Reflection::getPropertyType($props[6])); +Assert::same(['mixed'], Reflection::getPropertyTypes($props[6])); +Assert::same(['A', 'array'], Reflection::getPropertyTypes($props[7])); +Assert::same(['A', 'array', 'null'], Reflection::getPropertyTypes($props[8])); + +Assert::exception(function () use ($props) { + Reflection::getPropertyType($props[7]); +}, Nette\InvalidStateException::class, 'The A::$union is not expected to have a union or intersection type.'); + +Assert::exception(function () use ($props) { + Reflection::getPropertyType($props[8]); +}, Nette\InvalidStateException::class, 'The A::$nullableUnion is not expected to have a union or intersection type.'); + +$class = new ReflectionClass('AExt'); +$props = $class->getProperties(); + +Assert::same('A', Reflection::getPropertyType($props[0])); diff --git a/tests/Utils/Reflection.getReturnType.80.phpt b/tests/Utils/Reflection.getReturnType.80.phpt deleted file mode 100644 index e08b50a10..000000000 --- a/tests/Utils/Reflection.getReturnType.80.phpt +++ /dev/null @@ -1,137 +0,0 @@ -onEvent(); -}); diff --git a/tests/Utils/SmartObject.events.phpt b/tests/Utils/SmartObject.events.phpt index 4d3899627..d7952fadf 100644 --- a/tests/Utils/SmartObject.events.phpt +++ b/tests/Utils/SmartObject.events.phpt @@ -19,6 +19,8 @@ class TestClass public static $onPublicStatic; + public array $onEvent; + protected $onProtected; private $onPrivate; @@ -81,3 +83,8 @@ Assert::exception(function () use ($obj) { $obj->onPublic = 'string'; $obj->onPublic(); }, Nette\UnexpectedValueException::class, 'Property TestClass::$onPublic must be iterable or null, string given.'); + +Assert::noError(function () { + $obj = new TestClass; + $obj->onEvent(); +}); diff --git a/tests/Utils/SmartObject.undeclaredMethod.phpt b/tests/Utils/SmartObject.undeclaredMethod.phpt index 60d255cb8..c11e52cd1 100644 --- a/tests/Utils/SmartObject.undeclaredMethod.phpt +++ b/tests/Utils/SmartObject.undeclaredMethod.phpt @@ -120,18 +120,12 @@ Assert::exception(function () { $obj::callMissingParentStatic(); }, Nette\MemberAccessException::class, 'Call to undefined static method InterClass::callMissingParentStatic().'); -Assert::exception( - function () { - $obj = new ChildClass; - $obj->callPrivateParent(); - }, - Nette\MemberAccessException::class, - PHP_VERSION_ID < 70400 - ? 'Call to private method InterClass::callPrivateParent() from scope ChildClass.' - : (PHP_VERSION_ID < 80100 - ? 'Call to undefined static method InterClass::callPrivateParent().' // differs from native error message - : 'Call to undefined method InterClass::callPrivateParent().') -); +Assert::exception(function () { + $obj = new ChildClass; + $obj->callPrivateParent(); +}, Nette\MemberAccessException::class, PHP_VERSION_ID < 80100 + ? 'Call to undefined static method InterClass::callPrivateParent().' // differs from native error message + : 'Call to undefined method InterClass::callPrivateParent().'); Assert::exception(function () { $obj = new ChildClass; From 62e14f4687ff19312958bbef78b7106e535a63fd Mon Sep 17 00:00:00 2001 From: David Grudl Date: Tue, 2 Mar 2021 15:50:58 +0100 Subject: [PATCH 05/24] removed deprecated stuff --- src/Utils/Callback.php | 41 ---------------- src/Utils/ObjectMixin.php | 41 ---------------- src/Utils/Reflection.php | 30 ------------ tests/Utils/Callback.invoke.phpt | 49 ------------------- .../Utils/Reflection.getParameterType.81.phpt | 4 -- tests/Utils/Reflection.getParameterType.phpt | 4 -- .../Utils/Reflection.getPropertyType.81.phpt | 4 -- tests/Utils/Reflection.getPropertyType.phpt | 4 -- tests/Utils/Reflection.getReturnType.81.phpt | 7 --- tests/Utils/Reflection.getReturnType.phpt | 6 --- 10 files changed, 190 deletions(-) delete mode 100644 src/Utils/ObjectMixin.php delete mode 100644 tests/Utils/Callback.invoke.phpt diff --git a/src/Utils/Callback.php b/src/Utils/Callback.php index d73016d00..bd29bb2b8 100644 --- a/src/Utils/Callback.php +++ b/src/Utils/Callback.php @@ -20,47 +20,6 @@ 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 diff --git a/src/Utils/ObjectMixin.php b/src/Utils/ObjectMixin.php deleted file mode 100644 index 33249509b..000000000 --- a/src/Utils/ObjectMixin.php +++ /dev/null @@ -1,41 +0,0 @@ -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. @@ -81,16 +71,6 @@ public static function getParameterType(\ReflectionParameter $param): ?string } - /** - * @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. @@ -102,16 +82,6 @@ public static function getPropertyType(\ReflectionProperty $prop): ?string } - /** - * @deprecated - */ - public static function getPropertyTypes(\ReflectionProperty $prop): array - { - $type = Type::fromReflection($prop); - return $type ? $type->getNames() : []; - } - - /** * @param \ReflectionFunction|\ReflectionMethod|\ReflectionParameter|\ReflectionProperty $reflection */ diff --git a/tests/Utils/Callback.invoke.phpt b/tests/Utils/Callback.invoke.phpt deleted file mode 100644 index 9d072f29f..000000000 --- a/tests/Utils/Callback.invoke.phpt +++ /dev/null @@ -1,49 +0,0 @@ - Date: Sun, 19 Sep 2021 19:03:08 +0200 Subject: [PATCH 06/24] coding style --- src/Iterators/CachingIterator.php | 2 +- src/Utils/Callback.php | 2 +- src/Utils/DateTime.php | 6 +-- src/Utils/FileSystem.php | 20 ++++---- src/Utils/Html.php | 2 +- src/Utils/Image.php | 12 ++--- src/Utils/ObjectHelpers.php | 18 ++++---- src/Utils/Random.php | 9 ++-- src/Utils/Strings.php | 2 +- src/Utils/Type.php | 31 +++++-------- src/Utils/Validators.php | 46 +++++++++---------- tests/Iterators/Mapper.phpt | 4 +- tests/Utils/Arrays.associate().phpt | 38 +++++++-------- tests/Utils/Arrays.every().phpt | 14 +++--- tests/Utils/Arrays.invoke.phpt | 4 +- tests/Utils/Arrays.invokeMethod.phpt | 4 +- tests/Utils/Arrays.map().phpt | 8 ++-- tests/Utils/Arrays.normalize.phpt | 4 +- tests/Utils/Arrays.some().phpt | 14 +++--- tests/Utils/Callback.invokeSafe.phpt | 4 +- tests/Utils/DateTime.JSON.phpt | 2 +- tests/Utils/DateTime.from.phpt | 12 ++--- tests/Utils/DateTime.modifyClone.phpt | 2 +- tests/Utils/Json.decode().phpt | 2 +- tests/Utils/Reflection.expandClassName.phpt | 8 ++-- .../Utils/Reflection.groupUseStatements.phpt | 2 +- .../Reflection.nonClassUseStatements.phpt | 2 +- tests/Utils/Strings.replace().phpt | 2 +- tests/Utils/Type.fromReflection.method.phpt | 4 +- .../fixtures.reflection/defaultValue.php | 2 +- 30 files changed, 136 insertions(+), 146 deletions(-) diff --git a/src/Iterators/CachingIterator.php b/src/Iterators/CachingIterator.php index b07bbbfa4..cf8f6785f 100644 --- a/src/Iterators/CachingIterator.php +++ b/src/Iterators/CachingIterator.php @@ -48,7 +48,7 @@ public function __construct($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))); + throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, is_object($iterator) ? $iterator::class : gettype($iterator))); } parent::__construct($iterator, 0); diff --git a/src/Utils/Callback.php b/src/Utils/Callback.php index bd29bb2b8..23ef8a54c 100644 --- a/src/Utils/Callback.php +++ b/src/Utils/Callback.php @@ -61,7 +61,7 @@ public static function check($callable, bool $syntax = false) 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)), ); } diff --git a/src/Utils/DateTime.php b/src/Utils/DateTime.php index aa61a4b4f..e53f41de0 100644 --- a/src/Utils/DateTime.php +++ b/src/Utils/DateTime.php @@ -32,10 +32,10 @@ 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; /** @@ -73,7 +73,7 @@ public static function fromParts( int $day, int $hour = 0, int $minute = 0, - float $second = 0.0 + float $second = 0.0, ) { $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second); if ( diff --git a/src/Utils/FileSystem.php b/src/Utils/FileSystem.php index c61603380..826d07b2d 100644 --- a/src/Utils/FileSystem.php +++ b/src/Utils/FileSystem.php @@ -30,7 +30,7 @@ public static function createDir(string $dir, int $mode = 0777): void "Unable to create directory '%s' with mode %s. %s", self::normalizePath($dir), decoct($mode), - Helpers::getLastError() + Helpers::getLastError(), )); } } @@ -73,7 +73,7 @@ public static function copy(string $origin, string $target, bool $overwrite = tr "Unable to copy file '%s' to '%s'. %s", self::normalizePath($origin), self::normalizePath($target), - Helpers::getLastError() + Helpers::getLastError(), )); } } @@ -92,7 +92,7 @@ public static function delete(string $path): void throw new Nette\IOException(sprintf( "Unable to delete '%s'. %s", self::normalizePath($path), - Helpers::getLastError() + Helpers::getLastError(), )); } } elseif (is_dir($path)) { @@ -104,7 +104,7 @@ public static function delete(string $path): void throw new Nette\IOException(sprintf( "Unable to delete directory '%s'. %s", self::normalizePath($path), - Helpers::getLastError() + Helpers::getLastError(), )); } } @@ -135,7 +135,7 @@ public static function rename(string $origin, string $target, bool $overwrite = "Unable to rename file or directory '%s' to '%s'. %s", self::normalizePath($origin), self::normalizePath($target), - Helpers::getLastError() + Helpers::getLastError(), )); } } @@ -153,7 +153,7 @@ public static function read(string $file): string throw new Nette\IOException(sprintf( "Unable to read file '%s'. %s", self::normalizePath($file), - Helpers::getLastError() + Helpers::getLastError(), )); } @@ -172,7 +172,7 @@ public static function write(string $file, string $content, ?int $mode = 0666): throw new Nette\IOException(sprintf( "Unable to write file '%s'. %s", self::normalizePath($file), - Helpers::getLastError() + Helpers::getLastError(), )); } @@ -181,7 +181,7 @@ public static function write(string $file, string $content, ?int $mode = 0666): "Unable to chmod file '%s' to mode %s. %s", self::normalizePath($file), decoct($mode), - Helpers::getLastError() + Helpers::getLastError(), )); } } @@ -199,7 +199,7 @@ public static function makeWritable(string $path, int $dirMode = 0777, int $file "Unable to chmod file '%s' to mode %s. %s", self::normalizePath($path), decoct($fileMode), - Helpers::getLastError() + Helpers::getLastError(), )); } } elseif (is_dir($path)) { @@ -212,7 +212,7 @@ public static function makeWritable(string $path, int $dirMode = 0777, int $file "Unable to chmod directory '%s' to mode %s. %s", self::normalizePath($path), decoct($dirMode), - Helpers::getLastError() + Helpers::getLastError(), )); } } else { diff --git a/src/Utils/Html.php b/src/Utils/Html.php index cf2cddc29..b052b1735 100644 --- a/src/Utils/Html.php +++ b/src/Utils/Html.php @@ -853,7 +853,7 @@ final public function attributes(): string . str_replace( ['&', $q, '<'], ['&', $q === '"' ? '"' : ''', self::$xhtml ? '<' : '<'], - $value + $value, ) . (strpos($value, '`') !== false && strpbrk($value, ' <>"\'') === false ? ' ' : '') . $q; diff --git a/src/Utils/Image.php b/src/Utils/Image.php index 42bb696ec..15de610d0 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -354,7 +354,7 @@ public function resize($width, $height, int $flags = self::FIT) $newWidth, $newHeight, $this->getWidth(), - $this->getHeight() + $this->getHeight(), ); $this->image = $newImage; } @@ -377,7 +377,7 @@ public static function calculateSize( int $srcHeight, $newWidth, $newHeight, - int $flags = self::FIT + int $flags = self::FIT, ): array { if ($newWidth === null) { } elseif (self::isPercent($newWidth)) { @@ -575,7 +575,7 @@ public function place(self $image, $left = 0, $top = 0, int $opacity = 100) 0, 0, $width, - $height + $height, ); return $this; } @@ -606,7 +606,7 @@ public function save(string $file, ?int $quality = null, ?int $type = null): voi */ public function toString(int $type = self::JPEG, ?int $quality = null): string { - return Helpers::capture(function () use ($type, $quality) { + return Helpers::capture(function () use ($type, $quality): void { $this->output($type, $quality); }); } @@ -699,13 +699,13 @@ public function __call(string $name, array $args) $value['red'], $value['green'], $value['blue'], - $value['alpha'] + $value['alpha'], ) ?: imagecolorresolvealpha( $this->image, $value['red'], $value['green'], $value['blue'], - $value['alpha'] + $value['alpha'], ); } } diff --git a/src/Utils/ObjectHelpers.php b/src/Utils/ObjectHelpers.php index 6cdfc5850..a85184b1f 100644 --- a/src/Utils/ObjectHelpers.php +++ b/src/Utils/ObjectHelpers.php @@ -28,8 +28,8 @@ public static function strictGet(string $class, string $name): void { $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 +43,8 @@ public static function strictSet(string $class, string $name): void { $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 +76,7 @@ public static function strictCall(string $class, string $method, array $addition $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 +107,8 @@ public static function strictStaticCall(string $class, string $method): void } 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 +133,7 @@ public static function getMagicProperties(string $class): array '~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx', (string) $rc->getDocComment(), $matches, - PREG_SET_ORDER + PREG_SET_ORDER, ); $props = []; @@ -199,7 +199,7 @@ private static function parseFullDoc(\ReflectionClass $rc, string $pattern): arr } } while ($rc = $rc->getParentClass()); - return preg_match_all($pattern, implode($doc), $m) ? $m[1] : []; + return preg_match_all($pattern, implode('', $doc), $m) ? $m[1] : []; } diff --git a/src/Utils/Random.php b/src/Utils/Random.php index f900628a8..d8ebcdc7c 100644 --- a/src/Utils/Random.php +++ b/src/Utils/Random.php @@ -25,9 +25,12 @@ 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) { diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index 1314c62fc..ee7c493cf 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -186,7 +186,7 @@ public static function toAscii(string $s): string $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- <->|-.' + 'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.', ); $s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); } else { diff --git a/src/Utils/Type.php b/src/Utils/Type.php index e00f8f535..9e7822f8e 100644 --- a/src/Utils/Type.php +++ b/src/Utils/Type.php @@ -52,10 +52,10 @@ public static function fromReflection($reflection): ?self } elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) { return new self( array_map( - function ($t) use ($reflection) { return self::resolve($t->getName(), $reflection); }, - $type->getTypes() + fn($t) => self::resolve($t->getName(), $reflection), + $type->getTypes(), ), - $type instanceof \ReflectionUnionType ? '|' : '&' + $type instanceof \ReflectionUnionType ? '|' : '&', ); } else { @@ -142,7 +142,7 @@ public function getNames(): array */ public function getTypes(): array { - return array_map(function ($name) { return self::fromString($name); }, $this->types); + return array_map(fn($name) => self::fromString($name), $this->types); } @@ -223,28 +223,21 @@ public function allows(string $type): bool $type = self::fromString($type); 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 + return $type->isIntersection() + && Arrays::every($this->types, function ($currentType) use ($type) { + $builtin = Reflection::isBuiltinType($currentType); + return Arrays::some($type->types, fn($testedType) => $builtin ? strcasecmp($currentType, $testedType) === 0 - : is_a($testedType, $currentType, true); + : is_a($testedType, $currentType, true)); }); - }); } $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); - }); + return Arrays::some($this->types, fn($currentType) => $builtin + ? strcasecmp($currentType, $testedType) === 0 + : is_a($testedType, $currentType, true)); }); } } diff --git a/src/Utils/Validators.php b/src/Utils/Validators.php index 7ec7b6ac4..def992495 100644 --- a/src/Utils/Validators.php +++ b/src/Utils/Validators.php @@ -99,7 +99,7 @@ public static function assert($value, string $expected, string $label = 'variabl if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) { $type .= ' ' . var_export($value, true); } elseif (is_object($value)) { - $type .= ' ' . get_class($value); + $type .= ' ' . $value::class; } throw new AssertionException("The $label expects to be $expected, $type given."); @@ -117,7 +117,7 @@ public static function assertField( array $array, $key, ?string $expected = null, - string $label = "item '%' in array" + string $label = "item '%' in array", ): void { if (!array_key_exists($key, $array)) { throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.'); @@ -320,14 +320,13 @@ public static function isEmail(string $value): bool $atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part $alpha = "a-z\x80-\xFF"; // superset of IDN return (bool) preg_match(<< 'Grudl', ]; -$callback = function ($item, $key) { - return $key . ': ' . $item; -}; +$callback = fn($item, $key) => $key . ': ' . $item; $iterator = new Iterators\Mapper(new ArrayIterator($arr), $callback); diff --git a/tests/Utils/Arrays.associate().phpt b/tests/Utils/Arrays.associate().phpt index 80e439e8f..d7cde4a53 100644 --- a/tests/Utils/Arrays.associate().phpt +++ b/tests/Utils/Arrays.associate().phpt @@ -28,12 +28,12 @@ Assert::same( 'Mary' => ['name' => 'Mary', 'age' => null], 'Paul' => ['name' => 'Paul', 'age' => 44], ], - Arrays::associate($arr, 'name') + Arrays::associate($arr, 'name'), ); Assert::same( [], - Arrays::associate([], 'name') + Arrays::associate([], 'name'), ); Assert::same( @@ -42,17 +42,17 @@ Assert::same( 'Mary' => ['name' => 'Mary', 'age' => null], 'Paul' => ['name' => 'Paul', 'age' => 44], ], - Arrays::associate($arr, 'name=') + Arrays::associate($arr, 'name='), ); Assert::same( ['John' => 22, 'Mary' => null, 'Paul' => 44], - Arrays::associate($arr, 'name=age') + Arrays::associate($arr, 'name=age'), ); Assert::same(// path as array ['John' => 22, 'Mary' => null, 'Paul' => 44], - Arrays::associate($arr, ['name', '=', 'age']) + Arrays::associate($arr, ['name', '=', 'age']), ); Assert::equal( @@ -70,7 +70,7 @@ Assert::equal( 'age' => 44, ], ], - Arrays::associate($arr, 'name->') + Arrays::associate($arr, 'name->'), ); Assert::equal( @@ -88,7 +88,7 @@ Assert::equal( 'Paul' => ['name' => 'Paul', 'age' => 44], ], ], - Arrays::associate($arr, 'age->name') + Arrays::associate($arr, 'age->name'), ); Assert::equal( @@ -97,12 +97,12 @@ Assert::equal( 'Mary' => ['name' => 'Mary', 'age' => null], 'Paul' => ['name' => 'Paul', 'age' => 44], ], - Arrays::associate($arr, '->name') + Arrays::associate($arr, '->name'), ); Assert::equal( (object) [], - Arrays::associate([], '->name') + Arrays::associate([], '->name'), ); Assert::same( @@ -118,7 +118,7 @@ Assert::same( 44 => ['name' => 'Paul', 'age' => 44], ], ], - Arrays::associate($arr, 'name|age') + Arrays::associate($arr, 'name|age'), ); Assert::same( @@ -127,7 +127,7 @@ Assert::same( 'Mary' => ['name' => 'Mary', 'age' => null], 'Paul' => ['name' => 'Paul', 'age' => 44], ], - Arrays::associate($arr, 'name|') + Arrays::associate($arr, 'name|'), ); Assert::same( @@ -143,7 +143,7 @@ Assert::same( ['name' => 'Paul', 'age' => 44], ], ], - Arrays::associate($arr, 'name[]') + Arrays::associate($arr, 'name[]'), ); Assert::same( @@ -153,12 +153,12 @@ Assert::same( ['Mary' => ['name' => 'Mary', 'age' => null]], ['Paul' => ['name' => 'Paul', 'age' => 44]], ], - Arrays::associate($arr, '[]name') + Arrays::associate($arr, '[]name'), ); Assert::same( ['John', 'John', 'Mary', 'Paul'], - Arrays::associate($arr, '[]=name') + Arrays::associate($arr, '[]=name'), ); Assert::same( @@ -174,12 +174,12 @@ Assert::same( [44 => ['name' => 'Paul', 'age' => 44]], ], ], - Arrays::associate($arr, 'name[]age') + Arrays::associate($arr, 'name[]age'), ); Assert::same( $arr, - Arrays::associate($arr, '[]') + Arrays::associate($arr, '[]'), ); // converts object to array @@ -190,7 +190,7 @@ Assert::same( (object) ['name' => 'John', 'age' => 22], (object) ['name' => 'Mary', 'age' => null], (object) ['name' => 'Paul', 'age' => 44], - ], '[]') + ], '[]'), ); // allowes objects in keys @@ -198,11 +198,11 @@ Assert::equal( ['2014-02-05 00:00:00' => new DateTime('2014-02-05')], Arrays::associate($arr = [ ['date' => new DateTime('2014-02-05')], - ], 'date=date') + ], 'date=date'), ); Assert::equal( (object) ['2014-02-05 00:00:00' => new DateTime('2014-02-05')], Arrays::associate($arr = [ ['date' => new DateTime('2014-02-05')], - ], '->date=date') + ], '->date=date'), ); diff --git a/tests/Utils/Arrays.every().phpt b/tests/Utils/Arrays.every().phpt index eed82ec59..074bf0b1f 100644 --- a/tests/Utils/Arrays.every().phpt +++ b/tests/Utils/Arrays.every().phpt @@ -18,7 +18,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; }, ); Assert::true($res); Assert::same([], $log); @@ -29,7 +29,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::true($res); Assert::same([], $log); @@ -40,7 +40,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; }, ); Assert::false($res); Assert::same([['a', 0, $arr]], $log); @@ -51,7 +51,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::true($res); Assert::same([['a', 0, $arr], ['b', 1, $arr]], $log); @@ -62,7 +62,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; }, ); Assert::false($res); Assert::same([['a', 0, $arr], ['b', 1, $arr]], $log); @@ -73,7 +73,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::true($res); Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log); @@ -84,7 +84,7 @@ test('', function () { $log = []; $res = Arrays::every( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::true($res); Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log); diff --git a/tests/Utils/Arrays.invoke.phpt b/tests/Utils/Arrays.invoke.phpt index 173106ce4..19d1094f0 100644 --- a/tests/Utils/Arrays.invoke.phpt +++ b/tests/Utils/Arrays.invoke.phpt @@ -30,10 +30,10 @@ $list['key'] = [new Test, 'fn2']; Assert::same( ['Test::fn1 a,b', 'key' => 'Test::fn2 a,b'], - Arrays::invoke($list, 'a', 'b') + Arrays::invoke($list, 'a', 'b'), ); Assert::same( ['Test::fn1 a,b', 'key' => 'Test::fn2 a,b'], - Arrays::invoke(new ArrayIterator($list), 'a', 'b') + Arrays::invoke(new ArrayIterator($list), 'a', 'b'), ); diff --git a/tests/Utils/Arrays.invokeMethod.phpt b/tests/Utils/Arrays.invokeMethod.phpt index 8e1eec51d..8c42d3186 100644 --- a/tests/Utils/Arrays.invokeMethod.phpt +++ b/tests/Utils/Arrays.invokeMethod.phpt @@ -26,10 +26,10 @@ $list = [new Test1, 'key' => new Test2]; Assert::same( ['Test1 a,b', 'key' => 'Test2 a,b'], - Arrays::invokeMethod($list, 'fn', 'a', 'b') + Arrays::invokeMethod($list, 'fn', 'a', 'b'), ); Assert::same( ['Test1 a,b', 'key' => 'Test2 a,b'], - Arrays::invokeMethod(new ArrayIterator($list), 'fn', 'a', 'b') + Arrays::invokeMethod(new ArrayIterator($list), 'fn', 'a', 'b'), ); diff --git a/tests/Utils/Arrays.map().phpt b/tests/Utils/Arrays.map().phpt index 5cdabdedf..2970e9059 100644 --- a/tests/Utils/Arrays.map().phpt +++ b/tests/Utils/Arrays.map().phpt @@ -18,7 +18,7 @@ test('', function () { $log = []; $res = Arrays::map( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::same([], $res); Assert::same([], $log); @@ -29,7 +29,7 @@ test('', function () { $log = []; $res = Arrays::map( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v . $v; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v . $v; }, ); Assert::same(['aa', 'bb'], $res); Assert::same([['a', 0, $arr], ['b', 1, $arr]], $log); @@ -40,7 +40,7 @@ test('', function () { $log = []; $res = Arrays::map( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v . $v; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v . $v; }, ); Assert::same(['x' => 'aa', 'y' => 'bb'], $res); Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log); @@ -51,7 +51,7 @@ test('', function () { $log = []; $res = Arrays::map( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v . $v; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v . $v; }, ); Assert::same(['x' => 'aa', 'y' => 'bb'], $res); Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log); diff --git a/tests/Utils/Arrays.normalize.phpt b/tests/Utils/Arrays.normalize.phpt index 0ff385a96..44eab09f7 100644 --- a/tests/Utils/Arrays.normalize.phpt +++ b/tests/Utils/Arrays.normalize.phpt @@ -25,7 +25,7 @@ Assert::same( 'a' => 'second', 'd' => ['third'], 7 => 'fourth', - ]) + ]), ); @@ -37,5 +37,5 @@ Assert::same( Arrays::normalize([ 1 => 'first', '' => 'second', - ], true) + ], true), ); diff --git a/tests/Utils/Arrays.some().phpt b/tests/Utils/Arrays.some().phpt index 3cbfadb3a..7a34bc26e 100644 --- a/tests/Utils/Arrays.some().phpt +++ b/tests/Utils/Arrays.some().phpt @@ -18,7 +18,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; }, ); Assert::false($res); Assert::same([], $log); @@ -29,7 +29,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::false($res); Assert::same([], $log); @@ -40,7 +40,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return false; }, ); Assert::false($res); Assert::same([['a', 0, $arr], ['b', 1, $arr]], $log); @@ -51,7 +51,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return true; }, ); Assert::true($res); Assert::same([['a', 0, $arr]], $log); @@ -62,7 +62,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; }, ); Assert::true($res); Assert::same([['a', 0, $arr]], $log); @@ -73,7 +73,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; }, ); Assert::true($res); Assert::same([['a', 'x', $arr]], $log); @@ -84,7 +84,7 @@ test('', function () { $log = []; $res = Arrays::some( $arr, - function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; } + function ($v, $k, $arr) use (&$log) { $log[] = func_get_args(); return $v === 'a'; }, ); Assert::true($res); Assert::same([['a', 'x', $arr]], $log); diff --git a/tests/Utils/Callback.invokeSafe.phpt b/tests/Utils/Callback.invokeSafe.phpt index 80df9ff07..4ac046d31 100644 --- a/tests/Utils/Callback.invokeSafe.phpt +++ b/tests/Utils/Callback.invokeSafe.phpt @@ -31,9 +31,7 @@ Assert::same('OK1', $res); // ignored error -Callback::invokeSafe('preg_match', ['ab', 'foo'], function () { - return false; -}); +Callback::invokeSafe('preg_match', ['ab', 'foo'], fn() => false); Assert::same('preg_match(): Delimiter must not be alphanumeric or backslash', $res); diff --git a/tests/Utils/DateTime.JSON.phpt b/tests/Utils/DateTime.JSON.phpt index 90761833a..5683bd981 100644 --- a/tests/Utils/DateTime.JSON.phpt +++ b/tests/Utils/DateTime.JSON.phpt @@ -14,4 +14,4 @@ require __DIR__ . '/../bootstrap.php'; date_default_timezone_set('Europe/Prague'); -Assert::same('"1978-01-23T11:40:00+01:00"', json_encode(DateTime::from(254400000))); +Assert::same('"1978-01-23T11:40:00+01:00"', json_encode(DateTime::from(254_400_000))); diff --git a/tests/Utils/DateTime.from.phpt b/tests/Utils/DateTime.from.phpt index 9cf9adc25..7a296ee01 100644 --- a/tests/Utils/DateTime.from.phpt +++ b/tests/Utils/DateTime.from.phpt @@ -14,14 +14,14 @@ require __DIR__ . '/../bootstrap.php'; date_default_timezone_set('Europe/Prague'); -Assert::same('1978-01-23 11:40:00', (string) DateTime::from(254400000)); -Assert::same('1978-01-23 11:40:00', (string) (new DateTime)->setTimestamp(254400000)); -Assert::same(254400000, DateTime::from(254400000)->getTimestamp()); +Assert::same('1978-01-23 11:40:00', (string) DateTime::from(254_400_000)); +Assert::same('1978-01-23 11:40:00', (string) (new DateTime)->setTimestamp(254_400_000)); +Assert::same(254_400_000, DateTime::from(254_400_000)->getTimestamp()); Assert::same(time() + 60, (int) DateTime::from(60)->format('U')); -Assert::same(PHP_VERSION_ID < 80100 ? '2050-08-13 11:40:00' : '2050-08-13 12:40:00', (string) DateTime::from(2544000000)); -Assert::same(PHP_VERSION_ID < 80100 ? '2050-08-13 11:40:00' : '2050-08-13 12:40:00', (string) (new DateTime)->setTimestamp(2544000000)); -Assert::same(is_int(2544000000) ? 2544000000 : '2544000000', DateTime::from(2544000000)->getTimestamp()); // 64 bit +Assert::same(PHP_VERSION_ID < 80100 ? '2050-08-13 11:40:00' : '2050-08-13 12:40:00', (string) DateTime::from(2_544_000_000)); +Assert::same(PHP_VERSION_ID < 80100 ? '2050-08-13 11:40:00' : '2050-08-13 12:40:00', (string) (new DateTime)->setTimestamp(2_544_000_000)); +Assert::same(is_int(2_544_000_000) ? 2_544_000_000 : '2544000000', DateTime::from(2_544_000_000)->getTimestamp()); // 64 bit Assert::same('1978-05-05 00:00:00', (string) DateTime::from('1978-05-05')); diff --git a/tests/Utils/DateTime.modifyClone.phpt b/tests/Utils/DateTime.modifyClone.phpt index 7e7fbb34b..b3ed3fbc2 100644 --- a/tests/Utils/DateTime.modifyClone.phpt +++ b/tests/Utils/DateTime.modifyClone.phpt @@ -14,7 +14,7 @@ require __DIR__ . '/../bootstrap.php'; date_default_timezone_set('Europe/Prague'); -$date = DateTime::from(254400000); +$date = DateTime::from(254_400_000); $dolly = $date->modifyClone(); Assert::type(DateTime::class, $dolly); Assert::notSame($date, $dolly); diff --git a/tests/Utils/Json.decode().phpt b/tests/Utils/Json.decode().phpt index ec0368237..e3ac443c9 100644 --- a/tests/Utils/Json.decode().phpt +++ b/tests/Utils/Json.decode().phpt @@ -66,7 +66,7 @@ Assert::exception(function () { if (defined('JSON_C_VERSION')) { if (PHP_INT_SIZE > 4) { // 64-bit - Assert::same([9223372036854775807], Json::decode('[12345678901234567890]')); // trimmed to max 64-bit integer + Assert::same([9_223_372_036_854_775_807], Json::decode('[12345678901234567890]')); // trimmed to max 64-bit integer } else { // 32-bit Assert::same(['9223372036854775807'], Json::decode('[12345678901234567890]')); // trimmed to max 64-bit integer diff --git a/tests/Utils/Reflection.expandClassName.phpt b/tests/Utils/Reflection.expandClassName.phpt index 904c1b125..389ff39d3 100644 --- a/tests/Utils/Reflection.expandClassName.phpt +++ b/tests/Utils/Reflection.expandClassName.phpt @@ -140,21 +140,21 @@ foreach ($cases as $alias => $fqn) { Assert::same( ['C' => 'A\B'], - Reflection::getUseStatements(new ReflectionClass('Test')) + Reflection::getUseStatements(new ReflectionClass('Test')), ); Assert::same( [], - Reflection::getUseStatements(new ReflectionClass('Test\Space\Foo')) + Reflection::getUseStatements(new ReflectionClass('Test\Space\Foo')), ); Assert::same( ['AAA' => 'AAA', 'B' => 'BBB', 'DDD' => 'CCC\DDD', 'F' => 'EEE\FFF', 'G' => 'GGG'], - Reflection::getUseStatements(new ReflectionClass('Test\Space\Bar')) + Reflection::getUseStatements(new ReflectionClass('Test\Space\Bar')), ); Assert::same( [], - Reflection::getUseStatements(new ReflectionClass('stdClass')) + Reflection::getUseStatements(new ReflectionClass('stdClass')), ); Assert::exception(function () use ($rcTest) { diff --git a/tests/Utils/Reflection.groupUseStatements.phpt b/tests/Utils/Reflection.groupUseStatements.phpt index 7332b69bd..560985937 100644 --- a/tests/Utils/Reflection.groupUseStatements.phpt +++ b/tests/Utils/Reflection.groupUseStatements.phpt @@ -16,5 +16,5 @@ require __DIR__ . '/fixtures.reflection/expandClass.groupUse.php'; Assert::same( ['A' => 'A\B\A', 'C' => 'A\B\B\C', 'D' => 'A\B\C', 'E' => 'D\E'], - Reflection::getUseStatements(new ReflectionClass('GroupUseTest')) + Reflection::getUseStatements(new ReflectionClass('GroupUseTest')), ); diff --git a/tests/Utils/Reflection.nonClassUseStatements.phpt b/tests/Utils/Reflection.nonClassUseStatements.phpt index c6ce04430..bad5b7ace 100644 --- a/tests/Utils/Reflection.nonClassUseStatements.phpt +++ b/tests/Utils/Reflection.nonClassUseStatements.phpt @@ -16,5 +16,5 @@ require __DIR__ . '/fixtures.reflection/expandClass.nonClassUse.php'; Assert::same( [], - Reflection::getUseStatements(new ReflectionClass('NonClassUseTest')) + Reflection::getUseStatements(new ReflectionClass('NonClassUseTest')), ); diff --git a/tests/Utils/Strings.replace().phpt b/tests/Utils/Strings.replace().phpt index c1b94b504..5dda17322 100644 --- a/tests/Utils/Strings.replace().phpt +++ b/tests/Utils/Strings.replace().phpt @@ -24,7 +24,7 @@ class Test Assert::same('hello world!', Strings::replace('hello world!', '#([E-L])+#', '#')); Assert::same('#o wor#d!', Strings::replace('hello world!', ['#([e-l])+#'], '#')); Assert::same('#o wor#d!', Strings::replace('hello world!', '#([e-l])+#', '#')); -Assert::same('@o wor@d!', Strings::replace('hello world!', '#[e-l]+#', function () { return '@'; })); +Assert::same('@o wor@d!', Strings::replace('hello world!', '#[e-l]+#', fn() => '@')); Assert::same('@o wor@d!', Strings::replace('hello world!', '#[e-l]+#', Closure::fromCallable('Test::cb'))); Assert::same('@o wor@d!', Strings::replace('hello world!', ['#[e-l]+#'], Closure::fromCallable('Test::cb'))); Assert::same('@o wor@d!', Strings::replace('hello world!', '#[e-l]+#', ['Test', 'cb'])); diff --git a/tests/Utils/Type.fromReflection.method.phpt b/tests/Utils/Type.fromReflection.method.phpt index 4d665a2f6..43e0cedad 100644 --- a/tests/Utils/Type.fromReflection.method.phpt +++ b/tests/Utils/Type.fromReflection.method.phpt @@ -35,5 +35,5 @@ $class = new class { }; $type = Type::fromReflection((new ReflectionObject($class))->getMethod('foo')); -Assert::same([get_class($class)], $type->getNames()); -Assert::same(get_class($class), (string) $type); +Assert::same([$class::class], $type->getNames()); +Assert::same($class::class, (string) $type); diff --git a/tests/Utils/fixtures.reflection/defaultValue.php b/tests/Utils/fixtures.reflection/defaultValue.php index 196ab13de..f2e646ad4 100644 --- a/tests/Utils/fixtures.reflection/defaultValue.php +++ b/tests/Utils/fixtures.reflection/defaultValue.php @@ -41,7 +41,7 @@ public function method( $m = DEFINED, $n = UNDEFINED, $o = NS_DEFINED, - $p = parent::PUBLIC_DEFINED + $p = parent::PUBLIC_DEFINED, ) { } } From 5b6cccd9abefc786244f9537f609e598a099b926 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Mar 2021 19:15:56 +0100 Subject: [PATCH 07/24] added property typehints --- src/Iterators/CachingIterator.php | 3 +-- src/Utils/ArrayList.php | 3 +-- src/Utils/Html.php | 12 +++++------- src/Utils/Image.php | 3 +-- src/Utils/Paginator.php | 12 ++++-------- src/Utils/Type.php | 11 +++-------- tests/Utils/SmartObject.arrayProperty.phpt | 2 +- 7 files changed, 16 insertions(+), 30 deletions(-) diff --git a/src/Iterators/CachingIterator.php b/src/Iterators/CachingIterator.php index cf8f6785f..f368d1a60 100644 --- a/src/Iterators/CachingIterator.php +++ b/src/Iterators/CachingIterator.php @@ -28,8 +28,7 @@ class CachingIterator extends \CachingIterator implements \Countable { use Nette\SmartObject; - /** @var int */ - private $counter = 0; + private int $counter = 0; public function __construct($iterator) diff --git a/src/Utils/ArrayList.php b/src/Utils/ArrayList.php index de9220922..938a921fd 100644 --- a/src/Utils/ArrayList.php +++ b/src/Utils/ArrayList.php @@ -20,8 +20,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate { use Nette\SmartObject; - /** @var mixed[] */ - private $list = []; + private array $list = []; /** diff --git a/src/Utils/Html.php b/src/Utils/Html.php index b052b1735..67c9e9036 100644 --- a/src/Utils/Html.php +++ b/src/Utils/Html.php @@ -238,10 +238,9 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab /** @var array element's attributes */ public $attrs = []; - /** @var bool use XHTML syntax? */ - public static $xhtml = false; + public static bool $xhtml = false; - /** @var array void elements */ + /** void elements */ public static $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, @@ -251,11 +250,10 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab /** @var array nodes */ protected $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; /** diff --git a/src/Utils/Image.php b/src/Utils/Image.php index 15de610d0..dc5894308 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -126,8 +126,7 @@ class Image private const FORMATS = [self::JPEG => 'jpeg', self::PNG => 'png', self::GIF => 'gif', self::WEBP => 'webp', self::AVIF => 'avif', self::BMP => 'bmp']; - /** @var resource|\GdImage */ - private $image; + private \GdImage $image; /** diff --git a/src/Utils/Paginator.php b/src/Utils/Paginator.php index 4517a8cc3..636570ac3 100644 --- a/src/Utils/Paginator.php +++ b/src/Utils/Paginator.php @@ -34,17 +34,13 @@ class Paginator { use Nette\SmartObject; - /** @var int */ - private $base = 1; + private int $base = 1; - /** @var int */ - private $itemsPerPage = 1; + private int $itemsPerPage = 1; - /** @var int */ - private $page = 1; + private int $page = 1; - /** @var int|null */ - private $itemCount; + private ?int $itemCount = null; /** diff --git a/src/Utils/Type.php b/src/Utils/Type.php index 9e7822f8e..067faa2b2 100644 --- a/src/Utils/Type.php +++ b/src/Utils/Type.php @@ -17,14 +17,9 @@ */ final class Type { - /** @var array */ - private $types; - - /** @var bool */ - private $single; - - /** @var string |, & */ - private $kind; + private array $types; + private bool $single; + private string $kind; // | & /** diff --git a/tests/Utils/SmartObject.arrayProperty.phpt b/tests/Utils/SmartObject.arrayProperty.phpt index bc6f0ba1c..f935ff943 100644 --- a/tests/Utils/SmartObject.arrayProperty.phpt +++ b/tests/Utils/SmartObject.arrayProperty.phpt @@ -18,7 +18,7 @@ class TestClass { use Nette\SmartObject; - private $items = []; + private array $items = []; public function &getItems() From 7cdc0a917e2da52f73da03b82c63a7973621d20f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 12 Dec 2021 18:05:45 +0100 Subject: [PATCH 08/24] added PHP 8 typehints --- src/Iterators/CachingIterator.php | 6 +- src/Iterators/Mapper.php | 3 +- src/SmartObject.php | 20 ++--- src/StaticClass.php | 3 +- src/Translator.php | 4 +- src/Utils/ArrayHash.php | 6 +- src/Utils/ArrayList.php | 8 +- src/Utils/Arrays.php | 43 ++++------ src/Utils/Callback.php | 13 +-- src/Utils/DateTime.php | 25 ++---- src/Utils/Helpers.php | 10 +-- src/Utils/Html.php | 75 +++++------------ src/Utils/Image.php | 83 +++++++------------ src/Utils/Json.php | 6 +- src/Utils/ObjectHelpers.php | 4 +- src/Utils/Paginator.php | 12 +-- src/Utils/Reflection.php | 10 +-- src/Utils/Strings.php | 10 ++- src/Utils/Type.php | 13 +-- src/Utils/Validators.php | 31 +++---- tests/Utils/Arrays.get().phpt | 2 +- tests/Utils/Arrays.getKeyOffset().phpt | 1 - tests/Utils/Arrays.getRef().phpt | 2 +- tests/Utils/Arrays.pick().phpt | 2 +- tests/Utils/Arrays.renameKey().phpt | 2 +- tests/Utils/DateTime.createFromFormat.phpt | 4 - .../Strings.replace().errors.callback.phpt | 6 -- 27 files changed, 136 insertions(+), 268 deletions(-) diff --git a/src/Iterators/CachingIterator.php b/src/Iterators/CachingIterator.php index f368d1a60..55368d9d7 100644 --- a/src/Iterators/CachingIterator.php +++ b/src/Iterators/CachingIterator.php @@ -147,9 +147,8 @@ public function rewind(): void /** * Returns the next key. - * @return mixed */ - public function getNextKey() + public function getNextKey(): mixed { return $this->getInnerIterator()->key(); } @@ -157,9 +156,8 @@ public function getNextKey() /** * Returns the next element. - * @return mixed */ - public function getNextValue() + public function getNextValue(): mixed { return $this->getInnerIterator()->current(); } diff --git a/src/Iterators/Mapper.php b/src/Iterators/Mapper.php index 1a8647c60..87823baf9 100644 --- a/src/Iterators/Mapper.php +++ b/src/Iterators/Mapper.php @@ -27,8 +27,7 @@ public function __construct(\Traversable $iterator, callable $callback) } - #[\ReturnTypeWillChange] - public function current() + public function current(): mixed { return ($this->callback)(parent::current(), parent::key()); } diff --git a/src/SmartObject.php b/src/SmartObject.php index 7d4f30368..f7824c981 100644 --- a/src/SmartObject.php +++ b/src/SmartObject.php @@ -24,7 +24,7 @@ trait SmartObject /** * @throws MemberAccessException */ - public function __call(string $name, array $args) + public function __call(string $name, array $args): mixed { $class = static::class; @@ -37,26 +37,27 @@ public function __call(string $name, array $args) } elseif ($handlers !== null) { throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . gettype($handlers) . ' given.'); } - } else { - ObjectHelpers::strictCall($class, $name); + + return null; } + + ObjectHelpers::strictCall($class, $name); } /** * @throws MemberAccessException */ - public static function __callStatic(string $name, array $args) + public static function __callStatic(string $name, array $args): mixed { ObjectHelpers::strictStaticCall(static::class, $name); } /** - * @return mixed * @throws MemberAccessException if the property is not defined. */ - public function &__get(string $name) + public function &__get(string $name): mixed { $class = static::class; @@ -87,11 +88,9 @@ public function &__get(string $name) /** - * @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 +120,9 @@ public function __set(string $name, $value) /** - * @return void * @throws MemberAccessException */ - public function __unset(string $name) + public function __unset(string $name): void { $class = static::class; if (!ObjectHelpers::hasProperty($class, $name)) { diff --git a/src/StaticClass.php b/src/StaticClass.php index 8fed0ef31..c719e3b6f 100644 --- a/src/StaticClass.php +++ b/src/StaticClass.php @@ -27,10 +27,9 @@ final public function __construct() /** * Call to undefined static method. - * @return void * @throws MemberAccessException */ - public static function __callStatic(string $name, array $args) + public static function __callStatic(string $name, array $args): mixed { Utils\ObjectHelpers::strictStaticCall(static::class, $name); } diff --git a/src/Translator.php b/src/Translator.php index 0f343a274..e8c5f7d06 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -17,10 +17,8 @@ interface Translator { /** * Translates the given string. - * @param mixed $message - * @param mixed ...$parameters */ - function translate($message, ...$parameters): string; + function translate(mixed $message, mixed ...$parameters): string; } diff --git a/src/Utils/ArrayHash.php b/src/Utils/ArrayHash.php index 46cad2ba0..a2ed381a7 100644 --- a/src/Utils/ArrayHash.php +++ b/src/Utils/ArrayHash.php @@ -21,9 +21,8 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator /** * Transforms array to ArrayHash. * @param array $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) { @@ -75,8 +74,7 @@ public function offsetSet($key, $value): void * @param string|int $key * @return T */ - #[\ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet($key): mixed { return $this->$key; } diff --git a/src/Utils/ArrayList.php b/src/Utils/ArrayList.php index 938a921fd..54684c751 100644 --- a/src/Utils/ArrayList.php +++ b/src/Utils/ArrayList.php @@ -26,9 +26,8 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate /** * Transforms array to ArrayList. * @param array $array - * @return static */ - 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.'); @@ -85,8 +84,7 @@ public function offsetSet($index, $value): void * @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'); @@ -125,7 +123,7 @@ public function offsetUnset($index): void * Prepends a 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); diff --git a/src/Utils/Arrays.php b/src/Utils/Arrays.php index 2aea25fa9..9f79fbe8f 100644 --- a/src/Utils/Arrays.php +++ b/src/Utils/Arrays.php @@ -29,7 +29,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 +55,7 @@ public static function get(array $array, $key, $default = null) * @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,10 +94,8 @@ public static function mergeTree(array $array1, array $array2): array /** * 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)); } @@ -114,9 +112,8 @@ public static function searchKey(array $array, $key): ?int /** * 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); } @@ -128,7 +125,7 @@ public static function contains(array $array, $value): bool * @param array $array * @return ?T */ - public static function first(array $array) + public static function first(array $array): mixed { return count($array) ? reset($array) : null; } @@ -140,7 +137,7 @@ public static function first(array $array) * @param array $array * @return ?T */ - public static function last(array $array) + public static function last(array $array): mixed { return count($array) ? end($array) : null; } @@ -149,9 +146,8 @@ public static function last(array $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) @@ -163,9 +159,8 @@ public static function insertBefore(array &$array, $key, array $inserted): void /** * 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; @@ -179,10 +174,8 @@ public static function insertAfter(array &$array, $key, array $inserted): void /** * 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) { @@ -225,9 +218,8 @@ public static function flatten(array $array, bool $preserveKeys = false): array /** * Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list. - * @param mixed $value */ - 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) @@ -239,9 +231,8 @@ public static function isList($value): bool /** * 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 +284,8 @@ public static function associate(array $array, $path) /** * 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 +301,11 @@ public static function normalize(array $array, $filling = null): array * or returns $default, if provided. * @template T * @param array $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]; @@ -415,7 +404,7 @@ public static function invokeMethod(iterable $objects, string $method, ...$args) * @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,10 +416,8 @@ public static function toObject(iterable $array, $object) /** * 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]); } diff --git a/src/Utils/Callback.php b/src/Utils/Callback.php index 23ef8a54c..7cbcb97db 100644 --- a/src/Utils/Callback.php +++ b/src/Utils/Callback.php @@ -22,9 +22,8 @@ final class Callback /** * 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__) { @@ -51,11 +50,10 @@ public static function invokeSafe(string $function, array $args, callable $onErr /** * 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( @@ -71,9 +69,8 @@ public static function check($callable, bool $syntax = false) /** * 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); @@ -88,7 +85,6 @@ public static function toString($callable): string /** * 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 @@ -120,9 +116,8 @@ public static function isStatic(callable $callable): bool /** * 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) === '}') { diff --git a/src/Utils/DateTime.php b/src/Utils/DateTime.php index e53f41de0..73f0832bb 100644 --- a/src/Utils/DateTime.php +++ b/src/Utils/DateTime.php @@ -40,11 +40,9 @@ class DateTime extends \DateTime implements \JsonSerializable /** * 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()); @@ -64,7 +62,6 @@ public static function from($time) /** * Creates DateTime object. - * @return static * @throws Nette\InvalidArgumentException if the date and time are not valid. */ public static function fromParts( @@ -74,7 +71,7 @@ public static function fromParts( int $hour = 0, int $minute = 0, float $second = 0.0, - ) { + ): static { $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second); if ( !checkdate($month, $day, $year) @@ -94,22 +91,17 @@ public static function fromParts( /** * 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 */ - #[\ReturnTypeWillChange] - public static function createFromFormat($format, $time, $timezone = null) - { + public static function createFromFormat( + string $format, + string $time, + string|\DateTimeZone|null $timezone = null, + ): static|false { if ($timezone === null) { $timezone = new \DateTimeZone(date_default_timezone_get()); } elseif (is_string($timezone)) { $timezone = new \DateTimeZone($timezone); - - } elseif (!$timezone instanceof \DateTimeZone) { - throw new Nette\InvalidArgumentException('Invalid timezone given'); } $date = parent::createFromFormat($format, $time, $timezone); @@ -137,9 +129,8 @@ public function __toString(): string /** * Creates a copy with a modified time. - * @return static */ - public function modifyClone(string $modify = '') + public function modifyClone(string $modify = ''): static { $dolly = clone $this; return $modify ? $dolly->modify($modify) : $dolly; diff --git a/src/Utils/Helpers.php b/src/Utils/Helpers.php index 224a1fe5c..f27b9e7be 100644 --- a/src/Utils/Helpers.php +++ b/src/Utils/Helpers.php @@ -45,10 +45,8 @@ public static function getLastError(): string /** * 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 +54,8 @@ public static function falseToNull($value) /** * 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)."); diff --git a/src/Utils/Html.php b/src/Utils/Html.php index 67c9e9036..28f3e41d5 100644 --- a/src/Utils/Html.php +++ b/src/Utils/Html.php @@ -259,9 +259,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab /** * 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); @@ -287,7 +286,7 @@ public static function el(?string $name = null, $attrs = null) /** * 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); } @@ -296,7 +295,7 @@ public static function fromHtml(string $html): self /** * 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); } @@ -331,9 +330,8 @@ public static function htmlToText(string $html): string /** * 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]); @@ -361,9 +359,8 @@ final public function isEmpty(): bool /** * 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; @@ -372,11 +369,8 @@ public function addAttributes(array $attrs) /** * 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] : []; @@ -398,10 +392,8 @@ public function appendAttribute(string $name, $value, $option = true) /** * 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; @@ -410,9 +402,8 @@ public function setAttribute(string $name, $value) /** * Returns element's attribute. - * @return mixed */ - public function getAttribute(string $name) + public function getAttribute(string $name): mixed { return $this->attrs[$name] ?? null; } @@ -420,9 +411,8 @@ public function getAttribute(string $name) /** * Unsets element's attribute. - * @return static */ - public function removeAttribute(string $name) + public function removeAttribute(string $name): static { unset($this->attrs[$name]); return $this; @@ -431,9 +421,8 @@ public function removeAttribute(string $name) /** * 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]); @@ -445,9 +434,8 @@ public function removeAttributes(array $attributes) /** * 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; } @@ -455,9 +443,8 @@ final public function __set(string $name, $value): void /** * 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]; } @@ -483,9 +470,8 @@ final public function __unset(string $name): void /** * 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') { @@ -514,9 +500,8 @@ final public function __call(string $m, array $args) /** * 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 = null): static { if ($query) { $query = http_build_query($query, '', '&'); @@ -532,10 +517,8 @@ final public function href(string $path, ?array $query = null) /** * 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; @@ -551,10 +534,8 @@ public function data(string $name, $value = null) /** * Sets element's HTML content. - * @param HtmlStringable|string $html - * @return static */ - final public function setHtml($html) + final public function setHtml(HtmlStringable|string $html): static { $this->children = [(string) $html]; return $this; @@ -572,10 +553,8 @@ final public function getHtml(): string /** * Sets element's textual content. - * @param HtmlStringable|string|int|float $text - * @return static */ - final public function setText($text) + final public function setText(HtmlStringable|string|int|float $text): static { if (!$text instanceof HtmlStringable) { $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); @@ -597,10 +576,8 @@ final public function getText(): string /** * 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); } @@ -608,10 +585,8 @@ final public function addHtml($child) /** * Appends plain-text string to element content. - * @param HtmlStringable|string|int|float $text - * @return static */ - public function addText($text) + public function addText(HtmlStringable|string|int|float $text): static { if (!$text instanceof HtmlStringable) { $text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8'); @@ -623,10 +598,8 @@ public function addText($text) /** * 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; @@ -635,10 +608,8 @@ final public function create(string $name, $attrs = null) /** * 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 @@ -666,10 +637,8 @@ final public function offsetSet($index, $child): void /** * 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]; } diff --git a/src/Utils/Image.php b/src/Utils/Image.php index dc5894308..469d332a0 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -92,7 +92,7 @@ * @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 + * @property-read \GdImage $imageResource */ class Image { @@ -147,9 +147,8 @@ public static function rgb(int $red, int $green, int $blue, int $transparency = * 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.'); @@ -166,11 +165,10 @@ public static function fromFile(string $file, ?int &$type = null) /** * 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.'); @@ -185,7 +183,7 @@ public static function fromString(string $s, ?int &$type = null) } - 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 { @@ -204,10 +202,9 @@ private static function invokeSafe(string $func, string $arg, string $message, s /** * Creates a new true color image of the given dimensions. The default color is black. - * @return static * @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, ?array $color = null): static { if (!extension_loaded('gd')) { throw new Nette\NotSupportedException('PHP extension GD is not loaded.'); @@ -274,9 +271,8 @@ public static function typeToMimeType(int $type): string /** * Wraps GD image. - * @param resource|\GdImage $image */ - public function __construct($image) + public function __construct(\GdImage $image) { $this->setImageResource($image); imagesavealpha($image, true); @@ -303,15 +299,9 @@ public function getHeight(): int /** * 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; } @@ -319,21 +309,17 @@ protected function setImageResource($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. */ - public function resize($width, $height, int $flags = self::FIT) + public function resize(int|string|null $width, int|string|null $height, int $flags = self::FIT): static { if ($flags & self::EXACT) { return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height); @@ -367,9 +353,7 @@ public function resize($width, $height, int $flags = self::FIT) /** - * 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. */ public static function calculateSize( int $srcWidth, @@ -435,14 +419,9 @@ public static function calculateSize( /** - * 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); @@ -460,14 +439,16 @@ public function crop($left, $top, $width, $height) /** - * 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); } @@ -502,9 +483,8 @@ public static function calculateCutout(int $srcWidth, int $srcHeight, $left, $to /** * Sharpens image a little bit. - * @return static */ - public function sharpen() + public function sharpen(): static { imageconvolution($this->image, [ // my magic numbers ;) [-1, -1, -1], @@ -516,13 +496,10 @@ public function sharpen() /** - * Puts another image into this image. - * @param int|string $left in pixels or percent - * @param int|string $top in pixels or percent + * Puts another image into this image. Left and top accepts pixels or percent. * @param int $opacity 0..100 - * @return static */ - 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) { @@ -678,10 +655,9 @@ private function output(int $type, ?int $quality, ?string $file = null): void /** * 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)) { @@ -710,7 +686,7 @@ public function __call(string $name, array $args) } $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; } @@ -724,10 +700,7 @@ public function __clone() } - /** - * @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) === '%') { $num = (float) substr($num, 0, -1); diff --git a/src/Utils/Json.php b/src/Utils/Json.php index 91b8f56d3..3a9850b7a 100644 --- a/src/Utils/Json.php +++ b/src/Utils/Json.php @@ -27,10 +27,9 @@ final class Json /** * 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 * @throws JsonException */ - public static function encode($value, int $flags = 0): string + public static function encode(mixed $value, int $flags = 0): string { $flags = ($flags & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE) | JSON_UNESCAPED_SLASHES @@ -48,10 +47,9 @@ public static function encode($value, int $flags = 0): string /** * 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 * @throws JsonException */ - public static function decode(string $json, int $flags = 0) + public static function decode(string $json, int $flags = 0): mixed { $value = json_decode($json, null, 512, $flags | JSON_BIGINT_AS_STRING); if ($error = json_last_error()) { diff --git a/src/Utils/ObjectHelpers.php b/src/Utils/ObjectHelpers.php index a85184b1f..f1969c2d2 100644 --- a/src/Utils/ObjectHelpers.php +++ b/src/Utils/ObjectHelpers.php @@ -205,10 +205,10 @@ private static function parseFullDoc(\ReflectionClass $rc, string $pattern): arr /** * 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]; diff --git a/src/Utils/Paginator.php b/src/Utils/Paginator.php index 636570ac3..c0e031828 100644 --- a/src/Utils/Paginator.php +++ b/src/Utils/Paginator.php @@ -45,9 +45,8 @@ class Paginator /** * Sets current page number. - * @return static */ - public function setPage(int $page) + public function setPage(int $page): static { $this->page = $page; return $this; @@ -105,9 +104,8 @@ public function getLastItemOnPage(): int /** * Sets first page (base) number. - * @return static */ - public function setBase(int $base) + public function setBase(int $base): static { $this->base = $base; return $this; @@ -168,9 +166,8 @@ public function getPageCount(): ?int /** * 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; @@ -188,9 +185,8 @@ public function getItemsPerPage(): int /** * 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; diff --git a/src/Utils/Reflection.php b/src/Utils/Reflection.php index d7a6c4943..ce92a727e 100644 --- a/src/Utils/Reflection.php +++ b/src/Utils/Reflection.php @@ -82,10 +82,7 @@ public static function getPropertyType(\ReflectionProperty $prop): ?string } - /** - * @param \ReflectionFunction|\ReflectionMethod|\ReflectionParameter|\ReflectionProperty $reflection - */ - private static function getType($reflection, ?\ReflectionType $type): ?string + private static function getType(\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection, ?\ReflectionType $type): ?string { if ($type === null) { return null; @@ -104,10 +101,9 @@ private static function getType($reflection, ?\ReflectionType $type): ?string /** * 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(); @@ -374,7 +370,7 @@ private static function parseUseStatements(string $code, ?string $forClass = nul } - 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)) { diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index ee7c493cf..23e67eb9e 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -528,11 +528,13 @@ public static function matchAll(string $subject, string $pattern, int $flags = 0 /** * 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, + string|array $pattern, + string|callable $replacement = '', + int $limit = -1, + ): string { if (is_object($replacement) || is_array($replacement)) { if (!is_callable($replacement, false, $textual)) { throw new Nette\InvalidStateException("Callback '$textual' is not callable."); diff --git a/src/Utils/Type.php b/src/Utils/Type.php index 067faa2b2..a9f1f355c 100644 --- a/src/Utils/Type.php +++ b/src/Utils/Type.php @@ -25,10 +25,10 @@ final class Type /** * 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 \ReflectionMethod) { $type = $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 ? $reflection->getTentativeReturnType() : null); } else { @@ -84,10 +84,11 @@ public static function fromString(string $type): self /** * 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 $reflection, + ): string { $lower = strtolower($type); if ($reflection instanceof \ReflectionFunction) { return $type; diff --git a/src/Utils/Validators.php b/src/Utils/Validators.php index def992495..ff5e6e643 100644 --- a/src/Utils/Validators.php +++ b/src/Utils/Validators.php @@ -87,10 +87,9 @@ 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); @@ -110,7 +109,6 @@ public static function assert($value, string $expected, string $label = 'variabl /** * 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( @@ -130,9 +128,8 @@ public static function assertField( /** * 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) === '[]') { @@ -208,9 +205,8 @@ public static function everyIs(iterable $values, string $expected): bool /** * Checks if the value is an integer or a float. - * @param mixed $value */ - public static function isNumber($value): bool + public static function isNumber(mixed $value): bool { return is_int($value) || is_float($value); } @@ -218,9 +214,8 @@ public static function isNumber($value): bool /** * Checks if the value is an integer or a integer written in a string. - * @param mixed $value */ - 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 +223,8 @@ public static function isNumericInt($value): bool /** * Checks if the value is a number or a number written in a string. - * @param mixed $value */ - 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,9 +232,8 @@ public static function isNumeric($value): bool /** * 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); } @@ -248,9 +241,8 @@ public static function isCallable($value): bool /** * 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 +250,8 @@ public static function isUnicode($value): bool /** * Checks if the value is 0, '', false or null. - * @param mixed $value */ - public static function isNone($value): bool + public static function isNone(mixed $value): bool { return $value == null; // intentionally == } @@ -275,10 +266,9 @@ public static function isMixed(): bool /** * Checks if a variable is a zero-based integer indexed array. - * @param mixed $value * @deprecated use Nette\Utils\Arrays::isList */ - public static function isList($value): bool + public static function isList(mixed $value): bool { return Arrays::isList($value); } @@ -287,9 +277,8 @@ public static function isList($value): bool /** * 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; diff --git a/tests/Utils/Arrays.get().phpt b/tests/Utils/Arrays.get().phpt index 277af3bf3..9a3195a30 100644 --- a/tests/Utils/Arrays.get().phpt +++ b/tests/Utils/Arrays.get().phpt @@ -22,7 +22,7 @@ $arr = [ ]; test('Single item', function () use ($arr) { - Assert::same('first', Arrays::get($arr, null)); + Assert::same('first', Arrays::get($arr, '')); Assert::same('second', Arrays::get($arr, 1)); Assert::same('second', Arrays::get($arr, 1, 'x')); Assert::same('x', Arrays::get($arr, 'undefined', 'x')); diff --git a/tests/Utils/Arrays.getKeyOffset().phpt b/tests/Utils/Arrays.getKeyOffset().phpt index 357467755..2a2826f00 100644 --- a/tests/Utils/Arrays.getKeyOffset().phpt +++ b/tests/Utils/Arrays.getKeyOffset().phpt @@ -24,6 +24,5 @@ Assert::same(3, Arrays::getKeyOffset($arr, '1')); Assert::same(3, Arrays::getKeyOffset($arr, 1)); Assert::same(2, Arrays::getKeyOffset($arr, 7)); Assert::same(1, Arrays::getKeyOffset($arr, 0)); -Assert::same(0, Arrays::getKeyOffset($arr, null)); Assert::same(0, Arrays::getKeyOffset($arr, '')); Assert::null(Arrays::getKeyOffset($arr, 'undefined')); diff --git a/tests/Utils/Arrays.getRef().phpt b/tests/Utils/Arrays.getRef().phpt index ad242bb90..d45af2b53 100644 --- a/tests/Utils/Arrays.getRef().phpt +++ b/tests/Utils/Arrays.getRef().phpt @@ -23,7 +23,7 @@ $arr = [ test('Single item', function () use ($arr) { $dolly = $arr; - $item = &Arrays::getRef($dolly, null); + $item = &Arrays::getRef($dolly, ''); $item = 'changed'; Assert::same([ '' => 'changed', diff --git a/tests/Utils/Arrays.pick().phpt b/tests/Utils/Arrays.pick().phpt index b81ea0d89..787400df3 100644 --- a/tests/Utils/Arrays.pick().phpt +++ b/tests/Utils/Arrays.pick().phpt @@ -20,7 +20,7 @@ $arr = [ ]; test('Single item', function () use ($arr) { - Assert::same('null', Arrays::pick($arr, null)); + Assert::same('null', Arrays::pick($arr, '')); Assert::same('first', Arrays::pick($arr, 1)); Assert::same('x', Arrays::pick($arr, 1, 'x')); Assert::exception(function () use ($arr) { diff --git a/tests/Utils/Arrays.renameKey().phpt b/tests/Utils/Arrays.renameKey().phpt index c852df90f..64f58ee35 100644 --- a/tests/Utils/Arrays.renameKey().phpt +++ b/tests/Utils/Arrays.renameKey().phpt @@ -36,7 +36,7 @@ Assert::same([ 'new1' => 'third', ], $arr); -Arrays::renameKey($arr, null, 'new3'); +Arrays::renameKey($arr, '', 'new3'); Assert::same([ 'new3' => 'first', 'new2' => 'second', diff --git a/tests/Utils/DateTime.createFromFormat.phpt b/tests/Utils/DateTime.createFromFormat.phpt index 08ffdd709..321d43f99 100644 --- a/tests/Utils/DateTime.createFromFormat.phpt +++ b/tests/Utils/DateTime.createFromFormat.phpt @@ -22,8 +22,4 @@ Assert::same('2050-08-13 11:40:00.123450', DateTime::createFromFormat('Y-m-d H:i Assert::same('Europe/Prague', DateTime::createFromFormat('Y', '2050')->getTimezone()->getName()); Assert::same('Europe/Bratislava', DateTime::createFromFormat('Y', '2050', 'Europe/Bratislava')->getTimezone()->getName()); -Assert::error(function () { - DateTime::createFromFormat('Y-m-d H:i:s', '2050-08-13 11:40:00', 5); -}, Nette\InvalidArgumentException::class, 'Invalid timezone given'); - Assert::false(DateTime::createFromFormat('Y-m-d', '2014-10')); diff --git a/tests/Utils/Strings.replace().errors.callback.phpt b/tests/Utils/Strings.replace().errors.callback.phpt index d29ead08b..e3532c7aa 100644 --- a/tests/Utils/Strings.replace().errors.callback.phpt +++ b/tests/Utils/Strings.replace().errors.callback.phpt @@ -6,7 +6,6 @@ declare(strict_types=1); -use Nette\InvalidStateException; use Nette\Utils\Strings; use Tester\Assert; @@ -26,8 +25,3 @@ Assert::same('HELLO', Strings::replace('hello', '#.+#', function ($m) { preg_match('#\d#u', "0123456789\xFF"); // Malformed UTF-8 data return strtoupper($m[0]); })); - - -Assert::exception(function () { - Strings::replace('hello', '#.+#', [stdClass::class, 'foobar']); -}, InvalidStateException::class, "Callback 'stdClass::foobar' is not callable."); From c037a6fbcd6fa852646fed3435f014d67f9fad21 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 11 Mar 2021 21:55:53 +0100 Subject: [PATCH 09/24] removed community health files --- .github/ISSUE_TEMPLATE/Bug_report.md | 19 ------------- .github/ISSUE_TEMPLATE/Feature_request.md | 9 ------ .github/ISSUE_TEMPLATE/Support_question.md | 12 -------- .github/ISSUE_TEMPLATE/Support_us.md | 21 -------------- .github/funding.yml | 2 -- .github/pull_request_template.md | 11 -------- contributing.md | 33 ---------------------- 7 files changed, 107 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/Support_question.md delete mode 100644 .github/ISSUE_TEMPLATE/Support_us.md delete mode 100644 .github/funding.yml delete mode 100644 .github/pull_request_template.md delete mode 100644 contributing.md diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md deleted file mode 100644 index a4cd12634..000000000 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: "🐛 Bug Report" -about: "If something isn't working as expected 🤔" - ---- - -Version: ?.?.? - -### Bug Description -... A clear and concise description of what the bug is. A good bug report shouldn't leave others needing to chase you up for more information. - -### Steps To Reproduce -... If possible a minimal demo of the problem ... - -### Expected Behavior -... A clear and concise description of what you expected to happen. - -### Possible Solution -... Only if you have suggestions on a fix for the bug diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index d2e219489..000000000 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: "🚀 Feature Request" -about: "I have a suggestion (and may want to implement it) 🙂" - ---- - -- Is your feature request related to a problem? Please describe. -- Explain your intentions. -- It's up to you to make a strong case to convince the project's developers of the merits of this feature. diff --git a/.github/ISSUE_TEMPLATE/Support_question.md b/.github/ISSUE_TEMPLATE/Support_question.md deleted file mode 100644 index 75c48b6ed..000000000 --- a/.github/ISSUE_TEMPLATE/Support_question.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: "🤗 Support Question" -about: "If you have a question 💬, please check out our forum!" - ---- - ---------------^ Click "Preview" for a nicer view! -We primarily use GitHub as an issue tracker; for usage and support questions, please check out these resources below. Thanks! 😁. - -* Nette Forum: https://forum.nette.org -* Nette Gitter: https://gitter.im/nette/nette -* Slack (czech): https://pehapkari.slack.com/messages/C2R30BLKA diff --git a/.github/ISSUE_TEMPLATE/Support_us.md b/.github/ISSUE_TEMPLATE/Support_us.md deleted file mode 100644 index 92d8a4c3a..000000000 --- a/.github/ISSUE_TEMPLATE/Support_us.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: "❤️ Support us" -about: "If you would like to support our efforts in maintaining this project 🙌" - ---- - ---------------^ Click "Preview" for a nicer view! - -> https://nette.org/donate - -Help support Nette! - -We develop Nette Framework for more than 14 years. In order to make your life more comfortable. Nette cares about the safety of your sites. Nette saves you time. And gives job opportunities. - -Nette earns you money. And is absolutely free. - -To ensure future development and improving the documentation, we need your donation. - -Whether you are chief of IT company which benefits from Nette, or developer who goes for advice on our forum, if you like Nette, [please make a donation now](https://nette.org/donate). - -Thank you! diff --git a/.github/funding.yml b/.github/funding.yml deleted file mode 100644 index 25adc9520..000000000 --- a/.github/funding.yml +++ /dev/null @@ -1,2 +0,0 @@ -github: dg -custom: "https://nette.org/donate" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index f8aa3f408..000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,11 +0,0 @@ -- bug fix / new feature? -- BC break? yes/no -- doc PR: nette/docs#??? - - diff --git a/contributing.md b/contributing.md deleted file mode 100644 index 184152c02..000000000 --- a/contributing.md +++ /dev/null @@ -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: From 7aa0c10273691994c02162b780ee931b355c5a9d Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 22 Apr 2021 18:17:33 +0200 Subject: [PATCH 10/24] tests: added --- .../SmartObject.undeclaredMethod.native.phpt | 149 ++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 tests/Utils/SmartObject.undeclaredMethod.native.phpt diff --git a/tests/Utils/SmartObject.undeclaredMethod.native.phpt b/tests/Utils/SmartObject.undeclaredMethod.native.phpt new file mode 100644 index 000000000..d5d62e120 --- /dev/null +++ b/tests/Utils/SmartObject.undeclaredMethod.native.phpt @@ -0,0 +1,149 @@ +privateMethod(); + } + + + public function callPrivateStatic() + { + static::privateStaticMethod(); + } + + + private function callPrivateParent() + { + } +} + + +class InterClass extends ParentClass +{ + public function callParents() + { + parent::callParents(); + } +} + + +class ChildClass extends InterClass +{ + public function callParents() + { + parent::callParents(); + } + + + public function callMissingParent() + { + parent::callMissingParent(); + } + + + public static function callMissingParentStatic() + { + parent::callMissingParentStatic(); + } + + + public function callPrivateParent() + { + parent::callPrivateParent(); + } + + + protected function protectedMethod() + { + } + + + protected static function protectedStaticMethod() + { + } + + + private function privateMethod() + { + } + + + private static function privateStaticMethod() + { + } +} + + + +Assert::exception(function () { + $obj = new ParentClass; + $obj->undef(); +}, Error::class, 'Call to undefined method ParentClass::undef()'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->undef(); +}, Error::class, 'Call to undefined method ChildClass::undef()'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->callParents(); +}, Error::class, 'Call to undefined method ParentClass::callParents()'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->callMissingParent(); +}, Error::class, 'Call to undefined method InterClass::callMissingParent()'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->callMissingParentStatic(); +}, Error::class, 'Call to undefined method InterClass::callMissingParentStatic()'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj::callMissingParentStatic(); +}, Error::class, 'Call to undefined method InterClass::callMissingParentStatic()'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->callPrivateParent(); +}, Error::class, 'Call to private method ParentClass::callPrivateParent() from scope ChildClass'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->protectedMethod(); +}, Error::class, 'Call to protected method ChildClass::protectedMethod() from global scope'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->protectedStaticMethod(); +}, Error::class, 'Call to protected method ChildClass::protectedStaticMethod() from global scope'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj::protectedStaticMethod(); +}, Error::class, 'Call to protected method ChildClass::protectedStaticMethod() from global scope'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->callPrivate(); +}, Error::class, 'Call to private method ChildClass::privateMethod() from scope ParentClass'); + +Assert::exception(function () { + $obj = new ChildClass; + $obj->callPrivateStatic(); +}, Error::class, 'Call to private method ChildClass::privateStaticMethod() from scope ParentClass'); From e281d73c9e6919b79d8257000f9ccb348535254f Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 28 Jul 2021 12:42:14 +0200 Subject: [PATCH 11/24] used native PHP 8 functions --- src/Utils/Callback.php | 4 ++-- src/Utils/Html.php | 4 ++-- src/Utils/Image.php | 2 +- src/Utils/Strings.php | 6 +++--- src/Utils/Validators.php | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Utils/Callback.php b/src/Utils/Callback.php index 7cbcb97db..7841ab687 100644 --- a/src/Utils/Callback.php +++ b/src/Utils/Callback.php @@ -93,7 +93,7 @@ public static function toReflection($callable): \ReflectionFunctionAbstract $callable = self::unwrap($callable); } - if (is_string($callable) && strpos($callable, '::')) { + if (is_string($callable) && str_contains($callable, '::')) { return new \ReflectionMethod($callable); } elseif (is_array($callable)) { return new \ReflectionMethod($callable[0], $callable[1]); @@ -120,7 +120,7 @@ public static function isStatic(callable $callable): bool public static function unwrap(\Closure $closure): callable|array { $r = new \ReflectionFunction($closure); - if (substr($r->name, -1) === '}') { + if (str_ends_with($r->name, '}')) { return $closure; } elseif ($obj = $r->getClosureThis()) { diff --git a/src/Utils/Html.php b/src/Utils/Html.php index 28f3e41d5..4043fbdbb 100644 --- a/src/Utils/Html.php +++ b/src/Utils/Html.php @@ -815,14 +815,14 @@ final public function attributes(): string $value = (string) $value; } - $q = strpos($value, '"') === false ? '"' : "'"; + $q = str_contains($value, '"') ? "'" : '"'; $s .= ' ' . $key . '=' . $q . str_replace( ['&', $q, '<'], ['&', $q === '"' ? '"' : ''', self::$xhtml ? '<' : '<'], $value, ) - . (strpos($value, '`') !== false && strpbrk($value, ' <>"\'') === false ? ' ' : '') + . (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '') . $q; } diff --git a/src/Utils/Image.php b/src/Utils/Image.php index 469d332a0..3f6cdfd33 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -702,7 +702,7 @@ public function __clone() 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) { diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index 23e67eb9e..5d89a2d35 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -63,7 +63,7 @@ public static function chr(int $code): string */ public static function startsWith(string $haystack, string $needle): bool { - return strncmp($haystack, $needle, strlen($needle)) === 0; + return str_starts_with($haystack, $needle); } @@ -72,7 +72,7 @@ public static function startsWith(string $haystack, string $needle): bool */ public static function endsWith(string $haystack, string $needle): bool { - return $needle === '' || substr($haystack, -strlen($needle)) === $needle; + return str_ends_with($haystack, $needle); } @@ -81,7 +81,7 @@ public static function endsWith(string $haystack, string $needle): bool */ public static function contains(string $haystack, string $needle): bool { - return strpos($haystack, $needle) !== false; + return str_contains($haystack, $needle); } diff --git a/src/Utils/Validators.php b/src/Utils/Validators.php index ff5e6e643..362caa35a 100644 --- a/src/Utils/Validators.php +++ b/src/Utils/Validators.php @@ -132,13 +132,13 @@ public static function assertField( 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; From 48335af4afdad61a264709f20053980b0d09f9be Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 27 Aug 2021 11:40:10 +0200 Subject: [PATCH 12/24] Reflection: uses PhpToken --- src/Utils/Reflection.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Utils/Reflection.php b/src/Utils/Reflection.php index ce92a727e..0bff8d87a 100644 --- a/src/Utils/Reflection.php +++ b/src/Utils/Reflection.php @@ -284,7 +284,7 @@ public static function getUseStatements(\ReflectionClass $class): array 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 = []; @@ -297,7 +297,7 @@ private static function parseUseStatements(string $code, ?string $forClass = nul 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 = []; @@ -353,11 +353,11 @@ private static function parseUseStatements(string $code, ?string $forClass = nul case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: - case '{': + case ord('{'): $level++; break; - case '}': + case ord('}'): if ($level === $classLevel) { $class = $classLevel = null; } @@ -374,10 +374,9 @@ 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; } From ec50a824574d8fd360fce6f671ce6f9d2a604a74 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 20 Sep 2021 01:23:10 +0200 Subject: [PATCH 13/24] Reflection: getReturnType(), getParameterType(), getPropertyType() return objects Type (BC break) --- src/Utils/Reflection.php | 33 +++------------ .../Utils/Reflection.getParameterType.81.phpt | 31 +++++--------- tests/Utils/Reflection.getParameterType.phpt | 25 ++++------- .../Utils/Reflection.getPropertyType.81.phpt | 29 ++++--------- tests/Utils/Reflection.getPropertyType.phpt | 23 ++++------ tests/Utils/Reflection.getReturnType.81.phpt | 42 +++++++------------ tests/Utils/Reflection.getReturnType.phpt | 32 ++++++-------- 7 files changed, 69 insertions(+), 146 deletions(-) diff --git a/src/Utils/Reflection.php b/src/Utils/Reflection.php index 0bff8d87a..6313e3abc 100644 --- a/src/Utils/Reflection.php +++ b/src/Utils/Reflection.php @@ -51,51 +51,30 @@ public static function isClassKeyword(string $name): bool /** * 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 + public static function getReturnType(\ReflectionFunctionAbstract $func): ?Type { - $type = $func->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $func instanceof \ReflectionMethod ? $func->getTentativeReturnType() : null); - return self::getType($func, $type); + return Type::fromReflection($func); } /** * 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 + public static function getParameterType(\ReflectionParameter $param): ?Type { - return self::getType($param, $param->getType()); + return Type::fromReflection($param); } /** * 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 + public static function getPropertyType(\ReflectionProperty $prop): ?Type { - return self::getType($prop, $prop->getType()); - } - - - private static function getType(\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $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)); - } + return Type::fromReflection($prop); } diff --git a/tests/Utils/Reflection.getParameterType.81.phpt b/tests/Utils/Reflection.getParameterType.81.phpt index 15d75986f..5adb2643c 100644 --- a/tests/Utils/Reflection.getParameterType.81.phpt +++ b/tests/Utils/Reflection.getParameterType.81.phpt @@ -43,28 +43,17 @@ class AExt extends A $method = new ReflectionMethod('A', 'method'); $params = $method->getParameters(); -Assert::same('Undeclared', Reflection::getParameterType($params[0])); -Assert::same('Test\B', Reflection::getParameterType($params[1])); -Assert::same('array', Reflection::getParameterType($params[2])); -Assert::same('callable', Reflection::getParameterType($params[3])); -Assert::same('A', Reflection::getParameterType($params[4])); +Assert::same('Undeclared', (string) Reflection::getParameterType($params[0])); +Assert::same('Test\B', (string) Reflection::getParameterType($params[1])); +Assert::same('array', (string) Reflection::getParameterType($params[2])); +Assert::same('callable', (string) Reflection::getParameterType($params[3])); +Assert::same('A', (string) Reflection::getParameterType($params[4])); Assert::null(Reflection::getParameterType($params[5])); -Assert::same('Test\B', Reflection::getParameterType($params[6])); -Assert::same('mixed', Reflection::getParameterType($params[7])); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[8]); -}, Nette\InvalidStateException::class, 'The $union in A::method() is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[9]); -}, Nette\InvalidStateException::class, 'The $nullableUnion in A::method() is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[10]); -}, Nette\InvalidStateException::class, 'The $intersection in A::method() is not expected to have a union or intersection type.'); - +Assert::same('?Test\B', (string) Reflection::getParameterType($params[6])); +Assert::same('mixed', (string) Reflection::getParameterType($params[7])); +Assert::same('A|array', (string) Reflection::getParameterType($params[8], false)); +Assert::same('A|array|null', (string) Reflection::getParameterType($params[9], false)); $method = new ReflectionMethod('AExt', 'methodExt'); $params = $method->getParameters(); -Assert::same('A', Reflection::getParameterType($params[0])); +Assert::same('A', (string) Reflection::getParameterType($params[0])); diff --git a/tests/Utils/Reflection.getParameterType.phpt b/tests/Utils/Reflection.getParameterType.phpt index 9e00ed6d9..616c67325 100644 --- a/tests/Utils/Reflection.getParameterType.phpt +++ b/tests/Utils/Reflection.getParameterType.phpt @@ -41,25 +41,16 @@ class AExt extends A $method = new ReflectionMethod('A', 'method'); $params = $method->getParameters(); -Assert::same('Undeclared', Reflection::getParameterType($params[0])); -Assert::same('Test\B', Reflection::getParameterType($params[1])); -Assert::same('array', Reflection::getParameterType($params[2])); -Assert::same('callable', Reflection::getParameterType($params[3])); -Assert::same('A', Reflection::getParameterType($params[4])); +Assert::same('Undeclared', (string) Reflection::getParameterType($params[0])); +Assert::same('Test\B', (string) Reflection::getParameterType($params[1])); +Assert::same('array', (string) Reflection::getParameterType($params[2])); +Assert::same('callable', (string) Reflection::getParameterType($params[3])); +Assert::same('A', (string) Reflection::getParameterType($params[4])); Assert::null(Reflection::getParameterType($params[5])); -Assert::same('Test\B', Reflection::getParameterType($params[6])); -Assert::same('mixed', Reflection::getParameterType($params[7])); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[8]); -}, Nette\InvalidStateException::class, 'The $union in A::method() is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($params) { - Reflection::getParameterType($params[9]); -}, Nette\InvalidStateException::class, 'The $nullableUnion in A::method() is not expected to have a union or intersection type.'); - +Assert::same('?Test\B', (string) Reflection::getParameterType($params[6])); +Assert::same('mixed', (string) Reflection::getParameterType($params[7])); $method = new ReflectionMethod('AExt', 'methodExt'); $params = $method->getParameters(); -Assert::same('A', Reflection::getParameterType($params[0])); +Assert::same('A', (string) Reflection::getParameterType($params[0])); diff --git a/tests/Utils/Reflection.getPropertyType.81.phpt b/tests/Utils/Reflection.getPropertyType.81.phpt index d7905d6d6..38d42c4e0 100644 --- a/tests/Utils/Reflection.getPropertyType.81.phpt +++ b/tests/Utils/Reflection.getPropertyType.81.phpt @@ -37,27 +37,16 @@ class AExt extends A $class = new ReflectionClass('A'); $props = $class->getProperties(); -Assert::same('Undeclared', Reflection::getPropertyType($props[0])); -Assert::same('Test\B', Reflection::getPropertyType($props[1])); -Assert::same('array', Reflection::getPropertyType($props[2])); -Assert::same('A', Reflection::getPropertyType($props[3])); +Assert::same('Undeclared', (string) Reflection::getPropertyType($props[0])); +Assert::same('Test\B', (string) Reflection::getPropertyType($props[1])); +Assert::same('array', (string) Reflection::getPropertyType($props[2])); +Assert::same('A', (string) Reflection::getPropertyType($props[3])); Assert::null(Reflection::getPropertyType($props[4])); -Assert::same('Test\B', Reflection::getPropertyType($props[5])); -Assert::same('mixed', Reflection::getPropertyType($props[6])); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[7]); -}, Nette\InvalidStateException::class, 'The A::$union is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[8]); -}, Nette\InvalidStateException::class, 'The A::$nullableUnion is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[9]); -}, Nette\InvalidStateException::class, 'The A::$intersection is not expected to have a union or intersection type.'); - +Assert::same('?Test\B', (string) Reflection::getPropertyType($props[5])); +Assert::same('mixed', (string) Reflection::getPropertyType($props[6])); +Assert::same('A|array', (string) Reflection::getPropertyType($props[7], false)); +Assert::same('A|array|null', (string) Reflection::getPropertyType($props[8], false)); $class = new ReflectionClass('AExt'); $props = $class->getProperties(); -Assert::same('A', Reflection::getPropertyType($props[0])); +Assert::same('A', (string) Reflection::getPropertyType($props[0])); diff --git a/tests/Utils/Reflection.getPropertyType.phpt b/tests/Utils/Reflection.getPropertyType.phpt index 161e4ac7e..44abc0630 100644 --- a/tests/Utils/Reflection.getPropertyType.phpt +++ b/tests/Utils/Reflection.getPropertyType.phpt @@ -35,23 +35,14 @@ class AExt extends A $class = new ReflectionClass('A'); $props = $class->getProperties(); -Assert::same('Undeclared', Reflection::getPropertyType($props[0])); -Assert::same('Test\B', Reflection::getPropertyType($props[1])); -Assert::same('array', Reflection::getPropertyType($props[2])); -Assert::same('A', Reflection::getPropertyType($props[3])); +Assert::same('Undeclared', (string) Reflection::getPropertyType($props[0])); +Assert::same('Test\B', (string) Reflection::getPropertyType($props[1])); +Assert::same('array', (string) Reflection::getPropertyType($props[2])); +Assert::same('A', (string) Reflection::getPropertyType($props[3])); Assert::null(Reflection::getPropertyType($props[4])); -Assert::same('Test\B', Reflection::getPropertyType($props[5])); -Assert::same('mixed', Reflection::getPropertyType($props[6])); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[7]); -}, Nette\InvalidStateException::class, 'The A::$union is not expected to have a union or intersection type.'); - -Assert::exception(function () use ($props) { - Reflection::getPropertyType($props[8]); -}, Nette\InvalidStateException::class, 'The A::$nullableUnion is not expected to have a union or intersection type.'); - +Assert::same('?Test\B', (string) Reflection::getPropertyType($props[5])); +Assert::same('mixed', (string) Reflection::getPropertyType($props[6])); $class = new ReflectionClass('AExt'); $props = $class->getProperties(); -Assert::same('A', Reflection::getPropertyType($props[0])); +Assert::same('A', (string) Reflection::getPropertyType($props[0])); diff --git a/tests/Utils/Reflection.getReturnType.81.phpt b/tests/Utils/Reflection.getReturnType.81.phpt index 704c55dff..c0b8cfb57 100644 --- a/tests/Utils/Reflection.getReturnType.81.phpt +++ b/tests/Utils/Reflection.getReturnType.81.phpt @@ -106,48 +106,38 @@ function intersectionType(): AExt&A Assert::null(Reflection::getReturnType(new \ReflectionMethod(A::class, 'noType'))); -Assert::same('Test\B', Reflection::getReturnType(new \ReflectionMethod(A::class, 'classType'))); +Assert::same('Test\B', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'classType'))); -Assert::same('string', Reflection::getReturnType(new \ReflectionMethod(A::class, 'nativeType'))); +Assert::same('string', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'nativeType'))); -Assert::same('A', Reflection::getReturnType(new \ReflectionMethod(A::class, 'selfType'))); +Assert::same('A', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'selfType'))); -Assert::same('A', Reflection::getReturnType(new \ReflectionMethod(A::class, 'staticType'))); +Assert::same('A', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'staticType'))); -Assert::same('Test\B', Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableClassType'))); +Assert::same('?Test\B', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableClassType'))); -Assert::same('string', Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableNativeType'))); +Assert::same('?string', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableNativeType'))); -Assert::same('A', Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableSelfType'))); +Assert::same('?A', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableSelfType'))); -Assert::exception(function () { - Reflection::getReturnType(new \ReflectionMethod(A::class, 'unionType')); -}, Nette\InvalidStateException::class, 'The A::unionType() is not expected to have a union or intersection type.'); +Assert::same('A|array', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'unionType'))); -Assert::exception(function () { - Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableUnionType')); -}, Nette\InvalidStateException::class, 'The A::nullableUnionType() is not expected to have a union or intersection type.'); +Assert::same('A|array|null', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'nullableUnionType'))); -Assert::exception(function () { - Reflection::getReturnType(new \ReflectionMethod(A::class, 'intersectionType')); -}, Nette\InvalidStateException::class, 'The A::intersectionType() is not expected to have a union or intersection type.'); +Assert::same('AExt&A', (string) Reflection::getReturnType(new \ReflectionMethod(A::class, 'intersectionType'))); -Assert::same('A', Reflection::getReturnType(new \ReflectionMethod(AExt::class, 'parentTypeExt'))); +Assert::same('A', (string) Reflection::getReturnType(new \ReflectionMethod(AExt::class, 'parentTypeExt'))); Assert::null(Reflection::getReturnType(new \ReflectionFunction('noType'))); -Assert::same('Test\B', Reflection::getReturnType(new \ReflectionFunction('classType'))); +Assert::same('Test\B', (string) Reflection::getReturnType(new \ReflectionFunction('classType'))); -Assert::same('string', Reflection::getReturnType(new \ReflectionFunction('nativeType'))); +Assert::same('string', (string) Reflection::getReturnType(new \ReflectionFunction('nativeType'))); -Assert::exception(function () { - Reflection::getReturnType(new \ReflectionFunction('unionType')); -}, Nette\InvalidStateException::class, 'The unionType() is not expected to have a union or intersection type.'); +Assert::same('A|array', (string) Reflection::getReturnType(new \ReflectionFunction('unionType'))); -Assert::exception(function () { - Reflection::getReturnType(new \ReflectionFunction('intersectionType')); -}, Nette\InvalidStateException::class, 'The intersectionType() is not expected to have a union or intersection type.'); +Assert::same('AExt&A', (string) Reflection::getReturnType(new \ReflectionFunction('intersectionType'))); // tentative type -Assert::same('int', Reflection::getReturnType(new \ReflectionMethod(\ArrayObject::class, 'count'))); +Assert::same('int', (string) Reflection::getReturnType(new \ReflectionMethod(\ArrayObject::class, 'count'))); diff --git a/tests/Utils/Reflection.getReturnType.phpt b/tests/Utils/Reflection.getReturnType.phpt index 43021b705..93c0db963 100644 --- a/tests/Utils/Reflection.getReturnType.phpt +++ b/tests/Utils/Reflection.getReturnType.phpt @@ -95,36 +95,30 @@ function unionType(): array|A Assert::null(Reflection::getReturnType(new ReflectionMethod(A::class, 'noType'))); -Assert::same('Test\B', Reflection::getReturnType(new ReflectionMethod(A::class, 'classType'))); +Assert::same('Test\B', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'classType'))); -Assert::same('string', Reflection::getReturnType(new ReflectionMethod(A::class, 'nativeType'))); +Assert::same('string', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'nativeType'))); -Assert::same('A', Reflection::getReturnType(new ReflectionMethod(A::class, 'selfType'))); +Assert::same('A', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'selfType'))); -Assert::same('A', Reflection::getReturnType(new ReflectionMethod(A::class, 'staticType'))); +Assert::same('A', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'staticType'))); -Assert::same('Test\B', Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableClassType'))); +Assert::same('?Test\B', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableClassType'))); -Assert::same('string', Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableNativeType'))); +Assert::same('?string', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableNativeType'))); -Assert::same('A', Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableSelfType'))); +Assert::same('?A', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableSelfType'))); -Assert::exception(function () { - Reflection::getReturnType(new ReflectionMethod(A::class, 'unionType')); -}, Nette\InvalidStateException::class, 'The A::unionType() is not expected to have a union or intersection type.'); +Assert::same('A|array', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'unionType'))); -Assert::exception(function () { - Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableUnionType')); -}, Nette\InvalidStateException::class, 'The A::nullableUnionType() is not expected to have a union or intersection type.'); +Assert::same('A|array|null', (string) Reflection::getReturnType(new ReflectionMethod(A::class, 'nullableUnionType'))); -Assert::same('A', Reflection::getReturnType(new ReflectionMethod(AExt::class, 'parentTypeExt'))); +Assert::same('A', (string) Reflection::getReturnType(new ReflectionMethod(AExt::class, 'parentTypeExt'))); Assert::null(Reflection::getReturnType(new ReflectionFunction('noType'))); -Assert::same('Test\B', Reflection::getReturnType(new ReflectionFunction('classType'))); +Assert::same('Test\B', (string) Reflection::getReturnType(new ReflectionFunction('classType'))); -Assert::same('string', Reflection::getReturnType(new ReflectionFunction('nativeType'))); +Assert::same('string', (string) Reflection::getReturnType(new ReflectionFunction('nativeType'))); -Assert::exception(function () { - Reflection::getReturnType(new ReflectionFunction('unionType')); -}, Nette\InvalidStateException::class, 'The unionType() is not expected to have a union or intersection type.'); +Assert::same('A|array', (string) Reflection::getReturnType(new ReflectionFunction('unionType'))); From d927489111d4830e7092325806c9d2ddf93d1eeb Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 26 Sep 2021 12:34:29 +0200 Subject: [PATCH 14/24] Html: removed $xhtml (BC break) --- src/Utils/Html.php | 12 +++------ tests/Utils/Html.basic.phpt | 50 ++++++++++++++----------------------- 2 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/Utils/Html.php b/src/Utils/Html.php index 4043fbdbb..071105eee 100644 --- a/src/Utils/Html.php +++ b/src/Utils/Html.php @@ -238,8 +238,6 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab /** @var array element's attributes */ public $attrs = []; - public static bool $xhtml = false; - /** void elements */ public static $emptyElements = [ 'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1, @@ -748,7 +746,7 @@ final public function __toString(): string final public function startTag(): string { return $this->name - ? '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>') + ? '<' . $this->name . $this->attributes() . '>' : ''; } @@ -779,11 +777,7 @@ final public function attributes(): string continue; } elseif ($value === true) { - if (static::$xhtml) { - $s .= ' ' . $key . '="' . $key . '"'; - } else { - $s .= ' ' . $key; - } + $s .= ' ' . $key; continue; @@ -819,7 +813,7 @@ final public function attributes(): string $s .= ' ' . $key . '=' . $q . str_replace( ['&', $q, '<'], - ['&', $q === '"' ? '"' : ''', self::$xhtml ? '<' : '<'], + ['&', $q === '"' ? '"' : ''', '<'], $value, ) . (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '') diff --git a/tests/Utils/Html.basic.phpt b/tests/Utils/Html.basic.phpt index 6e4f5c1a7..064405e5f 100644 --- a/tests/Utils/Html.basic.phpt +++ b/tests/Utils/Html.basic.phpt @@ -14,63 +14,55 @@ require __DIR__ . '/../bootstrap.php'; test('', function () { - Html::$xhtml = true; $el = Html::el('img')->src('image.gif')->alt(''); - Assert::same('', (string) $el); - Assert::same('', $el->toHtml()); - Assert::same('', $el->startTag()); + Assert::same('', (string) $el); + Assert::same('', $el->toHtml()); + Assert::same('', $el->startTag()); Assert::same('', $el->endTag()); }); test('', function () { - Html::$xhtml = true; $el = Html::el('img')->setAttribute('src', 'image.gif')->setAttribute('alt', ''); - Assert::same('', (string) $el); - Assert::same('', $el->startTag()); + Assert::same('', (string) $el); + Assert::same('', $el->startTag()); Assert::same('', $el->endTag()); }); test('', function () { - Html::$xhtml = true; $el = Html::el('img')->accesskey(0, true)->alt('alt', false); - Assert::same('', (string) $el); - Assert::same('', (string) $el->accesskey(1, true)); - Assert::same('', (string) $el->accesskey(1, false)); - Assert::same('', (string) $el->accesskey(0, true)); - Assert::same('', (string) $el->accesskey(0)); + Assert::same('', (string) $el); + Assert::same('', (string) $el->accesskey(1, true)); + Assert::same('', (string) $el->accesskey(1, false)); + Assert::same('', (string) $el->accesskey(0, true)); + Assert::same('', (string) $el->accesskey(0)); unset($el->accesskey); - Assert::same('', (string) $el); + Assert::same('', (string) $el); }); test('', function () { - Html::$xhtml = true; $el = Html::el('img')->appendAttribute('accesskey', 0)->setAttribute('alt', false); - Assert::same('', (string) $el); - Assert::same('', (string) $el->appendAttribute('accesskey', 1)); - Assert::same('', (string) $el->appendAttribute('accesskey', 1, false)); - Assert::same('', (string) $el->appendAttribute('accesskey', 0)); - Assert::same('', (string) $el->setAttribute('accesskey', 0)); - Assert::same('', (string) $el->removeAttribute('accesskey')); + Assert::same('', (string) $el); + Assert::same('', (string) $el->appendAttribute('accesskey', 1)); + Assert::same('', (string) $el->appendAttribute('accesskey', 1, false)); + Assert::same('', (string) $el->appendAttribute('accesskey', 0)); + Assert::same('', (string) $el->setAttribute('accesskey', 0)); + Assert::same('', (string) $el->removeAttribute('accesskey')); }); test('', function () { $el = Html::el('img')->src('image.gif')->alt('')->setText('any content'); - Assert::same('', (string) $el); - Assert::same('', $el->startTag()); - Assert::same('', $el->endTag()); - - Html::$xhtml = false; Assert::same('', (string) $el); + Assert::same('', $el->startTag()); + Assert::same('', $el->endTag()); }); test('', function () { - Html::$xhtml = false; $el = Html::el('img')->setSrc('image.gif')->setAlt('alt1')->setAlt('alt2'); Assert::same('alt2', (string) $el); Assert::same('image.gif', $el->getSrc()); @@ -104,10 +96,6 @@ test('small & big numbers', function () { test('attributes escaping', function () { - Html::$xhtml = true; - Assert::same('', (string) Html::el('a')->one('"')->two("'")->three('<>')->four('&')); - - Html::$xhtml = false; Assert::same('', (string) Html::el('a')->one('"')->two("'")->three('<>')->four('&')); Assert::same('', (string) Html::el('a')->one('``xx')); // mXSS }); From 429196710ba145f688034d19563cbc4d48f933ca Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 9 Jan 2022 12:59:27 +0100 Subject: [PATCH 15/24] Strings, Arrays, Json, Image: $flags replaced with parameters --- src/Utils/Arrays.php | 3 ++- src/Utils/Image.php | 30 +++++++++++++-------- src/Utils/Json.php | 29 ++++++++++++++------ src/Utils/Strings.php | 42 ++++++++++++++++++++++------- tests/Utils/Arrays.grep().phpt | 5 ++++ tests/Utils/Image.resize.phpt | 40 +++++++++++++++++++++++++++ tests/Utils/Json.decode().phpt | 1 + tests/Utils/Json.encode().phpt | 2 ++ tests/Utils/Strings.match().phpt | 10 ++++--- tests/Utils/Strings.matchAll().phpt | 21 +++++++++++++-- tests/Utils/Strings.split().phpt | 16 +++++++++++ 11 files changed, 164 insertions(+), 35 deletions(-) diff --git a/src/Utils/Arrays.php b/src/Utils/Arrays.php index 9f79fbe8f..d028a87cd 100644 --- a/src/Utils/Arrays.php +++ b/src/Utils/Arrays.php @@ -196,8 +196,9 @@ public static function renameKey(array &$array, string|int $oldKey, string|int $ * @param string[] $array * @return string[] */ - public static function grep(array $array, string $pattern, int $flags = 0): array + public static function grep(array $array, string $pattern, bool|int $invert = false): array { + $flags = $invert ? PREG_GREP_INVERT : 0; return Strings::pcre('preg_grep', [$pattern, $array, $flags]); } diff --git a/src/Utils/Image.php b/src/Utils/Image.php index 3f6cdfd33..9eb577f0d 100644 --- a/src/Utils/Image.php +++ b/src/Utils/Image.php @@ -98,7 +98,7 @@ class Image { use Nette\SmartObject; - /** {@link resize()} only shrinks images */ + /** @deprecated */ public const SHRINK_ONLY = 0b0001; /** {@link resize()} will ignore aspect ratio */ @@ -318,14 +318,19 @@ public function getImageResource(): \GdImage /** * Scales an image. Width and height accept pixels or percent. + * @param self::FIT|self::FILL|self::STRETCH|self::EXACT $mode */ - public function resize(int|string|null $width, int|string|null $height, int $flags = self::FIT): static - { - if ($flags & self::EXACT) { + public function resize( + int|string|null $width, + int|string|null $height, + int $mode = self::FIT, + bool $shrinkOnly = false, + ): static { + if ($mode & self::EXACT) { return $this->resize($width, $height, self::FILL)->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, $shrinkOnly); if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize $newImage = static::fromBlank($newWidth, $newHeight, self::rgb(0, 0, 0, 127))->getImageResource(); @@ -354,14 +359,17 @@ public function resize(int|string|null $width, int|string|null $height, int $fla /** * Calculates dimensions of resized image. Width and height accept pixels or percent. + * @param self::FIT|self::FILL|self::STRETCH $mode */ public static function calculateSize( int $srcWidth, int $srcHeight, $newWidth, $newHeight, - int $flags = self::FIT, + int $mode = self::FIT, + bool $shrinkOnly = false, ): array { + $shrinkOnly = $shrinkOnly || ($mode & self::SHRINK_ONLY); // back compatibility if ($newWidth === null) { } elseif (self::isPercent($newWidth)) { $newWidth = (int) round($srcWidth / 100 * abs($newWidth)); @@ -373,17 +381,17 @@ public static function calculateSize( 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) { + if ($shrinkOnly) { $newWidth = (int) round($srcWidth * min(1, $newWidth / $srcWidth)); $newHeight = (int) round($srcHeight * min(1, $newHeight / $srcHeight)); } @@ -401,11 +409,11 @@ public static function calculateSize( $scale[] = $newHeight / $srcHeight; } - if ($flags & self::FILL) { + if ($mode & self::FILL) { $scale = [max($scale)]; } - if ($flags & self::SHRINK_ONLY) { + if ($shrinkOnly) { $scale[] = 1; } diff --git a/src/Utils/Json.php b/src/Utils/Json.php index 3a9850b7a..fab5b6f45 100644 --- a/src/Utils/Json.php +++ b/src/Utils/Json.php @@ -25,15 +25,24 @@ final class Json /** - * 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. + * Converts value to JSON format. Use $pretty for easier reading and clarity and $escapeUnicode for ASCII output. * @throws JsonException */ - public static function encode(mixed $value, int $flags = 0): string - { - $flags = ($flags & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE) + public static function encode( + mixed $value, + bool|int $pretty = false, + bool $escapeUnicode = false, + ): string { + $flags = 0; + if (is_int($pretty)) { // back compatibility + $escapeUnicode = $pretty & self::ESCAPE_UNICODE; + $flags = $pretty & ~self::ESCAPE_UNICODE; + $pretty = false; + } + + $flags |= ($escapeUnicode ? 0 : JSON_UNESCAPED_UNICODE) + | ($pretty ? JSON_PRETTY_PRINT : 0) | JSON_UNESCAPED_SLASHES - | ($flags & ~self::ESCAPE_UNICODE) | (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); @@ -46,11 +55,15 @@ public static function encode(mixed $value, int $flags = 0): string /** - * Parses JSON to PHP value. The flag can be Json::FORCE_ARRAY, which forces an array instead of an object as the return value. + * Parses JSON to PHP value. Parameter $forceArray forces an array instead of an object as the return value. * @throws JsonException */ - public static function decode(string $json, int $flags = 0): mixed + public static function decode(string $json, bool|int $forceArray = false): mixed { + $flags = is_int($forceArray) // back compatibility + ? $forceArray + : ($forceArray ? JSON_OBJECT_AS_ARRAY : 0); + $value = json_decode($json, null, 512, $flags | JSON_BIGINT_AS_STRING); if ($error = json_last_error()) { throw new JsonException(json_last_error_msg(), $error); diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index 5d89a2d35..8343491e9 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -483,20 +483,33 @@ private static function pos(string $haystack, string $needle, int $nth = 1): ?in /** * 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. */ - public static function split(string $subject, string $pattern, int $flags = 0): array - { + public static function split( + string $subject, + string $pattern, + bool|int $captureOffset = false, + bool $skipEmpty = false, + ): array { + $flags = is_int($captureOffset) && $captureOffset // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); return self::pcre('preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]); } /** * 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. */ - public static function match(string $subject, string $pattern, int $flags = 0, int $offset = 0): ?array - { + public static function match( + string $subject, + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + ): ?array { + $flags = is_int($captureOffset) && $captureOffset // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0); if ($offset > strlen($subject)) { return null; } @@ -508,11 +521,20 @@ public static function match(string $subject, string $pattern, int $flags = 0, i /** - * 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. + * 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). */ - public static function matchAll(string $subject, string $pattern, int $flags = 0, int $offset = 0): array - { + public static function matchAll( + string $subject, + string $pattern, + bool|int $captureOffset = false, + int $offset = 0, + bool $unmatchedAsNull = false, + bool $patternOrder = false, + ): array { + $flags = is_int($captureOffset) && $captureOffset // back compatibility + ? $captureOffset + : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0); if ($offset > strlen($subject)) { return []; } diff --git a/tests/Utils/Arrays.grep().phpt b/tests/Utils/Arrays.grep().phpt index f356405d3..f8613b8ae 100644 --- a/tests/Utils/Arrays.grep().phpt +++ b/tests/Utils/Arrays.grep().phpt @@ -21,3 +21,8 @@ Assert::same([ 0 => 'a', 2 => 'c', ], Arrays::grep(['a', '1', 'c'], '#\d#', PREG_GREP_INVERT)); + +Assert::same([ + 0 => 'a', + 2 => 'c', +], Arrays::grep(['a', '1', 'c'], '#\d#', invert: true)); diff --git a/tests/Utils/Image.resize.phpt b/tests/Utils/Image.resize.phpt index ef8a79887..7b3663cce 100644 --- a/tests/Utils/Image.resize.phpt +++ b/tests/Utils/Image.resize.phpt @@ -52,6 +52,14 @@ test('resizing Y shrink', function () use ($main) { }); +test('resizing Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(null, 150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('resizing X Y shrink', function () use ($main) { $image = clone $main; $image->resize(300, 150, Image::SHRINK_ONLY); @@ -60,6 +68,14 @@ test('resizing X Y shrink', function () use ($main) { }); +test('resizing X Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(300, 150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('resizing X Y', function () use ($main) { $image = clone $main; $image->resize(300, 150); @@ -84,6 +100,14 @@ test('resizing X Y shrink stretch', function () use ($main) { }); +test('resizing X Y shrink stretch', function () use ($main) { + $image = clone $main; + $image->resize(300, 100, Image::STRETCH, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(100, $image->height); +}); + + test('resizing X%', function () use ($main) { $image = clone $main; $image->resize('110%', null); @@ -116,6 +140,14 @@ test('flipping Y shrink', function () use ($main) { }); +test('flipping Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(null, -150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('flipping X Y shrink', function () use ($main) { $image = clone $main; $image->resize(-300, -150, Image::SHRINK_ONLY); @@ -124,6 +156,14 @@ test('flipping X Y shrink', function () use ($main) { }); +test('flipping X Y shrink', function () use ($main) { + $image = clone $main; + $image->resize(-300, -150, shrinkOnly: true); + Assert::same(176, $image->width); + Assert::same(104, $image->height); +}); + + test('exact resize', function () use ($main) { $image = clone $main; $image->resize(300, 150, Image::EXACT); diff --git a/tests/Utils/Json.decode().phpt b/tests/Utils/Json.decode().phpt index e3ac443c9..e9688f687 100644 --- a/tests/Utils/Json.decode().phpt +++ b/tests/Utils/Json.decode().phpt @@ -20,6 +20,7 @@ Assert::null(Json::decode(' null')); Assert::equal((object) ['a' => 1], Json::decode('{"a":1}')); Assert::same(['a' => 1], Json::decode('{"a":1}', Json::FORCE_ARRAY)); +Assert::same(['a' => 1], Json::decode('{"a":1}', forceArray: true)); Assert::exception(function () { diff --git a/tests/Utils/Json.encode().phpt b/tests/Utils/Json.encode().phpt index 888accbf4..61240e0e8 100644 --- a/tests/Utils/Json.encode().phpt +++ b/tests/Utils/Json.encode().phpt @@ -36,10 +36,12 @@ Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}")); // ESCAPE_UNICODE Assert::same('"/I\u00f1t\u00ebrn\u00e2ti\u00f4n\u00e0liz\u00e6ti\u00f8n"', Json::encode("/I\u{F1}t\u{EB}rn\u{E2}ti\u{F4}n\u{E0}liz\u{E6}ti\u{F8}n", Json::ESCAPE_UNICODE)); Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}", Json::ESCAPE_UNICODE)); +Assert::same('"\u2028\u2029"', Json::encode("\u{2028}\u{2029}", escapeUnicode: true)); // JSON_PRETTY_PRINT Assert::same("[\n 1,\n 2,\n 3\n]", Json::encode([1, 2, 3], Json::PRETTY)); +Assert::same("[\n 1,\n 2,\n 3\n]", Json::encode([1, 2, 3], pretty: true)); Assert::exception(function () { diff --git a/tests/Utils/Strings.match().phpt b/tests/Utils/Strings.match().phpt index 5814b5753..5b4196180 100644 --- a/tests/Utils/Strings.match().phpt +++ b/tests/Utils/Strings.match().phpt @@ -20,8 +20,12 @@ Assert::same(['hell', 'l'], Strings::match('hello world!', '#([e-l])+#')); Assert::same(['hell'], Strings::match('hello world!', '#[e-l]+#')); Assert::same([['hell', 0]], Strings::match('hello world!', '#[e-l]+#', PREG_OFFSET_CAPTURE)); +Assert::same([['hell', 0]], Strings::match('hello world!', '#[e-l]+#', captureOffset: true)); -Assert::same(['ll'], Strings::match('hello world!', '#[e-l]+#', 0, 2)); +Assert::same(['e', null], Strings::match('hello world!', '#e(x)*#', unmatchedAsNull: true)); +Assert::same(['e', null], Strings::match('hello world!', '#e(x)*#', 0, 0, unmatchedAsNull: true)); // $flags = 0 -Assert::null(Strings::match('hello world!', '', 0, 50)); -Assert::null(Strings::match('', '', 0, 1)); +Assert::same(['ll'], Strings::match('hello world!', '#[e-l]+#', offset: 2)); + +Assert::null(Strings::match('hello world!', '', offset: 50)); +Assert::null(Strings::match('', '', offset: 1)); diff --git a/tests/Utils/Strings.matchAll().phpt b/tests/Utils/Strings.matchAll().phpt index 384bbc18a..257887f95 100644 --- a/tests/Utils/Strings.matchAll().phpt +++ b/tests/Utils/Strings.matchAll().phpt @@ -32,14 +32,31 @@ Assert::same([ [['k', 14], ['k', 14], ['', 15]], ], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', PREG_OFFSET_CAPTURE)); +Assert::same([ + [['lu', 2], ['l', 2], ['u', 3]], + [['ou', 6], ['o', 6], ['u', 7]], + [['k', 10], ['k', 10], ['', 11]], + [['k', 14], ['k', 14], ['', 15]], +], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true)); + Assert::same([ [['lu', 2], ['ou', 6], ['k', 10], ['k', 14]], [['l', 2], ['o', 6], ['k', 10], ['k', 14]], [['u', 3], ['u', 7], ['', 11], ['', 15]], ], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER)); -Assert::same([['l'], ['k'], ['k']], Strings::matchAll('žluťoučký kůň', '#[e-l]+#u', 0, 2)); +Assert::same([ + [['lu', 2], ['ou', 6], ['k', 10], ['k', 14]], + [['l', 2], ['o', 6], ['k', 10], ['k', 14]], + [['u', 3], ['u', 7], ['', 11], ['', 15]], +], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true, patternOrder: true)); + +Assert::same([['l'], ['k'], ['k']], Strings::matchAll('žluťoučký kůň', '#[e-l]+#u', offset: 2)); Assert::same([['ll', 'l']], Strings::matchAll('hello world!', '#[e-l]+#', PREG_PATTERN_ORDER, 2)); +Assert::same([['ll', 'l']], Strings::matchAll('hello world!', '#[e-l]+#', offset: 2, patternOrder: true)); + +Assert::same([['e', null]], Strings::matchAll('hello world!', '#e(x)*#', unmatchedAsNull: true)); +Assert::same([['e', null]], Strings::matchAll('hello world!', '#e(x)*#', 0, 0, unmatchedAsNull: true)); // $flags = 0 -Assert::same([], Strings::matchAll('hello world!', '', 0, 50)); +Assert::same([], Strings::matchAll('hello world!', '', offset: 50)); diff --git a/tests/Utils/Strings.split().phpt b/tests/Utils/Strings.split().phpt index b291e9613..3a57a4c4e 100644 --- a/tests/Utils/Strings.split().phpt +++ b/tests/Utils/Strings.split().phpt @@ -29,6 +29,14 @@ Assert::same([ 'c', ], Strings::split('a, b, c', '#(,)\s*#', PREG_SPLIT_NO_EMPTY)); +Assert::same([ + 'a', + ',', + 'b', + ',', + 'c', +], Strings::split('a, b, c', '#(,)\s*#', skipEmpty: true)); + Assert::same([ ['a', 0], [',', 1], @@ -36,3 +44,11 @@ Assert::same([ [',', 4], ['c', 6], ], Strings::split('a, b, c', '#(,)\s*#', PREG_SPLIT_OFFSET_CAPTURE)); + +Assert::same([ + ['a', 0], + [',', 1], + ['b', 3], + [',', 4], + ['c', 6], +], Strings::split('a, b, c', '#(,)\s*#', captureOffset: true)); From d2d07247b179e6e6bff2b1cfdbe929a5be8c15bd Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 25 Oct 2021 15:57:10 +0200 Subject: [PATCH 16/24] Strings::replace() added parameters $captureOffset, $unmatchedAsNull --- src/Utils/Strings.php | 5 ++++- tests/Utils/Strings.replace().phpt | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index 8343491e9..0b74aadfe 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -556,13 +556,16 @@ public static function replace( string|array $pattern, string|callable $replacement = '', int $limit = -1, + bool $captureOffset = false, + bool $unmatchedAsNull = 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); + return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]); } elseif (is_array($pattern) && is_string(key($pattern))) { $replacement = array_values($pattern); diff --git a/tests/Utils/Strings.replace().phpt b/tests/Utils/Strings.replace().phpt index 5dda17322..8c8c0a5dc 100644 --- a/tests/Utils/Strings.replace().phpt +++ b/tests/Utils/Strings.replace().phpt @@ -34,3 +34,7 @@ Assert::same('#@ @@@#d!', Strings::replace('hello world!', [ ])); Assert::same(' !', Strings::replace('hello world!', '#\w#')); Assert::same(' !', Strings::replace('hello world!', ['#\w#'])); + +// flags & callback +Assert::same('hell0o worl9d!', Strings::replace('hello world!', '#[e-l]+#', fn($m) => implode('', $m[0]), captureOffset: true)); +Strings::replace('hello world!', '#e(x)*#', fn($m) => Assert::null($m[1]), unmatchedAsNull: true); From e4043a32db6dda5d871dec83b9e3550d284983db Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Oct 2021 13:15:19 +0200 Subject: [PATCH 17/24] Strings::split() added parameter $limit --- src/Utils/Strings.php | 3 ++- tests/Utils/Strings.split().phpt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index 0b74aadfe..f5333bdc0 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -489,11 +489,12 @@ public static function split( string $pattern, bool|int $captureOffset = false, bool $skipEmpty = false, + int $limit = -1, ): array { $flags = is_int($captureOffset) && $captureOffset // back compatibility ? $captureOffset : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); - return self::pcre('preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]); + return self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]); } diff --git a/tests/Utils/Strings.split().phpt b/tests/Utils/Strings.split().phpt index 3a57a4c4e..9638b2710 100644 --- a/tests/Utils/Strings.split().phpt +++ b/tests/Utils/Strings.split().phpt @@ -52,3 +52,5 @@ Assert::same([ [',', 4], ['c', 6], ], Strings::split('a, b, c', '#(,)\s*#', captureOffset: true)); + +Assert::same(['a', ',', 'b, c'], Strings::split('a, b, c', '#(,)\s*#', limit: 2)); From 92c593d8116fb31833084e5112d38e213afe15f4 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 27 Oct 2021 01:21:23 +0200 Subject: [PATCH 18/24] Strings: added support for UTF8 offsets in regexp --- src/Utils/Strings.php | 69 +++++++++++++++++++++++++++-- tests/Utils/Strings.match().phpt | 13 +++++- tests/Utils/Strings.matchAll().phpt | 17 +++++++ tests/Utils/Strings.replace().phpt | 6 +++ tests/Utils/Strings.split().phpt | 30 ++++++++++--- 5 files changed, 123 insertions(+), 12 deletions(-) diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index f5333bdc0..92c30c991 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -490,11 +490,18 @@ public static function split( bool|int $captureOffset = false, bool $skipEmpty = false, int $limit = -1, + bool $utf8 = false, ): array { $flags = is_int($captureOffset) && $captureOffset // back compatibility ? $captureOffset : ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0); - return self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]); + $pattern .= $utf8 ? 'u' : ''; + $m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]); + if ($utf8 && ($flags & PREG_SPLIT_OFFSET_CAPTURE)) { + return self::bytesToChars($subject, [$m])[0]; + } + + return $m; } @@ -507,17 +514,29 @@ public static function match( bool|int $captureOffset = false, int $offset = 0, bool $unmatchedAsNull = false, + bool $utf8 = false, ): ?array { $flags = is_int($captureOffset) && $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'; + } + if ($offset > strlen($subject)) { return null; } - return self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset]) - ? $m - : null; + if (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) { + return null; + } + + if ($utf8 && ($flags & PREG_OFFSET_CAPTURE)) { + return self::bytesToChars($subject, [$m])[0]; + } + + return $m; } @@ -532,10 +551,16 @@ public static function matchAll( int $offset = 0, bool $unmatchedAsNull = false, bool $patternOrder = false, + bool $utf8 = false, ): array { $flags = is_int($captureOffset) && $captureOffset // back compatibility ? $captureOffset : ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0); + if ($utf8) { + $offset = strlen(self::substring($subject, 0, $offset)); + $pattern .= 'u'; + } + if ($offset > strlen($subject)) { return []; } @@ -545,6 +570,10 @@ public static function matchAll( ($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER), $offset, ]); + if ($utf8 && ($flags & PREG_OFFSET_CAPTURE)) { + return self::bytesToChars($subject, $m); + } + return $m; } @@ -559,6 +588,7 @@ public static function replace( 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)) { @@ -566,6 +596,13 @@ public static function replace( } $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))) { @@ -573,10 +610,34 @@ public static function replace( $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) { diff --git a/tests/Utils/Strings.match().phpt b/tests/Utils/Strings.match().phpt index 5b4196180..34799a885 100644 --- a/tests/Utils/Strings.match().phpt +++ b/tests/Utils/Strings.match().phpt @@ -19,13 +19,22 @@ Assert::same(['hell', 'l'], Strings::match('hello world!', '#([e-l])+#')); Assert::same(['hell'], Strings::match('hello world!', '#[e-l]+#')); -Assert::same([['hell', 0]], Strings::match('hello world!', '#[e-l]+#', PREG_OFFSET_CAPTURE)); -Assert::same([['hell', 0]], Strings::match('hello world!', '#[e-l]+#', captureOffset: true)); +Assert::same([['l', 2]], Strings::match('žluťoučký kůň', '#[e-l]+#u', PREG_OFFSET_CAPTURE)); +Assert::same([['l', 2]], Strings::match('žluťoučký kůň', '#[e-l]+#u', captureOffset: true)); +Assert::same([['l', 1]], Strings::match('žluťoučký kůň', '#[e-l]+#u', captureOffset: true, utf8: true)); Assert::same(['e', null], Strings::match('hello world!', '#e(x)*#', unmatchedAsNull: true)); Assert::same(['e', null], Strings::match('hello world!', '#e(x)*#', 0, 0, unmatchedAsNull: true)); // $flags = 0 Assert::same(['ll'], Strings::match('hello world!', '#[e-l]+#', offset: 2)); +Assert::same(['l'], Strings::match('žluťoučký kůň', '#[e-l]+#u', offset: 2)); + +Assert::same(['k'], Strings::match('žluťoučký kůň', '#[e-l]+#u', utf8: true, offset: 2)); + +Assert::same(['žluťoučký'], Strings::match('žluťoučký kůň', '#\w+#', utf8: true)); // without modifier + +Assert::same([['k', 7]], Strings::match('žluťoučký kůň', '#[e-l]+#u', captureOffset: true, utf8: true, offset: 2)); + Assert::null(Strings::match('hello world!', '', offset: 50)); Assert::null(Strings::match('', '', offset: 1)); diff --git a/tests/Utils/Strings.matchAll().phpt b/tests/Utils/Strings.matchAll().phpt index 257887f95..002b43599 100644 --- a/tests/Utils/Strings.matchAll().phpt +++ b/tests/Utils/Strings.matchAll().phpt @@ -45,14 +45,31 @@ Assert::same([ [['u', 3], ['u', 7], ['', 11], ['', 15]], ], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', PREG_OFFSET_CAPTURE | PREG_PATTERN_ORDER)); +Assert::same([ + [['lu', 1], ['l', 1], ['u', 2]], + [['ou', 4], ['o', 4], ['u', 5]], + [['k', 7], ['k', 7], ['', 8]], + [['k', 10], ['k', 10], ['', 11]], +], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true, utf8: true)); + Assert::same([ [['lu', 2], ['ou', 6], ['k', 10], ['k', 14]], [['l', 2], ['o', 6], ['k', 10], ['k', 14]], [['u', 3], ['u', 7], ['', 11], ['', 15]], ], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true, patternOrder: true)); +Assert::same([ + [['lu', 1], ['ou', 4], ['k', 7], ['k', 10]], + [['l', 1], ['o', 4], ['k', 7], ['k', 10]], + [['u', 2], ['u', 5], ['', 8], ['', 11]], +], Strings::matchAll('žluťoučký kůň!', '#([a-z])([a-z]*)#u', captureOffset: true, patternOrder: true, utf8: true)); + Assert::same([['l'], ['k'], ['k']], Strings::matchAll('žluťoučký kůň', '#[e-l]+#u', offset: 2)); +Assert::same([['k'], ['k']], Strings::matchAll('žluťoučký kůň', '#[e-l]+#u', offset: 2, utf8: true)); + +Assert::same([['žluťoučký'], ['kůň']], Strings::matchAll('žluťoučký kůň', '#\w+#', utf8: true)); // without modifier + Assert::same([['ll', 'l']], Strings::matchAll('hello world!', '#[e-l]+#', PREG_PATTERN_ORDER, 2)); Assert::same([['ll', 'l']], Strings::matchAll('hello world!', '#[e-l]+#', offset: 2, patternOrder: true)); diff --git a/tests/Utils/Strings.replace().phpt b/tests/Utils/Strings.replace().phpt index 8c8c0a5dc..0ecd639cf 100644 --- a/tests/Utils/Strings.replace().phpt +++ b/tests/Utils/Strings.replace().phpt @@ -37,4 +37,10 @@ Assert::same(' !', Strings::replace('hello world!', ['#\w#'])); // flags & callback Assert::same('hell0o worl9d!', Strings::replace('hello world!', '#[e-l]+#', fn($m) => implode('', $m[0]), captureOffset: true)); +Assert::same('žl1uťoučk7ý k10ůň!', Strings::replace('žluťoučký kůň!', '#[e-l]+#u', fn($m) => implode('', $m[0]), captureOffset: true, utf8: true)); Strings::replace('hello world!', '#e(x)*#', fn($m) => Assert::null($m[1]), unmatchedAsNull: true); + +// utf-8 without modifier +Assert::same('* *', Strings::replace('žluťoučký kůň', '#\w+#', fn() => '*', utf8: true)); +Assert::same('* *', Strings::replace('žluťoučký kůň', '#\w+#', '*', utf8: true)); +Assert::same('* *', Strings::replace('žluťoučký kůň', ['#\w+#'], '*', utf8: true)); diff --git a/tests/Utils/Strings.split().phpt b/tests/Utils/Strings.split().phpt index 9638b2710..d888ffca9 100644 --- a/tests/Utils/Strings.split().phpt +++ b/tests/Utils/Strings.split().phpt @@ -46,11 +46,29 @@ Assert::same([ ], Strings::split('a, b, c', '#(,)\s*#', PREG_SPLIT_OFFSET_CAPTURE)); Assert::same([ - ['a', 0], - [',', 1], - ['b', 3], - [',', 4], - ['c', 6], -], Strings::split('a, b, c', '#(,)\s*#', captureOffset: true)); + ['ž', 0], + ['lu', 2], + ['ť', 4], + ['ou', 6], + ['č', 8], + ['k', 10], + ['ý ', 11], + ['k', 14], + ['ůň', 15], +], Strings::split('žluťoučký kůň', '#([a-z]+)\s*#u', captureOffset: true)); + +Assert::same([ + ['ž', 0], + ['lu', 1], + ['ť', 3], + ['ou', 4], + ['č', 6], + ['k', 7], + ['ý ', 8], + ['k', 10], + ['ůň', 11], +], Strings::split('žluťoučký kůň', '#([a-z]+)\s*#u', captureOffset: true, utf8: true)); + +Assert::same(['', ' ', ''], Strings::split('žluťoučký kůň', '#\w+#', utf8: true)); // without modifier Assert::same(['a', ',', 'b, c'], Strings::split('a, b, c', '#(,)\s*#', limit: 2)); From 177cfb8efc27919e0cd64298c75720ba6cf8b81c Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 1 Nov 2021 23:15:10 +0100 Subject: [PATCH 19/24] added Strings::ord() --- src/Utils/Strings.php | 18 ++++++++++++++++ tests/Utils/Strings.ord().phpt | 38 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 tests/Utils/Strings.ord().phpt diff --git a/src/Utils/Strings.php b/src/Utils/Strings.php index 92c30c991..6f47bb967 100644 --- a/src/Utils/Strings.php +++ b/src/Utils/Strings.php @@ -58,6 +58,24 @@ public static function chr(int $code): string } + /** + * 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]; + } + + /** * Starts the $haystack string with the prefix $needle? */ diff --git a/tests/Utils/Strings.ord().phpt b/tests/Utils/Strings.ord().phpt new file mode 100644 index 000000000..09f6ca2fd --- /dev/null +++ b/tests/Utils/Strings.ord().phpt @@ -0,0 +1,38 @@ + Date: Tue, 16 Nov 2021 14:45:46 +0100 Subject: [PATCH 20/24] Translator: changed interface, accepts and returns string|Stringable [Closes #231] --- src/Translator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Translator.php b/src/Translator.php index e8c5f7d06..f973f5f15 100644 --- a/src/Translator.php +++ b/src/Translator.php @@ -18,7 +18,7 @@ interface Translator /** * Translates the given string. */ - function translate(mixed $message, mixed ...$parameters): string; + function translate(string|\Stringable $message, mixed ...$parameters): string|\Stringable; } From a823f8e0d508dd73023e121152c6864c52eaa442 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sat, 11 Dec 2021 19:06:33 +0100 Subject: [PATCH 21/24] Reflection::getParameterDefaultValue() is deprecated Since PHP 8.0 ReflectionParameter::getDefaultValue() correctly returns value or throws exception when constant cannot be resolved --- src/Utils/Reflection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Utils/Reflection.php b/src/Utils/Reflection.php index 6313e3abc..3a3821554 100644 --- a/src/Utils/Reflection.php +++ b/src/Utils/Reflection.php @@ -79,8 +79,7 @@ public static function getPropertyType(\ReflectionProperty $prop): ?Type /** - * Returns the default value of parameter. If it is a constant, it returns its value. - * @throws \ReflectionException If the parameter does not have a default value or the constant cannot be resolved + * @deprecated use ReflectionParameter::getDefaultValue() */ public static function getParameterDefaultValue(\ReflectionParameter $param): mixed { From 70b3543ef60b8b3ed3557a23c2f867560ff640bc Mon Sep 17 00:00:00 2001 From: David Grudl Date: Wed, 5 Jan 2022 01:57:14 +0100 Subject: [PATCH 22/24] added Helpers::compare() --- src/Utils/Helpers.php | 19 ++++++++++++ tests/Utils/Helpers.compare().phpt | 47 ++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 tests/Utils/Helpers.compare().phpt diff --git a/src/Utils/Helpers.php b/src/Utils/Helpers.php index f27b9e7be..1471e5351 100644 --- a/src/Utils/Helpers.php +++ b/src/Utils/Helpers.php @@ -82,4 +82,23 @@ public static function getSuggestion(array $possibilities, string $value): ?stri return $best; } + + + /** + * Compares two values. Recognizes operators: >, >=, <, <=, =, ==, ===, !=, !==, <> + */ + public static function compare(mixed $l, string $operator, mixed $r): bool + { + return match ($operator) { + '>' => $l > $r, + '>=' => $l >= $r, + '<' => $l < $r, + '<=' => $l <= $r, + '=', '==' => $l == $r, + '===' => $l === $r, + '!=', '<>' => $l != $r, + '!==' => $l !== $r, + default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"), + }; + } } diff --git a/tests/Utils/Helpers.compare().phpt b/tests/Utils/Helpers.compare().phpt new file mode 100644 index 000000000..2790cbe42 --- /dev/null +++ b/tests/Utils/Helpers.compare().phpt @@ -0,0 +1,47 @@ +', 1)); +Assert::false(Helpers::compare(1, '>', 2)); +Assert::false(Helpers::compare(1, '<', 1)); +Assert::true(Helpers::compare(1, '<', 2)); + +Assert::true(Helpers::compare(1, '>=', 1)); +Assert::false(Helpers::compare(1, '>=', 2)); +Assert::true(Helpers::compare(1, '<=', 1)); +Assert::true(Helpers::compare(1, '<=', 2)); + +Assert::true(Helpers::compare(1, '=', 1)); +Assert::true(Helpers::compare(1.0, '=', 1)); +Assert::false(Helpers::compare(1, '=', 2)); + +Assert::true(Helpers::compare(1, '==', 1)); +Assert::true(Helpers::compare(1.0, '==', 1)); +Assert::false(Helpers::compare(1, '==', 2)); + +Assert::true(Helpers::compare(1, '===', 1)); +Assert::false(Helpers::compare(1.0, '===', 1)); +Assert::false(Helpers::compare(1, '===', 2)); + +Assert::false(Helpers::compare(1, '<>', 1)); +Assert::false(Helpers::compare(1.0, '<>', 1)); +Assert::true(Helpers::compare(1, '<>', 2)); + +Assert::false(Helpers::compare(1, '!=', 1)); +Assert::false(Helpers::compare(1.0, '!=', 1)); +Assert::true(Helpers::compare(1, '!=', 2)); + +Assert::false(Helpers::compare(1, '!==', 1)); +Assert::true(Helpers::compare(1.0, '!==', 1)); +Assert::true(Helpers::compare(1, '!==', 2)); + +Assert::exception(function () { + Helpers::compare(1, 'x', 1); +}, Nette\InvalidArgumentException::class, "Unknown operator 'x'"); From cb92bc6111dd9648bb45ef579979cd93b6f526c0 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 21 Jan 2022 08:08:02 +0100 Subject: [PATCH 23/24] Json: added decodeFile() --- src/Utils/Json.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Utils/Json.php b/src/Utils/Json.php index fab5b6f45..c28d89019 100644 --- a/src/Utils/Json.php +++ b/src/Utils/Json.php @@ -71,4 +71,19 @@ public static function decode(string $json, bool|int $forceArray = false): mixed return $value; } + + + /** + * Converts given JSON file to PHP value. + * @throws JsonException + */ + public static function decodeFile(string $file, bool|int $forceArray = false): mixed + { + if (!is_file($file)) { + throw new Nette\IOException("File '$file' does not exist."); + } + + $input = file_get_contents($file); + return self::decode($input, $forceArray); + } } From e77da731c023a01a912f6349fe36760e136e8a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bar=C3=A1=C5=A1ek?= Date: Mon, 31 Jan 2022 13:41:04 +0100 Subject: [PATCH 24/24] Delete: During the deletion of a folder, additional files may be added. --- src/Utils/FileSystem.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Utils/FileSystem.php b/src/Utils/FileSystem.php index 826d07b2d..80bd55344 100644 --- a/src/Utils/FileSystem.php +++ b/src/Utils/FileSystem.php @@ -96,9 +96,13 @@ public static function delete(string $path): void )); } } elseif (is_dir($path)) { - foreach (new \FilesystemIterator($path) as $item) { - static::delete($item->getPathname()); - } + $items = []; + do { + foreach ($items as $item) { + static::delete($item->getPathname()); + } + $items = iterator_to_array(new \FilesystemIterator($path)); + } while ($items !== []); if (!@rmdir($path)) { // @ is escalated to exception throw new Nette\IOException(sprintf(