🆙 Add cms i using 🆙

This commit is contained in:
Remco
2025-11-25 22:42:56 +01:00
parent 94704e0925
commit d44196149e
35591 changed files with 3601123 additions and 0 deletions
@@ -0,0 +1,25 @@
The MIT License
---------------
Copyright (c) 2017-present Tomáš Votruba (https://tomasvotruba.cz)
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.
@@ -0,0 +1,166 @@
# Rector - Instant Upgrades and Automated Refactoring
[![Downloads](https://img.shields.io/packagist/dt/rector/rector.svg?style=flat-square)](https://packagist.org/packages/rector/rector)
<br>
Rector instantly upgrades and refactors the PHP code of your application. It can help you in 2 major areas:
### 1. Instant Upgrades
Rector now supports upgrades from PHP 5.3 to 8.4 and major open-source projects like [Symfony](https://github.com/rectorphp/rector-symfony), [PHPUnit](https://github.com/rectorphp/rector-phpunit), and [Doctrine](https://github.com/rectorphp/rector-doctrine). Do you want to **be constantly on the latest PHP and Framework without effort**?
Use Rector to handle **instant upgrades** for you.
### 2. Automated Refactoring
Do you have code quality you need, but struggle to keep it with new developers in your team? Do you want to see smart code-reviews even when every senior developers sleeps?
Add Rector to your CI and let it **continuously refactor your code** and keep the code quality high.
Read our [blogpost](https://getrector.com/blog/new-setup-ci-command-to-let-rector-work-for-you) to see how to set up automated refactoring.
## Install
```bash
composer require rector/rector --dev
```
## Running Rector
There are 2 main ways to use Rector:
- a *single rule*, to have the change under control
- or group of rules called *sets*
To use them, create a `rector.php` in your root directory:
```bash
vendor/bin/rector
```
And modify it:
```php
use Rector\Config\RectorConfig;
use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromStrictConstructorRector;
return RectorConfig::configure()
// register single rule
->withRules([
TypedPropertyFromStrictConstructorRector::class
])
// here we can define, what prepared sets of rules will be applied
->withPreparedSets(
deadCode: true,
codeQuality: true
);
```
Then dry run Rector:
```bash
vendor/bin/rector src --dry-run
```
Rector will show you diff of files that it *would* change. To *make* the changes, drop `--dry-run`:
```bash
vendor/bin/rector src
```
## Documentation
* Find [full documentation here](https://getrector.com/documentation/).
* [Explore Rector Rules](https://getrector.com/find-rule)
<br>
## Learn Faster with a Book
Are you curious, how Rector works internally, how to create your own rules and test them and why Rector was born?
Read [Rector - The Power of Automated Refactoring](https://leanpub.com/rector-the-power-of-automated-refactoring) that will take you step by step through the Rector setup and how to create your own rules.
<br>
## Empowered by Community :heart:
The Rector community is powerful thanks to active maintainers who take care of Rector sets for particular projects.
Among there projects belong:
* [palantirnet/drupal-rector](https://github.com/palantirnet/drupal-rector)
* [craftcms/rector](https://github.com/craftcms/rector)
* [FriendsOfShopware/shopware-rector](https://github.com/FriendsOfShopware/shopware-rector)
* [sabbelasichon/typo3-rector](https://github.com/sabbelasichon/typo3-rector)
* [sulu/sulu-rector](https://github.com/sulu/sulu-rector)
* [efabrica-team/rector-nette](https://github.com/efabrica-team/rector-nette)
* [Sylius/SyliusRector](https://github.com/Sylius/SyliusRector)
* [CoditoNet/rector-money](https://github.com/CoditoNet/rector-money)
* [laminas/laminas-servicemanager-migration](https://github.com/laminas/laminas-servicemanager-migration)
* [cakephp/upgrade](https://github.com/cakephp/upgrade)
* [driftingly/rector-laravel](https://github.com/driftingly/rector-laravel)
* [contao/contao-rector](https://github.com/contao/contao-rector)
* [php-static-analysis/rector-rule](https://github.com/php-static-analysis/rector-rule)
* [ibexa/rector](https://github.com/ibexa/rector)
<br>
## Hire us to get Job Done :muscle:
Rector is a tool that [we develop](https://getrector.com/) and share for free, so anyone can automate their refactoring. But not everyone has dozens of hours to understand complexity of abstract-syntax-tree in their own time. **That's why we provide commercial support - to save your time**.
Would you like to apply Rector on your code base but don't have time for the struggle with your project? [Hire us](https://getrector.com/contact) to get there faster.
<br>
## How to Contribute
See [the contribution guide](/CONTRIBUTING.md) or go to development repository [rector/rector-src](https://github.com/rectorphp/rector-src).
<br>
## Debugging
You can use `--debug` option, that will print nested exceptions output:
```bash
vendor/bin/rector src/Controller --dry-run --debug
```
Or with Xdebug:
1. Make sure [Xdebug](https://xdebug.org/) is installed and configured
2. Add `--xdebug` option when running Rector
```bash
vendor/bin/rector src/Controller --dry-run --xdebug
```
To assist with simple debugging Rector provides 2 helpers to pretty-print AST-nodes:
```php
use PhpParser\Node\Scalar\String_;
$node = new String_('hello world!');
// prints node to string, as PHP code displays it
print_node($node);
```
<br>
## Known Drawbacks
* Rector uses [nikic/php-parser](https://github.com/nikic/PHP-Parser/), built on technology called an *abstract syntax tree* (AST). An AST doesn't know about spaces and when written to a file it produces poorly formatted code in both PHP and docblock annotations.
* Rector in parallel mode will work most of the times for most OS. On Windows, you may encounter issues unresolvable despite of following the [Troubleshooting Parallel](https://getrector.com/documentation/troubleshooting-parallel) guide. In such case, check if you are using Powershell 7 (pwsh). Change your terminal to command prompt (cmd) or bash for Windows.
### How to Apply Coding Standards?
**Your project needs to have a coding standard tool** and a set of formatting rules, so it can make Rector's output code nice and shiny again.
We're using [ECS](https://github.com/symplify/easy-coding-standard) with [this setup](https://github.com/rectorphp/rector-src/blob/main/ecs.php).
### May cause unexpected output on File with mixed PHP+HTML content
When you apply changes to files with PHP + HTML content, you may need to manually verify the changed file after apply the changes.
@@ -0,0 +1,106 @@
# Upgrading from Rector 1.x to 2.0
## PHP version requirements
Rector now uses PHP 7.4 or newer to run.
<br>
## Rector now uses PHP-Parser 5
See [upgrading guide](https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md) for PHP-Parser.
<br>
## Rector now uses PHPStan 2
See [upgrading guide](https://github.com/phpstan/phpstan-src/blob/2.0.x/UPGRADING.md) for PHPStan.
<br>
## Upgrade for custom Rules writers
### 1. `AbstractScopeAwareRector` is removed, use `AbstractRector` instead
The `Rector\Rector\AbstractScopeAwareRector` was too granular to fetch single helper object. It made creating new custom rules ambiguous, one layer more complex and confusing. This class has been removed in favor of standard `AbstractRector`. The `Scope` object can be fetched via `ScopeFetcher`.
**Before**
```php
use Rector\Rector\AbstractScopeAwareRector;
final class SimpleRector extends AbstractScopeAwareRector
{
public function refactorWithScope(Node $node, Scope $scope): ?Node
{
// ...
}
}
```
**After**
```php
use Rector\Rector\AbstractRector;
use Rector\PHPStan\ScopeFetcher;
final class SimpleRector extends AbstractRector
{
public function refactor(Node $node): ?Node
{
if (...) {
// this allow to fetch scope only when needed
$scope = ScopeFetcher::fetch($node);
}
// ...
}
}
```
### 2. `AbstractRector` get focused on code, the `getRuleDefinition()` is no longer required
Core rules need documentation, so people can read their feature and [search through](https://getrector.com/find-rule) them. Yet for writing custom rules and local rules, its not necessary. People often filled it empty, just to make Rector happy.
This is no longer needed. Now The `getRuleDefinition()` method has been removed:
```diff
use Rector\Rector\AbstractRector;
-use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
-use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class SimpleRector extends AbstractRector
{
- public function getRuleDefinition(): RuleDefinition
- {
- return new RuleDefinition('// @todo fill the description', [
- new CodeSample(
- <<<'CODE_SAMPLE'
-// @todo fill code before
-CODE_SAMPLE
- ,
- <<<'CODE_SAMPLE'
-// @todo fill code after
-CODE_SAMPLE
- ),
- ]);
- }
// valuable code here
}
```
If you need description yourself to understand rule after many months, use the common place for documentation - docblock above class.
### 3. `SetListInterface` was removed
The deprecated `SetListInterface` was removed, if you created your own list just remove the Interface from it:
```diff
-use Rector\Set\Contract\SetListInterface;
-final class YourSetList implements SetListInterface
+final class YourSetList
```
@@ -0,0 +1,88 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202510;
use RectorPrefix202510\Nette\Loaders\RobotLoader;
use Rector\Bridge\SetRectorsResolver;
use RectorPrefix202510\Symfony\Component\Console\Input\ArrayInput;
use RectorPrefix202510\Symfony\Component\Console\Output\ConsoleOutput;
use RectorPrefix202510\Symfony\Component\Console\Style\SymfonyStyle;
use RectorPrefix202510\Symfony\Component\Finder\Finder;
use RectorPrefix202510\Symfony\Component\Finder\SplFileInfo;
use RectorPrefix202510\Webmozart\Assert\Assert;
require __DIR__ . '/../vendor/autoload.php';
// 1. find all rector rules in here and in all vendor/rector dirs
$rectorClassFinder = new RectorClassFinder();
$rectorClasses = $rectorClassFinder->find([__DIR__ . '/../rules', __DIR__ . '/../vendor/rector/rector-doctrine', __DIR__ . '/../vendor/rector/rector-phpunit', __DIR__ . '/../vendor/rector/rector-symfony', __DIR__ . '/../vendor/rector/rector-downgrade-php']);
$symfonyStyle = new SymfonyStyle(new ArrayInput([]), new ConsoleOutput());
$symfonyStyle->writeln(\sprintf('<fg=green>Found Rector %d rules</>', \count($rectorClasses)));
$rectorSeFinder = new RectorSetFinder();
$rectorSetFiles = $rectorSeFinder->find([__DIR__ . '/../config/set', __DIR__ . '/../vendor/rector/rector-symfony/config/sets', __DIR__ . '/../vendor/rector/rector-doctrine/config/sets', __DIR__ . '/../vendor/rector/rector-phpunit/config/sets', __DIR__ . '/../vendor/rector/rector-downgrade-php/config/set']);
$symfonyStyle->writeln(\sprintf('<fg=green>Found %d sets</>', \count($rectorSetFiles)));
$usedRectorClassResolver = new UsedRectorClassResolver();
$usedRectorRules = $usedRectorClassResolver->resolve($rectorSetFiles);
$symfonyStyle->newLine();
$symfonyStyle->writeln(\sprintf('<fg=yellow>Found %d used Rector rules in sets</>', \count($usedRectorRules)));
$unusedRectorRules = \array_diff($rectorClasses, $usedRectorRules);
$symfonyStyle->writeln(\sprintf('<fg=yellow;options=bold>Found %d Rector rules not in any set</>', \count($unusedRectorRules)));
$symfonyStyle->newLine();
$symfonyStyle->listing($unusedRectorRules);
final class RectorClassFinder
{
/**
* @param string[] $dirs
* @return string[]
*/
public function find(array $dirs): array
{
$robotLoader = new RobotLoader();
$robotLoader->acceptFiles = ['*Rector.php'];
$robotLoader->addDirectory(...$dirs);
$robotLoader->setTempDirectory(\sys_get_temp_dir() . '/rector-rules');
$robotLoader->refresh();
return \array_keys($robotLoader->getIndexedClasses());
}
}
\class_alias('RectorClassFinder', 'RectorClassFinder', \false);
final class RectorSetFinder
{
/**
* @param string[] $configDirs
* @return string[]
*/
public function find(array $configDirs): array
{
Assert::allString($configDirs);
Assert::allDirectory($configDirs);
// find set files
$finder = (new Finder())->in($configDirs)->files()->name('*.php');
/** @var SplFileInfo[] $setFileInfos */
$setFileInfos = \iterator_to_array($finder->getIterator());
$setFiles = [];
foreach ($setFileInfos as $setFileInfo) {
$setFiles[] = $setFileInfo->getRealPath();
}
return $setFiles;
}
}
\class_alias('RectorSetFinder', 'RectorSetFinder', \false);
final class UsedRectorClassResolver
{
/**
* @param string[] $rectorSetFiles
* @return string[]
*/
public function resolve(array $rectorSetFiles): array
{
$setRectorsResolver = new SetRectorsResolver();
$rulesConfiguration = $setRectorsResolver->resolveFromFilePathsIncludingConfiguration($rectorSetFiles);
$usedRectorRules = [];
foreach ($rulesConfiguration as $ruleConfiguration) {
$usedRectorRules[] = \is_string($ruleConfiguration) ? $ruleConfiguration : \array_keys($ruleConfiguration)[0];
}
\sort($usedRectorRules);
return \array_unique($usedRectorRules);
}
}
\class_alias('UsedRectorClassResolver', 'UsedRectorClassResolver', \false);
@@ -0,0 +1,5 @@
#!/usr/bin/env php
<?php
namespace RectorPrefix202511;
require_once __DIR__ . '/rector.php';
@@ -0,0 +1,130 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use RectorPrefix202511\Nette\Utils\Json;
use Rector\Bootstrap\RectorConfigsResolver;
use Rector\ChangesReporting\Output\JsonOutputFormatter;
use Rector\Configuration\Option;
use Rector\Console\Style\SymfonyStyleFactory;
use Rector\DependencyInjection\LazyContainerFactory;
use Rector\DependencyInjection\RectorContainerFactory;
use Rector\Util\Reflection\PrivatesAccessor;
use RectorPrefix202511\Symfony\Component\Console\Application;
use RectorPrefix202511\Symfony\Component\Console\Command\Command;
use RectorPrefix202511\Symfony\Component\Console\Input\ArgvInput;
// @ intentionally: continue anyway
@\ini_set('memory_limit', '-1');
// Performance boost
\error_reporting(\E_ALL);
\ini_set('display_errors', 'stderr');
\gc_disable();
\define('__RECTOR_RUNNING__', \true);
// Require Composer autoload.php
$autoloadIncluder = new AutoloadIncluder();
$autoloadIncluder->includeDependencyOrRepositoryVendorAutoloadIfExists();
final class AutoloadIncluder
{
/**
* @var string[]
*/
private array $alreadyLoadedAutoloadFiles = [];
public function includeDependencyOrRepositoryVendorAutoloadIfExists(): void
{
// Rector's vendor is already loaded
if (\class_exists(LazyContainerFactory::class)) {
return;
}
// in Rector develop repository
$this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/autoload.php');
}
/**
* In case Rector is installed as vendor dependency,
* this autoloads the project vendor/autoload.php, including Rector
*/
public function autoloadProjectAutoloaderFile(): void
{
$this->loadIfExistsAndNotLoadedYet(__DIR__ . '/../../../autoload.php');
}
/**
* In case Rector is installed as global dependency
*/
public function autoloadRectorInstalledAsGlobalDependency(): void
{
if (\dirname(__DIR__) === \dirname(\getcwd(), 2)) {
return;
}
if (\is_dir('vendor/rector/rector')) {
return;
}
$this->loadIfExistsAndNotLoadedYet('vendor/autoload.php');
}
public function autoloadFromCommandLine(): void
{
$cliArgs = $_SERVER['argv'];
$aOptionPosition = \array_search('-a', $cliArgs, \true);
$autoloadFileOptionPosition = \array_search('--autoload-file', $cliArgs, \true);
if (\is_int($aOptionPosition)) {
$autoloadOptionPosition = $aOptionPosition;
} elseif (\is_int($autoloadFileOptionPosition)) {
$autoloadOptionPosition = $autoloadFileOptionPosition;
} else {
return;
}
$autoloadFileValuePosition = $autoloadOptionPosition + 1;
$fileToAutoload = $cliArgs[$autoloadFileValuePosition] ?? null;
if ($fileToAutoload === null) {
return;
}
$this->loadIfExistsAndNotLoadedYet($fileToAutoload);
}
public function loadIfExistsAndNotLoadedYet(string $filePath): void
{
if (!\file_exists($filePath)) {
return;
}
if (\in_array($filePath, $this->alreadyLoadedAutoloadFiles, \true)) {
return;
}
/** @var non-empty-string $realPath always string after file_exists() check */
$realPath = \realpath($filePath);
$this->alreadyLoadedAutoloadFiles[] = $realPath;
require_once $filePath;
}
}
\class_alias('RectorPrefix202511\AutoloadIncluder', 'AutoloadIncluder', \false);
if (\file_exists(__DIR__ . '/../preload.php') && \is_dir(__DIR__ . '/../vendor')) {
require_once __DIR__ . '/../preload.php';
}
// require rector-src on split packages
if (\file_exists(__DIR__ . '/../preload-split-package.php') && \is_dir(__DIR__ . '/../../../../vendor')) {
require_once __DIR__ . '/../preload-split-package.php';
}
$autoloadIncluder->loadIfExistsAndNotLoadedYet(__DIR__ . '/../vendor/scoper-autoload.php');
$autoloadIncluder->autoloadProjectAutoloaderFile();
$autoloadIncluder->autoloadRectorInstalledAsGlobalDependency();
$autoloadIncluder->autoloadFromCommandLine();
$rectorConfigsResolver = new RectorConfigsResolver();
try {
$bootstrapConfigs = $rectorConfigsResolver->provide();
$rectorContainerFactory = new RectorContainerFactory();
$container = $rectorContainerFactory->createFromBootstrapConfigs($bootstrapConfigs);
} catch (\Throwable $throwable) {
// for json output
$argvInput = new ArgvInput();
$outputFormat = $argvInput->getParameterOption('--' . Option::OUTPUT_FORMAT);
// report fatal error in json format
if ($outputFormat === JsonOutputFormatter::NAME) {
echo Json::encode(['fatal_errors' => [$throwable->getMessage()]]);
} else {
// report fatal errors in console format
$symfonyStyleFactory = new SymfonyStyleFactory(new PrivatesAccessor());
$symfonyStyle = $symfonyStyleFactory->create();
$symfonyStyle->error(\str_replace("\r\n", "\n", $throwable->getMessage()));
}
exit(Command::FAILURE);
}
/** @var Application $application */
$application = $container->get(Application::class);
exit($application->run());
@@ -0,0 +1,55 @@
<?php
declare (strict_types=1);
namespace RectorPrefix20220529;
// resolve git version stored in released rector
// tag for tagged one, hash for dev
/**
* Inspired by https://github.com/composer/composer/blob/master/src/Composer/Composer.php
* See https://github.com/composer/composer/blob/6587715d0f8cae0cd39073b3bc5f018d0e6b84fe/src/Composer/Compiler.php#L208
*
* @see \Rector\Core\Tests\Application\VersionResolverTest
*/
final class VersionResolver
{
/**
* @var int
*/
private const SUCCESS_CODE = 0;
public function resolve() : string
{
// resolve current tag
\exec('git tag --points-at', $tagExecOutput, $tagExecResultCode);
if ($tagExecResultCode !== self::SUCCESS_CODE) {
die('Ensure to run compile from composer git repository clone and that git binary is available.');
}
// debug output
\var_dump($tagExecOutput);
if ($tagExecOutput !== []) {
$tag = $tagExecOutput[0];
if ($tag !== '') {
return $tag;
}
}
\exec('git log --pretty="%H" -n1 HEAD', $commitHashExecOutput, $commitHashResultCode);
if ($commitHashResultCode !== 0) {
die('Ensure to run compile from composer git repository clone and that git binary is available.');
}
// debug output
\var_dump($commitHashExecOutput);
$version = \trim($commitHashExecOutput[0]);
return \trim($version, '"');
}
}
// resolve git version stored in released rector
// tag for tagged one, hash for dev
/**
* Inspired by https://github.com/composer/composer/blob/master/src/Composer/Composer.php
* See https://github.com/composer/composer/blob/6587715d0f8cae0cd39073b3bc5f018d0e6b84fe/src/Composer/Compiler.php#L208
*
* @see \Rector\Core\Tests\Application\VersionResolverTest
*/
\class_alias('VersionResolver', 'VersionResolver', \false);
$versionResolver = new \RectorPrefix20220529\VersionResolver();
echo $versionResolver->resolve() . \PHP_EOL;
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
use PHPParser\Node;
use PHPUnit\Runner\Version;
/**
* The preload.php contains 2 dependencies
* - phpstan/phpdoc-parser
* - nikic/php-parser
*
* They need to be loaded early to avoid conflict version between rector prefixed vendor and Project vendor
* For example, a project may use phpstan/phpdoc-parser v1, while rector uses phpstan/phpdoc-parser uses v2, that will error as class or logic are different.
*/
if (
// verify PHPUnit is running
defined('PHPUNIT_COMPOSER_INSTALL')
// no need to preload if Node interface exists
&& ! interface_exists(Node::class, false)
// load preload.php on local PHPUnit installation
&& ! class_exists(Version::class, false)
// load preload.php only on PHPUnit 12+
&& class_exists(Version::class, true) && (int) Version::id() >= 12
) {
require_once __DIR__ . '/preload.php';
}
// inspired by https://github.com/phpstan/phpstan/blob/master/bootstrap.php
spl_autoload_register(function (string $class): void {
static $composerAutoloader;
// already loaded in bin/rector.php
if (defined('__RECTOR_RUNNING__')) {
return;
}
// load prefixed or native class, e.g. for running tests
if (strpos($class, 'RectorPrefix') === 0 || strpos($class, 'Rector\\') === 0) {
if ($composerAutoloader === null) {
// prefixed version autoload
$composerAutoloader = require __DIR__ . '/vendor/autoload.php';
}
// some weird collision with PHPStan custom rule tests
if (! is_int($composerAutoloader)) {
$composerAutoloader->loadClass($class);
}
}
});
@@ -0,0 +1,30 @@
{
"name": "rector/rector",
"description": "Instant Upgrade and Automated Refactoring of any PHP code",
"homepage": "https://getrector.com/",
"license": "MIT",
"keywords": ["dev", "refactoring", "automation", "migration"],
"bin": [
"bin/rector"
],
"require": {
"php": "^7.4|^8.0",
"phpstan/phpstan": "^2.1.32"
},
"autoload": {
"files": [
"bootstrap.php"
]
},
"conflict": {
"rector/rector-phpunit": "*",
"rector/rector-symfony": "*",
"rector/rector-doctrine": "*",
"rector/rector-downgrade-php": "*"
},
"minimum-stability": "dev",
"prefer-stable": true,
"suggest": {
"ext-dom": "To manipulate phpunit.xml via the custom-rule command"
}
}
@@ -0,0 +1,39 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use RectorPrefix202511\OndraM\CiDetector\CiDetector;
use Rector\Bootstrap\ExtensionConfigResolver;
use Rector\Caching\ValueObject\Storage\MemoryCacheStorage;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->paths([]);
$rectorConfig->skip([]);
$rectorConfig->autoloadPaths([]);
$rectorConfig->bootstrapFiles([]);
$rectorConfig->parallel();
// to avoid autoimporting out of the box
$rectorConfig->importNames(\false, \false);
$rectorConfig->removeUnusedImports(\false);
$rectorConfig->importShortClasses();
$rectorConfig->indent(' ', 4);
$rectorConfig->fileExtensions(['php']);
$rectorConfig->cacheDirectory(\sys_get_temp_dir() . '/rector_cached_files');
$rectorConfig->containerCacheDirectory(\sys_get_temp_dir());
// use faster in-memory cache in CI.
// CI always starts from scratch, therefore IO intensive caching is not worth it
if ((new CiDetector())->isCiDetected()) {
$rectorConfig->cacheClass(MemoryCacheStorage::class);
}
// load internal rector-* extension configs
$extensionConfigResolver = new ExtensionConfigResolver();
foreach ($extensionConfigResolver->provide() as $extensionConfigFile) {
$rectorConfig->import($extensionConfigFile);
}
// use original php-parser printer to avoid BC break on fluent call
$rectorConfig->newLineOnFluentCall(\false);
// allow real paths in output formatters
$rectorConfig->reportingRealPath(\false);
$rectorConfig->treatClassesAsFinal(\false);
};
@@ -0,0 +1,2 @@
parameters:
inferPrivatePropertyTypeFromConstructor: true
@@ -0,0 +1,27 @@
# see original config.neon in phpstan.neon - https://github.com/phpstan/phpstan-src/blob/386eb913abb6ac05886c5642fd48b5d99db66a20/conf/config.neon#L1582
# this file overrides definitions from the config above
services:
defaultAnalysisParser:
factory: @pathRoutingParser
arguments!: []
cachedRectorParser:
class: PHPStan\Parser\CachedParser
arguments:
originalParser: @rectorParser
cachedNodesByStringCountMax: %cache.nodesByStringCountMax%
autowired: false
pathRoutingParser:
class: PHPStan\Parser\PathRoutingParser
arguments:
currentPhpVersionRichParser: @cachedRectorParser
currentPhpVersionSimpleParser: @cachedRectorParser
php8Parser: @php8Parser
autowired: false
rectorParser:
class: PHPStan\Parser\RichParser
arguments:
parser: @currentPhpVersionPhpParser
autowired: no
@@ -0,0 +1,10 @@
services:
- Rector\NodeTypeResolver\Reflection\BetterReflection\RectorBetterReflectionSourceLocatorFactory
- Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocator\IntermediateSourceLocator
- Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider
# basically decorates native PHPStan source locator with a dynamic source locator that is also available in Rector DI
betterReflectionSourceLocator:
class: PHPStan\BetterReflection\SourceLocator\Type\SourceLocator
factory: ['@Rector\NodeTypeResolver\Reflection\BetterReflection\RectorBetterReflectionSourceLocatorFactory', 'create']
autowired: false
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Assert\Rector\ClassMethod\AddAssertArrayFromClassMethodDocblockRector;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([AddAssertArrayFromClassMethodDocblockRector::class]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php80\Rector\Class_\AnnotationToAttributeRector;
use Rector\Php80\ValueObject\AnnotationToAttribute;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(AnnotationToAttributeRector::class, [new AnnotationToAttribute('Given', 'Behat\Step\Given', [], \true), new AnnotationToAttribute('When', 'Behat\Step\When', [], \true), new AnnotationToAttribute('Then', 'Behat\Step\Then', [], \true), new AnnotationToAttribute('BeforeSuite', 'Behat\Hook\BeforeSuite', [], \true), new AnnotationToAttribute('AfterSuite', 'Behat\Hook\AfterSuite', [], \true), new AnnotationToAttribute('BeforeFeature', 'Behat\Hook\BeforeFeature', [], \true), new AnnotationToAttribute('AfterFeature', 'Behat\Hook\AfterFeature', [], \true), new AnnotationToAttribute('BeforeScenario', 'Behat\Hook\BeforeScenario', [], \true), new AnnotationToAttribute('AfterScenario', 'Behat\Hook\AfterScenario', [], \true), new AnnotationToAttribute('BeforeStep', 'Behat\Hook\BeforeStep', [], \true), new AnnotationToAttribute('AfterStep', 'Behat\Hook\AfterStep', [], \true)]);
};
@@ -0,0 +1,15 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\Level\CodeQualityLevel;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
foreach (CodeQualityLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) {
$rectorConfig->ruleWithConfiguration($rectorClass, $configuration);
}
// the rule order matters, as its used in withCodeQualityLevel() method
// place the safest rules first, follow by more complex ones
$rectorConfig->rules(CodeQualityLevel::RULES);
};
@@ -0,0 +1,15 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\Level\CodingStyleLevel;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
foreach (CodingStyleLevel::RULES_WITH_CONFIGURATION as $rectorClass => $configuration) {
$rectorConfig->ruleWithConfiguration($rectorClass, $configuration);
}
// the rule order matters, as its used in withCodingStyleLevel() method
// place the safest rules first, follow by more complex ones
$rectorConfig->rules(CodingStyleLevel::RULES);
};
@@ -0,0 +1,13 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Carbon\Rector\FuncCall\DateFuncCallToCarbonRector;
use Rector\Carbon\Rector\FuncCall\TimeFuncCallToCarbonRector;
use Rector\Carbon\Rector\MethodCall\DateTimeMethodCallToCarbonRector;
use Rector\Carbon\Rector\New_\DateTimeInstanceToCarbonRector;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([DateFuncCallToCarbonRector::class, DateTimeInstanceToCarbonRector::class, DateTimeMethodCallToCarbonRector::class, TimeFuncCallToCarbonRector::class]);
};
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\Level\DeadCodeLevel;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules(DeadCodeLevel::RULES);
};
@@ -0,0 +1,17 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\EarlyReturn\Rector\Foreach_\ChangeNestedForeachIfsToEarlyContinueRector;
use Rector\EarlyReturn\Rector\If_\ChangeIfElseValueAssignToEarlyReturnRector;
use Rector\EarlyReturn\Rector\If_\ChangeNestedIfsToEarlyReturnRector;
use Rector\EarlyReturn\Rector\If_\ChangeOrIfContinueToMultiContinueRector;
use Rector\EarlyReturn\Rector\If_\RemoveAlwaysElseRector;
use Rector\EarlyReturn\Rector\Return_\PreparedValueToEarlyReturnRector;
use Rector\EarlyReturn\Rector\Return_\ReturnBinaryOrToEarlyReturnRector;
use Rector\EarlyReturn\Rector\StmtsAwareInterface\ReturnEarlyIfVariableRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ChangeNestedForeachIfsToEarlyContinueRector::class, ChangeIfElseValueAssignToEarlyReturnRector::class, ChangeNestedIfsToEarlyReturnRector::class, RemoveAlwaysElseRector::class, ChangeOrIfContinueToMultiContinueRector::class, PreparedValueToEarlyReturnRector::class, ReturnBinaryOrToEarlyReturnRector::class, ReturnEarlyIfVariableRector::class]);
};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,16 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\CodeQuality\Rector\FuncCall\InlineIsAInstanceOfRector;
use Rector\CodeQuality\Rector\Identical\FlipTypeControlToUseExclusiveTypeRector;
use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\If_\RemoveDeadInstanceOfRector;
use Rector\Instanceof_\Rector\Ternary\FlipNegatedTernaryInstanceofRector;
use Rector\TypeDeclaration\Rector\BooleanAnd\BinaryOpNullableToInstanceofRector;
use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector;
use Rector\TypeDeclaration\Rector\While_\WhileNullableToInstanceofRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([EmptyOnNullableObjectToInstanceOfRector::class, InlineIsAInstanceOfRector::class, FlipTypeControlToUseExclusiveTypeRector::class, RemoveDeadInstanceOfRector::class, FlipNegatedTernaryInstanceofRector::class, BinaryOpNullableToInstanceofRector::class, WhileNullableToInstanceofRector::class]);
};
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_53, SetList::PHP_52]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_54, LevelSetList::UP_TO_PHP_53]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_55, LevelSetList::UP_TO_PHP_54]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_56, LevelSetList::UP_TO_PHP_55]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_70, LevelSetList::UP_TO_PHP_56]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_71, LevelSetList::UP_TO_PHP_70]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_72, LevelSetList::UP_TO_PHP_71]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_73, LevelSetList::UP_TO_PHP_72]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_74, LevelSetList::UP_TO_PHP_73]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_80, LevelSetList::UP_TO_PHP_74]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_81, LevelSetList::UP_TO_PHP_80]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_82, LevelSetList::UP_TO_PHP_81]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_83, LevelSetList::UP_TO_PHP_82]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_84, LevelSetList::UP_TO_PHP_83]);
};
@@ -0,0 +1,11 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->sets([SetList::PHP_85, LevelSetList::UP_TO_PHP_84]);
};
@@ -0,0 +1,15 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Naming\Rector\Assign\RenameVariableToMatchMethodCallReturnTypeRector;
use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector;
use Rector\Naming\Rector\ClassMethod\RenameParamToMatchTypeRector;
use Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector;
use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchExprVariableRector;
use Rector\Naming\Rector\Foreach_\RenameForeachValueVariableToMatchMethodCallReturnTypeRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([RenameParamToMatchTypeRector::class, RenamePropertyToMatchTypeRector::class, RenameVariableToMatchNewTypeRector::class, RenameVariableToMatchMethodCallReturnTypeRector::class, RenameForeachValueVariableToMatchMethodCallReturnTypeRector::class, RenameForeachValueVariableToMatchExprVariableRector::class]);
};
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\NetteUtils\Rector\StaticCall\UtilsJsonStaticCallNamedArgRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([UtilsJsonStaticCallNamedArgRector::class]);
};
@@ -0,0 +1,18 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php73\Rector\BooleanOr\IsCountableRector;
use Rector\Php73\Rector\FuncCall\ArrayKeyFirstLastRector;
use Rector\Php80\Rector\Identical\StrEndsWithRector;
use Rector\Php80\Rector\Identical\StrStartsWithRector;
use Rector\Php80\Rector\NotIdentical\StrContainsRector;
use Rector\Php80\Rector\Ternary\GetDebugTypeRector;
// @note longer rule registration must be used here, to separate from withRules() from root rector.php
// these rules can be used ahead of PHP version,
// as long composer.json includes particular symfony/php-polyfill package
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ArrayKeyFirstLastRector::class, IsCountableRector::class, GetDebugTypeRector::class, StrStartsWithRector::class, StrEndsWithRector::class, StrContainsRector::class]);
};
@@ -0,0 +1,17 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php52\Rector\Property\VarToPublicPropertyRector;
use Rector\Php52\Rector\Switch_\ContinueToBreakInSwitchRector;
use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector;
use Rector\Removing\ValueObject\RemoveFuncCallArg;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([VarToPublicPropertyRector::class, ContinueToBreakInSwitchRector::class]);
$rectorConfig->ruleWithConfiguration(RemoveFuncCallArgRector::class, [
// see https://www.php.net/manual/en/function.ldap-first-attribute.php
new RemoveFuncCallArg('ldap_first_attribute', 2),
]);
};
@@ -0,0 +1,12 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php53\Rector\FuncCall\DirNameFileConstantToDirConstantRector;
use Rector\Php53\Rector\Ternary\TernaryToElvisRector;
use Rector\Php53\Rector\Variable\ReplaceHttpServerVarsByServerRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([TernaryToElvisRector::class, DirNameFileConstantToDirConstantRector::class, ReplaceHttpServerVarsByServerRector::class]);
};
@@ -0,0 +1,14 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php54\Rector\Array_\LongArrayToShortArrayRector;
use Rector\Php54\Rector\Break_\RemoveZeroBreakContinueRector;
use Rector\Php54\Rector\FuncCall\RemoveReferenceFromCallRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([LongArrayToShortArrayRector::class, RemoveReferenceFromCallRector::class, RemoveZeroBreakContinueRector::class]);
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, ['mysqli_param_count' => 'mysqli_stmt_param_count']);
};
@@ -0,0 +1,15 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php55\Rector\Class_\ClassConstantToSelfClassRector;
use Rector\Php55\Rector\ClassConstFetch\StaticToSelfOnFinalClassRector;
use Rector\Php55\Rector\FuncCall\GetCalledClassToSelfClassRector;
use Rector\Php55\Rector\FuncCall\GetCalledClassToStaticClassRector;
use Rector\Php55\Rector\FuncCall\PregReplaceEModifierRector;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([StringClassNameToClassConstantRector::class, ClassConstantToSelfClassRector::class, PregReplaceEModifierRector::class, GetCalledClassToSelfClassRector::class, GetCalledClassToStaticClassRector::class, StaticToSelfOnFinalClassRector::class]);
};
@@ -0,0 +1,12 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php56\Rector\FuncCall\PowToExpRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rule(PowToExpRector::class);
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, ['mcrypt_generic_end' => 'mcrypt_generic_deinit', 'set_socket_blocking' => 'stream_set_blocking', 'ocibindbyname' => 'oci_bind_by_name', 'ocicancel' => 'oci_cancel', 'ocicolumnisnull' => 'oci_field_is_null', 'ocicolumnname' => 'oci_field_name', 'ocicolumnprecision' => 'oci_field_precision', 'ocicolumnscale' => 'oci_field_scale', 'ocicolumnsize' => 'oci_field_size', 'ocicolumntype' => 'oci_field_type', 'ocicolumntyperaw' => 'oci_field_type_raw', 'ocicommit' => 'oci_commit', 'ocidefinebyname' => 'oci_define_by_name', 'ocierror' => 'oci_error', 'ociexecute' => 'oci_execute', 'ocifetch' => 'oci_fetch', 'ocifetchstatement' => 'oci_fetch_all', 'ocifreecursor' => 'oci_free_statement', 'ocifreestatement' => 'oci_free_statement', 'ociinternaldebug' => 'oci_internal_debug', 'ocilogoff' => 'oci_close', 'ocilogon' => 'oci_connect', 'ocinewcollection' => 'oci_new_collection', 'ocinewcursor' => 'oci_new_cursor', 'ocinewdescriptor' => 'oci_new_descriptor', 'ocinlogon' => 'oci_new_connect', 'ocinumcols' => 'oci_num_fields', 'ociparse' => 'oci_parse', 'ociplogon' => 'oci_pconnect', 'ociresult' => 'oci_result', 'ocirollback' => 'oci_rollback', 'ocirowcount' => 'oci_num_rows', 'ociserverversion' => 'oci_server_version', 'ocisetprefetch' => 'oci_set_prefetch', 'ocistatementtype' => 'oci_statement_type']);
};
@@ -0,0 +1,49 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php70\Rector\Assign\ListSplitStringRector;
use Rector\Php70\Rector\Assign\ListSwapArrayOrderRector;
use Rector\Php70\Rector\Break_\BreakNotInLoopOrSwitchToReturnRector;
use Rector\Php70\Rector\ClassMethod\Php4ConstructorRector;
use Rector\Php70\Rector\FuncCall\CallUserMethodRector;
use Rector\Php70\Rector\FuncCall\EregToPregMatchRector;
use Rector\Php70\Rector\FuncCall\MultiDirnameRector;
use Rector\Php70\Rector\FuncCall\RandomFunctionRector;
use Rector\Php70\Rector\FuncCall\RenameMktimeWithoutArgsToTimeRector;
use Rector\Php70\Rector\FunctionLike\ExceptionHandlerTypehintRector;
use Rector\Php70\Rector\If_\IfToSpaceshipRector;
use Rector\Php70\Rector\List_\EmptyListRector;
use Rector\Php70\Rector\MethodCall\ThisCallOnStaticMethodToStaticCallRector;
use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector;
use Rector\Php70\Rector\StmtsAwareInterface\IfIssetToCoalescingRector;
use Rector\Php70\Rector\Switch_\ReduceMultipleDefaultSwitchRector;
use Rector\Php70\Rector\Ternary\TernaryToNullCoalescingRector;
use Rector\Php70\Rector\Ternary\TernaryToSpaceshipRector;
use Rector\Php70\Rector\Variable\WrapVariableVariableNameInCurlyBracesRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([
Php4ConstructorRector::class,
TernaryToNullCoalescingRector::class,
RandomFunctionRector::class,
ExceptionHandlerTypehintRector::class,
MultiDirnameRector::class,
ListSplitStringRector::class,
EmptyListRector::class,
// be careful, run this just once, since it can keep swapping order back and forth
ListSwapArrayOrderRector::class,
CallUserMethodRector::class,
EregToPregMatchRector::class,
ReduceMultipleDefaultSwitchRector::class,
TernaryToSpaceshipRector::class,
WrapVariableVariableNameInCurlyBracesRector::class,
IfToSpaceshipRector::class,
StaticCallOnNonStaticToInstanceCallRector::class,
ThisCallOnStaticMethodToStaticCallRector::class,
BreakNotInLoopOrSwitchToReturnRector::class,
RenameMktimeWithoutArgsToTimeRector::class,
IfIssetToCoalescingRector::class,
]);
};
@@ -0,0 +1,15 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php71\Rector\Assign\AssignArrayToStringRector;
use Rector\Php71\Rector\BinaryOp\BinaryOpBetweenNumberAndStringRector;
use Rector\Php71\Rector\BooleanOr\IsIterableRector;
use Rector\Php71\Rector\FuncCall\RemoveExtraParametersRector;
use Rector\Php71\Rector\List_\ListToArrayDestructRector;
use Rector\Php71\Rector\TryCatch\MultiExceptionCatchRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([IsIterableRector::class, MultiExceptionCatchRector::class, AssignArrayToStringRector::class, RemoveExtraParametersRector::class, BinaryOpBetweenNumberAndStringRector::class, ListToArrayDestructRector::class]);
};
@@ -0,0 +1,30 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php72\Rector\Assign\ListEachRector;
use Rector\Php72\Rector\Assign\ReplaceEachAssignmentWithKeyCurrentRector;
use Rector\Php72\Rector\FuncCall\CreateFunctionToAnonymousFunctionRector;
use Rector\Php72\Rector\FuncCall\GetClassOnNullRector;
use Rector\Php72\Rector\FuncCall\ParseStrWithResultArgumentRector;
use Rector\Php72\Rector\FuncCall\StringifyDefineRector;
use Rector\Php72\Rector\FuncCall\StringsAssertNakedRector;
use Rector\Php72\Rector\Unset_\UnsetCastRector;
use Rector\Php72\Rector\While_\WhileEachToForeachRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [
# and imagewbmp
'jpeg2wbmp' => 'imagecreatefromjpeg',
# or imagewbmp
'png2wbmp' => 'imagecreatefrompng',
#migration72.deprecated.gmp_random-function
# http://php.net/manual/en/migration72.deprecated.php
# or gmp_random_range
'gmp_random' => 'gmp_random_bits',
'read_exif_data' => 'exif_read_data',
]);
$rectorConfig->rules([GetClassOnNullRector::class, ParseStrWithResultArgumentRector::class, StringsAssertNakedRector::class, CreateFunctionToAnonymousFunctionRector::class, StringifyDefineRector::class, WhileEachToForeachRector::class, ListEachRector::class, ReplaceEachAssignmentWithKeyCurrentRector::class, UnsetCastRector::class]);
};
@@ -0,0 +1,35 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php73\Rector\BooleanOr\IsCountableRector;
use Rector\Php73\Rector\ConstFetch\SensitiveConstantNameRector;
use Rector\Php73\Rector\FuncCall\ArrayKeyFirstLastRector;
use Rector\Php73\Rector\FuncCall\RegexDashEscapeRector;
use Rector\Php73\Rector\FuncCall\SensitiveDefineRector;
use Rector\Php73\Rector\FuncCall\SetCookieRector;
use Rector\Php73\Rector\FuncCall\StringifyStrNeedlesRector;
use Rector\Php73\Rector\String_\SensitiveHereNowDocRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [
# https://wiki.php.net/rfc/deprecations_php_7_3
'image2wbmp' => 'imagewbmp',
'mbregex_encoding' => 'mb_regex_encoding',
'mbereg' => 'mb_ereg',
'mberegi' => 'mb_eregi',
'mbereg_replace' => 'mb_ereg_replace',
'mberegi_replace' => 'mb_eregi_replace',
'mbsplit' => 'mb_split',
'mbereg_match' => 'mb_ereg_match',
'mbereg_search' => 'mb_ereg_search',
'mbereg_search_pos' => 'mb_ereg_search_pos',
'mbereg_search_regs' => 'mb_ereg_search_regs',
'mbereg_search_init' => 'mb_ereg_search_init',
'mbereg_search_getregs' => 'mb_ereg_search_getregs',
'mbereg_search_getpos' => 'mb_ereg_search_getpos',
]);
$rectorConfig->rules([StringifyStrNeedlesRector::class, RegexDashEscapeRector::class, SetCookieRector::class, IsCountableRector::class, ArrayKeyFirstLastRector::class, SensitiveDefineRector::class, SensitiveConstantNameRector::class, SensitiveHereNowDocRector::class]);
};
@@ -0,0 +1,31 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use PhpParser\Node\Expr\Cast\Double;
use Rector\Config\RectorConfig;
use Rector\Php74\Rector\ArrayDimFetch\CurlyToSquareBracketArrayStringRector;
use Rector\Php74\Rector\Assign\NullCoalescingOperatorRector;
use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector;
use Rector\Php74\Rector\FuncCall\ArrayKeyExistsOnPropertyRector;
use Rector\Php74\Rector\FuncCall\FilterVarToAddSlashesRector;
use Rector\Php74\Rector\FuncCall\HebrevcToNl2brHebrevRector;
use Rector\Php74\Rector\FuncCall\MbStrrposEncodingArgumentPositionRector;
use Rector\Php74\Rector\FuncCall\MoneyFormatToNumberFormatRector;
use Rector\Php74\Rector\FuncCall\RestoreIncludePathToIniRestoreRector;
use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector;
use Rector\Php74\Rector\StaticCall\ExportToReflectionFunctionRector;
use Rector\Php74\Rector\Ternary\ParenthesizeNestedTernaryRector;
use Rector\Renaming\Rector\Cast\RenameCastRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Renaming\ValueObject\RenameCast;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [
#the_real_type
# https://wiki.php.net/rfc/deprecations_php_7_4
'is_real' => 'is_float',
]);
$rectorConfig->rules([ArrayKeyExistsOnPropertyRector::class, FilterVarToAddSlashesRector::class, ExportToReflectionFunctionRector::class, MbStrrposEncodingArgumentPositionRector::class, NullCoalescingOperatorRector::class, ClosureToArrowFunctionRector::class, RestoreDefaultNullToNullableTypePropertyRector::class, CurlyToSquareBracketArrayStringRector::class, MoneyFormatToNumberFormatRector::class, ParenthesizeNestedTernaryRector::class, RestoreIncludePathToIniRestoreRector::class, HebrevcToNl2brHebrevRector::class]);
$rectorConfig->ruleWithConfiguration(RenameCastRector::class, [new RenameCast(Double::class, Double::KIND_REAL, Double::KIND_FLOAT)]);
};
@@ -0,0 +1,38 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Arguments\Rector\ClassMethod\ArgumentAdderRector;
use Rector\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector;
use Rector\Arguments\ValueObject\ArgumentAdder;
use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue;
use Rector\CodeQuality\Rector\ClassMethod\OptionalParametersAfterRequiredRector;
use Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector;
use Rector\Config\RectorConfig;
use Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector;
use Rector\Php80\Rector\Catch_\RemoveUnusedVariableInCatchRector;
use Rector\Php80\Rector\Class_\ClassPropertyAssignToConstructorPromotionRector;
use Rector\Php80\Rector\Class_\StringableForToStringRector;
use Rector\Php80\Rector\ClassConstFetch\ClassOnThisVariableObjectRector;
use Rector\Php80\Rector\ClassMethod\AddParamBasedOnParentClassMethodRector;
use Rector\Php80\Rector\ClassMethod\FinalPrivateToPrivateVisibilityRector;
use Rector\Php80\Rector\ClassMethod\SetStateToStaticRector;
use Rector\Php80\Rector\FuncCall\ClassOnObjectRector;
use Rector\Php80\Rector\Identical\StrEndsWithRector;
use Rector\Php80\Rector\Identical\StrStartsWithRector;
use Rector\Php80\Rector\NotIdentical\StrContainsRector;
use Rector\Php80\Rector\Switch_\ChangeSwitchToMatchRector;
use Rector\Php80\Rector\Ternary\GetDebugTypeRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Transform\Rector\StaticCall\StaticCallToFuncCallRector;
use Rector\Transform\ValueObject\StaticCallToFuncCall;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([StrContainsRector::class, StrStartsWithRector::class, StrEndsWithRector::class, StringableForToStringRector::class, ClassOnObjectRector::class, GetDebugTypeRector::class, RemoveUnusedVariableInCatchRector::class, ClassPropertyAssignToConstructorPromotionRector::class, ChangeSwitchToMatchRector::class, RemoveParentCallWithoutParentRector::class, SetStateToStaticRector::class, FinalPrivateToPrivateVisibilityRector::class, AddParamBasedOnParentClassMethodRector::class, ClassOnThisVariableObjectRector::class, ConsistentImplodeRector::class, OptionalParametersAfterRequiredRector::class]);
$rectorConfig->ruleWithConfiguration(StaticCallToFuncCallRector::class, [new StaticCallToFuncCall('Nette\Utils\Strings', 'startsWith', 'str_starts_with'), new StaticCallToFuncCall('Nette\Utils\Strings', 'endsWith', 'str_ends_with'), new StaticCallToFuncCall('Nette\Utils\Strings', 'contains', 'str_contains')]);
// nette\utils and Strings::replace()
$rectorConfig->ruleWithConfiguration(ArgumentAdderRector::class, [new ArgumentAdder('Nette\Utils\Strings', 'replace', 2, 'replacement', '')]);
// @see https://php.watch/versions/8.0/pgsql-aliases-deprecated
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, ['pg_clientencoding' => 'pg_client_encoding', 'pg_cmdtuples' => 'pg_affected_rows', 'pg_errormessage' => 'pg_last_error', 'pg_fieldisnull' => 'pg_field_is_null', 'pg_fieldname' => 'pg_field_name', 'pg_fieldnum' => 'pg_field_num', 'pg_fieldprtlen' => 'pg_field_prtlen', 'pg_fieldsize' => 'pg_field_size', 'pg_fieldtype' => 'pg_field_type', 'pg_freeresult' => 'pg_free_result', 'pg_getlastoid' => 'pg_last_oid', 'pg_loclose' => 'pg_lo_close', 'pg_locreate' => 'pg_lo_create', 'pg_loexport' => 'pg_lo_export', 'pg_loimport' => 'pg_lo_import', 'pg_loopen' => 'pg_lo_open', 'pg_loread' => 'pg_lo_read', 'pg_loreadall' => 'pg_lo_read_all', 'pg_lounlink' => 'pg_lo_unlink', 'pg_lowrite' => 'pg_lo_write', 'pg_numfields' => 'pg_num_fields', 'pg_numrows' => 'pg_num_rows', 'pg_result' => 'pg_fetch_result', 'pg_setclientencoding' => 'pg_set_client_encoding']);
$rectorConfig->ruleWithConfiguration(FunctionArgumentDefaultValueReplacerRector::class, [new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge'), new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'lte', 'le'), new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '', '!='), new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, '!', '!='), new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'g', 'gt'), new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'l', 'lt'), new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'n', 'ne'), new ReplaceFuncCallArgumentDefaultValue('get_headers', 1, 0, \false), new ReplaceFuncCallArgumentDefaultValue('get_headers', 1, 1, \true)]);
};
@@ -0,0 +1,42 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\CodingStyle\Rector\FuncCall\ClosureFromCallableToFirstClassCallableRector;
use Rector\CodingStyle\Rector\FuncCall\FunctionFirstClassCallableRector;
use Rector\CodingStyle\Rector\FunctionLike\FunctionLikeToFirstClassCallableRector;
use Rector\Config\RectorConfig;
use Rector\Php81\Rector\Array_\FirstClassCallableRector;
use Rector\Php81\Rector\Class_\MyCLabsClassToEnumRector;
use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector;
use Rector\Php81\Rector\ClassMethod\NewInInitializerRector;
use Rector\Php81\Rector\FuncCall\NullToStrictIntPregSlitFuncCallLimitArgRector;
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
use Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector;
use Rector\Php81\Rector\MethodCall\RemoveReflectionSetAccessibleCallsRector;
use Rector\Php81\Rector\MethodCall\SpatieEnumMethodCallToEnumConstRector;
use Rector\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector;
use Rector\Php81\Rector\Property\ReadOnlyPropertyRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([
ReturnNeverTypeRector::class,
MyCLabsClassToEnumRector::class,
MyCLabsMethodCallToEnumConstRector::class,
MyCLabsConstructorCallToEnumFromRector::class,
ReadOnlyPropertyRector::class,
SpatieEnumClassToEnumRector::class,
SpatieEnumMethodCallToEnumConstRector::class,
NullToStrictStringFuncCallArgRector::class,
NullToStrictIntPregSlitFuncCallLimitArgRector::class,
// array of local method call
FirstClassCallableRector::class,
// closure/arrow function
FunctionLikeToFirstClassCallableRector::class,
ClosureFromCallableToFirstClassCallableRector::class,
FunctionFirstClassCallableRector::class,
RemoveReflectionSetAccessibleCallsRector::class,
NewInInitializerRector::class,
]);
};
@@ -0,0 +1,13 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php82\Rector\Class_\ReadOnlyClassRector;
use Rector\Php82\Rector\Encapsed\VariableInStringInterpolationFixerRector;
use Rector\Php82\Rector\FuncCall\Utf8DecodeEncodeToMbConvertEncodingRector;
use Rector\Php82\Rector\New_\FilesystemIteratorSkipDotsRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ReadOnlyClassRector::class, Utf8DecodeEncodeToMbConvertEncodingRector::class, FilesystemIteratorSkipDotsRector::class, VariableInStringInterpolationFixerRector::class]);
};
@@ -0,0 +1,16 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php83\Rector\BooleanAnd\JsonValidateRector;
use Rector\Php83\Rector\Class_\ReadOnlyAnonymousClassRector;
use Rector\Php83\Rector\ClassConst\AddTypeToConstRector;
use Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector;
use Rector\Php83\Rector\FuncCall\CombineHostPortLdapUriRector;
use Rector\Php83\Rector\FuncCall\DynamicClassConstFetchRector;
use Rector\Php83\Rector\FuncCall\RemoveGetClassGetParentClassNoArgsRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([AddOverrideAttributeToOverriddenMethodsRector::class, AddTypeToConstRector::class, CombineHostPortLdapUriRector::class, RemoveGetClassGetParentClassNoArgsRector::class, ReadOnlyAnonymousClassRector::class, DynamicClassConstFetchRector::class, JsonValidateRector::class]);
};
@@ -0,0 +1,18 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Php84\Rector\Class_\DeprecatedAnnotationToDeprecatedAttributeRector;
use Rector\Php84\Rector\Foreach_\ForeachToArrayAllRector;
use Rector\Php84\Rector\Foreach_\ForeachToArrayAnyRector;
use Rector\Php84\Rector\Foreach_\ForeachToArrayFindKeyRector;
use Rector\Php84\Rector\Foreach_\ForeachToArrayFindRector;
use Rector\Php84\Rector\FuncCall\AddEscapeArgumentRector;
use Rector\Php84\Rector\FuncCall\RoundingModeEnumRector;
use Rector\Php84\Rector\MethodCall\NewMethodCallWithoutParenthesesRector;
use Rector\Php84\Rector\Param\ExplicitNullableParamTypeRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ExplicitNullableParamTypeRector::class, RoundingModeEnumRector::class, AddEscapeArgumentRector::class, NewMethodCallWithoutParenthesesRector::class, DeprecatedAnnotationToDeprecatedAttributeRector::class, ForeachToArrayFindRector::class, ForeachToArrayFindKeyRector::class, ForeachToArrayAllRector::class, ForeachToArrayAnyRector::class]);
};
@@ -0,0 +1,75 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use PhpParser\Node\Expr\Cast\Bool_;
use PhpParser\Node\Expr\Cast\Double;
use PhpParser\Node\Expr\Cast\Int_;
use PhpParser\Node\Expr\Cast\String_;
use Rector\Config\RectorConfig;
use Rector\Php85\Rector\ArrayDimFetch\ArrayFirstLastRector;
use Rector\Php85\Rector\Class_\SleepToSerializeRector;
use Rector\Php85\Rector\Class_\WakeupToUnserializeRector;
use Rector\Php85\Rector\ClassMethod\NullDebugInfoReturnRector;
use Rector\Php85\Rector\Const_\DeprecatedAnnotationToDeprecatedAttributeRector;
use Rector\Php85\Rector\FuncCall\ArrayKeyExistsNullToEmptyStringRector;
use Rector\Php85\Rector\FuncCall\ChrArgModuloRector;
use Rector\Php85\Rector\FuncCall\OrdSingleByteRector;
use Rector\Php85\Rector\FuncCall\RemoveFinfoBufferContextArgRector;
use Rector\Php85\Rector\ShellExec\ShellExecFunctionCallOverBackticksRector;
use Rector\Php85\Rector\Switch_\ColonAfterSwitchCaseRector;
use Rector\Removing\Rector\FuncCall\RemoveFuncCallArgRector;
use Rector\Removing\Rector\FuncCall\RemoveFuncCallRector;
use Rector\Removing\ValueObject\RemoveFuncCallArg;
use Rector\Renaming\Rector\Cast\RenameCastRector;
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstFetchRector;
use Rector\Renaming\Rector\ConstFetch\RenameConstantRector;
use Rector\Renaming\Rector\FuncCall\RenameFunctionRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\ValueObject\MethodCallRename;
use Rector\Renaming\ValueObject\RenameCast;
use Rector\Renaming\ValueObject\RenameClassAndConstFetch;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([ArrayFirstLastRector::class, RemoveFinfoBufferContextArgRector::class, NullDebugInfoReturnRector::class, DeprecatedAnnotationToDeprecatedAttributeRector::class, ColonAfterSwitchCaseRector::class, ArrayKeyExistsNullToEmptyStringRector::class, ChrArgModuloRector::class, SleepToSerializeRector::class, OrdSingleByteRector::class, WakeupToUnserializeRector::class, ShellExecFunctionCallOverBackticksRector::class]);
$rectorConfig->ruleWithConfiguration(RemoveFuncCallArgRector::class, [
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_key_length_parameter_of_openssl_pkey_derive
new RemoveFuncCallArg('openssl_pkey_derive', 2),
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_exclude_disabled_parameter_of_get_defined_functions
new RemoveFuncCallArg('get_defined_functions', 0),
]);
$rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_splobjectstoragecontains_splobjectstorageattach_and_splobjectstoragedetach
new MethodCallRename('SplObjectStorage', 'contains', 'offsetExists'),
new MethodCallRename('SplObjectStorage', 'attach', 'offsetSet'),
new MethodCallRename('SplObjectStorage', 'detach', 'offsetUnset'),
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods
new MethodCallRename('PDO', 'pgsqlCopyFromArray', 'copyFromArray'),
new MethodCallRename('PDO', 'pgsqlCopyFromFile', 'copyFromFile'),
new MethodCallRename('PDO', 'pgsqlCopyToArray', 'copyToArray'),
new MethodCallRename('PDO', 'pgsqlCopyToFile', 'copyToFile'),
new MethodCallRename('PDO', 'pgsqlGetNotify', 'getNotify'),
new MethodCallRename('PDO', 'pgsqlGetPid', 'getPid'),
new MethodCallRename('PDO', 'pgsqlLOBCreate', 'lobCreate'),
new MethodCallRename('PDO', 'pgsqlLOBOpen', 'lobOpen'),
new MethodCallRename('PDO', 'pgsqlLOBUnlink', 'lobUnlink'),
new MethodCallRename('PDO', 'sqliteCreateAggregate', 'createAggregate'),
new MethodCallRename('PDO', 'sqliteCreateCollation', 'createCollation'),
new MethodCallRename('PDO', 'sqliteCreateFunction', 'createFunction'),
]);
$rectorConfig->ruleWithConfiguration(RenameFunctionRector::class, [
// https://wiki.php.net/rfc/deprecations_php_8_5#formally_deprecate_socket_set_timeout
'socket_set_timeout' => 'stream_set_timeout',
// https://wiki.php.net/rfc/deprecations_php_8_5#formally_deprecate_mysqli_execute
'mysqli_execute' => 'mysqli_stmt_execute',
]);
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_driver_specific_pdo_constants_and_methods
$rectorConfig->ruleWithConfiguration(RenameClassConstFetchRector::class, [new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_CONNECTION_TIMEOUT', 'Pdo\Dblib', 'ATTR_CONNECTION_TIMEOUT'), new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_QUERY_TIMEOUT', 'Pdo\Dblib', 'ATTR_QUERY_TIMEOUT'), new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER', 'Pdo\Dblib', 'ATTR_STRINGIFY_UNIQUEIDENTIFIER'), new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_VERSION', 'Pdo\Dblib', 'ATTR_VERSION'), new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_TDS_VERSION', 'Pdo\Dblib', 'ATTR_TDS_VERSION'), new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_SKIP_EMPTY_ROWSETS', 'Pdo\Dblib', 'ATTR_SKIP_EMPTY_ROWSETS'), new RenameClassAndConstFetch('PDO', 'DBLIB_ATTR_DATETIME_CONVERT', 'Pdo\Dblib', 'ATTR_DATETIME_CONVERT'), new RenameClassAndConstFetch('PDO', 'FB_ATTR_DATE_FORMAT', 'Pdo\Firebird', 'ATTR_DATE_FORMAT'), new RenameClassAndConstFetch('PDO', 'FB_ATTR_TIME_FORMAT', 'Pdo\Firebird', 'ATTR_TIME_FORMAT'), new RenameClassAndConstFetch('PDO', 'FB_ATTR_TIMESTAMP_FORMAT', 'Pdo\Firebird', 'ATTR_TIMESTAMP_FORMAT'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_USE_BUFFERED_QUERY', 'Pdo\Mysql', 'ATTR_USE_BUFFERED_QUERY'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_LOCAL_INFILE', 'Pdo\Mysql', 'ATTR_LOCAL_INFILE'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_LOCAL_INFILE_DIRECTORY', 'Pdo\Mysql', 'ATTR_LOCAL_INFILE_DIRECTORY'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_INIT_COMMAND', 'Pdo\Mysql', 'ATTR_INIT_COMMAND'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_MAX_BUFFER_SIZE', 'Pdo\Mysql', 'ATTR_MAX_BUFFER_SIZE'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_READ_DEFAULT_FILE', 'Pdo\Mysql', 'ATTR_READ_DEFAULT_FILE'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_READ_DEFAULT_GROUP', 'Pdo\Mysql', 'ATTR_READ_DEFAULT_GROUP'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_COMPRESS', 'Pdo\Mysql', 'ATTR_COMPRESS'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_DIRECT_QUERY', 'Pdo\Mysql', 'ATTR_DIRECT_QUERY'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_FOUND_ROWS', 'Pdo\Mysql', 'ATTR_FOUND_ROWS'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_IGNORE_SPACE', 'Pdo\Mysql', 'ATTR_IGNORE_SPACE'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_KEY', 'Pdo\Mysql', 'ATTR_SSL_KEY'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CERT', 'Pdo\Mysql', 'ATTR_SSL_CERT'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CA', 'Pdo\Mysql', 'ATTR_SSL_CA'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CAPATH', 'Pdo\Mysql', 'ATTR_SSL_CAPATH'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_CIPHER', 'Pdo\Mysql', 'ATTR_SSL_CIPHER'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SSL_VERIFY_SERVER_CERT', 'Pdo\Mysql', 'ATTR_SSL_VERIFY_SERVER_CERT'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_SERVER_PUBLIC_KEY', 'Pdo\Mysql', 'ATTR_SERVER_PUBLIC_KEY'), new RenameClassAndConstFetch('PDO', 'MYSQL_ATTR_MULTI_STATEMENTS', 'Pdo\Mysql', 'ATTR_MULTI_STATEMENTS'), new RenameClassAndConstFetch('PDO', 'ODBC_ATTR_USE_CURSOR_LIBRARY', 'Pdo\Odbc', 'ATTR_USE_CURSOR_LIBRARY'), new RenameClassAndConstFetch('PDO', 'ODBC_ATTR_ASSUME_UTF8', 'Pdo\Odbc', 'ATTR_ASSUME_UTF8'), new RenameClassAndConstFetch('PDO', 'ODBC_SQL_USE_IF_NEEDED', 'Pdo\Odbc', 'SQL_USE_IF_NEEDED'), new RenameClassAndConstFetch('PDO', 'ODBC_SQL_USE_DRIVER', 'Pdo\Odbc', 'SQL_USE_DRIVER'), new RenameClassAndConstFetch('PDO', 'ODBC_SQL_USE_ODBC', 'Pdo\Odbc', 'SQL_USE_ODBC'), new RenameClassAndConstFetch('PDO', 'PGSQL_ATTR_DISABLE_PREPARES', 'Pdo\Pgsql', 'ATTR_DISABLE_PREPARES'), new RenameClassAndConstFetch('PDO', 'SQLITE_ATTR_EXTENDED_RESULT_CODES', 'Pdo\Sqlite', 'ATTR_EXTENDED_RESULT_CODES'), new RenameClassAndConstFetch('PDO', 'SQLITE_ATTR_OPEN_FLAGS', 'Pdo\Sqlite', 'OPEN_FLAGS'), new RenameClassAndConstFetch('PDO', 'SQLITE_ATTR_READONLY_STATEMENT', 'Pdo\Sqlite', 'ATTR_READONLY_STATEMENT'), new RenameClassAndConstFetch('PDO', 'SQLITE_DETERMINISTIC', 'Pdo\Sqlite', 'DETERMINISTIC'), new RenameClassAndConstFetch('PDO', 'SQLITE_OPEN_READONLY', 'Pdo\Sqlite', 'OPEN_READONLY'), new RenameClassAndConstFetch('PDO', 'SQLITE_OPEN_READWRITE', 'Pdo\Sqlite', 'OPEN_READWRITE'), new RenameClassAndConstFetch('PDO', 'SQLITE_OPEN_CREATE', 'Pdo\Sqlite', 'OPEN_CREATE')]);
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_non-standard_cast_names
$rectorConfig->ruleWithConfiguration(RenameCastRector::class, [new RenameCast(Int_::class, Int_::KIND_INTEGER, Int_::KIND_INT), new RenameCast(Bool_::class, Bool_::KIND_BOOLEAN, Bool_::KIND_BOOL), new RenameCast(Double::class, Double::KIND_DOUBLE, Double::KIND_FLOAT), new RenameCast(String_::class, String_::KIND_BINARY, String_::KIND_STRING)]);
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_no-op_functions_from_the_resource_to_object_conversion
// these function have no effect when use
$rectorConfig->ruleWithConfiguration(RemoveFuncCallRector::class, ['curl_close', 'curl_share_close', 'finfo_close', 'imagedestroy', 'xml_parser_free']);
// https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_filter_default_constant
$rectorConfig->ruleWithConfiguration(RenameConstantRector::class, ['FILTER_DEFAULT' => 'FILTER_UNSAFE_RAW']);
};
@@ -0,0 +1,12 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Privatization\Rector\ClassMethod\PrivatizeFinalClassMethodRector;
use Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector;
use Rector\Privatization\Rector\Property\PrivatizeFinalClassPropertyRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([PrivatizeLocalGetterToPropertyRector::class, PrivatizeFinalClassPropertyRector::class, PrivatizeFinalClassMethodRector::class]);
};
@@ -0,0 +1,12 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\CodingStyle\Rector\PostInc\PostIncDecToPreIncDecRector;
use Rector\Config\RectorConfig;
use Rector\Privatization\Rector\Class_\FinalizeTestCaseClassRector;
use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([DeclareStrictTypesRector::class, PostIncDecToPreIncDecRector::class, FinalizeTestCaseClassRector::class]);
};
@@ -0,0 +1,10 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\RectorConfig;
use Rector\Strict\Rector\Empty_\DisallowedEmptyRuleFixerRector;
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules([DisallowedEmptyRuleFixerRector::class]);
};
@@ -0,0 +1,13 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\Level\TypeDeclarationDocblocksLevel;
use Rector\Config\RectorConfig;
/**
* @experimental * 2025-09, experimental hidden set for type declaration in docblocks
*/
return static function (RectorConfig $rectorConfig): void {
$rectorConfig->rules(TypeDeclarationDocblocksLevel::RULES);
};
@@ -0,0 +1,12 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202511;
use Rector\Config\Level\TypeDeclarationLevel;
use Rector\Config\RectorConfig;
return static function (RectorConfig $rectorConfig): void {
// the rule order matters, as its used in withTypeCoverageLevel() method
// place the safest rules first, follow by more complex ones
$rectorConfig->rules(TypeDeclarationLevel::RULES);
};
@@ -0,0 +1,380 @@
<?php
declare(strict_types=1);
use PhpParser\Node;
use PHPStan\Testing\PHPStanTestCase;
if (defined('__PHPSTAN_RUNNING__')) {
return;
}
// edge case during Rector tests case, happens when
// 1. phpstan autoload test case is triggered first,
// 2. all php-parser classes are loaded,
if (defined('PHPUNIT_COMPOSER_INSTALL') && isPHPStanTestPreloaded()) {
return;
}
function isPHPStanTestPreloaded(): bool
{
if (! class_exists(PHPStanTestCase::class, false)) {
return false;
}
return interface_exists(Node::class, false);
}
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node.php';
require_once __DIR__ . '/src/Contract/PhpParser/Node/StmtsAwareInterface.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeAbstract.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitorAbstract.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Comment.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinterAbstract.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Parser.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/FunctionLike.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassLike.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/ComplexType.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/CallLike.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Name.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeTraverserInterface.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Error.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenPolyfill.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AsymmetricVisibilityTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PipeOperatorEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/PropertyTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/VoidCastEmulator.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Modifiers.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NameContext.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Arg.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/ArrayItem.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Attribute.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/AttributeGroup.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/ClosureUse.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Const_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/DeclareItem.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrayDimFetch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Array_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ArrowFunction.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Assign.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseAnd.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseOr.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/BitwiseXor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Coalesce.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Concat.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Div.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Minus.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mod.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Mul.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Plus.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/Pow.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftLeft.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignOp/ShiftRight.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/AssignRef.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseAnd.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseOr.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BitwiseXor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanAnd.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/BooleanOr.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Coalesce.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Concat.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Div.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Equal.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Greater.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/GreaterOrEqual.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Identical.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalAnd.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalOr.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/LogicalXor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Minus.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mod.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Mul.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotEqual.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/NotIdentical.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pipe.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Plus.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Pow.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftLeft.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/ShiftRight.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Smaller.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/SmallerOrEqual.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BinaryOp/Spaceship.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BitwiseNot.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/BooleanNot.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Array_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Bool_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Double.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Int_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Object_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/String_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Unset_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Cast/Void_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ClassConstFetch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Clone_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Closure.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ConstFetch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Empty_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Error.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ErrorSuppress.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Eval_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Exit_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/FuncCall.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Include_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Instanceof_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Isset_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/List_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Match_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/MethodCall.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/New_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafeMethodCall.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/NullsafePropertyFetch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostDec.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PostInc.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreDec.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PreInc.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Print_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/PropertyFetch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/ShellExec.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticCall.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/StaticPropertyFetch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Ternary.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Throw_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryMinus.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/UnaryPlus.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Variable.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/YieldFrom.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Expr/Yield_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Identifier.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/InterpolatedStringPart.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/IntersectionType.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/MatchArm.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Name/FullyQualified.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Name/Relative.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/NullableType.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Param.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/PropertyHook.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/PropertyItem.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/DNumber.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Encapsed.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Float_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/Int_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/InterpolatedString.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Class_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Dir.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/File.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Function_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Line.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Method.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Namespace_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Property.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/MagicConst/Trait_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Scalar/String_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/StaticVar.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Block.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Break_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Case_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Catch_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassConst.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ClassMethod.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Class_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Const_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Continue_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Declare_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Do_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Echo_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/ElseIf_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Else_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/EnumCase.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Enum_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Expression.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Finally_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/For_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Foreach_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Function_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Global_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Goto_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/GroupUse.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/HaltCompiler.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/If_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/InlineHTML.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Interface_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Label.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Namespace_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Nop.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Property.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Return_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Static_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Switch_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUse.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TraitUseAdaptation/Precedence.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Trait_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/TryCatch.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Unset_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/UseUse.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/Use_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/Stmt/While_.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/UnionType.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/UseItem.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/VarLikeIdentifier.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Node/VariadicPlaceholder.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeDumper.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeFinder.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeTraverser.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CloningVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/CommentAnnotatingVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FindingVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/FirstFindingVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NameResolver.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/NodeConnectingVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/NodeVisitor/ParentConnectingVisitor.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Parser/Php8.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/ParserFactory.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/PhpVersion.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/PrettyPrinter/Standard.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/Token.php';
require_once __DIR__ . '/vendor/nikic/php-parser/lib/PhpParser/compatibility_tokens.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Node.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Comment.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/NodeAttributes.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/TypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Attribute.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayItemNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprArrayNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFalseNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprFloatNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprIntegerNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprNullNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprStringNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstExprTrueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/ConstFetchNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/ConstExpr/DoctrineConstExprStringNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/NodeTraverser.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagMethodValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagPropertyValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/AssertTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/DeprecatedTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineAnnotation.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArgument.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArray.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineArrayItem.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/Doctrine/DoctrineTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ExtendsTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/GenericTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ImplementsTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueParameterNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MixinTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamClosureThisTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamImmediatelyInvokedCallableTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamLaterInvokedCallableTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamOutTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ParamTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocChildNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTagNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PhpDocTextNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PropertyTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/PureUnlessCallableIsImpureTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireExtendsTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/RequireImplementsTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ReturnTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/SealedTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/SelfOutTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TemplateTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/ThrowsTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasImportTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypeAliasTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/TypelessParamTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/UsesTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/VarTagValueNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeItemNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeUnsealedTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeForParameterNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConditionalTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ConstTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/IdentifierTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/NullableTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/ThisTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/ParserConfig.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php';
require_once __DIR__ . '/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php';
@@ -0,0 +1,171 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments;
use PhpParser\BuilderHelpers;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface;
use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue;
use Rector\PhpParser\Node\NodeFactory;
use Rector\PhpParser\Node\Value\ValueResolver;
final class ArgumentDefaultValueReplacer
{
/**
* @readonly
*/
private NodeFactory $nodeFactory;
/**
* @readonly
*/
private ValueResolver $valueResolver;
public function __construct(NodeFactory $nodeFactory, ValueResolver $valueResolver)
{
$this->nodeFactory = $nodeFactory;
$this->valueResolver = $valueResolver;
}
/**
* @template TCall as (MethodCall|StaticCall|ClassMethod|FuncCall|New_)
*
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\New_ $node
* @return TCall|null
*/
public function processReplaces($node, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue)
{
if ($node instanceof ClassMethod) {
if (!isset($node->params[$replaceArgumentDefaultValue->getPosition()])) {
return null;
}
return $this->processParams($node, $replaceArgumentDefaultValue);
}
if (!isset($node->args[$replaceArgumentDefaultValue->getPosition()])) {
return null;
}
return $this->processArgs($node, $replaceArgumentDefaultValue);
}
/**
* @param mixed $value
*/
private function isDefaultValueMatched(?Expr $expr, $value): bool
{
// allow any values before, also allow param without default value
if ($value === ReplaceArgumentDefaultValue::ANY_VALUE_BEFORE) {
return \true;
}
if (!$expr instanceof Expr) {
return \false;
}
if ($this->valueResolver->isValue($expr, $value)) {
return \true;
}
// ValueResolver::isValue returns false when default value is `null`
return $value === null && $this->valueResolver->isNull($expr);
}
private function processParams(ClassMethod $classMethod, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue): ?ClassMethod
{
$position = $replaceArgumentDefaultValue->getPosition();
if (!$this->isDefaultValueMatched($classMethod->params[$position]->default, $replaceArgumentDefaultValue->getValueBefore())) {
return null;
}
$classMethod->params[$position]->default = $this->normalizeValue($replaceArgumentDefaultValue->getValueAfter());
return $classMethod;
}
/**
* @template TCall as (MethodCall|StaticCall|FuncCall|New_)
*
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\FuncCall|\PhpParser\Node\Expr\New_ $expr
* @return TCall|null
*/
private function processArgs($expr, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue): ?Expr
{
if ($expr->isFirstClassCallable()) {
return null;
}
$position = $replaceArgumentDefaultValue->getPosition();
$particularArg = $expr->getArgs()[$position] ?? null;
if (!$particularArg instanceof Arg) {
return null;
}
$argValue = $this->valueResolver->getValue($particularArg->value);
if (is_scalar($replaceArgumentDefaultValue->getValueBefore()) && $argValue === $replaceArgumentDefaultValue->getValueBefore()) {
$expr->args[$position] = $this->normalizeValueToArgument($replaceArgumentDefaultValue->getValueAfter());
return $expr;
}
if (is_array($replaceArgumentDefaultValue->getValueBefore())) {
$newArgs = $this->processArrayReplacement($expr->getArgs(), $replaceArgumentDefaultValue);
if (is_array($newArgs)) {
$expr->args = $newArgs;
return $expr;
}
}
return null;
}
/**
* @param mixed $value
*/
private function normalizeValueToArgument($value): Arg
{
return new Arg($this->normalizeValue($value));
}
/**
* @return \PhpParser\Node\Expr\ClassConstFetch|\PhpParser\Node\Expr
* @param mixed $value
*/
private function normalizeValue($value)
{
// class constants → turn string to composite
if (is_string($value) && strpos($value, '::') !== \false) {
[$class, $constant] = explode('::', $value);
return $this->nodeFactory->createClassConstFetch($class, $constant);
}
return BuilderHelpers::normalizeValue($value);
}
/**
* @param array<int, Arg> $args
* @return array<int, Arg>|null
*/
private function processArrayReplacement(array $args, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue): ?array
{
$argumentValues = $this->resolveArgumentValuesToBeforeRecipe($args, $replaceArgumentDefaultValue);
if ($argumentValues !== $replaceArgumentDefaultValue->getValueBefore()) {
return null;
}
if (is_string($replaceArgumentDefaultValue->getValueAfter())) {
$args[$replaceArgumentDefaultValue->getPosition()] = $this->normalizeValueToArgument($replaceArgumentDefaultValue->getValueAfter());
// clear following arguments
$argumentCountToClear = count($replaceArgumentDefaultValue->getValueBefore());
for ($i = $replaceArgumentDefaultValue->getPosition() + 1; $i <= $replaceArgumentDefaultValue->getPosition() + $argumentCountToClear; ++$i) {
unset($args[$i]);
}
}
return $args;
}
/**
* @param Arg[] $argumentNodes
* @return mixed[]
*/
private function resolveArgumentValuesToBeforeRecipe(array $argumentNodes, ReplaceArgumentDefaultValueInterface $replaceArgumentDefaultValue): array
{
$argumentValues = [];
$valueBefore = $replaceArgumentDefaultValue->getValueBefore();
if (!is_array($valueBefore)) {
return [];
}
$beforeArgumentCount = count($valueBefore);
for ($i = 0; $i < $beforeArgumentCount; ++$i) {
if (!isset($argumentNodes[$replaceArgumentDefaultValue->getPosition() + $i])) {
continue;
}
$nextArg = $argumentNodes[$replaceArgumentDefaultValue->getPosition() + $i];
$argumentValues[] = $this->valueResolver->getValue($nextArg->value);
}
return $argumentValues;
}
}
@@ -0,0 +1,17 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\Contract;
interface ReplaceArgumentDefaultValueInterface
{
public function getPosition(): int;
/**
* @return mixed
*/
public function getValueBefore();
/**
* @return mixed
*/
public function getValueAfter();
}
@@ -0,0 +1,60 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\NodeAnalyzer;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use Rector\Arguments\ValueObject\ArgumentAdder;
use Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue;
use Rector\Enum\ObjectReference;
use Rector\NodeNameResolver\NodeNameResolver;
final class ArgumentAddingScope
{
/**
* @readonly
*/
private NodeNameResolver $nodeNameResolver;
/**
* @api
* @var string
*/
public const SCOPE_PARENT_CALL = 'parent_call';
/**
* @api
* @var string
*/
public const SCOPE_METHOD_CALL = 'method_call';
/**
* @api
* @var string
*/
public const SCOPE_CLASS_METHOD = 'class_method';
public function __construct(NodeNameResolver $nodeNameResolver)
{
$this->nodeNameResolver = $nodeNameResolver;
}
/**
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $expr
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
public function isInCorrectScope($expr, $argumentAdder): bool
{
if ($argumentAdder->getScope() === null) {
return \true;
}
$scope = $argumentAdder->getScope();
if ($expr instanceof StaticCall) {
if (!$expr->class instanceof Name) {
return \false;
}
if ($this->nodeNameResolver->isName($expr->class, ObjectReference::PARENT)) {
return $scope === self::SCOPE_PARENT_CALL;
}
return $scope === self::SCOPE_METHOD_CALL;
}
// MethodCall
return $scope === self::SCOPE_METHOD_CALL;
}
}
@@ -0,0 +1,54 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Param;
use PHPStan\Type\Type;
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\StaticTypeMapper\StaticTypeMapper;
final class ChangedArgumentsDetector
{
/**
* @readonly
*/
private ValueResolver $valueResolver;
/**
* @readonly
*/
private StaticTypeMapper $staticTypeMapper;
/**
* @readonly
*/
private TypeComparator $typeComparator;
public function __construct(ValueResolver $valueResolver, StaticTypeMapper $staticTypeMapper, TypeComparator $typeComparator)
{
$this->valueResolver = $valueResolver;
$this->staticTypeMapper = $staticTypeMapper;
$this->typeComparator = $typeComparator;
}
/**
* @param mixed $value
*/
public function isDefaultValueChanged(Param $param, $value): bool
{
if (!$param->default instanceof Expr) {
return \false;
}
return !$this->valueResolver->isValue($param->default, $value);
}
public function isTypeChanged(Param $param, ?Type $newType): bool
{
if (!$param->type instanceof Node) {
return \false;
}
if (!$newType instanceof Type) {
return \true;
}
$currentParamType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
return !$this->typeComparator->areTypesEqual($currentParamType, $newType);
}
}
@@ -0,0 +1,328 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\Rector\ClassMethod;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Arguments\NodeAnalyzer\ArgumentAddingScope;
use Rector\Arguments\NodeAnalyzer\ChangedArgumentsDetector;
use Rector\Arguments\ValueObject\ArgumentAdder;
use Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Enum\ObjectReference;
use Rector\Exception\ShouldNotHappenException;
use Rector\PhpParser\AstResolver;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use RectorPrefix202511\Webmozart\Assert\Assert;
/**
* @see \Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\ArgumentAdderRectorTest
*/
final class ArgumentAdderRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @readonly
*/
private ArgumentAddingScope $argumentAddingScope;
/**
* @readonly
*/
private ChangedArgumentsDetector $changedArgumentsDetector;
/**
* @readonly
*/
private AstResolver $astResolver;
/**
* @readonly
*/
private StaticTypeMapper $staticTypeMapper;
/**
* @var ArgumentAdder[]|ArgumentAdderWithoutDefaultValue[]
*/
private array $addedArguments = [];
private bool $hasChanged = \false;
public function __construct(ArgumentAddingScope $argumentAddingScope, ChangedArgumentsDetector $changedArgumentsDetector, AstResolver $astResolver, StaticTypeMapper $staticTypeMapper)
{
$this->argumentAddingScope = $argumentAddingScope;
$this->changedArgumentsDetector = $changedArgumentsDetector;
$this->astResolver = $astResolver;
$this->staticTypeMapper = $staticTypeMapper;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add new default arguments in calls of defined methods and class types', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
$someObject = new SomeExampleClass;
$someObject->someMethod();
class MyCustomClass extends SomeExampleClass
{
public function someMethod()
{
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
$someObject = new SomeExampleClass;
$someObject->someMethod(true);
class MyCustomClass extends SomeExampleClass
{
public function someMethod($value = true)
{
}
}
CODE_SAMPLE
, [new ArgumentAdder('SomeExampleClass', 'someMethod', 0, 'someArgument', \true, new ObjectType('SomeType'))])]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [MethodCall::class, StaticCall::class, Class_::class];
}
/**
* @param MethodCall|StaticCall|Class_ $node
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Stmt\Class_|null
*/
public function refactor(Node $node)
{
$this->hasChanged = \false;
if ($node instanceof MethodCall || $node instanceof StaticCall) {
$this->refactorCall($node);
} else {
foreach ($node->getMethods() as $classMethod) {
$this->refactorClassMethod($node, $classMethod);
}
}
if ($this->hasChanged) {
return $node;
}
return null;
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
Assert::allIsAnyOf($configuration, [ArgumentAdder::class, ArgumentAdderWithoutDefaultValue::class]);
$this->addedArguments = $configuration;
}
/**
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $call
*/
private function isObjectTypeMatch($call, ObjectType $objectType): bool
{
if ($call instanceof MethodCall) {
return $this->isObjectType($call->var, $objectType);
}
return $this->isObjectType($call->class, $objectType);
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $node
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
private function processPositionWithDefaultValues($node, $argumentAdder): void
{
if ($this->shouldSkipParameter($node, $argumentAdder)) {
return;
}
$argumentType = $argumentAdder->getArgumentType();
$position = $argumentAdder->getPosition();
if ($node instanceof ClassMethod) {
$this->addClassMethodParam($node, $argumentAdder, $argumentType, $position);
return;
}
if ($node instanceof StaticCall) {
$this->processStaticCall($node, $position, $argumentAdder);
return;
}
$this->processMethodCall($node, $argumentAdder, $position);
}
/**
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
private function processMethodCall(MethodCall $methodCall, $argumentAdder, int $position): void
{
if ($argumentAdder instanceof ArgumentAdderWithoutDefaultValue) {
return;
}
$defaultValue = $argumentAdder->getArgumentDefaultValue();
$arg = new Arg(BuilderHelpers::normalizeValue($defaultValue));
if (isset($methodCall->args[$position])) {
return;
}
$this->fillGapBetweenWithDefaultValue($methodCall, $position);
$methodCall->args[$position] = $arg;
$this->hasChanged = \true;
}
/**
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $node
*/
private function fillGapBetweenWithDefaultValue($node, int $position): void
{
$lastPosition = count($node->getArgs()) - 1;
if ($position <= $lastPosition) {
return;
}
if ($position - $lastPosition === 1) {
return;
}
$classMethod = $this->astResolver->resolveClassMethodFromCall($node);
if (!$classMethod instanceof ClassMethod) {
return;
}
for ($index = $lastPosition + 1; $index < $position; ++$index) {
$param = $classMethod->params[$index];
if (!$param->default instanceof Expr) {
throw new ShouldNotHappenException('Previous position does not have default value');
}
$node->args[$index] = new Arg($this->nodeFactory->createReprintedNode($param->default));
}
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $node
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
private function shouldSkipParameter($node, $argumentAdder): bool
{
$position = $argumentAdder->getPosition();
$argumentName = $argumentAdder->getArgumentName();
if ($argumentName === null) {
return \true;
}
if ($node instanceof ClassMethod) {
// already added?
if (!isset($node->params[$position])) {
return \false;
}
$param = $node->params[$position];
// argument added and name has been changed
if (!$this->isName($param, $argumentName)) {
return \true;
}
// argument added and default has been changed
if ($this->isDefaultValueChanged($argumentAdder, $node, $position)) {
return \true;
}
// argument added and type has been changed
return $this->changedArgumentsDetector->isTypeChanged($param, $argumentAdder->getArgumentType());
}
if (isset($node->args[$position])) {
return \true;
}
// Check if default value is the same
$classMethod = $this->astResolver->resolveClassMethodFromCall($node);
if (!$classMethod instanceof ClassMethod) {
// is correct scope?
return !$this->argumentAddingScope->isInCorrectScope($node, $argumentAdder);
}
if (!isset($classMethod->params[$position])) {
// is correct scope?
return !$this->argumentAddingScope->isInCorrectScope($node, $argumentAdder);
}
if ($this->isDefaultValueChanged($argumentAdder, $classMethod, $position)) {
// is correct scope?
return !$this->argumentAddingScope->isInCorrectScope($node, $argumentAdder);
}
return \true;
}
/**
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
private function isDefaultValueChanged($argumentAdder, ClassMethod $classMethod, int $position): bool
{
return $argumentAdder instanceof ArgumentAdder && $this->changedArgumentsDetector->isDefaultValueChanged($classMethod->params[$position], $argumentAdder->getArgumentDefaultValue());
}
/**
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
private function addClassMethodParam(ClassMethod $classMethod, $argumentAdder, ?Type $type, int $position): void
{
$argumentName = $argumentAdder->getArgumentName();
if ($argumentName === null) {
throw new ShouldNotHappenException();
}
if ($argumentAdder instanceof ArgumentAdder) {
$param = new Param(new Variable($argumentName), BuilderHelpers::normalizeValue($argumentAdder->getArgumentDefaultValue()));
} else {
$param = new Param(new Variable($argumentName));
}
if ($type instanceof Type) {
$param->type = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type, TypeKind::PARAM);
}
$classMethod->params[$position] = $param;
$this->hasChanged = \true;
}
/**
* @param \Rector\Arguments\ValueObject\ArgumentAdder|\Rector\Arguments\ValueObject\ArgumentAdderWithoutDefaultValue $argumentAdder
*/
private function processStaticCall(StaticCall $staticCall, int $position, $argumentAdder): void
{
if ($argumentAdder instanceof ArgumentAdderWithoutDefaultValue) {
return;
}
$argumentName = $argumentAdder->getArgumentName();
if ($argumentName === null) {
throw new ShouldNotHappenException();
}
if (!$staticCall->class instanceof Name) {
return;
}
if (!$this->isName($staticCall->class, ObjectReference::PARENT)) {
return;
}
$this->fillGapBetweenWithDefaultValue($staticCall, $position);
$staticCall->args[$position] = new Arg(new Variable($argumentName));
$this->hasChanged = \true;
}
/**
* @param \PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall $call
*/
private function refactorCall($call): void
{
if ($call->isFirstClassCallable()) {
return;
}
$callName = $this->getName($call->name);
if ($callName === null) {
return;
}
foreach ($this->addedArguments as $addedArgument) {
if (!$this->nodeNameResolver->isStringName($callName, $addedArgument->getMethod())) {
continue;
}
if (!$this->isObjectTypeMatch($call, $addedArgument->getObjectType())) {
continue;
}
$this->processPositionWithDefaultValues($call, $addedArgument);
}
}
private function refactorClassMethod(Class_ $class, ClassMethod $classMethod): void
{
foreach ($this->addedArguments as $addedArgument) {
if (!$this->isName($classMethod, $addedArgument->getMethod())) {
continue;
}
if (!$this->isObjectType($class, $addedArgument->getObjectType())) {
continue;
}
$this->processPositionWithDefaultValues($classMethod, $addedArgument);
}
}
}
@@ -0,0 +1,113 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\Arguments\ArgumentDefaultValueReplacer;
use Rector\Arguments\ValueObject\ReplaceArgumentDefaultValue;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Rector\ValueObject\MethodName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use RectorPrefix202511\Webmozart\Assert\Assert;
/**
* @api used in rector-symfony
* @see \Rector\Tests\Arguments\Rector\ClassMethod\ReplaceArgumentDefaultValueRector\ReplaceArgumentDefaultValueRectorTest
*/
final class ReplaceArgumentDefaultValueRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @readonly
*/
private ArgumentDefaultValueReplacer $argumentDefaultValueReplacer;
/**
* @var ReplaceArgumentDefaultValue[]
*/
private array $replaceArgumentDefaultValues = [];
public function __construct(ArgumentDefaultValueReplacer $argumentDefaultValueReplacer)
{
$this->argumentDefaultValueReplacer = $argumentDefaultValueReplacer;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replace defined map of arguments in defined methods and their calls', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
$someObject = new SomeClass;
$someObject->someMethod(SomeClass::OLD_CONSTANT);
CODE_SAMPLE
, <<<'CODE_SAMPLE'
$someObject = new SomeClass;
$someObject->someMethod(false);
CODE_SAMPLE
, [new ReplaceArgumentDefaultValue('SomeClass', 'someMethod', 0, 'SomeClass::OLD_CONSTANT', \false)])]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [MethodCall::class, StaticCall::class, ClassMethod::class, New_::class];
}
/**
* @param MethodCall|StaticCall|ClassMethod|New_ $node
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Expr\New_|null
*/
public function refactor(Node $node)
{
if ($node instanceof New_) {
return $this->refactorNew($node);
}
$nodeName = $this->getName($node->name);
if ($nodeName === null) {
return null;
}
$hasChanged = \false;
$currentNode = $node;
foreach ($this->replaceArgumentDefaultValues as $replaceArgumentDefaultValue) {
if (!$this->nodeNameResolver->isStringName($nodeName, $replaceArgumentDefaultValue->getMethod())) {
continue;
}
if (!$this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType($currentNode, $replaceArgumentDefaultValue->getObjectType())) {
continue;
}
$replacedNode = $this->argumentDefaultValueReplacer->processReplaces($currentNode, $replaceArgumentDefaultValue);
if ($replacedNode !== null && $replacedNode !== $currentNode) {
$currentNode = $replacedNode;
$hasChanged = \true;
}
}
return $hasChanged ? $currentNode : null;
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
Assert::allIsAOf($configuration, ReplaceArgumentDefaultValue::class);
$this->replaceArgumentDefaultValues = $configuration;
}
private function refactorNew(New_ $new): ?New_
{
$hasChanged = \false;
$currentNode = $new;
foreach ($this->replaceArgumentDefaultValues as $replaceArgumentDefaultValue) {
if ($replaceArgumentDefaultValue->getMethod() !== MethodName::CONSTRUCT) {
continue;
}
if (!$this->isObjectType($currentNode, $replaceArgumentDefaultValue->getObjectType())) {
continue;
}
$replacedNode = $this->argumentDefaultValueReplacer->processReplaces($currentNode, $replaceArgumentDefaultValue);
if ($replacedNode !== null && $replacedNode !== $currentNode) {
$currentNode = $replacedNode;
$hasChanged = \true;
}
}
return $hasChanged ? $currentNode : null;
}
}
@@ -0,0 +1,77 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use Rector\Arguments\ArgumentDefaultValueReplacer;
use Rector\Arguments\ValueObject\ReplaceFuncCallArgumentDefaultValue;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use RectorPrefix202511\Webmozart\Assert\Assert;
/**
* @see \Rector\Tests\Arguments\Rector\FuncCall\FunctionArgumentDefaultValueReplacerRector\FunctionArgumentDefaultValueReplacerRectorTest
*/
final class FunctionArgumentDefaultValueReplacerRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @readonly
*/
private ArgumentDefaultValueReplacer $argumentDefaultValueReplacer;
/**
* @var ReplaceFuncCallArgumentDefaultValue[]
*/
private array $replacedArguments = [];
public function __construct(ArgumentDefaultValueReplacer $argumentDefaultValueReplacer)
{
$this->argumentDefaultValueReplacer = $argumentDefaultValueReplacer;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Streamline the operator arguments of `version_compare` function', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
version_compare(PHP_VERSION, '5.6', 'gte');
CODE_SAMPLE
, <<<'CODE_SAMPLE'
version_compare(PHP_VERSION, '5.6', 'ge');
CODE_SAMPLE
, [new ReplaceFuncCallArgumentDefaultValue('version_compare', 2, 'gte', 'ge')])]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?\PhpParser\Node\Expr\FuncCall
{
$hasChanged = \false;
foreach ($this->replacedArguments as $replacedArgument) {
if (!$this->isName($node->name, $replacedArgument->getFunction())) {
continue;
}
$changedNode = $this->argumentDefaultValueReplacer->processReplaces($node, $replacedArgument);
if ($changedNode instanceof Node) {
$hasChanged = \true;
}
}
if ($hasChanged) {
return $node;
}
return null;
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
Assert::allIsAOf($configuration, ReplaceFuncCallArgumentDefaultValue::class);
$this->replacedArguments = $configuration;
}
}
@@ -0,0 +1,100 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use Rector\Arguments\ValueObject\RemoveMethodCallParam;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use RectorPrefix202511\Webmozart\Assert\Assert;
/**
* @note used extensively https://github.com/search?q=RemoveMethodCallParamRector%3A%3Aclass+language%3APHP&type=code&l=PHP
* @see \Rector\Tests\Arguments\Rector\MethodCall\RemoveMethodCallParamRector\RemoveMethodCallParamRectorTest
*/
final class RemoveMethodCallParamRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var RemoveMethodCallParam[]
*/
private array $removeMethodCallParams = [];
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove parameter of method call', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
final class SomeClass
{
public function run(Caller $caller)
{
$caller->process(1, 2);
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
final class SomeClass
{
public function run(Caller $caller)
{
$caller->process(1);
}
}
CODE_SAMPLE
, [new RemoveMethodCallParam('Caller', 'process', 1)])]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [MethodCall::class, StaticCall::class];
}
/**
* @param MethodCall|StaticCall $node
*/
public function refactor(Node $node): ?Node
{
$hasChanged = \false;
if ($node->isFirstClassCallable()) {
return null;
}
foreach ($this->removeMethodCallParams as $removeMethodCallParam) {
if (!$this->isName($node->name, $removeMethodCallParam->getMethodName())) {
continue;
}
if (!$this->isCallerObjectType($node, $removeMethodCallParam)) {
continue;
}
$args = $node->getArgs();
if (!isset($args[$removeMethodCallParam->getParamPosition()])) {
continue;
}
unset($node->args[$removeMethodCallParam->getParamPosition()]);
$hasChanged = \true;
}
if (!$hasChanged) {
return null;
}
return $node;
}
/**
* @param mixed[] $configuration
*/
public function configure(array $configuration): void
{
Assert::allIsInstanceOf($configuration, RemoveMethodCallParam::class);
$this->removeMethodCallParams = $configuration;
}
/**
* @param \PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall $call
*/
private function isCallerObjectType($call, RemoveMethodCallParam $removeMethodCallParam): bool
{
if ($call instanceof MethodCall) {
return $this->isObjectType($call->var, $removeMethodCallParam->getObjectType());
}
return $this->isObjectType($call->class, $removeMethodCallParam->getObjectType());
}
}
@@ -0,0 +1,85 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\ValueObject;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Validation\RectorAssert;
final class ArgumentAdder
{
/**
* @readonly
*/
private string $class;
/**
* @readonly
*/
private string $method;
/**
* @readonly
*/
private int $position;
/**
* @readonly
*/
private ?string $argumentName = null;
/**
* @var mixed|null
*/
private $argumentDefaultValue = null;
/**
* @readonly
* @var \PHPStan\Type\Type|null
*/
private $argumentType = null;
/**
* @readonly
*/
private ?string $scope = null;
/**
* @param mixed|null $argumentDefaultValue
*/
public function __construct(string $class, string $method, int $position, ?string $argumentName = null, $argumentDefaultValue = null, ?\PHPStan\Type\Type $argumentType = null, ?string $scope = null)
{
$this->class = $class;
$this->method = $method;
$this->position = $position;
$this->argumentName = $argumentName;
$this->argumentDefaultValue = $argumentDefaultValue;
$this->argumentType = $argumentType;
$this->scope = $scope;
RectorAssert::className($class);
}
public function getObjectType(): ObjectType
{
return new ObjectType($this->class);
}
public function getMethod(): string
{
return $this->method;
}
public function getPosition(): int
{
return $this->position;
}
public function getArgumentName(): ?string
{
return $this->argumentName;
}
/**
* @return mixed|null
*/
public function getArgumentDefaultValue()
{
return $this->argumentDefaultValue;
}
public function getArgumentType(): ?Type
{
return $this->argumentType;
}
public function getScope(): ?string
{
return $this->scope;
}
}
@@ -0,0 +1,70 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\ValueObject;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\Validation\RectorAssert;
final class ArgumentAdderWithoutDefaultValue
{
/**
* @readonly
*/
private string $class;
/**
* @readonly
*/
private string $method;
/**
* @readonly
*/
private int $position;
/**
* @readonly
*/
private ?string $argumentName = null;
/**
* @readonly
* @var \PHPStan\Type\Type|null
*/
private $argumentType = null;
/**
* @readonly
*/
private ?string $scope = null;
public function __construct(string $class, string $method, int $position, ?string $argumentName = null, ?\PHPStan\Type\Type $argumentType = null, ?string $scope = null)
{
$this->class = $class;
$this->method = $method;
$this->position = $position;
$this->argumentName = $argumentName;
$this->argumentType = $argumentType;
$this->scope = $scope;
RectorAssert::className($class);
}
public function getObjectType(): ObjectType
{
return new ObjectType($this->class);
}
public function getMethod(): string
{
return $this->method;
}
public function getPosition(): int
{
return $this->position;
}
public function getArgumentName(): ?string
{
return $this->argumentName;
}
public function getArgumentType(): ?Type
{
return $this->argumentType;
}
public function getScope(): ?string
{
return $this->scope;
}
}
@@ -0,0 +1,42 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\ValueObject;
use PHPStan\Type\ObjectType;
use Rector\Validation\RectorAssert;
final class RemoveMethodCallParam
{
/**
* @readonly
*/
private string $class;
/**
* @readonly
*/
private string $methodName;
/**
* @readonly
*/
private int $paramPosition;
public function __construct(string $class, string $methodName, int $paramPosition)
{
$this->class = $class;
$this->methodName = $methodName;
$this->paramPosition = $paramPosition;
RectorAssert::className($class);
RectorAssert::methodName($methodName);
}
public function getObjectType(): ObjectType
{
return new ObjectType($this->class);
}
public function getMethodName(): string
{
return $this->methodName;
}
public function getParamPosition(): int
{
return $this->paramPosition;
}
}
@@ -0,0 +1,78 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\ValueObject;
use PHPStan\Type\ObjectType;
use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface;
use Rector\Validation\RectorAssert;
final class ReplaceArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface
{
/**
* @readonly
*/
private string $class;
/**
* @readonly
*/
private string $method;
/**
* @var int<0, max>
* @readonly
*/
private int $position;
/**
* @readonly
* @var mixed
*/
private $valueBefore;
/**
* @readonly
* @var mixed
*/
private $valueAfter;
/**
* @var string
*/
public const ANY_VALUE_BEFORE = '*ANY_VALUE_BEFORE*';
/**
* @param int<0, max> $position
* @param mixed $valueBefore
* @param mixed $valueAfter
*/
public function __construct(string $class, string $method, int $position, $valueBefore, $valueAfter)
{
$this->class = $class;
$this->method = $method;
$this->position = $position;
$this->valueBefore = $valueBefore;
$this->valueAfter = $valueAfter;
RectorAssert::className($class);
}
public function getObjectType(): ObjectType
{
return new ObjectType($this->class);
}
public function getMethod(): string
{
return $this->method;
}
public function getPosition(): int
{
return $this->position;
}
/**
* @return mixed
*/
public function getValueBefore()
{
return $this->valueBefore;
}
/**
* @return mixed
*/
public function getValueAfter()
{
return $this->valueAfter;
}
}
@@ -0,0 +1,60 @@
<?php
declare (strict_types=1);
namespace Rector\Arguments\ValueObject;
use Rector\Arguments\Contract\ReplaceArgumentDefaultValueInterface;
final class ReplaceFuncCallArgumentDefaultValue implements ReplaceArgumentDefaultValueInterface
{
/**
* @readonly
*/
private string $function;
/**
* @readonly
*/
private int $position;
/**
* @readonly
* @var mixed
*/
private $valueBefore;
/**
* @readonly
* @var mixed
*/
private $valueAfter;
/**
* @param mixed $valueBefore
* @param mixed $valueAfter
*/
public function __construct(string $function, int $position, $valueBefore, $valueAfter)
{
$this->function = $function;
$this->position = $position;
$this->valueBefore = $valueBefore;
$this->valueAfter = $valueAfter;
}
public function getFunction(): string
{
return $this->function;
}
public function getPosition(): int
{
return $this->position;
}
/**
* @return mixed
*/
public function getValueBefore()
{
return $this->valueBefore;
}
/**
* @return mixed
*/
public function getValueAfter()
{
return $this->valueAfter;
}
}
@@ -0,0 +1,16 @@
<?php
declare (strict_types=1);
namespace Rector\Assert\Enum;
final class AssertClassName
{
/**
* @var string
*/
public const WEBMOZART = 'Webmozart\Assert\Assert';
/**
* @var string
*/
public const BEBERLEI = 'Assert\Assertion';
}
@@ -0,0 +1,42 @@
<?php
declare (strict_types=1);
namespace Rector\Assert\NodeAnalyzer;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\PrettyPrinter\Standard;
use Rector\Assert\Enum\AssertClassName;
final class ExistingAssertStaticCallResolver
{
/**
* @return string[]
*/
public function resolve(ClassMethod $classMethod): array
{
if ($classMethod->stmts === null) {
return [];
}
$existingAssertCallHashes = [];
$standard = new Standard();
foreach ($classMethod->stmts as $currentStmt) {
if (!$currentStmt instanceof Expression) {
continue;
}
if (!$currentStmt->expr instanceof StaticCall) {
continue;
}
$staticCall = $currentStmt->expr;
if (!$staticCall->class instanceof Name) {
continue;
}
if (!in_array($staticCall->class->toString(), [AssertClassName::WEBMOZART, AssertClassName::BEBERLEI], \true)) {
continue;
}
$existingAssertCallHashes[] = $standard->prettyPrintExpr($staticCall);
}
return $existingAssertCallHashes;
}
}
@@ -0,0 +1,208 @@
<?php
declare (strict_types=1);
namespace Rector\Assert\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\PrettyPrinter\Standard;
use PHPStan\Type\ArrayType;
use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use Rector\Assert\Enum\AssertClassName;
use Rector\Assert\NodeAnalyzer\ExistingAssertStaticCallResolver;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\PHPStan\ScopeFetcher;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use RectorPrefix202511\Webmozart\Assert\Assert;
/**
* @experimental Check generic array key/value types in runtime with assert. Generics for impatient people.
*
* @see \Rector\Tests\Assert\Rector\ClassMethod\AddAssertArrayFromClassMethodDocblockRector\AddAssertArrayFromClassMethodDocblockRectorTest
*/
final class AddAssertArrayFromClassMethodDocblockRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @readonly
*/
private PhpDocInfoFactory $phpDocInfoFactory;
/**
* @readonly
*/
private ExistingAssertStaticCallResolver $existingAssertStaticCallResolver;
private string $assertClass = AssertClassName::WEBMOZART;
public function __construct(PhpDocInfoFactory $phpDocInfoFactory, ExistingAssertStaticCallResolver $existingAssertStaticCallResolver)
{
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->existingAssertStaticCallResolver = $existingAssertStaticCallResolver;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Add key and value assert based on docblock @param type declarations (pick from "webmozart" or "beberlei" asserts)', [new ConfiguredCodeSample(<<<'CODE_SAMPLE'
<?php
namespace RectorPrefix202511;
class SomeClass
{
/**
* @param int[] $items
*/
public function run(array $items)
{
}
}
\class_alias('SomeClass', 'SomeClass', \false);
CODE_SAMPLE
, <<<'CODE_SAMPLE'
<?php
namespace RectorPrefix202511;
use RectorPrefix202511\Webmozart\Assert\Assert;
class SomeClass
{
/**
* @param int[] $items
*/
public function run(array $items)
{
Assert::allInteger($items);
}
}
\class_alias('SomeClass', 'SomeClass', \false);
CODE_SAMPLE
, [AssertClassName::WEBMOZART])]);
}
public function getNodeTypes(): array
{
return [ClassMethod::class];
}
/**
* @param ClassMethod $node
*/
public function refactor(Node $node): ?ClassMethod
{
$scope = ScopeFetcher::fetch($node);
if (!$scope->isInClass()) {
return null;
}
if ($node->stmts === null || $node->isAbstract()) {
return null;
}
$methodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
if (!$methodPhpDocInfo instanceof PhpDocInfo) {
return null;
}
$paramTagValueNodes = $methodPhpDocInfo->getParamTagValueNodes();
if ($paramTagValueNodes === []) {
return null;
}
$assertStaticCallStmts = [];
foreach ($node->getParams() as $param) {
if (!$param->type instanceof Identifier) {
continue;
}
// handle arrays only
if (!$this->isName($param->type, 'array')) {
continue;
}
if (!$param->var instanceof Variable) {
continue;
}
$paramName = $param->var->name;
if (!is_string($paramName)) {
continue;
}
$paramDocType = $methodPhpDocInfo->getParamType($paramName);
if (!$paramDocType instanceof ArrayType) {
continue;
}
$valueAssertMethod = $this->matchTypeToAssertMethod($paramDocType->getItemType());
if (is_string($valueAssertMethod)) {
$assertStaticCallStmts[] = $this->createAssertExpression($param->var, $valueAssertMethod);
}
$keyAssertMethod = $this->matchTypeToAssertMethod($paramDocType->getKeyType());
if (is_string($keyAssertMethod)) {
$arrayKeys = new FuncCall(new Name('array_keys'), [new Arg($param->var)]);
$assertStaticCallStmts[] = $this->createAssertExpression($arrayKeys, $keyAssertMethod);
}
}
// filter existing assert to avoid duplication
if ($assertStaticCallStmts === []) {
return null;
}
$existingAssertCallHashes = $this->existingAssertStaticCallResolver->resolve($node);
$assertStaticCallStmts = $this->filterOutExistingStaticCall($assertStaticCallStmts, $existingAssertCallHashes);
if ($assertStaticCallStmts === []) {
return null;
}
$node->stmts = array_merge($assertStaticCallStmts, $node->stmts);
return $node;
}
/**
* @param array<string> $configuration
*/
public function configure(array $configuration): void
{
if ($configuration === []) {
// default
return;
}
Assert::count($configuration, 1);
Assert::inArray($configuration[0], [AssertClassName::BEBERLEI, AssertClassName::WEBMOZART]);
$this->assertClass = $configuration[0];
}
private function createAssertExpression(Expr $expr, string $methodName): Expression
{
$assertFullyQualified = new FullyQualified($this->assertClass);
$staticCall = new StaticCall($assertFullyQualified, $methodName, [new Arg($expr)]);
return new Expression($staticCall);
}
/**
* @param Expression[] $assertStaticCallStmts
* @param string[] $existingAssertCallHashes
* @return Expression[]
*/
private function filterOutExistingStaticCall(array $assertStaticCallStmts, array $existingAssertCallHashes): array
{
$standard = new Standard();
return array_filter($assertStaticCallStmts, function (Expression $assertStaticCallExpression) use ($standard, $existingAssertCallHashes): bool {
$currentStaticCallHash = $standard->prettyPrintExpr($assertStaticCallExpression->expr);
return !in_array($currentStaticCallHash, $existingAssertCallHashes, \true);
});
}
private function matchTypeToAssertMethod(Type $type): ?string
{
if ($type instanceof IntegerType) {
return 'allInteger';
}
if ($type instanceof StringType) {
return 'allString';
}
if ($type instanceof FloatType) {
return 'allFloat';
}
if ($type instanceof BooleanType) {
return 'allBoolean';
}
return null;
}
}
@@ -0,0 +1,142 @@
<?php
declare (strict_types=1);
namespace Rector\Carbon\NodeFactory;
use RectorPrefix202511\Nette\Utils\Strings;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
final class CarbonCallFactory
{
/**
* @var string
* @see https://regex101.com/r/LLMrFw/1
*/
private const PLUS_MINUS_COUNT_REGEX = '#(?<operator>\+|-)(\s+)?(?<count>\d+)(\s+)?(?<unit>seconds|second|sec|minutes|minute|min|hours|hour|days|day|weeks|week|months|month|years|year)#';
/**
* @var string
* @see https://regex101.com/r/IhxHTO/1
*/
private const STATIC_DATE_REGEX = '#now|yesterday|today|tomorrow#';
/**
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall
*/
public function createFromDateTimeString(FullyQualified $carbonFullyQualified, String_ $string)
{
$carbonCall = $this->createStaticCall($carbonFullyQualified, $string);
$string->value = Strings::replace($string->value, self::STATIC_DATE_REGEX);
// Handle add/sub multiple times
while ($match = Strings::match($string->value, self::PLUS_MINUS_COUNT_REGEX)) {
$methodCall = $this->createModifyMethodCall($carbonCall, new Int_((int) $match['count']), $match['unit'], $match['operator']);
if ($methodCall instanceof MethodCall) {
$carbonCall = $methodCall;
$string->value = Strings::replace($string->value, self::PLUS_MINUS_COUNT_REGEX, '', 1);
}
}
// If we still have something in the string, we go back to the first method and replace this with a parse
if (($rest = Strings::trim($string->value)) !== '') {
$currentCall = $carbonCall;
$callStack = [];
while ($currentCall instanceof MethodCall) {
$callStack[] = $currentCall;
$currentCall = $currentCall->var;
}
if (!$currentCall instanceof StaticCall) {
return $carbonCall;
}
// If we fallback to a parse we want to include tomorrow/today/yesterday etc
if ($currentCall->name instanceof Identifier && $currentCall->name->name !== 'now') {
$rest .= ' ' . $currentCall->name->name;
}
$currentCall->name = new Identifier('parse');
$currentCall->args = [new Arg(new String_($rest))];
// Rebuild original call from callstack
$carbonCall = $this->rebuildCallStack($currentCall, $callStack);
}
return $carbonCall;
}
private function createStaticCall(FullyQualified $carbonFullyQualified, String_ $string): StaticCall
{
$startDate = Strings::match($string->value, self::STATIC_DATE_REGEX)[0] ?? 'now';
return new StaticCall($carbonFullyQualified, new Identifier($startDate));
}
/**
* @param \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall $carbonCall
*/
private function createModifyMethodCall($carbonCall, Int_ $int, string $unit, string $operator): ?MethodCall
{
switch ($unit) {
case 'sec':
case 'second':
case 'seconds':
$unit = 'seconds';
break;
case 'min':
case 'minute':
case 'minutes':
$unit = 'minutes';
break;
case 'hour':
case 'hours':
$unit = 'hours';
break;
case 'day':
case 'days':
$unit = 'days';
break;
case 'week':
case 'weeks':
$unit = 'weeks';
break;
case 'month':
case 'months':
$unit = 'months';
break;
case 'year':
case 'years':
$unit = 'years';
break;
default:
$unit = null;
break;
}
switch ($operator) {
case '+':
$operator = 'add';
break;
case '-':
$operator = 'sub';
break;
default:
$operator = null;
break;
}
if ($unit === null || $operator === null) {
return null;
}
$methodName = $operator . ucfirst($unit);
return new MethodCall($carbonCall, new Identifier($methodName), [new Arg($int)]);
}
/**
* @param MethodCall[] $callStack
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall
*/
private function rebuildCallStack(StaticCall $staticCall, array $callStack)
{
if ($callStack === []) {
return $staticCall;
}
$currentCall = $staticCall;
$callStack = array_reverse($callStack);
foreach ($callStack as $call) {
$call->var = $currentCall;
$currentCall = $call;
}
return $currentCall;
}
}
@@ -0,0 +1,207 @@
<?php
declare (strict_types=1);
namespace Rector\Carbon\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\Minus;
use PhpParser\Node\Expr\BinaryOp\Mul;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\String_;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\Carbon\Rector\FuncCall\DateFuncCallToCarbonRector\DateFuncCallToCarbonRectorTest
*/
final class DateFuncCallToCarbonRector extends AbstractRector
{
private const TIME_UNITS = [['weeks', 604800], ['days', 86400], ['hours', 3600], ['minutes', 60], ['seconds', 1]];
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Convert `date()` function call to `Carbon::now()->format(*)`', [new CodeSample(<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$date = date('Y-m-d');
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$date = \Carbon\Carbon::now()->format('Y-m-d');
}
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Minus::class, FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if ($node instanceof Minus) {
$left = $node->left;
if ($left instanceof FuncCall && !$left->isFirstClassCallable() && $this->isName($left->name, 'time')) {
$timeUnit = $this->detectTimeUnit($node->right);
if ($timeUnit !== null) {
return $this->createCarbonSubtract($timeUnit);
}
}
return null;
}
if (!$node instanceof FuncCall) {
return null;
}
if ($node->isFirstClassCallable()) {
return null;
}
if ($this->isName($node->name, 'date') && isset($node->args[1]) && $node->args[1] instanceof Arg) {
$format = $this->getArgValue($node, 0);
if (!$format instanceof Expr) {
return null;
}
$timestamp = $node->args[1]->value;
if ($timestamp instanceof FuncCall && $this->isName($timestamp->name, 'strtotime') && isset($timestamp->args[0]) && $timestamp->args[0] instanceof Arg) {
$dateExpr = $timestamp->args[0]->value;
return $this->createCarbonParseFormat($dateExpr, $format);
}
// @phpstan-ignore if.alwaysTrue
if ($this->getType($timestamp)->isInteger()) {
return $this->createCarbonFromTimestamp($timestamp, $format);
}
}
if ($this->isName($node->name, 'date') && isset($node->args[0])) {
$format = $this->getArgValue($node, 0);
if ($format instanceof String_) {
return $this->createCarbonNowFormat($format);
}
}
if ($this->isName($node->name, 'strtotime') && isset($node->args[0])) {
$dateExpr = $this->getArgValue($node, 0);
$baseTimestamp = $this->getArgValue($node, 1);
if ($dateExpr instanceof Expr && !$baseTimestamp instanceof Expr) {
return $this->createCarbonParseTimestamp($dateExpr);
}
if ($dateExpr instanceof Expr && $baseTimestamp instanceof String_) {
$isRelative = strncmp($baseTimestamp->value, '+', strlen('+')) === 0 || strncmp($baseTimestamp->value, '-', strlen('-')) === 0;
if ($isRelative) {
return null;
// @todo implement relative changes based on second arg
}
}
}
return null;
}
private function getArgValue(FuncCall $funcCall, int $index): ?Expr
{
if (!isset($funcCall->args[$index]) || !$funcCall->args[$index] instanceof Arg) {
return null;
}
return $funcCall->args[$index]->value;
}
private function createCarbonNowFormat(String_ $string): MethodCall
{
return new MethodCall($this->createCarbonNow(), 'format', [new Arg($string)]);
}
private function createCarbonNow(): StaticCall
{
return new StaticCall(new FullyQualified('Carbon\Carbon'), 'now');
}
private function createCarbonParseTimestamp(Expr $dateExpr): MethodCall
{
$staticCall = new StaticCall(new FullyQualified('Carbon\Carbon'), 'parse', [new Arg($dateExpr)]);
return new MethodCall($staticCall, 'getTimestamp');
}
private function createCarbonParseFormat(Expr $dateExpr, Expr $format): MethodCall
{
$staticCall = new StaticCall(new FullyQualified('Carbon\Carbon'), 'parse', [new Arg($dateExpr)]);
return new MethodCall($staticCall, 'format', [new Arg($format)]);
}
private function createCarbonFromTimestamp(Expr $timestampExpr, Expr $format): MethodCall
{
$staticCall = new StaticCall(new FullyQualified('Carbon\Carbon'), 'createFromTimestamp', [new Arg($timestampExpr)]);
return new MethodCall($staticCall, 'format', [new Arg($format)]);
}
/**
* @param array{unit: string, value: int} $timeUnit
*/
private function createCarbonSubtract(array $timeUnit): MethodCall
{
$staticCall = new StaticCall(new FullyQualified('Carbon\Carbon'), 'now');
$methodName = 'sub' . ucfirst($timeUnit['unit']);
$methodCall = new MethodCall($staticCall, $methodName, [new Arg(new Int_($timeUnit['value']))]);
return new MethodCall($methodCall, 'getTimestamp');
}
/**
* @return array{unit: string, value: int}|null
*/
private function detectTimeUnit(Expr $expr): ?array
{
$product = $this->calculateProduct($expr);
if ($product === null) {
return null;
}
foreach (self::TIME_UNITS as [$unit, $seconds]) {
if ($product % $seconds === 0) {
return ['unit' => (string) $unit, 'value' => (int) ($product / $seconds)];
}
}
return null;
}
/**
* @return float|int|null
*/
private function calculateProduct(Expr $expr)
{
if ($expr instanceof Int_) {
return $expr->value;
}
if (!$expr instanceof Mul) {
return null;
}
$multipliers = $this->extractMultipliers($expr);
if ($multipliers === []) {
return null;
}
return array_product($multipliers);
}
/**
* @return int[]
*/
private function extractMultipliers(Node $node): array
{
$multipliers = [];
if (!$node instanceof Mul) {
return $multipliers;
}
if ($node->left instanceof Int_) {
$multipliers[] = $node->left->value;
} elseif ($node->left instanceof Mul) {
$multipliers = array_merge($multipliers, $this->extractMultipliers($node->left));
}
if ($node->right instanceof Int_) {
$multipliers[] = $node->right->value;
} elseif ($node->right instanceof Mul) {
$multipliers = array_merge($multipliers, $this->extractMultipliers($node->right));
}
return $multipliers;
}
}
@@ -0,0 +1,69 @@
<?php
declare (strict_types=1);
namespace Rector\Carbon\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name\FullyQualified;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\Carbon\Rector\FuncCall\TimeFuncCallToCarbonRector\TimeFuncCallToCarbonRectorTest
*/
final class TimeFuncCallToCarbonRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Convert `time()` function call to `Carbon::now()->getTimestamp()`', [new CodeSample(<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$time = time();
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$time = \Carbon\Carbon::now()->getTimestamp();
}
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [FuncCall::class];
}
/**
* @param FuncCall $node
*/
public function refactor(Node $node): ?Node
{
if (!$this->isName($node->name, 'time')) {
return null;
}
$firstClassCallable = $node->isFirstClassCallable();
if (!$firstClassCallable && count($node->getArgs()) !== 0) {
return null;
}
// create now and format()
$nowStaticCall = new StaticCall(new FullyQualified('Carbon\Carbon'), 'now');
$methodCall = new MethodCall($nowStaticCall, 'getTimestamp');
if ($firstClassCallable) {
return new ArrowFunction(['static' => \true, 'expr' => $methodCall]);
}
return $methodCall;
}
}
@@ -0,0 +1,93 @@
<?php
declare (strict_types=1);
namespace Rector\Carbon\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use Rector\Carbon\NodeFactory\CarbonCallFactory;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\Carbon\Rector\MethodCall\DateTimeMethodCallToCarbonRector\DateTimeMethodCallToCarbonRectorTest
*/
final class DateTimeMethodCallToCarbonRector extends AbstractRector
{
/**
* @readonly
*/
private CarbonCallFactory $carbonCallFactory;
public function __construct(CarbonCallFactory $carbonCallFactory)
{
$this->carbonCallFactory = $carbonCallFactory;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Convert `new DateTime()` with a method call to `Carbon::*()`', [new CodeSample(<<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$date = (new \DateTime('today +20 day'))->format('Y-m-d');
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
class SomeClass
{
public function run()
{
$date = \Carbon\Carbon::today()->addDays(20)->format('Y-m-d')
}
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [MethodCall::class];
}
/**
* @param MethodCall $node
*/
public function refactor(Node $node): ?Node
{
if (!$node->var instanceof New_) {
return null;
}
$new = $node->var;
if (!$new->class instanceof Name) {
return null;
}
if (!$this->isName($new->class, 'DateTime') && !$this->isName($new->class, 'DateTimeImmutable')) {
return null;
}
if ($new->isFirstClassCallable()) {
return null;
}
if (count($new->getArgs()) !== 1) {
// @todo handle in separate static call
return null;
}
$firstArg = $new->getArgs()[0];
if (!$firstArg->value instanceof String_) {
return null;
}
if ($this->isName($new->class, 'DateTime')) {
$carbonFullyQualified = new FullyQualified('Carbon\Carbon');
} else {
$carbonFullyQualified = new FullyQualified('Carbon\CarbonImmutable');
}
$carbonCall = $this->carbonCallFactory->createFromDateTimeString($carbonFullyQualified, $firstArg->value);
$node->var = $carbonCall;
return $node;
}
}
@@ -0,0 +1,81 @@
<?php
declare (strict_types=1);
namespace Rector\Carbon\Rector\New_;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Scalar\String_;
use Rector\Carbon\NodeFactory\CarbonCallFactory;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\Carbon\Rector\New_\DateTimeInstanceToCarbonRector\DateTimeInstanceToCarbonRectorTest
*/
final class DateTimeInstanceToCarbonRector extends AbstractRector
{
/**
* @readonly
*/
private CarbonCallFactory $carbonCallFactory;
public function __construct(CarbonCallFactory $carbonCallFactory)
{
$this->carbonCallFactory = $carbonCallFactory;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Convert `new DateTime()` to `Carbon::*()`', [new CodeSample(<<<'CODE_SAMPLE'
$date = new \DateTime('today');
CODE_SAMPLE
, <<<'CODE_SAMPLE'
$date = \Carbon\Carbon::today();
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [New_::class];
}
/**
* @param New_ $node
*/
public function refactor(Node $node): ?Node
{
if ($node->isFirstClassCallable()) {
return null;
}
if ($this->isName($node->class, 'DateTime')) {
return $this->refactorWithClass($node, 'Carbon\Carbon');
}
if ($this->isName($node->class, 'DateTimeImmutable')) {
return $this->refactorWithClass($node, 'Carbon\CarbonImmutable');
}
return null;
}
/**
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\StaticCall|null
*/
private function refactorWithClass(New_ $new, string $className)
{
// no arg? ::now()
$carbonFullyQualified = new FullyQualified($className);
if ($new->args === []) {
return new StaticCall($carbonFullyQualified, new Identifier('now'));
}
if (count($new->getArgs()) === 1) {
$firstArg = $new->getArgs()[0];
if ($firstArg->value instanceof String_) {
return $this->carbonCallFactory->createFromDateTimeString($carbonFullyQualified, $firstArg->value);
}
}
return null;
}
}
@@ -0,0 +1,55 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality;
use PhpParser\Node\Arg;
use PhpParser\Node\ArrayItem;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use Rector\Exception\ShouldNotHappenException;
use Rector\PhpParser\Node\Value\ValueResolver;
final class CompactConverter
{
/**
* @readonly
*/
private ValueResolver $valueResolver;
public function __construct(ValueResolver $valueResolver)
{
$this->valueResolver = $valueResolver;
}
public function hasAllArgumentsNamed(FuncCall $funcCall): bool
{
foreach ($funcCall->args as $arg) {
// VariadicPlaceholder doesn't has name, so it return false directly
if (!$arg instanceof Arg) {
return \false;
}
/** @var string|null $variableName */
$variableName = $this->valueResolver->getValue($arg->value);
if (!is_string($variableName)) {
return \false;
}
}
return \true;
}
public function convertToArray(FuncCall $funcCall): Array_
{
$array = new Array_();
foreach ($funcCall->args as $arg) {
if (!$arg instanceof Arg) {
throw new ShouldNotHappenException();
}
/** @var string|null $variableName */
$variableName = $this->valueResolver->getValue($arg->value);
if (!is_string($variableName)) {
throw new ShouldNotHappenException();
}
$array->items[] = new ArrayItem(new Variable($variableName), new String_($variableName));
}
return $array;
}
}
@@ -0,0 +1,22 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeAnalyzer;
use PhpParser\Node\Stmt\Class_;
final class ClassLikeAnalyzer
{
/**
* @return string[]
*/
public function resolvePropertyNames(Class_ $class): array
{
$propertyNames = [];
foreach ($class->getProperties() as $property) {
foreach ($property->props as $prop) {
$propertyNames[] = $prop->name->toString();
}
}
return $propertyNames;
}
}
@@ -0,0 +1,51 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeAnalyzer;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Foreach_;
use Rector\PhpParser\Comparing\NodeComparator;
final class ForeachAnalyzer
{
/**
* @readonly
*/
private NodeComparator $nodeComparator;
public function __construct(NodeComparator $nodeComparator)
{
$this->nodeComparator = $nodeComparator;
}
/**
* Matches$
* foreach ($values as $value) {
* <$assigns[]> = $value;
* }
*/
public function matchAssignItemsOnlyForeachArrayVariable(Foreach_ $foreach): ?Expr
{
if (count($foreach->stmts) !== 1) {
return null;
}
$onlyStatement = $foreach->stmts[0];
if ($onlyStatement instanceof Expression) {
$onlyStatement = $onlyStatement->expr;
}
if (!$onlyStatement instanceof Assign) {
return null;
}
if (!$onlyStatement->var instanceof ArrayDimFetch) {
return null;
}
if ($onlyStatement->var->dim instanceof Expr) {
return null;
}
if (!$this->nodeComparator->areNodesEqual($foreach->valueVar, $onlyStatement->expr)) {
return null;
}
return $onlyStatement->var->var;
}
}
@@ -0,0 +1,181 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Function_;
use PhpParser\NodeVisitor;
use PHPStan\Analyser\Scope;
use PHPStan\Type\MixedType;
use Rector\CodeQuality\TypeResolver\ArrayDimFetchTypeResolver;
use Rector\CodeQuality\ValueObject\DefinedPropertyWithType;
use Rector\NodeAnalyzer\PropertyFetchAnalyzer;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser;
final class LocalPropertyAnalyzer
{
/**
* @readonly
*/
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser;
/**
* @readonly
*/
private NodeNameResolver $nodeNameResolver;
/**
* @readonly
*/
private ArrayDimFetchTypeResolver $arrayDimFetchTypeResolver;
/**
* @readonly
*/
private NodeTypeResolver $nodeTypeResolver;
/**
* @readonly
*/
private PropertyFetchAnalyzer $propertyFetchAnalyzer;
/**
* @readonly
*/
private TypeFactory $typeFactory;
/**
* @var string
*/
private const LARAVEL_COLLECTION_CLASS = 'Illuminate\Support\Collection';
public function __construct(SimpleCallableNodeTraverser $simpleCallableNodeTraverser, NodeNameResolver $nodeNameResolver, ArrayDimFetchTypeResolver $arrayDimFetchTypeResolver, NodeTypeResolver $nodeTypeResolver, PropertyFetchAnalyzer $propertyFetchAnalyzer, TypeFactory $typeFactory)
{
$this->simpleCallableNodeTraverser = $simpleCallableNodeTraverser;
$this->nodeNameResolver = $nodeNameResolver;
$this->arrayDimFetchTypeResolver = $arrayDimFetchTypeResolver;
$this->nodeTypeResolver = $nodeTypeResolver;
$this->propertyFetchAnalyzer = $propertyFetchAnalyzer;
$this->typeFactory = $typeFactory;
}
/**
* @return DefinedPropertyWithType[]
*/
public function resolveFetchedPropertiesToTypesFromClass(Class_ $class): array
{
$definedPropertiesWithTypes = [];
foreach ($class->getMethods() as $classMethod) {
$methodName = $this->nodeNameResolver->getName($classMethod);
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod->getStmts(), function (Node $node) use (&$definedPropertiesWithTypes, $methodName): ?int {
if ($this->shouldSkip($node)) {
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
if ($node instanceof Assign && ($node->var instanceof PropertyFetch || $node->var instanceof ArrayDimFetch)) {
$propertyFetch = $node->var;
$propertyName = $this->resolvePropertyName($propertyFetch instanceof ArrayDimFetch ? $propertyFetch->var : $propertyFetch);
if ($propertyName === null) {
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
if ($propertyFetch instanceof ArrayDimFetch) {
$propertyType = $this->arrayDimFetchTypeResolver->resolve($propertyFetch, $node);
$definedPropertiesWithTypes[] = new DefinedPropertyWithType($propertyName, $propertyType, $methodName);
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
$propertyType = $this->nodeTypeResolver->getType($node->expr);
$definedPropertiesWithTypes[] = new DefinedPropertyWithType($propertyName, $propertyType, $methodName);
return NodeVisitor::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
$propertyName = $this->resolvePropertyName($node);
if ($propertyName === null) {
return null;
}
$definedPropertiesWithTypes[] = new DefinedPropertyWithType($propertyName, new MixedType(), $methodName);
return null;
});
}
return $this->normalizeToSingleType($definedPropertiesWithTypes);
}
private function shouldSkip(Node $node): bool
{
// skip anonymous classes and inner function
if ($node instanceof Class_ || $node instanceof Function_) {
return \true;
}
// skip closure call
if ($node instanceof MethodCall && $node->var instanceof Closure) {
return \true;
}
if ($node instanceof StaticCall) {
return $this->nodeNameResolver->isName($node->class, self::LARAVEL_COLLECTION_CLASS);
}
return \false;
}
private function resolvePropertyName(Node $node): ?string
{
if (!$node instanceof PropertyFetch) {
return null;
}
if (!$this->propertyFetchAnalyzer->isLocalPropertyFetch($node)) {
return null;
}
if ($this->shouldSkipPropertyFetch($node)) {
return null;
}
return $this->nodeNameResolver->getName($node->name);
}
private function shouldSkipPropertyFetch(PropertyFetch $propertyFetch): bool
{
if ($this->isPartOfClosureBind($propertyFetch)) {
return \true;
}
return !$propertyFetch->name instanceof Identifier;
}
/**
* @param DefinedPropertyWithType[] $definedPropertiesWithTypes
* @return DefinedPropertyWithType[]
*/
private function normalizeToSingleType(array $definedPropertiesWithTypes): array
{
$definedPropertiesWithTypesByPropertyName = [];
foreach ($definedPropertiesWithTypes as $definedPropertyWithType) {
$definedPropertiesWithTypesByPropertyName[$definedPropertyWithType->getName()][] = $definedPropertyWithType;
}
$normalizedDefinedPropertiesWithTypes = [];
foreach ($definedPropertiesWithTypesByPropertyName as $propertyName => $definedPropertiesWithTypes) {
if (count($definedPropertiesWithTypes) === 1) {
$normalizedDefinedPropertiesWithTypes[] = $definedPropertiesWithTypes[0];
continue;
}
$propertyTypes = [];
foreach ($definedPropertiesWithTypes as $definedPropertyWithType) {
/** @var DefinedPropertyWithType $definedPropertyWithType */
$propertyTypes[] = $definedPropertyWithType->getType();
}
$normalizePropertyType = $this->typeFactory->createMixedPassedOrUnionType($propertyTypes);
$normalizedDefinedPropertiesWithTypes[] = new DefinedPropertyWithType(
$propertyName,
$normalizePropertyType,
// skip as multiple places can define the same property
null
);
}
return $normalizedDefinedPropertiesWithTypes;
}
/**
* Local property is actually not local one, but belongs to passed object
* See https://ocramius.github.io/blog/accessing-private-php-class-members-without-reflection/
*/
private function isPartOfClosureBind(PropertyFetch $propertyFetch): bool
{
$scope = $propertyFetch->getAttribute(AttributeKey::SCOPE);
if (!$scope instanceof Scope) {
return \false;
}
return $scope->isInClosureBind();
}
}
@@ -0,0 +1,52 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeAnalyzer;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
use Rector\CodeQuality\ValueObject\DefinedPropertyWithType;
use Rector\NodeAnalyzer\PropertyPresenceChecker;
final class MissingPropertiesResolver
{
/**
* @readonly
*/
private \Rector\CodeQuality\NodeAnalyzer\ClassLikeAnalyzer $classLikeAnalyzer;
/**
* @readonly
*/
private PropertyPresenceChecker $propertyPresenceChecker;
public function __construct(\Rector\CodeQuality\NodeAnalyzer\ClassLikeAnalyzer $classLikeAnalyzer, PropertyPresenceChecker $propertyPresenceChecker)
{
$this->classLikeAnalyzer = $classLikeAnalyzer;
$this->propertyPresenceChecker = $propertyPresenceChecker;
}
/**
* @param DefinedPropertyWithType[] $definedPropertiesWithTypes
* @return DefinedPropertyWithType[]
*/
public function resolve(Class_ $class, ClassReflection $classReflection, array $definedPropertiesWithTypes): array
{
$existingPropertyNames = $this->classLikeAnalyzer->resolvePropertyNames($class);
$missingPropertiesWithTypes = [];
foreach ($definedPropertiesWithTypes as $definedPropertyWithType) {
// 1. property already exists, skip it
if (in_array($definedPropertyWithType->getName(), $existingPropertyNames, \true)) {
continue;
}
// 2. is part of class docblock or another magic, skip it
if ($classReflection->hasInstanceProperty($definedPropertyWithType->getName())) {
continue;
}
// 3. is fetched by parent class on non-private property etc., skip it
$hasClassContextProperty = $this->propertyPresenceChecker->hasClassContextProperty($class, $definedPropertyWithType);
if ($hasClassContextProperty) {
continue;
}
// it's most likely missing!
$missingPropertiesWithTypes[] = $definedPropertyWithType;
}
return $missingPropertiesWithTypes;
}
}
@@ -0,0 +1,86 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeAnalyzer;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Return_;
use Rector\CodeQuality\ValueObject\KeyAndExpr;
use Rector\Exception\NotImplementedYetException;
use Rector\NodeAnalyzer\ExprAnalyzer;
use Rector\PhpParser\Node\Value\ValueResolver;
final class VariableDimFetchAssignResolver
{
/**
* @readonly
*/
private ExprAnalyzer $exprAnalyzer;
/**
* @readonly
*/
private ValueResolver $valueResolver;
public function __construct(ExprAnalyzer $exprAnalyzer, ValueResolver $valueResolver)
{
$this->exprAnalyzer = $exprAnalyzer;
$this->valueResolver = $valueResolver;
}
/**
* @param Stmt[] $stmts
* @return array<mixed, KeyAndExpr[]>
*/
public function resolveFromStmtsAndVariable(array $stmts, ?Assign $emptyArrayAssign): array
{
$exprs = [];
$key = 0;
foreach ($stmts as $stmt) {
if ($stmt instanceof Expression && $stmt->expr === $emptyArrayAssign) {
continue;
}
if ($stmt instanceof Return_) {
continue;
}
if (!$stmt instanceof Expression) {
return [];
}
$stmtExpr = $stmt->expr;
if (!$stmtExpr instanceof Assign) {
return [];
}
$assign = $stmtExpr;
$dimValues = [];
$arrayDimFetch = $assign->var;
while ($arrayDimFetch instanceof ArrayDimFetch) {
if ($arrayDimFetch->dim instanceof Expr && $this->exprAnalyzer->isDynamicExpr($arrayDimFetch->dim)) {
return [];
}
$dimValues[] = $arrayDimFetch->dim instanceof Expr ? $this->valueResolver->getValue($arrayDimFetch->dim) : $key;
$arrayDimFetch = $arrayDimFetch->var;
}
++$key;
$this->setNestedKeysExpr($exprs, $dimValues, $assign->expr);
}
return $exprs;
}
/**
* @param mixed[] $exprsByKeys
* @param array<string|int> $keys
*/
private function setNestedKeysExpr(array &$exprsByKeys, array $keys, Expr $expr): void
{
$reference =& $exprsByKeys;
$keys = array_reverse($keys);
foreach ($keys as $key) {
if ($reference instanceof Array_) {
// currently it fails here with Cannot use object of type PhpParser\Node\Expr\Array_ as array
throw new NotImplementedYetException();
}
$reference =& $reference[$key];
}
$reference = $expr;
}
}
@@ -0,0 +1,64 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeFactory;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\PropertyItem;
use PhpParser\Node\Stmt\Property;
use Rector\CodeQuality\ValueObject\DefinedPropertyWithType;
use Rector\Php\PhpVersionProvider;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\ValueObject\MethodName;
use Rector\ValueObject\PhpVersionFeature;
final class MissingPropertiesFactory
{
/**
* @readonly
*/
private \Rector\CodeQuality\NodeFactory\PropertyTypeDecorator $propertyTypeDecorator;
/**
* @readonly
*/
private PhpVersionProvider $phpVersionProvider;
/**
* @readonly
*/
private StaticTypeMapper $staticTypeMapper;
public function __construct(\Rector\CodeQuality\NodeFactory\PropertyTypeDecorator $propertyTypeDecorator, PhpVersionProvider $phpVersionProvider, StaticTypeMapper $staticTypeMapper)
{
$this->propertyTypeDecorator = $propertyTypeDecorator;
$this->phpVersionProvider = $phpVersionProvider;
$this->staticTypeMapper = $staticTypeMapper;
}
/**
* @param DefinedPropertyWithType[] $definedPropertiesWithType
* @return Property[]
*/
public function create(array $definedPropertiesWithType): array
{
$newProperties = [];
foreach ($definedPropertiesWithType as $definedPropertyWithType) {
$visibilityModifier = $this->isFromAlwaysDefinedMethod($definedPropertyWithType) ? Modifiers::PRIVATE : Modifiers::PUBLIC;
$property = new Property($visibilityModifier, [new PropertyItem($definedPropertyWithType->getName())]);
if ($this->isFromAlwaysDefinedMethod($definedPropertyWithType) && $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) {
$propertyType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($definedPropertyWithType->getType(), TypeKind::PROPERTY);
if ($propertyType instanceof Node) {
$property->type = $propertyType;
$newProperties[] = $property;
continue;
}
}
// fallback to docblock
$this->propertyTypeDecorator->decorateProperty($property, $definedPropertyWithType->getType());
$newProperties[] = $property;
}
return $newProperties;
}
private function isFromAlwaysDefinedMethod(DefinedPropertyWithType $definedPropertyWithType): bool
{
return in_array($definedPropertyWithType->getDefinedInMethodName(), [MethodName::CONSTRUCT, MethodName::SET_UP], \true);
}
}
@@ -0,0 +1,39 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeFactory;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Privatization\TypeManipulator\TypeNormalizer;
final class PropertyTypeDecorator
{
/**
* @readonly
*/
private PhpDocTypeChanger $phpDocTypeChanger;
/**
* @readonly
*/
private PhpDocInfoFactory $phpDocInfoFactory;
/**
* @readonly
*/
private TypeNormalizer $typeNormalizer;
public function __construct(PhpDocTypeChanger $phpDocTypeChanger, PhpDocInfoFactory $phpDocInfoFactory, TypeNormalizer $typeNormalizer)
{
$this->phpDocTypeChanger = $phpDocTypeChanger;
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->typeNormalizer = $typeNormalizer;
}
public function decorateProperty(Property $property, Type $propertyType): void
{
// generalize false/true type to bool, as mostly default value but accepts both
$propertyType = $this->typeNormalizer->generalizeConstantTypes($propertyType);
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$phpDocInfo->makeMultiLined();
$this->phpDocTypeChanger->changeVarType($property, $phpDocInfo, $propertyType);
}
}
@@ -0,0 +1,46 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeFactory;
use PhpParser\Modifiers;
use PhpParser\Node;
use PhpParser\Node\ComplexType;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use PhpParser\Node\NullableType;
use PhpParser\Node\PropertyItem;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\StaticTypeMapper\StaticTypeMapper;
final class TypedPropertyFactory
{
/**
* @readonly
*/
private StaticTypeMapper $staticTypeMapper;
public function __construct(StaticTypeMapper $staticTypeMapper)
{
$this->staticTypeMapper = $staticTypeMapper;
}
public function createFromPropertyTagValueNode(PropertyTagValueNode $propertyTagValueNode, Class_ $class, string $propertyName): Property
{
$propertyItem = new PropertyItem($propertyName);
$propertyTypeNode = $this->createPropertyTypeNode($propertyTagValueNode, $class);
return new Property(Modifiers::PRIVATE, [$propertyItem], [], $propertyTypeNode);
}
/**
* @return \PhpParser\Node\Name|\PhpParser\Node\ComplexType|\PhpParser\Node\Identifier|null
*/
public function createPropertyTypeNode(PropertyTagValueNode $propertyTagValueNode, Class_ $class, bool $isNullable = \true)
{
$propertyType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($propertyTagValueNode->type, $class);
$typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($propertyType, TypeKind::PROPERTY);
if ($isNullable && !$typeNode instanceof NullableType && !$typeNode instanceof ComplexType && $typeNode instanceof Node) {
return new NullableType($typeNode);
}
return $typeNode;
}
}
@@ -0,0 +1,76 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\NodeManipulator;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Cast\Bool_;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\NodeTypeResolver\PHPStan\Type\StaticTypeAnalyzer;
use Rector\PhpParser\Node\NodeFactory;
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
final class ExprBoolCaster
{
/**
* @readonly
*/
private NodeTypeResolver $nodeTypeResolver;
/**
* @readonly
*/
private TypeUnwrapper $typeUnwrapper;
/**
* @readonly
*/
private StaticTypeAnalyzer $staticTypeAnalyzer;
/**
* @readonly
*/
private NodeFactory $nodeFactory;
public function __construct(NodeTypeResolver $nodeTypeResolver, TypeUnwrapper $typeUnwrapper, StaticTypeAnalyzer $staticTypeAnalyzer, NodeFactory $nodeFactory)
{
$this->nodeTypeResolver = $nodeTypeResolver;
$this->typeUnwrapper = $typeUnwrapper;
$this->staticTypeAnalyzer = $staticTypeAnalyzer;
$this->nodeFactory = $nodeFactory;
}
public function boolCastOrNullCompareIfNeeded(Expr $expr): Expr
{
$exprStaticType = $this->nodeTypeResolver->getType($expr);
if (!TypeCombinator::containsNull($exprStaticType)) {
if (!$this->isBoolCastNeeded($expr, $exprStaticType)) {
return $expr;
}
return new Bool_($expr);
}
// if we remove null type, still has to be trueable
if ($exprStaticType instanceof UnionType) {
$unionTypeWithoutNullType = $this->typeUnwrapper->removeNullTypeFromUnionType($exprStaticType);
if ($this->staticTypeAnalyzer->isAlwaysTruableType($unionTypeWithoutNullType)) {
return new NotIdentical($expr, $this->nodeFactory->createNull());
}
} elseif ($this->staticTypeAnalyzer->isAlwaysTruableType($exprStaticType)) {
return new NotIdentical($expr, $this->nodeFactory->createNull());
}
if (!$this->isBoolCastNeeded($expr, $exprStaticType)) {
return $expr;
}
return new Bool_($expr);
}
private function isBoolCastNeeded(Expr $expr, Type $exprType): bool
{
if ($expr instanceof BooleanNot) {
return \false;
}
if ($exprType->isBoolean()->yes()) {
return \false;
}
return !$expr instanceof BinaryOp;
}
}
@@ -0,0 +1,61 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\Assign;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp\BitwiseXor;
use Rector\PhpParser\Node\AssignAndBinaryMap;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\Assign\CombinedAssignRector\CombinedAssignRectorTest
*/
final class CombinedAssignRector extends AbstractRector
{
/**
* @readonly
*/
private AssignAndBinaryMap $assignAndBinaryMap;
public function __construct(AssignAndBinaryMap $assignAndBinaryMap)
{
$this->assignAndBinaryMap = $assignAndBinaryMap;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Simplify $value = $value + 5; assignments to shorter ones', [new CodeSample('$value = $value + 5;', '$value += 5;')]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
if (!$node->expr instanceof BinaryOp) {
return null;
}
/** @var BinaryOp $binaryNode */
$binaryNode = $node->expr;
if (!$this->nodeComparator->areNodesEqual($node->var, $binaryNode->left)) {
return null;
}
if ($binaryNode->left instanceof ArrayDimFetch && $node->expr instanceof BitwiseXor) {
return null;
}
$assignClass = $this->assignAndBinaryMap->getAlternative($binaryNode);
if ($assignClass === null) {
return null;
}
return new $assignClass($node->var, $binaryNode->right);
}
}
@@ -0,0 +1,62 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\BooleanAnd;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Instanceof_;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\BooleanAnd\RemoveUselessIsObjectCheckRector\RemoveUselessIsObjectCheckRectorTest
*/
final class RemoveUselessIsObjectCheckRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Remove useless is_object() check on combine with instanceof check', [new CodeSample('is_object($obj) && $obj instanceof DateTime', '$obj instanceof DateTime')]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [BooleanAnd::class];
}
/**
* @param BooleanAnd $node
*/
public function refactor(Node $node): ?Node
{
if ($node->left instanceof FuncCall && $this->isName($node->left, 'is_object') && $node->right instanceof Instanceof_) {
return $this->processRemoveUselessIsObject($node->left, $node->right);
}
if (!$node->left instanceof Instanceof_) {
return null;
}
if (!$node->right instanceof FuncCall) {
return null;
}
if (!$this->isName($node->right, 'is_object')) {
return null;
}
return $this->processRemoveUselessIsObject($node->right, $node->left);
}
private function processRemoveUselessIsObject(FuncCall $funcCall, Instanceof_ $instanceof): ?Instanceof_
{
if ($funcCall->isFirstClassCallable()) {
return null;
}
$args = $funcCall->getArgs();
if (!isset($args[0])) {
return null;
}
if (!$this->nodeComparator->areNodesEqual($args[0]->value, $instanceof->expr)) {
return null;
}
return $instanceof;
}
}
@@ -0,0 +1,158 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\BooleanAnd;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\NotEqual;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use Rector\CodeQuality\ValueObject\ComparedExprAndValueExpr;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\BooleanAnd\RepeatedAndNotEqualToNotInArrayRector\RepeatedAndNotEqualToNotInArrayRectorTest
*/
final class RepeatedAndNotEqualToNotInArrayRector extends AbstractRector
{
/**
* @readonly
*/
private BetterNodeFinder $betterNodeFinder;
public function __construct(BetterNodeFinder $betterNodeFinder)
{
$this->betterNodeFinder = $betterNodeFinder;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Simplify repeated && compare of same value, to ! in_array() call', [new CodeSample(<<<'CODE_SAMPLE'
if ($value !== 10 && $value !== 20 && $value !== 30) {
// ...
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
if (! in_array($value, [10, 20, 30], true)) {
// ...
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [BooleanAnd::class];
}
/**
* @param BooleanAnd $node
*/
public function refactor(Node $node): ?BooleanNot
{
if (!$this->isNotEqualOrNotIdentical($node->right)) {
return null;
}
// match compared variable and expr
if (!$node->left instanceof BooleanAnd && !$this->isNotEqualOrNotIdentical($node->left)) {
return null;
}
$comparedExprAndValueExprs = $this->matchComparedAndDesiredValues($node);
if ($comparedExprAndValueExprs === null) {
return null;
}
if (count($comparedExprAndValueExprs) < 3) {
return null;
}
// ensure all compared expr are the same
$valueExprs = $this->resolveValueExprs($comparedExprAndValueExprs);
/** @var ComparedExprAndValueExpr $firstComparedExprAndValue */
$firstComparedExprAndValue = array_pop($comparedExprAndValueExprs);
// all compared expr must be equal
foreach ($comparedExprAndValueExprs as $comparedExprAndValueExpr) {
if (!$this->nodeComparator->areNodesEqual($firstComparedExprAndValue->getComparedExpr(), $comparedExprAndValueExpr->getComparedExpr())) {
return null;
}
}
$array = $this->nodeFactory->createArray($valueExprs);
$args = $this->nodeFactory->createArgs([$firstComparedExprAndValue->getComparedExpr(), $array]);
if ($this->isStrictComparison($node)) {
$args[] = new Arg(new ConstFetch(new Name('true')));
}
$inArrayFuncCall = new FuncCall(new Name('in_array'), $args);
return new BooleanNot($inArrayFuncCall);
}
private function isNotEqualOrNotIdentical(Expr $expr): bool
{
if ($expr instanceof NotIdentical) {
return \true;
}
return $expr instanceof NotEqual;
}
/**
* @param \PhpParser\Node\Expr\BinaryOp\NotIdentical|\PhpParser\Node\Expr\BinaryOp\NotEqual $expr
*/
private function matchComparedExprAndValueExpr($expr): ComparedExprAndValueExpr
{
return new ComparedExprAndValueExpr($expr->left, $expr->right);
}
/**
* @param ComparedExprAndValueExpr[] $comparedExprAndValueExprs
* @return Expr[]
*/
private function resolveValueExprs(array $comparedExprAndValueExprs): array
{
$valueExprs = [];
foreach ($comparedExprAndValueExprs as $comparedExprAndValueExpr) {
$valueExprs[] = $comparedExprAndValueExpr->getValueExpr();
}
return $valueExprs;
}
/**
* @return null|ComparedExprAndValueExpr[]
*/
private function matchComparedAndDesiredValues(BooleanAnd $booleanAnd): ?array
{
/** @var NotIdentical|NotEqual $rightCompare */
$rightCompare = $booleanAnd->right;
// match compared expr and desired value
$comparedExprAndValueExprs = [$this->matchComparedExprAndValueExpr($rightCompare)];
$currentBooleanAnd = $booleanAnd;
while ($currentBooleanAnd->left instanceof BooleanAnd) {
if (!$this->isNotEqualOrNotIdentical($currentBooleanAnd->left->right)) {
return null;
}
/** @var NotIdentical|NotEqual $leftRight */
$leftRight = $currentBooleanAnd->left->right;
$comparedExprAndValueExprs[] = $this->matchComparedExprAndValueExpr($leftRight);
$currentBooleanAnd = $currentBooleanAnd->left;
}
if (!$this->isNotEqualOrNotIdentical($currentBooleanAnd->left)) {
return null;
}
/** @var NotIdentical|NotEqual $leftCompare */
$leftCompare = $currentBooleanAnd->left;
$comparedExprAndValueExprs[] = $this->matchComparedExprAndValueExpr($leftCompare);
// keep original natural order, as left/right goes from bottom up
return array_reverse($comparedExprAndValueExprs);
}
private function isStrictComparison(BooleanAnd $booleanAnd): bool
{
$notIdenticals = $this->betterNodeFinder->findInstanceOf($booleanAnd, NotIdentical::class);
$notEquals = $this->betterNodeFinder->findInstanceOf($booleanAnd, NotEqual::class);
if ($notIdenticals !== []) {
// mix not identical and not equals, keep as is
// @see https://3v4l.org/2SoHZ
return $notEquals === [];
}
return \false;
}
}
@@ -0,0 +1,93 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\BooleanAnd;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\Empty_;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\Variable;
use Rector\NodeManipulator\BinaryOpManipulator;
use Rector\Php71\ValueObject\TwoNodeMatch;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\BooleanAnd\SimplifyEmptyArrayCheckRector\SimplifyEmptyArrayCheckRectorTest
*/
final class SimplifyEmptyArrayCheckRector extends AbstractRector
{
/**
* @readonly
*/
private BinaryOpManipulator $binaryOpManipulator;
public function __construct(BinaryOpManipulator $binaryOpManipulator)
{
$this->binaryOpManipulator = $binaryOpManipulator;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Simplify `is_array` and `empty` functions combination into a simple identical check for an empty array', [new CodeSample('is_array($values) && empty($values)', '$values === []')]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [BooleanAnd::class];
}
/**
* @param BooleanAnd $node
*/
public function refactor(Node $node): ?Node
{
$twoNodeMatch = $this->resolveTwoNodeMatch($node);
if (!$twoNodeMatch instanceof TwoNodeMatch) {
return null;
}
/** @var FuncCall $isArrayExpr */
$isArrayExpr = $twoNodeMatch->getFirstExpr();
$firstArgValue = $isArrayExpr->getArgs()[0]->value;
/** @var Empty_ $emptyOrNotIdenticalNode */
$emptyOrNotIdenticalNode = $twoNodeMatch->getSecondExpr();
if ($emptyOrNotIdenticalNode->expr instanceof FuncCall && $this->nodeComparator->areNodesEqual($emptyOrNotIdenticalNode->expr->getArgs()[0]->value, $firstArgValue)) {
return new Identical($emptyOrNotIdenticalNode->expr, new Array_());
}
if (!$this->nodeComparator->areNodesEqual($emptyOrNotIdenticalNode->expr, $firstArgValue)) {
return null;
}
return new Identical($emptyOrNotIdenticalNode->expr, new Array_());
}
private function resolveTwoNodeMatch(BooleanAnd $booleanAnd): ?TwoNodeMatch
{
return $this->binaryOpManipulator->matchFirstAndSecondConditionNode(
$booleanAnd,
// is_array(...)
function (Node $node): bool {
if (!$node instanceof FuncCall) {
return \false;
}
if ($node->isFirstClassCallable()) {
return \false;
}
if (!$this->isName($node, 'is_array')) {
return \false;
}
if (isset($node->getArgs()[0])) {
return $node->getArgs()[0]->value instanceof Variable;
}
return \false;
},
// empty(...)
function (Node $node): bool {
if (!$node instanceof Empty_) {
return \false;
}
return $node->expr instanceof Variable;
}
);
}
}
@@ -0,0 +1,71 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\BooleanNot;
use PhpParser\Node;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Name;
use Rector\PhpParser\Node\Value\ValueResolver;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* Replace negated boolean literals with their simplified equivalents
*
* @see \Rector\Tests\CodeQuality\Rector\BooleanNot\ReplaceConstantBooleanNotRector\ReplaceConstantBooleanNotRectorTest
*/
final class ReplaceConstantBooleanNotRector extends AbstractRector
{
/**
* @readonly
*/
private ValueResolver $valueResolver;
public function __construct(ValueResolver $valueResolver)
{
$this->valueResolver = $valueResolver;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replace negated boolean literals (!false, !true) with their simplified equivalents (true, false)', [new CodeSample(<<<'CODE_SAMPLE'
if (!false) {
return 'always true';
}
if (!true) {
return 'never reached';
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
if (true) {
return 'always true';
}
if (false) {
return 'never reached';
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [BooleanNot::class];
}
/**
* @param BooleanNot $node
*/
public function refactor(Node $node): ?Node
{
if ($this->valueResolver->isFalse($node->expr)) {
return new ConstFetch(new Name('true'));
}
if ($this->valueResolver->isTrue($node->expr)) {
return new ConstFetch(new Name('false'));
}
return null;
}
}
@@ -0,0 +1,54 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\BooleanNot;
use PhpParser\Node;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Cast\Bool_;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\BooleanNot\ReplaceMultipleBooleanNotRector\ReplaceMultipleBooleanNotRectorTest
*/
final class ReplaceMultipleBooleanNotRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Replace the Double not operator (!!) by type-casting to boolean', [new CodeSample(<<<'CODE_SAMPLE'
$bool = !!$var;
CODE_SAMPLE
, <<<'CODE_SAMPLE'
$bool = (bool) $var;
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [BooleanNot::class];
}
/**
* @param BooleanNot $node
*/
public function refactor(Node $node): ?Node
{
$depth = 0;
$expr = $node->expr;
while ($expr instanceof BooleanNot) {
++$depth;
$expr = $expr->expr;
}
if ($depth === 0) {
return null;
}
if ($depth % 2 === 0) {
$node->expr = $expr;
return $node;
}
return new Bool_($expr);
}
}
@@ -0,0 +1,58 @@
<?php
declare (strict_types=1);
namespace Rector\CodeQuality\Rector\BooleanNot;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp;
use PhpParser\Node\Expr\BinaryOp\BooleanOr;
use PhpParser\Node\Expr\BooleanNot;
use Rector\NodeManipulator\BinaryOpManipulator;
use Rector\Rector\AbstractRector;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\CodeQuality\Rector\BooleanNot\SimplifyDeMorganBinaryRector\SimplifyDeMorganBinaryRectorTest
*/
final class SimplifyDeMorganBinaryRector extends AbstractRector
{
/**
* @readonly
*/
private BinaryOpManipulator $binaryOpManipulator;
public function __construct(BinaryOpManipulator $binaryOpManipulator)
{
$this->binaryOpManipulator = $binaryOpManipulator;
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Simplify negated conditions with de Morgan theorem', [new CodeSample(<<<'CODE_SAMPLE'
$a = 5;
$b = 10;
$result = !($a > 20 || $b <= 50);
CODE_SAMPLE
, <<<'CODE_SAMPLE'
$a = 5;
$b = 10;
$result = $a <= 20 && $b > 50;
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [BooleanNot::class];
}
/**
* @param BooleanNot $node
*/
public function refactor(Node $node): ?BinaryOp
{
if (!$node->expr instanceof BooleanOr) {
return null;
}
return $this->binaryOpManipulator->inverseBooleanOr($node->expr);
}
}

Some files were not shown because too many files have changed in this diff Show More