Update tools

This commit is contained in:
Sebastian Bergmann 2025-12-31 14:59:27 +01:00
parent d4d9bba3c5
commit 69a57cf8d4
No known key found for this signature in database
GPG Key ID: 4AA394086372C20A
45 changed files with 298 additions and 282 deletions

View File

@ -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>

View File

@ -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": {

View File

@ -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"
}

View File

@ -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',
),

View File

@ -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": [
{

View File

@ -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(),

View File

@ -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"
}
}
}

View File

@ -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.
 <!---->

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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 = [];

View File

@ -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]);
}

View File

@ -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'");
}
}
}

View File

@ -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 = '')

View File

@ -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

View File

@ -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)
) {

View File

@ -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)];
}
}

View File

@ -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');

View File

@ -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;
}

View File

@ -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(

View File

@ -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;

View File

@ -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
}

View File

@ -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),
),
);
}

View File

@ -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',
),
);

View File

@ -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>
&nbsp;&nbsp;&nbsp;
<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>

View File

@ -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);

View File

@ -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",

View File

@ -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-----

View File

@ -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

View File

@ -1,5 +0,0 @@
/vendor
composer.lock
.idea
/.phpunit.cache

View File

@ -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

View File

@ -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/*',
]);

View File

@ -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
{

View File

@ -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
{

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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 [];

View File

@ -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;

View File

@ -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;

View File

@ -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;

Binary file not shown.

Binary file not shown.