diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index cb7a029c70e..f05994dbcab 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 354 Rules Overview +# 355 Rules Overview
@@ -44,7 +44,7 @@ - [Php82](#php82) (4) -- [Php83](#php83) (2) +- [Php83](#php83) (3) - [Privatization](#privatization) (4) @@ -5312,6 +5312,19 @@ Add const to type
+### CombineHostPortLdapUriRector + +Combine separated host and port on `ldap_connect()` args + +- class: [`Rector\Php83\Rector\FuncCall\CombineHostPortLdapUriRector`](../rules/Php83/Rector/FuncCall/CombineHostPortLdapUriRector.php) + +```diff +-ldap_connect('ldap://ldap.example.com', 389); ++ldap_connect('ldap://ldap.example.com:389'); +``` + +
+ ## Privatization ### FinalizeClassesWithoutChildrenRector @@ -6850,13 +6863,15 @@ Add return type based on strict parameter type Change return type based on strict scalar returns - string, int, float or bool +:wrench: **configure it!** + - class: [`Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictScalarReturnExprRector`](../rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector.php) ```diff final class SomeClass { -- public function run($value) -+ public function run($value): string +- public function foo($value) ++ public function foo($value): string { if ($value) { return 'yes'; @@ -6864,6 +6879,12 @@ Change return type based on strict scalar returns - string, int, float or bool return 'no'; } + +- public function bar(string $value) ++ public function bar(string $value): int + { + return strlen($value); + } } ``` diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/bool.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/bool.php.inc new file mode 100644 index 00000000000..9dc195787ba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/bool.php.inc @@ -0,0 +1,35 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/final_method.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/final_method.php.inc new file mode 100644 index 00000000000..502ad71f4e0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/final_method.php.inc @@ -0,0 +1,49 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float.php.inc new file mode 100644 index 00000000000..c889e3a2cb7 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float_negative.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float_negative.php.inc new file mode 100644 index 00000000000..c1557ebcd46 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float_negative.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float_positive.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float_positive.php.inc new file mode 100644 index 00000000000..5d83a79b901 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/float_positive.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int.php.inc new file mode 100644 index 00000000000..2130915d2d2 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int_negative.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int_negative.php.inc new file mode 100644 index 00000000000..8b519ecbc74 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int_negative.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int_positive.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int_positive.php.inc new file mode 100644 index 00000000000..45c5477fe70 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/int_positive.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/skip_calc.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/skip_calc.php.inc new file mode 100644 index 00000000000..64ff5905099 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/FixtureHardCodedOnly/skip_calc.php.inc @@ -0,0 +1,11 @@ + +----- + diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/HardCodedOnlyTest.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/HardCodedOnlyTest.php new file mode 100644 index 00000000000..dd188c25927 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/HardCodedOnlyTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureHardCodedOnly'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_hard_coded_only.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/config/configured_rule_hard_coded_only.php b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/config/configured_rule_hard_coded_only.php new file mode 100644 index 00000000000..f27974dd8e5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector/config/configured_rule_hard_coded_only.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(ReturnTypeFromStrictScalarReturnExprRector::class, [ + ReturnTypeFromStrictScalarReturnExprRector::HARD_CODED_ONLY => true, + ]); +}; diff --git a/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictScalarReturnTypeAnalyzer.php b/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictScalarReturnTypeAnalyzer.php index bfa22955fa9..289f58ee640 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictScalarReturnTypeAnalyzer.php +++ b/rules/TypeDeclaration/NodeAnalyzer/ReturnTypeAnalyzer/StrictScalarReturnTypeAnalyzer.php @@ -6,6 +6,9 @@ use PhpParser\Node\Expr; use PhpParser\Node\Expr\Closure; +use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\Expr\UnaryMinus; +use PhpParser\Node\Scalar; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; use PHPStan\Type\Type; @@ -21,8 +24,10 @@ public function __construct( ) { } - public function matchAlwaysScalarReturnType(ClassMethod|Closure|Function_ $functionLike): ?Type - { + public function matchAlwaysScalarReturnType( + ClassMethod|Closure|Function_ $functionLike, + bool $hardCodedOnly = false + ): ?Type { $returns = $this->alwaysStrictReturnAnalyzer->matchAlwaysStrictReturns($functionLike); if ($returns === []) { return null; @@ -36,6 +41,10 @@ public function matchAlwaysScalarReturnType(ClassMethod|Closure|Function_ $funct return null; } + if ($hardCodedOnly && ! $this->isHardCodedExpression($return->expr)) { + return null; + } + $scalarType = $this->alwaysStrictScalarExprAnalyzer->matchStrictScalarExpr($return->expr); if (! $scalarType instanceof Type) { return null; @@ -46,4 +55,25 @@ public function matchAlwaysScalarReturnType(ClassMethod|Closure|Function_ $funct return $this->typeFactory->createMixedPassedOrUnionType($scalarTypes); } + + private function isHardCodedExpression(Expr $expr): bool + { + // Normal scalar values like strings, integers and floats + if ($expr instanceof Scalar) { + return true; + } + + // true / false / null are constants + if ($expr instanceof ConstFetch && + in_array($expr->name->toLowerString(), ['true', 'false', 'null'], true)) { + return true; + } + + // Negative numbers are wrapped in UnaryMinus, so check expression inside it + if (($expr instanceof UnaryMinus || $expr instanceof Expr\UnaryPlus) && $expr->expr instanceof Scalar) { + return true; + } + + return false; + } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector.php index d98b90db680..db676cb55ee 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ReturnTypeFromStrictScalarReturnExprRector.php @@ -11,6 +11,7 @@ use PhpParser\Node\UnionType; use PHPStan\Analyser\Scope; use PHPStan\Type\Type; +use Rector\Core\Contract\Rector\ConfigurableRectorInterface; use Rector\Core\Rector\AbstractScopeAwareRector; use Rector\Core\ValueObject\PhpVersion; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; @@ -18,14 +19,22 @@ use Rector\TypeDeclaration\NodeAnalyzer\ReturnTypeAnalyzer\StrictScalarReturnTypeAnalyzer; use Rector\VendorLocker\NodeVendorLocker\ClassMethodReturnTypeOverrideGuard; use Rector\VersionBonding\Contract\MinPhpVersionInterface; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictScalarReturnExprRector\ReturnTypeFromStrictScalarReturnExprRectorTest */ -final class ReturnTypeFromStrictScalarReturnExprRector extends AbstractScopeAwareRector implements MinPhpVersionInterface +final class ReturnTypeFromStrictScalarReturnExprRector extends AbstractScopeAwareRector implements MinPhpVersionInterface, ConfigurableRectorInterface { + /** + * @var string + */ + public const HARD_CODED_ONLY = 'hard_coded_only'; + + private bool $hardCodedOnly = false; + public function __construct( private readonly StrictScalarReturnTypeAnalyzer $strictScalarReturnTypeAnalyzer, private readonly ClassMethodReturnTypeOverrideGuard $classMethodReturnTypeOverrideGuard, @@ -36,11 +45,11 @@ public function __construct( public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Change return type based on strict scalar returns - string, int, float or bool', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' final class SomeClass { - public function run($value) + public function foo($value) { if ($value) { return 'yes'; @@ -48,14 +57,18 @@ public function run($value) return 'no'; } + + public function bar(string $value) + { + return strlen($value); + } } CODE_SAMPLE - , <<<'CODE_SAMPLE' final class SomeClass { - public function run($value): string + public function foo($value): string { if ($value) { return 'yes'; @@ -63,8 +76,17 @@ public function run($value): string return 'no'; } + + public function bar(string $value): int + { + return strlen($value); + } } CODE_SAMPLE + , + [ + ReturnTypeFromStrictScalarReturnExprRector::HARD_CODED_ONLY => false, + ] ), ]); } @@ -87,7 +109,10 @@ public function refactorWithScope(Node $node, Scope $scope): ?Node return null; } - $scalarReturnType = $this->strictScalarReturnTypeAnalyzer->matchAlwaysScalarReturnType($node); + $scalarReturnType = $this->strictScalarReturnTypeAnalyzer->matchAlwaysScalarReturnType( + $node, + $this->hardCodedOnly + ); if (! $scalarReturnType instanceof Type) { return null; } @@ -116,4 +141,12 @@ public function provideMinPhpVersion(): int { return PhpVersion::PHP_70; } + + public function configure(array $configuration): void + { + $hardCodedOnly = $configuration[self::HARD_CODED_ONLY] ?? false; + Assert::boolean($hardCodedOnly); + + $this->hardCodedOnly = $hardCodedOnly; + } }