Passed
Push — master ( dae4ba...d6dea1 )
by Sergei
11:47
created

ApplicationRunner   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Test Coverage

Coverage 98.28%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 47
c 4
b 1
f 0
dl 0
loc 174
ccs 57
cts 58
cp 0.9828
rs 10
wmc 19

10 Methods

Rating   Name   Duplication   Size   Complexity  
A runBootstrap() 0 8 2
A withConfig() 0 5 1
A withContainer() 0 5 1
A __construct() 0 15 1
A getContainer() 0 9 2
A createDefaultContainer() 0 27 5
A getConfiguration() 0 4 2
A createDefaultConfig() 0 13 1
A getConfig() 0 3 1
A checkEvents() 0 10 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Runner;
6
7
use ErrorException;
8
use Psr\Container\ContainerExceptionInterface;
9
use Psr\Container\ContainerInterface;
10
use Psr\Container\NotFoundExceptionInterface;
11
use RuntimeException;
12
use Yiisoft\Config\Config;
13
use Yiisoft\Config\ConfigInterface;
14
use Yiisoft\Config\ConfigPaths;
15
use Yiisoft\Config\Modifier\RecursiveMerge;
16
use Yiisoft\Config\Modifier\ReverseMerge;
17
use Yiisoft\Definitions\Exception\InvalidConfigException;
18
use Yiisoft\Di\Container;
19
use Yiisoft\Di\ContainerConfig;
20
use Yiisoft\Yii\Event\ListenerConfigurationChecker;
21
22
/**
23
 * Provides basic functionality for creating adapters.
24
 */
