diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/AddAssertArrayFromClassMethodDocblockRectorTest.php b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/AddAssertArrayFromClassMethodDocblockRectorTest.php new file mode 100644 index 00000000000..5bfabbcf375 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/AddAssertArrayFromClassMethodDocblockRectorTest.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/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/multiple_parameters.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/multiple_parameters.php.inc new file mode 100644 index 00000000000..40cb00376b5 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/multiple_parameters.php.inc @@ -0,0 +1,36 @@ + $items + * @param string[] $names + */ + public function run(array $items, array $names) + { + } +} + +?> +----- + $items + * @param string[] $names + */ + public function run(array $items, array $names) + { + \Webmozart\Assert\Assert::allString($items); + \Webmozart\Assert\Assert::allString(array_keys($items)); + \Webmozart\Assert\Assert::allString($names); + } +} + +?> diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/simple_array.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/simple_array.php.inc new file mode 100644 index 00000000000..693cec4da04 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/simple_array.php.inc @@ -0,0 +1,32 @@ + +----- + diff --git a/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/skip_already_set.php.inc b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/skip_already_set.php.inc new file mode 100644 index 00000000000..b3163619454 --- /dev/null +++ b/rules-tests/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector/Fixture/skip_already_set.php.inc @@ -0,0 +1,14 @@ +rule(AddAssertArrayFromClassMethodDocblockRector::class); +}; diff --git a/rules/Assert/Enum/AssertClassName.php b/rules/Assert/Enum/AssertClassName.php new file mode 100644 index 00000000000..9a653249215 --- /dev/null +++ b/rules/Assert/Enum/AssertClassName.php @@ -0,0 +1,13 @@ +stmts === null) { + return []; + } + + $existingAssertCallHashes = []; + $standard = new Standard(); + + foreach ($classMethod->stmts as $currentStmt) { + if (! $currentStmt instanceof Expression) { + continue; + } + + if (! $currentStmt->expr instanceof StaticCall) { + continue; + } + + $staticCall = $currentStmt->expr; + if (! $staticCall->class instanceof Name) { + continue; + } + + if ($staticCall->class->toString() !== AssertClassName::ASSERT) { + continue; + } + + $existingAssertCallHashes[] = $standard->prettyPrintExpr($staticCall); + } + + return $existingAssertCallHashes; + } +} diff --git a/rules/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector.php b/rules/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector.php new file mode 100644 index 00000000000..c4143c794a8 --- /dev/null +++ b/rules/Assert/Rector/ClassMethod/AddAssertArrayFromClassMethodDocblockRector.php @@ -0,0 +1,195 @@ +isInClass()) { + return null; + } + + if ($node->stmts === null || $node->isAbstract()) { + return null; + } + + $methodPhpDocInfo = $this->phpDocInfoFactory->createFromNode($node); + if (! $methodPhpDocInfo instanceof PhpDocInfo) { + return null; + } + + $paramTagValueNodes = $methodPhpDocInfo->getParamTagValueNodes(); + if ($paramTagValueNodes === []) { + return null; + } + + $assertStaticCallStmts = []; + + foreach ($node->getParams() as $param) { + if (! $param->type instanceof Identifier) { + continue; + } + + // handle arrays only + if ($param->type->name !== 'array') { + continue; + } + + if (! $param->var instanceof Variable) { + continue; + } + + $paramName = $param->var->name; + if (! is_string($paramName)) { + continue; + } + + $paramDocType = $methodPhpDocInfo->getParamType($paramName); + if (! $paramDocType instanceof ArrayType) { + continue; + } + + // assert value + if ($paramDocType->getItemType() instanceof IntegerType) { + $assertStaticCallStmts[] = $this->createAssertExpression($param->var, 'allInteger'); + } elseif ($paramDocType->getItemType() instanceof StringType) { + $assertStaticCallStmts[] = $this->createAssertExpression($param->var, 'allString'); + } + + // assert keys + $arrayKeys = new FuncCall(new Name('array_keys'), [new Arg($param->var)]); + if ($paramDocType->getKeyType() instanceof StringType) { + $assertStaticCallStmts[] = $this->createAssertExpression($arrayKeys, 'allString'); + } elseif ($paramDocType->getKeyType() instanceof IntegerType) { + $assertStaticCallStmts[] = $this->createAssertExpression($arrayKeys, 'allInteger'); + } + } + + // filter existing assert to avoid duplication + if ($assertStaticCallStmts === []) { + return null; + } + + $existingAssertCallHashes = $this->existingAssertStaticCallResolver->resolve($node); + + $assertStaticCallStmts = $this->filterOutExistingStaticCall($assertStaticCallStmts, $existingAssertCallHashes); + + if ($assertStaticCallStmts === []) { + return null; + } + + $node->stmts = array_merge($assertStaticCallStmts, $node->stmts); + + return $node; + } + + private function createAssertExpression(Expr $expr, string $methodName): Expression + { + $staticCall = new StaticCall(new FullyQualified(AssertClassName::ASSERT), $methodName, [new Arg($expr)]); + + return new Expression($staticCall); + } + + /** + * @param Expression[] $assertStaticCallStmts + * @param string[] $existingAssertCallHashes + * @return Expression[] + */ + private function filterOutExistingStaticCall(array $assertStaticCallStmts, array $existingAssertCallHashes): array + { + $standard = new Standard(); + + return array_filter($assertStaticCallStmts, function (Expression $assertStaticCallExpression) use ( + $standard, + $existingAssertCallHashes + ): bool { + $currentStaticCallHash = $standard->prettyPrintExpr($assertStaticCallExpression->expr); + + return ! in_array($currentStaticCallHash, $existingAssertCallHashes, true); + }); + } +} diff --git a/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php b/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php index 8a36f3085c7..73af63c27ab 100644 --- a/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php +++ b/rules/Php85/Rector/FuncCall/ChrArgModuloRector.php @@ -23,7 +23,7 @@ final class ChrArgModuloRector extends AbstractRector implements MinPhpVersionIn { public function __construct( private readonly ValueResolver $valueResolver - ){ + ) { } @@ -80,7 +80,7 @@ public function refactor(Node $node): ?Node return null; } - if ( $value >= 0 && $value <= 255) { + if ($value >= 0 && $value <= 255) { return null; }