Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implementation for interfaces
  • Loading branch information
Bartłomiej Nowak committed Sep 8, 2025
commit cea6eb06b138c51c3f05ea258351b1cc9150057c
5 changes: 3 additions & 2 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ parameters:
consoleApplicationLoader: null
messenger:
handleTraitWrappers:
# move that params to tests only
# todo move that params to tests only
- MessengerHandleTrait\QueryBus::dispatch
- MessengerHandleTrait\QueryBus2::dispatch
- MessengerHandleTrait\QueryBus::dispatch2
- MessengerHandleTrait\QueryBusInterface::dispatch
featureToggles:
skipCheckGenericClasses:
- Symfony\Component\Form\AbstractType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Identifier;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Symfony\Configuration;
use PHPStan\Symfony\MessageMap;
use PHPStan\Symfony\MessageMapFactory;
Expand All @@ -19,7 +20,7 @@
* Configurable extension for resolving return types of methods that internally use HandleTrait.
*
* Configured via PHPStan parameters under symfony.messenger.handleTraitWrappers with
* "Class::method" patterns, e.g.:
* "class::method" patterns, e.g.:
* - App\Bus\QueryBus::dispatch
* - App\Bus\QueryBus::query
* - App\Bus\CommandBus::execute
Expand All @@ -37,10 +38,14 @@ final class MessengerHandleTraitWrapperReturnTypeExtension implements Expression
/** @var array<string> */
private $wrappers;

public function __construct(MessageMapFactory $messageMapFactory, Configuration $configuration)
/** @var ReflectionProvider */
private $reflectionProvider;

public function __construct(MessageMapFactory $messageMapFactory, Configuration $configuration, ReflectionProvider $reflectionProvider)
{
$this->messageMapFactory = $messageMapFactory;
$this->wrappers = $configuration->getMessengerHandleTraitWrappers();
$this->reflectionProvider = $reflectionProvider;
}

public function getType(Expr $expr, Scope $scope): ?Type
Expand Down Expand Up @@ -89,8 +94,23 @@ private function isSupported(Expr $expr, Scope $scope): bool
$className = $classNames[0];
$classMethodCombination = $className . '::' . $methodName;

// Check if this class::method combination is configured
return in_array($classMethodCombination, $this->wrappers, true);
// Check if this exact class::method combination is configured
if (in_array($classMethodCombination, $this->wrappers, true)) {
return true;
}

// Check if any interface implemented by this class::method is configured
if ($this->reflectionProvider->hasClass($className)) {
$classReflection = $this->reflectionProvider->getClass($className);
foreach ($classReflection->getInterfaces() as $interface) {
$interfaceMethodCombination = $interface->getName() . '::' . $methodName;
if (in_array($interfaceMethodCombination, $this->wrappers, true)) {
return true;
}
}
}

return false;
}

private function getMessageMap(): MessageMap
Expand Down
20 changes: 16 additions & 4 deletions tests/Type/Symfony/data/messenger_handle_trait.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,18 @@ public function dispatch(object $query): mixed
{
return $this->handle($query);
}

public function dispatch2(object $query): mixed
{
return $this->handle($query);
}
}

interface QueryBusInterface {
public function dispatch(object $query): mixed;
}

class QueryBus2 {
class QueryBusWithInterface implements QueryBusInterface {
use HandleTrait;

public function dispatch(object $query): mixed
Expand All @@ -87,11 +96,14 @@ public function action()

assertType(TaggedResult::class, $queryBus->dispatch(new TaggedQuery()));

assertType(RegularQueryResult::class, $queryBus->dispatch2(new RegularQuery()));

$queryBusWithInterface = new QueryBusWithInterface();

assertType(RegularQueryResult::class, $queryBusWithInterface->dispatch(new RegularQuery()));

// HandleTrait will throw exception in fact due to multiple handle methods/handlers per single query
assertType('mixed', $queryBus->dispatch(new MultiHandlesForInTheSameHandlerQuery()));
assertType('mixed', $queryBus->dispatch(new MultiHandlersForTheSameMessageQuery()));

$queryBus2 = new QueryBus2();
assertType(TaggedResult::class, $queryBus2->dispatch(new TaggedQuery()));
}
}