Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.29% covered (warning)
74.29%
26 / 35
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
SyncQueryBus
74.29% covered (warning)
74.29%
26 / 35
66.67% covered (warning)
66.67%
2 / 3
7.83
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 dispatch
50.00% covered (danger)
50.00%
9 / 18
0.00% covered (danger)
0.00%
0 / 1
4.12
 resolveHandler
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10namespace Phexium\Plugin\QueryBus\Adapter;
11
12use InvalidArgumentException;
13use Override;
14use Phexium\Application\Query\QueryHandlerInterface;
15use Phexium\Application\Query\QueryInterface;
16use Phexium\Application\Query\QueryResponseInterface;
17use Phexium\Plugin\Logger\Port\LoggerInterface;
18use Phexium\Plugin\QueryBus\Internal\QueryHandlerNamingConvention;
19use Phexium\Plugin\QueryBus\Port\QueryBusInterface;
20use Psr\Container\ContainerInterface;
21
22final readonly class SyncQueryBus implements QueryBusInterface
23{
24    public function __construct(
25        private ContainerInterface $container,
26        private LoggerInterface $logger
27    ) {}
28
29    #[Override]
30    public function dispatch(QueryInterface $query): QueryResponseInterface
31    {
32        $queryClass = $query::class;
33
34        $this->logger->debug('QueryBus: Dispatching query', [
35            'query' => $queryClass,
36        ]);
37
38        $handler = $this->resolveHandler($queryClass);
39
40        if (!is_callable($handler)) {
41            throw new InvalidArgumentException(sprintf(
42                'Handler %s must be invokable.',
43                $handler::class
44            ));
45        }
46
47        $response = $handler($query);
48
49        if (!$response instanceof QueryResponseInterface) {
50            throw new InvalidArgumentException(sprintf(
51                'Handler %s must return a %s.',
52                $handler::class,
53                QueryResponseInterface::class
54            ));
55        }
56
57        return $response;
58    }
59
60    private function resolveHandler(string $queryClass): QueryHandlerInterface
61    {
62        $handlerClass = QueryHandlerNamingConvention::resolveHandlerClass($queryClass);
63
64        if (!class_exists($handlerClass)) {
65            throw new InvalidArgumentException(
66                sprintf(
67                    'Handler not found for query %s. Expected class: %s',
68                    $queryClass,
69                    $handlerClass
70                )
71            );
72        }
73
74        $handler = $this->container->get($handlerClass);
75
76        if (!$handler instanceof QueryHandlerInterface) {
77            throw new InvalidArgumentException(sprintf(
78                'Handler must implement %s.',
79                QueryHandlerInterface::class
80            ));
81        }
82
83        return $handler;
84    }
85}