25
abstract class ApplicationRunner implements RunnerInterface
26
{
27
    private ?ConfigInterface $config = null;
28
    private ?ContainerInterface $container = null;
29
30
    /**
31
     * @param string $rootPath The absolute path to the project root.
32
     * @param bool $debug Whether the debug mode is enabled.
33
     * @param bool $checkEvents Whether to check events' configuration.
34
     * @param string|null $environment The environment name.
35
     * @param string $bootstrapGroup The bootstrap configuration group name.
36
     * @param string $eventsGroup The events' configuration group name.
37
     * @param string $diGroup The container definitions' configuration group name.
38
     * @param string $diProvidersGroup The container providers' configuration group name.
39
     * @param string $diDelegatesGroup The container delegates' configuration group name.
40
     * @param string $diTagsGroup The container tags' configuration group name.
41
     * @param string $paramsGroup The configuration parameters group name.
42
     * @param array $nestedParamsGroups Configuration group names that are included into configuration parameters group.
43
     * This is needed for recursive merging of parameters.
44
     * @param array $nestedEventsGroups Configuration group names that are included into events' configuration group.
45
     * This is needed for reverse and recursive merge of events' configurations.
46
     *
47
     * @psalm-param list<string> $nestedParamsGroups
48
     * @psalm-param list<string> $nestedEventsGroups
49
     */
50 13
    public function __construct(
51
        protected string $rootPath,
52
        protected bool $debug,
53
        protected bool $checkEvents,
54
        protected ?string $environment,
55
        protected string $bootstrapGroup,
56
        protected string $eventsGroup,
57
        protected string $diGroup,
58
        protected string $diProvidersGroup,
59
        protected string $diDelegatesGroup,
60
        protected string $diTagsGroup,
61
        protected string $paramsGroup,
62
        protected array $nestedParamsGroups,
63
        protected array $nestedEventsGroups,
64
    ) {
65 13
    }
66
67
    abstract public function run(): void;
68
69
    /**
70
     * Returns a new instance with the specified config instance {@see ConfigInterface}.
71
     *
72
     * @param ConfigInterface $config The config instance.
73
     */
74 4
    final public function withConfig(ConfigInterface $config): static
75
    {
76 4
        $new = clone $this;
77 4
        $new->config = $config;
78 4
        return $new;
79
    }
80
81
    /**
82
     * Returns a new instance with the specified container instance {@see ContainerInterface}.
83
     *
84
     * @param ContainerInterface $container The container instance.
85
     */
86 4
    final public function withContainer(ContainerInterface $container): static
87
    {
88 4
        $new = clone $this;
89 4
        $new->container = $container;
90 4
        return $new;
91
    }
92
93
    /**
94
     * @throws ErrorException|RuntimeException
95
     */
96 3
    final protected function runBootstrap(): void
97
    {
98 3
        $bootstrapList = $this->getConfiguration($this->bootstrapGroup);
99 3
        if (empty($bootstrapList)) {
100
            return;
101
        }
102
103 3
        (new BootstrapRunner($this->getContainer(), $bootstrapList))->run();
104
    }
105
106
    /**
107
     * @throws ContainerExceptionInterface|ErrorException|NotFoundExceptionInterface
108
     */
109 3
    final protected function checkEvents(): void
110
    {
111
        if (
112 3
            $this->checkEvents
113 3
            && null !== $configuration = $this->getConfiguration($this->eventsGroup)
114
        ) {
115
            /** @psalm-suppress MixedMethodCall */
116 3
            $this->getContainer()
117 3
                ->get(ListenerConfigurationChecker::class)
118 3
                ->check($configuration);
119
        }
120
    }
121
122
    /**
123
     * @throws ErrorException
124
     */
125 10
    final public function getConfig(): ConfigInterface
126
    {
127 10
        return $this->config ??= $this->createDefaultConfig();
128
    }
129
130
    /**
131
     * @throws ErrorException|InvalidConfigException
132
     */
133 9
    final public function getContainer(): ContainerInterface
134
    {
135 9
        $this->container ??= $this->createDefaultContainer();
136
137 9
        if ($this->container instanceof Container) {
138 8
            return $this->container->get(ContainerInterface::class);
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

138
            return $this->container->/** @scrutinizer ignore-call */ get(ContainerInterface::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
139
        }
140
141 1
        return $this->container;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->container could return the type null which is incompatible with the type-hinted return Psr\Container\ContainerInterface. Consider adding an additional type-check to rule them out.
Loading history...
142
    }
143
144 7
    final protected function getConfiguration(string $name): ?array
145
    {
146 7
        $config = $this->getConfig();
147 7
        return $config->has($name) ? $config->get($name) : null;
148
    }
149
150
    /**
151
     * @throws ErrorException
152
     */
153 7
    private function createDefaultConfig(): Config
154
    {
155 7
        $paramsGroups = [$this->paramsGroup, ...$this->nestedParamsGroups];
156 7
        $eventsGroups = [$this->eventsGroup, ...$this->nestedEventsGroups];
157
158 7
        return new Config(
159 7
            new ConfigPaths($this->rootPath, 'config'),
160 7
            $this->environment,
161 7
            [
162 7
                ReverseMerge::groups(...$eventsGroups),
0 ignored issues
show
Bug introduced by
It seems like $eventsGroups can also be of type array; however, parameter $groups of Yiisoft\Config\Modifier\ReverseMerge::groups() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

162
                ReverseMerge::groups(/** @scrutinizer ignore-type */ ...$eventsGroups),
Loading history...
163 7
                RecursiveMerge::groups(...$paramsGroups, ...$eventsGroups),
0 ignored issues
show
Bug introduced by
It seems like $paramsGroups can also be of type array; however, parameter $groups of Yiisoft\Config\Modifier\RecursiveMerge::groups() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

163
                RecursiveMerge::groups(/** @scrutinizer ignore-type */ ...$paramsGroups, ...$eventsGroups),
Loading history...
164 7
            ],
165 7
            $this->paramsGroup,
166 7
        );
167
    }
168
169
    /**
170
     * @throws ErrorException|InvalidConfigException
171
     */
172 6
    private function createDefaultContainer(): Container
173
    {
174 6
        $containerConfig = ContainerConfig::create()->withValidate($this->debug);
175
176 6
        $config = $this->getConfig();
177
178 6
        if (null !== $definitions = $this->getConfiguration($this->diGroup)) {
179 6
            $containerConfig = $containerConfig->withDefinitions($definitions);
180
        }
181
182 6
        if (null !== $providers = $this->getConfiguration($this->diProvidersGroup)) {
183 6
            $containerConfig = $containerConfig->withProviders($providers);
184
        }
185
186 6
        if (null !== $delegates = $this->getConfiguration($this->diDelegatesGroup)) {
187 6
            $containerConfig = $containerConfig->withDelegates($delegates);
188
        }
189
190 6
        if (null !== $tags = $this->getConfiguration($this->diTagsGroup)) {
191 6
            $containerConfig = $containerConfig->withTags($tags);
192
        }
193
194 6
        $containerConfig = $containerConfig->withDefinitions(
195 6
            array_merge($containerConfig->getDefinitions(), [ConfigInterface::class => $config])
196 6
        );
197
198 6
        return new Container($containerConfig);
199
    }
200
}
201