diff --git a/composer.json b/composer.json index 08a5a24b312..c915e1a1143 100644 --- a/composer.json +++ b/composer.json @@ -36,6 +36,7 @@ "symfony/console": "^6.4.24", "symfony/filesystem": "^7.4", "symfony/finder": "^6.4", + "symfony/polyfill-php84": "^1.38", "symfony/process": "^7.4", "symplify/easy-parallel": "^11.2.2", "symplify/rule-doc-generator-contracts": "^11.2", diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/AddArrayAnyAllClosureParamTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/AddArrayAnyAllClosureParamTypeRectorTest.php new file mode 100644 index 00000000000..99ada7f2b16 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/AddArrayAnyAllClosureParamTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/array_all_closure.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/array_all_closure.php.inc new file mode 100644 index 00000000000..d5727072698 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/array_all_closure.php.inc @@ -0,0 +1,31 @@ + 0; + }); +} + +?> +----- + 0; + }); +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/array_any_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/array_any_arrow_function.php.inc new file mode 100644 index 00000000000..bd72d9dd258 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/array_any_arrow_function.php.inc @@ -0,0 +1,27 @@ + $item !== ''); +} + +?> +----- + $item !== ''); +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/skip_already_typed.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/skip_already_typed.php.inc new file mode 100644 index 00000000000..16159e4eee9 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/skip_already_typed.php.inc @@ -0,0 +1,11 @@ + $item !== ''); +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/skip_mixed_array.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/skip_mixed_array.php.inc new file mode 100644 index 00000000000..185e74aaf52 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/Fixture/skip_mixed_array.php.inc @@ -0,0 +1,8 @@ + $item !== ''); +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..9d73e7faf3f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(AddArrayAnyAllClosureParamTypeRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_all_closure.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_all_closure.php.inc new file mode 100644 index 00000000000..967ef60b56f --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_all_closure.php.inc @@ -0,0 +1,31 @@ + 0; + }); +} + +?> +----- + 0; + }); +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_any_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_any_arrow_function.php.inc new file mode 100644 index 00000000000..07dfe2834dd --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_any_arrow_function.php.inc @@ -0,0 +1,27 @@ + $item !== ''); +} + +?> +----- + $item !== ''); +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_find.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_find.php.inc new file mode 100644 index 00000000000..92e49d84363 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/array_find.php.inc @@ -0,0 +1,27 @@ + $item !== ''); +} + +?> +----- + $item !== ''); +} + +?> diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_already_non_nullable.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_already_non_nullable.php.inc new file mode 100644 index 00000000000..6b0b8bfbebf --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_already_non_nullable.php.inc @@ -0,0 +1,11 @@ + $item !== ''); +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_array_filter.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_array_filter.php.inc new file mode 100644 index 00000000000..525ae79ffd5 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_array_filter.php.inc @@ -0,0 +1,11 @@ + $item !== ''); +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_nullable_item_type.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_nullable_item_type.php.inc new file mode 100644 index 00000000000..7106f2a3ade --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/Fixture/skip_nullable_item_type.php.inc @@ -0,0 +1,11 @@ + $items */ + $items = ['a', null]; + + return array_any($items, fn (?string $item): bool => $item !== ''); +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/NarrowArrayAnyAllNullableParamTypeRectorTest.php b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/NarrowArrayAnyAllNullableParamTypeRectorTest.php new file mode 100644 index 00000000000..c29c16411b0 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/NarrowArrayAnyAllNullableParamTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/config/configured_rule.php b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/config/configured_rule.php new file mode 100644 index 00000000000..09855ac7b5d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector/config/configured_rule.php @@ -0,0 +1,13 @@ +rule(NarrowArrayAnyAllNullableParamTypeRector::class); + + $rectorConfig->phpVersion(PhpVersion::PHP_84); +}; diff --git a/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php b/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php index 8dd09b84655..0ce0dad3fd3 100644 --- a/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php +++ b/rules/CodeQuality/Rector/Catch_/ThrowWithPreviousExceptionRector.php @@ -14,6 +14,7 @@ use PhpParser\Node\Name; use PhpParser\Node\Stmt\Catch_; use PhpParser\NodeVisitor; +use PHPStan\Reflection\ExtendedParameterReflection; use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Type\ObjectType; @@ -233,25 +234,18 @@ private function hasParameter(New_ $new, string $parameterName): bool $extendedParametersAcceptor = ParametersAcceptorSelector::combineAcceptors( $extendedMethodReflection->getVariants() ); - - foreach ($extendedParametersAcceptor->getParameters() as $extendedParameterReflection) { - if ($extendedParameterReflection->getName() === $parameterName) { - return true; - } - } - - return false; + return array_any( + $extendedParametersAcceptor->getParameters(), + fn (ExtendedParameterReflection $extendedParameterReflection): bool => $extendedParameterReflection->getName() === $parameterName + ); } private function hasArgument(New_ $new, string $argumentName): bool { - foreach ($new->getArgs() as $arg) { - if ($arg->name instanceof Identifier && $arg->name->toString() === $argumentName) { - return true; - } - } - - return false; + return array_any( + $new->getArgs(), + fn (Arg $arg): bool => $arg->name instanceof Identifier && $arg->name->toString() === $argumentName + ); } private function resolveExceptionArgumentPosition(Name $exceptionName): ?int diff --git a/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php b/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php index d287ff519fe..309ea2428f2 100644 --- a/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php +++ b/rules/CodeQuality/Rector/FuncCall/ChangeArrayPushToArrayAssignRector.php @@ -5,6 +5,7 @@ namespace Rector\CodeQuality\Rector\FuncCall; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\FuncCall; @@ -100,12 +101,6 @@ public function refactor(Node $node): ?array private function hasArraySpread(FuncCall $funcCall): bool { - foreach ($funcCall->getArgs() as $arg) { - if ($arg->unpack) { - return true; - } - } - - return false; + return array_any($funcCall->getArgs(), fn (Arg $arg): bool => $arg->unpack); } } diff --git a/rules/CodingStyle/ClassNameImport/ValueObject/PendingImports.php b/rules/CodingStyle/ClassNameImport/ValueObject/PendingImports.php index d76178b5a7a..4920eeeb450 100644 --- a/rules/CodingStyle/ClassNameImport/ValueObject/PendingImports.php +++ b/rules/CodingStyle/ClassNameImport/ValueObject/PendingImports.php @@ -4,6 +4,7 @@ namespace Rector\CodingStyle\ClassNameImport\ValueObject; +use PHPStan\Type\Type; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; /** @@ -98,13 +99,12 @@ public function isShortImported(FullyQualifiedObjectType $fullyQualifiedObjectTy } } - foreach ($this->functionImports as $functionImport) { - if (strtolower($functionImport->getShortName()) === $shortName) { - return true; - } - } - - return false; + return array_any( + $this->functionImports, + fn (FullyQualifiedObjectType $fullyQualifiedObjectType): bool => strtolower( + $fullyQualifiedObjectType->getShortName() + ) === $shortName + ); } public function isImportShortable(FullyQualifiedObjectType $fullyQualifiedObjectType): bool @@ -121,12 +121,6 @@ public function isImportShortable(FullyQualifiedObjectType $fullyQualifiedObject } } - foreach ($this->functionImports as $functionImport) { - if ($fullyQualifiedObjectType->equals($functionImport)) { - return true; - } - } - - return false; + return array_any($this->functionImports, fn (Type $type): bool => $fullyQualifiedObjectType->equals($type)); } } diff --git a/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php b/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php index 135169ce83c..9cea1b90e14 100644 --- a/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php +++ b/rules/CodingStyle/Guard/ArrowFunctionAndClosureFirstClassCallableGuard.php @@ -189,13 +189,7 @@ private function isUsingThisInNonObjectContext(FuncCall|MethodCall|StaticCall $c */ private function isUsingByRef(array $params): bool { - foreach ($params as $param) { - if ($param->byRef) { - return true; - } - } - - return false; + return array_any($params, fn (Param $param): bool => $param->byRef); } /** @@ -203,13 +197,7 @@ private function isUsingByRef(array $params): bool */ private function isUsingNamedArgs(array $args): bool { - foreach ($args as $arg) { - if ($arg->name instanceof Identifier) { - return true; - } - } - - return false; + return array_any($args, fn (Arg $arg): bool => $arg->name instanceof Identifier); } private function isChainedCall(FuncCall|MethodCall|StaticCall $callLike): bool diff --git a/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php b/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php index d46621085c0..5bc416d7f26 100644 --- a/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php +++ b/rules/DeadCode/NodeAnalyzer/NoDiscardCallAnalyzer.php @@ -85,12 +85,9 @@ public function isNoDiscardCall(Expr $expr): bool */ private function hasNoDiscardAttribute(array $attributes): bool { - foreach ($attributes as $attribute) { - if ($attribute->getName() === 'NoDiscard') { - return true; - } - } - - return false; + return array_any( + $attributes, + fn (AttributeReflection $attributeReflection): bool => $attributeReflection->getName() === 'NoDiscard' + ); } } diff --git a/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php b/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php index 8ed117cd107..ddba64e1666 100644 --- a/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php +++ b/rules/DeadCode/PhpDoc/Guard/TemplateTypeRemovalGuard.php @@ -21,13 +21,6 @@ public function isLegal(Type $docType): bool $types = $docType instanceof UnionType ? $docType->getTypes() : [$docType]; - - foreach ($types as $type) { - if ($type instanceof TemplateType) { - return false; - } - } - - return true; + return array_all($types, fn (Type $type): bool => ! $type instanceof TemplateType); } } diff --git a/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php b/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php index 8a5ea5448f9..74758264ba1 100644 --- a/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php +++ b/rules/DeadCode/Rector/ClassConst/RemoveUnusedPrivateClassConstantRector.php @@ -104,12 +104,9 @@ private function shouldSkipClassConst(ClassConst $classConst): bool private function hasParentClassOfEnumSuffix(ClassReflection $classReflection): bool { - foreach ($classReflection->getParentClassesNames() as $parentClassesName) { - if (str_ends_with($parentClassesName, 'Enum')) { - return true; - } - } - - return false; + return array_any( + $classReflection->getParentClassesNames(), + fn (string $parentClassesName): bool => str_ends_with($parentClassesName, 'Enum') + ); } } diff --git a/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php b/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php index d9659ee557c..3bec4b1de46 100644 --- a/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php +++ b/rules/DeadCode/Rector/ClassLike/RemoveTypedPropertyNonMockDocblockRector.php @@ -11,6 +11,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; use PHPStan\Type\ObjectType; +use PHPStan\Type\Type; use PHPStan\Type\UnionType; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; @@ -142,12 +143,9 @@ private function isVarTagUnionTypeMockObject(PhpDocInfo $phpDocInfo, Property $p return false; } - foreach ($varTagType->getTypes() as $unionedType) { - if ($unionedType->isSuperTypeOf(new ObjectType(ClassName::MOCK_OBJECT))->yes()) { - return true; - } - } - - return false; + return array_any( + $varTagType->getTypes(), + fn (Type $unionedType): bool => $unionedType->isSuperTypeOf(new ObjectType(ClassName::MOCK_OBJECT))->yes() + ); } } diff --git a/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php b/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php index 2ecc8ddd341..a7a11b1a215 100644 --- a/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php +++ b/rules/DeadCode/Rector/FunctionLike/NarrowWideUnionReturnTypeRector.php @@ -5,6 +5,7 @@ namespace Rector\DeadCode\Rector\FunctionLike; use PhpParser\Node; +use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\ConstFetch; @@ -238,13 +239,7 @@ private function shouldSkipNode(ClassMethod|Function_|Closure|ArrowFunction $nod */ private function hasImplicitNullReturn(array $returnStatements): bool { - foreach ($returnStatements as $returnStatement) { - if ($returnStatement->expr === null) { - return true; - } - } - - return false; + return array_any($returnStatements, fn (Return_ $return): bool => ! $return->expr instanceof Expr); } /** diff --git a/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php b/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php index 4475f3889e0..4ad533970b7 100644 --- a/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php +++ b/rules/DeadCode/Rector/Switch_/RemoveDuplicatedCaseInSwitchRector.php @@ -5,6 +5,7 @@ namespace Rector\DeadCode\Rector\Switch_; use PhpParser\Node; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Return_; @@ -180,13 +181,10 @@ private function areSwitchStmtsEqualsAndWithBreak(Case_ $currentCase, Case_ $nex return false; } - foreach ($currentCase->stmts as $stmt) { - if ($stmt instanceof Break_ || $stmt instanceof Return_) { - return true; - } - } - - return false; + return array_any( + $currentCase->stmts, + fn (Stmt $stmt): bool => $stmt instanceof Break_ || $stmt instanceof Return_ + ); } private function areSwitchStmtsEqualsConsideringComments(Case_ $currentCase, Case_ $nextCase): bool diff --git a/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php b/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php index 74e797ecdb9..c1b2e331449 100644 --- a/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php +++ b/rules/DeadCode/TypeNodeAnalyzer/GenericTypeNodeAnalyzer.php @@ -5,6 +5,7 @@ namespace Rector\DeadCode\TypeNodeAnalyzer; use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode; +use PHPStan\PhpDocParser\Ast\Type\TypeNode; use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode; final class GenericTypeNodeAnalyzer @@ -12,13 +13,6 @@ final class GenericTypeNodeAnalyzer public function hasGenericType(BracketsAwareUnionTypeNode $bracketsAwareUnionTypeNode): bool { $types = $bracketsAwareUnionTypeNode->types; - - foreach ($types as $type) { - if ($type instanceof GenericTypeNode) { - return true; - } - } - - return false; + return array_any($types, fn (TypeNode $typeNode): bool => $typeNode instanceof GenericTypeNode); } } diff --git a/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php b/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php index 8f75aab7990..fe1de1acf25 100644 --- a/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php +++ b/rules/EarlyReturn/Rector/If_/RemoveAlwaysElseRector.php @@ -176,13 +176,10 @@ private function doesNotLastStatementBreakFlow(If_ | ElseIf_ | Else_ $node): boo return true; } - foreach ($lastStmt->elseifs as $elseIf) { - if ($this->doesNotLastStatementBreakFlow($elseIf)) { - return true; - } - } - - return false; + return array_any( + $lastStmt->elseifs, + fn (If_|ElseIf_|Else_ $elseIf): bool => $this->doesNotLastStatementBreakFlow($elseIf) + ); } return ! ($lastStmt instanceof Return_ diff --git a/rules/Naming/VariableRenamer.php b/rules/Naming/VariableRenamer.php index 18c97291cb7..43c38939083 100644 --- a/rules/Naming/VariableRenamer.php +++ b/rules/Naming/VariableRenamer.php @@ -125,13 +125,10 @@ private function isParamInParentFunction(Variable $variable, ?FunctionLike $func return false; } - foreach ($functionLike->getParams() as $param) { - if ($this->nodeNameResolver->isName($param, $variableName)) { - return true; - } - } - - return false; + return array_any( + $functionLike->getParams(), + fn (Node|array $param): bool => $this->nodeNameResolver->isName($param, $variableName) + ); } private function renameVariableIfMatchesName( diff --git a/rules/Php71/Rector/List_/ListToArrayDestructRector.php b/rules/Php71/Rector/List_/ListToArrayDestructRector.php index eaafa1ea313..4dbdae1ee32 100644 --- a/rules/Php71/Rector/List_/ListToArrayDestructRector.php +++ b/rules/Php71/Rector/List_/ListToArrayDestructRector.php @@ -115,12 +115,6 @@ public function provideMinPhpVersion(): int private function hasPartialDestruct(List_ $list): bool { - foreach ($list->items as $listItem) { - if (! $listItem instanceof ArrayItem) { - return true; - } - } - - return false; + return array_any($list->items, fn (?ArrayItem $arrayItem): bool => ! $arrayItem instanceof ArrayItem); } } diff --git a/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php b/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php index b4c1021cdb8..1cf29e52be8 100644 --- a/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php +++ b/rules/Php74/NodeAnalyzer/ClosureArrowFunctionAnalyzer.php @@ -76,13 +76,10 @@ function (Node $node) use ($variables): bool { return false; } - foreach ($variables as $variable) { - if ($this->compactFuncCallAnalyzer->isInCompact($node, $variable)) { - return true; - } - } - - return false; + return array_any( + $variables, + fn (Variable $variable): bool => $this->compactFuncCallAnalyzer->isInCompact($node, $variable) + ); } ); } @@ -111,17 +108,16 @@ private function shouldSkipForUsedReferencedValue(Closure $closure): bool return false; } - $isFoundInStmt = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped($closure, function (Node $node) use ( - $referencedValues - ): bool { - foreach ($referencedValues as $referencedValue) { - if ($this->nodeComparator->areNodesEqual($node, $referencedValue)) { - return true; - } - } - - return false; - }); + $isFoundInStmt = (bool) $this->betterNodeFinder->findFirstInFunctionLikeScoped( + $closure, + fn (Node $node): bool => array_any( + $referencedValues, + fn (Node|array|null $referencedValue): bool => $this->nodeComparator->areNodesEqual( + $node, + $referencedValue + ) + ) + ); if ($isFoundInStmt) { return true; diff --git a/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php b/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php index 3f84b83fd5c..563ff2afe82 100644 --- a/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php +++ b/rules/Php80/NodeAnalyzer/MatchSwitchAnalyzer.php @@ -35,13 +35,10 @@ public function __construct( */ public function isReturnCondsAndExprs(array $condAndExprs): bool { - foreach ($condAndExprs as $condAndExpr) { - if ($condAndExpr->equalsMatchKind(MatchKind::RETURN)) { - return true; - } - } - - return false; + return array_any( + $condAndExprs, + fn (CondAndExpr $condAndExpr): bool => $condAndExpr->equalsMatchKind(MatchKind::RETURN) + ); } /** @@ -110,13 +107,7 @@ public function haveCondAndExprsMatchPotential(array $condAndExprs): bool */ public function hasCondsAndExprDefaultValue(array $condAndExprs): bool { - foreach ($condAndExprs as $condAndExpr) { - if ($condAndExpr->getCondExprs() === null) { - return true; - } - } - - return false; + return array_any($condAndExprs, fn (CondAndExpr $condAndExpr): bool => $condAndExpr->getCondExprs() === null); } public function hasDefaultValue(Match_ $match): bool diff --git a/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php b/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php index db7ed01771d..53d35c91325 100644 --- a/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php +++ b/rules/Php80/NodeAnalyzer/SwitchAnalyzer.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Break_; use PhpParser\Node\Stmt\Case_; use PhpParser\Node\Stmt\Return_; @@ -126,12 +127,6 @@ private function hasBreakOrReturnOrEmpty(Case_ $case): bool private function containsCaseReturn(Case_ $case): bool { - foreach ($case->stmts as $stmt) { - if ($stmt instanceof Return_) { - return true; - } - } - - return false; + return array_any($case->stmts, fn (Stmt $stmt): bool => $stmt instanceof Return_); } } diff --git a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php index deabb618384..cd2038334ea 100644 --- a/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php +++ b/rules/Php80/Rector/Class_/ClassPropertyAssignToConstructorPromotionRector.php @@ -10,6 +10,8 @@ use PhpParser\Node\Expr\Variable; use PhpParser\Node\FunctionLike; use PhpParser\Node\Identifier; +use PhpParser\Node\IntersectionType; +use PhpParser\Node\Name; use PhpParser\Node\NullableType; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; @@ -353,18 +355,19 @@ private function shouldSkipParam(Param $param): bool return false; } - foreach ($type->types as $type) { - if ($this->isCallableTypeIdentifier($type)) { - return true; - } - } - - return false; + return array_any( + $type->types, + fn (Identifier|IntersectionType|Name $type): bool => $this->isCallableTypeIdentifier($type) + ); } private function isCallableTypeIdentifier(?Node $node): bool { - return $node instanceof Identifier && $this->isName($node, 'callable'); + if (! $node instanceof Identifier) { + return false; + } + + return $this->isName($node, 'callable'); } private function shouldSkipPropertyOrParam(Property $property, Param $param): bool diff --git a/rules/Php80/ValueObject/NestedAnnotationToAttribute.php b/rules/Php80/ValueObject/NestedAnnotationToAttribute.php index 1b2be75a3d8..7ab86e4112c 100644 --- a/rules/Php80/ValueObject/NestedAnnotationToAttribute.php +++ b/rules/Php80/ValueObject/NestedAnnotationToAttribute.php @@ -62,12 +62,11 @@ public function shouldRemoveOriginal(): bool public function hasExplicitParameters(): bool { - foreach ($this->annotationPropertiesToAttributeClasses as $annotationPropertyToAttributeClass) { - if (is_string($annotationPropertyToAttributeClass->getAnnotationProperty())) { - return true; - } - } - - return false; + return array_any( + $this->annotationPropertiesToAttributeClasses, + fn (AnnotationPropertyToAttributeClass $annotationPropertyToAttributeClass): bool => is_string( + $annotationPropertyToAttributeClass->getAnnotationProperty() + ) + ); } } diff --git a/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php b/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php index a24ee847841..1fa9153bcbe 100644 --- a/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php +++ b/rules/Php82/NodeManipulator/ReadonlyClassManipulator.php @@ -4,6 +4,7 @@ namespace Rector\Php82\NodeManipulator; +use PhpParser\Node; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Class_; @@ -71,14 +72,7 @@ private function resolveParentClassReflections(Scope $scope): array */ private function hasNonTypedProperty(array $properties): bool { - foreach ($properties as $property) { - // properties of readonly class must always have type - if ($property->type === null) { - return true; - } - } - - return false; + return array_any($properties, fn (Property $property): bool => ! $property->type instanceof Node); } private function shouldSkip(Class_ $class, Scope $scope): bool @@ -161,13 +155,10 @@ private function shouldSkipConsumeTraitProperty(Class_ $class): bool */ private function hasReadonlyProperty(array $properties): bool { - foreach ($properties as $property) { - if (! $property->isReadOnly()) { - return true; - } - } - - return false; + return array_any( + $properties, + fn (ReflectionProperty $reflectionProperty): bool => ! $reflectionProperty->isReadOnly() + ); } /** @@ -175,13 +166,7 @@ private function hasReadonlyProperty(array $properties): bool */ private function isExtendsReadonlyClass(array $parents): bool { - foreach ($parents as $parent) { - if ($parent->isReadOnly()) { - return true; - } - } - - return false; + return array_any($parents, fn (ClassReflection $classReflection): bool => $classReflection->isReadOnly()); } /** @@ -189,13 +174,7 @@ private function isExtendsReadonlyClass(array $parents): bool */ private function hasWritableProperty(array $properties): bool { - foreach ($properties as $property) { - if (! $property->isReadonly()) { - return true; - } - } - - return false; + return array_any($properties, fn (Property $property): bool => ! $property->isReadonly()); } private function shouldSkipClass(Class_ $class): bool diff --git a/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php b/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php index d6157bcdbd7..c029df214fe 100644 --- a/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php +++ b/rules/Php83/Rector/ClassConst/AddTypeToConstRector.php @@ -161,14 +161,10 @@ public function provideMinPhpVersion(): int public function isConstGuardedByParents(Const_ $const, array $parentClassReflections): bool { $constantName = $this->getName($const); - - foreach ($parentClassReflections as $parentClassReflection) { - if ($parentClassReflection->hasConstant($constantName)) { - return true; - } - } - - return false; + return array_any( + $parentClassReflections, + fn (ClassReflection $parentClassReflection): bool => $parentClassReflection->hasConstant($constantName) + ); } private function findValueType(Expr $expr): ?Identifier diff --git a/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php b/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php index 64b8985a157..786244d45c5 100644 --- a/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php +++ b/rules/Php84/Rector/FuncCall/AddEscapeArgumentRector.php @@ -108,13 +108,9 @@ public function provideMinPhpVersion(): int private function shouldSkipNamedArg(FuncCall|MethodCall $node): bool { - foreach ($node->getArgs() as $arg) { - // already defined in named arg - if ($arg->name instanceof Identifier && $arg->name->toString() === 'escape') { - return true; - } - } - - return false; + return array_any( + $node->getArgs(), + fn (Arg $arg): bool => $arg->name instanceof Identifier && $arg->name->toString() === 'escape' + ); } } diff --git a/rules/Privatization/Guard/OverrideByParentClassGuard.php b/rules/Privatization/Guard/OverrideByParentClassGuard.php index 7ac82b14120..8bb42b00f3e 100644 --- a/rules/Privatization/Guard/OverrideByParentClassGuard.php +++ b/rules/Privatization/Guard/OverrideByParentClassGuard.php @@ -4,6 +4,7 @@ namespace Rector\Privatization\Guard; +use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\Class_; use PHPStan\Reflection\ReflectionProvider; @@ -26,12 +27,9 @@ public function isLegal(Class_ $class): bool return false; } - foreach ($class->implements as $implement) { - if (! $this->reflectionProvider->hasClass($implement->toString())) { - return false; - } - } - - return true; + return array_all( + $class->implements, + fn (Name $name): bool => $this->reflectionProvider->hasClass($name->toString()) + ); } } diff --git a/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php b/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php index ee85d5d1b2f..b699e7e9bbd 100644 --- a/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php +++ b/rules/Privatization/VisibilityGuard/ClassMethodVisibilityGuard.php @@ -40,14 +40,10 @@ public function isClassMethodVisibilityGuardedByTrait( $parentTraitReflections = $this->getLocalAndParentTraitReflections($classReflection); $methodName = $this->nodeNameResolver->getName($classMethod); - - foreach ($parentTraitReflections as $parentTraitReflection) { - if ($parentTraitReflection->hasMethod($methodName)) { - return true; - } - } - - return false; + return array_any( + $parentTraitReflections, + fn (ClassReflection $classReflection): bool => $classReflection->hasMethod($methodName) + ); } /** diff --git a/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php index 567b14895f6..e81297e81d5 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/BoolReturnTypeFromBooleanStrictReturnsRector.php @@ -15,6 +15,7 @@ use PhpParser\Node\Stmt\Function_; use PhpParser\Node\Stmt\Return_; use PHPStan\Analyser\Scope; +use PHPStan\Reflection\ExtendedParametersAcceptor; use PHPStan\Reflection\ReflectionProvider; use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\PhpParser\Node\BetterNodeFinder; @@ -170,13 +171,12 @@ private function isNativeBooleanReturnTypeFuncCall(FuncCall $funcCall): bool return false; } - foreach ($functionReflection->getVariants() as $extendedParametersAcceptor) { - if (! $extendedParametersAcceptor->getNativeReturnType()->isBoolean()->yes()) { - return false; - } - } - - return true; + return array_all( + $functionReflection->getVariants(), + fn (ExtendedParametersAcceptor $extendedParametersAcceptor): bool => $extendedParametersAcceptor->getNativeReturnType() + ->isBoolean() + ->yes() + ); } /** diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php index 73db69cfc6c..aa8da3d326a 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php @@ -236,12 +236,6 @@ private function refactorFunctionLike( private function hasAtLeastOneParamWithoutType(ClassMethod|Function_|Closure|ArrowFunction $functionLike): bool { - foreach ($functionLike->params as $param) { - if (! $param->type instanceof Node) { - return true; - } - } - - return false; + return array_any($functionLike->params, fn (Param $param): bool => ! $param->type instanceof Node); } } diff --git a/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php b/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php index 9f48e7fdffa..27f12028eee 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php @@ -5,6 +5,7 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Arg; use PhpParser\Node\Expr; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\Assign; @@ -199,13 +200,10 @@ private function isEchoed(Node $node, string $paramName): bool return false; } - foreach ($node->exprs as $expr) { - if ($expr instanceof Variable && $this->isName($expr, $paramName)) { - return true; - } - } - - return false; + return array_any( + $node->exprs, + fn (Expr $expr): bool => $expr instanceof Variable && $this->isName($expr, $paramName) + ); } private function shouldStop(Node $node, string $paramName): bool @@ -267,13 +265,10 @@ private function isReassignAndUseAsArg(Node $node, string $paramName): bool return false; } - foreach ($node->expr->getArgs() as $arg) { - if ($arg->value instanceof Variable && $this->isName($arg->value, $paramName)) { - return true; - } - } - - return false; + return array_any( + $node->expr->getArgs(), + fn (Arg $arg): bool => $arg->value instanceof Variable && $this->isName($arg->value, $paramName) + ); } private function isEmptyOrEchoedOrCasted(Node $node, string $paramName): bool diff --git a/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php b/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php index dfad709348b..18439a43a02 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/StringReturnTypeFromStrictStringReturnsRector.php @@ -132,14 +132,10 @@ private function shouldSkipClassMethodForOverride(ClassMethod|Function_ $functio */ private function hasAlwaysStringScalarReturn(array $returns): bool { - foreach ($returns as $return) { - // we need exact string "value" return - if (! $return->expr instanceof String_ && ! $return->expr instanceof InterpolatedString) { - return false; - } - } - - return true; + return array_all( + $returns, + fn (Return_ $return): bool => ! (! $return->expr instanceof String_ && ! $return->expr instanceof InterpolatedString) + ); } /** diff --git a/rules/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector.php b/rules/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector.php new file mode 100644 index 00000000000..3992ba9841b --- /dev/null +++ b/rules/TypeDeclaration/Rector/FuncCall/AddArrayAnyAllClosureParamTypeRector.php @@ -0,0 +1,150 @@ + $item !== ''); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +/** @var string[] $items */ +array_any($items, fn (string $item): bool => $item !== ''); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if (! $this->isNames($node, self::FUNCTION_NAMES)) { + return null; + } + + $arrayArg = $node->getArg('array', 0); + $callbackArg = $node->getArg('callback', 1); + if (! $arrayArg instanceof Arg || ! $callbackArg instanceof Arg) { + return null; + } + + $callbackExpr = $callbackArg->value; + if (! $callbackExpr instanceof ArrowFunction && ! $callbackExpr instanceof Closure) { + return null; + } + + $valueParam = $callbackExpr->getParams()[0] ?? null; + if (! $valueParam instanceof Param) { + return null; + } + + // only fill in a param that has no type yet + if ($valueParam->type instanceof Node) { + return null; + } + + $itemType = $this->resolveArrayItemType($this->getType($arrayArg->value)); + if (! $itemType instanceof Type) { + return null; + } + + if ($itemType instanceof MixedType) { + return null; + } + + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($itemType, TypeKind::PARAM); + if (! $paramTypeNode instanceof Node) { + return null; + } + + $valueParam->type = $paramTypeNode; + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_ANY; + } + + private function resolveArrayItemType(Type $arrayType): ?Type + { + if ($arrayType instanceof ConstantArrayType || $arrayType instanceof ArrayType) { + return $arrayType->getItemType(); + } + + if ($arrayType instanceof IntersectionType) { + foreach ($arrayType->getTypes() as $subType) { + if ($subType instanceof AccessoryArrayListType) { + continue; + } + + if (! $subType instanceof ArrayType) { + continue; + } + + return $subType->getItemType(); + } + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector.php b/rules/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector.php new file mode 100644 index 00000000000..11503232b7e --- /dev/null +++ b/rules/TypeDeclaration/Rector/FuncCall/NarrowArrayAnyAllNullableParamTypeRector.php @@ -0,0 +1,152 @@ + $item !== ''); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +/** @var string[] $items */ +array_any($items, fn (string $item): bool => $item !== ''); +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [FuncCall::class]; + } + + /** + * @param FuncCall $node + */ + public function refactor(Node $node): ?Node + { + if ($node->isFirstClassCallable()) { + return null; + } + + if (! $this->isNames($node, self::FUNCTION_NAMES)) { + return null; + } + + $arrayArg = $node->getArg('array', 0); + $callbackArg = $node->getArg('callback', 1); + if (! $arrayArg instanceof Arg || ! $callbackArg instanceof Arg) { + return null; + } + + $callbackExpr = $callbackArg->value; + if (! $callbackExpr instanceof ArrowFunction && ! $callbackExpr instanceof Closure) { + return null; + } + + $valueParam = $callbackExpr->getParams()[0] ?? null; + if (! $valueParam instanceof Param) { + return null; + } + + // only narrow a param that currently allows null + if (! $valueParam->type instanceof NullableType) { + return null; + } + + $itemType = $this->resolveArrayItemType($this->getType($arrayArg->value)); + if (! $itemType instanceof Type) { + return null; + } + + // nothing to narrow when the item type is itself nullable or unknown + if ($itemType instanceof MixedType || ! $itemType->isNull()->no()) { + return null; + } + + $paramTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($itemType, TypeKind::PARAM); + if (! $paramTypeNode instanceof Node) { + return null; + } + + $valueParam->type = $paramTypeNode; + + return $node; + } + + public function provideMinPhpVersion(): int + { + return PhpVersionFeature::ARRAY_ANY; + } + + private function resolveArrayItemType(Type $arrayType): ?Type + { + if ($arrayType instanceof ConstantArrayType || $arrayType instanceof ArrayType) { + return $arrayType->getItemType(); + } + + if ($arrayType instanceof IntersectionType) { + foreach ($arrayType->getTypes() as $subType) { + if ($subType instanceof AccessoryArrayListType) { + continue; + } + + if (! $subType instanceof ArrayType) { + continue; + } + + return $subType->getItemType(); + } + } + + return null; + } +} diff --git a/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php index 6f54f3fe03d..f014d56a3ae 100644 --- a/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php +++ b/rules/TypeDeclaration/Rector/FunctionLike/AddClosureParamTypeFromIterableMethodCallRector.php @@ -257,12 +257,9 @@ private function methodSignatureUsesCallableWithIteratorTypes(string $className, */ private function callUsesClosures(array $args): bool { - foreach ($args as $arg) { - if ($arg instanceof Arg && $arg->value instanceof Closure) { - return true; - } - } - - return false; + return array_any( + $args, + fn (Arg|VariadicPlaceholder $arg): bool => $arg instanceof Arg && $arg->value instanceof Closure + ); } } diff --git a/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php b/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php index 16305eeb9be..72d8f123bda 100644 --- a/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php +++ b/rules/TypeDeclaration/TypeAnalyzer/GenericClassStringTypeNormalizer.php @@ -5,18 +5,13 @@ namespace Rector\TypeDeclaration\TypeAnalyzer; use PHPStan\Type\Generic\GenericClassStringType; +use PHPStan\Type\Type; use PHPStan\Type\UnionType; final readonly class GenericClassStringTypeNormalizer { public function isAllGenericClassStringType(UnionType $unionType): bool { - foreach ($unionType->getTypes() as $type) { - if (! $type instanceof GenericClassStringType) { - return false; - } - } - - return true; + return array_all($unionType->getTypes(), fn (Type $type): bool => $type instanceof GenericClassStringType); } } diff --git a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php index 3698f2bb697..d2cbf4d2cd5 100644 --- a/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/ClassMethod/AddReturnDocblockForDimFetchArrayFromAssignsRector.php @@ -206,13 +206,10 @@ private function isVariableInstantiated(ClassMethod $classMethod, string $return private function isConstantArrayType(Type $returnedExprType): bool { if ($returnedExprType instanceof UnionType) { - foreach ($returnedExprType->getTypes() as $unionedType) { - if (! $unionedType instanceof ConstantArrayType) { - return false; - } - } - - return true; + return array_all( + $returnedExprType->getTypes(), + fn (Type $unionedType): bool => $unionedType instanceof ConstantArrayType + ); } return $returnedExprType instanceof ConstantArrayType; diff --git a/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php b/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php index 2c7efa4d820..a89a42de16b 100644 --- a/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php +++ b/src/BetterPhpDocParser/Guard/NewPhpDocFromPHPStanTypeGuard.php @@ -21,12 +21,6 @@ public function isLegal(Type $type): bool private function isLegalUnionType(UnionType $type): bool { - foreach ($type->getTypes() as $unionType) { - if ($unionType instanceof MixedType) { - return false; - } - } - - return true; + return array_all($type->getTypes(), fn (Type $unionType): bool => ! $unionType instanceof MixedType); } } diff --git a/src/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php b/src/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php index cdc528c66ce..e556cf17118 100644 --- a/src/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php +++ b/src/BetterPhpDocParser/Printer/PhpDocInfoPrinter.php @@ -187,13 +187,10 @@ private function printPhpDocNode(PhpDocNode $phpDocNode): string private function hasDocblockStart(string $output): bool { - foreach (self::DOCBLOCK_STARTS as $docblockStart) { - if (str_starts_with($output, $docblockStart)) { - return true; - } - } - - return false; + return array_any( + self::DOCBLOCK_STARTS, + fn (string $docblockStart): bool => str_starts_with($output, $docblockStart) + ); } private function printDocChildNode( diff --git a/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php b/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php index 55c25bafd35..54d37f3ea2c 100644 --- a/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php +++ b/src/BetterPhpDocParser/ValueObject/Parser/BetterTokenIterator.php @@ -26,13 +26,7 @@ public function __construct(array $tokens, int $index = 0) */ public function isNextTokenTypes(array $types): bool { - foreach ($types as $type) { - if ($this->isNextTokenType($type)) { - return true; - } - } - - return false; + return array_any($types, fn (int $type): bool => $this->isNextTokenType($type)); } public function isTokenTypeOnPosition(int $tokenType, int $position): bool @@ -101,13 +95,7 @@ public function partialTokens(int $start, int $end): array public function containsTokenType(int $type): bool { - foreach ($this->getTokens() as $token) { - if ($token[1] === $type) { - return true; - } - } - - return false; + return array_any($this->getTokens(), fn (array $token): bool => $token[1] === $type); } private function nextTokenType(): ?int diff --git a/src/Config/Level/TypeDeclarationLevel.php b/src/Config/Level/TypeDeclarationLevel.php index 630af35b4a2..5923e119d4d 100644 --- a/src/Config/Level/TypeDeclarationLevel.php +++ b/src/Config/Level/TypeDeclarationLevel.php @@ -57,8 +57,10 @@ use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector; use Rector\TypeDeclaration\Rector\Closure\ClosureReturnTypeRector; use Rector\TypeDeclaration\Rector\Empty_\EmptyOnNullableObjectToInstanceOfRector; +use Rector\TypeDeclaration\Rector\FuncCall\AddArrayAnyAllClosureParamTypeRector; use Rector\TypeDeclaration\Rector\FuncCall\AddArrayFunctionClosureParamTypeRector; use Rector\TypeDeclaration\Rector\FuncCall\AddArrowFunctionParamArrayWhereDimFetchRector; +use Rector\TypeDeclaration\Rector\FuncCall\NarrowArrayAnyAllNullableParamTypeRector; use Rector\TypeDeclaration\Rector\Function_\AddFunctionVoidReturnTypeWhereNoReturnRector; use Rector\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayMapRector; use Rector\TypeDeclaration\Rector\FunctionLike\AddClosureParamTypeForArrayReduceRector; @@ -171,5 +173,8 @@ final class TypeDeclarationLevel TypedPropertyFromDocblockSetUpDefinedRector::class, AddClosureParamTypeFromIterableMethodCallRector::class, TypedStaticPropertyInBehatContextRector::class, + // PHP 8.4 + NarrowArrayAnyAllNullableParamTypeRector::class, + AddArrayAnyAllClosureParamTypeRector::class, ]; } diff --git a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php index 8255677d41e..e882449fa7e 100644 --- a/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php +++ b/src/FamilyTree/NodeAnalyzer/ClassChildAnalyzer.php @@ -27,13 +27,7 @@ public function hasAbstractParentClassMethod(ClassReflection $classReflection, s return false; } - foreach ($parentClassMethods as $parentClassMethod) { - if ($parentClassMethod->isAbstract()) { - return true; - } - } - - return false; + return array_any($parentClassMethods, fn ($parentClassMethod) => $parentClassMethod->isAbstract()); } /** diff --git a/src/NodeAnalyzer/ArgsAnalyzer.php b/src/NodeAnalyzer/ArgsAnalyzer.php index d5ff196b867..d8dfafab50a 100644 --- a/src/NodeAnalyzer/ArgsAnalyzer.php +++ b/src/NodeAnalyzer/ArgsAnalyzer.php @@ -20,13 +20,7 @@ public function __construct( */ public function hasNamedArg(array $args): bool { - foreach ($args as $arg) { - if ($arg->name instanceof Identifier) { - return true; - } - } - - return false; + return array_any($args, fn (Arg $arg): bool => $arg->name instanceof Identifier); } /** diff --git a/src/NodeAnalyzer/CallAnalyzer.php b/src/NodeAnalyzer/CallAnalyzer.php index 641f6fee790..4c89e952fab 100644 --- a/src/NodeAnalyzer/CallAnalyzer.php +++ b/src/NodeAnalyzer/CallAnalyzer.php @@ -42,13 +42,10 @@ public function isObjectCall(Expr $expr): bool return $isObjectCallLeft || $isObjectCallRight; } - foreach (self::OBJECT_CALL_TYPES as $objectCallType) { - if ($expr instanceof $objectCallType) { - return true; - } - } - - return false; + return array_any( + self::OBJECT_CALL_TYPES, + fn (string $objectCallType): bool => $expr instanceof $objectCallType + ); } /** @@ -56,13 +53,7 @@ public function isObjectCall(Expr $expr): bool */ public function doesIfHasObjectCall(array $ifs): bool { - foreach ($ifs as $if) { - if ($this->isObjectCall($if->cond)) { - return true; - } - } - - return false; + return array_any($ifs, fn (If_ $if): bool => $this->isObjectCall($if->cond)); } public function isNewInstance(Variable $variable): bool diff --git a/src/NodeAnalyzer/CallLikeArgumentNameAdder.php b/src/NodeAnalyzer/CallLikeArgumentNameAdder.php index 7d19ce9b764..25e2a2a9118 100644 --- a/src/NodeAnalyzer/CallLikeArgumentNameAdder.php +++ b/src/NodeAnalyzer/CallLikeArgumentNameAdder.php @@ -85,13 +85,7 @@ private function shouldSkip(CallLike $callLike): bool return true; } - foreach ($args as $arg) { - if ($arg->unpack) { - return true; - } - } - - return false; + return array_any($args, fn ($arg) => $arg->unpack); } /** diff --git a/src/NodeAnalyzer/DoctrineEntityAnalyzer.php b/src/NodeAnalyzer/DoctrineEntityAnalyzer.php index 5f3c18cd402..e9dce6221a8 100644 --- a/src/NodeAnalyzer/DoctrineEntityAnalyzer.php +++ b/src/NodeAnalyzer/DoctrineEntityAnalyzer.php @@ -50,13 +50,11 @@ public function hasClassReflectionAttribute(ClassReflection $classReflection): b return false; } - foreach (self::DOCTRINE_MAPPING_CLASSES as $doctrineMappingClass) { - // skip entities - if ($nativeReflectionClass->getAttributes($doctrineMappingClass) !== []) { - return true; - } - } - - return false; + return array_any( + self::DOCTRINE_MAPPING_CLASSES, + fn (string $doctrineMappingClass): bool => $nativeReflectionClass->getAttributes( + $doctrineMappingClass + ) !== [] + ); } } diff --git a/src/NodeAnalyzer/ParamAnalyzer.php b/src/NodeAnalyzer/ParamAnalyzer.php index 779a125fd85..b216d0b1f7c 100644 --- a/src/NodeAnalyzer/ParamAnalyzer.php +++ b/src/NodeAnalyzer/ParamAnalyzer.php @@ -5,6 +5,7 @@ namespace Rector\NodeAnalyzer; use PhpParser\Node; +use PhpParser\Node\ClosureUse; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\Closure; @@ -102,13 +103,7 @@ public function isParamUsedInClassMethod(ClassMethod $classMethod, Param $param) */ public function hasPropertyPromotion(array $params): bool { - foreach ($params as $param) { - if ($param->isPromoted()) { - return true; - } - } - - return false; + return array_any($params, fn (Param $param): bool => $param->isPromoted()); } public function isNullable(Param $param): bool @@ -144,13 +139,10 @@ public function isParamReassign(ClassMethod $classMethod, Param $param): bool private function isVariableInClosureUses(Closure $closure, Variable $variable): bool { - foreach ($closure->uses as $use) { - if ($this->nodeComparator->areNodesEqual($use->var, $variable)) { - return true; - } - } - - return false; + return array_any( + $closure->uses, + fn (ClosureUse $use): bool => $this->nodeComparator->areNodesEqual($use->var, $variable) + ); } private function isUsedAsArg(Node $node, Param $param): bool diff --git a/src/NodeAnalyzer/ScopeAnalyzer.php b/src/NodeAnalyzer/ScopeAnalyzer.php index 8bc7252f827..1734898b92e 100644 --- a/src/NodeAnalyzer/ScopeAnalyzer.php +++ b/src/NodeAnalyzer/ScopeAnalyzer.php @@ -18,12 +18,9 @@ final class ScopeAnalyzer public function isRefreshable(Node $node): bool { - foreach (self::NON_REFRESHABLE_NODES as $noScopeNode) { - if ($node instanceof $noScopeNode) { - return false; - } - } - - return true; + return array_all( + self::NON_REFRESHABLE_NODES, + fn (string $noScopeNode): bool => ! $node instanceof $noScopeNode + ); } } diff --git a/src/NodeAnalyzer/VariadicAnalyzer.php b/src/NodeAnalyzer/VariadicAnalyzer.php index a20b998de74..6c0b1b382ff 100644 --- a/src/NodeAnalyzer/VariadicAnalyzer.php +++ b/src/NodeAnalyzer/VariadicAnalyzer.php @@ -10,6 +10,7 @@ use PhpParser\Node\Expr\StaticCall; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\MethodReflection; +use PHPStan\Reflection\ParametersAcceptor; use Rector\Reflection\ReflectionResolver; final readonly class VariadicAnalyzer @@ -31,13 +32,9 @@ public function hasVariadicParameters(FuncCall | StaticCall | MethodCall | New_ private function hasVariadicVariant(MethodReflection | FunctionReflection $functionLikeReflection): bool { - foreach ($functionLikeReflection->getVariants() as $parametersAcceptor) { - // can be any number of arguments → nothing to limit here - if ($parametersAcceptor->isVariadic()) { - return true; - } - } - - return false; + return array_any( + $functionLikeReflection->getVariants(), + fn (ParametersAcceptor $parametersAcceptor): bool => $parametersAcceptor->isVariadic() + ); } } diff --git a/src/NodeManipulator/ClassDependencyManipulator.php b/src/NodeManipulator/ClassDependencyManipulator.php index e4577a87426..565ab0099a1 100644 --- a/src/NodeManipulator/ClassDependencyManipulator.php +++ b/src/NodeManipulator/ClassDependencyManipulator.php @@ -5,6 +5,7 @@ namespace Rector\NodeManipulator; use PhpParser\Modifiers; +use PhpParser\Node; use PhpParser\Node\Arg; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\StaticCall; @@ -334,13 +335,10 @@ private function hasClassParentClassMethod(Class_ $class, string $methodName): b return false; } - foreach ($classReflection->getParents() as $parentClassReflection) { - if ($parentClassReflection->hasMethod($methodName)) { - return true; - } - } - - return false; + return array_any( + $classReflection->getParents(), + fn (ClassReflection $parentClassReflection): bool => $parentClassReflection->hasMethod($methodName) + ); } private function createParentClassMethodCall(string $methodName): Expression @@ -357,13 +355,10 @@ private function isParamInConstructor(Class_ $class, string $propertyName): bool return false; } - foreach ($constructClassMethod->params as $param) { - if ($this->nodeNameResolver->isName($param, $propertyName)) { - return true; - } - } - - return false; + return array_any( + $constructClassMethod->params, + fn (Node|array $param): bool => $this->nodeNameResolver->isName($param, $propertyName) + ); } private function hasClassPropertyAndDependency(Class_ $class, PropertyMetadata $propertyMetadata): bool diff --git a/src/NodeManipulator/ClassMethodManipulator.php b/src/NodeManipulator/ClassMethodManipulator.php index f7a452f9b96..2d23690f4c6 100644 --- a/src/NodeManipulator/ClassMethodManipulator.php +++ b/src/NodeManipulator/ClassMethodManipulator.php @@ -58,12 +58,9 @@ public function hasParentMethodOrInterfaceMethod(Class_ $class, string $methodNa } } - foreach ($classReflection->getInterfaces() as $interfaceReflection) { - if ($interfaceReflection->hasMethod($methodName)) { - return true; - } - } - - return false; + return array_any( + $classReflection->getInterfaces(), + fn (ClassReflection $classReflection): bool => $classReflection->hasMethod($methodName) + ); } } diff --git a/src/NodeNameResolver/NodeNameResolver.php b/src/NodeNameResolver/NodeNameResolver.php index 51d8c62ba52..620ea64ecbd 100644 --- a/src/NodeNameResolver/NodeNameResolver.php +++ b/src/NodeNameResolver/NodeNameResolver.php @@ -61,13 +61,7 @@ public function isNames(Node $node, array $names): bool return false; } - foreach ($names as $name) { - if ($this->isStringName($nodeName, $name)) { - return true; - } - } - - return false; + return array_any($names, fn (string $name): bool => $this->isStringName($nodeName, $name)); } /** @@ -77,14 +71,7 @@ public function isNames(Node $node, array $names): bool public function isName(Node | array $node, string $name): bool { $nodes = is_array($node) ? $node : [$node]; - - foreach ($nodes as $node) { - if ($this->isSingleName($node, $name)) { - return true; - } - } - - return false; + return array_any($nodes, fn (Node $node): bool => $this->isSingleName($node, $name)); } /** diff --git a/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php b/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php index d988a153561..40d773291e6 100644 --- a/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php +++ b/src/NodeTypeResolver/PHPStan/Type/StaticTypeAnalyzer.php @@ -55,13 +55,7 @@ private function isAlwaysTruableUnionType(Type $type): bool return false; } - foreach ($type->getTypes() as $unionedType) { - if (! $this->isAlwaysTruableType($unionedType)) { - return false; - } - } - - return true; + return array_all($type->getTypes(), fn (Type $unionedType): bool => $this->isAlwaysTruableType($unionedType)); } private function isAlwaysTruableArrayType(ArrayType $arrayType): bool diff --git a/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php b/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php index 003fe4129ea..e281cd14df3 100644 --- a/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php +++ b/src/PhpAttribute/NodeFactory/AnnotationToAttributeIntegerValueCaster.php @@ -92,13 +92,7 @@ private function containsInteger(Type $type): bool return false; } - foreach ($type->getTypes() as $unionedType) { - if ($unionedType->isInteger()->yes()) { - return true; - } - } - - return false; + return array_any($type->getTypes(), fn (Type $unionedType): bool => $unionedType->isInteger()->yes()); } /** diff --git a/src/PhpParser/Enum/NodeGroup.php b/src/PhpParser/Enum/NodeGroup.php index fce2d1cec8f..43e3826488d 100644 --- a/src/PhpParser/Enum/NodeGroup.php +++ b/src/PhpParser/Enum/NodeGroup.php @@ -82,12 +82,6 @@ final class NodeGroup public static function isStmtAwareNode(Node $node): bool { - foreach (self::STMTS_AWARE as $stmtAwareClass) { - if ($node instanceof $stmtAwareClass) { - return true; - } - } - - return false; + return array_any(self::STMTS_AWARE, fn (string $stmtAwareClass): bool => $node instanceof $stmtAwareClass); } } diff --git a/src/PhpParser/Node/BetterNodeFinder.php b/src/PhpParser/Node/BetterNodeFinder.php index 714fee68d93..5743cbf390c 100644 --- a/src/PhpParser/Node/BetterNodeFinder.php +++ b/src/PhpParser/Node/BetterNodeFinder.php @@ -111,15 +111,10 @@ public function hasInstancesOf(Node | array $nodes, array $types): bool { Assert::allIsAOf($types, Node::class); - return (bool) $this->nodeFinder->findFirst($nodes, static function (Node $node) use ($types): bool { - foreach ($types as $type) { - if ($node instanceof $type) { - return true; - } - } - - return false; - }); + return (bool) $this->nodeFinder->findFirst( + $nodes, + static fn (Node $node): bool => array_any($types, fn (string $type): bool => $node instanceof $type) + ); } /** diff --git a/src/PhpParser/Node/FileNode.php b/src/PhpParser/Node/FileNode.php index 8d818e993d9..c39b2d0c27b 100644 --- a/src/PhpParser/Node/FileNode.php +++ b/src/PhpParser/Node/FileNode.php @@ -140,13 +140,12 @@ public function getPendingImports(): PendingImports public function hasImport(FullyQualifiedObjectType $fullyQualifiedObjectType): bool { - foreach ($this->resolveUsedImportTypes() as $useImport) { - if ($useImport->equals($fullyQualifiedObjectType)) { - return true; - } - } - - return false; + return array_any( + $this->resolveUsedImportTypes(), + fn (AliasedObjectType|FullyQualifiedObjectType $useImport): bool => $useImport->equals( + $fullyQualifiedObjectType + ) + ); } /** @@ -224,13 +223,7 @@ public function getSubNodeNames(): array public function isNamespaced(): bool { - foreach ($this->stmts as $stmt) { - if ($stmt instanceof Namespace_) { - return true; - } - } - - return false; + return array_any($this->stmts, fn (Stmt $stmt): bool => $stmt instanceof Namespace_); } public function getNamespace(): ?Namespace_ diff --git a/src/VendorLocker/NodeVendorLocker/ClassMethodParamVendorLockResolver.php b/src/VendorLocker/NodeVendorLocker/ClassMethodParamVendorLockResolver.php index 10c793967fb..3dd34c32bd8 100644 --- a/src/VendorLocker/NodeVendorLocker/ClassMethodParamVendorLockResolver.php +++ b/src/VendorLocker/NodeVendorLocker/ClassMethodParamVendorLockResolver.php @@ -46,12 +46,9 @@ public function isVendorLocked(ClassMethod $classMethod): bool */ private function hasParentInterfaceMethod(ClassReflection $classReflection, string $methodName): bool { - foreach ($classReflection->getInterfaces() as $interfaceClassReflection) { - if ($interfaceClassReflection->hasMethod($methodName)) { - return true; - } - } - - return false; + return array_any( + $classReflection->getInterfaces(), + fn (ClassReflection $interfaceClassReflection): bool => $interfaceClassReflection->hasMethod($methodName) + ); } }