Test Failed
Pull Request — master (#93)
by
unknown
22:27
created

RoadRunnerGrpcApplicationRunner::run()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

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