Passed
Pull Request — master (#93)
by
unknown
23:47
created

RoadRunnerGrpcApplicationRunner::afterRespond()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Runner\RoadRunner;
6
7
use Psr\Container\ContainerExceptionInterface;
8
use Psr\Container\ContainerInterface;
9
use Psr\Container\NotFoundExceptionInterface;
10
use Spiral\RoadRunner\GRPC\Invoker;
11
use Spiral\RoadRunner\GRPC\InvokerInterface;
12
use Spiral\RoadRunner\GRPC\Server;
13
use Spiral\RoadRunner\GRPC\ServiceInterface;
14
use Spiral\RoadRunner\Worker;
15
use Yiisoft\Definitions\Exception\InvalidConfigException;
16
use Yiisoft\Di\StateResetter;
17
use Yiisoft\ErrorHandler\ErrorHandler;
0 ignored issues
show
Bug introduced by
The type Yiisoft\ErrorHandler\ErrorHandler was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use Yiisoft\ErrorHandler\Exception\ErrorException;
19
use Yiisoft\ErrorHandler\Renderer\PlainTextRenderer;
20
use Yiisoft\Log\Logger;
21
use Yiisoft\Log\Target\File\FileTarget;
22
use Yiisoft\Yii\Runner\ApplicationRunner;
23
24
/**
25
 * `RoadRunnerGrpcApplicationRunner` runs the Yii gRPC application using RoadRunner.
26
 */
27
final class RoadRunnerGrpcApplicationRunner extends ApplicationRunner
28
{
29
    private ?ErrorHandler $temporaryErrorHandler = null;
30
    private ?InvokerInterface $invoker = null;
31
    private array $services = [];
32
    private ?Worker $worker = null;
33
34
    /**
35
     * @param string $rootPath The absolute path to the project root.
36
     * @param bool $debug Whether the debug mode is enabled.
37
     * @param bool $checkEvents Whether to check events' configuration.
38
     * @param string|null $environment The environment name.
39
     * @param string $bootstrapGroup The bootstrap configuration group name.
40
     * @param string $eventsGroup The events' configuration group name.
41
     * @param string $diGroup The container definitions' configuration group name.
42
     * @param string $diProvidersGroup The container providers' configuration group name.
43
     * @param string $diDelegatesGroup The container delegates' configuration group name.
44
     * @param string $diTagsGroup The container tags' configuration group name.
45
     * @param string $paramsGroup The configuration parameters group name.
46
     * @param array $nestedParamsGroups Configuration group names that are included into configuration parameters group.
47
     * This is needed for recursive merging of parameters.
48
     * @param array $nestedEventsGroups Configuration group names that are included into events' configuration group.
49
     * This is needed for reverse and recursive merge of events' configurations.
50
     *
51
     * @psalm-param list<string> $nestedParamsGroups
52
     * @psalm-param list<string> $nestedEventsGroups
53
     */
54 2
    public function __construct(
55
        string $rootPath,
56
        bool $debug = false,
57
        bool $checkEvents = false,
58
        ?string $environment = null,
59
        string $bootstrapGroup = 'bootstrap-grpc',
60
        string $eventsGroup = 'events-grpc',
61
        string $diGroup = 'di-grpc',
62
        string $diProvidersGroup = 'di-providers-grpc',
63
        string $diDelegatesGroup = 'di-delegates-grpc',
64
        string $diTagsGroup = 'di-tags-grpc',
65
        string $paramsGroup = 'params-grpc',
66
        array $nestedParamsGroups = ['params'],
67
        array $nestedEventsGroups = ['events'],
68
    ) {
69 2
        parent::__construct(
70 2
            $rootPath,
71 2
            $debug,
72 2
            $checkEvents,
73 2
            $environment,
74 2
            $bootstrapGroup,
75 2
            $eventsGroup,
76 2
            $diGroup,
77 2
            $diProvidersGroup,
78 2
            $diDelegatesGroup,
79 2
            $diTagsGroup,
80 2
            $paramsGroup,
81 2
            $nestedParamsGroups,
82 2
            $nestedEventsGroups,
83 2
        );
84
    }
85
86
    /**
87
     * @return void
88
     * @throws ContainerExceptionInterface
89
     * @throws ErrorException
90
     * @throws NotFoundExceptionInterface
91
     * @throws \ErrorException
92
     * @throws InvalidConfigException
93
     */
94 1
    public function run(): void
95
    {
96
        // Register temporary error handler to catch error while container is building.
97 1
        $temporaryErrorHandler = $this->createTemporaryErrorHandler();
98 1
        $this->registerErrorHandler($temporaryErrorHandler);
99
100 1
        $container = $this->getContainer();
101
102
        // Register error handler with real container-configured dependencies.
103
        /** @var ErrorHandler $actualErrorHandler */
104 1
        $actualErrorHandler = $container->get(ErrorHandler::class);
105 1
        $this->registerErrorHandler($actualErrorHandler, $temporaryErrorHandler);
106
107 1
        $this->runBootstrap();
108 1
        $this->checkEvents();
109
110 1
        $server = new Server($this->getInvoker(), ['debug' => $this->debug]);
111
112
        /**
113
         * @var class-string<ServiceInterface> $interface
114
         */
115 1
        foreach ($this->getServices() as $interface) {
116
            /** @var ServiceInterface $service */
117 1
            $service = $container->get($interface);
118 1
            $server->registerService($interface, $service);
119
        }
120
121 1
        $server->serve($this->getWorker(), finalize: function () use ($container) {
122 1
            $this->afterRespond($container);
123 1
        });
124
    }
125
126
    /**
127
     * @return ErrorHandler
128
     */
129 1
    private function createTemporaryErrorHandler(): ErrorHandler {
130 1
        if ($this->temporaryErrorHandler !== null) {
131
            return $this->temporaryErrorHandler;
132
        }
133
134 1
        $logger = new Logger([new FileTarget("$this->rootPath/runtime/logs/grpc.log")]);
135 1
        return new ErrorHandler($logger, new PlainTextRenderer());
136
    }
137
138
    /**
139
     * @throws ErrorException
140
     */
141 1
    private function registerErrorHandler(ErrorHandler $registered, ErrorHandler $unregistered = null): void {
142 1
        $unregistered?->unregister();
143
144 1
        if ($this->debug) {
145
            $registered->debug();
146
        }
147
148 1
        $registered->register();
149
    }
150
151
    /**
152
     * @param ContainerInterface $container
153
     * @return void
154
     * @throws ContainerExceptionInterface
155
     * @throws NotFoundExceptionInterface
156
     */
157 1
    private function afterRespond(ContainerInterface $container): void {
158
        /** @psalm-suppress MixedMethodCall */
159 1
        $container->get(StateResetter::class)->reset();
160 1
        gc_collect_cycles();
161
    }
162
163
    /**
164
     * Returns a new instance with the specified gRPC worker instance
165
     *
166
     * @return $this
167
     */
168 1
    public function withWorker(Worker $worker): self
169
    {
170 1
        $instance = clone $this;
171 1
        $instance->worker = $worker;
172
173 1
        return $instance;
174
    }
175
176
    /**
177
     * Transmitted services for registration gRPC server
178
     *
179
     * @param array $services Services array (key-value pairs)
180
     * ```php
181
     * [
182
     *      ServiceInterface::class
183
     * ]
184
     * ```
185
     *
186
     * @return $this
187
     */
188 2
    public function setServices(array $services): self
189
    {
190 2
        $this->services = $services;
191
192 2
        return $this;
193
    }
194
195 2
    public function getServices(): array
196
    {
197 2
        return $this->services;
198
    }
199
200 1
    public function getWorker(): Worker
201
    {
202 1
        return $this->worker ?? Worker::create();
203
    }
204
205 1
    public function getInvoker(): InvokerInterface
206
    {
207 1
        return $this->invoker ?? new Invoker();
208
    }
209
}
210