Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Laravel 11 new generic types #266

Merged
merged 2 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions docs/rector_rules_overview.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 70 Rules Overview
# 71 Rules Overview

## AbortIfRector

Expand Down Expand Up @@ -63,8 +63,6 @@ Adds the `@extends` annotation to Factories.

Add generic return type to relations in child of `Illuminate\Database\Eloquent\Model`

:wrench: **configure it!**

- class: [`RectorLaravel\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector`](../src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php)

```diff
Expand Down
3 changes: 3 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ parameters:
- '#Parameter \#1 \$node (.*?) of method RectorLaravel\\(.*?)\:\:(refactor|refactorWithScope)\(\) should be contravariant with parameter \$node \(PhpParser\\Node\) of method Rector\\Contract\\Rector\\RectorInterface\:\:refactor\(\)#'

- '#Parameter \#1 \$className of method Rector\\Reflection\\ReflectionResolver\:\:resolveMethodReflection\(\) expects class\-string, string given#'

# Laravel Container not being recognized properly in some of the tests
- '#Call to method needs\(\) on an unknown class Illuminate\\Contracts\\Container\\ContextualBindingBuilder#'
50 changes: 22 additions & 28 deletions src/Rector/ClassMethod/AddGenericReturnTypeToRelationsRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,19 @@
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\Type\FullyQualifiedIdentifierTypeNode;
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
use Rector\Contract\Rector\ConfigurableRectorInterface;
use Rector\NodeTypeResolver\TypeComparator\TypeComparator;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\Rector\AbstractScopeAwareRector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use ReflectionClassConstant;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use Webmozart\Assert\Assert;

/**
* @see \RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\AddGenericReturnTypeToRelationsRectorNewGenericsTest
* @see \RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\AddGenericReturnTypeToRelationsRectorOldGenericsTest
*/
class AddGenericReturnTypeToRelationsRector extends AbstractScopeAwareRector implements ConfigurableRectorInterface
class AddGenericReturnTypeToRelationsRector extends AbstractScopeAwareRector
{
// Relation methods which are supported by this Rector.
private const RELATION_METHODS = [
Expand All @@ -59,6 +58,7 @@ public function __construct(
private readonly PhpDocInfoFactory $phpDocInfoFactory,
private readonly BetterNodeFinder $betterNodeFinder,
private readonly StaticTypeMapper $staticTypeMapper,
private readonly string $applicationClass = 'Illuminate\Foundation\Application',
) {
}

Expand All @@ -67,7 +67,7 @@ public function getRuleDefinition(): RuleDefinition
return new RuleDefinition(
'Add generic return type to relations in child of Illuminate\Database\Eloquent\Model',
[
new ConfiguredCodeSample(
new CodeSample(
<<<'CODE_SAMPLE'
use App\Account;
use Illuminate\Database\Eloquent\Model;
Expand Down Expand Up @@ -96,9 +96,9 @@ public function accounts(): HasMany
return $this->hasMany(Account::class);
}
}
CODE_SAMPLE,
['shouldUseNewGenerics' => false]),
new ConfiguredCodeSample(
CODE_SAMPLE
),
new CodeSample(
<<<'CODE_SAMPLE'
use App\Account;
use Illuminate\Database\Eloquent\Model;
Expand Down Expand Up @@ -127,8 +127,8 @@ public function accounts(): HasMany
return $this->hasMany(Account::class);
}
}
CODE_SAMPLE,
['shouldUseNewGenerics' => true]),
CODE_SAMPLE
),
]
);
}
Expand Down Expand Up @@ -196,6 +196,9 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node
return null;
}

// Put here to make the check as late as possible
$this->setShouldUseNewGenerics();

$classForChildGeneric = $this->getClassForChildGeneric($scope, $relationMethodCall);
$classForIntermediateGeneric = $this->getClassForIntermediateGeneric($relationMethodCall);

Expand Down Expand Up @@ -232,24 +235,6 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node
return $node;
}

/**
* {@inheritDoc}
*/
public function configure(array $configuration): void
{
if ($configuration === []) {
$this->shouldUseNewGenerics = false;

return;
}

Assert::count($configuration, 1);
Assert::keyExists($configuration, 'shouldUseNewGenerics');
Assert::boolean($configuration['shouldUseNewGenerics']);

$this->shouldUseNewGenerics = $configuration['shouldUseNewGenerics'];
}

private function getRelatedModelClassFromMethodCall(MethodCall $methodCall): ?string
{
$argType = $this->getType($methodCall->getArgs()[0]->value);
Expand Down Expand Up @@ -489,4 +474,13 @@ private function getGenericTypes(string $relatedClass, ?string $childClass, ?str

return $generics;
}

private function setShouldUseNewGenerics(): void
{
$reflectionClassConstant = new ReflectionClassConstant($this->applicationClass, 'VERSION');

if (is_string($reflectionClassConstant->getValue())) {
$this->shouldUseNewGenerics = version_compare($reflectionClassConstant->getValue(), '11.15.0', '>=');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ final class AddGenericReturnTypeToRelationsRectorNewGenericsTest extends Abstrac
{
public static function provideData(): Iterator
{
// yield [__DIR__ . '/Fixture/NewGenerics/has-one-through.php.inc'];
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture/NewGenerics');
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Source;

use Illuminate\Foundation\Application;

class NewApplication extends Application
{
const VERSION = '11.15.0';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Source;

use Illuminate\Foundation\Application;

class OldApplication extends Application
{
const VERSION = '11.14.0';
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

use Rector\Config\RectorConfig;
use RectorLaravel\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector;
use RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Source\NewApplication;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');

$rectorConfig->ruleWithConfiguration(AddGenericReturnTypeToRelationsRector::class, [
'shouldUseNewGenerics' => true,
]);
$rectorConfig->when(AddGenericReturnTypeToRelationsRector::class)
->needs('$applicationClass')
->give(NewApplication::class);

$rectorConfig->rule(AddGenericReturnTypeToRelationsRector::class);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@

use Rector\Config\RectorConfig;
use RectorLaravel\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector;
use RectorLaravel\Tests\Rector\ClassMethod\AddGenericReturnTypeToRelationsRector\Source\OldApplication;

return static function (RectorConfig $rectorConfig): void {
$rectorConfig->import(__DIR__ . '/../../../../../config/config.php');

$rectorConfig->ruleWithConfiguration(AddGenericReturnTypeToRelationsRector::class, []);
$rectorConfig->when(AddGenericReturnTypeToRelationsRector::class)
->needs('$applicationClass')
->give(OldApplication::class);

$rectorConfig->rule(AddGenericReturnTypeToRelationsRector::class);
};
Loading