diff --git a/composer.json b/composer.json index 852ee181121..8a07b62df51 100644 --- a/composer.json +++ b/composer.json @@ -47,13 +47,13 @@ "symfony/finder": "^4.4.8|^5.1", "symfony/http-kernel": "^4.4.8|^5.1", "symfony/process": "^4.4.8|^5.1", - "symplify/autowire-array-parameter": "^8.3.48", - "symplify/composer-json-manipulator": "^8.3.48", - "symplify/console-color-diff": "^8.3.48", - "symplify/easy-testing": "^8.3.48", - "symplify/package-builder": "^8.3.48", - "symplify/set-config-resolver": "^8.3.48", - "symplify/smart-file-system": "^8.3.48", + "symplify/autowire-array-parameter": "8.3.48", + "symplify/composer-json-manipulator": "8.3.48", + "symplify/console-color-diff": "8.3.48", + "symplify/easy-testing": "8.3.48", + "symplify/package-builder": "8.3.48", + "symplify/set-config-resolver": "8.3.48", + "symplify/smart-file-system": "8.3.48", "webmozart/assert": "^1.9" }, "require-dev": { @@ -67,11 +67,11 @@ "php-parallel-lint/php-parallel-lint": "^1.2", "phpunit/phpunit": "^8.5|^9.2", "psr/event-dispatcher": "^1.0", - "symplify/changelog-linker": "^8.3.48", - "symplify/easy-coding-standard": "^8.3.48", - "symplify/easy-testing": "^8.3.48", - "symplify/monorepo-builder": "^8.3.48", - "symplify/phpstan-extensions": "^8.3.48", + "symplify/changelog-linker": "8.3.48", + "symplify/easy-coding-standard": "8.3.48", + "symplify/easy-testing": "8.3.48", + "symplify/monorepo-builder": "8.3.48", + "symplify/phpstan-extensions": "8.3.48", "tracy/tracy": "^2.7" }, "replace": { @@ -345,7 +345,6 @@ "sort-packages": true, "process-timeout": 0 }, - "prefer-stable": true, "extra": { "patches": { "nette/application": [ diff --git a/config/set/laravel57.php b/config/set/laravel57.php index b0469e97ba5..4082ee560e3 100644 --- a/config/set/laravel57.php +++ b/config/set/laravel57.php @@ -8,54 +8,48 @@ use Rector\Generic\Rector\ClassMethod\ChangeMethodVisibilityRector; use Rector\Generic\ValueObject\ArgumentAdder; use Rector\Generic\ValueObject\ArgumentRemover; use Rector\Generic\ValueObject\ChangeMethodVisibility; +use Rector\Laravel\Rector\ClassMethod\AddParentBootToModelClassMethodRector; use Rector\Laravel\Rector\StaticCall\Redirect301ToPermanentRedirectRector; use function Rector\SymfonyPhpConfig\inline_value_objects; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; # see: https://laravel.com/docs/5.7/upgrade - return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); - - $services->set(ChangeMethodVisibilityRector::class) - ->call('configure', [[ - ChangeMethodVisibilityRector::METHOD_VISIBILITIES => inline_value_objects([ - new ChangeMethodVisibility('Illuminate\Routing\Router', 'addRoute', 'public'), - new ChangeMethodVisibility('Illuminate\Contracts\Auth\Access\Gate', 'raw', 'public'), - ]), - ]]); - - $services->set(ArgumentAdderRector::class) - ->call('configure', [[ - ArgumentAdderRector::ADDED_ARGUMENTS => inline_value_objects([ - new ArgumentAdder('Illuminate\Auth\Middleware\Authenticate', 'authenticate', 0, 'request'), - new ArgumentAdder( - 'Illuminate\Foundation\Auth\ResetsPasswords', - 'sendResetResponse', - 0, - 'request', - null, - 'Illuminate\Http\Illuminate\Http' - ), - new ArgumentAdder( - 'Illuminate\Foundation\Auth\SendsPasswordResetEmails', - 'sendResetLinkResponse', - 0, - 'request', - null, - 'Illuminate\Http\Illuminate\Http' - ), - ]), - ]]); - + $services->set(ChangeMethodVisibilityRector::class)->call('configure', [[ + ChangeMethodVisibilityRector::METHOD_VISIBILITIES => inline_value_objects([ + new ChangeMethodVisibility('Illuminate\Routing\Router', 'addRoute', 'public'), + new ChangeMethodVisibility('Illuminate\Contracts\Auth\Access\Gate', 'raw', 'public'), + ]), + ]]); + $services->set(ArgumentAdderRector::class)->call('configure', [[ + ArgumentAdderRector::ADDED_ARGUMENTS => inline_value_objects([ + new ArgumentAdder('Illuminate\Auth\Middleware\Authenticate', 'authenticate', 0, 'request'), + new ArgumentAdder( + 'Illuminate\Foundation\Auth\ResetsPasswords', + 'sendResetResponse', + 0, + 'request', + null, + 'Illuminate\Http\Illuminate\Http' + ), + new ArgumentAdder( + 'Illuminate\Foundation\Auth\SendsPasswordResetEmails', + 'sendResetLinkResponse', + 0, + 'request', + null, + 'Illuminate\Http\Illuminate\Http' + ), + ]), + ]]); $services->set(Redirect301ToPermanentRedirectRector::class); - - $services->set(ArgumentRemoverRector::class) - ->call('configure', [[ - ArgumentRemoverRector::REMOVED_ARGUMENTS => inline_value_objects([ - new ArgumentRemover('Illuminate\Foundation\Application', 'register', 1, [ - 'name' => 'options', - ]), + $services->set(ArgumentRemoverRector::class)->call('configure', [[ + ArgumentRemoverRector::REMOVED_ARGUMENTS => inline_value_objects([ + new ArgumentRemover('Illuminate\Foundation\Application', 'register', 1, [ + 'name' => 'options', ]), - ]]); + ]), + ]]); + $services->set(AddParentBootToModelClassMethodRector::class); }; diff --git a/docs/rector_rules_overview.md b/docs/rector_rules_overview.md index 1b4e6509ac8..03f00e78420 100644 --- a/docs/rector_rules_overview.md +++ b/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# All 604 Rectors Overview +# All 605 Rectors Overview - [Projects](#projects) --- @@ -23,7 +23,7 @@ - [FileSystemRector](#filesystemrector) (1) - [Generic](#generic) (34) - [JMS](#jms) (2) -- [Laravel](#laravel) (3) +- [Laravel](#laravel) (4) - [Legacy](#legacy) (4) - [MagicDisclosure](#magicdisclosure) (3) - [MockeryToProphecy](#mockerytoprophecy) (2) @@ -7061,6 +7061,27 @@ Removes JMS\DiExtraBundle\Annotation\Services annotation ## Laravel +### `AddParentBootToModelClassMethodRector` + +- class: [`Rector\Laravel\Rector\ClassMethod\AddParentBootToModelClassMethodRector`](/rules/laravel/src/Rector/ClassMethod/AddParentBootToModelClassMethodRector.php) +- [test fixtures](/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture) + +Add parent::boot(); call to `boot()` class method in child of `Illuminate\Database\Eloquent\Model` + +```diff + use Illuminate\Database\Eloquent\Model; + + class Product extends Model + { + public function boot() + { ++ parent::boot(); + } + } +``` + +

+ ### `MinutesToSecondsInCacheRector` - class: [`Rector\Laravel\Rector\StaticCall\MinutesToSecondsInCacheRector`](/rules/laravel/src/Rector/StaticCall/MinutesToSecondsInCacheRector.php) diff --git a/packages/simple-php-doc-parser/composer.json b/packages/simple-php-doc-parser/composer.json index 00160fbe9a9..e8f468e033b 100644 --- a/packages/simple-php-doc-parser/composer.json +++ b/packages/simple-php-doc-parser/composer.json @@ -7,11 +7,11 @@ "symfony/dependency-injection": "^4.4.8|^5.1", "symfony/config": "^4.4.8|^5.1", "symfony/http-kernel": "^4.4.8|^5.1", - "symplify/package-builder": "^8.3.48" + "symplify/package-builder": "8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.2", - "symplify/easy-testing": "^8.3.48" + "symplify/easy-testing": "8.3.48" }, "autoload": { "psr-4": { diff --git a/packages/symfony-php-config/composer.json b/packages/symfony-php-config/composer.json index 9e0406b8d29..1586b18a719 100644 --- a/packages/symfony-php-config/composer.json +++ b/packages/symfony-php-config/composer.json @@ -6,7 +6,7 @@ "php": "^7.2.4|^8.0", "symfony/dependency-injection": "^4.4.8|^5.1", "symfony/http-kernel": "^4.4.8|^5.1", - "symplify/package-builder": "^8.3.48" + "symplify/package-builder": "8.3.48" }, "require-dev": { "phpunit/phpunit": "^8.5|^9.2" diff --git a/rules/laravel/src/Rector/ClassMethod/AddParentBootToModelClassMethodRector.php b/rules/laravel/src/Rector/ClassMethod/AddParentBootToModelClassMethodRector.php new file mode 100644 index 00000000000..a091429ae16 --- /dev/null +++ b/rules/laravel/src/Rector/ClassMethod/AddParentBootToModelClassMethodRector.php @@ -0,0 +1,120 @@ +staticCallAnalyzer = $staticCallAnalyzer; + } + + public function getDefinition(): RectorDefinition + { + return new RectorDefinition( + 'Add parent::boot(); call to boot() class method in child of Illuminate\Database\Eloquent\Model', + [ + new CodeSample( + <<<'CODE_SAMPLE' +use Illuminate\Database\Eloquent\Model; + +class Product extends Model +{ + public function boot() + { + } +} +CODE_SAMPLE + + , + <<<'CODE_SAMPLE' +use Illuminate\Database\Eloquent\Model; + +class Product extends Model +{ + public function boot() + { + parent::boot(); + } +} +CODE_SAMPLE + + ), + + ]); + } + + /** + * @return string[] + */ + public function getNodeTypes(): array + { + return [ClassMethod::class]; + } + + /** + * @param ClassMethod $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isInObjectType($node, 'Illuminate\Database\Eloquent\Model')) { + return null; + } + + if (! $this->isName($node->name, self::BOOT)) { + return null; + } + + foreach ((array) $node->stmts as $key => $classMethodStmt) { + if ($classMethodStmt instanceof Expression) { + $classMethodStmt = $classMethodStmt->expr; + } + + // is in the 1st position? → only correct place + // @see https://laracasts.com/discuss/channels/laravel/laravel-57-upgrade-observer-problem?page=0#reply=454409 + if (! $this->staticCallAnalyzer->isParentCallNamed($classMethodStmt, self::BOOT)) { + continue; + } + + if ($key === 0) { + return null; + } + + // wrong location → remove it + unset($node->stmts[$key]); + } + + // missing, we need to add one + $staticCall = $this->nodeFactory->createStaticCall('parent', self::BOOT); + $parentStaticCallExpression = new Expression($staticCall); + + $node->stmts = array_merge([$parentStaticCallExpression], (array) $node->stmts); + + return $node; + } +} diff --git a/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/AddParentBootToModelClassMethodRectorTest.php b/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/AddParentBootToModelClassMethodRectorTest.php new file mode 100644 index 00000000000..6d422f0b4fd --- /dev/null +++ b/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/AddParentBootToModelClassMethodRectorTest.php @@ -0,0 +1,31 @@ +doTestFileInfo($fileInfo); + } + + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + protected function getRectorClass(): string + { + return AddParentBootToModelClassMethodRector::class; + } +} diff --git a/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture/fixture.php.inc b/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture/fixture.php.inc new file mode 100644 index 00000000000..e8f6af479df --- /dev/null +++ b/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture/fixture.php.inc @@ -0,0 +1,30 @@ + +----- + diff --git a/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture/on_the_last_line.php.inc b/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture/on_the_last_line.php.inc new file mode 100644 index 00000000000..5294d6cb2c1 --- /dev/null +++ b/rules/laravel/tests/Rector/ClassMethod/AddParentBootToModelClassMethodRector/Fixture/on_the_last_line.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules/nette/src/NodeAnalyzer/StaticCallAnalyzer.php b/rules/nette/src/NodeAnalyzer/StaticCallAnalyzer.php index 01dbedb281a..d10841f562f 100644 --- a/rules/nette/src/NodeAnalyzer/StaticCallAnalyzer.php +++ b/rules/nette/src/NodeAnalyzer/StaticCallAnalyzer.php @@ -3,6 +3,7 @@ declare(strict_types=1); namespace Rector\Nette\NodeAnalyzer; +use PhpParser\Node; use PhpParser\Node\Expr; use PhpParser\Node\Expr\StaticCall; use Rector\NodeNameResolver\NodeNameResolver; @@ -19,20 +20,24 @@ final class StaticCallAnalyzer $this->nodeNameResolver = $nodeNameResolver; } - public function isParentCallNamed(StaticCall $staticCall, string $desiredMethodName): bool + public function isParentCallNamed(Node $node, string $desiredMethodName): bool { - if ($staticCall->class instanceof Expr) { + if (! $node instanceof StaticCall) { return false; } - if (! $this->nodeNameResolver->isName($staticCall->class, 'parent')) { + if ($node->class instanceof Expr) { return false; } - if ($staticCall->name instanceof Expr) { + if (! $this->nodeNameResolver->isName($node->class, 'parent')) { return false; } - return $this->nodeNameResolver->isName($staticCall->name, $desiredMethodName); + if ($node->name instanceof Expr) { + return false; + } + + return $this->nodeNameResolver->isName($node->name, $desiredMethodName); } } diff --git a/src/Console/Command/InitCommand.php b/src/Console/Command/InitCommand.php index 0b041cc7643..54468838e50 100644 --- a/src/Console/Command/InitCommand.php +++ b/src/Console/Command/InitCommand.php @@ -45,10 +45,10 @@ final class InitCommand extends AbstractCommand $rectorConfigFiles = $this->smartFileSystem->exists(getcwd() . '/rector.php'); if (! $rectorConfigFiles) { - $this->smartFileSystem->copy(__DIR__ . '/../../../rector.php.dist', getcwd() . '/rector.php'); - $this->symfonyStyle->success('rector.php config file has been generated successfully'); + $this->smartFileSystem->copy(__DIR__ . '/../../../templates/rector.php.dist', getcwd() . '/rector.php'); + $this->symfonyStyle->success('"rector.php" config file has been generated successfully!'); } else { - $this->symfonyStyle->error('Config file not generated. A rector.php configuration file already exists'); + $this->symfonyStyle->error('Config file not generated. A "rector.php" configuration file already exists'); } return ShellCode::SUCCESS; diff --git a/src/PhpParser/Node/NodeFactory.php b/src/PhpParser/Node/NodeFactory.php index 940f3f626d9..aa7dbfa82d8 100644 --- a/src/PhpParser/Node/NodeFactory.php +++ b/src/PhpParser/Node/NodeFactory.php @@ -425,6 +425,11 @@ final class NodeFactory return $uses; } + public function createStaticCall(string $class, string $method): StaticCall + { + return new StaticCall(new Name($class), $method); + } + /** * @param mixed $item * @param string|int|null $key diff --git a/stubs/Illuminate/Database/Eloquent/Model.php b/stubs/Illuminate/Database/Eloquent/Model.php new file mode 100644 index 00000000000..891cedb528c --- /dev/null +++ b/stubs/Illuminate/Database/Eloquent/Model.php @@ -0,0 +1,11 @@ +