From ab8fb5a7d6ee3a563f54f691e08d6045c4814c17 Mon Sep 17 00:00:00 2001 From: Marcelo Rocha Date: Fri, 11 Oct 2024 16:25:52 -0300 Subject: [PATCH] Extension to detect return type of MailerAwareTrait::getMailer --- extension.neon | 4 + ...tMailerExpressionTypeResolverExtension.php | 102 ++++++++++++++++++ .../Command/MyTestExtendedCommand.php | 30 ++++++ tests/test_app/Command/MyTestLoadCommand.php | 4 + tests/test_app/Mailer/MyTestLoadMailer.php | 7 ++ 5 files changed, 147 insertions(+) create mode 100644 src/Type/GetMailerExpressionTypeResolverExtension.php create mode 100644 tests/test_app/Command/MyTestExtendedCommand.php diff --git a/extension.neon b/extension.neon index 6f08bd6..57c95ff 100644 --- a/extension.neon +++ b/extension.neon @@ -62,3 +62,7 @@ services: class: CakeDC\PHPStan\PhpDoc\TableAssociationTypeNodeResolverExtension tags: - phpstan.phpDoc.typeNodeResolverExtension + - + class: CakeDC\PHPStan\Type\GetMailerExpressionTypeResolverExtension + tags: + - phpstan.broker.expressionTypeResolverExtension diff --git a/src/Type/GetMailerExpressionTypeResolverExtension.php b/src/Type/GetMailerExpressionTypeResolverExtension.php new file mode 100644 index 0000000..507c3ba --- /dev/null +++ b/src/Type/GetMailerExpressionTypeResolverExtension.php @@ -0,0 +1,102 @@ +targetTrait = MailerAwareTrait::class; + $this->methodName = 'getMailer'; + $this->namespaceFormat = '%s\\Mailer\\%sMailer'; + } + + /** + * @param \PhpParser\Node\Expr $expr + * @param \PHPStan\Analyser\Scope $scope + * @return \PHPStan\Type\Type|null + */ + public function getType(Expr $expr, Scope $scope): ?Type + { + if ( + !$expr instanceof Expr\MethodCall + || !$expr->name instanceof Identifier + || $expr->name->toString() !== $this->methodName + ) { + return null; + } + + $callerType = $scope->getType($expr->var); + + if ( + !$callerType instanceof ThisType + || !$this->isFromTargetTrait($callerType->getClassReflection()) + ) { + return null; + } + + $value = $expr->getArgs()[0]->value ?? null; + if (!$value instanceof String_) { + return null; + } + $className = CakeNameRegistry::getClassName($value->value, $this->namespaceFormat); + if ($className !== null) { + return new ObjectType($className); + } + + return null; + } + + /** + * @param \PHPStan\Reflection\ClassReflection $reflection + * @return bool + */ + protected function isFromTargetTrait(ClassReflection $reflection): bool + { + foreach ($reflection->getTraits() as $trait) { + if ($trait->getName() === $this->targetTrait) { + return true; + } + } + foreach ($reflection->getParents() as $parent) { + if ($this->isFromTargetTrait($parent)) { + return true; + } + } + + return false; + } +} diff --git a/tests/test_app/Command/MyTestExtendedCommand.php b/tests/test_app/Command/MyTestExtendedCommand.php new file mode 100644 index 0000000..b4e4ac1 --- /dev/null +++ b/tests/test_app/Command/MyTestExtendedCommand.php @@ -0,0 +1,30 @@ +fetchTable('VeryCustomize00009Articles')->newSample(); + $io->helper('MyHeading')->headingOne('Sample Text 02'); + $this->getMailer('MyTestLoad')->testing(); + } +} diff --git a/tests/test_app/Command/MyTestLoadCommand.php b/tests/test_app/Command/MyTestLoadCommand.php index 42db57a..e94aab1 100644 --- a/tests/test_app/Command/MyTestLoadCommand.php +++ b/tests/test_app/Command/MyTestLoadCommand.php @@ -16,9 +16,12 @@ use Cake\Command\Command; use Cake\Console\Arguments; use Cake\Console\ConsoleIo; +use Cake\Mailer\MailerAwareTrait; class MyTestLoadCommand extends Command { + use MailerAwareTrait; + /** * @inheritDoc */ @@ -29,5 +32,6 @@ public function execute(Arguments $args, ConsoleIo $io) $io->helper('progress')->increment(1); $io->out($io->helper('BazBaz')->foo()); $io->helper('MyHeading')->headingOne('Sample Text 01'); + $this->getMailer('MyTestLoad')->testing(); } } diff --git a/tests/test_app/Mailer/MyTestLoadMailer.php b/tests/test_app/Mailer/MyTestLoadMailer.php index 2401217..5e111d7 100644 --- a/tests/test_app/Mailer/MyTestLoadMailer.php +++ b/tests/test_app/Mailer/MyTestLoadMailer.php @@ -28,4 +28,11 @@ protected function sampleLoading() ->newSample(); $this->viewBuilder()->setVar('article', $article); } + + /** + * @return void + */ + public function testing() + { + } }