diff --git a/extension.neon b/extension.neon index e1bb695..badf38b 100644 --- a/extension.neon +++ b/extension.neon @@ -51,10 +51,14 @@ services: tags: - phpstan.broker.expressionTypeResolverExtension - - factory: CakeDC\PHPStan\Type\BaseTraitExpressionTypeResolverExtension(Cake\ORM\Locator\LocatorAwareTrait, fetchTable, %s\Model\Table\%sTable, defaultTable) + factory: CakeDC\PHPStan\Type\BaseTraitExpressionTypeResolverExtension(Cake\ORM\Locator\LocatorAwareTrait, fetchTable, %s\Model\Table\%sTable, @CakeDC\PHPStan\Utility\CakeNameRegistry, defaultTable) tags: - phpstan.broker.expressionTypeResolverExtension - class: CakeDC\PHPStan\Type\TypeFactoryBuildDynamicReturnTypeExtension tags: - phpstan.broker.dynamicStaticMethodReturnTypeExtension + - + class: CakeDC\PHPStan\Utility\CakeNameRegistry + arguments: + appNamespace: %cakeDC.appNamespace% diff --git a/rules.neon b/rules.neon index 28af64d..aaccac6 100644 --- a/rules.neon +++ b/rules.neon @@ -1,5 +1,6 @@ parameters: cakeDC: + appNamespace: App addAssociationExistsTableClassRule: true addAssociationMatchOptionsTypesRule: true addBehaviorExistsClassRule: true @@ -13,6 +14,7 @@ parameters: disallowDebugStaticCallRule: true parametersSchema: cakeDC: structure([ + appNamespace: string() addAssociationExistsTableClassRule: anyOf(bool(), arrayOf(bool())) addAssociationMatchOptionsTypesRule: anyOf(bool(), arrayOf(bool())) addBehaviorExistsClassRule: anyOf(bool(), arrayOf(bool())) diff --git a/src/Rule/Controller/LoadComponentExistsClassRule.php b/src/Rule/Controller/LoadComponentExistsClassRule.php index bfbc1a5..203d4ad 100644 --- a/src/Rule/Controller/LoadComponentExistsClassRule.php +++ b/src/Rule/Controller/LoadComponentExistsClassRule.php @@ -38,12 +38,22 @@ class LoadComponentExistsClassRule extends LoadObjectExistsCakeClassRule 'load', ]; + private readonly CakeNameRegistry $cakeNameRegistry; + + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) + { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); + } + /** * @inheritDoc */ protected function getTargetClassName(string $name): ?string { - return CakeNameRegistry::getComponentClassName($name); + return $this->cakeNameRegistry->getComponentClassName($name); } /** diff --git a/src/Rule/Mailer/GetMailerExistsClassRule.php b/src/Rule/Mailer/GetMailerExistsClassRule.php index 623804e..9a67ec4 100644 --- a/src/Rule/Mailer/GetMailerExistsClassRule.php +++ b/src/Rule/Mailer/GetMailerExistsClassRule.php @@ -29,6 +29,16 @@ class GetMailerExistsClassRule implements Rule */ protected string $identifier = 'cake.getMailer.existClass'; + private readonly CakeNameRegistry $cakeNameRegistry; + + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) + { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); + } + /** * @inheritDoc */ @@ -66,7 +76,7 @@ public function processNode(Node $node, Scope $scope): array } $reflection = $callerType->getClassReflection(); - if (CakeNameRegistry::getMailerClassName($value->value) !== null) { + if ($this->cakeNameRegistry->getMailerClassName($value->value) !== null) { return []; } diff --git a/src/Rule/Model/AddAssociationExistsTableClassRule.php b/src/Rule/Model/AddAssociationExistsTableClassRule.php index 9795743..2ef394b 100644 --- a/src/Rule/Model/AddAssociationExistsTableClassRule.php +++ b/src/Rule/Model/AddAssociationExistsTableClassRule.php @@ -42,12 +42,22 @@ class AddAssociationExistsTableClassRule extends LoadObjectExistsCakeClassRule */ protected array $associationCollectionMethods = ['load']; + private readonly CakeNameRegistry $cakeNameRegistry; + + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) + { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); + } + /** * @inheritDoc */ protected function getTargetClassName(string $name): ?string { - return CakeNameRegistry::getTableClassName($name); + return $this->cakeNameRegistry->getTableClassName($name); } /** diff --git a/src/Rule/Model/AddBehaviorExistsClassRule.php b/src/Rule/Model/AddBehaviorExistsClassRule.php index 4272ad3..c8aebf5 100644 --- a/src/Rule/Model/AddBehaviorExistsClassRule.php +++ b/src/Rule/Model/AddBehaviorExistsClassRule.php @@ -38,12 +38,22 @@ class AddBehaviorExistsClassRule extends LoadObjectExistsCakeClassRule 'load', ]; + private readonly CakeNameRegistry $cakeNameRegistry; + + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) + { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); + } + /** * @inheritDoc */ protected function getTargetClassName(string $name): ?string { - return CakeNameRegistry::getBehaviorClassName($name); + return $this->cakeNameRegistry->getBehaviorClassName($name); } /** diff --git a/src/Traits/BaseCakeRegistryReturnTrait.php b/src/Traits/BaseCakeRegistryReturnTrait.php index 540ff6d..80ae240 100644 --- a/src/Traits/BaseCakeRegistryReturnTrait.php +++ b/src/Traits/BaseCakeRegistryReturnTrait.php @@ -13,7 +13,6 @@ namespace CakeDC\PHPStan\Traits; -use CakeDC\PHPStan\Utility\CakeNameRegistry; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; @@ -75,7 +74,7 @@ public function isMethodSupported(MethodReflection $methodReflection): bool */ protected function getCakeType(string $baseName): ObjectType { - $className = CakeNameRegistry::getClassName($baseName, $this->namespaceFormat); + $className = $this->cakeNameRegistry->getClassName($baseName, $this->namespaceFormat); if ($className !== null) { return new ObjectType($className); } diff --git a/src/Type/BaseTraitExpressionTypeResolverExtension.php b/src/Type/BaseTraitExpressionTypeResolverExtension.php index 3b5d090..a114ba2 100644 --- a/src/Type/BaseTraitExpressionTypeResolverExtension.php +++ b/src/Type/BaseTraitExpressionTypeResolverExtension.php @@ -41,6 +41,7 @@ public function __construct( protected string $targetTrait, protected string $methodName, protected string $namespaceFormat, + protected CakeNameRegistry $cakeNameRegistry, protected ?string $propertyDefaultValue = null, ) { } @@ -74,7 +75,7 @@ public function getType(Expr $expr, Scope $scope): ?Type if ($baseName === null) { return null; } - $className = CakeNameRegistry::getClassName($baseName, $this->namespaceFormat); + $className = $this->cakeNameRegistry->getClassName($baseName, $this->namespaceFormat); if ($className !== null) { return new ObjectType($className); } diff --git a/src/Type/ComponentLoadDynamicReturnTypeExtension.php b/src/Type/ComponentLoadDynamicReturnTypeExtension.php index e5eb1a5..19dbdfa 100644 --- a/src/Type/ComponentLoadDynamicReturnTypeExtension.php +++ b/src/Type/ComponentLoadDynamicReturnTypeExtension.php @@ -16,6 +16,7 @@ use Cake\Controller\Component; use Cake\Controller\Controller; use CakeDC\PHPStan\Traits\BaseCakeRegistryReturnTrait; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PHPStan\Type\DynamicMethodReturnTypeExtension; class ComponentLoadDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension @@ -40,14 +41,17 @@ class ComponentLoadDynamicReturnTypeExtension implements DynamicMethodReturnType */ protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * TableLocatorDynamicReturnTypeExtension constructor. */ - public function __construct() + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) { $this->className = Controller::class; $this->methodName = 'loadComponent'; $this->defaultClass = Component::class; $this->namespaceFormat = '%s\\Controller\Component\\%sComponent'; + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } } diff --git a/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php b/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php index f2cd3db..55343d2 100644 --- a/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php +++ b/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php @@ -16,6 +16,7 @@ use Cake\Console\ConsoleIo; use Cake\Console\Helper; use CakeDC\PHPStan\Traits\BaseCakeRegistryReturnTrait; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PHPStan\Type\DynamicMethodReturnTypeExtension; use PHPStan\Type\Type; @@ -42,15 +43,18 @@ class ConsoleHelperLoadDynamicReturnTypeExtension implements DynamicMethodReturn */ protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * TableLocatorDynamicReturnTypeExtension constructor. */ - public function __construct() + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) { $this->className = ConsoleIo::class; $this->methodName = 'helper'; $this->defaultClass = Helper::class; $this->namespaceFormat = '%s\\Command\Helper\\%sHelper'; + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php index 1b1fc5b..7dc39ee 100644 --- a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php +++ b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php @@ -18,6 +18,7 @@ use Cake\Utility\Inflector; use CakeDC\PHPStan\Traits\BaseCakeRegistryReturnTrait; use CakeDC\PHPStan\Traits\RepositoryReferenceTrait; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; @@ -56,14 +57,17 @@ class RepositoryEntityDynamicReturnTypeExtension implements DynamicMethodReturnT */ protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * @param class-string $className The target className. */ - public function __construct(string $className) + public function __construct(string $className, ?CakeNameRegistry $cakeNameRegistry = null) { $this->className = $className; $this->defaultClass = EntityInterface::class; $this->namespaceFormat = '%s\\Model\Entity\\%s'; + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index 818868f..05efe64 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -15,6 +15,7 @@ use Cake\Datasource\EntityInterface; use CakeDC\PHPStan\Traits\BaseCakeRegistryReturnTrait; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PhpParser\Node\Expr\MethodCall; use PHPStan\Analyser\Scope; use PHPStan\Reflection\MethodReflection; @@ -56,14 +57,17 @@ class RepositoryFirstArgIsTheReturnTypeExtension implements DynamicMethodReturnT */ protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * @param class-string $className The target className. */ - public function __construct(string $className) + public function __construct(string $className, ?CakeNameRegistry $cakeNameRegistry = null) { $this->className = $className; $this->defaultClass = EntityInterface::class; $this->namespaceFormat = '%s\\Model\Entity\\%s'; + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index 2584872..a7a3309 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -15,6 +15,7 @@ use Cake\ORM\Table; use CakeDC\PHPStan\Traits\BaseCakeRegistryReturnTrait; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Scalar\String_; use PHPStan\Analyser\Scope; @@ -41,18 +42,21 @@ class TableLocatorDynamicReturnTypeExtension implements DynamicMethodReturnTypeE protected string $defaultClass; protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * TableLocatorDynamicReturnTypeExtension constructor. * * @param class-string $className The target className. * @param string $methodName The dynamic method to handle. */ - public function __construct(string $className, string $methodName) + public function __construct(string $className, string $methodName, ?CakeNameRegistry $cakeNameRegistry = null) { $this->className = $className; $this->methodName = $methodName; $this->defaultClass = Table::class; $this->namespaceFormat = '%s\\Model\\Table\\%sTable'; + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Utility/CakeNameRegistry.php b/src/Utility/CakeNameRegistry.php index 80e7f17..2710a00 100644 --- a/src/Utility/CakeNameRegistry.php +++ b/src/Utility/CakeNameRegistry.php @@ -7,6 +7,21 @@ class CakeNameRegistry { + /** + * @param string $appNamespace The application's namespace. + */ + public function __construct(private readonly string $appNamespace) + { + } + + /** + * @param string $appNamespace The application's namespace. + */ + public static function instance(string $appNamespace = 'App'): self + { + return new self($appNamespace); + } + /** * @param string $baseName * @return array{string|null,string} @@ -22,14 +37,14 @@ protected static function pluginSplit(string $baseName): array * @param array|string $namespaceFormat * @return string|null */ - public static function getClassName(string $baseName, string|array $namespaceFormat): ?string + public function getClassName(string $baseName, string|array $namespaceFormat): ?string { if (str_contains($baseName, '\\')) { return class_exists($baseName) ? $baseName : null; } [$plugin, $name] = static::pluginSplit($baseName); - $prefixes = $plugin !== null ? [$plugin] : ['App', 'Cake']; + $prefixes = $plugin !== null ? [$plugin] : [$this->appNamespace, 'Cake']; $namespaceFormat = (array)$namespaceFormat; foreach ($namespaceFormat as $format) { foreach ($prefixes as $prefix) { @@ -48,9 +63,9 @@ public static function getClassName(string $baseName, string|array $namespaceFor * @param string $name * @return string|null */ - public static function getComponentClassName(string $name): ?string + public function getComponentClassName(string $name): ?string { - return static::getClassName($name, [ + return $this->getClassName($name, [ '%s\\Controller\\Component\\%sComponent', '%s\\Controller\\Component\\%sComponent', ]); @@ -60,9 +75,9 @@ public static function getComponentClassName(string $name): ?string * @param string $name * @return string|null */ - public static function getBehaviorClassName(string $name): ?string + public function getBehaviorClassName(string $name): ?string { - return static::getClassName($name, [ + return $this->getClassName($name, [ '%s\\Model\\Behavior\\%sBehavior', '%s\\ORM\\Behavior\\%sBehavior', ]); @@ -72,9 +87,9 @@ public static function getBehaviorClassName(string $name): ?string * @param string $name * @return string|null */ - public static function getTableClassName(string $name): ?string + public function getTableClassName(string $name): ?string { - return static::getClassName($name, [ + return $this->getClassName($name, [ '%s\\Model\\Table\\%sTable', ]); } @@ -83,9 +98,9 @@ public static function getTableClassName(string $name): ?string * @param string $name * @return string|null */ - public static function getMailerClassName(string $name): ?string + public function getMailerClassName(string $name): ?string { - return static::getClassName($name, [ + return $this->getClassName($name, [ '%s\\Mailer\\%sMailer', ]); } diff --git a/tests/TestCase/Rule/Controller/LoadComponentExistsClassRuleTest.php b/tests/TestCase/Rule/Controller/LoadComponentExistsClassRuleTest.php index 0f627d1..f92c453 100644 --- a/tests/TestCase/Rule/Controller/LoadComponentExistsClassRuleTest.php +++ b/tests/TestCase/Rule/Controller/LoadComponentExistsClassRuleTest.php @@ -13,6 +13,7 @@ namespace CakeDC\PHPStan\Test\TestCase\Rule\Controller; use CakeDC\PHPStan\Rule\Controller\LoadComponentExistsClassRule; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -24,7 +25,7 @@ class LoadComponentExistsClassRuleTest extends RuleTestCase protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule - return new LoadComponentExistsClassRule(); + return new LoadComponentExistsClassRule(new CakeNameRegistry('App')); } /** diff --git a/tests/TestCase/Rule/Mailer/GetMailerExistsClassRuleTest.php b/tests/TestCase/Rule/Mailer/GetMailerExistsClassRuleTest.php index 1476324..a8c52af 100644 --- a/tests/TestCase/Rule/Mailer/GetMailerExistsClassRuleTest.php +++ b/tests/TestCase/Rule/Mailer/GetMailerExistsClassRuleTest.php @@ -14,6 +14,7 @@ namespace CakeDC\PHPStan\Test\TestCase\Rule\Mailer; use CakeDC\PHPStan\Rule\Mailer\GetMailerExistsClassRule; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -25,7 +26,7 @@ class GetMailerExistsClassRuleTest extends RuleTestCase protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule - return new GetMailerExistsClassRule(); + return new GetMailerExistsClassRule(new CakeNameRegistry('App')); } /** diff --git a/tests/TestCase/Rule/Model/AddAssociationExistsTableClassRuleTest.php b/tests/TestCase/Rule/Model/AddAssociationExistsTableClassRuleTest.php index 8b24df0..4ac4664 100644 --- a/tests/TestCase/Rule/Model/AddAssociationExistsTableClassRuleTest.php +++ b/tests/TestCase/Rule/Model/AddAssociationExistsTableClassRuleTest.php @@ -14,6 +14,7 @@ namespace CakeDC\PHPStan\Test\TestCase\Rule\Model; use CakeDC\PHPStan\Rule\Model\AddAssociationExistsTableClassRule; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -25,7 +26,7 @@ class AddAssociationExistsTableClassRuleTest extends RuleTestCase protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule - return new AddAssociationExistsTableClassRule(); + return new AddAssociationExistsTableClassRule(new CakeNameRegistry('App')); } /** diff --git a/tests/TestCase/Rule/Model/AddBehaviorExistsClassRuleTest.php b/tests/TestCase/Rule/Model/AddBehaviorExistsClassRuleTest.php index 23c87d8..cd1c792 100644 --- a/tests/TestCase/Rule/Model/AddBehaviorExistsClassRuleTest.php +++ b/tests/TestCase/Rule/Model/AddBehaviorExistsClassRuleTest.php @@ -14,6 +14,7 @@ namespace CakeDC\PHPStan\Test\TestCase\Rule\Model; use CakeDC\PHPStan\Rule\Model\AddBehaviorExistsClassRule; +use CakeDC\PHPStan\Utility\CakeNameRegistry; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; @@ -25,7 +26,7 @@ class AddBehaviorExistsClassRuleTest extends RuleTestCase protected function getRule(): Rule { // getRule() method needs to return an instance of the tested rule - return new AddBehaviorExistsClassRule(); + return new AddBehaviorExistsClassRule(new CakeNameRegistry('App')); } /**