Update tools
This commit is contained in:
parent
d4d9bba3c5
commit
69a57cf8d4
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phive xmlns="https://phar.io/phive">
|
||||
<phar name="php-cs-fixer" version="^3.71" installed="3.88.0" location="./tools/php-cs-fixer" copy="true"/>
|
||||
<phar name="composer" version="^2.8" installed="2.8.12" location="./tools/composer" copy="true"/>
|
||||
<phar name="php-cs-fixer" version="^3.71" installed="3.92.3" location="./tools/php-cs-fixer" copy="true"/>
|
||||
<phar name="composer" version="^2.8" installed="2.9.3" location="./tools/composer" copy="true"/>
|
||||
<phar name="infection" version="^0.29" installed="0.29.14" location="./tools/infection" copy="true"/>
|
||||
</phive>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1.29",
|
||||
"phpstan/phpstan": "^2.1.33",
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.7",
|
||||
"tomasvotruba/type-coverage": "^2.0.2",
|
||||
"tomasvotruba/type-coverage": "^2.1.0",
|
||||
"ergebnis/phpstan-rules": "^2.12.0"
|
||||
},
|
||||
"config": {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "8f0377de28eecfcab5091f1dd4d26669",
|
||||
"content-hash": "4409cdc49212f7e878b14b7ae9285bb2",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
|
|
@ -82,20 +82,20 @@
|
|||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v4.0.8",
|
||||
"version": "v4.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede"
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "8.0 - 8.5"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/finder": "<3",
|
||||
|
|
@ -118,7 +118,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -165,9 +165,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.8"
|
||||
"source": "https://github.com/nette/utils/tree/v4.1.1"
|
||||
},
|
||||
"time": "2025-08-06T21:43:34+00:00"
|
||||
"time": "2025-12-22T12:14:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/extension-installer",
|
||||
|
|
@ -219,16 +219,11 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.29",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phar-composer-source.git",
|
||||
"reference": "git"
|
||||
},
|
||||
"version": "2.1.33",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
|
||||
"reference": "d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
|
||||
"reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -273,7 +268,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-25T06:58:18+00:00"
|
||||
"time": "2025-12-05T10:24:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
|
|
@ -325,16 +320,16 @@
|
|||
},
|
||||
{
|
||||
"name": "tomasvotruba/type-coverage",
|
||||
"version": "2.0.2",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TomasVotruba/type-coverage.git",
|
||||
"reference": "d033429580f2c18bda538fa44f2939236a990e0c"
|
||||
"reference": "468354b3964120806a69890cbeb3fcf005876391"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c",
|
||||
"reference": "d033429580f2c18bda538fa44f2939236a990e0c",
|
||||
"url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/468354b3964120806a69890cbeb3fcf005876391",
|
||||
"reference": "468354b3964120806a69890cbeb3fcf005876391",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -366,7 +361,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/TomasVotruba/type-coverage/issues",
|
||||
"source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2"
|
||||
"source": "https://github.com/TomasVotruba/type-coverage/tree/2.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -378,7 +373,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-01-07T00:10:26+00:00"
|
||||
"time": "2025-12-05T16:38:02+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
@ -388,5 +383,5 @@
|
|||
"prefer-lowest": false,
|
||||
"platform": {},
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,43 +11,43 @@ class ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7
|
|||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'T' =>
|
||||
'T' =>
|
||||
array (
|
||||
'TomasVotruba\\TypeCoverage\\' => 26,
|
||||
),
|
||||
'P' =>
|
||||
'P' =>
|
||||
array (
|
||||
'PHPStan\\ExtensionInstaller\\' => 27,
|
||||
'PHPStan\\' => 8,
|
||||
),
|
||||
'N' =>
|
||||
'N' =>
|
||||
array (
|
||||
'Nette\\' => 6,
|
||||
),
|
||||
'E' =>
|
||||
'E' =>
|
||||
array (
|
||||
'Ergebnis\\PHPStan\\Rules\\' => 23,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'TomasVotruba\\TypeCoverage\\' =>
|
||||
'TomasVotruba\\TypeCoverage\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/tomasvotruba/type-coverage/src',
|
||||
),
|
||||
'PHPStan\\ExtensionInstaller\\' =>
|
||||
'PHPStan\\ExtensionInstaller\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/phpstan/extension-installer/src',
|
||||
),
|
||||
'PHPStan\\' =>
|
||||
'PHPStan\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src',
|
||||
),
|
||||
'Nette\\' =>
|
||||
'Nette\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nette/utils/src',
|
||||
),
|
||||
'Ergebnis\\PHPStan\\Rules\\' =>
|
||||
'Ergebnis\\PHPStan\\Rules\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ergebnis/phpstan-rules/src',
|
||||
),
|
||||
|
|
|
|||
|
|
@ -78,21 +78,21 @@
|
|||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v4.0.8",
|
||||
"version_normalized": "4.0.8.0",
|
||||
"version": "v4.1.1",
|
||||
"version_normalized": "4.1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede"
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "8.0 - 8.5"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/finder": "<3",
|
||||
|
|
@ -112,11 +112,11 @@
|
|||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"time": "2025-08-06T21:43:34+00:00",
|
||||
"time": "2025-12-22T12:14:32+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
|
|
@ -164,7 +164,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.8"
|
||||
"source": "https://github.com/nette/utils/tree/v4.1.1"
|
||||
},
|
||||
"install-path": "../nette/utils"
|
||||
},
|
||||
|
|
@ -221,17 +221,12 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.29",
|
||||
"version_normalized": "2.1.29.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phar-composer-source.git",
|
||||
"reference": "git"
|
||||
},
|
||||
"version": "2.1.33",
|
||||
"version_normalized": "2.1.33.0",
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
|
||||
"reference": "d618573eed4a1b6b75e37b2e0b65ac65c885d88e",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/9e800e6bee7d5bd02784d4c6069b48032d16224f",
|
||||
"reference": "9e800e6bee7d5bd02784d4c6069b48032d16224f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -240,7 +235,7 @@
|
|||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"time": "2025-09-25T06:58:18+00:00",
|
||||
"time": "2025-12-05T10:24:31+00:00",
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
|
|
@ -333,17 +328,17 @@
|
|||
},
|
||||
{
|
||||
"name": "tomasvotruba/type-coverage",
|
||||
"version": "2.0.2",
|
||||
"version_normalized": "2.0.2.0",
|
||||
"version": "2.1.0",
|
||||
"version_normalized": "2.1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/TomasVotruba/type-coverage.git",
|
||||
"reference": "d033429580f2c18bda538fa44f2939236a990e0c"
|
||||
"reference": "468354b3964120806a69890cbeb3fcf005876391"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c",
|
||||
"reference": "d033429580f2c18bda538fa44f2939236a990e0c",
|
||||
"url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/468354b3964120806a69890cbeb3fcf005876391",
|
||||
"reference": "468354b3964120806a69890cbeb3fcf005876391",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -351,7 +346,7 @@
|
|||
"php": "^7.4 || ^8.0",
|
||||
"phpstan/phpstan": "^2.0"
|
||||
},
|
||||
"time": "2025-01-07T00:10:26+00:00",
|
||||
"time": "2025-12-05T16:38:02+00:00",
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
|
|
@ -377,7 +372,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/TomasVotruba/type-coverage/issues",
|
||||
"source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2"
|
||||
"source": "https://github.com/TomasVotruba/type-coverage/tree/2.1.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '016951ae10980765e4e7aee491eb288c64e505b7',
|
||||
'reference' => 'd4d9bba3c5904cdebea5c9bebab27d8abd6c1af1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -13,7 +13,7 @@
|
|||
'__root__' => array(
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '016951ae10980765e4e7aee491eb288c64e505b7',
|
||||
'reference' => 'd4d9bba3c5904cdebea5c9bebab27d8abd6c1af1',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -29,9 +29,9 @@
|
|||
'dev_requirement' => true,
|
||||
),
|
||||
'nette/utils' => array(
|
||||
'pretty_version' => 'v4.0.8',
|
||||
'version' => '4.0.8.0',
|
||||
'reference' => 'c930ca4e3cf4f17dcfb03037703679d2396d2ede',
|
||||
'pretty_version' => 'v4.1.1',
|
||||
'version' => '4.1.1.0',
|
||||
'reference' => 'c99059c0315591f1a0db7ad6002000288ab8dc72',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nette/utils',
|
||||
'aliases' => array(),
|
||||
|
|
@ -47,9 +47,9 @@
|
|||
'dev_requirement' => true,
|
||||
),
|
||||
'phpstan/phpstan' => array(
|
||||
'pretty_version' => '2.1.29',
|
||||
'version' => '2.1.29.0',
|
||||
'reference' => 'd618573eed4a1b6b75e37b2e0b65ac65c885d88e',
|
||||
'pretty_version' => '2.1.33',
|
||||
'version' => '2.1.33.0',
|
||||
'reference' => '9e800e6bee7d5bd02784d4c6069b48032d16224f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpstan/phpstan',
|
||||
'aliases' => array(),
|
||||
|
|
@ -65,9 +65,9 @@
|
|||
'dev_requirement' => true,
|
||||
),
|
||||
'tomasvotruba/type-coverage' => array(
|
||||
'pretty_version' => '2.0.2',
|
||||
'version' => '2.0.2.0',
|
||||
'reference' => 'd033429580f2c18bda538fa44f2939236a990e0c',
|
||||
'pretty_version' => '2.1.0',
|
||||
'version' => '2.1.0.0',
|
||||
'reference' => '468354b3964120806a69890cbeb3fcf005876391',
|
||||
'type' => 'phpstan-extension',
|
||||
'install_path' => __DIR__ . '/../tomasvotruba/type-coverage',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "8.0 - 8.5"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.5",
|
||||
|
|
@ -48,7 +48,7 @@
|
|||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,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.5.
|
||||
Nette Utils 4.1 is compatible with PHP 8.2 to 8.5.
|
||||
|
||||
<!---->
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class CachingIterator extends \CachingIterator implements \Countable
|
|||
public function __construct(iterable|\stdClass $iterable)
|
||||
{
|
||||
$iterable = $iterable instanceof \stdClass
|
||||
? new \ArrayIterator($iterable)
|
||||
? new \ArrayIterator((array) $iterable)
|
||||
: Nette\Utils\Iterables::toIterator($iterable);
|
||||
parent::__construct($iterable, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,14 +21,4 @@ trait StaticClass
|
|||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to undefined static method.
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public static function __callStatic(string $name, array $args): mixed
|
||||
{
|
||||
Utils\ObjectHelpers::strictStaticCall(static::class, $name);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
|||
* @param array-key $key
|
||||
* @return T
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
public function offsetGet($key): mixed
|
||||
{
|
||||
return $this->$key;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ use function array_slice, array_splice, count, is_int;
|
|||
*/
|
||||
class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
private array $list = [];
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ namespace Nette\Utils;
|
|||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function array_combine, array_intersect_key, array_is_list, array_key_exists, array_key_first, array_key_last, array_keys, array_reverse, array_search, array_slice, array_walk_recursive, count, func_num_args, in_array, is_array, is_int, is_object, key, preg_split, range;
|
||||
use const PHP_VERSION_ID, PREG_GREP_INVERT, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
|
||||
use function array_combine, array_intersect_key, array_is_list, array_key_exists, array_key_first, array_key_last, array_keys, array_reverse, array_search, array_slice, array_walk_recursive, count, func_num_args, in_array, is_array, is_int, is_object, key, preg_split;
|
||||
use const PREG_GREP_INVERT, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -278,11 +278,7 @@ class Arrays
|
|||
*/
|
||||
public static function isList(mixed $value): bool
|
||||
{
|
||||
return is_array($value) && (
|
||||
PHP_VERSION_ID < 80100
|
||||
? !$value || array_keys($value) === range(0, count($value) - 1)
|
||||
: array_is_list($value)
|
||||
);
|
||||
return is_array($value) && array_is_list($value);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -533,7 +529,7 @@ class Arrays
|
|||
*/
|
||||
public static function toKey(mixed $value): int|string
|
||||
{
|
||||
return key([$value => null]);
|
||||
return key(@[$value => null]);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_merge, checkdate, implode, is_numeric, is_string, preg_replace_callback, sprintf, time, trim;
|
||||
|
||||
|
||||
|
|
@ -18,8 +17,6 @@ use function array_merge, checkdate, implode, is_numeric, is_string, preg_replac
|
|||
*/
|
||||
class DateTime extends \DateTime implements \JsonSerializable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** minute in seconds */
|
||||
public const MINUTE = 60;
|
||||
|
||||
|
|
@ -63,7 +60,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
|
||||
/**
|
||||
* Creates DateTime object.
|
||||
* @throws Nette\InvalidArgumentException if the date and time are not valid.
|
||||
* @throws \Exception if the date and time are not valid.
|
||||
*/
|
||||
public static function fromParts(
|
||||
int $year,
|
||||
|
|
@ -74,17 +71,10 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
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)
|
||||
|| $hour < 0 || $hour > 23
|
||||
|| $minute < 0 || $minute > 59
|
||||
|| $second < 0 || $second >= 60
|
||||
) {
|
||||
throw new Nette\InvalidArgumentException("Invalid date '$s'");
|
||||
}
|
||||
|
||||
return new static($s);
|
||||
$sec = (int) floor($second);
|
||||
return (new static(''))
|
||||
->setDate($year, $month, $day)
|
||||
->setTime($hour, $minute, $sec, (int) round(($second - $sec) * 1e6));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -122,7 +112,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
public function setDate(int $year, int $month, int $day): static
|
||||
{
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
trigger_error(sprintf(self::class . ': The date %04d-%02d-%02d is not valid.', $year, $month, $day), E_USER_WARNING);
|
||||
throw new \Exception(sprintf('The date %04d-%02d-%02d is not valid.', $year, $month, $day));
|
||||
}
|
||||
return parent::setDate($year, $month, $day);
|
||||
}
|
||||
|
|
@ -136,7 +126,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
|| $second < 0 || $second >= 60
|
||||
|| $microsecond < 0 || $microsecond >= 1_000_000
|
||||
) {
|
||||
trigger_error(sprintf(self::class . ': The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000), E_USER_WARNING);
|
||||
throw new \Exception(sprintf('The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000));
|
||||
}
|
||||
return parent::setTime($hour, $minute, $second, $microsecond);
|
||||
}
|
||||
|
|
@ -213,7 +203,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
$errors = self::getLastErrors();
|
||||
$errors = array_merge($errors['errors'] ?? [], $errors['warnings'] ?? []);
|
||||
if ($errors) {
|
||||
trigger_error(self::class . ': ' . implode(', ', $errors) . " '$value'", E_USER_WARNING);
|
||||
throw new \Exception(implode(', ', $errors) . " '$value'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use const DIRECTORY_SEPARATOR;
|
|||
*/
|
||||
final class FileInfo extends \SplFileInfo
|
||||
{
|
||||
private string $relativePath;
|
||||
private readonly string $relativePath;
|
||||
|
||||
|
||||
public function __construct(string $file, string $relativePath = '')
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ final class FileSystem
|
|||
* Creates a directory if it does not exist, including parent directories.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function createDir(string $dir, int $mode = 0777): void
|
||||
public static function createDir(string $dir, int $mode = 0o777): void
|
||||
{
|
||||
if (!is_dir($dir) && !@mkdir($dir, $mode, recursive: true) && !is_dir($dir)) { // @ - dir may already exist
|
||||
throw new Nette\IOException(sprintf(
|
||||
|
|
@ -211,7 +211,7 @@ final class FileSystem
|
|||
* Writes the string to a file.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function write(string $file, string $content, ?int $mode = 0666): void
|
||||
public static function write(string $file, string $content, ?int $mode = 0o666): void
|
||||
{
|
||||
static::createDir(dirname($file));
|
||||
if (@file_put_contents($file, $content) === false) { // @ is escalated to exception
|
||||
|
|
@ -238,7 +238,7 @@ final class FileSystem
|
|||
* Recursively traverses and sets permissions on the entire contents of the directory as well.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void
|
||||
public static function makeWritable(string $path, int $dirMode = 0o777, int $fileMode = 0o666): void
|
||||
{
|
||||
if (is_file($path)) {
|
||||
if (!@chmod($path, $fileMode)) { // @ is escalated to exception
|
||||
|
|
|
|||
|
|
@ -26,8 +26,6 @@ use const GLOB_NOESCAPE, GLOB_NOSORT, GLOB_ONLYDIR;
|
|||
*/
|
||||
class Finder implements \IteratorAggregate
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array<array{string, string}> */
|
||||
private array $find = [];
|
||||
|
||||
|
|
@ -387,7 +385,7 @@ class Finder implements \IteratorAggregate
|
|||
$relativePathname = FileSystem::unixSlashes($file->getRelativePathname());
|
||||
foreach ($searches as $search) {
|
||||
if (
|
||||
$file->{'is' . $search->mode}()
|
||||
"is_$search->mode"(Helpers::IsWindows && $file->isLink() ? $file->getLinkTarget() : $file->getPathname())
|
||||
&& preg_match($search->pattern, $relativePathname)
|
||||
&& $this->proveFilters($this->filters, $file, $cache)
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -106,4 +106,16 @@ class Helpers
|
|||
default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits a class name into namespace and short class name.
|
||||
* @return array{string, string}
|
||||
*/
|
||||
public static function splitClassName(string $name): array
|
||||
{
|
||||
return ($pos = strrpos($name, '\\')) === false
|
||||
? ['', $name]
|
||||
: [substr($name, 0, $pos), substr($name, $pos + 1)];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Nette\HtmlStringable;
|
||||
use function array_merge, array_splice, count, explode, func_num_args, html_entity_decode, htmlspecialchars, http_build_query, implode, is_array, is_bool, is_float, is_object, is_string, json_encode, max, number_format, rtrim, str_contains, str_repeat, str_replace, strip_tags, strncmp, strpbrk, substr;
|
||||
use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
|
||||
|
|
@ -234,20 +233,18 @@ use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
|
|||
*/
|
||||
class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array<string, mixed> element's attributes */
|
||||
public $attrs = [];
|
||||
public array $attrs = [];
|
||||
|
||||
/** void elements */
|
||||
public static $emptyElements = [
|
||||
public static array $emptyElements = [
|
||||
'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1,
|
||||
'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1,
|
||||
'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1,
|
||||
];
|
||||
|
||||
/** @var array<int, HtmlStringable|string> nodes */
|
||||
protected $children = [];
|
||||
protected array $children = [];
|
||||
|
||||
/** element's name */
|
||||
private string $name = '';
|
||||
|
|
@ -576,7 +573,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
|||
/**
|
||||
* Adds new element's child.
|
||||
*/
|
||||
final public function addHtml(mixed $child): static
|
||||
final public function addHtml(HtmlStringable|string $child): static
|
||||
{
|
||||
return $this->insert(null, $child);
|
||||
}
|
||||
|
|
@ -585,7 +582,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
|||
/**
|
||||
* Appends plain-text string to element content.
|
||||
*/
|
||||
public function addText(mixed $text): static
|
||||
public function addText(\Stringable|string|int|null $text): static
|
||||
{
|
||||
if (!$text instanceof HtmlStringable) {
|
||||
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
|
||||
|
|
|
|||
|
|
@ -239,8 +239,8 @@ class Image
|
|||
*/
|
||||
public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int
|
||||
{
|
||||
[$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
|
||||
return isset(self::Formats[$type]) ? $type : null;
|
||||
[$width, $height, $type] = Helpers::falseToNull(@getimagesize($file)); // @ - files smaller than 12 bytes causes read error
|
||||
return $type && isset(self::Formats[$type]) ? $type : null;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -250,8 +250,8 @@ class Image
|
|||
*/
|
||||
public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int
|
||||
{
|
||||
[$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error
|
||||
return isset(self::Formats[$type]) ? $type : null;
|
||||
[$width, $height, $type] = Helpers::falseToNull(@getimagesizefromstring($s)); // @ - strings smaller than 12 bytes causes read error
|
||||
return $type && isset(self::Formats[$type]) ? $type : null;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,31 @@ final class Iterables
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a repeatable iterator from a factory function.
|
||||
* The factory is called every time the iterator is iterated.
|
||||
* @template K
|
||||
* @template V
|
||||
* @param callable(): iterable<K, V> $factory
|
||||
* @return \IteratorAggregate<K, V>
|
||||
*/
|
||||
public static function repeatable(callable $factory): \IteratorAggregate
|
||||
{
|
||||
return new class ($factory) implements \IteratorAggregate {
|
||||
public function __construct(
|
||||
private $factory,
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
public function getIterator(): \Iterator
|
||||
{
|
||||
return Iterables::toIterator(($this->factory)());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps around iterator and caches its keys and values during iteration.
|
||||
* This allows the data to be re-iterated multiple times.
|
||||
|
|
@ -186,7 +211,7 @@ final class Iterables
|
|||
* @param iterable<K, V> $iterable
|
||||
* @return \IteratorAggregate<K, V>
|
||||
*/
|
||||
public static function memoize(iterable $iterable): iterable
|
||||
public static function memoize(iterable $iterable): \IteratorAggregate
|
||||
{
|
||||
return new class (self::toIterator($iterable)) implements \IteratorAggregate {
|
||||
public function __construct(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Nette\Utils;
|
|||
|
||||
use Nette;
|
||||
use function constant, current, defined, end, explode, file_get_contents, implode, ltrim, next, ord, strrchr, strtolower, substr;
|
||||
use const PHP_VERSION_ID, T_AS, T_CLASS, T_COMMENT, T_CURLY_OPEN, T_DOC_COMMENT, T_DOLLAR_OPEN_CURLY_BRACES, T_ENUM, T_INTERFACE, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAMESPACE, T_NS_SEPARATOR, T_STRING, T_TRAIT, T_USE, T_WHITESPACE, TOKEN_PARSE;
|
||||
use const T_AS, T_CLASS, T_COMMENT, T_CURLY_OPEN, T_DOC_COMMENT, T_DOLLAR_OPEN_CURLY_BRACES, T_ENUM, T_INTERFACE, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAMESPACE, T_NS_SEPARATOR, T_STRING, T_TRAIT, T_USE, T_WHITESPACE, TOKEN_PARSE;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -28,7 +28,7 @@ final class Reflection
|
|||
}
|
||||
|
||||
|
||||
/** @deprecated use Nette\Utils\Validators::isClassKeyword() */
|
||||
#[\Deprecated('use Nette\Utils\Validators::isClassKeyword()')]
|
||||
public static function isClassKeyword(string $name): bool
|
||||
{
|
||||
return Validators::isClassKeyword($name);
|
||||
|
|
@ -137,9 +137,7 @@ final class Reflection
|
|||
} elseif ($ref instanceof \ReflectionMethod) {
|
||||
return $ref->getDeclaringClass()->name . '::' . $ref->name . '()';
|
||||
} elseif ($ref instanceof \ReflectionFunction) {
|
||||
return PHP_VERSION_ID >= 80200 && $ref->isAnonymous()
|
||||
? '{closure}()'
|
||||
: $ref->name . '()';
|
||||
return $ref->isAnonymous() ? '{closure}()' : $ref->name . '()';
|
||||
} elseif ($ref instanceof \ReflectionProperty) {
|
||||
return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name;
|
||||
} elseif ($ref instanceof \ReflectionParameter) {
|
||||
|
|
@ -241,7 +239,7 @@ final class Reflection
|
|||
case T_CLASS:
|
||||
case T_INTERFACE:
|
||||
case T_TRAIT:
|
||||
case PHP_VERSION_ID < 80100 ? T_CLASS : T_ENUM:
|
||||
case T_ENUM:
|
||||
if ($name = self::fetch($tokens, T_STRING)) {
|
||||
$class = $namespace . $name;
|
||||
$classLevel = $level + 1;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class Strings
|
|||
|
||||
public const TrimCharacters = " \t\n\r\0\x0B\u{A0}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{200B}\u{2028}\u{3000}";
|
||||
|
||||
/** @deprecated use Strings::TrimCharacters */
|
||||
#[\Deprecated('use Strings::TrimCharacters')]
|
||||
public const TRIM_CHARACTERS = self::TrimCharacters;
|
||||
|
||||
|
||||
|
|
@ -186,17 +186,12 @@ class Strings
|
|||
*/
|
||||
public static function toAscii(string $s): string
|
||||
{
|
||||
$iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
|
||||
static $transliterator = null;
|
||||
if ($transliterator === null) {
|
||||
if (class_exists('Transliterator', false)) {
|
||||
$transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
|
||||
} else {
|
||||
trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE);
|
||||
$transliterator = false;
|
||||
}
|
||||
if (!extension_loaded('intl')) {
|
||||
throw new Nette\NotSupportedException(__METHOD__ . '() requires INTL extension that is not loaded.');
|
||||
}
|
||||
|
||||
$iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
|
||||
|
||||
// remove control characters and check UTF-8 validity
|
||||
$s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]);
|
||||
|
||||
|
|
@ -206,39 +201,15 @@ class Strings
|
|||
$s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔
|
||||
}
|
||||
|
||||
if ($transliterator) {
|
||||
$s = $transliterator->transliterate($s);
|
||||
// use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
|
||||
if ($iconv === 'glibc') {
|
||||
$s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
$s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
|
||||
} elseif ($iconv === 'libiconv') {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
} else { // null or 'unknown' (#216)
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
|
||||
}
|
||||
} elseif ($iconv === 'glibc' || $iconv === 'libiconv') {
|
||||
// temporarily hide these characters to distinguish them from the garbage that iconv creates
|
||||
$s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
|
||||
if ($iconv === 'glibc') {
|
||||
// glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved
|
||||
$s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s);
|
||||
$s = strtr(
|
||||
$s,
|
||||
"\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
|
||||
'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.',
|
||||
);
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]);
|
||||
} else {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
}
|
||||
|
||||
// remove garbage that iconv creates during transliteration (eg Ý -> Y')
|
||||
$s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s);
|
||||
// restore temporarily hidden characters
|
||||
$s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
|
||||
} else {
|
||||
$s = \Transliterator::create('Any-Latin; Latin-ASCII')->transliterate($s);
|
||||
// use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
|
||||
if ($iconv === 'glibc') {
|
||||
$s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
$s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
|
||||
} elseif ($iconv === 'libiconv') {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
} else { // null or 'unknown' (#216)
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,14 +10,13 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_map, array_search, array_splice, count, explode, implode, is_a, is_string, strcasecmp, strtolower, substr, trim;
|
||||
use const PHP_VERSION_ID;
|
||||
use function array_map, array_search, array_splice, count, explode, implode, is_a, is_resource, is_string, strcasecmp, strtolower, substr, trim;
|
||||
|
||||
|
||||
/**
|
||||
* PHP type reflection.
|
||||
*/
|
||||
final class Type
|
||||
final readonly class Type
|
||||
{
|
||||
/** @var array<int, string|self> */
|
||||
private array $types;
|
||||
|
|
@ -34,7 +33,7 @@ final class Type
|
|||
): ?self
|
||||
{
|
||||
$type = $reflection instanceof \ReflectionFunctionAbstract
|
||||
? $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
|
||||
? $reflection->getReturnType() ?? ($reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
|
||||
: $reflection->getType();
|
||||
|
||||
return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null;
|
||||
|
|
@ -86,6 +85,23 @@ final class Type
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Type object based on the actual type of value.
|
||||
*/
|
||||
public static function fromValue(mixed $value): self
|
||||
{
|
||||
$type = get_debug_type($value);
|
||||
if (is_resource($value)) {
|
||||
$type = 'mixed';
|
||||
} elseif (str_ends_with($type, '@anonymous')) {
|
||||
$parent = substr($type, 0, -10);
|
||||
$type = $parent === 'class' ? 'object' : $parent;
|
||||
}
|
||||
|
||||
return new self([$type]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves 'self', 'static' and 'parent' to the actual class name.
|
||||
*/
|
||||
|
|
@ -138,6 +154,23 @@ final class Type
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a type that accepts both the current type and the given type.
|
||||
*/
|
||||
public function with(string|self $type): self
|
||||
{
|
||||
$type = is_string($type) ? self::fromString($type) : $type;
|
||||
return match (true) {
|
||||
$this->allows($type) => $this,
|
||||
$type->allows($this) => $type,
|
||||
default => new self(array_unique(
|
||||
array_merge($this->isIntersection() ? [$this] : $this->types, $type->isIntersection() ? [$type] : $type->types),
|
||||
SORT_REGULAR,
|
||||
), '|'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the array of subtypes that make up the compound type as strings.
|
||||
* @return array<int, string|string[]>
|
||||
|
|
@ -196,7 +229,7 @@ final class Type
|
|||
}
|
||||
|
||||
|
||||
/** @deprecated use isSimple() */
|
||||
#[\Deprecated('use isSimple()')]
|
||||
public function isSingle(): bool
|
||||
{
|
||||
return $this->simple;
|
||||
|
|
@ -233,36 +266,36 @@ final class Type
|
|||
/**
|
||||
* Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter.
|
||||
*/
|
||||
public function allows(string $subtype): bool
|
||||
public function allows(string|self $type): bool
|
||||
{
|
||||
if ($this->types === ['mixed']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$subtype = self::fromString($subtype);
|
||||
return $subtype->isUnion()
|
||||
? Arrays::every($subtype->types, fn($t) => $this->allows2($t instanceof self ? $t->types : [$t]))
|
||||
: $this->allows2($subtype->types);
|
||||
$type = is_string($type) ? self::fromString($type) : $type;
|
||||
return $type->isUnion()
|
||||
? Arrays::every($type->types, fn($t) => $this->allowsAny($t instanceof self ? $t->types : [$t]))
|
||||
: $this->allowsAny($type->types);
|
||||
}
|
||||
|
||||
|
||||
private function allows2(array $subtypes): bool
|
||||
private function allowsAny(array $givenTypes): bool
|
||||
{
|
||||
return $this->isUnion()
|
||||
? Arrays::some($this->types, fn($t) => $this->allows3($t instanceof self ? $t->types : [$t], $subtypes))
|
||||
: $this->allows3($this->types, $subtypes);
|
||||
? Arrays::some($this->types, fn($t) => $this->allowsAll($t instanceof self ? $t->types : [$t], $givenTypes))
|
||||
: $this->allowsAll($this->types, $givenTypes);
|
||||
}
|
||||
|
||||
|
||||
private function allows3(array $types, array $subtypes): bool
|
||||
private function allowsAll(array $ourTypes, array $givenTypes): bool
|
||||
{
|
||||
return Arrays::every(
|
||||
$types,
|
||||
fn($type) => Arrays::some(
|
||||
$subtypes,
|
||||
fn($subtype) => Validators::isBuiltinType($type)
|
||||
? strcasecmp($type, $subtype) === 0
|
||||
: is_a($subtype, $type, allow_string: true),
|
||||
$ourTypes,
|
||||
fn($ourType) => Arrays::some(
|
||||
$givenTypes,
|
||||
fn($givenType) => Validators::isBuiltinType($ourType)
|
||||
? strcasecmp($ourType, $givenType) === 0
|
||||
: is_a($givenType, $ourType, allow_string: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ final class GeneratedConfig
|
|||
0 => 'config/extension.neon',
|
||||
),
|
||||
),
|
||||
'version' => '2.0.2',
|
||||
'version' => '2.1.0',
|
||||
'phpstanVersionConstraint' => '>=2.0.0.0-dev, <3.0.0.0-dev',
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,10 @@ Want your logo here? [Learn more »](https://phpstan.org/sponsor)
|
|||
### Gold Sponsors
|
||||
|
||||
<a href="https://ma.tt/"><img src="website/src/images/sponsor/matt.png" alt="Matt Mullenweg" width="290" height="64"></a>
|
||||
|
||||
<a href="https://mojam.co/"><img src="website/src/images/sponsor/mojam.png" alt="Mojam" width="290" height="64"></a>
|
||||
<br>
|
||||
<a href="https://www.check24.de/"><img src="website/src/images/sponsor/check24.png" alt="CHECK24" width="283" height="64"></a>
|
||||
|
||||
<br><br>
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,26 @@ final class PharAutoloader
|
|||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php83/Php83.php';
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php83/bootstrap.php';
|
||||
}
|
||||
|
||||
if (
|
||||
PHP_VERSION_ID < 80400
|
||||
&& empty ($GLOBALS['__composer_autoload_files']['9d2b9fc6db0f153a0a149fefb182415e'])
|
||||
&& !class_exists(\Symfony\Polyfill\Php83\Php84::class, false)
|
||||
) {
|
||||
$GLOBALS['__composer_autoload_files']['9d2b9fc6db0f153a0a149fefb182415e'] = true;
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php84/Php84.php';
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php84/bootstrap.php';
|
||||
}
|
||||
|
||||
if (
|
||||
PHP_VERSION_ID < 80500
|
||||
&& empty ($GLOBALS['__composer_autoload_files']['606a39d89246991a373564698c2d8383'])
|
||||
&& !class_exists(\Symfony\Polyfill\Php83\Php85::class, false)
|
||||
) {
|
||||
$GLOBALS['__composer_autoload_files']['606a39d89246991a373564698c2d8383'] = true;
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php85/Php85.php';
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php85/bootstrap.php';
|
||||
}
|
||||
}
|
||||
|
||||
$filename = str_replace('\\', DIRECTORY_SEPARATOR, $class);
|
||||
|
|
|
|||
|
|
@ -17,9 +17,9 @@
|
|||
"files": ["bootstrap.php"]
|
||||
},
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-phar-composer-source.git",
|
||||
"reference": "git"
|
||||
"type": "",
|
||||
"url": "",
|
||||
"reference": ""
|
||||
},
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,16 +1,16 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmjU5+gACgkQUcZzBf/C
|
||||
5cAsfg/7Ba9duYiO9tl4AaiTLkSN9Obp7/gkrbQCbtRmjJ7L3RdqeGXqVzl0MGu3
|
||||
EbfVx8FrIuz6Ei6b30tYzTBPbTxdQFnsCmLTRn7u9Luqi35UXon0NKKcJBv2jFDS
|
||||
xTGl3BEA/TtjCEQc8ZWEtapeDzuQqjZBuvk4rFTYKXMHdekyw7zOR/FwtaUSon3/
|
||||
ISvG/nna5jut5spw03jn6HqJ0y2c0Cr3y6UvXB/H3Qy8pkIP21DzBM3juRYD1bh0
|
||||
AHjyZ4AKS/a3LnLzSoMo1lwKWfxa3/SXX4f6NAf8D/QIrYpsJKX661+LIZ1CzK/u
|
||||
Kl9R5lbwTDycpGS309IkkKnRAKeedbvVIFuxcAM1jlCmJWgLSUTOCFP8ZwMHFqf1
|
||||
+W8FcK1spKUanG7WmsJuFXxd9dDLSaQIMfkUCd0JoGgtmr1yMs8a+XZbngtLmhv2
|
||||
y57efu4ogGd6zC8xuCLRt71o1TU/hd/hizDd7Hn7M/yN8/dsNmQyzcD6+NcnRilw
|
||||
c0JsvUlcqXiTvbP6VT8YrTOdwjy33sijXa8CxzpBJaWP9YXS9XNvLTDsdN/ooH8/
|
||||
JghI5QEGAyqOwPEKJnv3JRs0qErvnONx+Gi452psWzFaMWQQ1l6ACpD9sih+LCYO
|
||||
936uBc+rjxcBjGFbMNx8ZyD3bmxkjHbmJGI0O+J8rBzurLSSdAw=
|
||||
=LCr3
|
||||
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmkysr4ACgkQUcZzBf/C
|
||||
5cDE+RAAlbDueQxsbyo+wqm1sURcXT4fPbwdFAv1RXSYfZl7XraoagxYHssIHt9n
|
||||
TcKuBSOPOmlKgZ4FmVW05TRr0yoLZtckpRDCWuJoPzI9ilW2tiVi6AbEVhV0EqwL
|
||||
xVToGIzZXmti+X5Mm2OwQMCdpAbqpAy0mwmv/TudFiKdgZBzl5zHWDUSNCwP8s2m
|
||||
pLGBupQis6sA45+u28vLpyR7vn3mIq3F3hJgb5nrZlWt33jVJ7MDFnpGpeJ6+jBn
|
||||
/1BmefK0sXwN4EqaJbrRkHdRePISt2+jQXa/V/L3oJ0a3hVFufcGze41ARecSQ/0
|
||||
203aB0XcBtIFlshE5OIpXW+ibmgBabd+ggZEkRoS3OXtV5cJizLh49B+16A97QOr
|
||||
H0NWgLfY/W1u2t0Iu+WDT37Na5LPsuucW9vv9PwS6WY9eCr4Jbbmq1PtF0T17aqx
|
||||
STyPWNjs5A8NNjVUQ+Ufj/AAdH3hmNj4B5H6psxHD5UsFHoOLVFtwih4GdHrKXIZ
|
||||
N2lznVo8HDCcvKo6C4F8ejG1t4f5qBHNpYeEC9aVQVvXrBockQQz4lUclibW7MIe
|
||||
g33fXgO/GIm0dn2u0NCzjYof+TAVOfvtbK4GY0Z9BOlJcQnzu7V1LxqFamG5ef+u
|
||||
A2ABQa0HVEeKTomB3jzNQQVKw9QzWyfI21xSskfpFs4FXW3A5xc=
|
||||
=edJd
|
||||
-----END PGP SIGNATURE-----
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
/vendor
|
||||
composer.lock
|
||||
|
||||
.idea
|
||||
/.phpunit.cache
|
||||
|
|
@ -2,25 +2,34 @@
|
|||
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
<img src="/docs/required_type_level.jpg" style="width: 25em" alt="AI abilities sea level rising... as way to rise type coverage for class elements">
|
||||
</div>
|
||||
A PHPStan extension, to check and require minimal type coverage of PHP code.
|
||||
|
||||
<br>
|
||||
|
||||
The type coverage rate = total count of **defined** type declarations / total count of **possible** type declarations.
|
||||
|
||||
E.g. we have 10 methods, but only 7 have defined return type = 70 % return type coverage.
|
||||
|
||||
---
|
||||
|
||||
PHPStan uses type declarations to determine the type of variables, properties and other expression. Sometimes it's hard to see what PHPStan errors are the important ones among thousands of others.
|
||||
|
||||
Instead of fixing all PHPStan errors at once, we can start with minimal require type coverage.
|
||||
|
||||
<br>
|
||||
|
||||
What is the type coverage you ask? We have 4 type possible declarations in total here:
|
||||
|
||||
## How to increase type coverage?
|
||||
|
||||
Here we have 3 possible type declarations:
|
||||
|
||||
* property,
|
||||
* param
|
||||
* and return type
|
||||
|
||||
```php
|
||||
final class ConferenceFactory
|
||||
{
|
||||
const SPEAKER_TAG = 'speaker';
|
||||
|
||||
private $talkFactory;
|
||||
|
||||
public function createConference(array $data)
|
||||
|
|
@ -32,20 +41,17 @@ final class ConferenceFactory
|
|||
}
|
||||
```
|
||||
|
||||
*Note: Class constant types require PHP 8.3 to run.*
|
||||
The param type is defined as `array`.
|
||||
|
||||
The param type is defined. But the property, return and constant types are missing.
|
||||
1 defined / 3 possible = **33.3 % type coverage**
|
||||
|
||||
* 1 out of 4 = 25 % coverage
|
||||
<br>
|
||||
|
||||
Our code quality is only at one-quarter of its potential. Let's get to 100 %!
|
||||
Our code quality is only at one-third of its potential. Let's get to 100 %!
|
||||
|
||||
```diff
|
||||
final class ConferenceFactory
|
||||
{
|
||||
- public const SPEAKER_TAG = 'speaker';
|
||||
+ public const string SPEAKER_TAG = 'speaker';
|
||||
|
||||
- private $talkFactory;
|
||||
+ private TalkFactory $talkFactory;
|
||||
|
||||
|
|
@ -69,7 +75,7 @@ This technique is very simple to start even on legacy project. Also, you're now
|
|||
composer require tomasvotruba/type-coverage --dev
|
||||
```
|
||||
|
||||
The package is available on PHP 7.2+ version in tagged releases.
|
||||
The package is available on PHP 7.2+.
|
||||
|
||||
<br>
|
||||
|
||||
|
|
@ -86,6 +92,8 @@ parameters:
|
|||
return: 50
|
||||
param: 35.5
|
||||
property: 70
|
||||
|
||||
# since PHP 8.3
|
||||
constant: 85
|
||||
```
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 61 KiB |
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Config\RectorConfig;
|
||||
|
||||
return RectorConfig::configure()
|
||||
->withPaths([
|
||||
__DIR__ . '/src',
|
||||
__DIR__ . '/tests',
|
||||
])
|
||||
->withPhpSets()
|
||||
->withPreparedSets(deadCode: true, codeQuality: true, codingStyle: true, typeDeclarations: true, privatization: true, naming: true)
|
||||
->withImportNames(removeUnusedImports: true)
|
||||
->withSkip([
|
||||
'*/Fixture/*',
|
||||
'*/Source/*',
|
||||
]);
|
||||
|
|
@ -26,7 +26,7 @@ final class ConstantTypeDeclarationCollector implements Collector
|
|||
|
||||
/**
|
||||
* @param ClassConstantsNode $node
|
||||
* @return mixed[]
|
||||
* @return array<int, int|list<(int | int<1, max>)>>
|
||||
*/
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ final class PropertyTypeDeclarationCollector implements Collector
|
|||
|
||||
/**
|
||||
* @param InClassNode $node
|
||||
* @return mixed[]
|
||||
* @return array<int, int|list<(int | int<1, max>)>>
|
||||
*/
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ final class Configuration
|
|||
|
||||
public function isConstantTypeCoverageEnabled(): bool
|
||||
{
|
||||
// constant types are available only on PHP 8.3+
|
||||
if (PHP_VERSION_ID < 80300) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ final class ScopeConfigurationResolver
|
|||
private static function getPrivateProperty(object $object, string $propertyName): object
|
||||
{
|
||||
$reflectionProperty = new ReflectionProperty($object, $propertyName);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
if (PHP_VERSION_ID < 80100) {
|
||||
$reflectionProperty->setAccessible(true);
|
||||
}
|
||||
|
||||
return $reflectionProperty->getValue($object);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ final class ConstantTypeCoverageRule implements Rule
|
|||
*/
|
||||
private CollectorDataNormalizer $collectorDataNormalizer;
|
||||
|
||||
public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
|
||||
{
|
||||
public function __construct(
|
||||
TypeCoverageFormatter $typeCoverageFormatter,
|
||||
Configuration $configuration,
|
||||
CollectorDataNormalizer $collectorDataNormalizer
|
||||
) {
|
||||
$this->typeCoverageFormatter = $typeCoverageFormatter;
|
||||
$this->configuration = $configuration;
|
||||
$this->collectorDataNormalizer = $collectorDataNormalizer;
|
||||
|
|
@ -69,6 +72,11 @@ final class ConstantTypeCoverageRule implements Rule
|
|||
*/
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
// enable only on PHP 8.3+
|
||||
if (PHP_VERSION_ID < 80300) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// if only subpaths are analysed, skip as data will be false positive
|
||||
if (! ScopeConfigurationResolver::areFullPathsAnalysed($scope)) {
|
||||
return [];
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ final class ParamTypeCoverageRule implements Rule
|
|||
*/
|
||||
private CollectorDataNormalizer $collectorDataNormalizer;
|
||||
|
||||
public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
|
||||
{
|
||||
public function __construct(
|
||||
TypeCoverageFormatter $typeCoverageFormatter,
|
||||
Configuration $configuration,
|
||||
CollectorDataNormalizer $collectorDataNormalizer
|
||||
) {
|
||||
$this->typeCoverageFormatter = $typeCoverageFormatter;
|
||||
$this->configuration = $configuration;
|
||||
$this->collectorDataNormalizer = $collectorDataNormalizer;
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ final class PropertyTypeCoverageRule implements Rule
|
|||
*/
|
||||
private CollectorDataNormalizer $collectorDataNormalizer;
|
||||
|
||||
public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
|
||||
{
|
||||
public function __construct(
|
||||
TypeCoverageFormatter $typeCoverageFormatter,
|
||||
Configuration $configuration,
|
||||
CollectorDataNormalizer $collectorDataNormalizer
|
||||
) {
|
||||
$this->typeCoverageFormatter = $typeCoverageFormatter;
|
||||
$this->configuration = $configuration;
|
||||
$this->collectorDataNormalizer = $collectorDataNormalizer;
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ final class ReturnTypeCoverageRule implements Rule
|
|||
*/
|
||||
private CollectorDataNormalizer $collectorDataNormalizer;
|
||||
|
||||
public function __construct(TypeCoverageFormatter $typeCoverageFormatter, Configuration $configuration, CollectorDataNormalizer $collectorDataNormalizer)
|
||||
{
|
||||
public function __construct(
|
||||
TypeCoverageFormatter $typeCoverageFormatter,
|
||||
Configuration $configuration,
|
||||
CollectorDataNormalizer $collectorDataNormalizer
|
||||
) {
|
||||
$this->typeCoverageFormatter = $typeCoverageFormatter;
|
||||
$this->configuration = $configuration;
|
||||
$this->collectorDataNormalizer = $collectorDataNormalizer;
|
||||
|
|
|
|||
BIN
tools/composer
BIN
tools/composer
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue