From ec51da34e79b9eaedd04fa9963eac6e8540301a6 Mon Sep 17 00:00:00 2001 From: Anasatasiia Sh Date: Wed, 22 Jan 2025 19:04:32 +0200 Subject: [PATCH 1/6] Add Cyrillic Sniff, method call consistency rule, namespace consistency rule --- config/phpstan.services.neon | 10 + src/PhpCs/FiveLab/ErrorCodes.php | 1 + .../FiveLab/Sniffs/Strings/CyrillicSniff.php | 91 +++++++++ src/PhpStan/MethodCallConsistencyRule.php | 183 ++++++++++++++++++ src/PhpStan/NamespaceConsistencyRule.php | 110 +++++++++++ .../Sniffs/Strings/CyrillicSniffTest.php | 100 ++++++++++ .../Strings/Resources/cyrillic/wrong.php | 29 +++ .../PhpStan/MethodCallConsistencyRuleTest.php | 49 +++++ .../PhpStan/NamespaceConsistencyRuleTest.php | 41 ++++ .../ClassForProperty.php | 56 ++++++ .../ExampleService.php | 10 + .../NamespaceConsistencyRule/TestService.php | 10 + 12 files changed, 690 insertions(+) create mode 100644 src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php create mode 100644 src/PhpStan/MethodCallConsistencyRule.php create mode 100644 src/PhpStan/NamespaceConsistencyRule.php create mode 100644 tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php create mode 100644 tests/PhpCs/FiveLab/Sniffs/Strings/Resources/cyrillic/wrong.php create mode 100644 tests/PhpStan/MethodCallConsistencyRuleTest.php create mode 100644 tests/PhpStan/NamespaceConsistencyRuleTest.php create mode 100644 tests/PhpStan/Resources/MethodCallConsistency/ClassForProperty.php create mode 100644 tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php create mode 100644 tests/PhpStan/Resources/NamespaceConsistencyRule/TestService.php diff --git a/config/phpstan.services.neon b/config/phpstan.services.neon index bfbdd2d..7047dee 100644 --- a/config/phpstan.services.neon +++ b/config/phpstan.services.neon @@ -23,3 +23,13 @@ services: class: \FiveLab\Component\CiRules\PhpStan\ForbiddenPassArgumentAsReferenceRule arguments: [ PhpParser\Node\Stmt\ClassMethod ] tags: [ phpstan.rules.rule ] + + - + class: \FiveLab\Component\CiRules\PhpStan\MethodCallConsistencyRule + arguments: + - @reflectionProvider + tags: [ phpstan.rules.rule ] + + - + class: \FiveLab\Component\CiRules\PhpStan\NamespaceConsistencyRule + tags: [ phpstan.rules.rule ] diff --git a/src/PhpCs/FiveLab/ErrorCodes.php b/src/PhpCs/FiveLab/ErrorCodes.php index 2ae7d88..9a2a847 100644 --- a/src/PhpCs/FiveLab/ErrorCodes.php +++ b/src/PhpCs/FiveLab/ErrorCodes.php @@ -30,4 +30,5 @@ final class ErrorCodes public const LINE_AFTER_NOT_ALLOWED = 'LineAfterNotAllowed'; public const LINE_BEFORE_NOT_ALLOWED = 'LineBeforeNotAllowed'; public const MISSED_CONSTANT_TYPE = 'MissedConstantType'; + public const CYRILLIC_FOUND = 'CyrillicFound'; } diff --git a/src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php b/src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php new file mode 100644 index 0000000..023edde --- /dev/null +++ b/src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php @@ -0,0 +1,91 @@ +getTokens(); + $content = $tokens[$stackPtr]['content']; + + if ($this->containsCyrillic($content)) { + $phpcsFile->addError( + 'Use cyrillic symbols is forbidden: "%s"', + $stackPtr, + ErrorCodes::CYRILLIC_FOUND, + [\trim($content)] + ); + } + } + + private function containsCyrillic(string $content): bool + { + foreach (mb_str_split($content) as $char) { + if (array_key_exists($char, self::$cyrillicMap)) { + return true; + } + } + + return false; + } +} diff --git a/src/PhpStan/MethodCallConsistencyRule.php b/src/PhpStan/MethodCallConsistencyRule.php new file mode 100644 index 0000000..ff9ca34 --- /dev/null +++ b/src/PhpStan/MethodCallConsistencyRule.php @@ -0,0 +1,183 @@ +checkStaticCall($node, $scope); + } + + if ($node instanceof Node\Expr\MethodCall) { + return $this->checkInstanceCall($node, $scope); + } + + return []; + } + + private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): array + { + + $className = $this->resolveClassNameForStaticCall($node, $scope); + + if (!$className) { + return []; + } + + $methodReflection = $this->getMethodReflection($node, $className, $scope); + + if (!$methodReflection?->isStatic()) { + return [\sprintf( + 'Method "%s::%s" is not static but called statically.', + $className, + $methodReflection->getName() + ),]; + } + + return []; + } + + private function checkInstanceCall(Node\Expr\MethodCall $node, Scope $scope): array + { + $className = $this->resolveClassNameForInstanceCall($node, $scope); + + if (!$className) { + return []; + } + + $methodReflection = $this->getMethodReflection($node, $className, $scope); + + if ($methodReflection?->isStatic()) { + return [\sprintf( + 'Method "%s->%s" is static but called dynamically.', + $className, + $methodReflection->getName() + ),]; + } + + return []; + } + + private function resolveClassNameForStaticCall(Node\Expr\StaticCall $node, Scope $scope): ?string + { + if ($node->class instanceof Node\Name && 'parent' === $node->class->toString()) { + return null; + } + + // self::method() + if ($node->class instanceof Node\Name && 'self' === $node->class->toString()) { + return $scope->getClassReflection()?->getName(); + } + + // ClassName::method() + if ($node->class instanceof Node\Name) { + return $scope->resolveName($node->class); + } + + // $this->property::method() + if ($node->class instanceof Node\Expr\PropertyFetch) { + $type = $scope->getType($node->class); + + if (\method_exists($type, 'getClassName') && $type->isObject()->yes()) { + return $type->getClassName(); + } + } + + // $var::method() + if ($node->class instanceof Node\Expr\Variable) { + $variableName = $node->class->name; + + if (\is_string($variableName)) { + $type = $scope->getType(new Node\Expr\Variable($variableName)); + + if (\method_exists($type, 'getClassName') && $type->isObject()->yes()) { + return $type->getClassName(); + } + } + } + + return null; + } + + private function resolveClassNameForInstanceCall(Node\Expr\MethodCall $node, Scope $scope): ?string + { + // $this->method() + if ($node->var instanceof Node\Expr\Variable && $node->var->name === 'this') { + return $scope->getClassReflection()?->getName(); + } + + // $this->property->method() + if ($node->var instanceof Node\Expr\PropertyFetch) { + $type = $scope->getType($node->var); + + if (\method_exists($type, 'getClassName') && $type->isObject()->yes()) { + return $type->getClassName(); + } + } + + // $var->method() + if ($node->var instanceof Node\Expr\Variable) { + $variableName = $node->var->name; + + if (is_string($variableName)) { + $type = $scope->getType(new Node\Expr\Variable($variableName)); + + if (\method_exists($type, 'getClassName') && $type->isObject()->yes()) { + return $type->getClassName(); + } + } + } + + return null; + } + + private function getMethodReflection(Node\Expr\StaticCall|Node\Expr\MethodCall $node, string $className, Scope $scope): ?ExtendedMethodReflection + { + $methodName = $node->name instanceof Node\Identifier ? $node->name->name : null; + + if (!$className || !$methodName) { + return null; + } + + $classReflection = $this->reflectionProvider->getClass($className); + + if (!$classReflection->hasMethod($methodName)) { + return null; + } + + return $classReflection->getMethod($methodName, $scope); + } +} diff --git a/src/PhpStan/NamespaceConsistencyRule.php b/src/PhpStan/NamespaceConsistencyRule.php new file mode 100644 index 0000000..4dc8698 --- /dev/null +++ b/src/PhpStan/NamespaceConsistencyRule.php @@ -0,0 +1,110 @@ +getFile(); + $declaredNamespace = $node->name?->toString(); + $expectedNamespace = $this->getExpectedNamespace($filePath, $scope); + if ($declaredNamespace !== $expectedNamespace) { + return [ + \sprintf( + 'Namespace mismatch in file "%s". Expected namespace "%s", found "%s".', + $filePath, + $expectedNamespace, + $declaredNamespace + ), + ]; + } + + return []; + } + + private function getExpectedNamespace(string $filePath, Scope $scope): ?string + { + $composerJsonPath = $this->findProjectComposerJson($scope); + $composerJson = $this->loadComposerJson($composerJsonPath); + + $psr4 = \array_merge( + $composerJson['autoload']['psr-4'] ?? [], + $composerJson['autoload-dev']['psr-4'] ?? [] + ); + + foreach ($psr4 as $namespace => $directory) { + $normalizedDirectory = \realpath(\dirname($composerJsonPath).'/'.$directory); + $normalizedFilePath = \realpath($filePath); + + if ($normalizedDirectory && $normalizedFilePath && \str_starts_with($normalizedFilePath, $normalizedDirectory)) { + $relativePath = \substr($normalizedFilePath, \strlen($normalizedDirectory) + 1); + $expectedNamespace = \rtrim($namespace, '\\').'\\'.\str_replace('/', '\\', \dirname($relativePath)); + + return \trim($expectedNamespace, '\\, .'); + } + } + + return null; + } + + private function loadComposerJson(string $composerJsonPath): array + { + if (!\file_exists($composerJsonPath)) { + throw new \RuntimeException(\sprintf('composer.json not found at "%s".', $composerJsonPath)); + } + + $content = \file_get_contents($composerJsonPath); + + $decoded = $content ? \json_decode($content, true, flags: JSON_THROW_ON_ERROR) : null; + + if (!\is_array($decoded)) { + throw new \RuntimeException(\sprintf('Invalid composer.json format at "%s".', $composerJsonPath)); + } + + return $decoded; + } + + private function findProjectComposerJson(Scope $scope): string + { + $projectDir = $scope->getFile(); + $currentDir = \dirname($projectDir); + + while ($currentDir !== \dirname($currentDir)) { + $composerJsonPath = $currentDir.'/composer.json'; + if (\file_exists($composerJsonPath)) { + return $composerJsonPath; + } + + $currentDir = \dirname($currentDir); + } + + throw new \RuntimeException('composer.json not found in the project root.'); + } +} diff --git a/tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php new file mode 100644 index 0000000..af0fa78 --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php @@ -0,0 +1,100 @@ + [ + __DIR__.'/Resources/cyrillic/wrong.php', + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "Пространство"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "\'Файл\'"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "класс2"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "КОНСТАНТА"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "\'Значение\'"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "$переменная"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "\'Привет\'"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "Строка документации"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "Класс"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "// комментарий"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "метод"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "\'Тест\'"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "Интерфейс"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "Трейт"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + [ + //phpcs:ignore + 'message' => 'Use cyrillic symbols is forbidden: "метка"', + 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', + ], + ], + ]; + } +} diff --git a/tests/PhpCs/FiveLab/Sniffs/Strings/Resources/cyrillic/wrong.php b/tests/PhpCs/FiveLab/Sniffs/Strings/Resources/cyrillic/wrong.php new file mode 100644 index 0000000..0b27a6d --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Strings/Resources/cyrillic/wrong.php @@ -0,0 +1,29 @@ +getByType(ReflectionProvider::class); + + return new MethodCallConsistencyRule($reflectionProvider); + } + + /** + * @test + */ + public function shouldSuccessProcessForIsset(): void + { + $this->analyse( + [__DIR__.'/Resources/MethodCallConsistency/ClassForProperty.php'], + [ + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\Example::instanceMethod" is not static but called statically.', 26], + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\Example->staticMethod" is static but called dynamically.', 27], + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\ClassForProperty::instanceMethod1" is not static but called statically.', 29], + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\ClassForProperty->staticMethod1" is static but called dynamically.', 30], + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\ClassForProperty::instanceMethod1" is not static but called statically.', 36], + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\ClassForProperty->staticMethod1" is static but called dynamically.', 37], + ['Method "FiveLab\Component\CiRules\Tests\PhpStan\Resources\MethodCallConsistency\ClassForProperty::instanceMethod1" is not static but called statically.', 40], + ], + ); + } +} diff --git a/tests/PhpStan/NamespaceConsistencyRuleTest.php b/tests/PhpStan/NamespaceConsistencyRuleTest.php new file mode 100644 index 0000000..6d744d5 --- /dev/null +++ b/tests/PhpStan/NamespaceConsistencyRuleTest.php @@ -0,0 +1,41 @@ +analyse( + [$filePathError, __DIR__.'/Resources/NamespaceConsistencyRule/ExampleService.php'], + [ + ['Namespace mismatch in file "'.$filePathError.'". Expected namespace "FiveLab\Component\CiRules\Tests\PhpStan\Resources\NamespaceConsistencyRule", found "FiveLab\Component\CiRules\Tests".', 05], + ], + ); + } +} diff --git a/tests/PhpStan/Resources/MethodCallConsistency/ClassForProperty.php b/tests/PhpStan/Resources/MethodCallConsistency/ClassForProperty.php new file mode 100644 index 0000000..ed60a60 --- /dev/null +++ b/tests/PhpStan/Resources/MethodCallConsistency/ClassForProperty.php @@ -0,0 +1,56 @@ +instanceMethod(); + + $this->property->instanceMethod1(); + $this->property::staticMethod1(); + + self::instanceMethod(); + $this->staticMethod(); + + $this->property::instanceMethod1(); + $this->property->staticMethod1(); + + $var = new ClassForProperty(); + $var->instanceMethod1(); + $var::staticMethod1(); + + $var::instanceMethod1(); + $var->staticMethod1(); + + ClassForProperty::staticMethod1(); + ClassForProperty::instanceMethod1(); + } +} + +class ParentClass { + public static function staticMethod2(): void {} + public function instanceMethod2(): void {} +} + +class ChildClass extends ParentClass { + public static function staticMethod2(): void + { + parent::staticMethod2(); + parent::instanceMethod2(); + } +} + diff --git a/tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php b/tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php new file mode 100644 index 0000000..f667210 --- /dev/null +++ b/tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php @@ -0,0 +1,10 @@ + Date: Thu, 23 Jan 2025 20:29:00 +0200 Subject: [PATCH 2/6] Add Cyrillic Sniff, method call consistency rule, namespace consistency rule --- config/phpstan.services.neon | 7 +- src/PhpCs/FiveLab/ErrorCodes.php | 2 +- .../Sniffs/Namespace/NamespaceSniff.php} | 65 ++++++------ .../FiveLab/Sniffs/Strings/AsciiSniff.php | 59 +++++++++++ .../FiveLab/Sniffs/Strings/CyrillicSniff.php | 91 ---------------- src/PhpStan/MethodCallConsistencyRule.php | 4 +- .../ProhibitedAttributeSniffTest.php | 2 +- .../Sniffs/Namespace/NamespaceSniffTest.php | 38 +++++++ .../Namespace/Resources/ExampleService.php | 10 ++ .../Namespace/Resources}/TestService.php | 0 .../FiveLab/Sniffs/Strings/AsciiSniffTest.php | 94 ++++++++++++++++ .../Sniffs/Strings/CyrillicSniffTest.php | 100 ------------------ .../Resources/{cyrillic => ascii}/wrong.php | 0 .../PhpStan/NamespaceConsistencyRuleTest.php | 41 ------- .../ExampleService.php | 10 -- 15 files changed, 238 insertions(+), 285 deletions(-) rename src/{PhpStan/NamespaceConsistencyRule.php => PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php} (59%) create mode 100644 src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php delete mode 100644 src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php create mode 100644 tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php create mode 100644 tests/PhpCs/FiveLab/Sniffs/Namespace/Resources/ExampleService.php rename tests/{PhpStan/Resources/NamespaceConsistencyRule => PhpCs/FiveLab/Sniffs/Namespace/Resources}/TestService.php (100%) create mode 100644 tests/PhpCs/FiveLab/Sniffs/Strings/AsciiSniffTest.php delete mode 100644 tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php rename tests/PhpCs/FiveLab/Sniffs/Strings/Resources/{cyrillic => ascii}/wrong.php (100%) delete mode 100644 tests/PhpStan/NamespaceConsistencyRuleTest.php delete mode 100644 tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php diff --git a/config/phpstan.services.neon b/config/phpstan.services.neon index 7047dee..af41192 100644 --- a/config/phpstan.services.neon +++ b/config/phpstan.services.neon @@ -26,10 +26,5 @@ services: - class: \FiveLab\Component\CiRules\PhpStan\MethodCallConsistencyRule - arguments: - - @reflectionProvider - tags: [ phpstan.rules.rule ] - - - - class: \FiveLab\Component\CiRules\PhpStan\NamespaceConsistencyRule + arguments: [ @reflectionProvider ] tags: [ phpstan.rules.rule ] diff --git a/src/PhpCs/FiveLab/ErrorCodes.php b/src/PhpCs/FiveLab/ErrorCodes.php index 9a2a847..4f45298 100644 --- a/src/PhpCs/FiveLab/ErrorCodes.php +++ b/src/PhpCs/FiveLab/ErrorCodes.php @@ -30,5 +30,5 @@ final class ErrorCodes public const LINE_AFTER_NOT_ALLOWED = 'LineAfterNotAllowed'; public const LINE_BEFORE_NOT_ALLOWED = 'LineBeforeNotAllowed'; public const MISSED_CONSTANT_TYPE = 'MissedConstantType'; - public const CYRILLIC_FOUND = 'CyrillicFound'; + public const NAMESPACE_WRONG = 'NamespaceWrong'; } diff --git a/src/PhpStan/NamespaceConsistencyRule.php b/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php similarity index 59% rename from src/PhpStan/NamespaceConsistencyRule.php rename to src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php index 4dc8698..6c26d9b 100644 --- a/src/PhpStan/NamespaceConsistencyRule.php +++ b/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php @@ -11,47 +11,47 @@ declare(strict_types = 1); -namespace FiveLab\Component\CiRules\PhpStan; +namespace FiveLab\Component\CiRules\PhpCs\FiveLab\Sniffs\Namespace; -use PhpParser\Node; -use PHPStan\Analyser\Scope; -use PHPStan\Rules\Rule; +use FiveLab\Component\CiRules\PhpCs\FiveLab\ErrorCodes; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Sniffs\Sniff; -class NamespaceConsistencyRule implements Rule +class NamespaceSniff implements Sniff { - public function getNodeType(): string + public function register(): array { - return Node\Stmt\Namespace_::class; + return [T_NAMESPACE]; } - /** - * @param Node\Stmt\Namespace_ $node - * @param Scope $scope - * - * @return array - */ - public function processNode(Node $node, Scope $scope): array + public function process(File $phpcsFile, $stackPtr): void { - $filePath = $scope->getFile(); - $declaredNamespace = $node->name?->toString(); - $expectedNamespace = $this->getExpectedNamespace($filePath, $scope); - if ($declaredNamespace !== $expectedNamespace) { - return [ - \sprintf( - 'Namespace mismatch in file "%s". Expected namespace "%s", found "%s".', - $filePath, - $expectedNamespace, - $declaredNamespace - ), - ]; + $tokens = $phpcsFile->getTokens(); + $endToken = $phpcsFile->findNext(T_SEMICOLON, $stackPtr); + + if (false === $endToken) { + return; + } + + $declaredNamespace = ''; + for ($i = $stackPtr + 1; $i < $endToken; $i++) { + $declaredNamespace .= $tokens[$i]['content']; } - return []; + $expectedNamespace = $this->getExpectedNamespace($phpcsFile); + if (\trim($declaredNamespace) !== $expectedNamespace) { + $phpcsFile->addError( + 'Namespace mismatch in file "%s". Expected namespace "%s", found "%s".', + $stackPtr, + ErrorCodes::NAMESPACE_WRONG, + [$phpcsFile->path, $expectedNamespace, $declaredNamespace] + ); + } } - private function getExpectedNamespace(string $filePath, Scope $scope): ?string + private function getExpectedNamespace(File $phpcsFile): ?string { - $composerJsonPath = $this->findProjectComposerJson($scope); + $composerJsonPath = $this->findProjectComposerJson($phpcsFile); $composerJson = $this->loadComposerJson($composerJsonPath); $psr4 = \array_merge( @@ -61,7 +61,7 @@ private function getExpectedNamespace(string $filePath, Scope $scope): ?string foreach ($psr4 as $namespace => $directory) { $normalizedDirectory = \realpath(\dirname($composerJsonPath).'/'.$directory); - $normalizedFilePath = \realpath($filePath); + $normalizedFilePath = \realpath($phpcsFile->path); if ($normalizedDirectory && $normalizedFilePath && \str_starts_with($normalizedFilePath, $normalizedDirectory)) { $relativePath = \substr($normalizedFilePath, \strlen($normalizedDirectory) + 1); @@ -91,10 +91,9 @@ private function loadComposerJson(string $composerJsonPath): array return $decoded; } - private function findProjectComposerJson(Scope $scope): string + private function findProjectComposerJson(File $phpcsFile): string { - $projectDir = $scope->getFile(); - $currentDir = \dirname($projectDir); + $currentDir = \dirname($phpcsFile->getFilename()); while ($currentDir !== \dirname($currentDir)) { $composerJsonPath = $currentDir.'/composer.json'; diff --git a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php new file mode 100644 index 0000000..c2f5681 --- /dev/null +++ b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php @@ -0,0 +1,59 @@ +getTokens(); + $content = $tokens[$stackPtr]['content']; + $forbiddenSymbols = []; + + foreach (mb_str_split($content) as $char) { + $ascii = ord($char); + if ($ascii !== 10 && ($ascii < 32 || $ascii > 127)) { + $forbiddenSymbols[] = $ascii; + } + } + + if ($forbiddenSymbols) { + $phpcsFile->addError( + 'Use not ASCII printable symbols is forbidden: "%s"', + $stackPtr, + ErrorCodes::PROHIBITED, + [\implode(', ', $forbiddenSymbols)] + ); + } + } +} diff --git a/src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php b/src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php deleted file mode 100644 index 023edde..0000000 --- a/src/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniff.php +++ /dev/null @@ -1,91 +0,0 @@ -getTokens(); - $content = $tokens[$stackPtr]['content']; - - if ($this->containsCyrillic($content)) { - $phpcsFile->addError( - 'Use cyrillic symbols is forbidden: "%s"', - $stackPtr, - ErrorCodes::CYRILLIC_FOUND, - [\trim($content)] - ); - } - } - - private function containsCyrillic(string $content): bool - { - foreach (mb_str_split($content) as $char) { - if (array_key_exists($char, self::$cyrillicMap)) { - return true; - } - } - - return false; - } -} diff --git a/src/PhpStan/MethodCallConsistencyRule.php b/src/PhpStan/MethodCallConsistencyRule.php index ff9ca34..6af0192 100644 --- a/src/PhpStan/MethodCallConsistencyRule.php +++ b/src/PhpStan/MethodCallConsistencyRule.php @@ -64,7 +64,7 @@ private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): arra 'Method "%s::%s" is not static but called statically.', $className, $methodReflection->getName() - ),]; + ), ]; } return []; @@ -85,7 +85,7 @@ private function checkInstanceCall(Node\Expr\MethodCall $node, Scope $scope): ar 'Method "%s->%s" is static but called dynamically.', $className, $methodReflection->getName() - ),]; + ), ]; } return []; diff --git a/tests/PhpCs/FiveLab/Sniffs/Attributes/ProhibitedAttributeSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Attributes/ProhibitedAttributeSniffTest.php index 4b4c570..22c7318 100644 --- a/tests/PhpCs/FiveLab/Sniffs/Attributes/ProhibitedAttributeSniffTest.php +++ b/tests/PhpCs/FiveLab/Sniffs/Attributes/ProhibitedAttributeSniffTest.php @@ -11,7 +11,7 @@ declare(strict_types = 1); -namespace FiveLab\Component\CiRules\Tests\PhpCs\FiveLab\Sniffs\Commenting; +namespace FiveLab\Component\CiRules\Tests\PhpCs\FiveLab\Sniffs\Attributes; use FiveLab\Component\CiRules\PhpCs\FiveLab\Sniffs\Attributes\ProhibitedAttributeSniff; use FiveLab\Component\CiRules\Tests\PhpCs\FiveLab\Sniffs\SniffTestCase; diff --git a/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php new file mode 100644 index 0000000..5ee242a --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php @@ -0,0 +1,38 @@ + [ + __DIR__.'/Resources/TestService.php', + [ + 'message' => 'Namespace mismatch in file "/code/tests/PhpCs/FiveLab/Sniffs/Namespace/Resources/TestService.php". Expected namespace "FiveLab\Component\CiRules\Tests\PhpCs\FiveLab\Sniffs\Namespace\Resources", found " FiveLab\Component\CiRules\Tests".', + 'source' => 'FiveLab.Namespace.Namespace.NamespaceWrong', + ], + ], + ]; + } +} diff --git a/tests/PhpCs/FiveLab/Sniffs/Namespace/Resources/ExampleService.php b/tests/PhpCs/FiveLab/Sniffs/Namespace/Resources/ExampleService.php new file mode 100644 index 0000000..c4bfb8f --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Namespace/Resources/ExampleService.php @@ -0,0 +1,10 @@ + [ + __DIR__.'/Resources/ascii/wrong.php', + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 209, 208, 209, 209, 209, 208, 208, 209, 209, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 209, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 208, 208, 208, 208, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 209, 208, 208, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 209, 208, 208, 208, 208, 208, 208, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 209, 208, 208, 208, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 209, 209, 208, 208, 208, 208, 208, 208, 209, 208, 208, 208, 209, 208, 209, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 209, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 208, 208, 208, 209, 208, 209, 208, 208, 10"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 209, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 209, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 209, 208, 209, 209, 208, 208, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 209, 208, 208, 209"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + [ + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 209, 208, 208"', + 'source' => 'FiveLab.Strings.Ascii.Prohibited', + ], + ], + ]; + } +} diff --git a/tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php deleted file mode 100644 index af0fa78..0000000 --- a/tests/PhpCs/FiveLab/Sniffs/Strings/CyrillicSniffTest.php +++ /dev/null @@ -1,100 +0,0 @@ - [ - __DIR__.'/Resources/cyrillic/wrong.php', - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "Пространство"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "\'Файл\'"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "класс2"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "КОНСТАНТА"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "\'Значение\'"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "$переменная"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "\'Привет\'"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "Строка документации"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "Класс"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "// комментарий"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "метод"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "\'Тест\'"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "Интерфейс"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "Трейт"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - [ - //phpcs:ignore - 'message' => 'Use cyrillic symbols is forbidden: "метка"', - 'source' => 'FiveLab.Strings.Cyrillic.CyrillicFound', - ], - ], - ]; - } -} diff --git a/tests/PhpCs/FiveLab/Sniffs/Strings/Resources/cyrillic/wrong.php b/tests/PhpCs/FiveLab/Sniffs/Strings/Resources/ascii/wrong.php similarity index 100% rename from tests/PhpCs/FiveLab/Sniffs/Strings/Resources/cyrillic/wrong.php rename to tests/PhpCs/FiveLab/Sniffs/Strings/Resources/ascii/wrong.php diff --git a/tests/PhpStan/NamespaceConsistencyRuleTest.php b/tests/PhpStan/NamespaceConsistencyRuleTest.php deleted file mode 100644 index 6d744d5..0000000 --- a/tests/PhpStan/NamespaceConsistencyRuleTest.php +++ /dev/null @@ -1,41 +0,0 @@ -analyse( - [$filePathError, __DIR__.'/Resources/NamespaceConsistencyRule/ExampleService.php'], - [ - ['Namespace mismatch in file "'.$filePathError.'". Expected namespace "FiveLab\Component\CiRules\Tests\PhpStan\Resources\NamespaceConsistencyRule", found "FiveLab\Component\CiRules\Tests".', 05], - ], - ); - } -} diff --git a/tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php b/tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php deleted file mode 100644 index f667210..0000000 --- a/tests/PhpStan/Resources/NamespaceConsistencyRule/ExampleService.php +++ /dev/null @@ -1,10 +0,0 @@ - Date: Wed, 29 Jan 2025 18:24:32 +0200 Subject: [PATCH 3/6] sync with master --- .../FiveLab/Sniffs/Strings/AsciiSniff.php | 2 +- src/PhpStan/MethodCallConsistencyRule.php | 29 ++++++++++++------- .../Sniffs/Namespace/NamespaceSniffTest.php | 2 +- .../FiveLab/Sniffs/Strings/AsciiSniffTest.php | 4 +-- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php index c2f5681..3d9c1e1 100644 --- a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php +++ b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php @@ -42,7 +42,7 @@ public function process(File $phpcsFile, $stackPtr): void foreach (mb_str_split($content) as $char) { $ascii = ord($char); - if ($ascii !== 10 && ($ascii < 32 || $ascii > 127)) { + if (10 !== $ascii && (32 > $ascii || 127 < $ascii)) { $forbiddenSymbols[] = $ascii; } } diff --git a/src/PhpStan/MethodCallConsistencyRule.php b/src/PhpStan/MethodCallConsistencyRule.php index 6af0192..2aaa650 100644 --- a/src/PhpStan/MethodCallConsistencyRule.php +++ b/src/PhpStan/MethodCallConsistencyRule.php @@ -18,6 +18,7 @@ use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ReflectionProvider; use PHPStan\Rules\Rule; +use PHPStan\Rules\RuleErrorBuilder; readonly class MethodCallConsistencyRule implements Rule { @@ -60,11 +61,15 @@ private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): arra $methodReflection = $this->getMethodReflection($node, $className, $scope); if (!$methodReflection?->isStatic()) { - return [\sprintf( - 'Method "%s::%s" is not static but called statically.', - $className, - $methodReflection->getName() - ), ]; + return [ + RuleErrorBuilder::message(\sprintf( + 'Method "%s::%s" is not static but called statically.', + $className, + $methodReflection->getName() + )) + ->identifier('methodCallConsistency.error') + ->build(), + ]; } return []; @@ -81,11 +86,15 @@ private function checkInstanceCall(Node\Expr\MethodCall $node, Scope $scope): ar $methodReflection = $this->getMethodReflection($node, $className, $scope); if ($methodReflection?->isStatic()) { - return [\sprintf( - 'Method "%s->%s" is static but called dynamically.', - $className, - $methodReflection->getName() - ), ]; + return [ + RuleErrorBuilder::message(\sprintf( + 'Method "%s->%s" is static but called dynamically.', + $className, + $methodReflection->getName() + )) + ->identifier('methodCallConsistency.error') + ->build(), + ]; } return []; diff --git a/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php index 5ee242a..211acc5 100644 --- a/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php +++ b/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php @@ -23,7 +23,7 @@ protected function getSniffClass(): string return NamespaceSniff::class; } - public function provideDataSet(): array + public static function provideDataSet(): array { return [ 'wrong' => [ diff --git a/tests/PhpCs/FiveLab/Sniffs/Strings/AsciiSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Strings/AsciiSniffTest.php index c736bdc..0e52136 100644 --- a/tests/PhpCs/FiveLab/Sniffs/Strings/AsciiSniffTest.php +++ b/tests/PhpCs/FiveLab/Sniffs/Strings/AsciiSniffTest.php @@ -23,7 +23,7 @@ protected function getSniffClass(): string return AsciiSniff::class; } - public function provideDataSet(): array + public static function provideDataSet(): array { return [ 'wrong' => [ @@ -65,7 +65,7 @@ public function provideDataSet(): array 'source' => 'FiveLab.Strings.Ascii.Prohibited', ], [ - 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 208, 208, 208, 209, 208, 209, 208, 208, 10"', + 'message' => 'Use not ASCII printable symbols is forbidden: "208, 208, 208, 208, 208, 208, 209, 208, 209, 208, 208"', 'source' => 'FiveLab.Strings.Ascii.Prohibited', ], [ From bf1f5b2d268b506800f4f9db4186cb309f755c66 Mon Sep 17 00:00:00 2001 From: Anasatasiia Sh Date: Thu, 30 Jan 2025 09:47:11 +0200 Subject: [PATCH 4/6] Change method call consistency rule identifier --- src/PhpStan/MethodCallConsistencyRule.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpStan/MethodCallConsistencyRule.php b/src/PhpStan/MethodCallConsistencyRule.php index 2aaa650..10d7923 100644 --- a/src/PhpStan/MethodCallConsistencyRule.php +++ b/src/PhpStan/MethodCallConsistencyRule.php @@ -67,7 +67,7 @@ private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): arra $className, $methodReflection->getName() )) - ->identifier('methodCallConsistency.error') + ->identifier('methodCall.consistency') ->build(), ]; } @@ -92,7 +92,7 @@ private function checkInstanceCall(Node\Expr\MethodCall $node, Scope $scope): ar $className, $methodReflection->getName() )) - ->identifier('methodCallConsistency.error') + ->identifier('methodCall.consistency') ->build(), ]; } From 891b0a2699b05defb4e9edab59a0bdde32d72e48 Mon Sep 17 00:00:00 2001 From: Anasatasiia Sh Date: Thu, 30 Jan 2025 10:29:43 +0200 Subject: [PATCH 5/6] Fix code style --- src/PhpCs/FiveLab/ErrorCodes.php | 2 +- src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php | 3 +++ src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php | 4 ++-- src/PhpStan/MethodCallConsistencyRule.php | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/PhpCs/FiveLab/ErrorCodes.php b/src/PhpCs/FiveLab/ErrorCodes.php index 4f45298..2295eb2 100644 --- a/src/PhpCs/FiveLab/ErrorCodes.php +++ b/src/PhpCs/FiveLab/ErrorCodes.php @@ -30,5 +30,5 @@ final class ErrorCodes public const LINE_AFTER_NOT_ALLOWED = 'LineAfterNotAllowed'; public const LINE_BEFORE_NOT_ALLOWED = 'LineBeforeNotAllowed'; public const MISSED_CONSTANT_TYPE = 'MissedConstantType'; - public const NAMESPACE_WRONG = 'NamespaceWrong'; + public const NAMESPACE_WRONG = 'NamespaceWrong'; } diff --git a/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php b/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php index 6c26d9b..309cde9 100644 --- a/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php +++ b/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php @@ -34,11 +34,13 @@ public function process(File $phpcsFile, $stackPtr): void } $declaredNamespace = ''; + for ($i = $stackPtr + 1; $i < $endToken; $i++) { $declaredNamespace .= $tokens[$i]['content']; } $expectedNamespace = $this->getExpectedNamespace($phpcsFile); + if (\trim($declaredNamespace) !== $expectedNamespace) { $phpcsFile->addError( 'Namespace mismatch in file "%s". Expected namespace "%s", found "%s".', @@ -97,6 +99,7 @@ private function findProjectComposerJson(File $phpcsFile): string while ($currentDir !== \dirname($currentDir)) { $composerJsonPath = $currentDir.'/composer.json'; + if (\file_exists($composerJsonPath)) { return $composerJsonPath; } diff --git a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php index 3d9c1e1..0c5eebb 100644 --- a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php +++ b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php @@ -40,8 +40,8 @@ public function process(File $phpcsFile, $stackPtr): void $content = $tokens[$stackPtr]['content']; $forbiddenSymbols = []; - foreach (mb_str_split($content) as $char) { - $ascii = ord($char); + foreach (\mb_str_split($content) as $char) { + $ascii = \ord($char); if (10 !== $ascii && (32 > $ascii || 127 < $ascii)) { $forbiddenSymbols[] = $ascii; } diff --git a/src/PhpStan/MethodCallConsistencyRule.php b/src/PhpStan/MethodCallConsistencyRule.php index 10d7923..458e1ad 100644 --- a/src/PhpStan/MethodCallConsistencyRule.php +++ b/src/PhpStan/MethodCallConsistencyRule.php @@ -161,7 +161,7 @@ private function resolveClassNameForInstanceCall(Node\Expr\MethodCall $node, Sco if ($node->var instanceof Node\Expr\Variable) { $variableName = $node->var->name; - if (is_string($variableName)) { + if (\is_string($variableName)) { $type = $scope->getType(new Node\Expr\Variable($variableName)); if (\method_exists($type, 'getClassName') && $type->isObject()->yes()) { From 5e1c44ae3d0bf42809c7ac05df3fd8157a0a0c75 Mon Sep 17 00:00:00 2001 From: Anasatasiia Sh Date: Thu, 30 Jan 2025 16:03:38 +0200 Subject: [PATCH 6/6] Fix code style --- .../Sniffs/Namespace/NamespaceSniff.php | 14 +++++++-- .../FiveLab/Sniffs/Strings/AsciiSniff.php | 3 +- src/PhpStan/MethodCallConsistencyRule.php | 31 ++++++++++++++----- .../Sniffs/Namespace/NamespaceSniffTest.php | 2 +- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php b/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php index 309cde9..728884c 100644 --- a/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php +++ b/src/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniff.php @@ -24,7 +24,7 @@ public function register(): array return [T_NAMESPACE]; } - public function process(File $phpcsFile, $stackPtr): void + public function process(File $phpcsFile, mixed $stackPtr): void { $tokens = $phpcsFile->getTokens(); $endToken = $phpcsFile->findNext(T_SEMICOLON, $stackPtr); @@ -43,10 +43,10 @@ public function process(File $phpcsFile, $stackPtr): void if (\trim($declaredNamespace) !== $expectedNamespace) { $phpcsFile->addError( - 'Namespace mismatch in file "%s". Expected namespace "%s", found "%s".', + 'Expected namespace "%s", found "%s".', $stackPtr, ErrorCodes::NAMESPACE_WRONG, - [$phpcsFile->path, $expectedNamespace, $declaredNamespace] + [$expectedNamespace, $declaredNamespace] ); } } @@ -76,6 +76,14 @@ private function getExpectedNamespace(File $phpcsFile): ?string return null; } + /** + * Load composer.json + * + * @param string $composerJsonPath + * + * @return array + * @throws \JsonException + */ private function loadComposerJson(string $composerJsonPath): array { if (!\file_exists($composerJsonPath)) { diff --git a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php index 0c5eebb..a0c1e65 100644 --- a/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php +++ b/src/PhpCs/FiveLab/Sniffs/Strings/AsciiSniff.php @@ -34,7 +34,7 @@ public function register(): array ]; } - public function process(File $phpcsFile, $stackPtr): void + public function process(File $phpcsFile, mixed $stackPtr): void { $tokens = $phpcsFile->getTokens(); $content = $tokens[$stackPtr]['content']; @@ -42,6 +42,7 @@ public function process(File $phpcsFile, $stackPtr): void foreach (\mb_str_split($content) as $char) { $ascii = \ord($char); + if (10 !== $ascii && (32 > $ascii || 127 < $ascii)) { $forbiddenSymbols[] = $ascii; } diff --git a/src/PhpStan/MethodCallConsistencyRule.php b/src/PhpStan/MethodCallConsistencyRule.php index 458e1ad..438f390 100644 --- a/src/PhpStan/MethodCallConsistencyRule.php +++ b/src/PhpStan/MethodCallConsistencyRule.php @@ -17,9 +17,14 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ReflectionProvider; +use PHPStan\Rules\IdentifierRuleError; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\ShouldNotHappenException; +/** + * @implements Rule + */ readonly class MethodCallConsistencyRule implements Rule { public function __construct(private ReflectionProvider $reflectionProvider) @@ -31,11 +36,6 @@ public function getNodeType(): string return Node\Expr::class; } - /** - * {@inheritdoc} - * - * @param Node\Expr\MethodCall|Node\Expr\StaticCall $node - */ public function processNode(Node $node, Scope $scope): array { if ($node instanceof Node\Expr\StaticCall) { @@ -49,9 +49,17 @@ public function processNode(Node $node, Scope $scope): array return []; } + /** + * Check static method call + * + * @param Node\Expr\StaticCall $node + * @param Scope $scope + * + * @return list + * @throws ShouldNotHappenException + */ private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): array { - $className = $this->resolveClassNameForStaticCall($node, $scope); if (!$className) { @@ -65,7 +73,7 @@ private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): arra RuleErrorBuilder::message(\sprintf( 'Method "%s::%s" is not static but called statically.', $className, - $methodReflection->getName() + $methodReflection?->getName() )) ->identifier('methodCall.consistency') ->build(), @@ -75,6 +83,15 @@ private function checkStaticCall(Node\Expr\StaticCall $node, Scope $scope): arra return []; } + /** + * Check instance method call + * + * @param Node\Expr\MethodCall $node + * @param Scope $scope + * + * @return list + * @throws ShouldNotHappenException + */ private function checkInstanceCall(Node\Expr\MethodCall $node, Scope $scope): array { $className = $this->resolveClassNameForInstanceCall($node, $scope); diff --git a/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php index 211acc5..f918f7d 100644 --- a/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php +++ b/tests/PhpCs/FiveLab/Sniffs/Namespace/NamespaceSniffTest.php @@ -29,7 +29,7 @@ public static function provideDataSet(): array 'wrong' => [ __DIR__.'/Resources/TestService.php', [ - 'message' => 'Namespace mismatch in file "/code/tests/PhpCs/FiveLab/Sniffs/Namespace/Resources/TestService.php". Expected namespace "FiveLab\Component\CiRules\Tests\PhpCs\FiveLab\Sniffs\Namespace\Resources", found " FiveLab\Component\CiRules\Tests".', + 'message' => 'Expected namespace "FiveLab\Component\CiRules\Tests\PhpCs\FiveLab\Sniffs\Namespace\Resources", found " FiveLab\Component\CiRules\Tests".', 'source' => 'FiveLab.Namespace.Namespace.NamespaceWrong', ], ],