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