Passed
Pull Request — master (#39)
by Alexander
05:25 queued 02:31
created

ApplicationRunner::getConfiguration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 2
rs 10
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 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 config parameters group name.
42
     * @param array $nestedParamsGroups Configuration group names that included to config parameters group. This is
43
     * needed for recursive merge parameters.
44
     * @param array $nestedEventsGroups Configuration group names that included to events' configuration group. This is
45
     * needed for reverse and recursive merge 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->debug
113 3
            && $this->checkEvents
114 3
            && null !== $configuration = $this->getConfiguration($this->eventsGroup)
115
        ) {
116
            /** @psalm-suppress MixedMethodCall */
117 3
            $this->getContainer()
118 3
                ->get(ListenerConfigurationChecker::class)
119 3
                ->check($configuration);
120
        }
121
    }
122
123
    /**
124
     * @throws ErrorException
125
     */
126 10
    final protected function getConfig(): ConfigInterface
127
    {
128 10
        return $this->config ??= $this->createDefaultConfig();
129
    }
130
131
    /**
132
     * @throws ErrorException|InvalidConfigException
133
     */
134 9
    final protected function getContainer(): ContainerInterface
135
    {
136 9
        $this->container ??= $this->createDefaultContainer();
137
138 9
        if ($this->container instanceof Container) {
139 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

139
            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...
140
        }
141
142 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...
143
    }
144
145 7
    final protected function getConfiguration(string $name): ?array
146
    {
147 7
        $config = $this->getConfig();
148 7
        return $config->has($name) ? $config->get($name) : null;
149
    }
150
151
    /**
152
     * @throws ErrorException
153
     */
154 7
    private function createDefaultConfig(): Config
155
    {
156 7
        $paramsGroups = [$this->paramsGroup, ...$this->nestedParamsGroups];
157 7
        $eventsGroups = [$this->eventsGroup, ...$this->nestedEventsGroups];
158
159 7
        return new Config(
160 7
            new ConfigPaths($this->rootPath, 'config'),
161 7
            $this->environment,
162 7
            [
163 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

163
                ReverseMerge::groups(/** @scrutinizer ignore-type */ ...$eventsGroups),
Loading history...
164 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

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