From fadf5bdae7ee7de0dc1c109e7b5be9997be83ed2 Mon Sep 17 00:00:00 2001 From: George Constantinou Date: Thu, 26 Mar 2026 11:37:10 +0200 Subject: [PATCH 1/2] feat: Support custom application namespace This changeset adds support for defining a custom application namespace via configuration, for example: ```yaml parameters: cakeDC: appNamespace: MyApp ``` Related issue: https://github.com/CakeDC/cakephp-phpstan/issues/63 --- extension.neon | 6 ++++- rules.neon | 2 ++ .../LoadComponentExistsClassRule.php | 9 ++++++- src/Rule/Mailer/GetMailerExistsClassRule.php | 9 ++++++- .../AddAssociationExistsTableClassRule.php | 9 ++++++- src/Rule/Model/AddBehaviorExistsClassRule.php | 9 ++++++- src/Traits/BaseCakeRegistryReturnTrait.php | 3 +-- ...seTraitExpressionTypeResolverExtension.php | 3 ++- ...omponentLoadDynamicReturnTypeExtension.php | 3 ++- ...leHelperLoadDynamicReturnTypeExtension.php | 3 ++- ...sitoryEntityDynamicReturnTypeExtension.php | 3 ++- ...sitoryFirstArgIsTheReturnTypeExtension.php | 3 ++- ...TableLocatorDynamicReturnTypeExtension.php | 8 ++++-- src/Utility/CakeNameRegistry.php | 27 ++++++++++++------- .../LoadComponentExistsClassRuleTest.php | 3 ++- .../Mailer/GetMailerExistsClassRuleTest.php | 3 ++- ...AddAssociationExistsTableClassRuleTest.php | 3 ++- .../Model/AddBehaviorExistsClassRuleTest.php | 3 ++- 18 files changed, 81 insertions(+), 28 deletions(-) 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..fb0892c 100644 --- a/src/Rule/Controller/LoadComponentExistsClassRule.php +++ b/src/Rule/Controller/LoadComponentExistsClassRule.php @@ -38,12 +38,19 @@ class LoadComponentExistsClassRule extends LoadObjectExistsCakeClassRule 'load', ]; + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + { + } + /** * @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..9b8058f 100644 --- a/src/Rule/Mailer/GetMailerExistsClassRule.php +++ b/src/Rule/Mailer/GetMailerExistsClassRule.php @@ -29,6 +29,13 @@ class GetMailerExistsClassRule implements Rule */ protected string $identifier = 'cake.getMailer.existClass'; + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + { + } + /** * @inheritDoc */ @@ -66,7 +73,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..3da691d 100644 --- a/src/Rule/Model/AddAssociationExistsTableClassRule.php +++ b/src/Rule/Model/AddAssociationExistsTableClassRule.php @@ -42,12 +42,19 @@ class AddAssociationExistsTableClassRule extends LoadObjectExistsCakeClassRule */ protected array $associationCollectionMethods = ['load']; + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + { + } + /** * @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..a55fee7 100644 --- a/src/Rule/Model/AddBehaviorExistsClassRule.php +++ b/src/Rule/Model/AddBehaviorExistsClassRule.php @@ -38,12 +38,19 @@ class AddBehaviorExistsClassRule extends LoadObjectExistsCakeClassRule 'load', ]; + /** + * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry + */ + public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + { + } + /** * @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..df9c2a5 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 @@ -43,7 +44,7 @@ class ComponentLoadDynamicReturnTypeExtension implements DynamicMethodReturnType /** * TableLocatorDynamicReturnTypeExtension constructor. */ - public function __construct() + public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) { $this->className = Controller::class; $this->methodName = 'loadComponent'; diff --git a/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php b/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php index f2cd3db..e4bb204 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; @@ -45,7 +46,7 @@ class ConsoleHelperLoadDynamicReturnTypeExtension implements DynamicMethodReturn /** * TableLocatorDynamicReturnTypeExtension constructor. */ - public function __construct() + public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) { $this->className = ConsoleIo::class; $this->methodName = 'helper'; diff --git a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php index 1b1fc5b..a696b5e 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; @@ -59,7 +60,7 @@ class RepositoryEntityDynamicReturnTypeExtension implements DynamicMethodReturnT /** * @param class-string $className The target className. */ - public function __construct(string $className) + public function __construct(string $className, private readonly CakeNameRegistry $cakeNameRegistry) { $this->className = $className; $this->defaultClass = EntityInterface::class; diff --git a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php index 818868f..6ab577f 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; @@ -59,7 +60,7 @@ class RepositoryFirstArgIsTheReturnTypeExtension implements DynamicMethodReturnT /** * @param class-string $className The target className. */ - public function __construct(string $className) + public function __construct(string $className, private readonly CakeNameRegistry $cakeNameRegistry) { $this->className = $className; $this->defaultClass = EntityInterface::class; diff --git a/src/Type/TableLocatorDynamicReturnTypeExtension.php b/src/Type/TableLocatorDynamicReturnTypeExtension.php index 2584872..ae3dbe8 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; @@ -47,8 +48,11 @@ class TableLocatorDynamicReturnTypeExtension implements DynamicMethodReturnTypeE * @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, + private readonly CakeNameRegistry $cakeNameRegistry, + ) { $this->className = $className; $this->methodName = $methodName; $this->defaultClass = Table::class; diff --git a/src/Utility/CakeNameRegistry.php b/src/Utility/CakeNameRegistry.php index 80e7f17..074f467 100644 --- a/src/Utility/CakeNameRegistry.php +++ b/src/Utility/CakeNameRegistry.php @@ -7,6 +7,13 @@ class CakeNameRegistry { + /** + * @param string $appNamespace The application's namespace. + */ + public function __construct(private readonly string $appNamespace) + { + } + /** * @param string $baseName * @return array{string|null,string} @@ -22,14 +29,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 +55,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 +67,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 +79,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 +90,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')); } /** From 3072552b54c6d7923a49eb51329ffe7fb985aade Mon Sep 17 00:00:00 2001 From: George Constantinou Date: Thu, 26 Mar 2026 15:10:33 +0200 Subject: [PATCH 2/2] Make constructor dependency optional to keep BC, and self-instantiate if no instance is provided --- src/Rule/Controller/LoadComponentExistsClassRule.php | 5 ++++- src/Rule/Mailer/GetMailerExistsClassRule.php | 5 ++++- src/Rule/Model/AddAssociationExistsTableClassRule.php | 5 ++++- src/Rule/Model/AddBehaviorExistsClassRule.php | 5 ++++- src/Type/ComponentLoadDynamicReturnTypeExtension.php | 5 ++++- .../ConsoleHelperLoadDynamicReturnTypeExtension.php | 5 ++++- .../RepositoryEntityDynamicReturnTypeExtension.php | 5 ++++- .../RepositoryFirstArgIsTheReturnTypeExtension.php | 5 ++++- src/Type/TableLocatorDynamicReturnTypeExtension.php | 10 +++++----- src/Utility/CakeNameRegistry.php | 8 ++++++++ 10 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/Rule/Controller/LoadComponentExistsClassRule.php b/src/Rule/Controller/LoadComponentExistsClassRule.php index fb0892c..203d4ad 100644 --- a/src/Rule/Controller/LoadComponentExistsClassRule.php +++ b/src/Rule/Controller/LoadComponentExistsClassRule.php @@ -38,11 +38,14 @@ class LoadComponentExistsClassRule extends LoadObjectExistsCakeClassRule 'load', ]; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry */ - public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Rule/Mailer/GetMailerExistsClassRule.php b/src/Rule/Mailer/GetMailerExistsClassRule.php index 9b8058f..9a67ec4 100644 --- a/src/Rule/Mailer/GetMailerExistsClassRule.php +++ b/src/Rule/Mailer/GetMailerExistsClassRule.php @@ -29,11 +29,14 @@ class GetMailerExistsClassRule implements Rule */ protected string $identifier = 'cake.getMailer.existClass'; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry */ - public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Rule/Model/AddAssociationExistsTableClassRule.php b/src/Rule/Model/AddAssociationExistsTableClassRule.php index 3da691d..2ef394b 100644 --- a/src/Rule/Model/AddAssociationExistsTableClassRule.php +++ b/src/Rule/Model/AddAssociationExistsTableClassRule.php @@ -42,11 +42,14 @@ class AddAssociationExistsTableClassRule extends LoadObjectExistsCakeClassRule */ protected array $associationCollectionMethods = ['load']; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry */ - public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Rule/Model/AddBehaviorExistsClassRule.php b/src/Rule/Model/AddBehaviorExistsClassRule.php index a55fee7..c8aebf5 100644 --- a/src/Rule/Model/AddBehaviorExistsClassRule.php +++ b/src/Rule/Model/AddBehaviorExistsClassRule.php @@ -38,11 +38,14 @@ class AddBehaviorExistsClassRule extends LoadObjectExistsCakeClassRule 'load', ]; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * @param \CakeDC\PHPStan\Utility\CakeNameRegistry $cakeNameRegistry */ - public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + public function __construct(?CakeNameRegistry $cakeNameRegistry = null) { + $this->cakeNameRegistry = $cakeNameRegistry ?? CakeNameRegistry::instance(); } /** diff --git a/src/Type/ComponentLoadDynamicReturnTypeExtension.php b/src/Type/ComponentLoadDynamicReturnTypeExtension.php index df9c2a5..19dbdfa 100644 --- a/src/Type/ComponentLoadDynamicReturnTypeExtension.php +++ b/src/Type/ComponentLoadDynamicReturnTypeExtension.php @@ -41,14 +41,17 @@ class ComponentLoadDynamicReturnTypeExtension implements DynamicMethodReturnType */ protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * TableLocatorDynamicReturnTypeExtension constructor. */ - public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + 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 e4bb204..55343d2 100644 --- a/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php +++ b/src/Type/ConsoleHelperLoadDynamicReturnTypeExtension.php @@ -43,15 +43,18 @@ class ConsoleHelperLoadDynamicReturnTypeExtension implements DynamicMethodReturn */ protected string $namespaceFormat; + private readonly CakeNameRegistry $cakeNameRegistry; + /** * TableLocatorDynamicReturnTypeExtension constructor. */ - public function __construct(private readonly CakeNameRegistry $cakeNameRegistry) + 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 a696b5e..7dc39ee 100644 --- a/src/Type/RepositoryEntityDynamicReturnTypeExtension.php +++ b/src/Type/RepositoryEntityDynamicReturnTypeExtension.php @@ -57,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, private readonly CakeNameRegistry $cakeNameRegistry) + 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 6ab577f..05efe64 100644 --- a/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php +++ b/src/Type/RepositoryFirstArgIsTheReturnTypeExtension.php @@ -57,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, private readonly CakeNameRegistry $cakeNameRegistry) + 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 ae3dbe8..a7a3309 100644 --- a/src/Type/TableLocatorDynamicReturnTypeExtension.php +++ b/src/Type/TableLocatorDynamicReturnTypeExtension.php @@ -42,21 +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, - private readonly CakeNameRegistry $cakeNameRegistry, - ) { + 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 074f467..2710a00 100644 --- a/src/Utility/CakeNameRegistry.php +++ b/src/Utility/CakeNameRegistry.php @@ -14,6 +14,14 @@ 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}