diff --git a/config/set/php83.php b/config/set/php83.php index 25ded98cf97..d9fc64c8e17 100644 --- a/config/set/php83.php +++ b/config/set/php83.php @@ -1,12 +1,14 @@ rules([ - \Rector\Php83\Rector\ClassMethod\AddOverrideAttributeToOverriddenMethodsRector::class, - \Rector\Php83\Rector\ClassConst\AddTypeToConstRector::class, + AddOverrideAttributeToOverriddenMethodsRector::class, + AddTypeToConstRector::class, ]); }; diff --git a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php index 8f829a6384e..7de083ad670 100644 --- a/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php +++ b/rules-tests/Php83/Rector/ClassConst/AddTypeToConstRector/config/configured_rule.php @@ -1,12 +1,13 @@ rule(\Rector\Php83\Rector\ClassConst\AddTypeToConstRector::class); + $rectorConfig->rule(AddTypeToConstRector::class); $rectorConfig->phpVersion(PhpVersion::PHP_83); }; diff --git a/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/negated_string.php.inc b/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/negated_string.php.inc index f687d4acf99..952e9d2f4b6 100644 --- a/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/negated_string.php.inc +++ b/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/negated_string.php.inc @@ -32,7 +32,7 @@ final class NegatedString public function run() { - if ($this->name === '') { + if ($this->name === '' || $this->name === '0') { return 'name'; } diff --git a/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_null_and_false.php.inc b/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_null_and_false.php.inc index 16ff184dcd2..aab23d712d7 100644 --- a/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_null_and_false.php.inc +++ b/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_null_and_false.php.inc @@ -28,7 +28,7 @@ class StringNullAndFalse { public function run(string|null|false $name) { - if ($name === '' || $name === null || $name === false) { + if ($name === '' || $name === '0' || $name === null || $name === false) { return 'no name'; } diff --git a/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_union_integer.php.inc b/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_union_integer.php.inc index a58c3093e1a..6d7db36b1e5 100644 --- a/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_union_integer.php.inc +++ b/rules-tests/Strict/Rector/BooleanNot/BooleanInBooleanNotRuleFixerRector/Fixture/string_union_integer.php.inc @@ -28,7 +28,7 @@ final class StringUnionInteger { public function run(string|int $maye) { - if ($maye === '' || $maye === 0) { + if ($maye === '' || $maye === '0' || $maye === 0) { return true; } diff --git a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc index d33cd3c4acd..4adbfa53c86 100644 --- a/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc +++ b/rules-tests/Strict/Rector/Empty_/DisallowedEmptyRuleFixerRector/Fixture/from_return_method.php.inc @@ -30,12 +30,12 @@ final class FromReturnCall { public function run() { - return $this->getProperty() === ''; + return $this->getProperty() === '' || $this->getProperty() === '0'; } public function run2() { - return $this->getProperty() !== ''; + return $this->getProperty() !== '' && $this->getProperty() !== '0'; } public function getProperty(): string diff --git a/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/elseif_negated_string.php.inc b/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/elseif_negated_string.php.inc index 74a70a18273..f8f3b77cb26 100644 --- a/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/elseif_negated_string.php.inc +++ b/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/elseif_negated_string.php.inc @@ -36,7 +36,7 @@ final class ElseIfNegatedString { if ($boolValue) { return 'bool'; - } elseif ($this->name !== '') { + } elseif ($this->name !== '' && $this->name !== '0') { return 'name'; } diff --git a/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/integer_union_string.php.inc b/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/integer_union_string.php.inc index fe57bdc66e6..3d0eb4a014b 100644 --- a/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/integer_union_string.php.inc +++ b/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/integer_union_string.php.inc @@ -28,7 +28,7 @@ final class IntegerUnionString { public function run(int|string $value) { - if ($value !== 0 && $value !== '') { + if ($value !== 0 && ($value !== '' && $value !== '0')) { return 'name'; } diff --git a/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/negated_string.php.inc b/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/negated_string.php.inc index 049da534342..e8f028b62be 100644 --- a/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/negated_string.php.inc +++ b/rules-tests/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector/Fixture/negated_string.php.inc @@ -32,7 +32,7 @@ final class NegatedString public function run() { - if ($this->name !== '') { + if ($this->name !== '' && $this->name !== '0') { return 'name'; } diff --git a/rules-tests/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector/Fixture/string_silent_compare.php.inc b/rules-tests/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector/Fixture/string_silent_compare.php.inc index 90ceb3aabf2..c2179af871b 100644 --- a/rules-tests/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector/Fixture/string_silent_compare.php.inc +++ b/rules-tests/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector/Fixture/string_silent_compare.php.inc @@ -24,7 +24,7 @@ final class StringSilentCompare { public function run(string $string) { - return $string !== '' ? 1 : 2; + return $string !== '' && $string !== '0' ? 1 : 2; } } diff --git a/rules-tests/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector/Fixture/short_ternary_string.php.inc b/rules-tests/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector/Fixture/short_ternary_string.php.inc index 37522fc18d4..9570675964e 100644 --- a/rules-tests/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector/Fixture/short_ternary_string.php.inc +++ b/rules-tests/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector/Fixture/short_ternary_string.php.inc @@ -24,7 +24,7 @@ final class ShortTernaryString { public function run(string $string) { - return $string !== '' ? $string : 2; + return $string !== '' && $string !== '0' ? $string : 2; } } diff --git a/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php b/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php index 67df5df23ef..66baf8d3a90 100644 --- a/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php +++ b/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php @@ -4,6 +4,16 @@ namespace Rector\Php83\Rector\ClassConst; +use PhpParser\Node\Stmt\ClassConst; +use PhpParser\Node\Identifier; +use PhpParser\Node\Const_; +use PhpParser\Node\Expr; +use PhpParser\Node\Scalar\String_; +use PhpParser\Node\Scalar\LNumber; +use PhpParser\Node\Scalar\DNumber; +use PhpParser\Node\Expr\ConstFetch; +use PhpParser\Node\Expr\Array_; +use PhpParser\Node\Name; use PhpParser\Node; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; @@ -20,7 +30,7 @@ /** * @see \Rector\Tests\Php83\Rector\ClassConst\AddTypeToConstRector\AddTypeToConstRectorTest */ -class AddTypeToConstRector extends AbstractRector implements MinPhpVersionInterface +final class AddTypeToConstRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( private readonly ReflectionProvider $reflectionProvider, @@ -63,9 +73,7 @@ public function refactor(Node $node): Class_|null return null; } - $consts = array_filter($node->stmts, function (Node $stmt) { - return $stmt instanceof Node\Stmt\ClassConst; - }); + $consts = array_filter($node->stmts, static fn(Node $node): bool => $node instanceof ClassConst); if ($consts === []) { return null; @@ -91,13 +99,15 @@ public function refactor(Node $node): Class_|null if ($this->shouldSkipDueToInheritance($constNode, $parents, $implementations, $traits)) { continue; } + if ($this->canBeInheritied($const, $node)) { continue; } + $valueType = $this->findValueType($constNode->value); } - if (($valueType ?? null) === null) { + if (!($valueType ?? null) instanceof Identifier) { continue; } @@ -124,45 +134,52 @@ public function provideMinPhpVersion(): int * @param ClassReflection[] $traits */ public function shouldSkipDueToInheritance( - Node\Const_ $constNode, + Const_ $const, array $parents, array $implementations, array $traits, ): bool { foreach ([$parents, $implementations, $traits] as $inheritance) { foreach ($inheritance as $inheritanceItem) { - if ($constNode->name->name === '') { + if ($const->name->name === '') { continue; } + try { - $inheritanceItem->getConstant($constNode->name->name); + $inheritanceItem->getConstant($const->name->name); return true; } catch (MissingConstantFromReflectionException) { } } } + return false; } - private function findValueType(Node\Expr $value): ?Node\Identifier + private function findValueType(Expr $expr): ?Identifier { - if ($value instanceof Node\Scalar\String_) { - return new Node\Identifier('string'); + if ($expr instanceof String_) { + return new Identifier('string'); } - if ($value instanceof Node\Scalar\LNumber) { - return new Node\Identifier('int'); + + if ($expr instanceof LNumber) { + return new Identifier('int'); } - if ($value instanceof Node\Scalar\DNumber) { - return new Node\Identifier('float'); + + if ($expr instanceof DNumber) { + return new Identifier('float'); } - if ($value instanceof Node\Expr\ConstFetch && $value->name->toLowerString() !== 'null') { - return new Node\Identifier('bool'); + + if ($expr instanceof ConstFetch && $expr->name->toLowerString() !== 'null') { + return new Identifier('bool'); } - if ($value instanceof Node\Expr\ConstFetch && $value->name->toLowerString() === 'null') { - return new Node\Identifier('null'); + + if ($expr instanceof ConstFetch && $expr->name->toLowerString() === 'null') { + return new Identifier('null'); } - if ($value instanceof Node\Expr\Array_) { - return new Node\Identifier('array'); + + if ($expr instanceof Array_) { + return new Identifier('array'); } return null; @@ -175,7 +192,7 @@ private function getParents(Class_ $class): array { $parents = array_filter([$class->extends]); - return array_map(function (Node\Name $name): ClassReflection { + return array_map(function (Name $name): ClassReflection { if (! $name instanceof FullyQualified) { throw new FullyQualifiedNameNotAutoloadedException($name); } @@ -193,7 +210,7 @@ private function getParents(Class_ $class): array */ private function getImplementations(Class_ $class): array { - return array_map(function (Node\Name $name): ClassReflection { + return array_map(function (Name $name): ClassReflection { if (! $name instanceof FullyQualified) { throw new FullyQualifiedNameNotAutoloadedException($name); } @@ -209,14 +226,14 @@ private function getImplementations(Class_ $class): array /** * @return ClassReflection[] */ - private function getTraits(Class_ $node): array + private function getTraits(Class_ $class): array { $traits = []; - foreach ($node->getTraitUses() as $traitUse) { + foreach ($class->getTraitUses() as $traitUse) { $traits = [...$traits, ...$traitUse->traits]; } - return array_map(function (Node\Name $name): ClassReflection { + return array_map(function (Name $name): ClassReflection { if (! $name instanceof FullyQualified) { throw new FullyQualifiedNameNotAutoloadedException($name); } @@ -229,8 +246,8 @@ private function getTraits(Class_ $node): array }, $traits); } - private function canBeInheritied(Node\Stmt\ClassConst $constNode, Class_ $node): bool + private function canBeInheritied(ClassConst $classConst, Class_ $class): bool { - return ! $node->isFinal() && ! $constNode->isPrivate(); + return ! $class->isFinal() && ! $classConst->isPrivate(); } } diff --git a/rules/Strict/NodeFactory/ExactCompareFactory.php b/rules/Strict/NodeFactory/ExactCompareFactory.php index 896a660770d..b3a95185f80 100644 --- a/rules/Strict/NodeFactory/ExactCompareFactory.php +++ b/rules/Strict/NodeFactory/ExactCompareFactory.php @@ -32,10 +32,18 @@ public function __construct( public function createIdenticalFalsyCompare( Type $exprType, Expr $expr, - bool $treatAsNonEmpty + bool $treatAsNonEmpty, + bool $isOnlyString = true ): Identical|BooleanOr|NotIdentical|BooleanNot|Instanceof_|BooleanAnd|null { if ($exprType->isString()->yes()) { - return new Identical($expr, new String_('')); + if ($treatAsNonEmpty || ! $isOnlyString) { + return new Identical($expr, new String_('')); + } + + return new BooleanOr( + new Identical($expr, new String_('')), + new Identical($expr, new String_('0')) + ); } if ($exprType->isInteger()->yes()) { @@ -58,16 +66,24 @@ public function createIdenticalFalsyCompare( return null; } - return $this->createTruthyFromUnionType($exprType, $expr, $treatAsNonEmpty); + return $this->createTruthyFromUnionType($exprType, $expr, $treatAsNonEmpty, false); } public function createNotIdenticalFalsyCompare( Type $exprType, Expr $expr, - bool $treatAsNotEmpty + bool $treatAsNotEmpty, + bool $isOnlyString = true ): Identical|Instanceof_|BooleanOr|NotIdentical|BooleanAnd|BooleanNot|null { if ($exprType->isString()->yes()) { - return new NotIdentical($expr, new String_('')); + if ($treatAsNotEmpty || ! $isOnlyString) { + return new NotIdentical($expr, new String_('')); + } + + return new BooleanAnd( + new NotIdentical($expr, new String_('')), + new NotIdentical($expr, new String_('0')) + ); } if ($exprType->isInteger()->yes()) { @@ -82,13 +98,14 @@ public function createNotIdenticalFalsyCompare( return null; } - return $this->createFromUnionType($exprType, $expr, $treatAsNotEmpty); + return $this->createFromUnionType($exprType, $expr, $treatAsNotEmpty, false); } private function createFromUnionType( UnionType $unionType, Expr $expr, - bool $treatAsNotEmpty + bool $treatAsNotEmpty, + bool $isOnlyString ): Identical|Instanceof_|BooleanOr|NotIdentical|BooleanAnd|BooleanNot|null { $unionType = TypeCombinator::removeNull($unionType); @@ -107,7 +124,7 @@ private function createFromUnionType( return $this->resolveFromCleanedNullUnionType($unionType, $expr, $treatAsNotEmpty); } - $compareExpr = $this->createNotIdenticalFalsyCompare($unionType, $expr, $treatAsNotEmpty); + $compareExpr = $this->createNotIdenticalFalsyCompare($unionType, $expr, $treatAsNotEmpty, $isOnlyString); if (! $compareExpr instanceof Expr) { return null; } @@ -208,7 +225,8 @@ private function createBooleanAnd( private function createTruthyFromUnionType( UnionType $unionType, Expr $expr, - bool $treatAsNonEmpty + bool $treatAsNonEmpty, + bool $isOnlyString ): BooleanOr|NotIdentical|Identical|BooleanNot|Instanceof_|BooleanAnd|null { $unionType = $this->cleanUpPossibleNullableUnionType($unionType); @@ -232,7 +250,7 @@ private function createTruthyFromUnionType( } // assume we have to check empty string, integer and bools - $scalarFalsyIdentical = $this->createIdenticalFalsyCompare($unionType, $expr, $treatAsNonEmpty); + $scalarFalsyIdentical = $this->createIdenticalFalsyCompare($unionType, $expr, $treatAsNonEmpty, $isOnlyString); if (! $scalarFalsyIdentical instanceof Expr) { return null; } diff --git a/src/Exception/FullyQualifiedNameNotAutoloadedException.php b/src/Exception/FullyQualifiedNameNotAutoloadedException.php index 7adac11de3c..92d2f8b8339 100644 --- a/src/Exception/FullyQualifiedNameNotAutoloadedException.php +++ b/src/Exception/FullyQualifiedNameNotAutoloadedException.php @@ -7,7 +7,7 @@ use PhpParser\Node\Name; use RuntimeException; -class FullyQualifiedNameNotAutoloadedException extends RuntimeException +final class FullyQualifiedNameNotAutoloadedException extends RuntimeException { public function __construct( protected Name $name diff --git a/tests/Issues/EmptyBooleanCompare/Fixture/always_exists_array_dim_fetch_item.php.inc b/tests/Issues/EmptyBooleanCompare/Fixture/always_exists_array_dim_fetch_item.php.inc index 99f8ab6b4c8..a7d541215b8 100644 --- a/tests/Issues/EmptyBooleanCompare/Fixture/always_exists_array_dim_fetch_item.php.inc +++ b/tests/Issues/EmptyBooleanCompare/Fixture/always_exists_array_dim_fetch_item.php.inc @@ -44,7 +44,7 @@ final class AlwaysExistsArrayDimFetchItem ]; } - if (isset($parts['host']) && $parts['host'] !== '') { + if (isset($parts['host']) && ($parts['host'] !== '' && $parts['host'] !== '0')) { return $parts['host']; } diff --git a/tests/Issues/EmptyBooleanCompare/Fixture/fixture.php.inc b/tests/Issues/EmptyBooleanCompare/Fixture/fixture.php.inc index 2b219f21723..f9a32e47315 100644 --- a/tests/Issues/EmptyBooleanCompare/Fixture/fixture.php.inc +++ b/tests/Issues/EmptyBooleanCompare/Fixture/fixture.php.inc @@ -28,7 +28,7 @@ final class SomeFixture { $parts = parse_url($url); - if (isset($parts['host']) && $parts['host'] !== '') { + if (isset($parts['host']) && ($parts['host'] !== '' && $parts['host'] !== '0')) { return $parts['host']; } diff --git a/tests/Issues/EmptyBooleanCompare/Fixture/on_array_object.php.inc b/tests/Issues/EmptyBooleanCompare/Fixture/on_array_object.php.inc index 0a6eed5aad3..251a2121973 100644 --- a/tests/Issues/EmptyBooleanCompare/Fixture/on_array_object.php.inc +++ b/tests/Issues/EmptyBooleanCompare/Fixture/on_array_object.php.inc @@ -28,7 +28,7 @@ final class OnArrayObject { $parts = new \ArrayObject(['host' => 'test'], \ArrayObject::ARRAY_AS_PROPS); - if (isset($parts['host']) && $parts['host'] !== '') { + if (isset($parts['host']) && ($parts['host'] !== '' && $parts['host'] !== '0')) { return $parts['host']; }