diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..ee242a8
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,28 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery
+* Personal attacks
+* Trolling or insulting/derogatory comments
+* Public or private harassment
+* Publishing other's private information, such as physical or electronic
+ addresses, without explicit permission
+* Other unethical or unprofessional conduct
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
+
+By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
+
+This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at sebastian@phpunit.de. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version]
+
+[homepage]: https://contributor-covenant.org
+[version]: https://contributor-covenant.org/version/1/3/0/
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index f68dbc7..bb816f5 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -7,7 +7,10 @@ We look forward to your contributions! Here are some examples how you can contri
* [Report a bug](https://github.com/sebastianbergmann/php-file-iterator/issues/new)
* [Send a pull request to fix a bug](https://github.com/sebastianbergmann/php-file-iterator/pulls)
-Please do not send pull requests that expand the scope of this project (see below).
+
+## We have a Code of Conduct
+
+Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
## Any contributions you make will be under the BSD-3-Clause License
@@ -15,6 +18,24 @@ Please do not send pull requests that expand the scope of this project (see belo
When you submit code changes, your submissions are understood to be under the same [BSD-3-Clause License](https://github.com/sebastianbergmann/php-file-iterator/blob/main/LICENSE) that covers the project. By contributing to this project, you agree that your contributions will be licensed under its BSD-3-Clause License.
+### Do Not Violate Copyright
+
+Only submit a pull request with your own original code. Do NOT submit a pull request containing code which you have largely copied from
+another project, unless you wrote the respective code yourself.
+
+Open Source does not mean that copyright does not apply. Copyright infringements will not be tolerated and can lead to you being banned from this project and repository.
+
+
+### Do Not Submit AI-Generated Pull Requests
+
+The same goes for (largely) AI-generated pull requests. These are not welcome as they will be based on copyrighted code from others
+without accreditation and without taking the license of the original code into account, let alone getting permission
+for the use of the code or for re-licensing.
+
+Aside from that, the experience is that AI-generated pull requests will be incorrect 100% of the time and cost reviewers too much time.
+Submitting a (largely) AI-generated pull request will lead to you being banned from this project and repository.
+
+
## Write bug reports with detail, background, and sample code
[This is an example](https://github.com/sebastianbergmann/phpunit/issues/4376) of a bug report I wrote, and I think it's not too bad.
@@ -29,13 +50,17 @@ In your bug report, please provide the following:
* What actually happens
* Notes (possibly including why you think this might be happening, or stuff you tried that didn't work)
+Please do not report a bug for a version of this library that is no longer supported. Please do not report a bug if you are using a version of PHP that is not supported by the version of this library you are using.
+
+The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. Support for this library follows the [support for the version of PHPUnit that uses a specific version of this library](https://phpunit.de/supported-versions.html).
+
Please post code and output as text ([using proper markup](https://guides.github.com/features/mastering-markdown/)). Do not post screenshots of code or output.
## Workflow for Pull Requests
1. Fork the repository.
-2. Create your branch from the oldest branch that is affected by the bug you plan to fix.
+2. Create your branch from `main` if you plan to implement new functionality or change existing code significantly; create your branch from the oldest branch that is affected by the bug if you plan to fix a bug.
3. Implement your change and add tests for it.
4. Ensure the test suite passes.
5. Ensure the code complies with our coding guidelines (see below).
@@ -45,6 +70,12 @@ Please make sure you have [set up your username and email address](https://git-s
We encourage you to [sign your Git commits with your GPG key](https://docs.github.com/en/github/authenticating-to-github/signing-commits).
+Pull requests for bug fixes must be made for the oldest branch that is supported (see above). Pull requests for new features must be based on the `main` branch.
+
+We are trying to keep backwards compatibility breaks to an absolute minimum. Please take this into account when proposing changes.
+
+Due to time constraints, we are not always able to respond as quickly as we would like. Please do not take delays personal and feel free to remind us if you feel that we forgot to respond.
+
## Development
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index c2fba0f..b43b838 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1 +1,4 @@
github: sebastianbergmann
+liberapay: sebastianbergmann
+thanks_dev: u/gh/sebastianbergmann
+tidelift: "packagist/phpunit/php-file-iterator"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0e7b768..b1c28ef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,13 +1,13 @@
# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
on:
- - "pull_request"
- - "push"
+ - pull_request
+ - push
-name: "CI"
+name: CI
env:
- COMPOSER_ROOT_VERSION: "5.1.x-dev"
+ COMPOSER_ROOT_VERSION: 6.0.x-dev
permissions:
contents: read
@@ -61,34 +61,37 @@ jobs:
fail-fast: false
matrix:
php-version:
- - "8.2"
- - "8.3"
- - "8.4"
- - "8.5"
+ - 8.3
+ - 8.4
+ - 8.5
steps:
- - name: "Checkout"
- uses: "actions/checkout@v4"
+ - name: Checkout
+ uses: actions/checkout@v4
- - name: "Install PHP with extensions"
- uses: "shivammathur/setup-php@v2"
+ - name: Install PHP with extensions
+ uses: shivammathur/setup-php@v2
with:
- php-version: "${{ matrix.php-version }}"
- coverage: "xdebug"
+ php-version: ${{ matrix.php-version }}
+ coverage: xdebug
- - name: "Install dependencies with Composer"
- run: "./tools/composer update --no-ansi --no-interaction --no-progress"
+ - name: Install dependencies with Composer
+ run: ./tools/composer update --no-ansi --no-interaction --no-progress
- - name: "Run tests with PHPUnit"
- run: "vendor/bin/phpunit --log-junit junit.xml --coverage-clover=coverage.xml"
+ - name: Run tests with PHPUnit
+ run: ./vendor/bin/phpunit --log-junit test-results.xml --coverage-clover=code-coverage.xml
- name: Upload test results to Codecov.io
if: ${{ !cancelled() }}
uses: codecov/test-results-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
+ disable_search: true
+ files: ./test-results.xml
- name: Upload code coverage data to Codecov.io
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
+ disable_search: true
+ files: ./code-coverage.xml
diff --git a/.phive/phars.xml b/.phive/phars.xml
index 79047d2..ce852a9 100644
--- a/.phive/phars.xml
+++ b/.phive/phars.xml
@@ -2,5 +2,4 @@
-
diff --git a/ChangeLog.md b/ChangeLog.md
index bc3d870..a0fa134 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
+## [6.0.0] - 2025-02-07
+
+### Removed
+
+* This component is no longer supported on PHP 8.2
+
## [5.1.0] - 2024-08-27
### Added
@@ -168,6 +174,7 @@ No changes
* [#23](https://github.com/sebastianbergmann/php-file-iterator/pull/23): Added support for wildcards (glob) in exclude
+[6.0.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/5.1...6.0.0
[5.1.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/5.0.1...5.1.0
[5.0.1]: https://github.com/sebastianbergmann/php-file-iterator/compare/5.0.0...5.0.1
[5.0.0]: https://github.com/sebastianbergmann/php-file-iterator/compare/4.1...5.0.0
diff --git a/build.xml b/build.xml
index e6a7804..5277044 100644
--- a/build.xml
+++ b/build.xml
@@ -17,10 +17,27 @@
-
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/composer.json b/composer.json
index 8653c22..528f298 100644
--- a/composer.json
+++ b/composer.json
@@ -21,17 +21,17 @@
},
"config": {
"platform": {
- "php": "8.2.0"
+ "php": "8.3.0"
},
"optimize-autoloader": true,
"sort-packages": true
},
"prefer-stable": true,
"require": {
- "php": ">=8.2"
+ "php": ">=8.3"
},
"require-dev": {
- "phpunit/phpunit": "^11.3"
+ "phpunit/phpunit": "^12.0"
},
"autoload": {
"classmap": [
@@ -40,7 +40,7 @@
},
"extra": {
"branch-alias": {
- "dev-main": "5.1-dev"
+ "dev-main": "6.0-dev"
}
}
}
diff --git a/phpstan.neon b/phpstan.neon
index e9a9e7e..94486a0 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -2,4 +2,46 @@ parameters:
level: 10
paths:
- src
- - tests
+ - tests/unit
+
+ checkTooWideReturnTypesInProtectedAndPublicMethods: true
+ reportAlwaysTrueInLastCondition: true
+ reportPossiblyNonexistentConstantArrayOffset: true
+ reportPossiblyNonexistentGeneralArrayOffset: true
+ treatPhpDocTypesAsCertain: false
+
+ strictRules:
+ allRules: false
+ booleansInConditions: true
+ closureUsesThis: true
+ disallowedBacktick: true
+ disallowedEmpty: true
+ disallowedImplicitArrayCreation: true
+ disallowedLooseComparison: true
+ disallowedShortTernary: true
+ illegalConstructorMethodCall: true
+ matchingInheritedMethodNames: true
+ noVariableVariables: true
+ numericOperandsInArithmeticOperators: true
+ overwriteVariablesWithLoop: true
+ requireParentConstructorCall: true
+ strictFunctionCalls: true
+ switchConditionsMatchingType: true
+ uselessCast: true
+
+ ergebnis:
+ allRules: false
+ final:
+ enabled: true
+ privateInFinalClass:
+ enabled: true
+
+ type_coverage:
+ declare: 100
+ return: 100
+ param: 100
+ property: 100
+ constant: 100
+
+includes:
+ - phar://phpstan.phar/conf/bleedingEdge.neon
diff --git a/phpunit.xml b/phpunit.xml
index 95cb949..de2c11b 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -1,10 +1,8 @@
globstar($path)) {
+ $locals = $this->globstar($path);
+
+ if ($locals !== []) {
$_paths[] = array_map('\realpath', $locals);
} else {
// @codeCoverageIgnoreStart
diff --git a/src/Iterator.php b/src/Iterator.php
index a1c9408..19e8dd3 100644
--- a/src/Iterator.php
+++ b/src/Iterator.php
@@ -24,8 +24,8 @@
*/
final class Iterator extends FilterIterator
{
- public const PREFIX = 0;
- public const SUFFIX = 1;
+ public const int PREFIX = 0;
+ public const int SUFFIX = 1;
private false|string $basePath;
/**
@@ -72,7 +72,7 @@ public function accept(): bool
private function acceptPath(string $path): bool
{
// Filter files in hidden directories by checking path that is relative to the base path.
- if (preg_match('=/\.[^/]*/=', str_replace((string) $this->basePath, '', $path))) {
+ if (preg_match('=/\.[^/]*/=', str_replace((string) $this->basePath, '', $path)) === 1) {
return false;
}
@@ -94,7 +94,7 @@ private function acceptSuffix(string $filename): bool
*/
private function acceptSubString(string $filename, array $subStrings, int $type): bool
{
- if (empty($subStrings)) {
+ if ($subStrings === []) {
return true;
}
diff --git a/tools/.phpstan/composer.json b/tools/.phpstan/composer.json
new file mode 100644
index 0000000..8ef20ff
--- /dev/null
+++ b/tools/.phpstan/composer.json
@@ -0,0 +1,14 @@
+{
+ "require-dev": {
+ "phpstan/phpstan": "^2.1.8",
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan-strict-rules": "^2.0.3",
+ "tomasvotruba/type-coverage": "^2.0.2",
+ "ergebnis/phpstan-rules": "^2.8.0"
+ },
+ "config": {
+ "allow-plugins": {
+ "phpstan/extension-installer": true
+ }
+ }
+}
diff --git a/tools/.phpstan/composer.lock b/tools/.phpstan/composer.lock
new file mode 100644
index 0000000..6c4d892
--- /dev/null
+++ b/tools/.phpstan/composer.lock
@@ -0,0 +1,388 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "7c032feec49c9dbbdaa0d52198cabb4c",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "ergebnis/phpstan-rules",
+ "version": "2.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ergebnis/phpstan-rules.git",
+ "reference": "30e790621fbad05573ef9cd355279fff5122e080"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/30e790621fbad05573ef9cd355279fff5122e080",
+ "reference": "30e790621fbad05573ef9cd355279fff5122e080",
+ "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"
+ },
+ "require-dev": {
+ "doctrine/orm": "^2.20.0 || ^3.3.0",
+ "ergebnis/composer-normalize": "^2.45.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",
+ "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",
+ "phpunit/phpunit": "^9.6.21",
+ "psr/container": "^2.0.2",
+ "symfony/finder": "^5.4.45",
+ "symfony/process": "^5.4.47"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Ergebnis\\PHPStan\\Rules\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Andreas Möller",
+ "email": "am@localheinz.com",
+ "homepage": "https://localheinz.com"
+ }
+ ],
+ "description": "Provides rules for phpstan/phpstan.",
+ "homepage": "https://github.com/ergebnis/phpstan-rules",
+ "keywords": [
+ "PHPStan",
+ "phpstan-rules"
+ ],
+ "support": {
+ "issues": "https://github.com/ergebnis/phpstan-rules/issues",
+ "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"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.0 - 8.4"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "dev-master",
+ "nette/tester": "^2.5",
+ "phpstan/phpstan": "^1.0",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.0.5"
+ },
+ "time": "2024-08-07T15:39:19+00:00"
+ },
+ {
+ "name": "phpstan/extension-installer",
+ "version": "1.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/extension-installer.git",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.9.0 || ^2.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2.0",
+ "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPStan\\ExtensionInstaller\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\ExtensionInstaller\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Composer plugin for automatic installation of PHPStan extensions",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/extension-installer/issues",
+ "source": "https://github.com/phpstan/extension-installer/tree/1.4.3"
+ },
+ "time": "2024-09-04T20:21:43+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
+ "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-09T09:30:48+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-strict-rules",
+ "version": "2.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-strict-rules.git",
+ "reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/8b88b5f818bfa301e0c99154ab622dace071c3ba",
+ "reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0.4"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "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"
+ },
+ "time": "2025-01-21T10:52:14+00:00"
+ },
+ {
+ "name": "tomasvotruba/type-coverage",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/TomasVotruba/type-coverage.git",
+ "reference": "d033429580f2c18bda538fa44f2939236a990e0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c",
+ "reference": "d033429580f2c18bda538fa44f2939236a990e0c",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^3.2 || ^4.0",
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "config/extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "TomasVotruba\\TypeCoverage\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Measure type coverage of your project",
+ "keywords": [
+ "phpstan-extension",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/TomasVotruba/type-coverage/issues",
+ "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/rectorphp",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "time": "2025-01-07T00:10:26+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {},
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {},
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
+}
diff --git a/tools/.phpstan/vendor/autoload.php b/tools/.phpstan/vendor/autoload.php
new file mode 100644
index 0000000..87b8902
--- /dev/null
+++ b/tools/.phpstan/vendor/autoload.php
@@ -0,0 +1,25 @@
+realpath = realpath($opened_path) ?: $opened_path;
+ $opened_path = $this->realpath;
+ $this->handle = fopen($this->realpath, $mode);
+ $this->position = 0;
+
+ return (bool) $this->handle;
+ }
+
+ public function stream_read($count)
+ {
+ $data = fread($this->handle, $count);
+
+ if ($this->position === 0) {
+ $data = preg_replace('{^#!.*\r?\n}', '', $data);
+ }
+
+ $this->position += strlen($data);
+
+ return $data;
+ }
+
+ public function stream_cast($castAs)
+ {
+ return $this->handle;
+ }
+
+ public function stream_close()
+ {
+ fclose($this->handle);
+ }
+
+ public function stream_lock($operation)
+ {
+ return $operation ? flock($this->handle, $operation) : true;
+ }
+
+ public function stream_seek($offset, $whence)
+ {
+ if (0 === fseek($this->handle, $offset, $whence)) {
+ $this->position = ftell($this->handle);
+ return true;
+ }
+
+ return false;
+ }
+
+ public function stream_tell()
+ {
+ return $this->position;
+ }
+
+ public function stream_eof()
+ {
+ return feof($this->handle);
+ }
+
+ public function stream_stat()
+ {
+ return array();
+ }
+
+ public function stream_set_option($option, $arg1, $arg2)
+ {
+ return true;
+ }
+
+ public function url_stat($path, $flags)
+ {
+ $path = substr($path, 17);
+ if (file_exists($path)) {
+ return stat($path);
+ }
+
+ return false;
+ }
+ }
+ }
+
+ if (
+ (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
+ || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
+ ) {
+ return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan');
+ }
+}
+
+return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan';
diff --git a/tools/.phpstan/vendor/bin/phpstan.phar b/tools/.phpstan/vendor/bin/phpstan.phar
new file mode 100755
index 0000000..fecf96f
--- /dev/null
+++ b/tools/.phpstan/vendor/bin/phpstan.phar
@@ -0,0 +1,119 @@
+#!/usr/bin/env php
+realpath = realpath($opened_path) ?: $opened_path;
+ $opened_path = $this->realpath;
+ $this->handle = fopen($this->realpath, $mode);
+ $this->position = 0;
+
+ return (bool) $this->handle;
+ }
+
+ public function stream_read($count)
+ {
+ $data = fread($this->handle, $count);
+
+ if ($this->position === 0) {
+ $data = preg_replace('{^#!.*\r?\n}', '', $data);
+ }
+
+ $this->position += strlen($data);
+
+ return $data;
+ }
+
+ public function stream_cast($castAs)
+ {
+ return $this->handle;
+ }
+
+ public function stream_close()
+ {
+ fclose($this->handle);
+ }
+
+ public function stream_lock($operation)
+ {
+ return $operation ? flock($this->handle, $operation) : true;
+ }
+
+ public function stream_seek($offset, $whence)
+ {
+ if (0 === fseek($this->handle, $offset, $whence)) {
+ $this->position = ftell($this->handle);
+ return true;
+ }
+
+ return false;
+ }
+
+ public function stream_tell()
+ {
+ return $this->position;
+ }
+
+ public function stream_eof()
+ {
+ return feof($this->handle);
+ }
+
+ public function stream_stat()
+ {
+ return array();
+ }
+
+ public function stream_set_option($option, $arg1, $arg2)
+ {
+ return true;
+ }
+
+ public function url_stat($path, $flags)
+ {
+ $path = substr($path, 17);
+ if (file_exists($path)) {
+ return stat($path);
+ }
+
+ return false;
+ }
+ }
+ }
+
+ if (
+ (function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
+ || (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
+ ) {
+ return include("phpvfscomposer://" . __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar');
+ }
+}
+
+return include __DIR__ . '/..'.'/phpstan/phpstan/phpstan.phar';
diff --git a/tools/.phpstan/vendor/composer/ClassLoader.php b/tools/.phpstan/vendor/composer/ClassLoader.php
new file mode 100644
index 0000000..7824d8f
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/ClassLoader.php
@@ -0,0 +1,579 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see https://www.php-fig.org/psr/psr-0/
+ * @see https://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ /** @var \Closure(string):void */
+ private static $includeFile;
+
+ /** @var string|null */
+ private $vendorDir;
+
+ // PSR-4
+ /**
+ * @var array>
+ */
+ private $prefixLengthsPsr4 = array();
+ /**
+ * @var array>
+ */
+ private $prefixDirsPsr4 = array();
+ /**
+ * @var list
+ */
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ /**
+ * List of PSR-0 prefixes
+ *
+ * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
+ *
+ * @var array>>
+ */
+ private $prefixesPsr0 = array();
+ /**
+ * @var list
+ */
+ private $fallbackDirsPsr0 = array();
+
+ /** @var bool */
+ private $useIncludePath = false;
+
+ /**
+ * @var array
+ */
+ private $classMap = array();
+
+ /** @var bool */
+ private $classMapAuthoritative = false;
+
+ /**
+ * @var array
+ */
+ private $missingClasses = array();
+
+ /** @var string|null */
+ private $apcuPrefix;
+
+ /**
+ * @var array
+ */
+ private static $registeredLoaders = array();
+
+ /**
+ * @param string|null $vendorDir
+ */
+ public function __construct($vendorDir = null)
+ {
+ $this->vendorDir = $vendorDir;
+ self::initializeIncludeClosure();
+ }
+
+ /**
+ * @return array>
+ */
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
+ }
+
+ return array();
+ }
+
+ /**
+ * @return array>
+ */
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ /**
+ * @return list
+ */
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ /**
+ * @return list
+ */
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ /**
+ * @return array Array of classname => path
+ */
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ *
+ * @return void
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @return void
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ $paths = (array) $paths;
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param list|string $paths The PSR-0 base directories
+ *
+ * @return void
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param list|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @return void
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ *
+ * @return void
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ *
+ * @return void
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ *
+ * @return void
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ *
+ * @return void
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+
+ if (null === $this->vendorDir) {
+ return;
+ }
+
+ if ($prepend) {
+ self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
+ } else {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ self::$registeredLoaders[$this->vendorDir] = $this;
+ }
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ *
+ * @return void
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+
+ if (null !== $this->vendorDir) {
+ unset(self::$registeredLoaders[$this->vendorDir]);
+ }
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return true|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ $includeFile = self::$includeFile;
+ $includeFile($file);
+
+ return true;
+ }
+
+ return null;
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ /**
+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
+ *
+ * @return array
+ */
+ public static function getRegisteredLoaders()
+ {
+ return self::$registeredLoaders;
+ }
+
+ /**
+ * @param string $class
+ * @param string $ext
+ * @return string|false
+ */
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+
+ /**
+ * @return void
+ */
+ private static function initializeIncludeClosure()
+ {
+ if (self::$includeFile !== null) {
+ return;
+ }
+
+ /**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ *
+ * @param string $file
+ * @return void
+ */
+ self::$includeFile = \Closure::bind(static function($file) {
+ include $file;
+ }, null, null);
+ }
+}
diff --git a/tools/.phpstan/vendor/composer/InstalledVersions.php b/tools/.phpstan/vendor/composer/InstalledVersions.php
new file mode 100644
index 0000000..6d29bff
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/InstalledVersions.php
@@ -0,0 +1,378 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer;
+
+use Composer\Autoload\ClassLoader;
+use Composer\Semver\VersionParser;
+
+/**
+ * This class is copied in every Composer installed project and available to all
+ *
+ * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
+ *
+ * To require its presence, you can require `composer-runtime-api ^2.0`
+ *
+ * @final
+ */
+class InstalledVersions
+{
+ /**
+ * @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}|array{}|null
+ */
+ private static $installed;
+
+ /**
+ * @var bool
+ */
+ private static $installedIsLocalDir;
+
+ /**
+ * @var bool|null
+ */
+ private static $canGetVendors;
+
+ /**
+ * @var array[]
+ * @psalm-var array}>
+ */
+ private static $installedByVendor = array();
+
+ /**
+ * Returns a list of all package names which are present, either by being installed, replaced or provided
+ *
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackages()
+ {
+ $packages = array();
+ foreach (self::getInstalled() as $installed) {
+ $packages[] = array_keys($installed['versions']);
+ }
+
+ if (1 === \count($packages)) {
+ return $packages[0];
+ }
+
+ return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
+ }
+
+ /**
+ * Returns a list of all package names with a specific type e.g. 'library'
+ *
+ * @param string $type
+ * @return string[]
+ * @psalm-return list
+ */
+ public static function getInstalledPackagesByType($type)
+ {
+ $packagesByType = array();
+
+ foreach (self::getInstalled() as $installed) {
+ foreach ($installed['versions'] as $name => $package) {
+ if (isset($package['type']) && $package['type'] === $type) {
+ $packagesByType[] = $name;
+ }
+ }
+ }
+
+ return $packagesByType;
+ }
+
+ /**
+ * Checks whether the given package is installed
+ *
+ * This also returns true if the package name is provided or replaced by another package
+ *
+ * @param string $packageName
+ * @param bool $includeDevRequirements
+ * @return bool
+ */
+ public static function isInstalled($packageName, $includeDevRequirements = true)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (isset($installed['versions'][$packageName])) {
+ return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks whether the given package satisfies a version constraint
+ *
+ * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
+ *
+ * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
+ *
+ * @param VersionParser $parser Install composer/semver to have access to this class and functionality
+ * @param string $packageName
+ * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
+ * @return bool
+ */
+ public static function satisfies(VersionParser $parser, $packageName, $constraint)
+ {
+ $constraint = $parser->parseConstraints((string) $constraint);
+ $provided = $parser->parseConstraints(self::getVersionRanges($packageName));
+
+ return $provided->matches($constraint);
+ }
+
+ /**
+ * Returns a version constraint representing all the range(s) which are installed for a given package
+ *
+ * It is easier to use this via isInstalled() with the $constraint argument if you need to check
+ * whether a given version of a package is installed, and not just whether it exists
+ *
+ * @param string $packageName
+ * @return string Version constraint usable with composer/semver
+ */
+ public static function getVersionRanges($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ $ranges = array();
+ if (isset($installed['versions'][$packageName]['pretty_version'])) {
+ $ranges[] = $installed['versions'][$packageName]['pretty_version'];
+ }
+ if (array_key_exists('aliases', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
+ }
+ if (array_key_exists('replaced', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
+ }
+ if (array_key_exists('provided', $installed['versions'][$packageName])) {
+ $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
+ }
+
+ return implode(' || ', $ranges);
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
+ */
+ public static function getPrettyVersion($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['pretty_version'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['pretty_version'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
+ */
+ public static function getReference($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ if (!isset($installed['versions'][$packageName]['reference'])) {
+ return null;
+ }
+
+ return $installed['versions'][$packageName]['reference'];
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @param string $packageName
+ * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
+ */
+ public static function getInstallPath($packageName)
+ {
+ foreach (self::getInstalled() as $installed) {
+ if (!isset($installed['versions'][$packageName])) {
+ continue;
+ }
+
+ return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
+ }
+
+ throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
+ }
+
+ /**
+ * @return array
+ * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
+ */
+ public static function getRootPackage()
+ {
+ $installed = self::getInstalled();
+
+ return $installed[0]['root'];
+ }
+
+ /**
+ * Returns the raw installed.php data for custom implementations
+ *
+ * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
+ * @return array[]
+ * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}
+ */
+ public static function getRawData()
+ {
+ @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ self::$installed = include __DIR__ . '/installed.php';
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ return self::$installed;
+ }
+
+ /**
+ * Returns the raw data of all installed.php which are currently loaded for custom implementations
+ *
+ * @return array[]
+ * @psalm-return list}>
+ */
+ public static function getAllRawData()
+ {
+ return self::getInstalled();
+ }
+
+ /**
+ * Lets you reload the static array from another file
+ *
+ * This is only useful for complex integrations in which a project needs to use
+ * this class but then also needs to execute another project's autoloader in process,
+ * and wants to ensure both projects have access to their version of installed.php.
+ *
+ * A typical case would be PHPUnit, where it would need to make sure it reads all
+ * the data it needs from this class, then call reload() with
+ * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
+ * the project in which it runs can then also use this class safely, without
+ * interference between PHPUnit's dependencies and the project's dependencies.
+ *
+ * @param array[] $data A vendor/composer/installed.php data set
+ * @return void
+ *
+ * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data
+ */
+ public static function reload($data)
+ {
+ self::$installed = $data;
+ self::$installedByVendor = array();
+
+ // when using reload, we disable the duplicate protection to ensure that self::$installed data is
+ // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
+ // so we have to assume it does not, and that may result in duplicate data being returned when listing
+ // all installed packages for example
+ self::$installedIsLocalDir = false;
+ }
+
+ /**
+ * @return array[]
+ * @psalm-return list}>
+ */
+ private static function getInstalled()
+ {
+ if (null === self::$canGetVendors) {
+ self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
+ }
+
+ $installed = array();
+ $copiedLocalDir = false;
+
+ if (self::$canGetVendors) {
+ $selfDir = strtr(__DIR__, '\\', '/');
+ foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
+ $vendorDir = strtr($vendorDir, '\\', '/');
+ if (isset(self::$installedByVendor[$vendorDir])) {
+ $installed[] = self::$installedByVendor[$vendorDir];
+ } elseif (is_file($vendorDir.'/composer/installed.php')) {
+ /** @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} $required */
+ $required = require $vendorDir.'/composer/installed.php';
+ self::$installedByVendor[$vendorDir] = $required;
+ $installed[] = $required;
+ if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
+ self::$installed = $required;
+ self::$installedIsLocalDir = true;
+ }
+ }
+ if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
+ $copiedLocalDir = true;
+ }
+ }
+ }
+
+ if (null === self::$installed) {
+ // only require the installed.php file if this file is loaded from its dumped location,
+ // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
+ if (substr(__DIR__, -8, 1) !== 'C') {
+ /** @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} $required */
+ $required = require __DIR__ . '/installed.php';
+ self::$installed = $required;
+ } else {
+ self::$installed = array();
+ }
+ }
+
+ if (self::$installed !== array() && !$copiedLocalDir) {
+ $installed[] = self::$installed;
+ }
+
+ return $installed;
+ }
+}
diff --git a/tools/.phpstan/vendor/composer/LICENSE b/tools/.phpstan/vendor/composer/LICENSE
new file mode 100644
index 0000000..f27399a
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/tools/.phpstan/vendor/composer/autoload_classmap.php b/tools/.phpstan/vendor/composer/autoload_classmap.php
new file mode 100644
index 0000000..feb7a8f
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/autoload_classmap.php
@@ -0,0 +1,59 @@
+ $vendorDir . '/composer/InstalledVersions.php',
+ 'Nette\\ArgumentOutOfRangeException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\DeprecatedException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\DirectoryNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\FileNotFoundException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\HtmlStringable' => $vendorDir . '/nette/utils/src/HtmlStringable.php',
+ 'Nette\\IOException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidArgumentException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidStateException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\Iterators\\CachingIterator' => $vendorDir . '/nette/utils/src/Iterators/CachingIterator.php',
+ 'Nette\\Iterators\\Mapper' => $vendorDir . '/nette/utils/src/Iterators/Mapper.php',
+ 'Nette\\Localization\\ITranslator' => $vendorDir . '/nette/utils/src/compatibility.php',
+ 'Nette\\Localization\\Translator' => $vendorDir . '/nette/utils/src/Translator.php',
+ 'Nette\\MemberAccessException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ '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\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php',
+ 'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php',
+ 'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php',
+ 'Nette\\Utils\\ArrayHash' => $vendorDir . '/nette/utils/src/Utils/ArrayHash.php',
+ 'Nette\\Utils\\ArrayList' => $vendorDir . '/nette/utils/src/Utils/ArrayList.php',
+ 'Nette\\Utils\\Arrays' => $vendorDir . '/nette/utils/src/Utils/Arrays.php',
+ 'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php',
+ 'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php',
+ 'Nette\\Utils\\FileInfo' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php',
+ 'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php',
+ 'Nette\\Utils\\Finder' => $vendorDir . '/nette/utils/src/Utils/Finder.php',
+ 'Nette\\Utils\\Floats' => $vendorDir . '/nette/utils/src/Utils/Floats.php',
+ 'Nette\\Utils\\Helpers' => $vendorDir . '/nette/utils/src/Utils/Helpers.php',
+ 'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php',
+ 'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/compatibility.php',
+ 'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php',
+ 'Nette\\Utils\\ImageColor' => $vendorDir . '/nette/utils/src/Utils/ImageColor.php',
+ 'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ImageType' => $vendorDir . '/nette/utils/src/Utils/ImageType.php',
+ 'Nette\\Utils\\Iterables' => $vendorDir . '/nette/utils/src/Utils/Iterables.php',
+ 'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php',
+ 'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php',
+ 'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php',
+ 'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php',
+ 'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php',
+ 'Nette\\Utils\\ReflectionMethod' => $vendorDir . '/nette/utils/src/Utils/ReflectionMethod.php',
+ 'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php',
+ 'Nette\\Utils\\Type' => $vendorDir . '/nette/utils/src/Utils/Type.php',
+ 'Nette\\Utils\\UnknownImageFileException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Validators' => $vendorDir . '/nette/utils/src/Utils/Validators.php',
+);
diff --git a/tools/.phpstan/vendor/composer/autoload_files.php b/tools/.phpstan/vendor/composer/autoload_files.php
new file mode 100644
index 0000000..b62e293
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/autoload_files.php
@@ -0,0 +1,10 @@
+ $vendorDir . '/phpstan/phpstan/bootstrap.php',
+);
diff --git a/tools/.phpstan/vendor/composer/autoload_namespaces.php b/tools/.phpstan/vendor/composer/autoload_namespaces.php
new file mode 100644
index 0000000..15a2ff3
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/autoload_namespaces.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/tomasvotruba/type-coverage/src'),
+ 'PHPStan\\ExtensionInstaller\\' => array($vendorDir . '/phpstan/extension-installer/src'),
+ 'PHPStan\\' => array($vendorDir . '/phpstan/phpstan-strict-rules/src'),
+ 'Ergebnis\\PHPStan\\Rules\\' => array($vendorDir . '/ergebnis/phpstan-rules/src'),
+);
diff --git a/tools/.phpstan/vendor/composer/autoload_real.php b/tools/.phpstan/vendor/composer/autoload_real.php
new file mode 100644
index 0000000..c91f94e
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/autoload_real.php
@@ -0,0 +1,48 @@
+register(true);
+
+ $filesToLoad = \Composer\Autoload\ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$files;
+ $requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
+ if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
+ $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
+
+ require $file;
+ }
+ }, null, null);
+ foreach ($filesToLoad as $fileIdentifier => $file) {
+ $requireFile($fileIdentifier, $file);
+ }
+
+ return $loader;
+ }
+}
diff --git a/tools/.phpstan/vendor/composer/autoload_static.php b/tools/.phpstan/vendor/composer/autoload_static.php
new file mode 100644
index 0000000..041214c
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/autoload_static.php
@@ -0,0 +1,110 @@
+ __DIR__ . '/..' . '/phpstan/phpstan/bootstrap.php',
+ );
+
+ public static $prefixLengthsPsr4 = array (
+ 'T' =>
+ array (
+ 'TomasVotruba\\TypeCoverage\\' => 26,
+ ),
+ 'P' =>
+ array (
+ 'PHPStan\\ExtensionInstaller\\' => 27,
+ 'PHPStan\\' => 8,
+ ),
+ 'E' =>
+ array (
+ 'Ergebnis\\PHPStan\\Rules\\' => 23,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'TomasVotruba\\TypeCoverage\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/tomasvotruba/type-coverage/src',
+ ),
+ 'PHPStan\\ExtensionInstaller\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/phpstan/extension-installer/src',
+ ),
+ 'PHPStan\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/phpstan/phpstan-strict-rules/src',
+ ),
+ 'Ergebnis\\PHPStan\\Rules\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/ergebnis/phpstan-rules/src',
+ ),
+ );
+
+ public static $classMap = array (
+ 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
+ 'Nette\\ArgumentOutOfRangeException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\DeprecatedException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\DirectoryNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\FileNotFoundException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\HtmlStringable' => __DIR__ . '/..' . '/nette/utils/src/HtmlStringable.php',
+ 'Nette\\IOException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidArgumentException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\InvalidStateException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\Iterators\\CachingIterator' => __DIR__ . '/..' . '/nette/utils/src/Iterators/CachingIterator.php',
+ 'Nette\\Iterators\\Mapper' => __DIR__ . '/..' . '/nette/utils/src/Iterators/Mapper.php',
+ 'Nette\\Localization\\ITranslator' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php',
+ 'Nette\\Localization\\Translator' => __DIR__ . '/..' . '/nette/utils/src/Translator.php',
+ 'Nette\\MemberAccessException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ '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\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php',
+ 'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php',
+ 'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
+ 'Nette\\Utils\\ArrayHash' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayHash.php',
+ 'Nette\\Utils\\ArrayList' => __DIR__ . '/..' . '/nette/utils/src/Utils/ArrayList.php',
+ 'Nette\\Utils\\Arrays' => __DIR__ . '/..' . '/nette/utils/src/Utils/Arrays.php',
+ 'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php',
+ 'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php',
+ 'Nette\\Utils\\FileInfo' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileInfo.php',
+ 'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php',
+ 'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/utils/src/Utils/Finder.php',
+ 'Nette\\Utils\\Floats' => __DIR__ . '/..' . '/nette/utils/src/Utils/Floats.php',
+ 'Nette\\Utils\\Helpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/Helpers.php',
+ 'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php',
+ 'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php',
+ 'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php',
+ 'Nette\\Utils\\ImageColor' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageColor.php',
+ 'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ImageType' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageType.php',
+ 'Nette\\Utils\\Iterables' => __DIR__ . '/..' . '/nette/utils/src/Utils/Iterables.php',
+ 'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php',
+ 'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php',
+ 'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php',
+ 'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php',
+ 'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php',
+ 'Nette\\Utils\\ReflectionMethod' => __DIR__ . '/..' . '/nette/utils/src/Utils/ReflectionMethod.php',
+ 'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php',
+ 'Nette\\Utils\\Type' => __DIR__ . '/..' . '/nette/utils/src/Utils/Type.php',
+ 'Nette\\Utils\\UnknownImageFileException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
+ 'Nette\\Utils\\Validators' => __DIR__ . '/..' . '/nette/utils/src/Utils/Validators.php',
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$prefixDirsPsr4;
+ $loader->classMap = ComposerStaticInitf9e7218f71d5874b5632927df4f72bd7::$classMap;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/tools/.phpstan/vendor/composer/installed.json b/tools/.phpstan/vendor/composer/installed.json
new file mode 100644
index 0000000..e7a7c7b
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/installed.json
@@ -0,0 +1,400 @@
+{
+ "packages": [
+ {
+ "name": "ergebnis/phpstan-rules",
+ "version": "2.8.0",
+ "version_normalized": "2.8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ergebnis/phpstan-rules.git",
+ "reference": "30e790621fbad05573ef9cd355279fff5122e080"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ergebnis/phpstan-rules/zipball/30e790621fbad05573ef9cd355279fff5122e080",
+ "reference": "30e790621fbad05573ef9cd355279fff5122e080",
+ "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"
+ },
+ "require-dev": {
+ "doctrine/orm": "^2.20.0 || ^3.3.0",
+ "ergebnis/composer-normalize": "^2.45.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",
+ "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",
+ "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",
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Ergebnis\\PHPStan\\Rules\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Andreas Möller",
+ "email": "am@localheinz.com",
+ "homepage": "https://localheinz.com"
+ }
+ ],
+ "description": "Provides rules for phpstan/phpstan.",
+ "homepage": "https://github.com/ergebnis/phpstan-rules",
+ "keywords": [
+ "PHPStan",
+ "phpstan-rules"
+ ],
+ "support": {
+ "issues": "https://github.com/ergebnis/phpstan-rules/issues",
+ "security": "https://github.com/ergebnis/phpstan-rules/blob/main/.github/SECURITY.md",
+ "source": "https://github.com/ergebnis/phpstan-rules"
+ },
+ "install-path": "../ergebnis/phpstan-rules"
+ },
+ {
+ "name": "nette/utils",
+ "version": "v4.0.5",
+ "version_normalized": "4.0.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/utils.git",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/utils/zipball/736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "reference": "736c567e257dbe0fcf6ce81b4d6dbe05c6899f96",
+ "shasum": ""
+ },
+ "require": {
+ "php": "8.0 - 8.4"
+ },
+ "conflict": {
+ "nette/finder": "<3",
+ "nette/schema": "<1.2.2"
+ },
+ "require-dev": {
+ "jetbrains/phpstorm-attributes": "dev-master",
+ "nette/tester": "^2.5",
+ "phpstan/phpstan": "^1.0",
+ "tracy/tracy": "^2.9"
+ },
+ "suggest": {
+ "ext-gd": "to use Image",
+ "ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
+ "ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
+ "ext-json": "to use Nette\\Utils\\Json",
+ "ext-mbstring": "to use Strings::lower() etc...",
+ "ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
+ },
+ "time": "2024-08-07T15:39:19+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "4.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause",
+ "GPL-2.0-only",
+ "GPL-3.0-only"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "🛠 Nette Utils: lightweight utilities for string & array manipulation, image handling, safe JSON encoding/decoding, validation, slug or strong password generating etc.",
+ "homepage": "https://nette.org",
+ "keywords": [
+ "array",
+ "core",
+ "datetime",
+ "images",
+ "json",
+ "nette",
+ "paginator",
+ "password",
+ "slugify",
+ "string",
+ "unicode",
+ "utf-8",
+ "utility",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/utils/issues",
+ "source": "https://github.com/nette/utils/tree/v4.0.5"
+ },
+ "install-path": "../nette/utils"
+ },
+ {
+ "name": "phpstan/extension-installer",
+ "version": "1.4.3",
+ "version_normalized": "1.4.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/extension-installer.git",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.9.0 || ^2.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2.0",
+ "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
+ },
+ "time": "2024-09-04T20:21:43+00:00",
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPStan\\ExtensionInstaller\\Plugin"
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\ExtensionInstaller\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Composer plugin for automatic installation of PHPStan extensions",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/extension-installer/issues",
+ "source": "https://github.com/phpstan/extension-installer/tree/1.4.3"
+ },
+ "install-path": "../phpstan/extension-installer"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.8",
+ "version_normalized": "2.1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f9adff3b87c03b12cc7e46a30a524648e497758f",
+ "reference": "f9adff3b87c03b12cc7e46a30a524648e497758f",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "time": "2025-03-09T09:30:48+00:00",
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "install-path": "../phpstan/phpstan"
+ },
+ {
+ "name": "phpstan/phpstan-strict-rules",
+ "version": "2.0.3",
+ "version_normalized": "2.0.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-strict-rules.git",
+ "reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/8b88b5f818bfa301e0c99154ab622dace071c3ba",
+ "reference": "8b88b5f818bfa301e0c99154ab622dace071c3ba",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0.4"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "time": "2025-01-21T10:52:14+00:00",
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "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"
+ },
+ "install-path": "../phpstan/phpstan-strict-rules"
+ },
+ {
+ "name": "tomasvotruba/type-coverage",
+ "version": "2.0.2",
+ "version_normalized": "2.0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/TomasVotruba/type-coverage.git",
+ "reference": "d033429580f2c18bda538fa44f2939236a990e0c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/TomasVotruba/type-coverage/zipball/d033429580f2c18bda538fa44f2939236a990e0c",
+ "reference": "d033429580f2c18bda538fa44f2939236a990e0c",
+ "shasum": ""
+ },
+ "require": {
+ "nette/utils": "^3.2 || ^4.0",
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0"
+ },
+ "time": "2025-01-07T00:10:26+00:00",
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "config/extension.neon"
+ ]
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "TomasVotruba\\TypeCoverage\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Measure type coverage of your project",
+ "keywords": [
+ "phpstan-extension",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/TomasVotruba/type-coverage/issues",
+ "source": "https://github.com/TomasVotruba/type-coverage/tree/2.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://www.paypal.me/rectorphp",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
+ }
+ ],
+ "install-path": "../tomasvotruba/type-coverage"
+ }
+ ],
+ "dev": true,
+ "dev-package-names": [
+ "ergebnis/phpstan-rules",
+ "nette/utils",
+ "phpstan/extension-installer",
+ "phpstan/phpstan",
+ "phpstan/phpstan-strict-rules",
+ "tomasvotruba/type-coverage"
+ ]
+}
diff --git a/tools/.phpstan/vendor/composer/installed.php b/tools/.phpstan/vendor/composer/installed.php
new file mode 100644
index 0000000..37d6777
--- /dev/null
+++ b/tools/.phpstan/vendor/composer/installed.php
@@ -0,0 +1,77 @@
+ array(
+ 'name' => '__root__',
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => 'ff662814b51b466b32c93fe10e75bcb7145ef305',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../../',
+ 'aliases' => array(),
+ 'dev' => true,
+ ),
+ 'versions' => array(
+ '__root__' => array(
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => 'ff662814b51b466b32c93fe10e75bcb7145ef305',
+ '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',
+ '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',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../nette/utils',
+ 'aliases' => array(),
+ 'dev_requirement' => true,
+ ),
+ 'phpstan/extension-installer' => array(
+ 'pretty_version' => '1.4.3',
+ 'version' => '1.4.3.0',
+ 'reference' => '85e90b3942d06b2326fba0403ec24fe912372936',
+ 'type' => 'composer-plugin',
+ 'install_path' => __DIR__ . '/../phpstan/extension-installer',
+ 'aliases' => array(),
+ 'dev_requirement' => true,
+ ),
+ 'phpstan/phpstan' => array(
+ 'pretty_version' => '2.1.8',
+ 'version' => '2.1.8.0',
+ 'reference' => 'f9adff3b87c03b12cc7e46a30a524648e497758f',
+ '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',
+ 'type' => 'phpstan-extension',
+ 'install_path' => __DIR__ . '/../phpstan/phpstan-strict-rules',
+ 'aliases' => array(),
+ 'dev_requirement' => true,
+ ),
+ 'tomasvotruba/type-coverage' => array(
+ 'pretty_version' => '2.0.2',
+ 'version' => '2.0.2.0',
+ 'reference' => 'd033429580f2c18bda538fa44f2939236a990e0c',
+ 'type' => 'phpstan-extension',
+ 'install_path' => __DIR__ . '/../tomasvotruba/type-coverage',
+ 'aliases' => array(),
+ 'dev_requirement' => true,
+ ),
+ ),
+);
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md
new file mode 100644
index 0000000..8b8d5ce
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/CHANGELOG.md
@@ -0,0 +1,637 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## Unreleased
+
+For a full diff see [`2.8.0...main`][2.8.0...main].
+
+## [`2.8.0`][2.8.0]
+
+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 `Expressions\NoAssignByReferenceRule`, which reports an error when a variable is assigned by reference ([#914]), by [@localheinz]
+
+## [`2.7.0`][2.7.0]
+
+For a full diff see [`2.6.1...2.7.0`][2.6.1...2.7.0].
+
+### Added
+
+- Added `Closures\NoParameterPassedByReferenceRule`, `Functions\NoParameterPassedByReferenceRule`, `Methods\NoParameterPassedByReferenceRule`, which report an error when a closure, a function, or a method has a parameter that is passed by reference ([#911]), by [@localheinz]
+- Added `Functions\NoReturnByReferenceRule` and `Methods\NoReturnByReferenceRule`, which report an error when a function or a method returns by reference ([#912]), by [@localheinz]
+
+## [`2.6.1`][2.6.1]
+
+For a full diff see [`2.6.0...2.6.1`][2.6.0...2.6.1].
+
+### Fixed
+
+- Adjusted `Methods\NoParameterWithNullableTypeDeclarationRule` to use the appropriate error identifier ([#902]), by [@manuelkiessling]
+
+## [`2.6.0`][2.6.0]
+
+For a full diff see [`2.5.2...2.6.0`][2.5.2...2.6.0].
+
+### Added
+
+- Added support for `phpstan/phpstan:^2.0.0` ([#873]), by [@localheinz]
+
+## [`2.5.2`][2.5.2]
+
+For a full diff see [`2.5.1...2.5.2`][2.5.1...2.5.2].
+
+### Fixed
+
+- Adjusted `Closures\NoNullableReturnTypeDeclarationRule`, `Closures\NoParameterWithNullableTypeDeclarationRule`, `Functions\NoNullableReturnTypeDeclarationRule`, `Functions\NoParameterWithNullableTypeDeclarationRule`, `Methods\NoNullableReturnTypeDeclarationRule`, `Methods\NoParameterWithNullableTypeDeclarationRule` to detect cases where `null` is referenced with incorrect case or relative to the root namespace ([#897]), by [@localheinz]
+
+## [`2.5.1`][2.5.1]
+
+For a full diff see [`2.5.0...2.5.1`][2.5.0...2.5.1].
+
+### Fixed
+
+- Returned rule with error identifier ([#882]), by [@localheinz]
+- Adjusted `Methods\FinalInAbstractClassRule` to ignore Doctrine embeddables and entities ([#396]), by [@localheinz]
+- Adjusted `Expressions\NoCompactRule` to detect usages of `compact()` with incorrect case ([#889]), by [@localheinz]
+- Adjusted `Methods\PrivateInFinalClassRule` to use more appropriate message when detecting a `protected` method in an anonymous class ([#890]), by [@localheinz]
+- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods from traits ([#891]), by [@localheinz]
+- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods with `phpunit/phpunit` attributes requiring methods to be `protected` ([#863]), by [@cosmastech]
+- Adjusted `Methods\PrivateInFinalClassRule` to ignore `protected` methods with `phpunit/phpunit` annotations requiring methods to be `protected` ([#895]), by [@cosmastech]
+
+## [`2.5.0`][2.5.0]
+
+For a full diff see [`2.4.0...2.5.0`][2.4.0...2.5.0].
+
+### Added
+
+- Added rule error identifiers ([#875]), by [@localheinz]
+- Added support for PHP 8.0 ([#877]), by [@localheinz]
+- Added support for PHP 7.4 ([#880]), by [@localheinz]
+
+### Changed
+
+- Removed dependency on `nikic/php-parser` ([#878]), by [@localheinz]
+
+## [`2.4.0`][2.4.0]
+
+For a full diff see [`2.3.0...2.4.0`][2.3.0...2.4.0].
+
+### Added
+
+- Added support for PHP 8.4 ([#872]), by [@localheinz]
+
+## [`2.3.0`][2.3.0]
+
+For a full diff see [`2.2.0...2.3.0`][2.2.0...2.3.0].
+
+### Changed
+
+- Allowed installation on PHP 8.4 ([#862]), by [@localheinz]
+
+## [`2.2.0`][2.2.0]
+
+For a full diff see [`2.1.0...2.2.0`][2.1.0...2.2.0].
+
+### Changed
+
+- Allowed installation of `nikic/php-parser:^5.0.0` ([#735]), by [@localheinz]
+
+## [`2.1.0`][2.1.0]
+
+For a full diff see [`2.0.0...2.1.0`][2.0.0...2.1.0].
+
+### Changed
+
+- Dropped support for PHP 8.0 ([#567]), by [@localheinz]
+- Added support for PHP 8.3 ([#604]), by [@nunomaduro]
+
+## [`2.0.0`][2.0.0]
+
+For a full diff see [`1.0.0...2.0.0`][1.0.0...2.0.0].
+
+### Added
+
+- Added `methodsAllowedToUseContainerTypeDeclarations` parameter to allow configuring a list of method names that are allowed to have container parameter type declarations ([#541), by [@localheinz]
+- Allowed disabling rules ([#542), by [@localheinz]
+- Added support for nullable union types ([#543), by [@localheinz]
+
+### Changed
+
+- Dropped support for PHP 7.2 ([#496]), by [@localheinz]
+- Dropped support for PHP 7.3 ([#498]), by [@localheinz]
+- Dropped support for PHP 7.4 ([#499]), by [@localheinz]
+- Added support for PHP 8.2 ([#540]), by [@localheinz]
+
+### Removed
+
+- Removed `Expressions\NoEmptyRule` ([#525]), by [@enumag]
+
+## [`1.0.0`][1.0.0]
+
+For a full diff see [`0.15.3...1.0.0`][0.15.3...1.0.0].
+
+### Changed
+
+- Added support for `phpstan/phpstan:^1.0.0` and dropped support for non-stable versions of `phpstan/phpstan` ([#381]), by [@rpkamp]
+
+### Fixed
+
+- Adjusted `Classes\FinalRule` to not report an error when a non-final class has a `Doctrinbe\ORM\Mapping\Entity` attribute ([#395]), by [@localheinz]
+
+## [`0.15.3`][0.15.3]
+
+For a full diff see [`0.15.2...0.15.3`][0.15.2...0.15.3].
+
+### Changed
+
+- Allow installation with PHP 8.0 ([#294]), by [@localheinz]
+
+## [`0.15.2`][0.15.2]
+
+For a full diff see [`0.15.1...0.15.2`][0.15.1...0.15.2].
+
+### Changed
+
+- Dropped support for PHP 7.1 ([#259]), by [@localheinz]
+
+## [`0.15.1`][0.15.1]
+
+For a full diff see [`0.15.0...0.15.1`][0.15.0...0.15.1].
+
+### Changed
+
+- Adjusted `Methods\FinalInAbstractClass` rule to allow non-`final` `public` constructors in abstract classes ([#248]), by [@Slamdunk]
+
+## [`0.15.0`][0.15.0]
+
+For a full diff see [`0.14.4...0.15.0`][0.14.4...0.15.0].
+
+### Added
+
+- Added `Classes\PHPUnit\Framework\TestCaseWithSuffixRule`, which reports an error when a concrete class extending `PHPUnit\Framework\TestCase` does not have a `Test` suffix ([#225]), by [@localheinz]
+
+## [`0.14.4`][0.14.4]
+
+For a full diff see [`0.14.3...0.14.4`][0.14.3...0.14.4].
+
+### Fixed
+
+- Ignored classes with `@ORM\Mapping\Entity` annotations in `FinalRule` ([#202]), by [@localheinz]
+
+## [`0.14.3`][0.14.3]
+
+For a full diff see [`0.14.2...0.14.3`][0.14.2...0.14.3].
+
+### Fixed
+
+- Ignored first line in `DeclareStrictTypesRule` when it is a shebang ([#186]), by [@Great-Antique]
+
+## [`0.14.2`][0.14.2]
+
+For a full diff see [`0.14.1...0.14.2`][0.14.1...0.14.2].
+
+### Fixed
+
+- Brought back support for PHP 7.1 ([#166]), by [@localheinz]
+
+## [`0.14.1`][0.14.1]
+
+For a full diff see [`0.14.0...0.14.1`][0.14.0...0.14.1].
+
+### Fixed
+
+- Removed an inappropriate `replace` configuration from `composer.json` ([#161]), by [@localheinz]
+
+## [`0.14.0`][0.14.0]
+
+For a full diff see [`0.13.0...0.14.0`][0.13.0...0.14.0].
+
+### Changed
+
+- Allowed installation of `phpstan/phpstan:~0.12.0` ([#147]), by [@localheinz]
+- Renamed vendor namespace `Localheinz` to `Ergebnis` after move to [@ergebnis] ([#157]), by [@localheinz]
+
+ Run
+
+ ```sh
+ composer remove localheinz/phpstan-rules
+ ```
+
+ and
+
+ ```sh
+ composer require ergebnis/phpstan-rules
+ ```
+
+ to update.
+
+ Run
+
+ ```sh
+ find . -type f -exec sed -i '.bak' 's/Localheinz\\PHPStan/Ergebnis\\PHPStan/g' {} \;
+ ```
+
+ to replace occurrences of `Localheinz\PHPStan` with `Ergebnis\PHPStan`.
+
+ Run
+
+ ```sh
+ find -type f -name '*.bak' -delete
+ ```
+
+ to delete backup files created in the previous step.
+
+- Moved parameters into `ergebnis` section to prevent conflicts with global parameters ([#158]), by [@localheinz]
+
+ Where previously `phpstan.neon` looked like the following
+
+ ```neon
+ parameters:
+ allowAbstractClasses: true
+ classesAllowedToBeExtended: []
+ classesNotRequiredToBeAbstractOrFinal: []
+ interfacesImplementedByContainers:
+ - Psr\Container\ContainerInterface
+ ```
+
+ these parameters now need to be moved into an `ergebnis` section:
+
+ ```diff
+ parameters:
+ - allowAbstractClasses: true
+ - classesAllowedToBeExtended: []
+ - classesNotRequiredToBeAbstractOrFinal: []
+ - interfacesImplementedByContainers:
+ - - Psr\Container\ContainerInterface
+ + ergebnis:
+ + allowAbstractClasses: true
+ + classesAllowedToBeExtended: []
+ + classesNotRequiredToBeAbstractOrFinal: []
+ + interfacesImplementedByContainers:
+ + - Psr\Container\ContainerInterface
+ ```
+
+### Fixed
+
+- Dropped support for PHP 7.1 ([#141]), by [@localheinz]
+
+## [`0.13.0`][0.13.0]
+
+For a full diff see [`0.12.2...0.13.0`][0.12.2...0.13.0].
+
+### Added
+
+- Added `Methods\PrivateInFinalClassRule` which reports an error when a method in a `final` class is `protected` when it could be `private` ([#126]), by [@localheinz]
+
+## [`0.12.2`][0.12.2]
+
+For a full diff see [`0.12.1...0.12.2`][0.12.1...0.12.2].
+
+### Fixed
+
+- Started ignoring interfaces from analysis by `Methods\FinalInAbstractClassRule` to avoid inappropriate errors ([#132]), by [@localheinz]
+
+## [`0.12.1`][0.12.1]
+
+For a full diff see [`0.12.0...0.12.1`][0.12.0...0.12.1].
+
+### Fixed
+
+- Started resolving class name in type declaration before attempting to analyze it in the `Methods\NoParameterWithContainerTypeDeclarationRule` to avoid errors where class `self` is not found ([#128]), by [@localheinz]
+
+## [`0.12.0`][0.12.0]
+
+For a full diff see [`0.11.0...0.12.0`][0.11.0...0.12.0].
+
+### Added
+
+- Added `Methods\NoParameterWithContainerTypeDeclarationRule`, which reports an error when a method has a type declaration that corresponds to a known dependency injection container or service locator ([#122]), by [@localheinz]
+- Added `Methods\FinalInAbstractClassRule`, which reports an error when a concrete `public` or `protected` method in an `abstract` class is not `final` ([#123]), by [@localheinz]
+
+## [`0.11.0`][0.11.0]
+
+For a full diff see [`0.10.0...0.11.0`][0.10.0...0.11.0].
+
+### Added
+
+- Added `Files\DeclareStrictTypesRule`, which reports an error when a PHP file does not have a `declare(strict_types=1)` declaration ([#79]
+- Added `Expressions\NoEmptyRule`, which reports an error when the language construct `empty()` is used ([#110]), by [@localheinz]
+- Added `Expressions\NoEvalRule`, which reports an error when the language construct `eval()` is used ([#112]), by [@localheinz]
+- Added `Expressions\NoErrorSuppressionRule`, which reports an error when `@` is used to suppress errors ([#113]), by [@localheinz]
+- Added `Expressions\NoCompactRule`, which reports an error when the function `compact()` is used ([#116]), by [@localheinz]
+- Added `Statements\NoSwitchRule`, which reports an error when the statement `switch()` is used ([#117]), by [@localheinz]
+
+### Changed
+
+- Require at least `nikic/php-parser:^4.2.3` ([#102]), by [@localheinz]
+- Require at least `phpstan/phpstan:~0.11.15` ([#103]), by [@localheinz]
+
+## [`0.10.0`][0.10.0]
+
+For a full diff see [`0.9.1...0.10.0`][0.9.1...0.10.0].
+
+### Changed
+
+- Require at least `phpstan/phpstan:~0.11.7` ([#91]), by [@localheinz]
+
+### Fixed
+
+- Added missing `parametersSchema` configuration to `rules.neon`, which is required for use with `bleedingEdge.neon`, see [`phpstan/phpstan@54a125d`](https://github.com/phpstan/phpstan/commit/54a125df47fa097b792cb9a3e70c49f753f66b85) ([#93]), by [@localheinz]
+*
+## [`0.9.1`][0.9.1]
+
+For a full diff see [`0.9.0...0.9.1`][0.9.0...0.9.1].
+
+### Changed
+
+- Allow usage with [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer) ([#89]), by [@localheinz]
+
+## [`0.9.0`][0.9.0]
+
+For a full diff see [`0.8.1...0.9.0`][0.8.1...0.9.0].
+
+### Changed
+
+- Adjusted `Classes\FinalRule` to ignore Doctrine entities when they are annotated ([#84]), by [@localheinz]
+
+## [`0.8.1`][0.8.1]
+
+For a full diff see [`0.8.0...0.8.1`][0.8.0...0.8.1].
+
+### Fixed
+
+- Actually enable `Expressions\NoIssetRule` ([#83]), by [@localheinz]
+
+## [`0.8.0`][0.8.0]
+
+For a full diff see [`0.7.1...0.8.0`][0.7.1...0.8.0].
+
+### Added
+
+- Added `Expressions\NoIssetRule`, which reports an error when the language construct `isset()` is used ([#81]), by [@localheinz]
+
+## [`0.7.1`][0.7.1]
+
+For a full diff see [`0.7.0...0.7.1`][0.7.0...0.7.1].
+
+### Changed
+
+- Configured `Classes\NoExtendsRule` to allow a set of default classes to be extended ([#73]), by [@localheinz]
+
+## [`0.7.0`][0.7.0]
+
+For a full diff see [`0.6.0...0.7.0`][0.6.0...0.7.0].
+
+### Added
+
+- Added `Classes\NoExtendsRule`, which reports an error when a class extends a class that is not allowed to be extended ([#68]), by [@localheinz]
+
+## [`0.6.0`][0.6.0]
+
+For a full diff see [`0.5.0...0.6.0`][0.5.0...0.6.0].
+
+### Added
+
+- Allow installation with `phpstan/phpstan:~0.11.0` ([#65]), by [@localheinz]
+
+## [`0.5.0`][0.5.0]
+
+For a full diff see [`0.4.0...0.5.0`][0.4.0...0.5.0].
+
+### Added
+
+- Added `Methods\NoConstructorParameterWithDefaultValueRule`, which reports an error when a constructor of an anonymous class or a class has a parameter with a default value ([#45]), by [@localheinz]
+- Added parameters `$allowAbstractClasses` and `$classesNotRequiredToBeAbstractOrFinal` to allow configuration of `Classes`FinalRule` ([#51]), by [@localheinz]
+
+### Removed
+
+- Removed `Classes\AbstractOrFinalRule` after merging behaviour into `Classes\FinalRule` ([#51]), by [@localheinz]
+- Removed default values from constructor of `Classes\FinalRule` ([#53]), by [@localheinz]
+
+## [`0.4.0`][0.4.0]
+
+For a full diff see [`0.3.0...0.4.0`][0.3.0...0.4.0]
+
+### Changed
+
+- Removed double-quotes from error messages to be more consistent with error messages generated by `phpstan/phstan` ([#39]), by [@localheinz]
+- Modified wording and placement of method, function, and parameter names in error messages to be more consistent with error messages generated by `phpstan/phstan` ([#42]), by [@localheinz]
+
+## [`0.3.0`][0.3.0]
+
+For a full diff see [`0.2.0...0.3.0`][0.2.0...0.3.0]
+
+### Added
+
+- Added `Functions\NoNullableReturnTypeDeclarationRule`, which reports an error when a function has a nullable return type declaration, and `Methods\NoNullableReturnTypeDeclarationRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a nullable return type declaration ([#16]), by [@localheinz]
+- Added `Closures\NoParameterWithNullDefaultValueRule`, which reports an error when a closure has a parameter with `null` as default value ([#26]), by [@localheinz]
+- Added `Closures\NoNullableReturnTypeDeclarationRule`, which reports an error when a closure has a nullable return type declaration ([#29]), by [@localheinz]
+- Added `Functions\NoParameterWithNullDefaultValueRule`, which reports an error when a function has a parameter with `null` as default value ([#31]), by [@localheinz]
+- Added `Methods\NoParameterWithNullDefaultValueRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with `null` as default value ([#32]), by [@localheinz]
+- Added `Closures\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a closure has a parameter with a nullable type declaration ([#33]), by [@localheinz]
+- Added `Functions\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a function has a parameter with a nullable type declaration ([#34]), by [@localheinz]
+- Added `Methods\NoParameterWithNullableTypeDeclarationRule`, which reports an error when a method declared in an anonymous class, a class, or an interface has a parameter with a nullable type declaration ([#35]), by [@localheinz]
+- Extracted `rules.neon`, so you can easily enable all rules by including it in your `phpstan.neon` ([#37]), by [@localheinz]
+
+## [`0.2.0`][0.2.0]
+
+For a full diff see [`0.1.0...0.2.0`][0.1.0...0.2.0]
+
+### Added
+
+- Added `Classes\FinalRule`, which reports an error when a non-anonymous class is not `final`, ([#4]), by [@localheinz]
+
+### Changed
+
+- Added an `$excludeClassNames` argument to the constructors of `Classes\AbstractOrFinalRule` and `Classes\FinalRule` to allow whitelisting of classes, ([#11]), by [@localheinz]
+
+## [`0.1.0`][0.1.0]
+
+For a full diff see [`362c7ea...0.1.0`][362c7ea...0.1.0].
+
+### Added
+
+- Added `Classes\AbstractOrFinalRule`, which reports an error when a non-anonymous class is neither `abstract` nor `final`, ([#1]), by [@localheinz]
+
+[0.1.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.1.0
+[0.2.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.2.0
+[0.3.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.3.0
+[0.4.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.4.0
+[0.5.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.5.0
+[0.6.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.6.0
+[0.7.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.7.0
+[0.7.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.7.1
+[0.8.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.8.0
+[0.8.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.8.1
+[0.9.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.9.0
+[0.9.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.9.1
+[0.10.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.10.0
+[0.11.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.11.0
+[0.12.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.0
+[0.12.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.1
+[0.12.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.12.2
+[0.13.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.13.0
+[0.14.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.0
+[0.14.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.1
+[0.14.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.2
+[0.14.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.3
+[0.14.4]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.14.4
+[0.15.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.0
+[0.15.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.1
+[0.15.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.2
+[0.15.3]: https://github.com/ergebnis/phpstan-rules/releases/tag/0.15.3
+[1.0.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/1.0.0
+[2.0.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.0.0
+[2.1.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.1.0
+[2.2.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.2.0
+[2.3.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.3.0
+[2.4.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.4.0
+[2.5.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.0
+[2.5.1]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.1
+[2.5.2]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.5.2
+[2.6.0]: https://github.com/ergebnis/phpstan-rules/releases/tag/2.6.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
+
+[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
+[0.2.0...0.3.0]: https://github.com/ergebnis/phpstan-rules/compare/0.2.0...0.3.0
+[0.3.0...0.4.0]: https://github.com/ergebnis/phpstan-rules/compare/0.3.0...0.4.0
+[0.4.0...0.5.0]: https://github.com/ergebnis/phpstan-rules/compare/0.4.0...0.5.0
+[0.5.0...0.6.0]: https://github.com/ergebnis/phpstan-rules/compare/0.5.0...0.6.0
+[0.6.0...0.7.0]: https://github.com/ergebnis/phpstan-rules/compare/0.6.0...0.7.0
+[0.7.0...0.7.1]: https://github.com/ergebnis/phpstan-rules/compare/0.7.0...0.7.1
+[0.7.1...0.8.0]: https://github.com/ergebnis/phpstan-rules/compare/0.7.1...0.8.0
+[0.8.0...0.8.1]: https://github.com/ergebnis/phpstan-rules/compare/0.8.0...0.8.1
+[0.8.1...0.9.0]: https://github.com/ergebnis/phpstan-rules/compare/0.8.1...0.9.0
+[0.9.0...0.9.1]: https://github.com/ergebnis/phpstan-rules/compare/0.9.0...0.9.1
+[0.9.1...0.10.0]: https://github.com/ergebnis/phpstan-rules/compare/0.9.1...0.10.0
+[0.10.0...0.11.0]: https://github.com/ergebnis/phpstan-rules/compare/0.10.0...0.11.0
+[0.11.0...0.12.0]: https://github.com/ergebnis/phpstan-rules/compare/0.11.0...0.12.0
+[0.12.0...0.12.1]: https://github.com/ergebnis/phpstan-rules/compare/0.12.0...0.12.1
+[0.12.1...0.12.2]: https://github.com/ergebnis/phpstan-rules/compare/0.12.1...0.12.2
+[0.12.2...0.13.0]: https://github.com/ergebnis/phpstan-rules/compare/0.12.2...0.13.0
+[0.13.0...0.14.0]: https://github.com/ergebnis/phpstan-rules/compare/0.13.0...0.14.0
+[0.14.0...0.14.1]: https://github.com/ergebnis/phpstan-rules/compare/0.14.0...0.14.1
+[0.14.1...0.14.2]: https://github.com/ergebnis/phpstan-rules/compare/0.14.1...0.14.2
+[0.14.2...0.14.3]: https://github.com/ergebnis/phpstan-rules/compare/0.14.2...0.14.3
+[0.14.3...0.14.4]: https://github.com/ergebnis/phpstan-rules/compare/0.14.3...0.14.4
+[0.14.4...0.15.0]: https://github.com/ergebnis/phpstan-rules/compare/0.14.4...0.15.0
+[0.15.0...0.15.1]: https://github.com/ergebnis/phpstan-rules/compare/0.15.0...0.15.1
+[0.15.1...0.15.2]: https://github.com/ergebnis/phpstan-rules/compare/0.15.1...0.15.2
+[0.15.2...0.15.3]: https://github.com/ergebnis/phpstan-rules/compare/0.15.2...0.15.3
+[0.15.3...1.0.0]: https://github.com/ergebnis/phpstan-rules/compare/0.15.3...1.0.0
+[1.0.0...2.0.0]: https://github.com/ergebnis/phpstan-rules/compare/1.0.0...2.0.0
+[2.0.0...2.1.0]: https://github.com/ergebnis/phpstan-rules/compare/2.0.0...2.1.0
+[2.1.0...2.2.0]: https://github.com/ergebnis/phpstan-rules/compare/2.1.0...2.2.0
+[2.2.0...2.3.0]: https://github.com/ergebnis/phpstan-rules/compare/2.2.0...2.3.0
+[2.3.0...2.4.0]: https://github.com/ergebnis/phpstan-rules/compare/2.3.0...2.4.0
+[2.4.0...2.5.0]: https://github.com/ergebnis/phpstan-rules/compare/2.4.0...2.5.0
+[2.5.0...2.5.1]: https://github.com/ergebnis/phpstan-rules/compare/2.5.0...2.5.1
+[2.5.1...2.5.2]: https://github.com/ergebnis/phpstan-rules/compare/2.5.1...2.5.2
+[2.5.2...2.6.0]: https://github.com/ergebnis/phpstan-rules/compare/2.5.2...2.6.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
+
+[#1]: https://github.com/ergebnis/phpstan-rules/pull/1
+[#4]: https://github.com/ergebnis/phpstan-rules/pull/4
+[#11]: https://github.com/ergebnis/phpstan-rules/pull/11
+[#16]: https://github.com/ergebnis/phpstan-rules/pull/16
+[#26]: https://github.com/ergebnis/phpstan-rules/pull/26
+[#29]: https://github.com/ergebnis/phpstan-rules/pull/29
+[#31]: https://github.com/ergebnis/phpstan-rules/pull/31
+[#32]: https://github.com/ergebnis/phpstan-rules/pull/32
+[#33]: https://github.com/ergebnis/phpstan-rules/pull/33
+[#34]: https://github.com/ergebnis/phpstan-rules/pull/34
+[#35]: https://github.com/ergebnis/phpstan-rules/pull/35
+[#37]: https://github.com/ergebnis/phpstan-rules/pull/37
+[#39]: https://github.com/ergebnis/phpstan-rules/pull/39
+[#42]: https://github.com/ergebnis/phpstan-rules/pull/42
+[#45]: https://github.com/ergebnis/phpstan-rules/pull/45
+[#51]: https://github.com/ergebnis/phpstan-rules/pull/51
+[#53]: https://github.com/ergebnis/phpstan-rules/pull/53
+[#65]: https://github.com/ergebnis/phpstan-rules/pull/65
+[#68]: https://github.com/ergebnis/phpstan-rules/pull/68
+[#73]: https://github.com/ergebnis/phpstan-rules/pull/73
+[#79]: https://github.com/ergebnis/phpstan-rules/pull/79
+[#81]: https://github.com/ergebnis/phpstan-rules/pull/81
+[#83]: https://github.com/ergebnis/phpstan-rules/pull/83
+[#84]: https://github.com/ergebnis/phpstan-rules/pull/84
+[#89]: https://github.com/ergebnis/phpstan-rules/pull/89
+[#91]: https://github.com/ergebnis/phpstan-rules/pull/91
+[#93]: https://github.com/ergebnis/phpstan-rules/pull/93
+[#102]: https://github.com/ergebnis/phpstan-rules/pull/102
+[#103]: https://github.com/ergebnis/phpstan-rules/pull/103
+[#110]: https://github.com/ergebnis/phpstan-rules/pull/110
+[#112]: https://github.com/ergebnis/phpstan-rules/pull/112
+[#113]: https://github.com/ergebnis/phpstan-rules/pull/113
+[#116]: https://github.com/ergebnis/phpstan-rules/pull/116
+[#117]: https://github.com/ergebnis/phpstan-rules/pull/117
+[#122]: https://github.com/ergebnis/phpstan-rules/pull/122
+[#123]: https://github.com/ergebnis/phpstan-rules/pull/123
+[#126]: https://github.com/ergebnis/phpstan-rules/pull/126
+[#128]: https://github.com/ergebnis/phpstan-rules/pull/128
+[#132]: https://github.com/ergebnis/phpstan-rules/pull/132
+[#141]: https://github.com/ergebnis/phpstan-rules/pull/141
+[#147]: https://github.com/ergebnis/phpstan-rules/pull/147
+[#157]: https://github.com/ergebnis/phpstan-rules/pull/157
+[#158]: https://github.com/ergebnis/phpstan-rules/pull/158
+[#161]: https://github.com/ergebnis/phpstan-rules/pull/161
+[#166]: https://github.com/ergebnis/phpstan-rules/pull/166
+[#186]: https://github.com/ergebnis/phpstan-rules/pull/186
+[#202]: https://github.com/ergebnis/phpstan-rules/pull/202
+[#225]: https://github.com/ergebnis/phpstan-rules/pull/225
+[#248]: https://github.com/ergebnis/phpstan-rules/pull/248
+[#259]: https://github.com/ergebnis/phpstan-rules/pull/259
+[#294]: https://github.com/ergebnis/phpstan-rules/pull/294
+[#381]: https://github.com/ergebnis/phpstan-rules/pull/381
+[#395]: https://github.com/ergebnis/phpstan-rules/pull/395
+[#396]: https://github.com/ergebnis/phpstan-rules/pull/396
+[#496]: https://github.com/ergebnis/phpstan-rules/pull/496
+[#498]: https://github.com/ergebnis/phpstan-rules/pull/498
+[#499]: https://github.com/ergebnis/phpstan-rules/pull/498
+[#525]: https://github.com/ergebnis/phpstan-rules/pull/525
+[#540]: https://github.com/ergebnis/phpstan-rules/pull/540
+[#541]: https://github.com/ergebnis/phpstan-rules/pull/541
+[#542]: https://github.com/ergebnis/phpstan-rules/pull/542
+[#543]: https://github.com/ergebnis/phpstan-rules/pull/543
+[#567]: https://github.com/ergebnis/phpstan-rules/pull/567
+[#735]: https://github.com/ergebnis/phpstan-rules/pull/735
+[#862]: https://github.com/ergebnis/phpstan-rules/pull/862
+[#863]: https://github.com/ergebnis/phpstan-rules/pull/863
+[#872]: https://github.com/ergebnis/phpstan-rules/pull/872
+[#873]: https://github.com/ergebnis/phpstan-rules/pull/873
+[#875]: https://github.com/ergebnis/phpstan-rules/pull/875
+[#877]: https://github.com/ergebnis/phpstan-rules/pull/877
+[#878]: https://github.com/ergebnis/phpstan-rules/pull/878
+[#880]: https://github.com/ergebnis/phpstan-rules/pull/880
+[#882]: https://github.com/ergebnis/phpstan-rules/pull/882
+[#889]: https://github.com/ergebnis/phpstan-rules/pull/889
+[#890]: https://github.com/ergebnis/phpstan-rules/pull/890
+[#891]: https://github.com/ergebnis/phpstan-rules/pull/891
+[#895]: https://github.com/ergebnis/phpstan-rules/pull/895
+[#897]: https://github.com/ergebnis/phpstan-rules/pull/897
+[#902]: https://github.com/ergebnis/phpstan-rules/pull/902
+[#911]: https://github.com/ergebnis/phpstan-rules/pull/911
+[#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
+
+[@cosmastech]: https://github.com/cosmastech
+[@enumag]: https://github.com/enumag
+[@ergebnis]: https://github.com/ergebnis
+[@Great-Antique]: https://github.com/Great-Antique
+[@localheinz]: https://github.com/localheinz
+[@manuelkiessling]: https://github.com/manuelkiessling
+[@nunomaduro]: https://github.com/nunomaduro
+[@rpkamp]: https://github.com/rpkamp
+[@Slamdunk]: https://github.com/Slamdunk
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md
new file mode 100644
index 0000000..130e719
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/LICENSE.md
@@ -0,0 +1,16 @@
+# The MIT License (MIT)
+
+Copyright (c) 2018-2025 Andreas Möller
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+documentation files (the _Software_), to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED **AS IS**, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md
new file mode 100644
index 0000000..8296743
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/README.md
@@ -0,0 +1,683 @@
+# phpstan-rules
+
+[](https://github.com/ergebnis/phpstan-rules/actions)
+[](https://github.com/ergebnis/phpstan-rules/actions)
+[](https://github.com/ergebnis/phpstan-rules/actions)
+[](https://github.com/ergebnis/phpstan-rules/actions)
+
+[](https://codecov.io/gh/ergebnis/phpstan-rules)
+
+[](https://packagist.org/packages/ergebnis/phpstan-rules)
+[](https://packagist.org/packages/ergebnis/phpstan-rules)
+[](https://packagist.org/packages/ergebnis/phpstan-rules)
+
+This project provides a [`composer`](https://getcomposer.org) package with rules for [`phpstan/phpstan`](https://github.com/phpstan/phpstan).
+
+## Installation
+
+Run
+
+```sh
+composer require --dev ergebnis/phpstan-rules
+```
+
+## Usage
+
+All of the [rules](https://github.com/ergebnis/phpstan-rules#rules) provided (and used) by this library are included in [`rules.neon`](rules.neon).
+
+When you are using [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer), `rules.neon` will be automatically included.
+
+Otherwise you need to include `rules.neon` in your `phpstan.neon`:
+
+```neon
+includes:
+ - vendor/ergebnis/phpstan-rules/rules.neon
+```
+
+:bulb: You probably want to use these rules on top of the rules provided by:
+
+- [`phpstan/phpstan`](https://github.com/phpstan/phpstan)
+- [`phpstan/phpstan-deprecation-rules`](https://github.com/phpstan/phpstan-deprecation-rules)
+- [`phpstan/phpstan-strict-rules`](https://github.com/phpstan/phpstan-strict-rules)
+
+## Rules
+
+This package provides the following rules for use with [`phpstan/phpstan`](https://github.com/phpstan/phpstan):
+
+- [`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)
+- [`Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnonullablereturntypedeclarationrule)
+- [`Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterpassedbyreferencerule)
+- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnullabletypedeclarationrule)
+- [`Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#closuresnoparameterwithnulldefaultvaluerule)
+- [`Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoassignbyreferencerule)
+- [`Ergebnis\PHPStan\Rules\Expressions\NoCompactRule`](https://github.com/ergebnis/phpstan-rules#expressionsnocompactrule)
+- [`Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoerrorsuppressionrule)
+- [`Ergebnis\PHPStan\Rules\Expressions\NoEvalRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoevalrule)
+- [`Ergebnis\PHPStan\Rules\Expressions\NoIssetRule`](https://github.com/ergebnis/phpstan-rules#expressionsnoissetrule)
+- [`Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule`](https://github.com/ergebnis/phpstan-rules#filesdeclarestricttypesrule)
+- [`Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnonullablereturntypedeclarationrule)
+- [`Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterpassedbyreferencerule)
+- [`Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#functionsnoparameterwithnullabletypedeclarationrule)
+- [`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\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)
+- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithcontainertypedeclarationrule)
+- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnullabletypedeclarationrule)
+- [`Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule`](https://github.com/ergebnis/phpstan-rules#methodsnoparameterwithnulldefaultvaluerule)
+- [`Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule`](https://github.com/ergebnis/phpstan-rules#methodsnoreturnbyreferencerule)
+- [`Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule`](https://github.com/ergebnis/phpstan-rules#methodsprivateinfinalclassrule)
+- [`Ergebnis\PHPStan\Rules\Statements\NoSwitchRule`](https://github.com/ergebnis/phpstan-rules#statementsnoswitchrule)
+
+### Classes
+
+#### `Classes\FinalRule`
+
+This rule reports an error when a non-anonymous class is not `final`.
+
+:bulb: This rule ignores classes that
+
+- use `@Entity`, `@ORM\Entity`, or `@ORM\Mapping\Entity` annotations
+- use `Doctrine\ORM\Mapping\Entity` attributes
+
+on the class level.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ final:
+ enabled: false
+```
+
+##### Disallowing `abstract` classes
+
+By default, this rule allows to declare `abstract` classes.
+
+You can set the `allowAbstractClasses` parameter to `false` to disallow abstract classes.
+
+```neon
+parameters:
+ ergebnis:
+ final:
+ allowAbstractClasses: false
+```
+
+##### Excluding classes from inspection
+
+You can set the `classesNotRequiredToBeAbstractOrFinal` parameter to a list of class names that you want to exclude from inspection.
+
+```neon
+parameters:
+ ergebnis:
+ final:
+ classesNotRequiredToBeAbstractOrFinal:
+ - Foo\Bar\NeitherAbstractNorFinal
+ - Bar\Baz\NeitherAbstractNorFinal
+```
+
+#### `Classes\NoExtendsRule`
+
+This rule reports an error when a class extends another class.
+
+##### Defaults
+
+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)
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noExtends:
+ enabled: false
+```
+
+##### Allowing classes to be extended
+
+You can set the `classesAllowedToBeExtended` parameter to a list of class names that you want to allow to be extended.
+
+```neon
+parameters:
+ ergebnis:
+ noExtends:
+ classesAllowedToBeExtended:
+ - Ergebnis\PHPStan\Rules\Test\Integration\AbstractTestCase
+ - Ergebnis\PHPStan\Rules\Test\Integration\AbstractTestCase
+```
+
+#### `Classes\PHPUnit\Framework\TestCaseWithSuffixRule`
+
+This rule reports an error when a concrete class is a sub-class of `PHPUnit\Framework\TestCase` but does not have a `Test` suffix.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ testCaseWithSuffix:
+ enabled: false
+```
+
+### Closures
+
+#### `Closures\NoNullableReturnTypeDeclarationRule`
+
+This rule reports an error when a closure uses a nullable return type declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noNullableReturnTypeDeclaration:
+ enabled: false
+```
+
+#### `Closures\NoParameterPassedByReferenceRule`
+
+This rule reports an error when a closure has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php).
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterPassedByReference:
+ enabled: false
+```
+
+#### `Closures\NoParameterWithNullableTypeDeclarationRule`
+
+This rule reports an error when a closure has a parameter with a nullable type declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithNullableTypeDeclaration:
+ enabled: false
+```
+
+#### `Closures\NoParameterWithNullDefaultValueRule`
+
+This rule reports an error when a closure has a parameter with `null` as default value.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithNullDefaultValue:
+ enabled: false
+```
+
+### Expressions
+
+#### `Expressions\NoAssignByReferenceRule`
+
+This rule reports an error when [a variable is assigned by reference](https://www.php.net/manual/en/language.references.whatdo.php#language.references.whatdo.assign).
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noAssignByReference:
+ enabled: false
+```
+
+#### `Expressions\NoCompactRule`
+
+This rule reports an error when the function [`compact()`](https://www.php.net/compact) is used.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noCompact:
+ 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.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noErrorSuppression:
+ enabled: false
+```
+
+#### `Expressions\NoIssetRule`
+
+This rule reports an error when the language construct [`isset()`](https://www.php.net/isset) is used.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noIsset:
+ enabled: false
+```
+
+### Files
+
+#### `Files\DeclareStrictTypesRule`
+
+This rule reports an error when a non-empty file does not contain a `declare(strict_types=1)` declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ declareStrictTypes:
+ enabled: false
+```
+
+### Functions
+
+#### `Functions\NoNullableReturnTypeDeclarationRule`
+
+This rule reports an error when a function uses a nullable return type declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noNullableReturnTypeDeclaration:
+ enabled: false
+```
+
+#### `Functions\NoParameterPassedByReferenceRule`
+
+This rule reports an error when a function has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php).
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterPassedByReference:
+ enabled: false
+```
+
+#### `Functions\NoParameterWithNullableTypeDeclarationRule`
+
+This rule reports an error when a function has a parameter with a nullable type declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithNullableTypeDeclaration:
+ enabled: false
+```
+
+#### `Functions\NoParameterWithNullDefaultValueRule`
+
+This rule reports an error when a function has a parameter with `null` as default value.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithNullDefaultValue:
+ enabled: false
+```
+
+#### `Functions\NoReturnByReferenceRule`
+
+This rule reports an error when a function [returns by reference](https://www.php.net/manual/en/language.references.return.php).
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noReturnByReference:
+ enabled: false
+```
+
+### Methods
+
+#### `Methods\FinalInAbstractClassRule`
+
+This rule reports an error when a concrete `public` or `protected `method in an `abstract` class is not `final`.
+
+:bulb: This rule ignores
+
+- Doctrine embeddables
+- Doctrine entities
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ finalInAbstractClass:
+ enabled: false
+```
+
+#### `Methods\NoConstructorParameterWithDefaultValueRule`
+
+This rule reports an error when a constructor declared in
+
+- an anonymous class
+- a class
+
+has a default value.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noConstructorParameterWithDefaultValue:
+ enabled: false
+```
+
+#### `Methods\NoParameterPassedByReferenceRule`
+
+This rule reports an error when a method has a parameter that is [passed by reference](https://www.php.net/manual/en/language.references.pass.php).
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterPassedByReference:
+ enabled: false
+```
+
+#### `Methods\NoNullableReturnTypeDeclarationRule`
+
+This rule reports an error when a method declared in
+
+- an anonymous class
+- a class
+- an interface
+
+uses a nullable return type declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noNullableReturnTypeDeclaration:
+ enabled: false
+```
+
+#### `Methods\NoParameterWithContainerTypeDeclarationRule`
+
+This rule reports an error when a method has a type declaration for a known dependency injection container or service locator.
+
+##### Defaults
+
+By default, this rule disallows the use of type declarations indicating an implementation of
+
+- [`Psr\Container\ContainerInterface`](https://github.com/php-fig/container/blob/1.0.0/src/ContainerInterface.php)
+
+is expected to be injected into a method.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithContainerTypeDeclaration:
+ enabled: false
+```
+
+##### Configuring container interfaces
+
+You can set the `interfacesImplementedByContainers` parameter to a list of interface names of additional containers and service locators.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithContainerTypeDeclaration:
+ interfacesImplementedByContainers:
+ - Fancy\DependencyInjection\ContainerInterface
+ - Other\ServiceLocatorInterface
+```
+
+##### Configuring methods allowed to use parameters with container type declarations
+
+You can set the `methodsAllowedToUseContainerTypeDeclarations` parameter to a list of method names that are allowed to use parameters with container type declarations.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithContainerTypeDeclaration:
+ methodsAllowedToUseContainerTypeDeclarations:
+ - loadExtension
+```
+
+#### `Methods\NoParameterWithNullableTypeDeclarationRule`
+
+This rule reports an error when a method declared in
+
+- an anonymous class
+- a class
+- an interface
+
+has a parameter with a nullable type declaration.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithNullableTypeDeclaration:
+ enabled: false
+```
+
+#### `Methods\NoParameterWithNullDefaultValueRule`
+
+This rule reports an error when a method declared in
+
+- an anonymous class
+- a class
+- an interface
+
+has a parameter with `null` as default value.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noParameterWithNullDefaultValue:
+ enabled: false
+```
+
+#### `Functions\NoReturnByReferenceRule`
+
+This rule reports an error when a method [returns by reference](https://www.php.net/manual/en/language.references.return.php).
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noReturnByReference:
+ enabled: false
+```
+
+#### `Methods\PrivateInFinalClassRule`
+
+This rule reports an error when a method in a `final` class is `protected` but could be `private`.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ privateInFinalClass:
+ enabled: false
+```
+
+### Statements
+
+#### `Statements\NoSwitchRule`
+
+This rule reports an error when the statement [`switch()`](https://www.php.net/manual/control-structures.switch.php) is used.
+
+##### Disabling the rule
+
+You can set the `enabled` parameter to `false` to disable this rule.
+
+```neon
+parameters:
+ ergebnis:
+ noSwitch:
+ enabled: false
+```
+
+## Disabling all rules
+
+You can disable all rules using the `allRules` configuration parameter:
+
+```neon
+parameters:
+ ergebnis:
+ allRules: false
+```
+
+## Enabling rules one-by-one
+
+If you have disabled all rules using the `allRules` configuration parameter, you can re-enable individual rules with their corresponding configuration parameters:
+
+```neon
+parameters:
+ ergebnis:
+ allRules: false
+ privateInFinalClass:
+ enabled: true
+```
+
+## Changelog
+
+The maintainers of this project record notable changes to this project in a [changelog](CHANGELOG.md).
+
+## Contributing
+
+The maintainers of this project suggest following the [contribution guide](.github/CONTRIBUTING.md).
+
+## Code of Conduct
+
+The maintainers of this project ask contributors to follow the [code of conduct](https://github.com/ergebnis/.github/blob/main/CODE_OF_CONDUCT.md).
+
+## General Support Policy
+
+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).
+
+## PHP Version Support Policy
+
+This project supports PHP versions with [active and security support](https://www.php.net/supported-versions.php).
+
+The maintainers of this project add support for a PHP version following its initial release and drop support for a PHP version when it has reached the end of security support.
+
+## Security Policy
+
+This project has a [security policy](.github/SECURITY.md).
+
+## License
+
+This project uses the [MIT license](LICENSE.md).
+
+## Credits
+
+The method [`FinalRule::isWhitelistedClass()`](src/Classes/FinalRule.php) is inspired by the work on [`FinalClassFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalClassFixer.php) and [`FinalInternalClassFixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalInternalClassFixer.php), contributed by [Dariusz Rumiński](https://github.com/keradus), [Filippo Tessarotto](https://github.com/Slamdunk), and [Spacepossum](https://github.com/SpacePossum) for [`friendsofphp/php-cs-fixer`](https://github.com/FriendsOfPHP/PHP-CS-Fixer) (originally licensed under MIT).
+
+## Social
+
+Follow [@localheinz](https://twitter.com/intent/follow?screen_name=localheinz) and [@ergebnis](https://twitter.com/intent/follow?screen_name=ergebnis) on Twitter.
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json
new file mode 100644
index 0000000..89d6871
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/composer.json
@@ -0,0 +1,76 @@
+{
+ "name": "ergebnis/phpstan-rules",
+ "description": "Provides rules for phpstan/phpstan.",
+ "license": "MIT",
+ "type": "phpstan-extension",
+ "keywords": [
+ "phpstan",
+ "phpstan-rules"
+ ],
+ "authors": [
+ {
+ "name": "Andreas Möller",
+ "email": "am@localheinz.com",
+ "homepage": "https://localheinz.com"
+ }
+ ],
+ "homepage": "https://github.com/ergebnis/phpstan-rules",
+ "support": {
+ "issues": "https://github.com/ergebnis/phpstan-rules/issues",
+ "source": "https://github.com/ergebnis/phpstan-rules",
+ "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",
+ "ext-mbstring": "*",
+ "phpstan/phpstan": "^2.0.0"
+ },
+ "require-dev": {
+ "doctrine/orm": "^2.20.0 || ^3.3.0",
+ "ergebnis/composer-normalize": "^2.45.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",
+ "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",
+ "phpunit/phpunit": "^9.6.21",
+ "psr/container": "^2.0.2",
+ "symfony/finder": "^5.4.45",
+ "symfony/process": "^5.4.47"
+ },
+ "autoload": {
+ "psr-4": {
+ "Ergebnis\\PHPStan\\Rules\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Ergebnis\\PHPStan\\Rules\\Test\\": "test/"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "ergebnis/composer-normalize": true,
+ "infection/extension-installer": true,
+ "phpstan/extension-installer": true
+ },
+ "audit": {
+ "abandoned": "report"
+ },
+ "platform": {
+ "php": "7.4.33"
+ },
+ "preferred-install": "dist",
+ "sort-packages": true
+ },
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon b/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon
new file mode 100644
index 0000000..a81a7e8
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/rules.neon
@@ -0,0 +1,263 @@
+conditionalTags:
+ Ergebnis\PHPStan\Rules\Classes\FinalRule:
+ phpstan.rules.rule: %ergebnis.final.enabled%
+ Ergebnis\PHPStan\Rules\Classes\NoExtendsRule:
+ phpstan.rules.rule: %ergebnis.noExtends.enabled%
+ Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule:
+ phpstan.rules.rule: %ergebnis.testCaseWithSuffix.enabled%
+ Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule:
+ phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled%
+ Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule:
+ phpstan.rules.rule: %ergebnis.noAssignByReference.enabled%
+ Ergebnis\PHPStan\Rules\Expressions\NoCompactRule:
+ phpstan.rules.rule: %ergebnis.noCompact.enabled%
+ Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule:
+ phpstan.rules.rule: %ergebnis.noErrorSuppression.enabled%
+ Ergebnis\PHPStan\Rules\Expressions\NoEvalRule:
+ phpstan.rules.rule: %ergebnis.noEval.enabled%
+ Ergebnis\PHPStan\Rules\Expressions\NoIssetRule:
+ phpstan.rules.rule: %ergebnis.noIsset.enabled%
+ Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule:
+ phpstan.rules.rule: %ergebnis.declareStrictTypes.enabled%
+ Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule:
+ phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled%
+ Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule:
+ phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled%
+ Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule:
+ phpstan.rules.rule: %ergebnis.noReturnByReference.enabled%
+ Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule:
+ phpstan.rules.rule: %ergebnis.finalInAbstractClass.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule:
+ phpstan.rules.rule: %ergebnis.noConstructorParameterWithDefaultValue.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noNullableReturnTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule:
+ phpstan.rules.rule: %ergebnis.noParameterPassedByReference.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noParameterWithContainerTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule:
+ phpstan.rules.rule: %ergebnis.noParameterWithNullableTypeDeclaration.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule:
+ phpstan.rules.rule: %ergebnis.noParameterWithNullDefaultValue.enabled%
+ Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule:
+ phpstan.rules.rule: %ergebnis.noReturnByReference.enabled%
+ Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule:
+ phpstan.rules.rule: %ergebnis.privateInFinalClass.enabled%
+ Ergebnis\PHPStan\Rules\Statements\NoSwitchRule:
+ phpstan.rules.rule: %ergebnis.noSwitch.enabled%
+
+parameters:
+ ergebnis:
+ allRules: true
+ declareStrictTypes:
+ enabled: %ergebnis.allRules%
+ final:
+ allowAbstractClasses: true
+ classesNotRequiredToBeAbstractOrFinal: []
+ enabled: %ergebnis.allRules%
+ finalInAbstractClass:
+ enabled: %ergebnis.allRules%
+ noAssignByReference:
+ enabled: %ergebnis.allRules%
+ noCompact:
+ enabled: %ergebnis.allRules%
+ noConstructorParameterWithDefaultValue:
+ enabled: %ergebnis.allRules%
+ noErrorSuppression:
+ enabled: %ergebnis.allRules%
+ noEval:
+ enabled: %ergebnis.allRules%
+ noExtends:
+ classesAllowedToBeExtended: []
+ enabled: %ergebnis.allRules%
+ noIsset:
+ enabled: %ergebnis.allRules%
+ noNullableReturnTypeDeclaration:
+ enabled: %ergebnis.allRules%
+ noParameterPassedByReference:
+ enabled: %ergebnis.allRules%
+ noParameterWithContainerTypeDeclaration:
+ enabled: %ergebnis.allRules%
+ interfacesImplementedByContainers:
+ - Psr\Container\ContainerInterface
+ methodsAllowedToUseContainerTypeDeclarations: []
+ noParameterWithNullableTypeDeclaration:
+ enabled: %ergebnis.allRules%
+ noParameterWithNullDefaultValue:
+ enabled: %ergebnis.allRules%
+ noReturnByReference:
+ enabled: %ergebnis.allRules%
+ noSwitch:
+ enabled: %ergebnis.allRules%
+ privateInFinalClass:
+ enabled: %ergebnis.allRules%
+ testCaseWithSuffix:
+ enabled: %ergebnis.allRules%
+
+parametersSchema:
+ ergebnis: structure([
+ allRules: bool()
+ declareStrictTypes: structure([
+ enabled: bool(),
+ ])
+ final: structure([
+ allowAbstractClasses: bool()
+ classesNotRequiredToBeAbstractOrFinal: listOf(string())
+ enabled: bool(),
+ ])
+ finalInAbstractClass: structure([
+ enabled: bool(),
+ ])
+ noAssignByReference: structure([
+ enabled: bool(),
+ ])
+ noCompact: structure([
+ enabled: bool(),
+ ])
+ noConstructorParameterWithDefaultValue: structure([
+ enabled: bool(),
+ ])
+ noErrorSuppression: structure([
+ enabled: bool(),
+ ])
+ noExtends: structure([
+ classesAllowedToBeExtended: listOf(string())
+ enabled: bool(),
+ ])
+ noEval: structure([
+ enabled: bool(),
+ ])
+ noIsset: structure([
+ enabled: bool(),
+ ])
+ noNullableReturnTypeDeclaration: structure([
+ enabled: bool(),
+ ])
+ noParameterPassedByReference: structure([
+ enabled: bool(),
+ ])
+ noParameterWithContainerTypeDeclaration: structure([
+ enabled: bool(),
+ interfacesImplementedByContainers: listOf(string())
+ methodsAllowedToUseContainerTypeDeclarations: listOf(string())
+ ])
+ noParameterWithNullableTypeDeclaration: structure([
+ enabled: bool(),
+ ])
+ noParameterWithNullDefaultValue: structure([
+ enabled: bool(),
+ ])
+ noReturnByReference: structure([
+ enabled: bool(),
+ ])
+ noSwitch: structure([
+ enabled: bool(),
+ ])
+ privateInFinalClass: structure([
+ enabled: bool(),
+ ])
+ testCaseWithSuffix: structure([
+ enabled: bool(),
+ ])
+ ])
+
+services:
+ -
+ class: Ergebnis\PHPStan\Rules\Analyzer
+
+ -
+ class: Ergebnis\PHPStan\Rules\Classes\FinalRule
+ arguments:
+ allowAbstractClasses: %ergebnis.final.allowAbstractClasses%
+ classesNotRequiredToBeAbstractOrFinal: %ergebnis.final.classesNotRequiredToBeAbstractOrFinal%
+
+ -
+ class: Ergebnis\PHPStan\Rules\Classes\NoExtendsRule
+ arguments:
+ classesAllowedToBeExtended: %ergebnis.noExtends.classesAllowedToBeExtended%
+
+ -
+ class: Ergebnis\PHPStan\Rules\Classes\PHPUnit\Framework\TestCaseWithSuffixRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Closures\NoNullableReturnTypeDeclarationRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Closures\NoParameterPassedByReferenceRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Closures\NoParameterWithNullableTypeDeclarationRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Expressions\NoAssignByReferenceRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Expressions\NoCompactRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Expressions\NoErrorSuppressionRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Expressions\NoEvalRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Expressions\NoIssetRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Files\DeclareStrictTypesRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Functions\NoNullableReturnTypeDeclarationRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Functions\NoParameterPassedByReferenceRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullableTypeDeclarationRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Functions\NoParameterWithNullDefaultValueRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Functions\NoReturnByReferenceRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\FinalInAbstractClassRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoConstructorParameterWithDefaultValueRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoNullableReturnTypeDeclarationRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoParameterPassedByReferenceRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithContainerTypeDeclarationRule
+ arguments:
+ interfacesImplementedByContainers: %ergebnis.noParameterWithContainerTypeDeclaration.interfacesImplementedByContainers%
+ methodsAllowedToUseContainerTypeDeclarations: %ergebnis.noParameterWithContainerTypeDeclaration.methodsAllowedToUseContainerTypeDeclarations%
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullableTypeDeclarationRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoParameterWithNullDefaultValueRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\NoReturnByReferenceRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Methods\PrivateInFinalClassRule
+
+ -
+ class: Ergebnis\PHPStan\Rules\Statements\NoSwitchRule
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php
new file mode 100644
index 0000000..938cab7
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Analyzer.php
@@ -0,0 +1,68 @@
+default instanceof Node\Expr\ConstFetch) {
+ return false;
+ }
+
+ return 'null' === $parameter->default->name->toLowerString();
+ }
+
+ /**
+ * @param null|Node\ComplexType|Node\Identifier|Node\Name $typeDeclaration
+ */
+ public function isNullableTypeDeclaration($typeDeclaration): bool
+ {
+ if ($typeDeclaration instanceof Node\NullableType) {
+ return true;
+ }
+
+ if ($typeDeclaration instanceof Node\UnionType) {
+ foreach ($typeDeclaration->types as $type) {
+ if (
+ $type instanceof Node\Identifier
+ && 'null' === $type->toLowerString()
+ ) {
+ return true;
+ }
+
+ if (
+ $type instanceof Node\Name\FullyQualified
+ && $type->hasAttribute('originalName')
+ ) {
+ $originalName = $type->getAttribute('originalName');
+
+ if (
+ $originalName instanceof Node\Name
+ && 'null' === $originalName->toLowerString()
+ ) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php
new file mode 100644
index 0000000..937bf0e
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/FinalRule.php
@@ -0,0 +1,162 @@
+
+ */
+final class FinalRule implements Rules\Rule
+{
+ /**
+ * @var list
+ */
+ private static array $whitelistedAnnotations = [
+ 'Entity',
+ 'ORM\Entity',
+ 'ORM\Mapping\Entity',
+ ];
+
+ /**
+ * @var list
+ */
+ private static array $whitelistedAttributes = [
+ ORM\Mapping\Entity::class,
+ ];
+ private bool $allowAbstractClasses;
+
+ /**
+ * @var list
+ */
+ private array $classesNotRequiredToBeAbstractOrFinal;
+ private string $errorMessageTemplate = 'Class %s is not final.';
+
+ /**
+ * @param list $classesNotRequiredToBeAbstractOrFinal
+ */
+ public function __construct(
+ bool $allowAbstractClasses,
+ array $classesNotRequiredToBeAbstractOrFinal
+ ) {
+ $this->allowAbstractClasses = $allowAbstractClasses;
+ $this->classesNotRequiredToBeAbstractOrFinal = \array_map(static function (string $classNotRequiredToBeAbstractOrFinal): string {
+ return $classNotRequiredToBeAbstractOrFinal;
+ }, $classesNotRequiredToBeAbstractOrFinal);
+
+ if ($allowAbstractClasses) {
+ $this->errorMessageTemplate = 'Class %s is neither abstract nor final.';
+ }
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Class_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (!isset($node->namespacedName)) {
+ return [];
+ }
+
+ if (\in_array($node->namespacedName->toString(), $this->classesNotRequiredToBeAbstractOrFinal, true)) {
+ return [];
+ }
+
+ if (
+ $this->allowAbstractClasses
+ && $node->isAbstract()
+ ) {
+ return [];
+ }
+
+ if ($node->isFinal()) {
+ return [];
+ }
+
+ if ($this->hasWhitelistedAnnotation($node)) {
+ return [];
+ }
+
+ if ($this->hasWhitelistedAttribute($node)) {
+ return [];
+ }
+
+ $message = \sprintf(
+ $this->errorMessageTemplate,
+ $node->namespacedName->toString(),
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::final()->toString())
+ ->build(),
+ ];
+ }
+
+ /**
+ * This method is inspired by the work on PhpCsFixer\Fixer\ClassNotation\FinalClassFixer and
+ * PhpCsFixer\Fixer\ClassNotation\FinalInternalClassFixer contributed by Dariusz Rumiński, Filippo Tessarotto, and
+ * Spacepossum for friendsofphp/php-cs-fixer.
+ *
+ * @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalClassFixer.php
+ * @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/blob/2.15/src/Fixer/ClassNotation/FinalInternalClassFixer.php
+ * @see https://github.com/keradus
+ * @see https://github.com/SpacePossum
+ * @see https://github.com/Slamdunk
+ */
+ private function hasWhitelistedAnnotation(Node\Stmt\Class_ $node): bool
+ {
+ $docComment = $node->getDocComment();
+
+ if (!$docComment instanceof Comment\Doc) {
+ return false;
+ }
+
+ $reformattedComment = $docComment->getReformattedText();
+
+ if (\is_int(\preg_match_all('/@(\S+)(?=\s|$)/', $reformattedComment, $matches))) {
+ foreach ($matches[1] as $annotation) {
+ foreach (self::$whitelistedAnnotations as $whitelistedAnnotation) {
+ if (0 === \mb_strpos($annotation, $whitelistedAnnotation)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private function hasWhitelistedAttribute(Node\Stmt\Class_ $node): bool
+ {
+ foreach ($node->attrGroups as $attributeGroup) {
+ foreach ($attributeGroup->attrs as $attribute) {
+ if (\in_array($attribute->name->toString(), self::$whitelistedAttributes, true)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php
new file mode 100644
index 0000000..93ecf7c
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/NoExtendsRule.php
@@ -0,0 +1,98 @@
+
+ */
+final class NoExtendsRule implements Rules\Rule
+{
+ /**
+ * @var list
+ */
+ private static array $defaultClassesAllowedToBeExtended = [
+ 'PHPUnit\\Framework\\TestCase',
+ ];
+
+ /**
+ * @var list
+ */
+ private array $classesAllowedToBeExtended;
+
+ /**
+ * @param list $classesAllowedToBeExtended
+ */
+ public function __construct(array $classesAllowedToBeExtended)
+ {
+ $this->classesAllowedToBeExtended = \array_values(\array_unique(\array_merge(
+ self::$defaultClassesAllowedToBeExtended,
+ \array_map(static function (string $classAllowedToBeExtended): string {
+ /** @var class-string $classAllowedToBeExtended */
+ return $classAllowedToBeExtended;
+ }, $classesAllowedToBeExtended),
+ )));
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Class_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (!$node->extends instanceof Node\Name) {
+ return [];
+ }
+
+ $extendedClassName = $node->extends->toString();
+
+ if (\in_array($extendedClassName, $this->classesAllowedToBeExtended, true)) {
+ return [];
+ }
+
+ if (!isset($node->namespacedName)) {
+ $message = \sprintf(
+ 'Anonymous class is not allowed to extend "%s".',
+ $extendedClassName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noExtends()->toString())
+ ->build(),
+ ];
+ }
+
+ $extendingClassName = $node->namespacedName->toString();
+
+ $message = \sprintf(
+ 'Class "%s" is not allowed to extend "%s".',
+ $extendingClassName,
+ $extendedClassName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noExtends()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php
new file mode 100644
index 0000000..76e96c5
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Classes/PHPUnit/Framework/TestCaseWithSuffixRule.php
@@ -0,0 +1,95 @@
+
+ */
+final class TestCaseWithSuffixRule implements Rules\Rule
+{
+ /**
+ * @var list
+ */
+ private static array $phpunitTestCaseClassNames = [
+ 'PHPUnit\Framework\TestCase',
+ ];
+ private Reflection\ReflectionProvider $reflectionProvider;
+
+ public function __construct(Reflection\ReflectionProvider $reflectionProvider)
+ {
+ $this->reflectionProvider = $reflectionProvider;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Class_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if ($node->isAbstract()) {
+ return [];
+ }
+
+ if (!$node->extends instanceof Node\Name) {
+ return [];
+ }
+
+ if (!isset($node->namespacedName)) {
+ return [];
+ }
+
+ $fullyQualifiedClassName = $node->namespacedName->toString();
+
+ $classReflection = $this->reflectionProvider->getClass($fullyQualifiedClassName);
+
+ $extendedPhpunitTestCaseClassName = '';
+
+ foreach (self::$phpunitTestCaseClassNames as $phpunitTestCaseClassName) {
+ if ($classReflection->isSubclassOf($phpunitTestCaseClassName)) {
+ $extendedPhpunitTestCaseClassName = $phpunitTestCaseClassName;
+
+ break;
+ }
+ }
+
+ if ('' === $extendedPhpunitTestCaseClassName) {
+ return [];
+ }
+
+ if (1 === \preg_match('/Test$/', $fullyQualifiedClassName)) {
+ return [];
+ }
+
+ $message = \sprintf(
+ 'Class %s extends %s, is concrete, but does not have a Test suffix.',
+ $fullyQualifiedClassName,
+ $extendedPhpunitTestCaseClassName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::testCaseWithSuffix()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php
new file mode 100644
index 0000000..6435157
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoNullableReturnTypeDeclarationRule.php
@@ -0,0 +1,53 @@
+
+ */
+final class NoNullableReturnTypeDeclarationRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Expr\Closure::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) {
+ return [];
+ }
+
+ return [
+ Rules\RuleErrorBuilder::message('Closure has a nullable return type declaration.')
+ ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php
new file mode 100644
index 0000000..2c47cb7
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterPassedByReferenceRule.php
@@ -0,0 +1,64 @@
+
+ */
+final class NoParameterPassedByReferenceRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Expr\Closure::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool {
+ return $parameter->byRef;
+ }));
+
+ if (0 === \count($parametersPassedByReference)) {
+ return [];
+ }
+
+ return \array_map(static function (Node\Param $parameterPassedByReference): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterPassedByReference->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Closure has parameter $%s that is passed by reference.',
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString())
+ ->build();
+ }, $parametersPassedByReference);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php
new file mode 100644
index 0000000..94bd146
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullDefaultValueRule.php
@@ -0,0 +1,72 @@
+
+ */
+final class NoParameterWithNullDefaultValueRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Expr\Closure::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool {
+ return $this->analyzer->hasNullDefaultValue($parameter);
+ }));
+
+ if (0 === \count($parametersWithNullDefaultValue)) {
+ return [];
+ }
+
+ return \array_map(static function (Node\Param $parameterWithNullDefaultValue): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullDefaultValue->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Closure has parameter $%s with null as default value.',
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString())
+ ->build();
+ }, $parametersWithNullDefaultValue);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php
new file mode 100644
index 0000000..3cff6ee
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Closures/NoParameterWithNullableTypeDeclarationRule.php
@@ -0,0 +1,72 @@
+
+ */
+final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Expr\Closure::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool {
+ return $this->analyzer->isNullableTypeDeclaration($parameter->type);
+ }));
+
+ if (0 === \count($parametersWithNullableTypeDeclaration)) {
+ return [];
+ }
+
+ return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullableTypeDeclaration->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Closure has parameter $%s with a nullable type declaration.',
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString())
+ ->build();
+ }, $parametersWithNullableTypeDeclaration);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php
new file mode 100644
index 0000000..3fcbb10
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/ErrorIdentifier.php
@@ -0,0 +1,130 @@
+value = $value;
+ }
+
+ public static function declareStrictTypes(): self
+ {
+ return new self('declareStrictTypes');
+ }
+
+ public static function final(): self
+ {
+ return new self('final');
+ }
+
+ public static function finalInAbstractClass(): self
+ {
+ return new self('finalInAbstractClass');
+ }
+
+ public static function noCompact(): self
+ {
+ return new self('noCompact');
+ }
+
+ public static function noConstructorParameterWithDefaultValue(): self
+ {
+ return new self('noConstructorParameterWithDefaultValue');
+ }
+
+ public static function noAssignByReference(): self
+ {
+ return new self('noAssignByReference');
+ }
+
+ public static function noErrorSuppression(): self
+ {
+ return new self('noErrorSuppression');
+ }
+
+ public static function noEval(): self
+ {
+ return new self('noEval');
+ }
+
+ public static function noExtends(): self
+ {
+ return new self('noExtends');
+ }
+
+ public static function noIsset(): self
+ {
+ return new self('noIsset');
+ }
+
+ public static function noParameterPassedByReference(): self
+ {
+ return new self('noParameterPassedByReference');
+ }
+
+ public static function noParameterWithContainerTypeDeclaration(): self
+ {
+ return new self('noParameterWithContainerTypeDeclaration');
+ }
+
+ public static function noParameterWithNullDefaultValue(): self
+ {
+ return new self('noParameterWithNullDefaultValue');
+ }
+
+ public static function noParameterWithNullableTypeDeclaration(): self
+ {
+ return new self('noParameterWithNullableTypeDeclaration');
+ }
+
+ public static function noNullableReturnTypeDeclaration(): self
+ {
+ return new self('noNullableReturnTypeDeclaration');
+ }
+
+ public static function noReturnByReference(): self
+ {
+ return new self('noReturnByReference');
+ }
+
+ public static function noSwitch(): self
+ {
+ return new self('noSwitch');
+ }
+
+ public static function privateInFinalClass(): self
+ {
+ return new self('privateInFinalClass');
+ }
+
+ public static function testCaseWithSuffix(): self
+ {
+ return new self('testCaseWithSuffix');
+ }
+
+ public function toString(): string
+ {
+ return \sprintf(
+ 'ergebnis.%s',
+ $this->value,
+ );
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php
new file mode 100644
index 0000000..ad8cc1e
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoAssignByReferenceRule.php
@@ -0,0 +1,41 @@
+
+ */
+final class NoAssignByReferenceRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Expr\AssignRef::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ return [
+ Rules\RuleErrorBuilder::message('Assign by reference should not be used.')
+ ->identifier(ErrorIdentifier::noAssignByReference()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php
new file mode 100644
index 0000000..cd12ffe
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoCompactRule.php
@@ -0,0 +1,49 @@
+
+ */
+final class NoCompactRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Expr\FuncCall::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (!$node->name instanceof Node\Name) {
+ return [];
+ }
+
+ if ('compact' !== \mb_strtolower($scope->resolveName($node->name))) {
+ return [];
+ }
+
+ return [
+ Rules\RuleErrorBuilder::message('Function compact() should not be used.')
+ ->identifier(ErrorIdentifier::noCompact()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php
new file mode 100644
index 0000000..49c0939
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoErrorSuppressionRule.php
@@ -0,0 +1,41 @@
+
+ */
+final class NoErrorSuppressionRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Expr\ErrorSuppress::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ return [
+ Rules\RuleErrorBuilder::message('Error suppression via "@" should not be used.')
+ ->identifier(ErrorIdentifier::noErrorSuppression()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php
new file mode 100644
index 0000000..b542057
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoEvalRule.php
@@ -0,0 +1,41 @@
+
+ */
+final class NoEvalRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Expr\Eval_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ return [
+ Rules\RuleErrorBuilder::message('Language construct eval() should not be used.')
+ ->identifier(ErrorIdentifier::noEval()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php
new file mode 100644
index 0000000..f1443d6
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Expressions/NoIssetRule.php
@@ -0,0 +1,41 @@
+
+ */
+final class NoIssetRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Expr\Isset_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ return [
+ Rules\RuleErrorBuilder::message('Language construct isset() should not be used.')
+ ->identifier(ErrorIdentifier::noIsset()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php
new file mode 100644
index 0000000..5f9ec11
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Files/DeclareStrictTypesRule.php
@@ -0,0 +1,70 @@
+
+ */
+final class DeclareStrictTypesRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return FileNode::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ $nodes = $node->getNodes();
+
+ if (0 === \count($nodes)) {
+ return [];
+ }
+
+ $firstNode = \array_shift($nodes);
+
+ if (
+ $firstNode instanceof Node\Stmt\InlineHTML
+ && 2 === $firstNode->getEndLine()
+ && 0 === \mb_strpos($firstNode->value, '#!')
+ ) {
+ $firstNode = \array_shift($nodes);
+ }
+
+ if ($firstNode instanceof Node\Stmt\Declare_) {
+ foreach ($firstNode->declares as $declare) {
+ if (
+ 'strict_types' === $declare->key->toLowerString()
+ && $declare->value instanceof Node\Scalar\LNumber
+ && 1 === $declare->value->value
+ ) {
+ return [];
+ }
+ }
+ }
+
+ return [
+ Rules\RuleErrorBuilder::message('File is missing a "declare(strict_types=1)" declaration.')
+ ->identifier(ErrorIdentifier::declareStrictTypes()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php
new file mode 100644
index 0000000..06da4f7
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoNullableReturnTypeDeclarationRule.php
@@ -0,0 +1,62 @@
+
+ */
+final class NoNullableReturnTypeDeclarationRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Function_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (!isset($node->namespacedName)) {
+ return [];
+ }
+
+ if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) {
+ return [];
+ }
+
+ $message = \sprintf(
+ 'Function %s() has a nullable return type declaration.',
+ $node->namespacedName->toString(),
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php
new file mode 100644
index 0000000..e461aa1
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterPassedByReferenceRule.php
@@ -0,0 +1,67 @@
+
+ */
+final class NoParameterPassedByReferenceRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Function_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool {
+ return $parameter->byRef;
+ }));
+
+ if (0 === \count($parametersPassedByReference)) {
+ return [];
+ }
+
+ $functionName = $node->namespacedName;
+
+ return \array_map(static function (Node\Param $parameterPassedByReference) use ($functionName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterPassedByReference->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Function %s() has parameter $%s that is passed by reference.',
+ $functionName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString())
+ ->build();
+ }, $parametersPassedByReference);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php
new file mode 100644
index 0000000..fe82a4e
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullDefaultValueRule.php
@@ -0,0 +1,75 @@
+
+ */
+final class NoParameterWithNullDefaultValueRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Function_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool {
+ return $this->analyzer->hasNullDefaultValue($parameter);
+ }));
+
+ if (0 === \count($parametersWithNullDefaultValue)) {
+ return [];
+ }
+
+ $functionName = $node->namespacedName;
+
+ return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($functionName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullDefaultValue->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Function %s() has parameter $%s with null as default value.',
+ $functionName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString())
+ ->build();
+ }, $parametersWithNullDefaultValue);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php
new file mode 100644
index 0000000..9027553
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoParameterWithNullableTypeDeclarationRule.php
@@ -0,0 +1,75 @@
+
+ */
+final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Function_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool {
+ return $this->analyzer->isNullableTypeDeclaration($parameter->type);
+ }));
+
+ if (0 === \count($parametersWithNullableTypeDeclaration)) {
+ return [];
+ }
+
+ $functionName = $node->namespacedName;
+
+ return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($functionName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullableTypeDeclaration->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Function %s() has parameter $%s with a nullable type declaration.',
+ $functionName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString())
+ ->build();
+ }, $parametersWithNullableTypeDeclaration);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php
new file mode 100644
index 0000000..0efb700
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Functions/NoReturnByReferenceRule.php
@@ -0,0 +1,50 @@
+
+ */
+final class NoReturnByReferenceRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Function_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (false === $node->byRef) {
+ return [];
+ }
+
+ $message = \sprintf(
+ 'Function %s() returns by reference.',
+ $node->namespacedName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noReturnByReference()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php
new file mode 100644
index 0000000..89fc226
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/FinalInAbstractClassRule.php
@@ -0,0 +1,123 @@
+
+ */
+final class FinalInAbstractClassRule implements Rules\Rule
+{
+ private const DOCTRINE_ATTRIBUTE_NAMES = [
+ 'Doctrine\\ORM\\Mapping\\Embeddable',
+ 'Doctrine\\ORM\\Mapping\\Entity',
+ ];
+ private const DOCTRINE_TAG_NAMES = [
+ '@ORM\\Mapping\\Embeddable',
+ '@ORM\\Embeddable',
+ '@Embeddable',
+ '@ORM\\Mapping\\Entity',
+ '@ORM\\Entity',
+ '@Entity',
+ ];
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ /** @var Reflection\ClassReflection $containingClass */
+ $containingClass = $scope->getClassReflection();
+
+ if (self::isDoctrineEntity($containingClass)) {
+ return [];
+ }
+
+ if (!$containingClass->isAbstract()) {
+ return [];
+ }
+
+ if ($containingClass->isInterface()) {
+ return [];
+ }
+
+ if ($node->isAbstract()) {
+ return [];
+ }
+
+ if ($node->isFinal()) {
+ return [];
+ }
+
+ if ($node->isPrivate()) {
+ return [];
+ }
+
+ if ('__construct' === $node->name->name) {
+ return [];
+ }
+
+ $message = \sprintf(
+ 'Method %s::%s() is not final, but since the containing class is abstract, it should be.',
+ $containingClass->getName(),
+ $node->name->toString(),
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::finalInAbstractClass()->toString())
+ ->build(),
+ ];
+ }
+
+ private static function isDoctrineEntity(Reflection\ClassReflection $containingClass): bool
+ {
+ $attributes = $containingClass->getNativeReflection()->getAttributes();
+
+ foreach ($attributes as $attribute) {
+ if (\in_array($attribute->getName(), self::DOCTRINE_ATTRIBUTE_NAMES, true)) {
+ return true;
+ }
+ }
+
+ $resolvedPhpDocBlock = $containingClass->getResolvedPhpDoc();
+
+ if ($resolvedPhpDocBlock instanceof PhpDoc\ResolvedPhpDocBlock) {
+ foreach ($resolvedPhpDocBlock->getPhpDocNodes() as $phpDocNode) {
+ foreach ($phpDocNode->children as $child) {
+ if (!$child instanceof PhpDocParser\Ast\PhpDoc\PhpDocTagNode) {
+ continue;
+ }
+
+ if (\in_array($child->name, self::DOCTRINE_TAG_NAMES, true)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php
new file mode 100644
index 0000000..e8b7e34
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoConstructorParameterWithDefaultValueRule.php
@@ -0,0 +1,99 @@
+
+ */
+final class NoConstructorParameterWithDefaultValueRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if ('__construct' !== $node->name->toLowerString()) {
+ return [];
+ }
+
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithDefaultValue = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool {
+ return self::hasDefaultValue($parameter);
+ }));
+
+ if (0 === \count($parametersWithDefaultValue)) {
+ return [];
+ }
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ return \array_map(static function (Node\Param $parameterWithDefaultValue): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithDefaultValue->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Constructor in anonymous class has parameter $%s with default value.',
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noConstructorParameterWithDefaultValue()->toString())
+ ->build();
+ }, $parametersWithDefaultValue);
+ }
+
+ $className = $classReflection->getName();
+
+ return \array_map(static function (Node\Param $parameterWithDefaultValue) use ($className): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithDefaultValue->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Constructor in %s has parameter $%s with default value.',
+ $className,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noConstructorParameterWithDefaultValue()->toString())
+ ->build();
+ }, $parametersWithDefaultValue);
+ }
+
+ private static function hasDefaultValue(Node\Param $parameter): bool
+ {
+ return null !== $parameter->default;
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php
new file mode 100644
index 0000000..e0e84c9
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoNullableReturnTypeDeclarationRule.php
@@ -0,0 +1,76 @@
+
+ */
+final class NoNullableReturnTypeDeclarationRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (!$this->analyzer->isNullableTypeDeclaration($node->getReturnType())) {
+ return [];
+ }
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ $message = \sprintf(
+ 'Method %s() in anonymous class has a nullable return type declaration.',
+ $node->name->name,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString())
+ ->build(),
+ ];
+ }
+
+ $message = \sprintf(
+ 'Method %s::%s() has a nullable return type declaration.',
+ $classReflection->getName(),
+ $node->name->name,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noNullableReturnTypeDeclaration()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php
new file mode 100644
index 0000000..4c90f69
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterPassedByReferenceRule.php
@@ -0,0 +1,94 @@
+
+ */
+final class NoParameterPassedByReferenceRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersExplicitlyPassedByReference = \array_values(\array_filter($node->params, static function (Node\Param $parameter): bool {
+ return $parameter->byRef;
+ }));
+
+ if (0 === \count($parametersExplicitlyPassedByReference)) {
+ return [];
+ }
+
+ $methodName = $node->name->toString();
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ return \array_map(static function (Node\Param $parameterExplicitlyPassedByReference) use ($methodName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterExplicitlyPassedByReference->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Method %s() in anonymous class has parameter $%s that is passed by reference.',
+ $methodName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString())
+ ->build();
+ }, $parametersExplicitlyPassedByReference);
+ }
+
+ $className = $classReflection->getName();
+
+ return \array_map(static function (Node\Param $parameterExplicitlyPassedByReference) use ($className, $methodName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterExplicitlyPassedByReference->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Method %s::%s() has parameter $%s that is passed by reference.',
+ $className,
+ $methodName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterPassedByReference()->toString())
+ ->build();
+ }, $parametersExplicitlyPassedByReference);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php
new file mode 100644
index 0000000..f64a831
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithContainerTypeDeclarationRule.php
@@ -0,0 +1,179 @@
+
+ */
+final class NoParameterWithContainerTypeDeclarationRule implements Rules\Rule
+{
+ private Reflection\ReflectionProvider $reflectionProvider;
+
+ /**
+ * @var list
+ */
+ private array $interfacesImplementedByContainers;
+
+ /**
+ * @var list
+ */
+ private array $methodsAllowedToUseContainerTypeDeclarations;
+
+ /**
+ * @param list $interfacesImplementedByContainers
+ * @param list $methodsAllowedToUseContainerTypeDeclarations
+ */
+ public function __construct(
+ Reflection\ReflectionProvider $reflectionProvider,
+ array $interfacesImplementedByContainers,
+ array $methodsAllowedToUseContainerTypeDeclarations
+ ) {
+ $this->reflectionProvider = $reflectionProvider;
+ $this->interfacesImplementedByContainers = \array_values(\array_filter(
+ \array_map(static function (string $interfaceImplementedByContainers): string {
+ return $interfaceImplementedByContainers;
+ }, $interfacesImplementedByContainers),
+ static function (string $interfaceImplementedByContainer): bool {
+ return \interface_exists($interfaceImplementedByContainer);
+ },
+ ));
+ $this->methodsAllowedToUseContainerTypeDeclarations = $methodsAllowedToUseContainerTypeDeclarations;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($this->interfacesImplementedByContainers)) {
+ return [];
+ }
+
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $methodName = $node->name->toString();
+
+ if (\in_array($methodName, $this->methodsAllowedToUseContainerTypeDeclarations, true)) {
+ return [];
+ }
+
+ /** @var Reflection\ClassReflection $containingClass */
+ $containingClass = $scope->getClassReflection();
+
+ return \array_values(\array_reduce(
+ $node->params,
+ function (array $errors, Node\Param $node) use ($scope, $containingClass, $methodName): array {
+ $type = $node->type;
+
+ if (!$type instanceof Node\Name) {
+ return $errors;
+ }
+
+ /** @var Node\Expr\Variable $variable */
+ $variable = $node->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $classUsedInTypeDeclaration = $this->reflectionProvider->getClass($scope->resolveName($type));
+
+ if ($classUsedInTypeDeclaration->isInterface()) {
+ foreach ($this->interfacesImplementedByContainers as $interfaceImplementedByContainer) {
+ if ($classUsedInTypeDeclaration->getName() === $interfaceImplementedByContainer) {
+ $errors[] = self::createError(
+ $containingClass,
+ $methodName,
+ $parameterName,
+ $classUsedInTypeDeclaration,
+ );
+
+ return $errors;
+ }
+
+ if ($classUsedInTypeDeclaration->getNativeReflection()->isSubclassOf($interfaceImplementedByContainer)) {
+ $errors[] = self::createError(
+ $containingClass,
+ $methodName,
+ $parameterName,
+ $classUsedInTypeDeclaration,
+ );
+
+ return $errors;
+ }
+ }
+ }
+
+ foreach ($this->interfacesImplementedByContainers as $interfaceImplementedByContainer) {
+ if ($classUsedInTypeDeclaration->getNativeReflection()->implementsInterface($interfaceImplementedByContainer)) {
+ $errors[] = self::createError(
+ $containingClass,
+ $methodName,
+ $parameterName,
+ $classUsedInTypeDeclaration,
+ );
+
+ return $errors;
+ }
+ }
+
+ return $errors;
+ },
+ [],
+ ));
+ }
+
+ private static function createError(
+ Reflection\ClassReflection $classReflection,
+ string $methodName,
+ string $parameterName,
+ Reflection\ClassReflection $classUsedInTypeDeclaration
+ ): Rules\RuleError {
+ if ($classReflection->isAnonymous()) {
+ $message = \sprintf(
+ 'Method %s() in anonymous class has a parameter $%s with a type declaration of %s, but containers should not be injected.',
+ $methodName,
+ $parameterName,
+ $classUsedInTypeDeclaration->getName(),
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithContainerTypeDeclaration()->toString())
+ ->build();
+ }
+
+ $message = \sprintf(
+ 'Method %s::%s() has a parameter $%s with a type declaration of %s, but containers should not be injected.',
+ $classReflection->getName(),
+ $methodName,
+ $parameterName,
+ $classUsedInTypeDeclaration->getName(),
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithContainerTypeDeclaration()->toString())
+ ->build();
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php
new file mode 100644
index 0000000..9888974
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullDefaultValueRule.php
@@ -0,0 +1,102 @@
+
+ */
+final class NoParameterWithNullDefaultValueRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithNullDefaultValue = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool {
+ return $this->analyzer->hasNullDefaultValue($parameter);
+ }));
+
+ if (0 === \count($parametersWithNullDefaultValue)) {
+ return [];
+ }
+
+ $methodName = $node->name->toString();
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($methodName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullDefaultValue->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Method %s() in anonymous class has parameter $%s with null as default value.',
+ $methodName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString())
+ ->build();
+ }, $parametersWithNullDefaultValue);
+ }
+
+ $className = $classReflection->getName();
+
+ return \array_map(static function (Node\Param $parameterWithNullDefaultValue) use ($className, $methodName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullDefaultValue->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Method %s::%s() has parameter $%s with null as default value.',
+ $className,
+ $methodName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullDefaultValue()->toString())
+ ->build();
+ }, $parametersWithNullDefaultValue);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php
new file mode 100644
index 0000000..632cd7b
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoParameterWithNullableTypeDeclarationRule.php
@@ -0,0 +1,102 @@
+
+ */
+final class NoParameterWithNullableTypeDeclarationRule implements Rules\Rule
+{
+ private Analyzer $analyzer;
+
+ public function __construct(Analyzer $analyzer)
+ {
+ $this->analyzer = $analyzer;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (0 === \count($node->params)) {
+ return [];
+ }
+
+ $parametersWithNullableTypeDeclaration = \array_values(\array_filter($node->params, function (Node\Param $parameter): bool {
+ return $this->analyzer->isNullableTypeDeclaration($parameter->type);
+ }));
+
+ if (0 === \count($parametersWithNullableTypeDeclaration)) {
+ return [];
+ }
+
+ $methodName = $node->name->toString();
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($methodName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullableTypeDeclaration->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Method %s() in anonymous class has parameter $%s with a nullable type declaration.',
+ $methodName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString())
+ ->build();
+ }, $parametersWithNullableTypeDeclaration);
+ }
+
+ $className = $classReflection->getName();
+
+ return \array_map(static function (Node\Param $parameterWithNullableTypeDeclaration) use ($className, $methodName): Rules\RuleError {
+ /** @var Node\Expr\Variable $variable */
+ $variable = $parameterWithNullableTypeDeclaration->var;
+
+ /** @var string $parameterName */
+ $parameterName = $variable->name;
+
+ $message = \sprintf(
+ 'Method %s::%s() has parameter $%s with a nullable type declaration.',
+ $className,
+ $methodName,
+ $parameterName,
+ );
+
+ return Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noParameterWithNullableTypeDeclaration()->toString())
+ ->build();
+ }, $parametersWithNullableTypeDeclaration);
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php
new file mode 100644
index 0000000..a1bf87f
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/NoReturnByReferenceRule.php
@@ -0,0 +1,72 @@
+
+ */
+final class NoReturnByReferenceRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ if (false === $node->byRef) {
+ return [];
+ }
+
+ $methodName = $node->name->toString();
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ $message = \sprintf(
+ 'Method %s() in anonymous class returns by reference.',
+ $methodName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noReturnByReference()->toString())
+ ->build(),
+ ];
+ }
+
+ $className = $classReflection->getName();
+
+ $message = \sprintf(
+ 'Method %s::%s() returns by reference.',
+ $className,
+ $methodName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::noReturnByReference()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php
new file mode 100644
index 0000000..9aa2c3c
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Methods/PrivateInFinalClassRule.php
@@ -0,0 +1,198 @@
+
+ */
+final class PrivateInFinalClassRule implements Rules\Rule
+{
+ /**
+ * @var list
+ */
+ private static array $whitelistedAnnotations = [
+ '@after',
+ '@before',
+ '@postCondition',
+ '@preCondition',
+ ];
+
+ /**
+ * @var list
+ */
+ private static array $whitelistedAttributes = [
+ Framework\Attributes\After::class,
+ Framework\Attributes\Before::class,
+ Framework\Attributes\PostCondition::class,
+ Framework\Attributes\PreCondition::class,
+ ];
+ private Type\FileTypeMapper $fileTypeMapper;
+
+ public function __construct(Type\FileTypeMapper $fileTypeMapper)
+ {
+ $this->fileTypeMapper = $fileTypeMapper;
+ }
+
+ public function getNodeType(): string
+ {
+ return Node\Stmt\ClassMethod::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ /** @var Reflection\ClassReflection $containingClass */
+ $containingClass = $scope->getClassReflection();
+
+ if (!$containingClass->isFinal()) {
+ return [];
+ }
+
+ if ($node->isPublic()) {
+ return [];
+ }
+
+ if ($node->isPrivate()) {
+ return [];
+ }
+
+ if ($this->hasWhitelistedAnnotation($node, $containingClass)) {
+ return [];
+ }
+
+ if (self::hasWhitelistedAttribute($node)) {
+ return [];
+ }
+
+ $methodName = $node->name->toString();
+
+ if (self::isDeclaredByParentClass($containingClass, $methodName)) {
+ return [];
+ }
+
+ if (self::isDeclaredByTrait($containingClass, $methodName)) {
+ return [];
+ }
+
+ /** @var Reflection\ClassReflection $classReflection */
+ $classReflection = $scope->getClassReflection();
+
+ if ($classReflection->isAnonymous()) {
+ $message = \sprintf(
+ 'Method %s() in anonymous class is protected, but since the containing class is final, it can be private.',
+ $node->name->name,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::privateInFinalClass()->toString())
+ ->build(),
+ ];
+ }
+
+ $message = \sprintf(
+ 'Method %s::%s() is protected, but since the containing class is final, it can be private.',
+ $containingClass->getName(),
+ $methodName,
+ );
+
+ return [
+ Rules\RuleErrorBuilder::message($message)
+ ->identifier(ErrorIdentifier::privateInFinalClass()->toString())
+ ->build(),
+ ];
+ }
+
+ private function hasWhitelistedAnnotation(
+ Node\Stmt\ClassMethod $node,
+ Reflection\ClassReflection $containingClass
+ ): bool {
+ $docComment = $node->getDocComment();
+
+ if (!$docComment instanceof Comment\Doc) {
+ return false;
+ }
+
+ $resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
+ null,
+ $containingClass->getName(),
+ null,
+ null,
+ $docComment->getText(),
+ );
+
+ foreach ($resolvedPhpDoc->getPhpDocNodes() as $phpDocNode) {
+ foreach ($phpDocNode->getTags() as $tag) {
+ if (\in_array($tag->name, self::$whitelistedAnnotations, true)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static function hasWhitelistedAttribute(Node\Stmt\ClassMethod $node): bool
+ {
+ foreach ($node->attrGroups as $attributeGroup) {
+ foreach ($attributeGroup->attrs as $attribute) {
+ if (\in_array($attribute->name->toString(), self::$whitelistedAttributes, true)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static function isDeclaredByParentClass(
+ Reflection\ClassReflection $containingClass,
+ string $methodName
+ ): bool {
+ $parentClass = $containingClass->getNativeReflection()->getParentClass();
+
+ if (!$parentClass instanceof \ReflectionClass) {
+ return false;
+ }
+
+ if (!$parentClass->hasMethod($methodName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static function isDeclaredByTrait(
+ Reflection\ClassReflection $containingClass,
+ string $methodName
+ ): bool {
+ foreach ($containingClass->getTraits() as $trait) {
+ if ($trait->hasMethod($methodName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php
new file mode 100644
index 0000000..127e5ee
--- /dev/null
+++ b/tools/.phpstan/vendor/ergebnis/phpstan-rules/src/Statements/NoSwitchRule.php
@@ -0,0 +1,41 @@
+
+ */
+final class NoSwitchRule implements Rules\Rule
+{
+ public function getNodeType(): string
+ {
+ return Node\Stmt\Switch_::class;
+ }
+
+ public function processNode(
+ Node $node,
+ Analyser\Scope $scope
+ ): array {
+ return [
+ Rules\RuleErrorBuilder::message('Control structures using switch should not be used.')
+ ->identifier(ErrorIdentifier::noSwitch()->toString())
+ ->build(),
+ ];
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php b/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php
new file mode 100644
index 0000000..25851af
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/.phpstorm.meta.php
@@ -0,0 +1,13 @@
+
+✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks
+✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …
+✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories
+✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers
+✅ [Helper Functions](https://doc.nette.org/utils/helpers)
+✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML
+✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images
+✅ [Iterables](https://doc.nette.org/utils/iterables)
+✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding
+✅ [Generating Random Strings](https://doc.nette.org/utils/random)
+✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math
+✅ [PHP Reflection](https://doc.nette.org/utils/reflection)
+✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions
+✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements
+✅ [Type](https://doc.nette.org/utils/type) - PHP data type
+✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs
+
+
+
+Installation
+------------
+
+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.
+
+
+
+[Support Me](https://github.com/sponsors/dg)
+--------------------------------------------
+
+Do you like Nette Utils? Are you looking forward to the new features?
+
+[](https://github.com/sponsors/dg)
+
+Thank you!
diff --git a/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php b/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php
new file mode 100644
index 0000000..d749d4e
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/HtmlStringable.php
@@ -0,0 +1,22 @@
+counter === 1 || ($gridWidth && $this->counter !== 0 && (($this->counter - 1) % $gridWidth) === 0);
+ }
+
+
+ /**
+ * Is the current element the last one?
+ */
+ public function isLast(?int $gridWidth = null): bool
+ {
+ return !$this->hasNext() || ($gridWidth && ($this->counter % $gridWidth) === 0);
+ }
+
+
+ /**
+ * Is the iterator empty?
+ */
+ public function isEmpty(): bool
+ {
+ return $this->counter === 0;
+ }
+
+
+ /**
+ * Is the counter odd?
+ */
+ public function isOdd(): bool
+ {
+ return $this->counter % 2 === 1;
+ }
+
+
+ /**
+ * Is the counter even?
+ */
+ public function isEven(): bool
+ {
+ return $this->counter % 2 === 0;
+ }
+
+
+ /**
+ * Returns the counter.
+ */
+ public function getCounter(): int
+ {
+ return $this->counter;
+ }
+
+
+ /**
+ * Returns the count of elements.
+ */
+ public function count(): int
+ {
+ $inner = $this->getInnerIterator();
+ if ($inner instanceof \Countable) {
+ return $inner->count();
+
+ } else {
+ throw new Nette\NotSupportedException('Iterator is not countable.');
+ }
+ }
+
+
+ /**
+ * Forwards to the next element.
+ */
+ public function next(): void
+ {
+ parent::next();
+ if (parent::valid()) {
+ $this->counter++;
+ }
+ }
+
+
+ /**
+ * Rewinds the Iterator.
+ */
+ public function rewind(): void
+ {
+ parent::rewind();
+ $this->counter = parent::valid() ? 1 : 0;
+ }
+
+
+ /**
+ * Returns the next key.
+ */
+ public function getNextKey(): mixed
+ {
+ return $this->getInnerIterator()->key();
+ }
+
+
+ /**
+ * Returns the next element.
+ */
+ public function getNextValue(): mixed
+ {
+ return $this->getInnerIterator()->current();
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php b/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php
new file mode 100644
index 0000000..284da29
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Iterators/Mapper.php
@@ -0,0 +1,33 @@
+callback = $callback;
+ }
+
+
+ public function current(): mixed
+ {
+ return ($this->callback)(parent::current(), parent::key());
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/SmartObject.php b/tools/.phpstan/vendor/nette/utils/src/SmartObject.php
new file mode 100644
index 0000000..3b2203f
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/SmartObject.php
@@ -0,0 +1,140 @@
+$name ?? null;
+ if (is_iterable($handlers)) {
+ foreach ($handlers as $handler) {
+ $handler(...$args);
+ }
+ } elseif ($handlers !== null) {
+ throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . get_debug_type($handlers) . ' given.');
+ }
+
+ return null;
+ }
+
+ ObjectHelpers::strictCall($class, $name);
+ }
+
+
+ /**
+ * @throws MemberAccessException
+ */
+ public static function __callStatic(string $name, array $args)
+ {
+ ObjectHelpers::strictStaticCall(static::class, $name);
+ }
+
+
+ /**
+ * @return mixed
+ * @throws MemberAccessException if the property is not defined.
+ */
+ public function &__get(string $name)
+ {
+ $class = static::class;
+
+ if ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property getter
+ if (!($prop & 0b0001)) {
+ throw new MemberAccessException("Cannot read a write-only property $class::\$$name.");
+ }
+
+ $m = ($prop & 0b0010 ? 'get' : 'is') . ucfirst($name);
+ if ($prop & 0b10000) {
+ $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call()
+ $loc = isset($trace['file'], $trace['line'])
+ ? " in $trace[file] on line $trace[line]"
+ : '';
+ trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED);
+ }
+
+ if ($prop & 0b0100) { // return by reference
+ return $this->$m();
+ } else {
+ $val = $this->$m();
+ return $val;
+ }
+ } else {
+ ObjectHelpers::strictGet($class, $name);
+ }
+ }
+
+
+ /**
+ * @throws MemberAccessException if the property is not defined or is read-only
+ */
+ public function __set(string $name, mixed $value): void
+ {
+ $class = static::class;
+
+ if (ObjectHelpers::hasProperty($class, $name)) { // unsetted property
+ $this->$name = $value;
+
+ } elseif ($prop = ObjectHelpers::getMagicProperties($class)[$name] ?? null) { // property setter
+ if (!($prop & 0b1000)) {
+ throw new MemberAccessException("Cannot write to a read-only property $class::\$$name.");
+ }
+
+ $m = 'set' . ucfirst($name);
+ if ($prop & 0b10000) {
+ $trace = debug_backtrace(0, 1)[0]; // suppose this method is called from __call()
+ $loc = isset($trace['file'], $trace['line'])
+ ? " in $trace[file] on line $trace[line]"
+ : '';
+ trigger_error("Property $class::\$$name is deprecated, use $class::$m() method$loc.", E_USER_DEPRECATED);
+ }
+
+ $this->$m($value);
+
+ } else {
+ ObjectHelpers::strictSet($class, $name);
+ }
+ }
+
+
+ /**
+ * @throws MemberAccessException
+ */
+ public function __unset(string $name): void
+ {
+ $class = static::class;
+ if (!ObjectHelpers::hasProperty($class, $name)) {
+ throw new MemberAccessException("Cannot unset the property $class::\$$name.");
+ }
+ }
+
+
+ public function __isset(string $name): bool
+ {
+ return isset(ObjectHelpers::getMagicProperties(static::class)[$name]);
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/StaticClass.php b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php
new file mode 100644
index 0000000..b1d8486
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/StaticClass.php
@@ -0,0 +1,34 @@
+
+ * @implements \ArrayAccess
+ */
+class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate
+{
+ /**
+ * Transforms array to ArrayHash.
+ * @param array $array
+ */
+ public static function from(array $array, bool $recursive = true): static
+ {
+ $obj = new static;
+ foreach ($array as $key => $value) {
+ $obj->$key = $recursive && is_array($value)
+ ? static::from($value)
+ : $value;
+ }
+
+ return $obj;
+ }
+
+
+ /**
+ * Returns an iterator over all items.
+ * @return \Iterator
+ */
+ public function &getIterator(): \Iterator
+ {
+ foreach ((array) $this as $key => $foo) {
+ yield $key => $this->$key;
+ }
+ }
+
+
+ /**
+ * Returns items count.
+ */
+ public function count(): int
+ {
+ return count((array) $this);
+ }
+
+
+ /**
+ * Replaces or appends a item.
+ * @param array-key $key
+ * @param T $value
+ */
+ public function offsetSet($key, $value): void
+ {
+ if (!is_scalar($key)) { // prevents null
+ throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', get_debug_type($key)));
+ }
+
+ $this->$key = $value;
+ }
+
+
+ /**
+ * Returns a item.
+ * @param array-key $key
+ * @return T
+ */
+ #[\ReturnTypeWillChange]
+ public function offsetGet($key)
+ {
+ return $this->$key;
+ }
+
+
+ /**
+ * Determines whether a item exists.
+ * @param array-key $key
+ */
+ public function offsetExists($key): bool
+ {
+ return isset($this->$key);
+ }
+
+
+ /**
+ * Removes the element from this list.
+ * @param array-key $key
+ */
+ public function offsetUnset($key): void
+ {
+ unset($this->$key);
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php
new file mode 100644
index 0000000..a402f9b
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/ArrayList.php
@@ -0,0 +1,136 @@
+
+ * @implements \ArrayAccess
+ */
+class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
+{
+ use Nette\SmartObject;
+
+ private array $list = [];
+
+
+ /**
+ * Transforms array to ArrayList.
+ * @param list $array
+ */
+ public static function from(array $array): static
+ {
+ if (!Arrays::isList($array)) {
+ throw new Nette\InvalidArgumentException('Array is not valid list.');
+ }
+
+ $obj = new static;
+ $obj->list = $array;
+ return $obj;
+ }
+
+
+ /**
+ * Returns an iterator over all items.
+ * @return \Iterator
+ */
+ public function &getIterator(): \Iterator
+ {
+ foreach ($this->list as &$item) {
+ yield $item;
+ }
+ }
+
+
+ /**
+ * Returns items count.
+ */
+ public function count(): int
+ {
+ return count($this->list);
+ }
+
+
+ /**
+ * Replaces or appends a item.
+ * @param int|null $index
+ * @param T $value
+ * @throws Nette\OutOfRangeException
+ */
+ public function offsetSet($index, $value): void
+ {
+ if ($index === null) {
+ $this->list[] = $value;
+
+ } elseif (!is_int($index) || $index < 0 || $index >= count($this->list)) {
+ throw new Nette\OutOfRangeException('Offset invalid or out of range');
+
+ } else {
+ $this->list[$index] = $value;
+ }
+ }
+
+
+ /**
+ * Returns a item.
+ * @param int $index
+ * @return T
+ * @throws Nette\OutOfRangeException
+ */
+ public function offsetGet($index): mixed
+ {
+ if (!is_int($index) || $index < 0 || $index >= count($this->list)) {
+ throw new Nette\OutOfRangeException('Offset invalid or out of range');
+ }
+
+ return $this->list[$index];
+ }
+
+
+ /**
+ * Determines whether a item exists.
+ * @param int $index
+ */
+ public function offsetExists($index): bool
+ {
+ return is_int($index) && $index >= 0 && $index < count($this->list);
+ }
+
+
+ /**
+ * Removes the element at the specified position in this list.
+ * @param int $index
+ * @throws Nette\OutOfRangeException
+ */
+ public function offsetUnset($index): void
+ {
+ if (!is_int($index) || $index < 0 || $index >= count($this->list)) {
+ throw new Nette\OutOfRangeException('Offset invalid or out of range');
+ }
+
+ array_splice($this->list, $index, 1);
+ }
+
+
+ /**
+ * Prepends a item.
+ * @param T $value
+ */
+ public function prepend(mixed $value): void
+ {
+ $first = array_slice($this->list, 0, 1);
+ $this->offsetSet(0, $value);
+ array_splice($this->list, 1, 0, $first);
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php
new file mode 100644
index 0000000..00a4a8c
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Arrays.php
@@ -0,0 +1,553 @@
+ $array
+ * @param array-key|array-key[] $key
+ * @param ?T $default
+ * @return ?T
+ * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
+ */
+ public static function get(array $array, string|int|array $key, mixed $default = null): mixed
+ {
+ foreach (is_array($key) ? $key : [$key] as $k) {
+ if (is_array($array) && array_key_exists($k, $array)) {
+ $array = $array[$k];
+ } else {
+ if (func_num_args() < 3) {
+ throw new Nette\InvalidArgumentException("Missing item '$k'.");
+ }
+
+ return $default;
+ }
+ }
+
+ return $array;
+ }
+
+
+ /**
+ * Returns reference to array item. If the index does not exist, new one is created with value null.
+ * @template T
+ * @param array $array
+ * @param array-key|array-key[] $key
+ * @return ?T
+ * @throws Nette\InvalidArgumentException if traversed item is not an array
+ */
+ public static function &getRef(array &$array, string|int|array $key): mixed
+ {
+ foreach (is_array($key) ? $key : [$key] as $k) {
+ if (is_array($array) || $array === null) {
+ $array = &$array[$k];
+ } else {
+ throw new Nette\InvalidArgumentException('Traversed item is not an array.');
+ }
+ }
+
+ return $array;
+ }
+
+
+ /**
+ * Recursively merges two fields. It is useful, for example, for merging tree structures. It behaves as
+ * the + operator for array, ie. it adds a key/value pair from the second array to the first one and retains
+ * the value from the first array in the case of a key collision.
+ * @template T1
+ * @template T2
+ * @param array $array1
+ * @param array $array2
+ * @return array
+ */
+ public static function mergeTree(array $array1, array $array2): array
+ {
+ $res = $array1 + $array2;
+ foreach (array_intersect_key($array1, $array2) as $k => $v) {
+ if (is_array($v) && is_array($array2[$k])) {
+ $res[$k] = self::mergeTree($v, $array2[$k]);
+ }
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Returns zero-indexed position of given array key. Returns null if key is not found.
+ */
+ public static function getKeyOffset(array $array, string|int $key): ?int
+ {
+ return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), strict: true));
+ }
+
+
+ /**
+ * @deprecated use getKeyOffset()
+ */
+ public static function searchKey(array $array, $key): ?int
+ {
+ return self::getKeyOffset($array, $key);
+ }
+
+
+ /**
+ * Tests an array for the presence of value.
+ */
+ public static function contains(array $array, mixed $value): bool
+ {
+ return in_array($value, $array, true);
+ }
+
+
+ /**
+ * Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array): bool $predicate
+ * @return ?V
+ */
+ public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed
+ {
+ $key = self::firstKey($array, $predicate);
+ return $key === null
+ ? ($else ? $else() : null)
+ : $array[$key];
+ }
+
+
+ /**
+ * Returns the last item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array): bool $predicate
+ * @return ?V
+ */
+ public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed
+ {
+ $key = self::lastKey($array, $predicate);
+ return $key === null
+ ? ($else ? $else() : null)
+ : $array[$key];
+ }
+
+
+ /**
+ * Returns the key of first item (matching the specified predicate if given) or null if there is no such item.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array): bool $predicate
+ * @return ?K
+ */
+ public static function firstKey(array $array, ?callable $predicate = null): int|string|null
+ {
+ if (!$predicate) {
+ return array_key_first($array);
+ }
+ foreach ($array as $k => $v) {
+ if ($predicate($v, $k, $array)) {
+ return $k;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Returns the key of last item (matching the specified predicate if given) or null if there is no such item.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param ?callable(V, K, array): bool $predicate
+ * @return ?K
+ */
+ public static function lastKey(array $array, ?callable $predicate = null): int|string|null
+ {
+ return $predicate
+ ? self::firstKey(array_reverse($array, preserve_keys: true), $predicate)
+ : array_key_last($array);
+ }
+
+
+ /**
+ * Inserts the contents of the $inserted array into the $array immediately after the $key.
+ * If $key is null (or does not exist), it is inserted at the beginning.
+ */
+ public static function insertBefore(array &$array, string|int|null $key, array $inserted): void
+ {
+ $offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key);
+ $array = array_slice($array, 0, $offset, preserve_keys: true)
+ + $inserted
+ + array_slice($array, $offset, count($array), preserve_keys: true);
+ }
+
+
+ /**
+ * Inserts the contents of the $inserted array into the $array before the $key.
+ * If $key is null (or does not exist), it is inserted at the end.
+ */
+ public static function insertAfter(array &$array, string|int|null $key, array $inserted): void
+ {
+ if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) {
+ $offset = count($array) - 1;
+ }
+
+ $array = array_slice($array, 0, $offset + 1, preserve_keys: true)
+ + $inserted
+ + array_slice($array, $offset + 1, count($array), preserve_keys: true);
+ }
+
+
+ /**
+ * Renames key in array.
+ */
+ public static function renameKey(array &$array, string|int $oldKey, string|int $newKey): bool
+ {
+ $offset = self::getKeyOffset($array, $oldKey);
+ if ($offset === null) {
+ return false;
+ }
+
+ $val = &$array[$oldKey];
+ $keys = array_keys($array);
+ $keys[$offset] = $newKey;
+ $array = array_combine($keys, $array);
+ $array[$newKey] = &$val;
+ return true;
+ }
+
+
+ /**
+ * Returns only those array items, which matches a regular expression $pattern.
+ * @param string[] $array
+ * @return string[]
+ */
+ public static function grep(
+ array $array,
+ #[Language('RegExp')]
+ string $pattern,
+ bool|int $invert = false,
+ ): array
+ {
+ $flags = $invert ? PREG_GREP_INVERT : 0;
+ return Strings::pcre('preg_grep', [$pattern, $array, $flags]);
+ }
+
+
+ /**
+ * Transforms multidimensional array to flat array.
+ */
+ public static function flatten(array $array, bool $preserveKeys = false): array
+ {
+ $res = [];
+ $cb = $preserveKeys
+ ? function ($v, $k) use (&$res): void { $res[$k] = $v; }
+ : function ($v) use (&$res): void { $res[] = $v; };
+ array_walk_recursive($array, $cb);
+ return $res;
+ }
+
+
+ /**
+ * Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list.
+ * @return ($value is list ? true : false)
+ */
+ public static function isList(mixed $value): bool
+ {
+ return is_array($value) && (PHP_VERSION_ID < 80100
+ ? !$value || array_keys($value) === range(0, count($value) - 1)
+ : array_is_list($value)
+ );
+ }
+
+
+ /**
+ * Reformats table to associative tree. Path looks like 'field|field[]field->field=field'.
+ * @param string|string[] $path
+ */
+ public static function associate(array $array, $path): array|\stdClass
+ {
+ $parts = is_array($path)
+ ? $path
+ : preg_split('#(\[\]|->|=|\|)#', $path, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+
+ if (!$parts || $parts === ['->'] || $parts[0] === '=' || $parts[0] === '|') {
+ throw new Nette\InvalidArgumentException("Invalid path '$path'.");
+ }
+
+ $res = $parts[0] === '->' ? new \stdClass : [];
+
+ foreach ($array as $rowOrig) {
+ $row = (array) $rowOrig;
+ $x = &$res;
+
+ for ($i = 0; $i < count($parts); $i++) {
+ $part = $parts[$i];
+ if ($part === '[]') {
+ $x = &$x[];
+
+ } elseif ($part === '=') {
+ if (isset($parts[++$i])) {
+ $x = $row[$parts[$i]];
+ $row = null;
+ }
+ } elseif ($part === '->') {
+ if (isset($parts[++$i])) {
+ if ($x === null) {
+ $x = new \stdClass;
+ }
+
+ $x = &$x->{$row[$parts[$i]]};
+ } else {
+ $row = is_object($rowOrig) ? $rowOrig : (object) $row;
+ }
+ } elseif ($part !== '|') {
+ $x = &$x[(string) $row[$part]];
+ }
+ }
+
+ if ($x === null) {
+ $x = $row;
+ }
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling.
+ */
+ public static function normalize(array $array, mixed $filling = null): array
+ {
+ $res = [];
+ foreach ($array as $k => $v) {
+ $res[is_int($k) ? $v : $k] = is_int($k) ? $filling : $v;
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Returns and removes the value of an item from an array. If it does not exist, it throws an exception,
+ * or returns $default, if provided.
+ * @template T
+ * @param array $array
+ * @param ?T $default
+ * @return ?T
+ * @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
+ */
+ public static function pick(array &$array, string|int $key, mixed $default = null): mixed
+ {
+ if (array_key_exists($key, $array)) {
+ $value = $array[$key];
+ unset($array[$key]);
+ return $value;
+
+ } elseif (func_num_args() < 3) {
+ throw new Nette\InvalidArgumentException("Missing item '$key'.");
+
+ } else {
+ return $default;
+ }
+ }
+
+
+ /**
+ * Tests whether at least one element in the array passes the test implemented by the provided function.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param callable(V, K, array): bool $predicate
+ */
+ public static function some(iterable $array, callable $predicate): bool
+ {
+ foreach ($array as $k => $v) {
+ if ($predicate($v, $k, $array)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Tests whether all elements in the array pass the test implemented by the provided function.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param callable(V, K, array): bool $predicate
+ */
+ public static function every(iterable $array, callable $predicate): bool
+ {
+ foreach ($array as $k => $v) {
+ if (!$predicate($v, $k, $array)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * Returns a new array containing all key-value pairs matching the given $predicate.
+ * @template K of int|string
+ * @template V
+ * @param array $array
+ * @param callable(V, K, array): bool $predicate
+ * @return array
+ */
+ public static function filter(array $array, callable $predicate): array
+ {
+ $res = [];
+ foreach ($array as $k => $v) {
+ if ($predicate($v, $k, $array)) {
+ $res[$k] = $v;
+ }
+ }
+ return $res;
+ }
+
+
+ /**
+ * Returns an array containing the original keys and results of applying the given transform function to each element.
+ * @template K of int|string
+ * @template V
+ * @template R
+ * @param array $array
+ * @param callable(V, K, array): R $transformer
+ * @return array
+ */
+ public static function map(iterable $array, callable $transformer): array
+ {
+ $res = [];
+ foreach ($array as $k => $v) {
+ $res[$k] = $transformer($v, $k, $array);
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Returns an array containing new keys and values generated by applying the given transform function to each element.
+ * If the function returns null, the element is skipped.
+ * @template K of int|string
+ * @template V
+ * @template ResK of int|string
+ * @template ResV
+ * @param array $array
+ * @param callable(V, K, array): ?array{ResK, ResV} $transformer
+ * @return array
+ */
+ public static function mapWithKeys(array $array, callable $transformer): array
+ {
+ $res = [];
+ foreach ($array as $k => $v) {
+ $pair = $transformer($v, $k, $array);
+ if ($pair) {
+ $res[$pair[0]] = $pair[1];
+ }
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Invokes all callbacks and returns array of results.
+ * @param callable[] $callbacks
+ */
+ public static function invoke(iterable $callbacks, ...$args): array
+ {
+ $res = [];
+ foreach ($callbacks as $k => $cb) {
+ $res[$k] = $cb(...$args);
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Invokes method on every object in an array and returns array of results.
+ * @param object[] $objects
+ */
+ public static function invokeMethod(iterable $objects, string $method, ...$args): array
+ {
+ $res = [];
+ foreach ($objects as $k => $obj) {
+ $res[$k] = $obj->$method(...$args);
+ }
+
+ return $res;
+ }
+
+
+ /**
+ * Copies the elements of the $array array to the $object object and then returns it.
+ * @template T of object
+ * @param T $object
+ * @return T
+ */
+ public static function toObject(iterable $array, object $object): object
+ {
+ foreach ($array as $k => $v) {
+ $object->$k = $v;
+ }
+
+ return $object;
+ }
+
+
+ /**
+ * Converts value to array key.
+ */
+ public static function toKey(mixed $value): int|string
+ {
+ return key([$value => null]);
+ }
+
+
+ /**
+ * Returns copy of the $array where every item is converted to string
+ * and prefixed by $prefix and suffixed by $suffix.
+ * @param string[] $array
+ * @return string[]
+ */
+ public static function wrap(array $array, string $prefix = '', string $suffix = ''): array
+ {
+ $res = [];
+ foreach ($array as $k => $v) {
+ $res[$k] = $prefix . $v . $suffix;
+ }
+
+ return $res;
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php
new file mode 100644
index 0000000..1777428
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Callback.php
@@ -0,0 +1,137 @@
+getClosureScopeClass()?->name;
+ if (str_ends_with($r->name, '}')) {
+ return $closure;
+
+ } elseif (($obj = $r->getClosureThis()) && $obj::class === $class) {
+ return [$obj, $r->name];
+
+ } elseif ($class) {
+ return [$class, $r->name];
+
+ } else {
+ return $r->name;
+ }
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php
new file mode 100644
index 0000000..6ad6520
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/DateTime.php
@@ -0,0 +1,140 @@
+format('Y-m-d H:i:s.u'), $time->getTimezone());
+
+ } elseif (is_numeric($time)) {
+ if ($time <= self::YEAR) {
+ $time += time();
+ }
+
+ return (new static)->setTimestamp((int) $time);
+
+ } else { // textual or null
+ return new static((string) $time);
+ }
+ }
+
+
+ /**
+ * Creates DateTime object.
+ * @throws Nette\InvalidArgumentException if the date and time are not valid.
+ */
+ public static function fromParts(
+ int $year,
+ int $month,
+ int $day,
+ int $hour = 0,
+ int $minute = 0,
+ float $second = 0.0,
+ ): static
+ {
+ $s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second);
+ if (
+ !checkdate($month, $day, $year)
+ || $hour < 0
+ || $hour > 23
+ || $minute < 0
+ || $minute > 59
+ || $second < 0
+ || $second >= 60
+ ) {
+ throw new Nette\InvalidArgumentException("Invalid date '$s'");
+ }
+
+ return new static($s);
+ }
+
+
+ /**
+ * Returns new DateTime object formatted according to the specified format.
+ */
+ public static function createFromFormat(
+ string $format,
+ string $time,
+ string|\DateTimeZone|null $timezone = null,
+ ): static|false
+ {
+ if ($timezone === null) {
+ $timezone = new \DateTimeZone(date_default_timezone_get());
+
+ } elseif (is_string($timezone)) {
+ $timezone = new \DateTimeZone($timezone);
+ }
+
+ $date = parent::createFromFormat($format, $time, $timezone);
+ return $date ? static::from($date) : false;
+ }
+
+
+ /**
+ * Returns JSON representation in ISO 8601 (used by JavaScript).
+ */
+ public function jsonSerialize(): string
+ {
+ return $this->format('c');
+ }
+
+
+ /**
+ * Returns the date and time in the format 'Y-m-d H:i:s'.
+ */
+ public function __toString(): string
+ {
+ return $this->format('Y-m-d H:i:s');
+ }
+
+
+ /**
+ * You'd better use: (clone $dt)->modify(...)
+ */
+ public function modifyClone(string $modify = ''): static
+ {
+ $dolly = clone $this;
+ return $modify ? $dolly->modify($modify) : $dolly;
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php
new file mode 100644
index 0000000..fb92d11
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileInfo.php
@@ -0,0 +1,69 @@
+setInfoClass(static::class);
+ $this->relativePath = $relativePath;
+ }
+
+
+ /**
+ * Returns the relative directory path.
+ */
+ public function getRelativePath(): string
+ {
+ return $this->relativePath;
+ }
+
+
+ /**
+ * Returns the relative path including file name.
+ */
+ public function getRelativePathname(): string
+ {
+ return ($this->relativePath === '' ? '' : $this->relativePath . DIRECTORY_SEPARATOR)
+ . $this->getBasename();
+ }
+
+
+ /**
+ * Returns the contents of the file.
+ * @throws Nette\IOException
+ */
+ public function read(): string
+ {
+ return FileSystem::read($this->getPathname());
+ }
+
+
+ /**
+ * Writes the contents to the file.
+ * @throws Nette\IOException
+ */
+ public function write(string $content): void
+ {
+ FileSystem::write($this->getPathname(), $content);
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php
new file mode 100644
index 0000000..ab9a7e8
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/FileSystem.php
@@ -0,0 +1,326 @@
+getPathname());
+ }
+
+ foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($origin, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item) {
+ if ($item->isDir()) {
+ static::createDir($target . '/' . $iterator->getSubPathName());
+ } else {
+ static::copy($item->getPathname(), $target . '/' . $iterator->getSubPathName());
+ }
+ }
+ } else {
+ static::createDir(dirname($target));
+ if (@stream_copy_to_stream(static::open($origin, 'rb'), static::open($target, 'wb')) === false) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to copy file '%s' to '%s'. %s",
+ self::normalizePath($origin),
+ self::normalizePath($target),
+ Helpers::getLastError(),
+ ));
+ }
+ }
+ }
+
+
+ /**
+ * Opens file and returns resource.
+ * @return resource
+ * @throws Nette\IOException on error occurred
+ */
+ public static function open(string $path, string $mode)
+ {
+ $f = @fopen($path, $mode); // @ is escalated to exception
+ if (!$f) {
+ throw new Nette\IOException(sprintf(
+ "Unable to open file '%s'. %s",
+ self::normalizePath($path),
+ Helpers::getLastError(),
+ ));
+ }
+ return $f;
+ }
+
+
+ /**
+ * Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first.
+ * @throws Nette\IOException on error occurred
+ */
+ public static function delete(string $path): void
+ {
+ if (is_file($path) || is_link($path)) {
+ $func = DIRECTORY_SEPARATOR === '\\' && is_dir($path) ? 'rmdir' : 'unlink';
+ if (!@$func($path)) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to delete '%s'. %s",
+ self::normalizePath($path),
+ Helpers::getLastError(),
+ ));
+ }
+ } elseif (is_dir($path)) {
+ foreach (new \FilesystemIterator($path) as $item) {
+ static::delete($item->getPathname());
+ }
+
+ if (!@rmdir($path)) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to delete directory '%s'. %s",
+ self::normalizePath($path),
+ Helpers::getLastError(),
+ ));
+ }
+ }
+ }
+
+
+ /**
+ * Renames or moves a file or a directory. Overwrites existing files and directories by default.
+ * @throws Nette\IOException on error occurred
+ * @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists
+ */
+ public static function rename(string $origin, string $target, bool $overwrite = true): void
+ {
+ if (!$overwrite && file_exists($target)) {
+ throw new Nette\InvalidStateException(sprintf("File or directory '%s' already exists.", self::normalizePath($target)));
+
+ } elseif (!file_exists($origin)) {
+ throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($origin)));
+
+ } else {
+ static::createDir(dirname($target));
+ if (realpath($origin) !== realpath($target)) {
+ static::delete($target);
+ }
+
+ if (!@rename($origin, $target)) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to rename file or directory '%s' to '%s'. %s",
+ self::normalizePath($origin),
+ self::normalizePath($target),
+ Helpers::getLastError(),
+ ));
+ }
+ }
+ }
+
+
+ /**
+ * Reads the content of a file.
+ * @throws Nette\IOException on error occurred
+ */
+ public static function read(string $file): string
+ {
+ $content = @file_get_contents($file); // @ is escalated to exception
+ if ($content === false) {
+ throw new Nette\IOException(sprintf(
+ "Unable to read file '%s'. %s",
+ self::normalizePath($file),
+ Helpers::getLastError(),
+ ));
+ }
+
+ return $content;
+ }
+
+
+ /**
+ * Reads the file content line by line. Because it reads continuously as we iterate over the lines,
+ * it is possible to read files larger than the available memory.
+ * @return \Generator
+ * @throws Nette\IOException on error occurred
+ */
+ public static function readLines(string $file, bool $stripNewLines = true): \Generator
+ {
+ return (function ($f) use ($file, $stripNewLines) {
+ $counter = 0;
+ do {
+ $line = Callback::invokeSafe('fgets', [$f], fn($error) => throw new Nette\IOException(sprintf(
+ "Unable to read file '%s'. %s",
+ self::normalizePath($file),
+ $error,
+ )));
+ if ($line === false) {
+ fclose($f);
+ break;
+ }
+ if ($stripNewLines) {
+ $line = rtrim($line, "\r\n");
+ }
+
+ yield $counter++ => $line;
+
+ } while (true);
+ })(static::open($file, 'r'));
+ }
+
+
+ /**
+ * Writes the string to a file.
+ * @throws Nette\IOException on error occurred
+ */
+ public static function write(string $file, string $content, ?int $mode = 0666): void
+ {
+ static::createDir(dirname($file));
+ if (@file_put_contents($file, $content) === false) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to write file '%s'. %s",
+ self::normalizePath($file),
+ Helpers::getLastError(),
+ ));
+ }
+
+ if ($mode !== null && !@chmod($file, $mode)) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to chmod file '%s' to mode %s. %s",
+ self::normalizePath($file),
+ decoct($mode),
+ Helpers::getLastError(),
+ ));
+ }
+ }
+
+
+ /**
+ * Sets file permissions to `$fileMode` or directory permissions to `$dirMode`.
+ * Recursively traverses and sets permissions on the entire contents of the directory as well.
+ * @throws Nette\IOException on error occurred
+ */
+ public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void
+ {
+ if (is_file($path)) {
+ if (!@chmod($path, $fileMode)) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to chmod file '%s' to mode %s. %s",
+ self::normalizePath($path),
+ decoct($fileMode),
+ Helpers::getLastError(),
+ ));
+ }
+ } elseif (is_dir($path)) {
+ foreach (new \FilesystemIterator($path) as $item) {
+ static::makeWritable($item->getPathname(), $dirMode, $fileMode);
+ }
+
+ if (!@chmod($path, $dirMode)) { // @ is escalated to exception
+ throw new Nette\IOException(sprintf(
+ "Unable to chmod directory '%s' to mode %s. %s",
+ self::normalizePath($path),
+ decoct($dirMode),
+ Helpers::getLastError(),
+ ));
+ }
+ } else {
+ throw new Nette\IOException(sprintf("File or directory '%s' not found.", self::normalizePath($path)));
+ }
+ }
+
+
+ /**
+ * Determines if the path is absolute.
+ */
+ public static function isAbsolute(string $path): bool
+ {
+ return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
+ }
+
+
+ /**
+ * Normalizes `..` and `.` and directory separators in path.
+ */
+ public static function normalizePath(string $path): string
+ {
+ $parts = $path === '' ? [] : preg_split('~[/\\\\]+~', $path);
+ $res = [];
+ foreach ($parts as $part) {
+ if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') {
+ array_pop($res);
+ } elseif ($part !== '.') {
+ $res[] = $part;
+ }
+ }
+
+ return $res === ['']
+ ? DIRECTORY_SEPARATOR
+ : implode(DIRECTORY_SEPARATOR, $res);
+ }
+
+
+ /**
+ * Joins all segments of the path and normalizes the result.
+ */
+ public static function joinPaths(string ...$paths): string
+ {
+ return self::normalizePath(implode('/', $paths));
+ }
+
+
+ /**
+ * Converts backslashes to slashes.
+ */
+ public static function unixSlashes(string $path): string
+ {
+ return strtr($path, '\\', '/');
+ }
+
+
+ /**
+ * Converts slashes to platform-specific directory separators.
+ */
+ public static function platformSlashes(string $path): string
+ {
+ return DIRECTORY_SEPARATOR === '/'
+ ? strtr($path, '\\', '/')
+ : str_replace(':\\\\', '://', strtr($path, '/', '\\')); // protocol://
+ }
+}
diff --git a/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php
new file mode 100644
index 0000000..a496528
--- /dev/null
+++ b/tools/.phpstan/vendor/nette/utils/src/Utils/Finder.php
@@ -0,0 +1,510 @@
+size('> 10kB')
+ * ->from('.')
+ * ->exclude('temp');
+ *
+ * @implements \IteratorAggregate
+ */
+class Finder implements \IteratorAggregate
+{
+ use Nette\SmartObject;
+
+ /** @var array */
+ private array $find = [];
+
+ /** @var string[] */
+ private array $in = [];
+
+ /** @var \Closure[] */
+ private array $filters = [];
+
+ /** @var \Closure[] */
+ private array $descentFilters = [];
+
+ /** @var array */
+ private array $appends = [];
+ private bool $childFirst = false;
+
+ /** @var ?callable */
+ private $sort;
+ private int $maxDepth = -1;
+ private bool $ignoreUnreadableDirs = true;
+
+
+ /**
+ * Begins search for files and directories matching mask.
+ */
+ public static function find(string|array $masks = ['*']): static
+ {
+ $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic
+ return (new static)->addMask($masks, 'dir')->addMask($masks, 'file');
+ }
+
+
+ /**
+ * Begins search for files matching mask.
+ */
+ public static function findFiles(string|array $masks = ['*']): static
+ {
+ $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic
+ return (new static)->addMask($masks, 'file');
+ }
+
+
+ /**
+ * Begins search for directories matching mask.
+ */
+ public static function findDirectories(string|array $masks = ['*']): static
+ {
+ $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic
+ return (new static)->addMask($masks, 'dir');
+ }
+
+
+ /**
+ * Finds files matching the specified masks.
+ */
+ public function files(string|array $masks = ['*']): static
+ {
+ return $this->addMask((array) $masks, 'file');
+ }
+
+
+ /**
+ * Finds directories matching the specified masks.
+ */
+ public function directories(string|array $masks = ['*']): static
+ {
+ return $this->addMask((array) $masks, 'dir');
+ }
+
+
+ private function addMask(array $masks, string $mode): static
+ {
+ foreach ($masks as $mask) {
+ $mask = FileSystem::unixSlashes($mask);
+ if ($mode === 'dir') {
+ $mask = rtrim($mask, '/');
+ }
+ if ($mask === '' || ($mode === 'file' && str_ends_with($mask, '/'))) {
+ throw new Nette\InvalidArgumentException("Invalid mask '$mask'");
+ }
+ if (str_starts_with($mask, '**/')) {
+ $mask = substr($mask, 3);
+ }
+ $this->find[] = [$mask, $mode];
+ }
+ return $this;
+ }
+
+
+ /**
+ * Searches in the given directories. Wildcards are allowed.
+ */
+ public function in(string|array $paths): static
+ {
+ $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic
+ $this->addLocation($paths, '');
+ return $this;
+ }
+
+
+ /**
+ * Searches recursively from the given directories. Wildcards are allowed.
+ */
+ public function from(string|array $paths): static
+ {
+ $paths = is_array($paths) ? $paths : func_get_args(); // compatibility with variadic
+ $this->addLocation($paths, '/**');
+ return $this;
+ }
+
+
+ private function addLocation(array $paths, string $ext): void
+ {
+ foreach ($paths as $path) {
+ if ($path === '') {
+ throw new Nette\InvalidArgumentException("Invalid directory '$path'");
+ }
+ $path = rtrim(FileSystem::unixSlashes($path), '/');
+ $this->in[] = $path . $ext;
+ }
+ }
+
+
+ /**
+ * Lists directory's contents before the directory itself. By default, this is disabled.
+ */
+ public function childFirst(bool $state = true): static
+ {
+ $this->childFirst = $state;
+ return $this;
+ }
+
+
+ /**
+ * Ignores unreadable directories. By default, this is enabled.
+ */
+ public function ignoreUnreadableDirs(bool $state = true): static
+ {
+ $this->ignoreUnreadableDirs = $state;
+ return $this;
+ }
+
+
+ /**
+ * Set a compare function for sorting directory entries. The function will be called to sort entries from the same directory.
+ * @param callable(FileInfo, FileInfo): int $callback
+ */
+ public function sortBy(callable $callback): static
+ {
+ $this->sort = $callback;
+ return $this;
+ }
+
+
+ /**
+ * Sorts files in each directory naturally by name.
+ */
+ public function sortByName(): static
+ {
+ $this->sort = fn(FileInfo $a, FileInfo $b): int => strnatcmp($a->getBasename(), $b->getBasename());
+ return $this;
+ }
+
+
+ /**
+ * Adds the specified paths or appends a new finder that returns.
+ */
+ public function append(string|array|null $paths = null): static
+ {
+ if ($paths === null) {
+ return $this->appends[] = new static;
+ }
+
+ $this->appends = array_merge($this->appends, (array) $paths);
+ return $this;
+ }
+
+
+ /********************* filtering ****************d*g**/
+
+
+ /**
+ * Skips entries that matches the given masks relative to the ones defined with the in() or from() methods.
+ */
+ public function exclude(string|array $masks): static
+ {
+ $masks = is_array($masks) ? $masks : func_get_args(); // compatibility with variadic
+ foreach ($masks as $mask) {
+ $mask = FileSystem::unixSlashes($mask);
+ if (!preg_match('~^/?(\*\*/)?(.+)(/\*\*|/\*|/|)$~D', $mask, $m)) {
+ throw new Nette\InvalidArgumentException("Invalid mask '$mask'");
+ }
+ $end = $m[3];
+ $re = $this->buildPattern($m[2]);
+ $filter = fn(FileInfo $file): bool => ($end && !$file->isDir())
+ || !preg_match($re, FileSystem::unixSlashes($file->getRelativePathname()));
+
+ $this->descentFilter($filter);
+ if ($end !== '/*') {
+ $this->filter($filter);
+ }
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Yields only entries which satisfy the given filter.
+ * @param callable(FileInfo): bool $callback
+ */
+ public function filter(callable $callback): static
+ {
+ $this->filters[] = \Closure::fromCallable($callback);
+ return $this;
+ }
+
+
+ /**
+ * It descends only to directories that match the specified filter.
+ * @param callable(FileInfo): bool $callback
+ */
+ public function descentFilter(callable $callback): static
+ {
+ $this->descentFilters[] = \Closure::fromCallable($callback);
+ return $this;
+ }
+
+
+ /**
+ * Sets the maximum depth of entries.
+ */
+ public function limitDepth(?int $depth): static
+ {
+ $this->maxDepth = $depth ?? -1;
+ return $this;
+ }
+
+
+ /**
+ * Restricts the search by size. $operator accepts "[operator] [size] [unit]" example: >=10kB
+ */
+ public function size(string $operator, ?int $size = null): static
+ {
+ if (func_num_args() === 1) { // in $operator is predicate
+ if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?((?:\d*\.)?\d+)\s*(K|M|G|)B?$#Di', $operator, $matches)) {
+ throw new Nette\InvalidArgumentException('Invalid size predicate format.');
+ }
+
+ [, $operator, $size, $unit] = $matches;
+ $units = ['' => 1, 'k' => 1e3, 'm' => 1e6, 'g' => 1e9];
+ $size *= $units[strtolower($unit)];
+ $operator = $operator ?: '=';
+ }
+
+ return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getSize(), $operator, $size));
+ }
+
+
+ /**
+ * Restricts the search by modified time. $operator accepts "[operator] [date]" example: >1978-01-23
+ */
+ public function date(string $operator, string|int|\DateTimeInterface|null $date = null): static
+ {
+ if (func_num_args() === 1) { // in $operator is predicate
+ if (!preg_match('#^(?:([=<>!]=?|<>)\s*)?(.+)$#Di', $operator, $matches)) {
+ throw new Nette\InvalidArgumentException('Invalid date predicate format.');
+ }
+
+ [, $operator, $date] = $matches;
+ $operator = $operator ?: '=';
+ }
+
+ $date = DateTime::from($date)->format('U');
+ return $this->filter(fn(FileInfo $file): bool => !$file->isFile() || Helpers::compare($file->getMTime(), $operator, $date));
+ }
+
+
+ /********************* iterator generator ****************d*g**/
+
+
+ /**
+ * Returns an array with all found files and directories.
+ * @return list
+ */
+ public function collect(): array
+ {
+ return iterator_to_array($this->getIterator(), preserve_keys: false);
+ }
+
+
+ /** @return \Generator */
+ public function getIterator(): \Generator
+ {
+ $plan = $this->buildPlan();
+ foreach ($plan as $dir => $searches) {
+ yield from $this->traverseDir($dir, $searches);
+ }
+
+ foreach ($this->appends as $item) {
+ if ($item instanceof self) {
+ yield from $item->getIterator();
+ } else {
+ $item = FileSystem::platformSlashes($item);
+ yield $item => new FileInfo($item);
+ }
+ }
+ }
+
+
+ /**
+ * @param array