From 53648cdf808df7f39072504b092697b58eb9a38c Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 9 Jun 2026 22:24:08 +0100 Subject: [PATCH] remove nette/utils and inline string regex operations to lower deps --- README.md | 95 ++++++++++++++++++- composer.json | 1 - src/DocBlock/UselessDocBlockCleaner.php | 6 +- ...oveMethodNameDuplicateDescriptionFixer.php | 4 +- .../RemovePHPStormAnnotationFixer.php | 4 +- ...vePropertyVariableNameDescriptionFixer.php | 3 +- .../ParamReturnAndVarTagMalformsFixer.php | 6 +- .../MalformWorker/DeadParamMalformWorker.php | 4 +- .../MalformWorker/InlineVarMalformWorker.php | 4 +- .../InlineVariableDocBlockMalformWorker.php | 8 +- .../MissingParamNameMalformWorker.php | 14 +-- .../MissingVarNameMalformWorker.php | 6 +- .../ParamNameReferenceMalformWorker.php | 4 +- .../ParamNameTypoMalformWorker.php | 6 +- .../SuperfluousReturnNameMalformWorker.php | 8 +- .../SuperfluousVarNameMalformWorker.php | 8 +- .../SwitchedTypeAndNameMalformWorker.php | 6 +- src/Utils/Regex.php | 44 +++++++++ tests/Utils/RegexTest.php | 51 ++++++++++ 19 files changed, 234 insertions(+), 48 deletions(-) create mode 100644 src/Utils/Regex.php create mode 100644 tests/Utils/RegexTest.php diff --git a/README.md b/README.md index c31ce2c7..02bd31cb 100644 --- a/README.md +++ b/README.md @@ -127,10 +127,12 @@ Each chain method call must be on own line ## ParamReturnAndVarTagMalformsFixer -Fixes @param, @return, `@var` and inline `@var` annotations broken formats +Fixes `@param`, `@return`, `@var` and inline `@var` annotations broken formats. This single rule covers a wide range of docblock malforms: - class: [`Symplify\CodingStandard\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer`](../src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php) +**Add a missing `@param` variable name** + ```diff /** - * @param string @@ -141,6 +143,97 @@ Fixes @param, @return, `@var` and inline `@var` annotations broken formats } ``` +**Reorder switched type and variable name** + +```diff + /** +- * @param $a string +- * @param $b string|null ++ * @param string $a ++ * @param string|null $b + */ + function test($a, string $b = null): string + { + } +``` + +**Remove a dead `@param` line that has only a name and no type** + +```diff + /** + * @param string $name +- * @param $age + */ + function withDeadParam(string $name, $age) + { + } +``` + +**Fix a typo in the `@param` variable name to match the real argument** + +```diff + /** + * @param string $one +- * @param string $twoTypo ++ * @param string $two + */ + function anotherFunction(string $one, string $two) + { + } +``` + +**Remove the reference `&` from a `@param` name** + +```diff + /** +- * @param string &$name ++ * @param string $name + */ + function paramReference($name) + { + } +``` + +**Remove a superfluous variable name from `@return`** + +```diff + /** +- * @return int $value ++ * @return int + */ + function function1(): int + { + } +``` + +**Remove a superfluous variable name from a property `@var`** + +```diff + /** +- * @var string $property ++ * @var string + */ + private $property; +``` + +**Add a missing variable name to an inline `@var`** + +```diff +-/** @var int */ ++/** @var int $value */ + $value = 1000; +``` + +**Normalize a malformed inline `@var` (single asterisk, switched name/type)** + +```diff +-/* @var $variable int */ ++/** @var int $variable */ + $variable = 5; +``` + +It also handles the `@phpstan-` and `@psalm-` prefixed variants of these tags. +
## RemovePropertyVariableNameDescriptionFixer diff --git a/composer.json b/composer.json index 07544ea6..b3b65d07 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,6 @@ "license": "MIT", "require": { "php": ">=8.4", - "nette/utils": "^4.1", "friendsofphp/php-cs-fixer": "^3.95.4" }, "require-dev": { diff --git a/src/DocBlock/UselessDocBlockCleaner.php b/src/DocBlock/UselessDocBlockCleaner.php index a7ceb77b..36265866 100644 --- a/src/DocBlock/UselessDocBlockCleaner.php +++ b/src/DocBlock/UselessDocBlockCleaner.php @@ -4,8 +4,8 @@ namespace Symplify\CodingStandard\DocBlock; -use Nette\Utils\Strings; use PhpCsFixer\Tokenizer\Token; +use Symplify\CodingStandard\Utils\Regex; final class UselessDocBlockCleaner { @@ -72,7 +72,7 @@ public function clearDocTokenContent(Token $currentToken, ?string $classLikeName } foreach (self::CLEANING_REGEXES as $cleaningRegex) { - $commentLine = Strings::replace($commentLine, $cleaningRegex); + $commentLine = Regex::replace($commentLine, $cleaningRegex); } $cleanedCommentLines[$key] = $commentLine; @@ -90,7 +90,7 @@ public function clearDocTokenContent(Token $currentToken, ?string $classLikeName $commentText = implode("\n", $cleanedCommentLines); // run multilines regex on final result - return Strings::replace($commentText, self::DOCTRINE_GENERATED_COMMENT_REGEX); + return Regex::replace($commentText, self::DOCTRINE_GENERATED_COMMENT_REGEX); } /** diff --git a/src/Fixer/Annotation/RemoveMethodNameDuplicateDescriptionFixer.php b/src/Fixer/Annotation/RemoveMethodNameDuplicateDescriptionFixer.php index 065fd2a2..3fcc29e0 100644 --- a/src/Fixer/Annotation/RemoveMethodNameDuplicateDescriptionFixer.php +++ b/src/Fixer/Annotation/RemoveMethodNameDuplicateDescriptionFixer.php @@ -4,7 +4,6 @@ namespace Symplify\CodingStandard\Fixer\Annotation; -use Nette\Utils\Strings; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Tokenizer\Token; @@ -13,6 +12,7 @@ use Symplify\CodingStandard\Fixer\AbstractSymplifyFixer; use Symplify\CodingStandard\Fixer\Naming\MethodNameResolver; use Symplify\CodingStandard\TokenRunner\Traverser\TokenReverser; +use Symplify\CodingStandard\Utils\Regex; /** * @see \Symplify\CodingStandard\Tests\Fixer\Annotation\RemoveMethodNameDuplicateDescriptionFixer\RemoveMethodNameDuplicateDescriptionFixerTest @@ -70,7 +70,7 @@ public function fix(SplFileInfo $fileInfo, Tokens $tokens): void $docblockLines = explode("\n", $originalDocContent); foreach ($docblockLines as $key => $docblockLine) { - $spacelessDocblockLine = Strings::replace($docblockLine, '#[\s\n]+#', ''); + $spacelessDocblockLine = Regex::replace($docblockLine, '#[\s\n]+#', ''); if (strtolower($spacelessDocblockLine) !== strtolower('*' . $methodName)) { continue; } diff --git a/src/Fixer/Annotation/RemovePHPStormAnnotationFixer.php b/src/Fixer/Annotation/RemovePHPStormAnnotationFixer.php index 83bafa4c..905415da 100644 --- a/src/Fixer/Annotation/RemovePHPStormAnnotationFixer.php +++ b/src/Fixer/Annotation/RemovePHPStormAnnotationFixer.php @@ -4,7 +4,6 @@ namespace Symplify\CodingStandard\Fixer\Annotation; -use Nette\Utils\Strings; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Tokenizer\Token; @@ -12,6 +11,7 @@ use SplFileInfo; use Symplify\CodingStandard\Fixer\AbstractSymplifyFixer; use Symplify\CodingStandard\TokenRunner\Traverser\TokenReverser; +use Symplify\CodingStandard\Utils\Regex; /** * @see \Symplify\CodingStandard\Tests\Fixer\Annotation\RemovePHPStormAnnotationFixer\RemovePHPStormAnnotationFixerTest @@ -56,7 +56,7 @@ public function fix(SplFileInfo $fileInfo, Tokens $tokens): void } $originalDocContent = $token->getContent(); - $cleanedDocContent = Strings::replace($originalDocContent, self::CREATED_BY_PHPSTORM_DOC_REGEX, ''); + $cleanedDocContent = Regex::replace($originalDocContent, self::CREATED_BY_PHPSTORM_DOC_REGEX, ''); if ($cleanedDocContent !== '') { continue; } diff --git a/src/Fixer/Annotation/RemovePropertyVariableNameDescriptionFixer.php b/src/Fixer/Annotation/RemovePropertyVariableNameDescriptionFixer.php index 49844f78..024ed7f5 100644 --- a/src/Fixer/Annotation/RemovePropertyVariableNameDescriptionFixer.php +++ b/src/Fixer/Annotation/RemovePropertyVariableNameDescriptionFixer.php @@ -4,7 +4,6 @@ namespace Symplify\CodingStandard\Fixer\Annotation; -use Nette\Utils\Strings; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; use PhpCsFixer\Tokenizer\Token; @@ -90,7 +89,7 @@ public function fix(SplFileInfo $fileInfo, Tokens $tokens): void } // remove last x characters - $docblockLine = Strings::substring($docblockLine, 0, -strlen(' ' . $propertyName)); + $docblockLine = mb_substr($docblockLine, 0, -strlen(' ' . $propertyName)); $hasChanged = true; $docblockLines[$key] = rtrim($docblockLine); diff --git a/src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php b/src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php index b8ce0148..fd11ef7c 100644 --- a/src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php +++ b/src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php @@ -4,7 +4,6 @@ namespace Symplify\CodingStandard\Fixer\Commenting; -use Nette\Utils\Strings; use Override; use PhpCsFixer\FixerDefinition\FixerDefinition; use PhpCsFixer\FixerDefinition\FixerDefinitionInterface; @@ -24,6 +23,7 @@ use Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker\SuperfluousVarNameMalformWorker; use Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker\SwitchedTypeAndNameMalformWorker; use Symplify\CodingStandard\TokenRunner\Traverser\TokenReverser; +use Symplify\CodingStandard\Utils\Regex; /** * @see \Symplify\CodingStandard\Tests\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer\ParamReturnAndVarTagMalformsFixerTest @@ -113,7 +113,7 @@ public function fix(SplFileInfo $fileInfo, Tokens $tokens): void } $docContent = $token->getContent(); - if (! Strings::match($docContent, self::TYPE_ANNOTATION_REGEX)) { + if (! Regex::match($docContent, self::TYPE_ANNOTATION_REGEX)) { continue; } @@ -154,6 +154,6 @@ public function getPriority(): int private function isEmptyDocBlock(string $docContent): bool { - return Strings::replace($docContent, '#/\*\*|\*/|\*|\s#', '') === ''; + return Regex::replace($docContent, '#/\*\*|\*/|\*|\s#', '') === ''; } } diff --git a/src/TokenRunner/DocBlock/MalformWorker/DeadParamMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/DeadParamMalformWorker.php index 57c65286..f3d872ab 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/DeadParamMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/DeadParamMalformWorker.php @@ -4,11 +4,11 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\DocBlock\DocBlock; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; /** * Removes dead param annotation lines that only contain a variable name and no type, @@ -29,7 +29,7 @@ public function work(string $docContent, Tokens $tokens, int $position): string $docBlock = new DocBlock($docContent); foreach ($docBlock->getLines() as $line) { - if (! Strings::match($line->getContent(), self::PARAM_WITHOUT_TYPE_REGEX)) { + if (! Regex::match($line->getContent(), self::PARAM_WITHOUT_TYPE_REGEX)) { continue; } diff --git a/src/TokenRunner/DocBlock/MalformWorker/InlineVarMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/InlineVarMalformWorker.php index cd3ec362..29019015 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/InlineVarMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/InlineVarMalformWorker.php @@ -4,10 +4,10 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class InlineVarMalformWorker implements MalformWorkerInterface { @@ -28,6 +28,6 @@ public function work(string $docContent, Tokens $tokens, int $position): string return $docContent; } - return Strings::replace($docContent, self::SINGLE_ASTERISK_START_REGEX, '/**$1'); + return Regex::replace($docContent, self::SINGLE_ASTERISK_START_REGEX, '/**$1'); } } diff --git a/src/TokenRunner/DocBlock/MalformWorker/InlineVariableDocBlockMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/InlineVariableDocBlockMalformWorker.php index af822b39..294bef0e 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/InlineVariableDocBlockMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/InlineVariableDocBlockMalformWorker.php @@ -4,10 +4,10 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class InlineVariableDocBlockMalformWorker implements MalformWorkerInterface { @@ -41,13 +41,13 @@ public function work(string $docContent, Tokens $tokens, int $position): string } // asterisk start - $docContent = Strings::replace($docContent, self::SINGLE_ASTERISK_START_REGEX, '/**$1'); + $docContent = Regex::replace($docContent, self::SINGLE_ASTERISK_START_REGEX, '/**$1'); // inline - $docContent = Strings::replace($docContent, self::SPACE_REGEX, ' '); + $docContent = Regex::replace($docContent, self::SPACE_REGEX, ' '); // remove asterisk leftover - return Strings::replace($docContent, self::ASTERISK_LEFTOVERS_REGEX, '$1'); + return Regex::replace($docContent, self::ASTERISK_LEFTOVERS_REGEX, '$1'); } /** diff --git a/src/TokenRunner/DocBlock/MalformWorker/MissingParamNameMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/MissingParamNameMalformWorker.php index 208ed8ac..446bb2cf 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/MissingParamNameMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/MissingParamNameMalformWorker.php @@ -4,13 +4,13 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\DocBlock\DocBlock; use PhpCsFixer\DocBlock\Line; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenAnalyzer\DocblockRelatedParamNamesResolver; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final readonly class MissingParamNameMalformWorker implements MalformWorkerInterface { @@ -64,7 +64,7 @@ private function filterOutExistingParamNames(string $docContent, array $function { foreach ($functionArgumentNames as $key => $functionArgumentName) { $pattern = '# ' . preg_quote($functionArgumentName, '#') . '\b#'; - if (Strings::match($docContent, $pattern)) { + if (Regex::match($docContent, $pattern)) { unset($functionArgumentNames[$key]); } } @@ -116,11 +116,11 @@ private function shouldSkipLine(Line $line): bool } // already has a param name - if (Strings::match($line->getContent(), self::PARAM_WITH_NAME_REGEX)) { + if (Regex::match($line->getContent(), self::PARAM_WITH_NAME_REGEX)) { return true; } - $match = Strings::match($line->getContent(), self::PARAM_WITHOUT_NAME_REGEX); + $match = Regex::match($line->getContent(), self::PARAM_WITHOUT_NAME_REGEX); return $match === null; } @@ -130,12 +130,12 @@ private function createNewLineContent(string $newArgumentName, Line $line): stri $missingDollarSignPattern = '#(@param\s+([\w\|\[\]\\\\]+\s)?)(' . ltrim($newArgumentName, '$') . ')#'; // missing \$ case - possibly own worker - if (Strings::match($line->getContent(), $missingDollarSignPattern)) { - return Strings::replace($line->getContent(), $missingDollarSignPattern, '$1$$3'); + if (Regex::match($line->getContent(), $missingDollarSignPattern)) { + return Regex::replace($line->getContent(), $missingDollarSignPattern, '$1$$3'); } $replacement = '@param $1 ' . $newArgumentName . '$2' . "\n"; - return Strings::replace($line->getContent(), self::PARAM_WITHOUT_NAME_REGEX, $replacement); + return Regex::replace($line->getContent(), self::PARAM_WITHOUT_NAME_REGEX, $replacement); } } diff --git a/src/TokenRunner/DocBlock/MalformWorker/MissingVarNameMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/MissingVarNameMalformWorker.php index 72ab2115..49e2d480 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/MissingVarNameMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/MissingVarNameMalformWorker.php @@ -4,10 +4,10 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class MissingVarNameMalformWorker implements MalformWorkerInterface { @@ -21,7 +21,7 @@ final class MissingVarNameMalformWorker implements MalformWorkerInterface */ public function work(string $docContent, Tokens $tokens, int $position): string { - if (! Strings::match($docContent, self::VAR_WITHOUT_NAME_REGEX)) { + if (! Regex::match($docContent, self::VAR_WITHOUT_NAME_REGEX)) { return $docContent; } @@ -30,7 +30,7 @@ public function work(string $docContent, Tokens $tokens, int $position): string return $docContent; } - return Strings::replace( + return Regex::replace( $docContent, self::VAR_WITHOUT_NAME_REGEX, static fn (array $match): string => $match['open'] . $match['type'] . ' ' . $nextVariableToken->getContent() . $match['close'] diff --git a/src/TokenRunner/DocBlock/MalformWorker/ParamNameReferenceMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/ParamNameReferenceMalformWorker.php index 650ad112..26c66909 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/ParamNameReferenceMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/ParamNameReferenceMalformWorker.php @@ -4,10 +4,10 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class ParamNameReferenceMalformWorker implements MalformWorkerInterface { @@ -21,7 +21,7 @@ final class ParamNameReferenceMalformWorker implements MalformWorkerInterface */ public function work(string $docContent, Tokens $tokens, int $position): string { - return Strings::replace( + return Regex::replace( $docContent, self::PARAM_NAME_REGEX, static fn ($match): string => $match['param'] . $match['paramName'] diff --git a/src/TokenRunner/DocBlock/MalformWorker/ParamNameTypoMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/ParamNameTypoMalformWorker.php index 29470043..b17f8fb5 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/ParamNameTypoMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/ParamNameTypoMalformWorker.php @@ -4,13 +4,13 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\DocBlock\Annotation; use PhpCsFixer\DocBlock\DocBlock; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenAnalyzer\DocblockRelatedParamNamesResolver; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final readonly class ParamNameTypoMalformWorker implements MalformWorkerInterface { @@ -68,7 +68,7 @@ private function getParamNames(string $docContent): array $paramNames = []; foreach ($paramAnnotations as $paramAnnotation) { - $match = Strings::match($paramAnnotation->getContent(), self::PARAM_NAME_REGEX); + $match = Regex::match($paramAnnotation->getContent(), self::PARAM_NAME_REGEX); if (isset($match['paramName'])) { // skip callables, as they contain nested params if (isset($match['callable']) && $match['callable'] === 'callable') { @@ -111,7 +111,7 @@ private function fixTypos(array $argumentNames, array $missArgumentNames, array $typoName = $paramNames[$key]; $replacePattern = '#@param(.*?)(' . preg_quote($typoName, '#') . '\b)#'; - $docContent = Strings::replace($docContent, $replacePattern, static function (array $matched) use ($argumentName, &$replacedParams) { + $docContent = Regex::replace($docContent, $replacePattern, static function (array $matched) use ($argumentName, &$replacedParams) { $paramName = $matched[2]; // 2. If the PHPDoc $paramName is one of the existing $argumentNames and has not already been replaced, it will be deferred diff --git a/src/TokenRunner/DocBlock/MalformWorker/SuperfluousReturnNameMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/SuperfluousReturnNameMalformWorker.php index 716bebbb..386099ef 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/SuperfluousReturnNameMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/SuperfluousReturnNameMalformWorker.php @@ -4,11 +4,11 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\DocBlock\DocBlock; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class SuperfluousReturnNameMalformWorker implements MalformWorkerInterface { @@ -38,7 +38,7 @@ public function work(string $docContent, Tokens $tokens, int $position): string $lines = $docBlock->getLines(); foreach ($lines as $line) { - $match = Strings::match($line->getContent(), self::RETURN_VARIABLE_NAME_REGEX); + $match = Regex::match($line->getContent(), self::RETURN_VARIABLE_NAME_REGEX); if ($match === null) { continue; } @@ -47,7 +47,7 @@ public function work(string $docContent, Tokens $tokens, int $position): string continue; } - $newLineContent = Strings::replace( + $newLineContent = Regex::replace( $line->getContent(), self::RETURN_VARIABLE_NAME_REGEX, static function (array $match) { @@ -76,6 +76,6 @@ private function shouldSkip(array $match, string $content): bool } // has multiple return values? "@return array $one, $two" - return count(Strings::matchAll($content, self::VARIABLE_NAME_REGEX)) >= 2; + return count(Regex::matchAll($content, self::VARIABLE_NAME_REGEX)) >= 2; } } diff --git a/src/TokenRunner/DocBlock/MalformWorker/SuperfluousVarNameMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/SuperfluousVarNameMalformWorker.php index 78b05289..e26b7cb4 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/SuperfluousVarNameMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/SuperfluousVarNameMalformWorker.php @@ -4,11 +4,11 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\DocBlock\DocBlock; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class SuperfluousVarNameMalformWorker implements MalformWorkerInterface { @@ -35,12 +35,12 @@ public function work(string $docContent, Tokens $tokens, int $position): string $lines = $docBlock->getLines(); foreach ($lines as $line) { - $match = Strings::match($line->getContent(), self::VAR_VARIABLE_NAME_REGEX); + $match = Regex::match($line->getContent(), self::VAR_VARIABLE_NAME_REGEX); if ($match === null) { continue; } - $newLineContent = Strings::replace( + $newLineContent = Regex::replace( $line->getContent(), self::VAR_VARIABLE_NAME_REGEX, static function (array $match): string { @@ -49,7 +49,7 @@ static function (array $match): string { $replacement .= $match['type']; } - if (Strings::match($match['propertyName'], self::THIS_VARIABLE_REGEX)) { + if (Regex::match($match['propertyName'], self::THIS_VARIABLE_REGEX)) { return $match['tag'] . ' self'; } diff --git a/src/TokenRunner/DocBlock/MalformWorker/SwitchedTypeAndNameMalformWorker.php b/src/TokenRunner/DocBlock/MalformWorker/SwitchedTypeAndNameMalformWorker.php index 9e36caf8..ac6d9db7 100644 --- a/src/TokenRunner/DocBlock/MalformWorker/SwitchedTypeAndNameMalformWorker.php +++ b/src/TokenRunner/DocBlock/MalformWorker/SwitchedTypeAndNameMalformWorker.php @@ -4,11 +4,11 @@ namespace Symplify\CodingStandard\TokenRunner\DocBlock\MalformWorker; -use Nette\Utils\Strings; use PhpCsFixer\DocBlock\DocBlock; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; use Symplify\CodingStandard\TokenRunner\Contract\DocBlock\MalformWorkerInterface; +use Symplify\CodingStandard\Utils\Regex; final class SwitchedTypeAndNameMalformWorker implements MalformWorkerInterface { @@ -27,7 +27,7 @@ public function work(string $docContent, Tokens $tokens, int $position): string $lines = $docBlock->getLines(); foreach ($lines as $line) { // $value is first, instead of type is first - $match = Strings::match($line->getContent(), self::NAME_THEN_TYPE_REGEX); + $match = Regex::match($line->getContent(), self::NAME_THEN_TYPE_REGEX); if ($match === null) { continue; } @@ -45,7 +45,7 @@ public function work(string $docContent, Tokens $tokens, int $position): string continue; } - $newLine = Strings::replace($line->getContent(), self::NAME_THEN_TYPE_REGEX, '@$1$2$5$4$3'); + $newLine = Regex::replace($line->getContent(), self::NAME_THEN_TYPE_REGEX, '@$1$2$5$4$3'); $line->setContent($newLine); } diff --git a/src/Utils/Regex.php b/src/Utils/Regex.php new file mode 100644 index 00000000..30577e4f --- /dev/null +++ b/src/Utils/Regex.php @@ -0,0 +1,44 @@ +|null + */ + public static function match(string $subject, string $pattern): ?array + { + $matches = []; + if (preg_match($pattern, $subject, $matches) === 1) { + return $matches; + } + + return null; + } + + /** + * @return array> + */ + public static function matchAll(string $subject, string $pattern): array + { + $matches = []; + preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER); + + return $matches; + } + + public static function replace(string $subject, string $pattern, string|callable $replacement = ''): string + { + if (is_callable($replacement)) { + return (string) preg_replace_callback($pattern, $replacement, $subject); + } + + return (string) preg_replace($pattern, $replacement, $subject); + } +} diff --git a/tests/Utils/RegexTest.php b/tests/Utils/RegexTest.php new file mode 100644 index 00000000..eb971947 --- /dev/null +++ b/tests/Utils/RegexTest.php @@ -0,0 +1,51 @@ +\w+)\s+\$(?\w+)#'); + + $this->assertNotNull($match); + $this->assertSame('string', $match['type']); + $this->assertSame('value', $match['name']); + } + + public function testMatchReturnsNullOnNoMatch(): void + { + $this->assertNull(Regex::match('nothing here', '#@param#')); + } + + public function testMatchAllReturnsSetOrder(): void + { + $matches = Regex::matchAll('$a $b $c', '#\$(?\w)#'); + + $this->assertCount(3, $matches); + $this->assertSame('a', $matches[0]['name']); + $this->assertSame('c', $matches[2]['name']); + } + + public function testReplaceWithString(): void + { + $this->assertSame('x-x', Regex::replace('1-2', '#\d#', 'x')); + } + + public function testReplaceWithDefaultRemovesMatch(): void + { + $this->assertSame('abc', Regex::replace('a1b2c3', '#\d#')); + } + + public function testReplaceWithCallable(): void + { + $result = Regex::replace('a1b', '#\d#', static fn (array $match): string => '[' . $match[0] . ']'); + + $this->assertSame('a[1]b', $result); + } +}