Merge branch '6.3'
* 6.3: Prepare release Ignore error Use SplObjectStorage::offsetSet() instead of deprecated SplObjectStorage::attach() Suppress warning Update tools Prepare release Use SplObjectStorage::offsetSet() instead of deprecated SplObjectStorage::attach() Suppress warning Prepare release Test with more versions Do not cache Composer dependencies Suppress warning Do not use implicitly nullable parameters and prepare release Do not use implicitly nullable parameters and prepare release
This commit is contained in:
commit
5c10a78583
|
|
@ -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.72.0" location="./tools/php-cs-fixer" copy="true"/>
|
||||
<phar name="composer" version="^2.8" installed="2.8.6" location="./tools/composer" copy="true"/>
|
||||
<phar name="php-cs-fixer" version="^3.71" installed="3.87.2" location="./tools/php-cs-fixer" copy="true"/>
|
||||
<phar name="composer" version="^2.8" installed="2.8.12" location="./tools/composer" copy="true"/>
|
||||
<phar name="infection" version="^0.29" installed="0.29.14" location="./tools/infection" copy="true"/>
|
||||
</phive>
|
||||
|
|
|
|||
201
ChangeLog.md
201
ChangeLog.md
|
|
@ -8,205 +8,4 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt
|
|||
|
||||
* This component is no longer supported on PHP 8.2
|
||||
|
||||
## [6.3.0] - 2024-12-05
|
||||
|
||||
### Added
|
||||
|
||||
* Optional constructor argument to control maximum string length
|
||||
|
||||
### Deprecated
|
||||
|
||||
* Optional argument for `shortenedRecursiveExport()` and `shortenedExport()` to control maximum string length
|
||||
|
||||
## [6.2.0] - 2024-12-05
|
||||
|
||||
### Added
|
||||
|
||||
* [#67](https://github.com/sebastianbergmann/exporter/issues/67): Optional argument for `shortenedRecursiveExport()` and `shortenedExport()` to control maximum string length
|
||||
|
||||
### Changed
|
||||
|
||||
* [#69](https://github.com/sebastianbergmann/exporter/pull/69): Do not initialize lazy objects during export
|
||||
|
||||
## [6.1.3] - 2024-07-03
|
||||
|
||||
### Changed
|
||||
|
||||
* [#66](https://github.com/sebastianbergmann/exporter/pull/66): Avoid using the Reflection API for some classes
|
||||
* This project now uses PHPStan instead of Psalm for static analysis
|
||||
|
||||
## [6.1.2] - 2024-06-18
|
||||
|
||||
### Changed
|
||||
|
||||
* [#64](https://github.com/sebastianbergmann/exporter/pull/64): Improve performance of `Exporter::exportString()`
|
||||
* [#65](https://github.com/sebastianbergmann/exporter/pull/65): Prevent unnecessary calls to `str_repeat()`
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#62](https://github.com/sebastianbergmann/exporter/issues/62): Do not limit export of arrays by default (to restore BC with versions prior to 6.1.0)
|
||||
|
||||
## [6.1.1] - 2024-06-18
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#61](https://github.com/sebastianbergmann/exporter/issues/61): `count(): Recursion detected` warning triggered
|
||||
|
||||
## [6.1.0] - 2024-06-18
|
||||
|
||||
### Added
|
||||
|
||||
* [#59](https://github.com/sebastianbergmann/exporter/pull/59): Option to limit export of (large) arrays (to speed up PHPUnit)
|
||||
|
||||
### Changed
|
||||
|
||||
* [#60](https://github.com/sebastianbergmann/exporter/pull/60): Take shortcut when exporting a string
|
||||
|
||||
## [6.0.3] - 2024-06-17
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed code coverage metadata
|
||||
|
||||
## [6.0.2] - 2024-06-17 [YANKED]
|
||||
|
||||
### Changed
|
||||
|
||||
* [#58](https://github.com/sebastianbergmann/exporter/pull/58): Remove unnecessary `sprintf()` in hot path
|
||||
|
||||
## [6.0.1] - 2024-03-02
|
||||
|
||||
### Changed
|
||||
|
||||
* Do not use implicitly nullable parameters
|
||||
|
||||
## [6.0.0] - 2024-02-02
|
||||
|
||||
### Removed
|
||||
|
||||
* This component is no longer supported on PHP 8.1
|
||||
|
||||
## [5.1.2] - 2024-03-02
|
||||
|
||||
### Changed
|
||||
|
||||
* Do not use implicitly nullable parameters
|
||||
|
||||
## [5.1.1] - 2023-09-24
|
||||
|
||||
### Changed
|
||||
|
||||
* [#52](https://github.com/sebastianbergmann/exporter/pull/52): Optimize export of large arrays and object graphs
|
||||
|
||||
## [5.1.0] - 2023-09-18
|
||||
|
||||
### Changed
|
||||
|
||||
* [#51](https://github.com/sebastianbergmann/exporter/pull/51): Export arrays using short array syntax
|
||||
|
||||
## [5.0.1] - 2023-09-08
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#49](https://github.com/sebastianbergmann/exporter/issues/49): `Exporter::toArray()` changes `SplObjectStorage` index
|
||||
|
||||
## [5.0.0] - 2023-02-03
|
||||
|
||||
### Changed
|
||||
|
||||
* [#42](https://github.com/sebastianbergmann/exporter/pull/42): Improve export of enumerations
|
||||
|
||||
### Removed
|
||||
|
||||
* This component is no longer supported on PHP 7.3, PHP 7.4 and PHP 8.0
|
||||
|
||||
## [4.0.5] - 2022-09-14
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#47](https://github.com/sebastianbergmann/exporter/pull/47): Fix `float` export precision
|
||||
|
||||
## [4.0.4] - 2021-11-11
|
||||
|
||||
### Changed
|
||||
|
||||
* [#37](https://github.com/sebastianbergmann/exporter/pull/37): Improve export of closed resources
|
||||
|
||||
## [4.0.3] - 2020-09-28
|
||||
|
||||
### Changed
|
||||
|
||||
* Changed PHP version constraint in `composer.json` from `^7.3 || ^8.0` to `>=7.3`
|
||||
|
||||
## [4.0.2] - 2020-06-26
|
||||
|
||||
### Added
|
||||
|
||||
* This component is now supported on PHP 8
|
||||
|
||||
## [4.0.1] - 2020-06-15
|
||||
|
||||
### Changed
|
||||
|
||||
* Tests etc. are now ignored for archive exports
|
||||
|
||||
## [4.0.0] - 2020-02-07
|
||||
|
||||
### Removed
|
||||
|
||||
* This component is no longer supported on PHP 7.0, PHP 7.1, and PHP 7.2
|
||||
|
||||
## [3.1.5] - 2022-09-14
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#47](https://github.com/sebastianbergmann/exporter/pull/47): Fix `float` export precision
|
||||
|
||||
## [3.1.4] - 2021-11-11
|
||||
|
||||
### Changed
|
||||
|
||||
* [#38](https://github.com/sebastianbergmann/exporter/pull/38): Improve export of closed resources
|
||||
|
||||
## [3.1.3] - 2020-11-30
|
||||
|
||||
### Changed
|
||||
|
||||
* Changed PHP version constraint in `composer.json` from `^7.0` to `>=7.0`
|
||||
|
||||
## [3.1.2] - 2019-09-14
|
||||
|
||||
### Fixed
|
||||
|
||||
* [#29](https://github.com/sebastianbergmann/exporter/pull/29): Second parameter for `str_repeat()` must be an integer
|
||||
|
||||
### Removed
|
||||
|
||||
* Remove HHVM-specific code that is no longer needed
|
||||
|
||||
[7.0.0]: https://github.com/sebastianbergmann/exporter/compare/6.3...7.0.0
|
||||
[6.3.0]: https://github.com/sebastianbergmann/exporter/compare/6.2.0...6.3.0
|
||||
[6.2.0]: https://github.com/sebastianbergmann/exporter/compare/6.1.3...6.2.0
|
||||
[6.1.3]: https://github.com/sebastianbergmann/exporter/compare/6.1.2...6.1.3
|
||||
[6.1.2]: https://github.com/sebastianbergmann/exporter/compare/6.1.1...6.1.2
|
||||
[6.1.1]: https://github.com/sebastianbergmann/exporter/compare/6.1.0...6.1.1
|
||||
[6.1.0]: https://github.com/sebastianbergmann/exporter/compare/6.0.3...6.1.0
|
||||
[6.0.3]: https://github.com/sebastianbergmann/exporter/compare/fe0dca49a60d34440e2f086951952dd13aa9a5d2...6.0.3
|
||||
[6.0.2]: https://github.com/sebastianbergmann/exporter/compare/6.0.1...fe0dca49a60d34440e2f086951952dd13aa9a5d2
|
||||
[6.0.1]: https://github.com/sebastianbergmann/exporter/compare/6.0.0...6.0.1
|
||||
[6.0.0]: https://github.com/sebastianbergmann/exporter/compare/5.1...6.0.0
|
||||
[5.1.2]: https://github.com/sebastianbergmann/exporter/compare/5.1.1...5.1.2
|
||||
[5.1.1]: https://github.com/sebastianbergmann/exporter/compare/5.1.0...5.1.1
|
||||
[5.1.0]: https://github.com/sebastianbergmann/exporter/compare/5.0.1...5.1.0
|
||||
[5.0.1]: https://github.com/sebastianbergmann/exporter/compare/5.0.0...5.0.1
|
||||
[5.0.0]: https://github.com/sebastianbergmann/exporter/compare/4.0.5...5.0.0
|
||||
[4.0.5]: https://github.com/sebastianbergmann/exporter/compare/4.0.4...4.0.5
|
||||
[4.0.4]: https://github.com/sebastianbergmann/exporter/compare/4.0.3...4.0.4
|
||||
[4.0.3]: https://github.com/sebastianbergmann/exporter/compare/4.0.2...4.0.3
|
||||
[4.0.2]: https://github.com/sebastianbergmann/exporter/compare/4.0.1...4.0.2
|
||||
[4.0.1]: https://github.com/sebastianbergmann/exporter/compare/4.0.0...4.0.1
|
||||
[4.0.0]: https://github.com/sebastianbergmann/exporter/compare/3.1.2...4.0.0
|
||||
[3.1.5]: https://github.com/sebastianbergmann/exporter/compare/3.1.4...3.1.5
|
||||
[3.1.4]: https://github.com/sebastianbergmann/exporter/compare/3.1.3...3.1.4
|
||||
[3.1.3]: https://github.com/sebastianbergmann/exporter/compare/3.1.2...3.1.3
|
||||
[3.1.2]: https://github.com/sebastianbergmann/exporter/compare/3.1.1...3.1.2
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ final readonly class Exporter
|
|||
|
||||
ini_set('precision', $precisionBackup);
|
||||
|
||||
if ((string) (int) $value === $valueAsString) {
|
||||
if ((string) @(int) $value === $valueAsString) {
|
||||
return $valueAsString . '.0';
|
||||
}
|
||||
|
||||
|
|
@ -405,6 +405,7 @@ final readonly class Exporter
|
|||
. ' ' .
|
||||
$this->recursiveExport($k, $indentation)
|
||||
. ' => ' .
|
||||
/** @phpstan-ignore offsetAccess.invalidOffset */
|
||||
$this->recursiveExport($value[$k], $indentation + 1, $processed)
|
||||
. ",\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ final class ExporterTest extends TestCase
|
|||
$obj->self = $obj;
|
||||
|
||||
$storage = new SplObjectStorage;
|
||||
$storage->attach($obj2);
|
||||
$storage->offsetSet($obj2);
|
||||
|
||||
$resource = fopen('php://memory', 'r');
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^2.1.8",
|
||||
"phpstan/phpstan": "^2.1.28",
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.3",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.6",
|
||||
"tomasvotruba/type-coverage": "^2.0.2",
|
||||
"ergebnis/phpstan-rules": "^2.8.0"
|
||||
"ergebnis/phpstan-rules": "^2.12.0"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
|
|
|||
|
|
@ -4,39 +4,40 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "7c032feec49c9dbbdaa0d52198cabb4c",
|
||||
"content-hash": "82891e636eae6e96ecd26fa1ee317643",
|
||||
"packages": [],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "ergebnis/phpstan-rules",
|
||||
"version": "2.8.0",
|
||||
"version": "2.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ergebnis/phpstan-rules.git",
|
||||
"reference": "30e790621fbad05573ef9cd355279fff5122e080"
|
||||
"reference": "c4e0121a937b3b551f800a86e7d78794da2783ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/30e790621fbad05573ef9cd355279fff5122e080",
|
||||
"reference": "30e790621fbad05573ef9cd355279fff5122e080",
|
||||
"url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/c4e0121a937b3b551f800a86e7d78794da2783ea",
|
||||
"reference": "c4e0121a937b3b551f800a86e7d78794da2783ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"phpstan/phpstan": "^2.0.0"
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"phpstan/phpstan": "^2.1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^4.0.0 || ^5.0.0",
|
||||
"doctrine/orm": "^2.20.0 || ^3.3.0",
|
||||
"ergebnis/composer-normalize": "^2.45.0",
|
||||
"ergebnis/composer-normalize": "^2.47.0",
|
||||
"ergebnis/license": "^2.6.0",
|
||||
"ergebnis/php-cs-fixer-config": "^6.43.0",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.18.0",
|
||||
"nette/di": "^3.1.10",
|
||||
"ergebnis/php-cs-fixer-config": "^6.54.0",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.20.0",
|
||||
"fakerphp/faker": "^1.24.1",
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0.4",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.3",
|
||||
"phpstan/phpstan-phpunit": "^2.0.7",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.6",
|
||||
"phpunit/phpunit": "^9.6.21",
|
||||
"psr/container": "^2.0.2",
|
||||
"symfony/finder": "^5.4.45",
|
||||
|
|
@ -77,33 +78,33 @@
|
|||
"security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md",
|
||||
"source": "https://github.com/ergebnis/phpstan-rules"
|
||||
},
|
||||
"time": "2025-02-18T11:20:05+00:00"
|
||||
"time": "2025-09-07T13:31:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v4.0.5",
|
||||
"version": "v4.0.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
|
||||
"reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "8.0 - 8.4"
|
||||
"php": "8.0 - 8.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/finder": "<3",
|
||||
"nette/schema": "<1.2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"jetbrains/phpstorm-attributes": "dev-master",
|
||||
"jetbrains/phpstorm-attributes": "^1.2",
|
||||
"nette/tester": "^2.5",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"tracy/tracy": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
|
|
@ -121,6 +122,9 @@
|
|||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
|
|
@ -161,9 +165,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.5"
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.8"
|
||||
},
|
||||
"time": "2024-08-07T15:39:19+00:00"
|
||||
"time": "2025-08-06T21:43:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/extension-installer",
|
||||
|
|
@ -215,16 +219,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.8",
|
||||
"version": "2.1.28",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
|
||||
"reference": "578fa296a166605d97b94091f724f1257185d278"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278",
|
||||
"reference": "578fa296a166605d97b94091f724f1257185d278",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -269,20 +273,20 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-03-09T09:30:48+00:00"
|
||||
"time": "2025-09-19T08:58:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba"
|
||||
"reference": "f9f77efa9de31992a832ff77ea52eb42d675b094"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/8b88b5f818bfa301e0c99154ab622dace071c3ba",
|
||||
"reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094",
|
||||
"reference": "f9f77efa9de31992a832ff77ea52eb42d675b094",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -315,9 +319,9 @@
|
|||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.3"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6"
|
||||
},
|
||||
"time": "2025-01-21T10:52:14+00:00"
|
||||
"time": "2025-07-21T12:19:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tomasvotruba/type-coverage",
|
||||
|
|
|
|||
|
|
@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
|
|||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
|
|
|||
|
|
@ -26,6 +26,12 @@ use Composer\Semver\VersionParser;
|
|||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||
* @internal
|
||||
*/
|
||||
private static $selfDir = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
|
|
@ -322,6 +328,18 @@ class InstalledVersions
|
|||
self::$installedIsLocalDir = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getSelfDir()
|
||||
{
|
||||
if (self::$selfDir === null) {
|
||||
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||
}
|
||||
|
||||
return self::$selfDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
|
||||
|
|
@ -336,7 +354,7 @@ class InstalledVersions
|
|||
$copiedLocalDir = false;
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
$selfDir = strtr(__DIR__, '\\', '/');
|
||||
$selfDir = self::getSelfDir();
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ return array(
|
|||
'Nette\\NotImplementedException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\NotSupportedException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\OutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\ShouldNotHappenException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php',
|
||||
'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php',
|
||||
'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ return array(
|
|||
'TomasVotruba\\TypeCoverage\\' => array($vendorDir . '/tomasvotruba/type-coverage/src'),
|
||||
'PHPStan\\ExtensionInstaller\\' => array($vendorDir . '/phpstan/extension-installer/src'),
|
||||
'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-strict-rules/src'),
|
||||
'Nette\\' => array($vendorDir . '/nette/utils/src'),
|
||||
'Ergebnis\\PHPStan\\Rules\\' => array($vendorDir . '/ergebnis/phpstan-rules/src'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ class ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7
|
|||
'PHPStan\\ExtensionInstaller\\' => 27,
|
||||
'PHPStan\\' => 8,
|
||||
),
|
||||
'N' =>
|
||||
array (
|
||||
'Nette\\' => 6,
|
||||
),
|
||||
'E' =>
|
||||
array (
|
||||
'Ergebnis\\PHPStan\\Rules\\' => 23,
|
||||
|
|
@ -39,6 +43,10 @@ class ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src',
|
||||
),
|
||||
'Nette\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nette/utils/src',
|
||||
),
|
||||
'Ergebnis\\PHPStan\\Rules\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ergebnis/phpstan-rules/src',
|
||||
|
|
@ -63,6 +71,7 @@ class ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7
|
|||
'Nette\\NotImplementedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\NotSupportedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\OutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\ShouldNotHappenException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php',
|
||||
'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php',
|
||||
'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
|
|
|
|||
|
|
@ -2,41 +2,42 @@
|
|||
"packages": [
|
||||
{
|
||||
"name": "ergebnis/phpstan-rules",
|
||||
"version": "2.8.0",
|
||||
"version_normalized": "2.8.0.0",
|
||||
"version": "2.12.0",
|
||||
"version_normalized": "2.12.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ergebnis/phpstan-rules.git",
|
||||
"reference": "30e790621fbad05573ef9cd355279fff5122e080"
|
||||
"reference": "c4e0121a937b3b551f800a86e7d78794da2783ea"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/30e790621fbad05573ef9cd355279fff5122e080",
|
||||
"reference": "30e790621fbad05573ef9cd355279fff5122e080",
|
||||
"url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/c4e0121a937b3b551f800a86e7d78794da2783ea",
|
||||
"reference": "c4e0121a937b3b551f800a86e7d78794da2783ea",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"phpstan/phpstan": "^2.0.0"
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"phpstan/phpstan": "^2.1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^4.0.0 || ^5.0.0",
|
||||
"doctrine/orm": "^2.20.0 || ^3.3.0",
|
||||
"ergebnis/composer-normalize": "^2.45.0",
|
||||
"ergebnis/composer-normalize": "^2.47.0",
|
||||
"ergebnis/license": "^2.6.0",
|
||||
"ergebnis/php-cs-fixer-config": "^6.43.0",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.18.0",
|
||||
"nette/di": "^3.1.10",
|
||||
"ergebnis/php-cs-fixer-config": "^6.54.0",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.20.0",
|
||||
"fakerphp/faker": "^1.24.1",
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0.4",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.3",
|
||||
"phpstan/phpstan-phpunit": "^2.0.7",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.6",
|
||||
"phpunit/phpunit": "^9.6.21",
|
||||
"psr/container": "^2.0.2",
|
||||
"symfony/finder": "^5.4.45",
|
||||
"symfony/process": "^5.4.47"
|
||||
},
|
||||
"time": "2025-02-18T11:20:05+00:00",
|
||||
"time": "2025-09-07T13:31:33+00:00",
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
|
|
@ -77,30 +78,30 @@
|
|||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v4.0.5",
|
||||
"version_normalized": "4.0.5.0",
|
||||
"version": "v4.0.8",
|
||||
"version_normalized": "4.0.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
|
||||
"reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"reference": "c930ca4e3cf4f17dcfb03037703679d2396d2ede",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "8.0 - 8.4"
|
||||
"php": "8.0 - 8.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/finder": "<3",
|
||||
"nette/schema": "<1.2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"jetbrains/phpstorm-attributes": "dev-master",
|
||||
"jetbrains/phpstorm-attributes": "^1.2",
|
||||
"nette/tester": "^2.5",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"tracy/tracy": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
|
|
@ -111,7 +112,7 @@
|
|||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"time": "2024-08-07T15:39:19+00:00",
|
||||
"time": "2025-08-06T21:43:34+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
|
@ -120,6 +121,9 @@
|
|||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
|
|
@ -160,7 +164,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.5"
|
||||
"source": "https://github.com/nette/utils/tree/v4.0.8"
|
||||
},
|
||||
"install-path": "../nette/utils"
|
||||
},
|
||||
|
|
@ -217,17 +221,17 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "2.1.8",
|
||||
"version_normalized": "2.1.8.0",
|
||||
"version": "2.1.28",
|
||||
"version_normalized": "2.1.28.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
|
||||
"reference": "578fa296a166605d97b94091f724f1257185d278"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||
"reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/578fa296a166605d97b94091f724f1257185d278",
|
||||
"reference": "578fa296a166605d97b94091f724f1257185d278",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -236,7 +240,7 @@
|
|||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"time": "2025-03-09T09:30:48+00:00",
|
||||
"time": "2025-09-19T08:58:49+00:00",
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
|
|
@ -278,17 +282,17 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-strict-rules",
|
||||
"version": "2.0.3",
|
||||
"version_normalized": "2.0.3.0",
|
||||
"version": "2.0.6",
|
||||
"version_normalized": "2.0.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
|
||||
"reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba"
|
||||
"reference": "f9f77efa9de31992a832ff77ea52eb42d675b094"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/8b88b5f818bfa301e0c99154ab622dace071c3ba",
|
||||
"reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f9f77efa9de31992a832ff77ea52eb42d675b094",
|
||||
"reference": "f9f77efa9de31992a832ff77ea52eb42d675b094",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -301,7 +305,7 @@
|
|||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"time": "2025-01-21T10:52:14+00:00",
|
||||
"time": "2025-07-21T12:19:29+00:00",
|
||||
"type": "phpstan-extension",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
|
|
@ -323,7 +327,7 @@
|
|||
"description": "Extra strict and opinionated rules for PHPStan",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.3"
|
||||
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.6"
|
||||
},
|
||||
"install-path": "../phpstan/phpstan-strict-rules"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => '__root__',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => null,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '0ef7d66753a1282b23890c2d9a8c775607e8e07f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
|
@ -11,27 +11,27 @@
|
|||
),
|
||||
'versions' => array(
|
||||
'__root__' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => null,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '0ef7d66753a1282b23890c2d9a8c775607e8e07f',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ergebnis/phpstan-rules' => array(
|
||||
'pretty_version' => '2.8.0',
|
||||
'version' => '2.8.0.0',
|
||||
'reference' => '30e790621fbad05573ef9cd355279fff5122e080',
|
||||
'pretty_version' => '2.12.0',
|
||||
'version' => '2.12.0.0',
|
||||
'reference' => 'c4e0121a937b3b551f800a86e7d78794da2783ea',
|
||||
'type' => 'phpstan-extension',
|
||||
'install_path' => __DIR__ . '/../ergebnis/phpstan-rules',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'nette/utils' => array(
|
||||
'pretty_version' => 'v4.0.5',
|
||||
'version' => '4.0.5.0',
|
||||
'reference' => '736c567e257dbe0fcf6ce81b4d6dbe05c6899f96',
|
||||
'pretty_version' => 'v4.0.8',
|
||||
'version' => '4.0.8.0',
|
||||
'reference' => 'c930ca4e3cf4f17dcfb03037703679d2396d2ede',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nette/utils',
|
||||
'aliases' => array(),
|
||||
|
|
@ -47,18 +47,18 @@
|
|||
'dev_requirement' => true,
|
||||
),
|
||||
'phpstan/phpstan' => array(
|
||||
'pretty_version' => '2.1.8',
|
||||
'version' => '2.1.8.0',
|
||||
'reference' => 'f9adff3b87c03b12cc7e46a30a524648e497758f',
|
||||
'pretty_version' => '2.1.28',
|
||||
'version' => '2.1.28.0',
|
||||
'reference' => '578fa296a166605d97b94091f724f1257185d278',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpstan/phpstan',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpstan/phpstan-strict-rules' => array(
|
||||
'pretty_version' => '2.0.3',
|
||||
'version' => '2.0.3.0',
|
||||
'reference' => '8b88b5f818bfa301e0c99154ab622dace071c3ba',
|
||||
'pretty_version' => '2.0.6',
|
||||
'version' => '2.0.6.0',
|
||||
'reference' => 'f9f77efa9de31992a832ff77ea52eb42d675b094',
|
||||
'type' => 'phpstan-extension',
|
||||
'install_path' => __DIR__ . '/../phpstan/phpstan-strict-rules',
|
||||
'aliases' => array(),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,87 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
|
||||
## Unreleased
|
||||
|
||||
For a full diff see [`2.8.0...main`][2.8.0...main].
|
||||
For a full diff see [`2.12.0...main`][2.12.0...main].
|
||||
|
||||
## [`2.12.0`][2.12.0]
|
||||
|
||||
For a full diff see [`2.11.0...2.12.0`][2.11.0...2.12.0].
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for PHP 8.5 ([#977]), by [@localheinz]
|
||||
|
||||
## [`2.11.0`][2.11.0]
|
||||
|
||||
For a full diff see [`2.10.5...2.11.0`][2.10.5...2.11.0].
|
||||
|
||||
### Changed
|
||||
|
||||
- Allowed installation on PHP 8.5 ([#972]), by [@localheinz]
|
||||
|
||||
## [`2.10.5`][2.10.5]
|
||||
|
||||
For a full diff see [`2.10.4...2.10.5`][2.10.4...2.10.5].
|
||||
|
||||
### Fixed
|
||||
|
||||
- Adjusted `Methods\NoNamedArgumentRule` to handle calls to constructors of variable class names ([#957]), by [@localheinz]
|
||||
- Adjusted `Methods\NoNamedArgumentRule` to describe known calls only ([#958]), by [@localheinz]
|
||||
|
||||
## [`2.10.4`][2.10.4]
|
||||
|
||||
For a full diff see [`2.10.3...2.10.4`][2.10.3...2.10.4].
|
||||
|
||||
### Fixed
|
||||
|
||||
- Adjusted `Methods\NoNamedArgumentRule` to handle static calls on variable expressions ([#947]), by [@localheinz]
|
||||
- Adjusted `Methods\NoNamedArgumentRule` to handle calls on invokables ([#948]), by [@localheinz]
|
||||
- Adjusted `Methods\NoNamedArgumentRule` to handle calls on callables assigned to properties ([#949]), by [@localheinz]
|
||||
- Adjusted `Methods\NoNamedArgumentRule` to handle all other calls with generic error message ([#951]), by [@localheinz]
|
||||
|
||||
## [`2.10.3`][2.10.3]
|
||||
|
||||
For a full diff see [`2.10.2...2.10.3`][2.10.2...2.10.3].
|
||||
|
||||
### Fixed
|
||||
|
||||
- Adjusted `Methods\InvokeParentHookMethodRule` to ignore comments ([#944]), by [@localheinz]
|
||||
|
||||
## [`2.10.2`][2.10.2]
|
||||
|
||||
For a full diff see [`2.10.1...2.10.2`][2.10.1...2.10.2].
|
||||
|
||||
### Fixed
|
||||
|
||||
- Renamed error identifier for `Methods\InvokeParentHookMethodRule` ([#943]), by [@localheinz]
|
||||
|
||||
## [`2.10.1`][2.10.1]
|
||||
|
||||
For a full diff see [`2.10.0...2.10.1`][2.10.0...2.10.1].
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed schema for configuration of `Methods\InvokeParentHookMethodRule` ([#940]), by [@localheinz]
|
||||
|
||||
## [`2.10.0`][2.10.0]
|
||||
|
||||
For a full diff see [`2.9.0...2.10.0`][2.9.0...2.10.0].
|
||||
|
||||
### Added
|
||||
|
||||
- Added `Methods\InvokeParentHookMethodRule`, which reports an error when a hook method that overrides a hook method in a parent class does not invoke the overridden hook method in the expected order ([#939]), by [@localheinz]
|
||||
|
||||
## [`2.9.0`][2.9.0]
|
||||
|
||||
For a full diff see [`2.8.0...2.9.0`][2.8.0...2.9.0].
|
||||
|
||||
### Added
|
||||
|
||||
- Added `CallLikes\NoNamedArgumentRule`, which reports an error when an anonymous function, a function, or a method is invoked using a named argument ([#914]), by [@localheinz]
|
||||
|
||||
### Changed
|
||||
|
||||
- Required `phpstan/phpstan:^2.1.8` ([#938]), by [@localheinz]
|
||||
|
||||
## [`2.8.0`][2.8.0]
|
||||
|
||||
|
|
@ -14,7 +94,7 @@ For a full diff see [`2.7.0...2.8.0`][2.7.0...2.8.0].
|
|||
|
||||
### Added
|
||||
|
||||
- Added `allRules` parameter to allow disabling and enabling all rules ([#913]), by [@localheinz]
|
||||
- Added `allRules` parameter to allow disabling and enabling all rules ([#913]), by [@localheinz]
|
||||
- Added `Expressions\NoAssignByReferenceRule`, which reports an error when a variable is assigned by reference ([#914]), by [@localheinz]
|
||||
|
||||
## [`2.7.0`][2.7.0]
|
||||
|
|
@ -499,6 +579,15 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
|
|||
[2.6.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.6.1
|
||||
[2.7.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.7.0
|
||||
[2.8.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.8.0
|
||||
[2.9.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.9.0
|
||||
[2.10.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.0
|
||||
[2.10.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.1
|
||||
[2.10.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.2
|
||||
[2.10.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.3
|
||||
[2.10.4]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.4
|
||||
[2.10.5]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.10.5
|
||||
[2.11.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.11.0
|
||||
[2.12.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.12.0
|
||||
|
||||
[362c7ea...0.1.0]: https://github.com/ergebnis/phpstan-rules/compare/362c7ea...0.1.0
|
||||
[0.1.0...0.2.0]: https://github.com/ergebnis/phpstan-rules/compare/0.1.0...0.2.0
|
||||
|
|
@ -540,7 +629,16 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
|
|||
[2.6.0...2.6.1]: https://github.com/ergebnis/phpstan-rules/compare/2.6.0...2.6.1
|
||||
[2.6.1...2.7.0]: https://github.com/ergebnis/phpstan-rules/compare/2.6.1...2.7.0
|
||||
[2.7.0...2.8.0]: https://github.com/ergebnis/phpstan-rules/compare/2.7.0...2.8.0
|
||||
[2.8.0...main]: https://github.com/ergebnis/phpstan-rules/compare/2.8.0...main
|
||||
[2.8.0...2.9.0]: https://github.com/ergebnis/phpstan-rules/compare/2.8.0...2.9.0
|
||||
[2.9.0...2.10.0]: https://github.com/ergebnis/phpstan-rules/compare/2.9.0...2.10.0
|
||||
[2.10.0...2.10.1]: https://github.com/ergebnis/phpstan-rules/compare/2.10.0...2.10.1
|
||||
[2.10.1...2.10.2]: https://github.com/ergebnis/phpstan-rules/compare/2.10.1...2.10.2
|
||||
[2.10.2...2.10.3]: https://github.com/ergebnis/phpstan-rules/compare/2.10.2...2.10.3
|
||||
[2.10.3...2.10.4]: https://github.com/ergebnis/phpstan-rules/compare/2.10.3...2.10.4
|
||||
[2.10.4...2.10.5]: https://github.com/ergebnis/phpstan-rules/compare/2.10.4...2.10.5
|
||||
[2.10.5...2.11.0]: https://github.com/ergebnis/phpstan-rules/compare/2.10.5...2.11.0
|
||||
[2.11.0...2.12.0]: https://github.com/ergebnis/phpstan-rules/compare/2.11.0...2.12.0
|
||||
[2.12.0...main]: https://github.com/ergebnis/phpstan-rules/compare/2.12.0...main
|
||||
|
||||
[#1]: https://github.com/ergebnis/phpstan-rules/pull/1
|
||||
[#4]: https://github.com/ergebnis/phpstan-rules/pull/4
|
||||
|
|
@ -625,6 +723,19 @@ For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
|
|||
[#912]: https://github.com/ergebnis/phpstan-rules/pull/912
|
||||
[#913]: https://github.com/ergebnis/phpstan-rules/pull/913
|
||||
[#914]: https://github.com/ergebnis/phpstan-rules/pull/914
|
||||
[#938]: https://github.com/ergebnis/phpstan-rules/pull/938
|
||||
[#939]: https://github.com/ergebnis/phpstan-rules/pull/939
|
||||
[#940]: https://github.com/ergebnis/phpstan-rules/pull/940
|
||||
[#943]: https://github.com/ergebnis/phpstan-rules/pull/943
|
||||
[#944]: https://github.com/ergebnis/phpstan-rules/pull/944
|
||||
[#947]: https://github.com/ergebnis/phpstan-rules/pull/947
|
||||
[#948]: https://github.com/ergebnis/phpstan-rules/pull/948
|
||||
[#949]: https://github.com/ergebnis/phpstan-rules/pull/949
|
||||
[#951]: https://github.com/ergebnis/phpstan-rules/pull/951
|
||||
[#957]: https://github.com/ergebnis/phpstan-rules/pull/957
|
||||
[#958]: https://github.com/ergebnis/phpstan-rules/pull/958
|
||||
[#972]: https://github.com/ergebnis/phpstan-rules/pull/972
|
||||
[#977]: https://github.com/ergebnis/phpstan-rules/pull/977
|
||||
|
||||
[@cosmastech]: https://github.com/cosmastech
|
||||
[@enumag]: https://github.com/enumag
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ includes:
|
|||
|
||||
This package provides the following rules for use with [`phpstan/phpstan`](https://github.com/phpstan/phpstan):
|
||||
|
||||
- [`Ergebnis\PHPStan\Rules\CallLikes\NoNamedArgumentRule`](https://github.com/ergebnis/phpstan-rules#calllikesnonamedargumentrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Classes\FinalRule`](https://github.com/ergebnis/phpstan-rules#classesfinalrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Classes\NoExtendsRule`](https://github.com/ergebnis/phpstan-rules#classesnoextendsrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule`](https://github.com/ergebnis/phpstan-rules#classesphpunitframeworktestcasewithsuffixrule)
|
||||
|
|
@ -63,6 +64,7 @@ This package provides the following rules for use with [`phpstan/phpstan`](https
|
|||
- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnulldefaultvaluerule)
|
||||
- [`Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoreturnbyreferencerule)
|
||||
- [`Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule`](https://github.com/ergebnis/phpstan-rules#methodsfinalinabstractclassrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Methods\InvokeParentHookMethodRule`](https://github.com/ergebnis/phpstan-rules#methodsinvokeparenthookmethodrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoconstructorparameterwithdefaultvaluerule)
|
||||
- [`Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnonullablereturntypedeclarationrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterpassedbyreferencerule)
|
||||
|
|
@ -73,6 +75,24 @@ This package provides the following rules for use with [`phpstan/phpstan`](https
|
|||
- [`Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule`](https://github.com/ergebnis/phpstan-rules#methodsprivateinfinalclassrule)
|
||||
- [`Ergebnis\PHPStan\Rules\Statements\NoSwitchRule`](https://github.com/ergebnis/phpstan-rules#statementsnoswitchrule)
|
||||
|
||||
|
||||
### CallLikes
|
||||
|
||||
#### `CallLikes\NoNamedArgumentRule`
|
||||
|
||||
This rule reports an error when an anonymous function, a function, or a method is invoked using a [named argument](https://www.php.net/manual/en/functions.arguments.php#functions.named-arguments).
|
||||
|
||||
##### Disabling the rule
|
||||
|
||||
You can set the `enabled` parameter to `false` to disable this rule.
|
||||
|
||||
```neon
|
||||
parameters:
|
||||
ergebnis:
|
||||
noNamedArgument:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
### Classes
|
||||
|
||||
#### `Classes\FinalRule`
|
||||
|
|
@ -131,7 +151,7 @@ This rule reports an error when a class extends another class.
|
|||
|
||||
By default, this rule allows the following classes to be extended:
|
||||
|
||||
- [`PHPUnit\Framework\TestCase`](https://github.com/sebastianbergmann/phpunit/blob/7.5.2/src/Framework/TestCase.php)
|
||||
- [`PHPUnit\Framework\TestCase`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php)
|
||||
|
||||
##### Disabling the rule
|
||||
|
||||
|
|
@ -266,21 +286,6 @@ parameters:
|
|||
enabled: false
|
||||
```
|
||||
|
||||
#### `Expressions\NoEvalRule`
|
||||
|
||||
This rule reports an error when the language construct [`eval()`](https://www.php.net/eval) is used.
|
||||
|
||||
##### Disabling the rule
|
||||
|
||||
You can set the `enabled` parameter to `false` to disable this rule.
|
||||
|
||||
```neon
|
||||
parameters:
|
||||
ergebnis:
|
||||
noEval:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
#### `Expressions\NoErrorSuppressionRule`
|
||||
|
||||
This rule reports an error when [`@`](https://www.php.net/manual/en/language.operators.errorcontrol.php) is used to suppress errors.
|
||||
|
|
@ -296,6 +301,21 @@ parameters:
|
|||
enabled: false
|
||||
```
|
||||
|
||||
#### `Expressions\NoEvalRule`
|
||||
|
||||
This rule reports an error when the language construct [`eval()`](https://www.php.net/eval) is used.
|
||||
|
||||
##### Disabling the rule
|
||||
|
||||
You can set the `enabled` parameter to `false` to disable this rule.
|
||||
|
||||
```neon
|
||||
parameters:
|
||||
ergebnis:
|
||||
noEval:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
#### `Expressions\NoIssetRule`
|
||||
|
||||
This rule reports an error when the language construct [`isset()`](https://www.php.net/isset) is used.
|
||||
|
|
@ -409,7 +429,7 @@ parameters:
|
|||
|
||||
#### `Methods\FinalInAbstractClassRule`
|
||||
|
||||
This rule reports an error when a concrete `public` or `protected `method in an `abstract` class is not `final`.
|
||||
This rule reports an error when a concrete `public` or `protected` method in an `abstract` class is not `final`.
|
||||
|
||||
:bulb: This rule ignores
|
||||
|
||||
|
|
@ -427,6 +447,63 @@ parameters:
|
|||
enabled: false
|
||||
```
|
||||
|
||||
#### `Methods\InvokeParentHookMethodRule`
|
||||
|
||||
This rule reports an error when a hook method that overrides a hook method in a parent class does not invoke the overridden hook method in the expected order.
|
||||
|
||||
##### Defaults
|
||||
|
||||
By default, this rule requires the following hook methods to be invoked before doing something in the overriding method:
|
||||
|
||||
- [`Codeception\PHPUnit\TestCase::_setUp()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L11-L13)
|
||||
- [`Codeception\PHPUnit\TestCase::_setUpBeforeClass()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L25-L27)
|
||||
- [`Codeception\Test\Unit::_before()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L63-L65)
|
||||
- [`Codeception\Test\Unit::_setUp()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L34-L58)
|
||||
- [`PHPUnit\Framework\TestCase::assertPreConditions()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2073-L2075)
|
||||
- [`PHPUnit\Framework\TestCase::setUp()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2063-L2065)
|
||||
- [`PHPUnit\Framework\TestCase::setUpBeforeClass()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2055-L2057)
|
||||
|
||||
By default, this rule requires the following hook methods to be invoked after doing something in the overriding method:
|
||||
|
||||
- [`Codeception\PHPUnit\TestCase::_tearDown()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L18-L20)
|
||||
- [`Codeception\PHPUnit\TestCase::_tearDownAfterClass()`](https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L32-L34)
|
||||
- [`Codeception\Test\Unit::_after()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L75-L77)
|
||||
- [`Codeception\Test\Unit::_tearDown()`](https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L67-L70)
|
||||
- [`PHPUnit\Framework\TestCase::assertPostConditions()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2083-L2085)
|
||||
- [`PHPUnit\Framework\TestCase::tearDown()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2091-L2093)
|
||||
- [`PHPUnit\Framework\TestCase::tearDownAfterClass()`](https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2098-L2100)
|
||||
|
||||
##### Disabling the rule
|
||||
|
||||
You can set the `enabled` parameter to `false` to disable this rule.
|
||||
|
||||
```neon
|
||||
parameters:
|
||||
ergebnis:
|
||||
invokeParentHookMethod:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
##### Configuring methods to invoke the parent method in the right order:
|
||||
|
||||
You can set the `hookMethods` parameter to a list of hook methods:
|
||||
|
||||
```neon
|
||||
parameters:
|
||||
ergebnis:
|
||||
invokeParentHookMethod:
|
||||
hookMethods:
|
||||
- className: "Example\Test\Functional\AbstractCest"
|
||||
methodName: "_before"
|
||||
hasContent: "yes"
|
||||
invocation: "first"
|
||||
```
|
||||
|
||||
- `className`: name of the class that declares the hook method
|
||||
- `methodName`: name of the hook method
|
||||
- `hasContent`: one of `"yes"`, `"no"`, `"maybe"`
|
||||
- `invocation`: one of `"any"` (needs to be invoked), `"first"` (needs to be invoked before all other statements in the overriding hook method, `"last"` (needs to be invoked after all other statements in the overriding hook method)
|
||||
|
||||
#### `Methods\NoConstructorParameterWithDefaultValueRule`
|
||||
|
||||
This rule reports an error when a constructor declared in
|
||||
|
|
@ -658,7 +735,7 @@ The maintainers of this project ask contributors to follow the [code of conduct]
|
|||
|
||||
The maintainers of this project provide limited support.
|
||||
|
||||
You can support the maintenance of this project by [sponsoring @localheinz](https://github.com/sponsors/localheinz) or [requesting an invoice for services related to this project](mailto:am@localheinz.com?subject=ergebnis/phpstan-rules:%20Requesting%20invoice%20for%20services).
|
||||
You can support the maintenance of this project by [sponsoring @ergebnis](https://github.com/sponsors/ergebnis).
|
||||
|
||||
## PHP Version Support Policy
|
||||
|
||||
|
|
|
|||
|
|
@ -21,21 +21,22 @@
|
|||
"security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md"
|
||||
},
|
||||
"require": {
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
|
||||
"php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||
"ext-mbstring": "*",
|
||||
"phpstan/phpstan": "^2.0.0"
|
||||
"phpstan/phpstan": "^2.1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "^4.0.0 || ^5.0.0",
|
||||
"doctrine/orm": "^2.20.0 || ^3.3.0",
|
||||
"ergebnis/composer-normalize": "^2.45.0",
|
||||
"ergebnis/composer-normalize": "^2.47.0",
|
||||
"ergebnis/license": "^2.6.0",
|
||||
"ergebnis/php-cs-fixer-config": "^6.43.0",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.18.0",
|
||||
"nette/di": "^3.1.10",
|
||||
"ergebnis/php-cs-fixer-config": "^6.54.0",
|
||||
"ergebnis/phpunit-slow-test-detector": "^2.20.0",
|
||||
"fakerphp/faker": "^1.24.1",
|
||||
"phpstan/extension-installer": "^1.4.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.1",
|
||||
"phpstan/phpstan-phpunit": "^2.0.4",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.3",
|
||||
"phpstan/phpstan-deprecation-rules": "^2.0.3",
|
||||
"phpstan/phpstan-phpunit": "^2.0.7",
|
||||
"phpstan/phpstan-strict-rules": "^2.0.6",
|
||||
"phpunit/phpunit": "^9.6.21",
|
||||
"psr/container": "^2.0.2",
|
||||
"symfony/finder": "^5.4.45",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
conditionalTags:
|
||||
Ergebnis\PHPStan\Rules\CallLikes\NoNamedArgumentRule:
|
||||
phpstan.rules.rule: %ergebnis.noNamedArgument.enabled%
|
||||
Ergebnis\PHPStan\Rules\Classes\FinalRule:
|
||||
phpstan.rules.rule: %ergebnis.final.enabled%
|
||||
Ergebnis\PHPStan\Rules\Classes\NoExtendsRule:
|
||||
|
|
@ -35,6 +37,8 @@ conditionalTags:
|
|||
phpstan.rules.rule: %ergebnis.noReturnByReference.enabled%
|
||||
Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule:
|
||||
phpstan.rules.rule: %ergebnis.finalInAbstractClass.enabled%
|
||||
Ergebnis\PHPStan\Rules\Methods\InvokeParentHookMethodRule:
|
||||
phpstan.rules.rule: %ergebnis.invokeParentHookMethod.enabled%
|
||||
Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule:
|
||||
phpstan.rules.rule: %ergebnis.noConstructorParameterWithDefaultValue.enabled%
|
||||
Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule:
|
||||
|
|
@ -65,6 +69,9 @@ parameters:
|
|||
enabled: %ergebnis.allRules%
|
||||
finalInAbstractClass:
|
||||
enabled: %ergebnis.allRules%
|
||||
invokeParentHookMethod:
|
||||
enabled: %ergebnis.allRules%
|
||||
hookMethods: []
|
||||
noAssignByReference:
|
||||
enabled: %ergebnis.allRules%
|
||||
noCompact:
|
||||
|
|
@ -80,6 +87,8 @@ parameters:
|
|||
enabled: %ergebnis.allRules%
|
||||
noIsset:
|
||||
enabled: %ergebnis.allRules%
|
||||
noNamedArgument:
|
||||
enabled: %ergebnis.allRules%
|
||||
noNullableReturnTypeDeclaration:
|
||||
enabled: %ergebnis.allRules%
|
||||
noParameterPassedByReference:
|
||||
|
|
@ -116,6 +125,15 @@ parametersSchema:
|
|||
finalInAbstractClass: structure([
|
||||
enabled: bool(),
|
||||
])
|
||||
invokeParentHookMethod: structure([
|
||||
enabled: bool(),
|
||||
hookMethods: listOf(structure([
|
||||
className: string(),
|
||||
hasContent: anyOf("no", "yes"),
|
||||
invocation: anyOf("any", "first", "last"),
|
||||
methodName: string(),
|
||||
]))
|
||||
])
|
||||
noAssignByReference: structure([
|
||||
enabled: bool(),
|
||||
])
|
||||
|
|
@ -138,6 +156,9 @@ parametersSchema:
|
|||
noIsset: structure([
|
||||
enabled: bool(),
|
||||
])
|
||||
noNamedArgument: structure([
|
||||
enabled: bool(),
|
||||
])
|
||||
noNullableReturnTypeDeclaration: structure([
|
||||
enabled: bool(),
|
||||
])
|
||||
|
|
@ -173,6 +194,9 @@ services:
|
|||
-
|
||||
class: Ergebnis\PHPStan\Rules\Analyzer
|
||||
|
||||
-
|
||||
class: Ergebnis\PHPStan\Rules\CallLikes\NoNamedArgumentRule
|
||||
|
||||
-
|
||||
class: Ergebnis\PHPStan\Rules\Classes\FinalRule
|
||||
arguments:
|
||||
|
|
@ -232,6 +256,11 @@ services:
|
|||
-
|
||||
class: Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule
|
||||
|
||||
-
|
||||
class: Ergebnis\PHPStan\Rules\Methods\InvokeParentHookMethodRule
|
||||
arguments:
|
||||
hookMethods: %ergebnis.invokeParentHookMethod.hookMethods%
|
||||
|
||||
-
|
||||
class: Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule
|
||||
|
||||
|
|
|
|||
175
tools/.phpstan/vendor/ergebnis/phpstan-rules/src/CallLikes/NoNamedArgumentRule.php
vendored
Normal file
175
tools/.phpstan/vendor/ergebnis/phpstan-rules/src/CallLikes/NoNamedArgumentRule.php
vendored
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules\CallLikes;
|
||||
|
||||
use Ergebnis\PHPStan\Rules\ErrorIdentifier;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser;
|
||||
use PHPStan\Rules;
|
||||
use PHPStan\ShouldNotHappenException;
|
||||
|
||||
/**
|
||||
* @implements Rules\Rule<Node\Expr\CallLike>
|
||||
*/
|
||||
final class NoNamedArgumentRule implements Rules\Rule
|
||||
{
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Node\Expr\CallLike::class;
|
||||
}
|
||||
|
||||
public function processNode(
|
||||
Node $node,
|
||||
Analyser\Scope $scope
|
||||
): array {
|
||||
if (0 === \count($node->getArgs())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
/** @var list<Node\Arg> $namedArguments */
|
||||
$namedArguments = \array_values(\array_filter($node->getArgs(), static function (Node\Arg $argument): bool {
|
||||
if (!$argument->name instanceof Node\Identifier) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}));
|
||||
|
||||
if (0 === \count($namedArguments)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$callLike = self::describeCallLike(
|
||||
$node,
|
||||
$scope,
|
||||
);
|
||||
|
||||
return \array_map(static function (Node\Arg $namedArgument) use ($callLike): Rules\RuleError {
|
||||
/** @var Node\Identifier $argumentName */
|
||||
$argumentName = $namedArgument->name;
|
||||
|
||||
$message = \sprintf(
|
||||
'%s is invoked with named argument for parameter $%s.',
|
||||
$callLike,
|
||||
$argumentName->toString(),
|
||||
);
|
||||
|
||||
return Rules\RuleErrorBuilder::message($message)
|
||||
->identifier(ErrorIdentifier::noNamedArgument()->toString())
|
||||
->build();
|
||||
}, $namedArguments);
|
||||
}
|
||||
|
||||
private static function describeCallLike(
|
||||
Node\Expr\CallLike $node,
|
||||
Analyser\Scope $scope
|
||||
): string {
|
||||
if ($node instanceof Node\Expr\FuncCall) {
|
||||
$functionName = $node->name;
|
||||
|
||||
if ($functionName instanceof Node\Expr\PropertyFetch) {
|
||||
return \sprintf(
|
||||
'Callable referenced by property $%s',
|
||||
$functionName->name,
|
||||
);
|
||||
}
|
||||
|
||||
if ($functionName instanceof Node\Expr\Variable) {
|
||||
return \sprintf(
|
||||
'Callable referenced by $%s',
|
||||
$functionName->name,
|
||||
);
|
||||
}
|
||||
|
||||
if ($functionName instanceof Node\Name) {
|
||||
return \sprintf(
|
||||
'Function %s()',
|
||||
$functionName->name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\MethodCall) {
|
||||
$methodName = $node->name;
|
||||
|
||||
if ($methodName instanceof Node\Identifier) {
|
||||
$objectType = $scope->getType($node->var);
|
||||
|
||||
$methodReflection = $scope->getMethodReflection(
|
||||
$objectType,
|
||||
$methodName->name,
|
||||
);
|
||||
|
||||
if (null === $methodReflection) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$declaringClass = $methodReflection->getDeclaringClass();
|
||||
|
||||
if ($declaringClass->isAnonymous()) {
|
||||
return \sprintf(
|
||||
'Method %s() of anonymous class',
|
||||
$methodName->toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return \sprintf(
|
||||
'Method %s::%s()',
|
||||
$declaringClass->getName(),
|
||||
$methodName->toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return 'Method';
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\StaticCall) {
|
||||
$methodName = $node->name;
|
||||
|
||||
if ($methodName instanceof Node\Identifier) {
|
||||
$className = $node->class;
|
||||
|
||||
if ($className instanceof Node\Name) {
|
||||
return \sprintf(
|
||||
'Method %s::%s()',
|
||||
$className->toString(),
|
||||
$methodName->toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return \sprintf(
|
||||
'Method %s()',
|
||||
$methodName->toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return 'Method';
|
||||
}
|
||||
|
||||
if ($node instanceof Node\Expr\New_) {
|
||||
$className = $node->class;
|
||||
|
||||
if ($className instanceof Node\Name) {
|
||||
return \sprintf(
|
||||
'Constructor of %s',
|
||||
$className->toString(),
|
||||
);
|
||||
}
|
||||
|
||||
return 'Constructor';
|
||||
}
|
||||
|
||||
return 'Callable';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ClassName
|
||||
{
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* @param class-string $value
|
||||
*/
|
||||
private function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param class-string $value
|
||||
*/
|
||||
public static function fromString(string $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
return $this->value === $other->value;
|
||||
}
|
||||
}
|
||||
|
|
@ -93,11 +93,11 @@ final class FinalRule implements Rules\Rule
|
|||
return [];
|
||||
}
|
||||
|
||||
if ($this->hasWhitelistedAnnotation($node)) {
|
||||
if (self::hasWhitelistedAnnotation($node)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($this->hasWhitelistedAttribute($node)) {
|
||||
if (self::hasWhitelistedAttribute($node)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +124,7 @@ final class FinalRule implements Rules\Rule
|
|||
* @see https://github.com/SpacePossum
|
||||
* @see https://github.com/Slamdunk
|
||||
*/
|
||||
private function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool
|
||||
private static function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool
|
||||
{
|
||||
$docComment = $node->getDocComment();
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ final class FinalRule implements Rules\Rule
|
|||
return false;
|
||||
}
|
||||
|
||||
private function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool
|
||||
private static function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool
|
||||
{
|
||||
foreach ($node->attrGroups as $attributeGroup) {
|
||||
foreach ($attributeGroup->attrs as $attribute) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ use Ergebnis\PHPStan\Rules\ErrorIdentifier;
|
|||
use PhpParser\Node;
|
||||
use PHPStan\Analyser;
|
||||
use PHPStan\Rules;
|
||||
use PHPUnit\Framework;
|
||||
|
||||
/**
|
||||
* @implements Rules\Rule<Node\Stmt\Class_>
|
||||
|
|
@ -27,7 +28,7 @@ final class NoExtendsRule implements Rules\Rule
|
|||
* @var list<class-string>
|
||||
*/
|
||||
private static array $defaultClassesAllowedToBeExtended = [
|
||||
'PHPUnit\\Framework\\TestCase',
|
||||
Framework\TestCase::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ final class TestCaseWithSuffixRule implements Rules\Rule
|
|||
$extendedPhpunitTestCaseClassName = '';
|
||||
|
||||
foreach (self::$phpunitTestCaseClassNames as $phpunitTestCaseClassName) {
|
||||
if ($classReflection->isSubclassOf($phpunitTestCaseClassName)) {
|
||||
if ($classReflection->isSubclassOfClass($this->reflectionProvider->getClass($phpunitTestCaseClassName))) {
|
||||
$extendedPhpunitTestCaseClassName = $phpunitTestCaseClassName;
|
||||
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ declare(strict_types=1);
|
|||
namespace Ergebnis\PHPStan\Rules;
|
||||
|
||||
/**
|
||||
* @internale
|
||||
* @internal
|
||||
*/
|
||||
final class ErrorIdentifier
|
||||
{
|
||||
|
|
@ -40,6 +40,11 @@ final class ErrorIdentifier
|
|||
return new self('finalInAbstractClass');
|
||||
}
|
||||
|
||||
public static function invokeParentHookMethod(): self
|
||||
{
|
||||
return new self('invokeParentHookMethod');
|
||||
}
|
||||
|
||||
public static function noCompact(): self
|
||||
{
|
||||
return new self('noCompact');
|
||||
|
|
@ -75,6 +80,11 @@ final class ErrorIdentifier
|
|||
return new self('noIsset');
|
||||
}
|
||||
|
||||
public static function noNamedArgument(): self
|
||||
{
|
||||
return new self('noNamedArgument');
|
||||
}
|
||||
|
||||
public static function noParameterPassedByReference(): self
|
||||
{
|
||||
return new self('noParameterPassedByReference');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class HasContent
|
||||
{
|
||||
private string $value;
|
||||
|
||||
private function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function fromString(string $value): self
|
||||
{
|
||||
$values = [
|
||||
'maybe',
|
||||
'no',
|
||||
'yes',
|
||||
];
|
||||
|
||||
if (!\in_array($value, $values, true)) {
|
||||
throw new \InvalidArgumentException(\sprintf(
|
||||
'Value needs to be one of "%s", got "%s" instead.',
|
||||
\implode('", "', $values),
|
||||
$value,
|
||||
));
|
||||
}
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public static function maybe(): self
|
||||
{
|
||||
return new self('maybe');
|
||||
}
|
||||
|
||||
public static function no(): self
|
||||
{
|
||||
return new self('no');
|
||||
}
|
||||
|
||||
public static function yes(): self
|
||||
{
|
||||
return new self('yes');
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
return $this->value === $other->value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class HookMethod
|
||||
{
|
||||
private ClassName $className;
|
||||
private MethodName $methodName;
|
||||
private Invocation $invocation;
|
||||
private HasContent $hasContent;
|
||||
|
||||
private function __construct(
|
||||
ClassName $className,
|
||||
MethodName $methodName,
|
||||
Invocation $invocation,
|
||||
HasContent $hasContent
|
||||
) {
|
||||
$this->className = $className;
|
||||
$this->methodName = $methodName;
|
||||
$this->invocation = $invocation;
|
||||
$this->hasContent = $hasContent;
|
||||
}
|
||||
|
||||
public static function create(
|
||||
ClassName $className,
|
||||
MethodName $methodName,
|
||||
Invocation $invocation,
|
||||
HasContent $hasContent
|
||||
): self {
|
||||
return new self(
|
||||
$className,
|
||||
$methodName,
|
||||
$invocation,
|
||||
$hasContent,
|
||||
);
|
||||
}
|
||||
|
||||
public function className(): ClassName
|
||||
{
|
||||
return $this->className;
|
||||
}
|
||||
|
||||
public function methodName(): MethodName
|
||||
{
|
||||
return $this->methodName;
|
||||
}
|
||||
|
||||
public function invocation(): Invocation
|
||||
{
|
||||
return $this->invocation;
|
||||
}
|
||||
|
||||
public function hasContent(): HasContent
|
||||
{
|
||||
return $this->hasContent;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class Invocation
|
||||
{
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*/
|
||||
private function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public static function fromString(string $value): self
|
||||
{
|
||||
$values = [
|
||||
'any',
|
||||
'first',
|
||||
'last',
|
||||
'never',
|
||||
];
|
||||
|
||||
if (!\in_array($value, $values, true)) {
|
||||
throw new \InvalidArgumentException(\sprintf(
|
||||
'Value needs to be one of "%s", got "%s" instead.',
|
||||
\implode('", "', $values),
|
||||
$value,
|
||||
));
|
||||
}
|
||||
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
public static function any(): self
|
||||
{
|
||||
return new self('any');
|
||||
}
|
||||
|
||||
public static function first(): self
|
||||
{
|
||||
return new self('first');
|
||||
}
|
||||
|
||||
public static function last(): self
|
||||
{
|
||||
return new self('last');
|
||||
}
|
||||
|
||||
public static function never(): self
|
||||
{
|
||||
return new self('never');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
return $this->value === $other->value;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class MethodName
|
||||
{
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*/
|
||||
private function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $value
|
||||
*/
|
||||
public static function fromString(string $value): self
|
||||
{
|
||||
return new self($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function toString(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function equals(self $other): bool
|
||||
{
|
||||
return $this->value === $other->value;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Ergebnis\PHPStan\Rules\Methods;
|
||||
|
||||
use Doctrine\ORM;
|
||||
use Ergebnis\PHPStan\Rules\ErrorIdentifier;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser;
|
||||
|
|
@ -27,10 +28,10 @@ use PHPStan\Rules;
|
|||
final class FinalInAbstractClassRule implements Rules\Rule
|
||||
{
|
||||
private const DOCTRINE_ATTRIBUTE_NAMES = [
|
||||
'Doctrine\\ORM\\Mapping\\Embeddable',
|
||||
'Doctrine\\ORM\\Mapping\\Entity',
|
||||
ORM\Mapping\Embeddable::class,
|
||||
ORM\Mapping\Entity::class,
|
||||
];
|
||||
private const DOCTRINE_TAG_NAMES = [
|
||||
private const DOCTRINE_ANNOTATION_NAMES = [
|
||||
'@ORM\\Mapping\\Embeddable',
|
||||
'@ORM\\Embeddable',
|
||||
'@Embeddable',
|
||||
|
|
@ -111,7 +112,7 @@ final class FinalInAbstractClassRule implements Rules\Rule
|
|||
continue;
|
||||
}
|
||||
|
||||
if (\in_array($child->name, self::DOCTRINE_TAG_NAMES, true)) {
|
||||
if (\in_array($child->name, self::DOCTRINE_ANNOTATION_NAMES, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
436
tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/InvokeParentHookMethodRule.php
vendored
Normal file
436
tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/InvokeParentHookMethodRule.php
vendored
Normal file
|
|
@ -0,0 +1,436 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Copyright (c) 2018-2025 Andreas Möller
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE.md file that was distributed with this source code.
|
||||
*
|
||||
* @see https://github.com/ergebnis/phpstan-rules
|
||||
*/
|
||||
|
||||
namespace Ergebnis\PHPStan\Rules\Methods;
|
||||
|
||||
use Codeception\PHPUnit;
|
||||
use Codeception\Test;
|
||||
use Ergebnis\PHPStan\Rules\ClassName;
|
||||
use Ergebnis\PHPStan\Rules\ErrorIdentifier;
|
||||
use Ergebnis\PHPStan\Rules\HasContent;
|
||||
use Ergebnis\PHPStan\Rules\HookMethod;
|
||||
use Ergebnis\PHPStan\Rules\Invocation;
|
||||
use Ergebnis\PHPStan\Rules\MethodName;
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser;
|
||||
use PHPStan\Reflection;
|
||||
use PHPStan\Rules;
|
||||
use PHPStan\ShouldNotHappenException;
|
||||
use PHPUnit\Framework;
|
||||
|
||||
/**
|
||||
* @implements Rules\Rule<Node\Stmt\ClassMethod>
|
||||
*/
|
||||
final class InvokeParentHookMethodRule implements Rules\Rule
|
||||
{
|
||||
private Reflection\ReflectionProvider $reflectionProvider;
|
||||
|
||||
/**
|
||||
* @var list<HookMethod>
|
||||
*/
|
||||
private array $hookMethods;
|
||||
|
||||
/**
|
||||
* @param array<class-string, array<string, string>> $hookMethods
|
||||
*/
|
||||
public function __construct(
|
||||
Reflection\ReflectionProvider $reflectionProvider,
|
||||
array $hookMethods = []
|
||||
) {
|
||||
$this->reflectionProvider = $reflectionProvider;
|
||||
$this->hookMethods = self::sort(
|
||||
$reflectionProvider,
|
||||
...self::filter(
|
||||
$reflectionProvider,
|
||||
...\array_merge(
|
||||
self::defaultHookMethods(),
|
||||
\array_map(static function (array $hookMethod): HookMethod {
|
||||
return HookMethod::create(
|
||||
ClassName::fromString($hookMethod['className']),
|
||||
MethodName::fromString($hookMethod['methodName']),
|
||||
Invocation::fromString($hookMethod['invocation']),
|
||||
HasContent::fromString($hookMethod['hasContent']),
|
||||
);
|
||||
}, $hookMethods),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Node\Stmt\ClassMethod::class;
|
||||
}
|
||||
|
||||
public function processNode(
|
||||
Node $node,
|
||||
Analyser\Scope $scope
|
||||
): array {
|
||||
$classReflection = $scope->getClassReflection();
|
||||
|
||||
if (null === $classReflection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$parentClassReflection = $classReflection->getParentClass();
|
||||
|
||||
if (null === $parentClassReflection) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$methodName = $node->name->toString();
|
||||
|
||||
$hookMethod = $this->findMatchingHookMethod(
|
||||
$scope,
|
||||
$parentClassReflection,
|
||||
$classReflection,
|
||||
$methodName,
|
||||
);
|
||||
|
||||
if (!$hookMethod instanceof HookMethod) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$statements = $node->getStmts();
|
||||
|
||||
if (!\is_array($statements)) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$parentHookMethodInvocation = self::findParentHookMethodInvocation(
|
||||
\array_values($statements),
|
||||
$hookMethod,
|
||||
);
|
||||
|
||||
if ($parentHookMethodInvocation->equals(Invocation::never())) {
|
||||
if ($hookMethod->hasContent()->equals(HasContent::no())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$message = \sprintf(
|
||||
'Method %s::%s() does not invoke parent::%s().',
|
||||
$classReflection->getName(),
|
||||
$methodName,
|
||||
$hookMethod->methodName()->toString(),
|
||||
);
|
||||
|
||||
return [
|
||||
Rules\RuleErrorBuilder::message($message)
|
||||
->identifier(ErrorIdentifier::invokeParentHookMethod()->toString())
|
||||
->build(),
|
||||
];
|
||||
}
|
||||
|
||||
if ($parentHookMethodInvocation->equals($hookMethod->invocation())) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if ($hookMethod->invocation()->equals(Invocation::first())) {
|
||||
$message = \sprintf(
|
||||
'Method %s::%s() does not invoke parent::%s() before all other statements.',
|
||||
$classReflection->getName(),
|
||||
$methodName,
|
||||
$hookMethod->methodName()->toString(),
|
||||
);
|
||||
|
||||
return [
|
||||
Rules\RuleErrorBuilder::message($message)
|
||||
->identifier(ErrorIdentifier::invokeParentHookMethod()->toString())
|
||||
->build(),
|
||||
];
|
||||
}
|
||||
|
||||
if ($hookMethod->invocation()->equals(Invocation::last())) {
|
||||
$message = \sprintf(
|
||||
'Method %s::%s() does not invoke parent::%s() after all other statements.',
|
||||
$classReflection->getName(),
|
||||
$methodName,
|
||||
$hookMethod->methodName()->toString(),
|
||||
);
|
||||
|
||||
return [
|
||||
Rules\RuleErrorBuilder::message($message)
|
||||
->identifier(ErrorIdentifier::invokeParentHookMethod()->toString())
|
||||
->build(),
|
||||
];
|
||||
}
|
||||
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
private function findMatchingHookMethod(
|
||||
Analyser\Scope $scope,
|
||||
Reflection\ClassReflection $parentClassReflection,
|
||||
Reflection\ClassReflection $classReflection,
|
||||
string $methodName
|
||||
): ?HookMethod {
|
||||
foreach ($this->hookMethods as $hookMethod) {
|
||||
if (!$classReflection->isSubclassOfClass($this->reflectionProvider->getClass($hookMethod->className()->toString()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\mb_strtolower($hookMethod->methodName()->toString()) !== \mb_strtolower($methodName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentMethodReflection = $parentClassReflection->getMethod(
|
||||
$methodName,
|
||||
$scope,
|
||||
);
|
||||
|
||||
$declaringClassReflection = $parentMethodReflection->getDeclaringClass();
|
||||
|
||||
if (\mb_strtolower($hookMethod->className()->toString()) !== \mb_strtolower($declaringClassReflection->getName())) {
|
||||
return HookMethod::create(
|
||||
ClassName::fromString($declaringClassReflection->getName()),
|
||||
MethodName::fromString($methodName),
|
||||
$hookMethod->invocation(),
|
||||
HasContent::maybe(),
|
||||
);
|
||||
}
|
||||
|
||||
return $hookMethod;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<Node\Stmt> $statements
|
||||
*/
|
||||
private static function findParentHookMethodInvocation(
|
||||
array $statements,
|
||||
HookMethod $hookMethod
|
||||
): Invocation {
|
||||
$statementsWithOperations = \array_filter($statements, static function (Node $statement): bool {
|
||||
if ($statement instanceof Node\Stmt\Nop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$statementCount = \count($statementsWithOperations);
|
||||
|
||||
foreach ($statementsWithOperations as $index => $statement) {
|
||||
if (!$statement instanceof Node\Stmt\Expression) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$statement->expr instanceof Node\Expr\StaticCall) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$statement->expr->class instanceof Node\Name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$className = (string) $statement->expr->class;
|
||||
|
||||
if (\mb_strtolower($className) !== 'parent') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$statement->expr->name instanceof Node\Identifier) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\mb_strtolower($statement->expr->name->toString()) === \mb_strtolower($hookMethod->methodName()->toString())) {
|
||||
if (1 === $statementCount) {
|
||||
return $hookMethod->invocation();
|
||||
}
|
||||
|
||||
if (0 === $index) {
|
||||
return Invocation::first();
|
||||
}
|
||||
|
||||
if ($statementCount - 1 === $index) {
|
||||
return Invocation::last();
|
||||
}
|
||||
|
||||
return Invocation::any();
|
||||
}
|
||||
}
|
||||
|
||||
return Invocation::never();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<HookMethod>
|
||||
*/
|
||||
private static function filter(
|
||||
Reflection\ReflectionProvider $reflectionProvider,
|
||||
HookMethod ...$hookMethods
|
||||
): array {
|
||||
return \array_values(\array_filter($hookMethods, static function (HookMethod $hookMethod) use ($reflectionProvider): bool {
|
||||
return $reflectionProvider->hasClass($hookMethod->className()->toString());
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<HookMethod>
|
||||
*/
|
||||
private static function sort(
|
||||
Reflection\ReflectionProvider $reflectionProvider,
|
||||
HookMethod ...$hookMethods
|
||||
): array {
|
||||
\usort($hookMethods, static function (HookMethod $a, HookMethod $b) use ($reflectionProvider): int {
|
||||
if (\mb_strtolower($a->className()->toString()) === \mb_strtolower($b->className()->toString())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($reflectionProvider->getClass($a->className()->toString())->isSubclassOfClass($reflectionProvider->getClass($b->className()->toString()))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
});
|
||||
|
||||
return $hookMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<HookMethod>
|
||||
*/
|
||||
private static function defaultHookMethods(): array
|
||||
{
|
||||
return [
|
||||
/**
|
||||
* @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2083-L2085
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Framework\TestCase::class),
|
||||
MethodName::fromString('assertPostConditions'),
|
||||
Invocation::last(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2073-L2075
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Framework\TestCase::class),
|
||||
MethodName::fromString('assertPreConditions'),
|
||||
Invocation::first(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2063-L2065
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Framework\TestCase::class),
|
||||
MethodName::fromString('setUp'),
|
||||
Invocation::first(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2055-L2057
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Framework\TestCase::class),
|
||||
MethodName::fromString('setUpBeforeClass'),
|
||||
Invocation::first(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2091-L2093
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Framework\TestCase::class),
|
||||
MethodName::fromString('tearDown'),
|
||||
Invocation::last(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/sebastianbergmann/phpunit/blob/6.0.0/src/Framework/TestCase.php#L2098-L2100
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Framework\TestCase::class),
|
||||
MethodName::fromString('tearDownAfterClass'),
|
||||
Invocation::last(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L11-L13
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(PHPUnit\TestCase::class),
|
||||
MethodName::fromString('_setUp'),
|
||||
Invocation::first(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L25-L27
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(PHPUnit\TestCase::class),
|
||||
MethodName::fromString('_setUpBeforeClass'),
|
||||
Invocation::first(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L18-L20
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(PHPUnit\TestCase::class),
|
||||
MethodName::fromString('_tearDown'),
|
||||
Invocation::last(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/phpunit-wrapper/blob/9.0.0/src/TestCase.php#L32-L34
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(PHPUnit\TestCase::class),
|
||||
MethodName::fromString('_tearDownAfterClass'),
|
||||
Invocation::last(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L75-L77
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Test\Unit::class),
|
||||
MethodName::fromString('_after'),
|
||||
Invocation::last(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L63-L65
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Test\Unit::class),
|
||||
MethodName::fromString('_before'),
|
||||
Invocation::first(),
|
||||
HasContent::no(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L34-L58
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Test\Unit::class),
|
||||
MethodName::fromString('_setUp'),
|
||||
Invocation::first(),
|
||||
HasContent::yes(),
|
||||
),
|
||||
/**
|
||||
* @see https://github.com/Codeception/Codeception/blob/4.2.2/src/Codeception/Test/Unit.php#L67-L70
|
||||
*/
|
||||
HookMethod::create(
|
||||
ClassName::fromString(Test\Unit::class),
|
||||
MethodName::fromString('_tearDown'),
|
||||
Invocation::last(),
|
||||
HasContent::yes(),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -15,13 +15,13 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "8.0 - 8.4"
|
||||
"php": "8.0 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.5",
|
||||
"tracy/tracy": "^2.9",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"jetbrains/phpstorm-attributes": "dev-master"
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"jetbrains/phpstorm-attributes": "^1.2"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/finder": "<3",
|
||||
|
|
@ -36,7 +36,10 @@
|
|||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
"classmap": ["src/"],
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -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.4.
|
||||
Nette Utils 4.0 is compatible with PHP 8.0 to 8.5.
|
||||
|
||||
<!---->
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function count, is_array, is_scalar, sprintf;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +60,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
|||
|
||||
|
||||
/**
|
||||
* Replaces or appends a item.
|
||||
* Replaces or appends an item.
|
||||
* @param array-key $key
|
||||
* @param T $value
|
||||
*/
|
||||
|
|
@ -74,7 +75,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
|||
|
||||
|
||||
/**
|
||||
* Returns a item.
|
||||
* Returns an item.
|
||||
* @param array-key $key
|
||||
* @return T
|
||||
*/
|
||||
|
|
@ -86,7 +87,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
|||
|
||||
|
||||
/**
|
||||
* Determines whether a item exists.
|
||||
* Determines whether an item exists.
|
||||
* @param array-key $key
|
||||
*/
|
||||
public function offsetExists($key): bool
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_slice, array_splice, count, is_int;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -63,7 +64,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
|
||||
|
||||
/**
|
||||
* Replaces or appends a item.
|
||||
* Replaces or appends an item.
|
||||
* @param int|null $index
|
||||
* @param T $value
|
||||
* @throws Nette\OutOfRangeException
|
||||
|
|
@ -83,7 +84,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
|
||||
|
||||
/**
|
||||
* Returns a item.
|
||||
* Returns an item.
|
||||
* @param int $index
|
||||
* @return T
|
||||
* @throws Nette\OutOfRangeException
|
||||
|
|
@ -99,7 +100,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
|
||||
|
||||
/**
|
||||
* Determines whether a item exists.
|
||||
* Determines whether an item exists.
|
||||
* @param int $index
|
||||
*/
|
||||
public function offsetExists($index): bool
|
||||
|
|
@ -124,7 +125,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
|||
|
||||
|
||||
/**
|
||||
* Prepends a item.
|
||||
* Prepends an item.
|
||||
* @param T $value
|
||||
*/
|
||||
public function prepend(mixed $value): void
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ namespace Nette\Utils;
|
|||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function is_array, is_int, is_object, count;
|
||||
use function array_combine, array_intersect_key, array_is_list, array_key_exists, array_key_first, array_key_last, array_keys, array_reverse, array_search, array_slice, array_walk_recursive, count, func_num_args, in_array, is_array, is_int, is_object, key, preg_split, range;
|
||||
use const PHP_VERSION_ID, PREG_GREP_INVERT, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -265,7 +266,7 @@ class Arrays
|
|||
$res = [];
|
||||
$cb = $preserveKeys
|
||||
? function ($v, $k) use (&$res): void { $res[$k] = $v; }
|
||||
: function ($v) use (&$res): void { $res[] = $v; };
|
||||
: function ($v) use (&$res): void { $res[] = $v; };
|
||||
array_walk_recursive($array, $cb);
|
||||
return $res;
|
||||
}
|
||||
|
|
@ -277,7 +278,8 @@ class Arrays
|
|||
*/
|
||||
public static function isList(mixed $value): bool
|
||||
{
|
||||
return is_array($value) && (PHP_VERSION_ID < 80100
|
||||
return is_array($value) && (
|
||||
PHP_VERSION_ID < 80100
|
||||
? !$value || array_keys($value) === range(0, count($value) - 1)
|
||||
: array_is_list($value)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array, is_object, is_string;
|
||||
use function explode, func_get_args, ini_get, is_array, is_callable, is_object, is_string, preg_replace, restore_error_handler, set_error_handler, sprintf, str_contains, str_ends_with;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_merge, checkdate, implode, is_numeric, is_string, preg_replace_callback, sprintf, time, trim;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -45,7 +46,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
public static function from(string|int|\DateTimeInterface|null $time): static
|
||||
{
|
||||
if ($time instanceof \DateTimeInterface) {
|
||||
return new static($time->format('Y-m-d H:i:s.u'), $time->getTimezone());
|
||||
return static::createFromInterface($time);
|
||||
|
||||
} elseif (is_numeric($time)) {
|
||||
if ($time <= self::YEAR) {
|
||||
|
|
@ -76,12 +77,9 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
$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
|
||||
|| $hour < 0 || $hour > 23
|
||||
|| $minute < 0 || $minute > 59
|
||||
|| $second < 0 || $second >= 60
|
||||
) {
|
||||
throw new Nette\InvalidArgumentException("Invalid date '$s'");
|
||||
}
|
||||
|
|
@ -91,26 +89,97 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
|
||||
|
||||
/**
|
||||
* Returns new DateTime object formatted according to the specified format.
|
||||
* Returns a new DateTime object formatted according to the specified format.
|
||||
*/
|
||||
public static function createFromFormat(
|
||||
string $format,
|
||||
string $time,
|
||||
string $datetime,
|
||||
string|\DateTimeZone|null $timezone = null,
|
||||
): static|false
|
||||
{
|
||||
if ($timezone === null) {
|
||||
$timezone = new \DateTimeZone(date_default_timezone_get());
|
||||
|
||||
} elseif (is_string($timezone)) {
|
||||
if (is_string($timezone)) {
|
||||
$timezone = new \DateTimeZone($timezone);
|
||||
}
|
||||
|
||||
$date = parent::createFromFormat($format, $time, $timezone);
|
||||
$date = parent::createFromFormat($format, $datetime, $timezone);
|
||||
return $date ? static::from($date) : false;
|
||||
}
|
||||
|
||||
|
||||
public function __construct(string $datetime = 'now', ?\DateTimeZone $timezone = null)
|
||||
{
|
||||
$this->apply($datetime, $timezone, true);
|
||||
}
|
||||
|
||||
|
||||
public function modify(string $modifier): static
|
||||
{
|
||||
$this->apply($modifier);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function setDate(int $year, int $month, int $day): static
|
||||
{
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
trigger_error(sprintf(self::class . ': The date %04d-%02d-%02d is not valid.', $year, $month, $day), E_USER_WARNING);
|
||||
}
|
||||
return parent::setDate($year, $month, $day);
|
||||
}
|
||||
|
||||
|
||||
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static
|
||||
{
|
||||
if (
|
||||
$hour < 0 || $hour > 23
|
||||
|| $minute < 0 || $minute > 59
|
||||
|| $second < 0 || $second >= 60
|
||||
|| $microsecond < 0 || $microsecond >= 1_000_000
|
||||
) {
|
||||
trigger_error(sprintf(self::class . ': The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000), E_USER_WARNING);
|
||||
}
|
||||
return parent::setTime($hour, $minute, $second, $microsecond);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a relative time string (e.g. '10 minut') to seconds.
|
||||
*/
|
||||
public static function relativeToSeconds(string $relativeTime): int
|
||||
{
|
||||
return (new self('@0 ' . $relativeTime))
|
||||
->getTimestamp();
|
||||
}
|
||||
|
||||
|
||||
private function apply(string $datetime, $timezone = null, bool $ctr = false): void
|
||||
{
|
||||
$relPart = '';
|
||||
$absPart = preg_replace_callback(
|
||||
'/[+-]?\s*\d+\s+((microsecond|millisecond|[mµu]sec)s?|[mµ]s|sec(ond)?s?|min(ute)?s?|hours?)(\s+ago)?\b/iu',
|
||||
function ($m) use (&$relPart) {
|
||||
$relPart .= $m[0] . ' ';
|
||||
return '';
|
||||
},
|
||||
$datetime,
|
||||
);
|
||||
|
||||
if ($ctr) {
|
||||
parent::__construct($absPart, $timezone);
|
||||
$this->handleErrors($datetime);
|
||||
} elseif (trim($absPart)) {
|
||||
parent::modify($absPart) && $this->handleErrors($datetime);
|
||||
}
|
||||
|
||||
if ($relPart) {
|
||||
$timezone ??= $this->getTimezone();
|
||||
$this->setTimezone(new \DateTimeZone('UTC'));
|
||||
parent::modify($relPart) && $this->handleErrors($datetime);
|
||||
$this->setTimezone($timezone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON representation in ISO 8601 (used by JavaScript).
|
||||
*/
|
||||
|
|
@ -137,4 +206,14 @@ class DateTime extends \DateTime implements \JsonSerializable
|
|||
$dolly = clone $this;
|
||||
return $modify ? $dolly->modify($modify) : $dolly;
|
||||
}
|
||||
|
||||
|
||||
private function handleErrors(string $value): void
|
||||
{
|
||||
$errors = self::getLastErrors();
|
||||
$errors = array_merge($errors['errors'] ?? [], $errors['warnings'] ?? []);
|
||||
if ($errors) {
|
||||
trigger_error(self::class . ': ' . implode(', ', $errors) . " '$value'", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_pop, chmod, decoct, dirname, end, fclose, file_exists, file_get_contents, file_put_contents, fopen, implode, is_dir, is_file, is_link, mkdir, preg_match, preg_split, realpath, rename, rmdir, rtrim, sprintf, str_replace, stream_copy_to_stream, stream_is_local, strtr;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -271,7 +273,7 @@ final class FileSystem
|
|||
*/
|
||||
public static function isAbsolute(string $path): bool
|
||||
{
|
||||
return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
|
||||
return (bool) preg_match('#([a-z]:)?[/\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -280,7 +282,7 @@ final class FileSystem
|
|||
*/
|
||||
public static function normalizePath(string $path): string
|
||||
{
|
||||
$parts = $path === '' ? [] : preg_split('~[/\\\\]+~', $path);
|
||||
$parts = $path === '' ? [] : preg_split('~[/\\\]+~', $path);
|
||||
$res = [];
|
||||
foreach ($parts as $part) {
|
||||
if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') {
|
||||
|
|
@ -305,6 +307,19 @@ final class FileSystem
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a path against a base path. If the path is absolute, returns it directly, if it's relative, joins it with the base path.
|
||||
*/
|
||||
public static function resolvePath(string $basePath, string $path): string
|
||||
{
|
||||
return match (true) {
|
||||
self::isAbsolute($path) => self::platformSlashes($path),
|
||||
$path === '' => self::platformSlashes($basePath),
|
||||
default => self::joinPaths($basePath, $path),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts backslashes to slashes.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_merge, count, func_get_args, func_num_args, glob, implode, is_array, is_dir, iterator_to_array, preg_match, preg_quote, preg_replace, preg_split, rtrim, spl_object_id, sprintf, str_ends_with, str_starts_with, strnatcmp, strpbrk, strrpos, strtolower, strtr, substr, usort;
|
||||
use const GLOB_NOESCAPE, GLOB_NOSORT, GLOB_ONLYDIR;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -299,7 +301,7 @@ class Finder implements \IteratorAggregate
|
|||
$operator = $operator ?: '=';
|
||||
}
|
||||
|
||||
$date = DateTime::from($date)->format('U');
|
||||
$date = DateTime::from($date)->getTimestamp();
|
||||
return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getMTime(), $operator, $date));
|
||||
}
|
||||
|
||||
|
|
@ -505,6 +507,6 @@ class Finder implements \IteratorAggregate
|
|||
'\-' => '-',
|
||||
],
|
||||
);
|
||||
return '#' . $anchor . $pattern . '$#D' . (defined('PHP_WINDOWS_VERSION_BUILD') ? 'i' : '');
|
||||
return '#' . $anchor . $pattern . '$#D' . (Helpers::IsWindows ? 'i' : '');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function abs, is_finite, is_nan, max, round;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,16 +10,21 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_unique, ini_get, levenshtein, max, min, ob_end_clean, ob_get_clean, ob_start, preg_replace, strlen;
|
||||
use const PHP_OS_FAMILY;
|
||||
|
||||
|
||||
class Helpers
|
||||
{
|
||||
public const IsWindows = PHP_OS_FAMILY === 'Windows';
|
||||
|
||||
|
||||
/**
|
||||
* Executes a callback and returns the captured output as a string.
|
||||
*/
|
||||
public static function capture(callable $func): string
|
||||
{
|
||||
ob_start(function () {});
|
||||
ob_start(fn() => '');
|
||||
try {
|
||||
$func();
|
||||
return ob_get_clean();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ namespace Nette\Utils;
|
|||
|
||||
use Nette;
|
||||
use Nette\HtmlStringable;
|
||||
use function is_array, is_float, is_object, is_string;
|
||||
use function array_merge, array_splice, count, explode, func_num_args, html_entity_decode, htmlspecialchars, http_build_query, implode, is_array, is_bool, is_float, is_object, is_string, json_encode, max, number_format, rtrim, str_contains, str_repeat, str_replace, strip_tags, strncmp, strpbrk, substr;
|
||||
use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array, is_int, is_string;
|
||||
use const IMG_BMP, IMG_FLIP_BOTH, IMG_FLIP_HORIZONTAL, IMG_FLIP_VERTICAL, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP, PATHINFO_EXTENSION;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -718,42 +720,27 @@ class Image
|
|||
*/
|
||||
private function output(int $type, ?int $quality, ?string $file = null): void
|
||||
{
|
||||
switch ($type) {
|
||||
case ImageType::JPEG:
|
||||
$quality = $quality === null ? 85 : max(0, min(100, $quality));
|
||||
$success = @imagejpeg($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
[$defQuality, $min, $max] = match ($type) {
|
||||
ImageType::JPEG => [85, 0, 100],
|
||||
ImageType::PNG => [9, 0, 9],
|
||||
ImageType::GIF => [null, null, null],
|
||||
ImageType::WEBP => [80, 0, 100],
|
||||
ImageType::AVIF => [30, 0, 100],
|
||||
ImageType::BMP => [null, null, null],
|
||||
default => throw new Nette\InvalidArgumentException("Unsupported image type '$type'."),
|
||||
};
|
||||
|
||||
case ImageType::PNG:
|
||||
$quality = $quality === null ? 9 : max(0, min(9, $quality));
|
||||
$success = @imagepng($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case ImageType::GIF:
|
||||
$success = @imagegif($this->image, $file); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case ImageType::WEBP:
|
||||
$quality = $quality === null ? 80 : max(0, min(100, $quality));
|
||||
$success = @imagewebp($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case ImageType::AVIF:
|
||||
$quality = $quality === null ? 30 : max(0, min(100, $quality));
|
||||
$success = @imageavif($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case ImageType::BMP:
|
||||
$success = @imagebmp($this->image, $file); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Nette\InvalidArgumentException("Unsupported image type '$type'.");
|
||||
$args = [$this->image, $file];
|
||||
if ($defQuality !== null) {
|
||||
$args[] = $quality === null ? $defQuality : max($min, min($max, $quality));
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
throw new ImageException(Helpers::getLastError() ?: 'Unknown error');
|
||||
}
|
||||
Callback::invokeSafe('image' . self::Formats[$type], $args, function (string $message) use ($file): void {
|
||||
if ($file !== null) {
|
||||
@unlink($file);
|
||||
}
|
||||
throw new ImageException($message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -786,7 +773,7 @@ class Image
|
|||
|
||||
public function __clone()
|
||||
{
|
||||
ob_start(function () {});
|
||||
ob_start(fn() => '');
|
||||
imagepng($this->image, null, 0);
|
||||
$this->setImageResource(imagecreatefromstring(ob_get_clean()));
|
||||
}
|
||||
|
|
@ -809,7 +796,7 @@ class Image
|
|||
/**
|
||||
* Prevents serialization.
|
||||
*/
|
||||
public function __sleep(): array
|
||||
public function __serialize(): array
|
||||
{
|
||||
throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function hexdec, ltrim, max, min, round, strlen;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use const IMAGETYPE_BMP, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_WEBP;
|
||||
|
||||
|
||||
/**
|
||||
* Type of image file.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -233,6 +234,7 @@ final class Iterables
|
|||
$iterable instanceof \Iterator => $iterable,
|
||||
$iterable instanceof \IteratorAggregate => self::toIterator($iterable->getIterator()),
|
||||
is_array($iterable) => new \ArrayIterator($iterable),
|
||||
default => throw new Nette\ShouldNotHappenException,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function defined, is_int, json_decode, json_encode, json_last_error, json_last_error_msg;
|
||||
use const JSON_BIGINT_AS_STRING, JSON_FORCE_OBJECT, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_OBJECT_AS_ARRAY, JSON_PRESERVE_ZERO_FRACTION, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ namespace Nette\Utils;
|
|||
|
||||
use Nette;
|
||||
use Nette\MemberAccessException;
|
||||
use function array_filter, array_merge, array_pop, array_unique, get_class_methods, get_parent_class, implode, is_a, levenshtein, method_exists, preg_match_all, preg_replace, strlen, ucfirst;
|
||||
use const PREG_SET_ORDER, SORT_REGULAR;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ namespace Nette\Utils;
|
|||
|
||||
use Nette;
|
||||
use Random\Randomizer;
|
||||
use function strlen;
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function constant, current, defined, end, explode, file_get_contents, implode, ltrim, next, ord, strrchr, strtolower, substr;
|
||||
use const 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;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -19,21 +21,20 @@ final class Reflection
|
|||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/** @deprecated use Nette\Utils\Validator::isBuiltinType() */
|
||||
/** @deprecated use Nette\Utils\Validators::isBuiltinType() */
|
||||
public static function isBuiltinType(string $type): bool
|
||||
{
|
||||
return Validators::isBuiltinType($type);
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated use Nette\Utils\Validator::isClassKeyword() */
|
||||
/** @deprecated use Nette\Utils\Validators::isClassKeyword() */
|
||||
public static function isClassKeyword(string $name): bool
|
||||
{
|
||||
return Validators::isClassKeyword($name);
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated use native ReflectionParameter::getDefaultValue() */
|
||||
public static function getParameterDefaultValue(\ReflectionParameter $param): mixed
|
||||
{
|
||||
if ($param->isDefaultValueConstant()) {
|
||||
|
|
@ -240,9 +241,7 @@ final class Reflection
|
|||
case T_CLASS:
|
||||
case T_INTERFACE:
|
||||
case T_TRAIT:
|
||||
case PHP_VERSION_ID < 80100
|
||||
? T_CLASS
|
||||
: T_ENUM:
|
||||
case PHP_VERSION_ID < 80100 ? T_CLASS : T_ENUM:
|
||||
if ($name = self::fetch($tokens, T_STRING)) {
|
||||
$class = $namespace . $name;
|
||||
$classLevel = $level + 1;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use function explode, is_string, str_contains;
|
||||
|
||||
|
||||
/**
|
||||
* ReflectionMethod preserving the original class name.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ namespace Nette\Utils;
|
|||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function is_array, is_object, strlen;
|
||||
use function array_keys, array_map, array_shift, array_values, bin2hex, class_exists, defined, extension_loaded, function_exists, htmlspecialchars, htmlspecialchars_decode, iconv, iconv_strlen, iconv_substr, implode, in_array, is_array, is_callable, is_int, is_object, is_string, key, max, mb_convert_case, mb_strlen, mb_strtolower, mb_strtoupper, mb_substr, pack, preg_last_error, preg_last_error_msg, preg_quote, preg_replace, str_contains, str_ends_with, str_repeat, str_replace, str_starts_with, strlen, strpos, strrev, strrpos, strtolower, strtoupper, strtr, substr, trim, unpack, utf8_decode;
|
||||
use const ENT_IGNORE, ENT_NOQUOTES, ICONV_IMPL, MB_CASE_TITLE, PHP_EOL, PREG_OFFSET_CAPTURE, PREG_PATTERN_ORDER, PREG_SET_ORDER, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY, PREG_SPLIT_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -21,14 +22,14 @@ class Strings
|
|||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
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}";
|
||||
public const TrimCharacters = " \t\n\r\0\x0B\u{A0}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{200B}\u{2028}\u{3000}";
|
||||
|
||||
/** @deprecated use Strings::TrimCharacters */
|
||||
public const TRIM_CHARACTERS = self::TrimCharacters;
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated use Nette\Utils\Validator::isUnicode()
|
||||
* @deprecated use Nette\Utils\Validators::isUnicode()
|
||||
*/
|
||||
public static function checkEncoding(string $s): bool
|
||||
{
|
||||
|
|
@ -547,7 +548,6 @@ class Strings
|
|||
return $utf8 && $captureOffset
|
||||
? self::bytesToChars($subject, [$m])[0]
|
||||
: $m;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ 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;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -260,8 +262,8 @@ final class Type
|
|||
$subtypes,
|
||||
fn($subtype) => Validators::isBuiltinType($type)
|
||||
? strcasecmp($type, $subtype) === 0
|
||||
: is_a($subtype, $type, allow_string: true)
|
||||
)
|
||||
: is_a($subtype, $type, allow_string: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
|||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_key_exists, class_exists, explode, gettype, interface_exists, is_callable, is_float, is_int, is_iterable, is_numeric, is_object, is_string, preg_match, str_ends_with, str_replace, str_starts_with, strlen, strtolower, substr, trait_exists, var_export;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Nette\Utils;
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an image error occurs.
|
||||
* An error occurred while working with the image.
|
||||
*/
|
||||
class ImageException extends \Exception
|
||||
{
|
||||
|
|
@ -19,7 +19,7 @@ class ImageException extends \Exception
|
|||
|
||||
|
||||
/**
|
||||
* The exception that indicates invalid image file.
|
||||
* The image file is invalid or in an unsupported format.
|
||||
*/
|
||||
class UnknownImageFileException extends ImageException
|
||||
{
|
||||
|
|
@ -27,7 +27,7 @@ class UnknownImageFileException extends ImageException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of JSON encoding/decoding.
|
||||
* JSON encoding or decoding failed.
|
||||
*/
|
||||
class JsonException extends \JsonException
|
||||
{
|
||||
|
|
@ -35,7 +35,7 @@ class JsonException extends \JsonException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of the last Regexp execution.
|
||||
* Regular expression pattern or execution failed.
|
||||
*/
|
||||
class RegexpException extends \Exception
|
||||
{
|
||||
|
|
@ -43,7 +43,7 @@ class RegexpException extends \Exception
|
|||
|
||||
|
||||
/**
|
||||
* The exception that indicates assertion error.
|
||||
* Type validation failed. The value doesn't match the expected type constraints.
|
||||
*/
|
||||
class AssertionException extends \Exception
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ namespace Nette;
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when the value of an argument is
|
||||
* outside the allowable range of values as defined by the invoked method.
|
||||
* The value is outside the allowed range.
|
||||
*/
|
||||
class ArgumentOutOfRangeException extends \InvalidArgumentException
|
||||
{
|
||||
|
|
@ -20,8 +19,7 @@ class ArgumentOutOfRangeException extends \InvalidArgumentException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a method call is invalid for the object's
|
||||
* current state, method has been invoked at an illegal or inappropriate time.
|
||||
* The object is in a state that does not allow the requested operation.
|
||||
*/
|
||||
class InvalidStateException extends \RuntimeException
|
||||
{
|
||||
|
|
@ -29,7 +27,7 @@ class InvalidStateException extends \RuntimeException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a requested method or operation is not implemented.
|
||||
* The requested feature is not implemented.
|
||||
*/
|
||||
class NotImplementedException extends \LogicException
|
||||
{
|
||||
|
|
@ -37,8 +35,7 @@ class NotImplementedException extends \LogicException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an invoked method is not supported. For scenarios where
|
||||
* it is sometimes possible to perform the requested operation, see InvalidStateException.
|
||||
* The requested operation is not supported.
|
||||
*/
|
||||
class NotSupportedException extends \LogicException
|
||||
{
|
||||
|
|
@ -46,7 +43,7 @@ class NotSupportedException extends \LogicException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a requested method or operation is deprecated.
|
||||
* The requested feature is deprecated and no longer available.
|
||||
*/
|
||||
class DeprecatedException extends NotSupportedException
|
||||
{
|
||||
|
|
@ -54,7 +51,7 @@ class DeprecatedException extends NotSupportedException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when accessing a class member (property or method) fails.
|
||||
* Cannot access the requested class property or method.
|
||||
*/
|
||||
class MemberAccessException extends \Error
|
||||
{
|
||||
|
|
@ -62,7 +59,7 @@ class MemberAccessException extends \Error
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an I/O error occurs.
|
||||
* Failed to read from or write to a file or stream.
|
||||
*/
|
||||
class IOException extends \RuntimeException
|
||||
{
|
||||
|
|
@ -70,7 +67,7 @@ class IOException extends \RuntimeException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when accessing a file that does not exist on disk.
|
||||
* The requested file does not exist.
|
||||
*/
|
||||
class FileNotFoundException extends IOException
|
||||
{
|
||||
|
|
@ -78,7 +75,7 @@ class FileNotFoundException extends IOException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when part of a file or directory cannot be found.
|
||||
* The requested directory does not exist.
|
||||
*/
|
||||
class DirectoryNotFoundException extends IOException
|
||||
{
|
||||
|
|
@ -86,7 +83,7 @@ class DirectoryNotFoundException extends IOException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an argument does not match with the expected value.
|
||||
* The provided argument has invalid type or format.
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
|
|
@ -94,7 +91,7 @@ class InvalidArgumentException extends \InvalidArgumentException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an illegal index was requested.
|
||||
* The requested array or collection index does not exist.
|
||||
*/
|
||||
class OutOfRangeException extends \OutOfRangeException
|
||||
{
|
||||
|
|
@ -102,8 +99,16 @@ class OutOfRangeException extends \OutOfRangeException
|
|||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a value (typically returned by function) does not match with the expected value.
|
||||
* The returned value has unexpected type or format.
|
||||
*/
|
||||
class UnexpectedValueException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Houston, we have a problem.
|
||||
*/
|
||||
class ShouldNotHappenException extends \LogicException
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ final class GeneratedConfig
|
|||
0 => 'rules.neon',
|
||||
),
|
||||
),
|
||||
'version' => '2.8.0',
|
||||
'phpstanVersionConstraint' => '>=2.0.0.0-dev, <3.0.0.0-dev',
|
||||
'version' => '2.12.0',
|
||||
'phpstanVersionConstraint' => '>=2.1.8.0-dev, <3.0.0.0-dev',
|
||||
),
|
||||
'phpstan/phpstan-strict-rules' =>
|
||||
array (
|
||||
|
|
@ -35,7 +35,7 @@ final class GeneratedConfig
|
|||
0 => 'rules.neon',
|
||||
),
|
||||
),
|
||||
'version' => '2.0.3',
|
||||
'version' => '2.0.6',
|
||||
'phpstanVersionConstraint' => '>=2.0.4.0-dev, <3.0.0.0-dev',
|
||||
),
|
||||
'tomasvotruba/type-coverage' =>
|
||||
|
|
@ -58,7 +58,7 @@ final class GeneratedConfig
|
|||
);
|
||||
|
||||
/** @var string|null */
|
||||
public const PHPSTAN_VERSION_CONSTRAINT = '>=2.0.4.0-dev, <3.0.0.0-dev';
|
||||
public const PHPSTAN_VERSION_CONSTRAINT = '>=2.1.8.0-dev, <3.0.0.0-dev';
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 Ondřej Mirtes
|
||||
Copyright (c) 2025 PHPStan s.r.o.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -6,30 +6,29 @@
|
|||
|
||||
[PHPStan](https://phpstan.org/) focuses on finding bugs in your code. But in PHP there's a lot of leeway in how stuff can be written. This repository contains additional rules that revolve around strictly and strongly typed code with no loose casting for those who want additional safety in extremely defensive programming:
|
||||
|
||||
* Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `||`.
|
||||
* Require numeric operands or arrays in `+` and numeric operands in `-`/`*`/`/`/`**`/`%`.
|
||||
* Require numeric operand in `$var++`, `$var--`, `++$var`and `--$var`.
|
||||
* These functions contain a `$strict` parameter for better type safety, it must be set to `true`:
|
||||
* `in_array` (3rd parameter)
|
||||
* `array_search` (3rd parameter)
|
||||
* `array_keys` (3rd parameter; only if the 2nd parameter `$search_value` is provided)
|
||||
* `base64_decode` (2nd parameter)
|
||||
* Variables assigned in `while` loop condition and `for` loop initial assignment cannot be used after the loop.
|
||||
* Variables set in foreach that's always looped thanks to non-empty arrays cannot be used after the loop.
|
||||
* Types in `switch` condition and `case` value must match. PHP compares them loosely by default and that can lead to unexpected results.
|
||||
* Check that statically declared methods are called statically.
|
||||
* Disallow `empty()` - it's a very loose comparison (see [manual](https://php.net/empty)), it's recommended to use more strict one.
|
||||
* Disallow short ternary operator (`?:`) - implies weak comparison, it's recommended to use null coalesce operator (`??`) or ternary operator with strict condition.
|
||||
* Disallow variable variables (`$$foo`, `$this->$method()` etc.)
|
||||
* Disallow overwriting variables with foreach key and value variables
|
||||
* Always true `instanceof`, type-checking `is_*` functions and strict comparisons `===`/`!==`. These checks can be turned off by setting `checkAlwaysTrueInstanceof`/`checkAlwaysTrueCheckTypeFunctionCall`/`checkAlwaysTrueStrictComparison` to false.
|
||||
* Correct case for referenced and called function names.
|
||||
* Correct case for inherited and implemented method names.
|
||||
* Contravariance for parameter types and covariance for return types in inherited methods (also known as Liskov substitution principle - LSP)
|
||||
* Check LSP even for static methods
|
||||
* Require calling parent constructor
|
||||
* Disallow usage of backtick operator (`` $ls = `ls -la` ``)
|
||||
* Closure should use `$this` directly instead of using `$this` variable indirectly
|
||||
| Configuration Parameters | Rule Description |
|
||||
|:---------------------------------------|:--------------------------------------------------------------------------------------------------------|
|
||||
| `booleansInConditions` | Require booleans in `if`, `elseif`, ternary operator, after `!`, and on both sides of `&&` and `\|\|`. |
|
||||
| `booleansInLoopConditions` | Require booleans in `while` and `do while` loop conditions. |
|
||||
| `numericOperandsInArithmeticOperators` | Require numeric operand in `+$var`, `-$var`, `$var++`, `$var--`, `++$var` and `--$var`. |
|
||||
| `numericOperandsInArithmeticOperators` | Require numeric operand in `$var++`, `$var--`, `++$var`and `--$var`. |
|
||||
| `strictFunctionCalls` | These functions contain a `$strict` parameter for better type safety, it must be set to `true`:<br>* `in_array` (3rd parameter)<br>* `array_search` (3rd parameter)<br>* `array_keys` (3rd parameter; only if the 2nd parameter `$search_value` is provided)<br>* `base64_decode` (2nd parameter). |
|
||||
| `overwriteVariablesWithLoop` | Variables assigned in `while` loop condition and `for` loop initial assignment cannot be used after the loop. |
|
||||
| `overwriteVariablesWithLoop` | Variables set in foreach that's always looped thanks to non-empty arrays cannot be used after the loop. |
|
||||
| `switchConditionsMatchingType` | Types in `switch` condition and `case` value must match. PHP compares them loosely by default and that can lead to unexpected results. |
|
||||
| `dynamicCallOnStaticMethod` | Check that statically declared methods are called statically. |
|
||||
| `disallowedEmpty` | Disallow `empty()` - it's a very loose comparison (see [manual](https://php.net/empty)), it's recommended to use more strict one. |
|
||||
| `disallowedShortTernary` | Disallow short ternary operator (`?:`) - implies weak comparison, it's recommended to use null coalesce operator (`??`) or ternary operator with strict condition. |
|
||||
| `noVariableVariables` | Disallow variable variables (`$$foo`, `$this->$method()` etc.). |
|
||||
| `overwriteVariablesWithLoop` | Disallow overwriting variables with foreach key and value variables. |
|
||||
| `checkAlwaysTrueInstanceof`, `checkAlwaysTrueCheckTypeFunctionCall`, `checkAlwaysTrueStrictComparison` | Always true `instanceof`, type-checking `is_*` functions and strict comparisons `===`/`!==`. These checks can be turned off by setting `checkAlwaysTrueInstanceof`, `checkAlwaysTrueCheckTypeFunctionCall` and `checkAlwaysTrueStrictComparison` to false. |
|
||||
| | Correct case for referenced and called function names. |
|
||||
| `matchingInheritedMethodNames` | Correct case for inherited and implemented method names. |
|
||||
| | Contravariance for parameter types and covariance for return types in inherited methods (also known as Liskov substitution principle - LSP).|
|
||||
| | Check LSP even for static methods. |
|
||||
| `requireParentConstructorCall` | Require calling parent constructor. |
|
||||
| `disallowedBacktick` | Disallow usage of backtick operator (`` $ls = `ls -la` ``). |
|
||||
| `closureUsesThis` | Closure should use `$this` directly instead of using `$this` variable indirectly. |
|
||||
|
||||
Additional rules are coming in subsequent releases!
|
||||
|
||||
|
|
@ -64,6 +63,7 @@ parameters:
|
|||
strictRules:
|
||||
disallowedLooseComparison: false
|
||||
booleansInConditions: false
|
||||
booleansInLoopConditions: false
|
||||
uselessCast: false
|
||||
requireParentConstructorCall: false
|
||||
disallowedBacktick: false
|
||||
|
|
@ -82,7 +82,7 @@ parameters:
|
|||
illegalConstructorMethodCall: false
|
||||
```
|
||||
|
||||
Aside from introducing new custom rules, phpstan-strict-rules also [change the default values of some configuration parameters](https://github.com/phpstan/phpstan-strict-rules/blob/1.6.x/rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis).
|
||||
Aside from introducing new custom rules, phpstan-strict-rules also [change the default values of some configuration parameters](./rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis).
|
||||
|
||||
## Enabling rules one-by-one
|
||||
|
||||
|
|
@ -105,4 +105,4 @@ parameters:
|
|||
booleansInConditions: true
|
||||
```
|
||||
|
||||
Even with `strictRules.allRules` set to `false`, part of this package is still in effect. That's because phpstan-strict-rules also [change the default values of some configuration parameters](https://github.com/phpstan/phpstan-strict-rules/blob/1.6.x/rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis).
|
||||
Even with `strictRules.allRules` set to `false`, part of this package is still in effect. That's because phpstan-strict-rules also [change the default values of some configuration parameters](./rules.neon#L1) that are present in PHPStan itself. These parameters are [documented on phpstan.org](https://phpstan.org/config-reference#stricter-analysis).
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ parameters:
|
|||
allRules: true
|
||||
disallowedLooseComparison: %strictRules.allRules%
|
||||
booleansInConditions: %strictRules.allRules%
|
||||
booleansInLoopConditions: [%strictRules.allRules%, %featureToggles.bleedingEdge%]
|
||||
uselessCast: %strictRules.allRules%
|
||||
requireParentConstructorCall: %strictRules.allRules%
|
||||
disallowedBacktick: %strictRules.allRules%
|
||||
|
|
@ -37,6 +38,7 @@ parametersSchema:
|
|||
allRules: anyOf(bool(), arrayOf(bool())),
|
||||
disallowedLooseComparison: anyOf(bool(), arrayOf(bool())),
|
||||
booleansInConditions: anyOf(bool(), arrayOf(bool()))
|
||||
booleansInLoopConditions: anyOf(bool(), arrayOf(bool()))
|
||||
uselessCast: anyOf(bool(), arrayOf(bool()))
|
||||
requireParentConstructorCall: anyOf(bool(), arrayOf(bool()))
|
||||
disallowedBacktick: anyOf(bool(), arrayOf(bool()))
|
||||
|
|
@ -64,12 +66,16 @@ conditionalTags:
|
|||
phpstan.rules.rule: %strictRules.booleansInConditions%
|
||||
PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule:
|
||||
phpstan.rules.rule: %strictRules.booleansInConditions%
|
||||
PHPStan\Rules\BooleansInConditions\BooleanInDoWhileConditionRule:
|
||||
phpstan.rules.rule: %strictRules.booleansInLoopConditions%
|
||||
PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule:
|
||||
phpstan.rules.rule: %strictRules.booleansInConditions%
|
||||
PHPStan\Rules\BooleansInConditions\BooleanInIfConditionRule:
|
||||
phpstan.rules.rule: %strictRules.booleansInConditions%
|
||||
PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule:
|
||||
phpstan.rules.rule: %strictRules.booleansInConditions%
|
||||
PHPStan\Rules\BooleansInConditions\BooleanInWhileConditionRule:
|
||||
phpstan.rules.rule: %strictRules.booleansInLoopConditions%
|
||||
PHPStan\Rules\Cast\UselessCastRule:
|
||||
phpstan.rules.rule: %strictRules.uselessCast%
|
||||
PHPStan\Rules\Classes\RequireParentConstructCallRule:
|
||||
|
|
@ -100,6 +106,10 @@ conditionalTags:
|
|||
phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators%
|
||||
PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule:
|
||||
phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators%
|
||||
PHPStan\Rules\Operators\OperandInArithmeticUnaryMinusRule:
|
||||
phpstan.rules.rule: [%strictRules.numericOperandsInArithmeticOperators%, %featureToggles.bleedingEdge%]
|
||||
PHPStan\Rules\Operators\OperandInArithmeticUnaryPlusRule:
|
||||
phpstan.rules.rule: [%strictRules.numericOperandsInArithmeticOperators%, %featureToggles.bleedingEdge%]
|
||||
PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule:
|
||||
phpstan.rules.rule: %strictRules.numericOperandsInArithmeticOperators%
|
||||
PHPStan\Rules\Operators\OperandsInArithmeticDivisionRule:
|
||||
|
|
@ -163,6 +173,9 @@ services:
|
|||
-
|
||||
class: PHPStan\Rules\BooleansInConditions\BooleanInBooleanOrRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\BooleansInConditions\BooleanInDoWhileConditionRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\BooleansInConditions\BooleanInElseIfConditionRule
|
||||
|
||||
|
|
@ -172,6 +185,9 @@ services:
|
|||
-
|
||||
class: PHPStan\Rules\BooleansInConditions\BooleanInTernaryOperatorRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\BooleansInConditions\BooleanInWhileConditionRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\Cast\UselessCastRule
|
||||
arguments:
|
||||
|
|
@ -230,6 +246,12 @@ services:
|
|||
-
|
||||
class: PHPStan\Rules\Operators\OperandInArithmeticPreIncrementRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\Operators\OperandInArithmeticUnaryMinusRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\Operators\OperandInArithmeticUnaryPlusRule
|
||||
|
||||
-
|
||||
class: PHPStan\Rules\Operators\OperandsInArithmeticAdditionRule
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\Rules\BooleansInConditions;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @implements Rule<Node\Stmt\Do_>
|
||||
*/
|
||||
class BooleanInDoWhileConditionRule implements Rule
|
||||
{
|
||||
|
||||
private BooleanRuleHelper $helper;
|
||||
|
||||
public function __construct(BooleanRuleHelper $helper)
|
||||
{
|
||||
$this->helper = $helper;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Node\Stmt\Do_::class;
|
||||
}
|
||||
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
if ($this->helper->passesAsBoolean($scope, $node->cond)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$conditionExpressionType = $scope->getType($node->cond);
|
||||
|
||||
return [
|
||||
RuleErrorBuilder::message(sprintf(
|
||||
'Only booleans are allowed in a do-while condition, %s given.',
|
||||
$conditionExpressionType->describe(VerbosityLevel::typeOnly()),
|
||||
))->identifier('doWhile.condNotBoolean')->build(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\Rules\BooleansInConditions;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @implements Rule<Node\Stmt\While_>
|
||||
*/
|
||||
class BooleanInWhileConditionRule implements Rule
|
||||
{
|
||||
|
||||
private BooleanRuleHelper $helper;
|
||||
|
||||
public function __construct(BooleanRuleHelper $helper)
|
||||
{
|
||||
$this->helper = $helper;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return Node\Stmt\While_::class;
|
||||
}
|
||||
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
if ($this->helper->passesAsBoolean($scope, $node->cond)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$conditionExpressionType = $scope->getType($node->cond);
|
||||
|
||||
return [
|
||||
RuleErrorBuilder::message(sprintf(
|
||||
'Only booleans are allowed in a while condition, %s given.',
|
||||
$conditionExpressionType->describe(VerbosityLevel::typeOnly()),
|
||||
))->identifier('while.condNotBoolean')->build(),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -55,12 +55,12 @@ class UselessCastRule implements Rule
|
|||
return $ruleErrorBuilder;
|
||||
}
|
||||
|
||||
$expressionTypeWithoutPhpDoc = $scope->getNativeType($node->expr);
|
||||
if ($castType->isSuperTypeOf($expressionTypeWithoutPhpDoc)->yes()) {
|
||||
if (!$this->treatPhpDocTypesAsCertainTip) {
|
||||
return $ruleErrorBuilder;
|
||||
}
|
||||
|
||||
if (!$this->treatPhpDocTypesAsCertainTip) {
|
||||
$expressionTypeWithoutPhpDoc = $scope->getNativeType($node->expr);
|
||||
if ($castType->isSuperTypeOf($expressionTypeWithoutPhpDoc)->yes()) {
|
||||
return $ruleErrorBuilder;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\Rules\Operators;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\UnaryMinus;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @phpstan-implements Rule<UnaryMinus>
|
||||
*/
|
||||
class OperandInArithmeticUnaryMinusRule implements Rule
|
||||
{
|
||||
|
||||
private OperatorRuleHelper $helper;
|
||||
|
||||
public function __construct(OperatorRuleHelper $helper)
|
||||
{
|
||||
$this->helper = $helper;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return UnaryMinus::class;
|
||||
}
|
||||
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
if (!$this->helper->isValidForArithmeticOperation($scope, $node->expr)) {
|
||||
$varType = $scope->getType($node->expr);
|
||||
|
||||
$messages[] = RuleErrorBuilder::message(sprintf(
|
||||
'Only numeric types are allowed in unary -, %s given.',
|
||||
$varType->describe(VerbosityLevel::typeOnly()),
|
||||
))->identifier('unaryMinus.nonNumeric')->build();
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php declare(strict_types = 1);
|
||||
|
||||
namespace PHPStan\Rules\Operators;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\UnaryPlus;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Rules\Rule;
|
||||
use PHPStan\Rules\RuleErrorBuilder;
|
||||
use PHPStan\Type\VerbosityLevel;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* @phpstan-implements Rule<UnaryPlus>
|
||||
*/
|
||||
class OperandInArithmeticUnaryPlusRule implements Rule
|
||||
{
|
||||
|
||||
private OperatorRuleHelper $helper;
|
||||
|
||||
public function __construct(OperatorRuleHelper $helper)
|
||||
{
|
||||
$this->helper = $helper;
|
||||
}
|
||||
|
||||
public function getNodeType(): string
|
||||
{
|
||||
return UnaryPlus::class;
|
||||
}
|
||||
|
||||
public function processNode(Node $node, Scope $scope): array
|
||||
{
|
||||
$messages = [];
|
||||
|
||||
if (!$this->helper->isValidForArithmeticOperation($scope, $node->expr)) {
|
||||
$varType = $scope->getType($node->expr);
|
||||
|
||||
$messages[] = RuleErrorBuilder::message(sprintf(
|
||||
'Only numeric types are allowed in unary +, %s given.',
|
||||
$varType->describe(VerbosityLevel::typeOnly()),
|
||||
))->identifier('unaryPlus.nonNumeric')->build();
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -71,8 +71,7 @@ class VariablePropertyFetchRule implements Rule
|
|||
ClassReflection $classReflection
|
||||
): bool
|
||||
{
|
||||
return $classReflection->getName() === SimpleXMLElement::class
|
||||
|| $classReflection->isSubclassOf(SimpleXMLElement::class);
|
||||
return $classReflection->is(SimpleXMLElement::class);
|
||||
}
|
||||
|
||||
private function isUniversalObjectCrate(
|
||||
|
|
@ -84,10 +83,7 @@ class VariablePropertyFetchRule implements Rule
|
|||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$classReflection->getName() === $className
|
||||
|| $classReflection->isSubclassOf($className)
|
||||
) {
|
||||
if ($classReflection->is($className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,29 @@ can be checked before you run the actual line.
|
|||
|
||||
## Sponsors
|
||||
|
||||
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><br>
|
||||
|
||||
### Silver Sponsors
|
||||
|
||||
<a href="https://www.startupjobs.cz/startup/shipmonk"><img src="website/src/images/sponsor/shipmonk.jpg" alt="ShipMonk" width="290" height="64"></a>
|
||||
<a href="https://www.shopware.com/en/"><img src="website/src/images/sponsor/shopware.png" alt="Shopware" width="284" height="64"></a>
|
||||
|
||||
<br><br>
|
||||
|
||||
### Bronze Sponsors
|
||||
|
||||
<a href="https://coders.thecodingmachine.com/phpstan"><img src="website/src/images/sponsor/tcm.png" alt="TheCodingMachine" width="247" height="64"></a>
|
||||
|
||||
<a href="https://packagist.com/?utm_source=phpstan&utm_medium=readme&utm_campaign=sponsorlogo"><img src="website/src/images/sponsor/packagist.png" alt="Private Packagist" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://www.cdn77.com/"><img src="website/src/images/sponsor/cdn77.png" alt="CDN77" width="283" height="64"></a>
|
||||
<a href="https://www.cdn77.com/"><img src="website/src/images/sponsor/cdn77.png" alt="CDN77" width="290" height="64"></a>
|
||||
|
||||
<a href="https://blackfire.io/docs/introduction?utm_source=phpstan&utm_medium=github_readme&utm_campaign=logo"><img src="website/src/images/sponsor/blackfire.png" alt="Blackfire.io" width="254" height="64"></a>
|
||||
<br>
|
||||
|
|
@ -36,23 +54,23 @@ can be checked before you run the actual line.
|
|||
|
||||
<a href="https://www.fame.fi/"><img src="website/src/images/sponsor/fame.png" alt="Fame Helsinki" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://www.startupjobs.cz/startup/shipmonk"><img src="website/src/images/sponsor/shipmonk.jpg" alt="ShipMonk" width="290" height="64"></a>
|
||||
<a href="https://werkenbijbelsimpel.nl/en/about-us/"><img src="website/src/images/sponsor/belsimpel.png" alt="Belsimpel" width="284" height="64"></a>
|
||||
|
||||
<a href="https://togetter.com/"><img src="website/src/images/sponsor/togetter.png" alt="Togetter" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://join.rightcapital.com/?utm_source=phpstan&utm_medium=github&utm_campaign=sponsorship"><img src="website/src/images/sponsor/rightcapital.png" alt="RightCapital" width="283" height="64"></a>
|
||||
|
||||
<a href="https://www.contentkingapp.com/?ref=php-developer&utm_source=phpstan&utm_medium=referral&utm_campaign=sponsorship"><img src="website/src/images/sponsor/contentking.png" alt="ContentKing" width="283" height="64"></a>
|
||||
<a href="https://www.shoptet.cz/"><img src="website/src/images/sponsor/shoptet.png" alt="Shoptet" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://zol.fr?utm_source=phpstan"><img src="website/src/images/sponsor/zol.png" alt="ZOL" width="283" height="64"></a>
|
||||
|
||||
<a href="https://www.edgenext.com/"><img src="website/src/images/sponsor/edgenext.png" alt="EdgeNext" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://www.shopware.com/en/"><img src="website/src/images/sponsor/shopware.png" alt="Shopware" width="284" height="64"></a>
|
||||
<a href="https://route4me.com/"><img src="website/src/images/sponsor/route4me.png" alt="Route4Me: Route Optimizer and Route Planner Software" width="283" height="64"></a>
|
||||
|
||||
<a href="https://craftcms.com/"><img src="website/src/images/sponsor/craftcms.png" alt="Craft CMS" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://www.worksome.com/"><img src="website/src/images/sponsor/worksome.png" alt="Worksome" width="283" height="64"></a>
|
||||
<a href="https://jobs.ticketswap.com/"><img src="website/src/images/sponsor/ticketswap.png" alt="TicketSwap" width="269" height="64"></a>
|
||||
|
||||
<a href="https://www.campoint.net/"><img src="website/src/images/sponsor/campoint.png" alt="campoint AG" width="283" height="64"></a>
|
||||
<br>
|
||||
|
|
@ -60,18 +78,10 @@ can be checked before you run the actual line.
|
|||
|
||||
<a href="https://inviqa.com/"><img src="website/src/images/sponsor/inviqa.png" alt="Inviqa" width="254" height="65"></a>
|
||||
<br>
|
||||
<a href="https://www.shoptet.cz/"><img src="website/src/images/sponsor/shoptet.png" alt="Shoptet" width="283" height="64"></a>
|
||||
|
||||
<a href="https://route4me.com/"><img src="website/src/images/sponsor/route4me.png" alt="Route4Me: Route Optimizer and Route Planner Software" width="283" height="64"></a>
|
||||
<br>
|
||||
<a href="https://werkenbijbelsimpel.nl/en/about-us/"><img src="website/src/images/sponsor/belsimpel.png" alt="Belsimpel" width="284" height="64"></a>
|
||||
|
||||
<a href="https://jobs.ticketswap.com/"><img src="website/src/images/sponsor/ticketswap.png" alt="TicketSwap" width="269" height="64"></a>
|
||||
|
||||
|
||||
[**You can now sponsor my open-source work on PHPStan through GitHub Sponsors.**](https://github.com/sponsors/ondrejmirtes)
|
||||
|
||||
Does GitHub already have your 💳? Do you use PHPStan to find 🐛 before they reach production? [Send a couple of 💸 a month my way too.](https://github.com/sponsors/ondrejmirtes) Thank you!
|
||||
[**You can sponsor my open-source work on PHPStan through GitHub Sponsors and also directly.**](https://phpstan.org/sponsor)
|
||||
|
||||
One-time donations [through Revolut.me](https://revolut.me/ondrejmirtes) are also accepted. To request an invoice, [contact me](mailto:ondrej@mirtes.cz) through e-mail.
|
||||
|
||||
|
|
|
|||
|
|
@ -92,6 +92,16 @@ final class PharAutoloader
|
|||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/Php81.php';
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php81/bootstrap.php';
|
||||
}
|
||||
|
||||
if (
|
||||
PHP_VERSION_ID < 80300
|
||||
&& empty ($GLOBALS['__composer_autoload_files']['662a729f963d39afe703c9d9b7ab4a8c'])
|
||||
&& !class_exists(\Symfony\Polyfill\Php83\Php83::class, false)
|
||||
) {
|
||||
$GLOBALS['__composer_autoload_files']['662a729f963d39afe703c9d9b7ab4a8c'] = true;
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php83/Php83.php';
|
||||
require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/symfony/polyfill-php83/bootstrap.php';
|
||||
}
|
||||
}
|
||||
|
||||
$filename = str_replace('\\', DIRECTORY_SEPARATOR, $class);
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,16 +1,16 @@
|
|||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmfNX6IACgkQUcZzBf/C
|
||||
5cAOGBAAibd5PkpJtT3HD4whLS7MsTSOcHoGoHNaqCqZWWk/sQPg8IqQUTCW/F4u
|
||||
e1BUQ3yTSs15XhoBh5vMzZ+LEzIdoXSwGyHSdBLVdYHVsg0HD8yUfuNiKy7ycJh0
|
||||
jfcckztoLWPxZCpaxbv2RQ4e7l73rw5fGYwtLiKUvuh4vmtXNHK/gn25cJtKbEn+
|
||||
Gocbe9LZfoB0IPWrb/JtES8SPqs307K8v1ATj7xEFK0EtFUmie36qlFOvJZstEJ4
|
||||
Ro7HWaHJJFMqeX8sABanUp42lbiEJcZEoVBnWl5UeJ5aA5NnqB8t1m95ByCn4wQp
|
||||
EMg4xiKOowtowfRByQngOd2jiNH/QuNZPNwNg4/3moJuKP/7MbAzI31FkPCoIonq
|
||||
gz34IhW0S0pNIWEwIHJOtRKkOio5AjS7JTlU+XWhYfEYVPat1WHBKnpv8pfK+Xoe
|
||||
qkkKjHhiZJD0zyf/d3LG8U+r7Q06/hJrhSghGOMcnh66qPLjl9k9MK1vAHbae9Y9
|
||||
j7SDTIZfjflFFF0iHypaU9IEUio/3tTJngE+gB6IhVhMaqySm97W3ot225hKbM1d
|
||||
VS4PqlOEPgOmPt3444VqaNJUoC2zJkGUhFKTyRm9aN+/8R6foKJ4GG4DICLmDr0R
|
||||
wXDbAthyzPpZ84kN2p6EgVkSFsomQgwU93VSvB69KvbNkkOfZ9I=
|
||||
=mtzY
|
||||
iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmjNGyEACgkQUcZzBf/C
|
||||
5cCyExAAmDcbTOVhPlAYAuH9Jdegk4x19Ax4BzQqkIV021xVGsNysyg0aIPtqCUH
|
||||
XpXZI0oA8plPhIctiobpyYqg4Ou2T/Y5PxxFVLhiD12AxDEa+Iy765BNeZCRRj3W
|
||||
VQQyDo1gj4y9N1Tmj4CihbIXNcxhXM8OMirekDPvEQmofYrp5bgXvwbyhmFsKwq/
|
||||
ToGURtUFZg8GB68FnR3SviRJgxg1z/iKA0keIYFg78tJtAgzS7eBNWBwpfWMHs8b
|
||||
2Sy87IJG0JVeq7x44ktLi5pUfi5sFwqF7cpFLnnBhFcaM+Z81KpwpfVmuvhArBsy
|
||||
Jw0iGsi2LilzPCyTETmv6YMMrzmyhsAmibVzEHUpGtoI+Qq2QiXfu1hhVG7nS+Bx
|
||||
VtUPeXu+XlXaXnFE/xm06mootkjs6NAaREKFrH/kCnH9mT5d5eRqb1KrO/hViAUC
|
||||
tGflHeuYY2BnDthpy3orOmmO+qEdTxu6M24G6ANL4RRbcad0+IC/R6+UCYy+XZlc
|
||||
QHgO2UGJw6tEvqZqhBoLpd9PEljyOdEmiCBlq4sXUnXiZvGplQzM4l0tqaaAK7AT
|
||||
+gsuo73rR6JcRMPagsOZ4WGae/s7wolq8uTclNSzM+EGTfgk6zI/1H1dFBtDGAdA
|
||||
q6bTMydHKDrTDPscsNDYPJ2OLq1tRE+LX6pojuOGadmetq5k000=
|
||||
=PLj2
|
||||
-----END PGP SIGNATURE-----
|
||||
|
|
|
|||
BIN
tools/composer
BIN
tools/composer
Binary file not shown.
109
tools/phive
109
tools/phive
|
|
@ -352,34 +352,33 @@ spl_autoload_register(
|
|||
|
||||
Phar::mapPhar('phive.phar');
|
||||
|
||||
$rc = (new Factory(new Cli\Request($_SERVER['argv']), new StaticPhiveVersion('0.15.3')))->getRunner()->run();
|
||||
$rc = (new Factory(new Cli\Request($_SERVER['argv']), new StaticPhiveVersion('0.16.0')))->getRunner()->run();
|
||||
exit($rc);
|
||||
|
||||
__HALT_COMPILER(); ?>
|
||||
nX | ||||