diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_static_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_static_method.php.inc new file mode 100644 index 00000000000..56d58471ac4 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/call_static_method.php.inc @@ -0,0 +1,25 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/get_and_set.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/get_and_set.php.inc new file mode 100644 index 00000000000..d93ec6f791c --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/get_and_set.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/isset_unset.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/isset_unset.php.inc new file mode 100644 index 00000000000..15894cb6271 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/Fixture/isset_unset.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/config/configured_rule.php index bc7d6210ac2..93c7de5d39f 100644 --- a/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/config/configured_rule.php +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector/config/configured_rule.php @@ -2,8 +2,10 @@ declare(strict_types=1); +use Rector\ValueObject\PhpVersionFeature; use Rector\Config\RectorConfig; use Rector\TypeDeclaration\Rector\ClassMethod\KnownMagicClassMethodTypeRector; return RectorConfig::configure() - ->withRules([KnownMagicClassMethodTypeRector::class]); + ->withRules([KnownMagicClassMethodTypeRector::class]) + ->withPhpVersion(PhpVersionFeature::MIXED_TYPE); diff --git a/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php index 7917e871b23..5121f11721c 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/KnownMagicClassMethodTypeRector.php @@ -8,9 +8,12 @@ use PhpParser\Node\Identifier; use PhpParser\Node\Name; use PhpParser\Node\Stmt\Class_; +use Rector\Php\PhpVersionProvider; use Rector\Rector\AbstractRector; use Rector\ValueObject\MethodName; +use Rector\ValueObject\PhpVersionFeature; use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard; +use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -19,12 +22,15 @@ * * @see https://www.php.net/manual/en/language.oop5.overloading.php#object.call */ -final class KnownMagicClassMethodTypeRector extends AbstractRector +final class KnownMagicClassMethodTypeRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private readonly ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard - ){ + private readonly ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard, + private readonly PhpVersionProvider $phpVersionProvider, + ) { + } + public function getRuleDefinition(): RuleDefinition { return new RuleDefinition( @@ -73,24 +79,45 @@ public function refactor(Node $node): ?Node continue; } - if (! $this->isName($classMethod, MethodName::CALL)) { - continue; - } - if ($this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod)) { return null; } - $firstParam = $classMethod->getParams()[0]; - if (! $firstParam->type instanceof Node) { - $firstParam->type = new Identifier('string'); - $hasChanged = true; + if ($this->isNames($classMethod, [MethodName::CALL, MethodName::CALL_STATIC])) { + $secondParam = $classMethod->getParams()[1]; + if (! $secondParam->type instanceof Node) { + $secondParam->type = new Name('array'); + $hasChanged = true; + } } - $secondParam = $classMethod->getParams()[1]; - if (! $secondParam->type instanceof Node) { - $secondParam->type = new Name('array'); - $hasChanged = true; + // first arg string + if ($this->isNames( + $classMethod, + [ + MethodName::CALL, + MethodName::CALL_STATIC, + MethodName::__SET, + MethodName::__GET, + MethodName::ISSET, + MethodName::UNSET, + ] + )) { + $firstParam = $classMethod->getParams()[0]; + if (! $firstParam->type instanceof Node) { + $firstParam->type = new Identifier('string'); + $hasChanged = true; + } + } + + if ($this->isName($classMethod, MethodName::__SET) && $this->phpVersionProvider->isAtLeastPhpVersion( + PhpVersionFeature::MIXED_TYPE + )) { + $secondParam = $classMethod->getParams()[1]; + if (! $secondParam->type instanceof Node) { + $secondParam->type = new Identifier('mixed'); + $hasChanged = true; + } } } @@ -100,4 +127,9 @@ public function refactor(Node $node): ?Node return null; } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::SCALAR_TYPES; + } } diff --git a/src/ValueObject/MethodName.php b/src/ValueObject/MethodName.php index 64352c4724e..357507b6508 100644 --- a/src/ValueObject/MethodName.php +++ b/src/ValueObject/MethodName.php @@ -58,6 +58,11 @@ final class MethodName */ public const CALL = '__call'; + /** + * @var string + */ + public const CALL_STATIC = '__callStatic'; + /** * @var string */ @@ -72,4 +77,9 @@ final class MethodName * @var string */ public const ISSET = '__isset'; + + /** + * @var string + */ + public const UNSET = '__unset'; }