-
Notifications
You must be signed in to change notification settings - Fork 90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for Messenger HandleTrait return types #404
base: 1.4.x
Are you sure you want to change the base?
Conversation
5d47f63
to
e3790e0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of DynamicMethodReturnTypeExtension you can use https://apiref.phpstan.org/1.11.x/PHPStan.Type.ExpressionTypeResolverExtension.html which is like that but lets you override any expression type.
e7063fd
to
7b4c17e
Compare
@ondrejmirtes Thanks for the tip about It took me some time, however finally I've got ready to review version of this PR :) Please take a look in you free time :) Thank you very much! |
As static analyzing should works now fine with the result of // src/Action/ListItems.php
namespace App\Action;
use App\Message\ListItemsQuery;
use App\MessageHandler\ListItemsQueryResult;
use Symfony\Component\Messenger\HandleTrait;
use Symfony\Component\Messenger\MessageBusInterface;
class ListItems
{
use HandleTrait;
public function __construct(
private MessageBusInterface $messageBus,
) {
}
public function __invoke(): void
{
$result = $this->handle(new ListItemsQuery(/* ... */));
// $result should be automatically recognized as `ListItemsQueryResult` now by phpstan via SF configuration
// Do something with the result
// ...
}
} However, my target goal was to cover QueryBus classes with dynamic return types based on dynamic query, something like: // src/MessageBus/QueryBus.php
namespace App\MessageBus;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\HandleTrait;
use Symfony\Component\Messenger\MessageBusInterface;
class QueryBus
{
use HandleTrait;
public function __construct(
private MessageBusInterface $messageBus,
) {
}
// wherever that method is called, phpstan should recognize return type based on passed query
public function query($query)
{
return $this->handle($query);
}
} Maybe it's trivial and doable with already existing php-doc annotation or more complex to write some next extension for that, but I don't know how to connect the dots and reuse covered typing of |
b94aa51
to
d62ec03
Compare
@ondrejmirtes any chances to review that, please? 🙏 |
@ondrejmirtes I can see that version 2.0.0 is released now. Is 1.x abandoned from now? Should I rebase that PR to 2.x branch? Any chances to review and/or merge it please? 🙂 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to rebase this, we could release this in 1.x.
.gitignore
Outdated
@@ -1,5 +1,6 @@ | |||
/tests/tmp | |||
/build-cs | |||
/vendor | |||
/.idea |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this please. This belongs to a global .gitignore.
src/Symfony/MessageMapFactory.php
Outdated
|
||
/** @var array{handles?: class-string, method?: string} $tagAttributes */ | ||
$tagAttributes = $tag->getAttributes(); | ||
$reflectionClass = $this->reflectionProvider->getClass($serviceClass); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unsafe. Ask for hasClass first.
src/Symfony/MessageMapFactory.php
Outdated
} | ||
|
||
foreach ($handles as $messageClassName => $options) { | ||
$methodReflection = $reflectionClass->getNativeMethod($options['method'] ?? self::DEFAULT_HANDLER_METHOD); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is unsafe. Ask for hasNativeMethod first.
src/Symfony/MessageMapFactory.php
Outdated
private function guessHandledMessages(ClassReflection $reflectionClass): iterable | ||
{ | ||
if ($reflectionClass->implementsInterface(MessageSubscriberInterface::class)) { | ||
foreach ($reflectionClass->getName()::getHandledMessages() as $index => $value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Save the class name into a variable first, then call the method.
src/Symfony/MessageMapFactory.php
Outdated
return new MessageMap($messageMap); | ||
} | ||
|
||
/** @return array<class-string, array<string, string>> */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not return an array. It's a generator.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right, changed to iterable :)
} | ||
|
||
/** @var array{handles?: class-string, method?: string} $tagAttributes */ | ||
$tagAttributes = $tag->getAttributes(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a @return
stub for this method instead of @var
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shape is dynamic and rely on what tag we're using from SF config. In this case I'm ensuring above that we're handling only messenger.message_handler
, so we know what shape it should have.
As far I know stubs are static only, so unfortunately we cannot use them here.
Do you have other idea or it could stay as it is?
src/Symfony/MessageMapFactory.php
Outdated
* @phpstan-assert-if-true class-string $index | ||
* @phpstan-assert-if-true array<string, mixed> $value | ||
* @phpstan-assert-if-false int $index | ||
* @phpstan-assert-if-false class-string $value |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these asserts need =
: https://phpstan.org/writing-php-code/narrowing-types#equality-assertions
if ($this->isSupported($expr, $scope)) { | ||
$arg = $expr->getArgs()[0]->value; | ||
/** @var class-string[] $argClassNames */ | ||
$argClassNames = $scope->getType($arg)->getObjectClassNames(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add this @var
.
} | ||
|
||
/** | ||
* @phpstan-assert-if-true MethodCall $expr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs =
.
|
||
return $declaringClassReflection->getName() === self::TRAIT_NAME; | ||
} catch (ReflectionException $e) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of catching exception, check if the method exists first.
@ondrejmirtes Thanks for review. I addressed all your comments, please take a look again when you have time :) Also, could you take a look on #404 (comment) and answer that if you have an idea how to solve it please? That's my initial goal to resolve by this PR :) |
} else { | ||
yield $value => ['method' => self::DEFAULT_HANDLER_METHOD]; | ||
} | ||
} catch (ShouldNotHappenException $e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never catch ShouldNotHappenException.
public function getType(Expr $expr, Scope $scope): ?Type | ||
{ | ||
if ($this->isSupported($expr, $scope)) { | ||
$arg = $expr->getArgs()[0]->value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$expr->getArgs()[0]
might not exist. Check the existence first.
The goal is to cover #207.
That's my very first PR in phpstan-symfony plugin, so any feedback/tips/advices are very welcome :)