From c6d2339cb72f51d572918054389e04582619f16b Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 30 Oct 2021 01:31:02 +0200 Subject: [PATCH] Add InflectorSingularResolverTest (#1107) --- .../Fixture/skip_with_number.php.inc | 13 ++++++ .../ClassMethod/UnSpreadOperatorRector.php | 28 ++++++++++--- .../InflectorSingularResolver.php | 37 ++++++++++++----- .../Rector/New_/NewToMethodCallRector.php | 14 +++---- .../StaticCall/StaticCallToNewRector.php | 6 +-- .../InflectorSingularResolverTest.php | 40 +++++++++++++++++++ 6 files changed, 113 insertions(+), 25 deletions(-) create mode 100644 rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc create mode 100644 tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php diff --git a/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc new file mode 100644 index 00000000000..db1b864fa7d --- /dev/null +++ b/rules-tests/Naming/Rector/Foreach_/RenameForeachValueVariableToMatchExprVariableRector/Fixture/skip_with_number.php.inc @@ -0,0 +1,13 @@ +getAttribute(AttributeKey::SCOPE); - if ($scope instanceof Scope && $classMethod->isPublic()) { - $classReflection = $scope->getClassReflection(); - if ($classReflection->isSubclassOf('PHPUnit\Framework\TestCase')) { - return null; - } + if ($this->isInPHPUnitTestCase($classMethod)) { + return null; } $spreadParams = $this->spreadVariablesCollector->resolveFromClassMethod($classMethod); @@ -218,4 +215,23 @@ private function hasUnpackedArgs(array $args): bool return false; } + + private function isInPHPUnitTestCase(ClassMethod $classMethod): bool + { + $scope = $classMethod->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return false; + } + + if (! $classMethod->isPublic()) { + return false; + } + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return false; + } + + return $classReflection->isSubclassOf('PHPUnit\Framework\TestCase'); + } } diff --git a/rules/Naming/ExpectedNameResolver/InflectorSingularResolver.php b/rules/Naming/ExpectedNameResolver/InflectorSingularResolver.php index 93980a9b35c..c105bfed74f 100644 --- a/rules/Naming/ExpectedNameResolver/InflectorSingularResolver.php +++ b/rules/Naming/ExpectedNameResolver/InflectorSingularResolver.php @@ -7,12 +7,15 @@ use Doctrine\Inflector\Inflector; use Nette\Utils\Strings; +/** + * @see \Rector\Core\Tests\Naming\ExpectedNameResolver\InflectorSingularResolverTest + */ final class InflectorSingularResolver { /** * @var array */ - private const SINGULAR_VERB = [ + private const SINGULARIZE_MAP = [ 'news' => 'new', ]; @@ -20,7 +23,7 @@ final class InflectorSingularResolver * @var string * @see https://regex101.com/r/lbQaGC/3 */ - private const CAMELCASE_REGEX = '#(?([a-z]+|[A-Z]{1,}[a-z]+|_))#'; + private const CAMELCASE_REGEX = '#(?([a-z\d]+|[A-Z\d]{1,}[a-z\d]+|_))#'; /** * @var string @@ -45,8 +48,9 @@ public function resolve(string $currentName): string return Strings::substring($currentName, 0, -strlen($matchBy['by'])); } - if (array_key_exists($currentName, self::SINGULAR_VERB)) { - return self::SINGULAR_VERB[$currentName]; + $resolvedValue = $this->resolveSingularizeMap($currentName); + if ($resolvedValue !== null) { + return $resolvedValue; } if (str_starts_with($currentName, self::SINGLE)) { @@ -59,11 +63,7 @@ public function resolve(string $currentName): string $singularValueVarName .= $this->inflector->singularize($camelCase['camelcase']); } - if ($singularValueVarName === '') { - return $currentName; - } - - if ($singularValueVarName === '_') { + if (in_array($singularValueVarName, ['', '_'], true)) { return $currentName; } @@ -81,4 +81,23 @@ public function resolve(string $currentName): string return $currentName; } + + private function resolveSingularizeMap(string $currentName): string|null + { + foreach (self::SINGULARIZE_MAP as $plural => $singular) { + if ($currentName === $plural) { + return $singular; + } + + if (Strings::match($currentName, '#' . ucfirst($plural) . '#')) { + return Strings::replace($currentName, '#' . ucfirst($plural) . '#', ucfirst($singular)); + } + + if (Strings::match($currentName, '#' . $plural . '#')) { + return Strings::replace($currentName, '#' . $plural . '#', $singular); + } + } + + return null; + } } diff --git a/rules/Transform/Rector/New_/NewToMethodCallRector.php b/rules/Transform/Rector/New_/NewToMethodCallRector.php index 2eb813b6b2b..13de43d01fb 100644 --- a/rules/Transform/Rector/New_/NewToMethodCallRector.php +++ b/rules/Transform/Rector/New_/NewToMethodCallRector.php @@ -90,12 +90,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - foreach ($this->newsToMethodCalls as $newsToMethodCall) { - if (! $this->isObjectType($node, $newsToMethodCall->getNewObjectType())) { + foreach ($this->newsToMethodCalls as $newToMethodCalls) { + if (! $this->isObjectType($node, $newToMethodCalls->getNewObjectType())) { continue; } - $serviceObjectType = $newsToMethodCall->getServiceObjectType(); + $serviceObjectType = $newToMethodCalls->getServiceObjectType(); $className = $node->getAttribute(AttributeKey::CLASS_NAME); if ($className === $serviceObjectType->getClassName()) { continue; @@ -106,17 +106,17 @@ public function refactor(Node $node): ?Node $propertyName = $this->getExistingFactoryPropertyName( $class, - $newsToMethodCall->getServiceObjectType() + $newToMethodCalls->getServiceObjectType() ); if ($propertyName === null) { - $serviceObjectType = $newsToMethodCall->getServiceObjectType(); + $serviceObjectType = $newToMethodCalls->getServiceObjectType(); $propertyName = $this->classNaming->getShortName($serviceObjectType->getClassName()); $propertyName = lcfirst($propertyName); $propertyMetadata = new PropertyMetadata( $propertyName, - $newsToMethodCall->getServiceObjectType(), + $newToMethodCalls->getServiceObjectType(), Class_::MODIFIER_PRIVATE ); $this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata); @@ -124,7 +124,7 @@ public function refactor(Node $node): ?Node $propertyFetch = new PropertyFetch(new Variable('this'), $propertyName); - return new MethodCall($propertyFetch, $newsToMethodCall->getServiceMethod(), $node->args); + return new MethodCall($propertyFetch, $newToMethodCalls->getServiceMethod(), $node->args); } return $node; diff --git a/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php b/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php index 4e9f545245e..95e8fdbe0f9 100644 --- a/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php +++ b/rules/Transform/Rector/StaticCall/StaticCallToNewRector.php @@ -76,12 +76,12 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - foreach ($this->staticCallsToNews as $staticCallToNews) { - if (! $this->isName($node->class, $staticCallToNews->getClass())) { + foreach ($this->staticCallsToNews as $staticCallsToNew) { + if (! $this->isName($node->class, $staticCallsToNew->getClass())) { continue; } - if (! $this->isName($node->name, $staticCallToNews->getMethod())) { + if (! $this->isName($node->name, $staticCallsToNew->getMethod())) { continue; } diff --git a/tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php b/tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php new file mode 100644 index 00000000000..5296172f6c4 --- /dev/null +++ b/tests/Naming/ExpectedNameResolver/InflectorSingularResolverTest.php @@ -0,0 +1,40 @@ +boot(); + $this->inflectorSingularResolver = $this->getService(InflectorSingularResolver::class); + } + + /** + * @dataProvider provideData() + */ + public function testResolveForForeach(string $currentName, string $expectedSingularName): void + { + $singularValue = $this->inflectorSingularResolver->resolve($currentName); + $this->assertSame($expectedSingularName, $singularValue); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + yield ['psr4NamespacesToPaths', 'psr4NamespaceToPath']; + yield ['nestedNews', 'nestedNew']; + yield ['news', 'new']; + yield ['argsOrOptions', 'argOrOption']; + } +}