Issues (4)

src/ApplicationRunner.php (4 issues)

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
     * @param object[] $configModifiers Modifiers for {@see Config}.
47
     * @param string $configDirectory The relative path from {@see $rootPath} to the configuration storage location.
48
     * @param string $vendorDirectory The relative path from {@see $rootPath} to the vendor directory.
49
     * @param string $configMergePlanFile The relative path from {@see $configDirectory} to merge plan.
50
     *
51
     * @psalm-param list<string> $nestedParamsGroups
52
     * @psalm-param list<string> $nestedEventsGroups
53
     * @psalm-param list<object> $configModifiers
54
     */
55 18
    public function __construct(
56
        protected string $rootPath,
57
        protected bool $debug,
58
        protected bool $checkEvents,
59
        protected ?string $environment,
60
        protected string $bootstrapGroup,
61
        protected string $eventsGroup,
62
        protected string $diGroup,
63
        protected string $diProvidersGroup,
64
        protected string $diDelegatesGroup,
65
        protected string $diTagsGroup,
66
        protected string $paramsGroup,
67
        protected array $nestedParamsGroups,
68
        protected array $nestedEventsGroups,
69
        protected array $configModifiers = [],
70
        protected string $configDirectory = 'config',
71
        protected string $vendorDirectory = 'vendor',
72
        protected string $configMergePlanFile = '.merge-plan.php',
73
    ) {
74 18
    }
75
76
    abstract public function run(): void;
77
78
    /**
79
     * Returns a new instance with the specified config instance {@see ConfigInterface}.
80
     *
81
     * @param ConfigInterface $config The config instance.
82
     */
83 4
    final public function withConfig(ConfigInterface $config): static
84
    {
85 4
        $new = clone $this;
86 4
        $new->config = $config;
87 4
        return $new;
88
    }
89
90
    /**
91
     * Returns a new instance with the specified container instance {@see ContainerInterface}.
92
     *
93
     * @param ContainerInterface $container The container instance.
94
     */
95 4
    final public function withContainer(ContainerInterface $container): static
96
    {
97 4
        $new = clone $this;
98 4
        $new->container = $container;
99 4
        return $new;
100
    }
101
102
    /**
103
     * @throws ErrorException|RuntimeException
104
     */
105 4
    final protected function runBootstrap(): void
106
    {
107 4
        $bootstrapList = $this->getConfiguration($this->bootstrapGroup);
108 4
        if (empty($bootstrapList)) {
109 1
            return;
110
        }
111
112 3
        (new BootstrapRunner($this->getContainer(), $bootstrapList))->run();
113
    }
114
115
    /**
116
     * @throws ContainerExceptionInterface|ErrorException|NotFoundExceptionInterface
117
     */
118 4
    final protected function checkEvents(): void
119
    {
120
        if (
121 4
            $this->checkEvents
122 4
            && null !== $configuration = $this->getConfiguration($this->eventsGroup)
123
        ) {
124
            /** @psalm-suppress MixedMethodCall */
125 3
            $this->getContainer()
126 3
                ->get(ListenerConfigurationChecker::class)
127 3
                ->check($configuration);
128
        }
129
    }
130
131
    /**
132
     * @throws ErrorException
133
     */
134 14
    final public function getConfig(): ConfigInterface
135
    {
136 14
        return $this->config ??= $this->createDefaultConfig();
137
    }
138
139
    /**
140
     * @throws ErrorException|InvalidConfigException
141
     */
142 9
    final public function getContainer(): ContainerInterface
143
    {
144 9
        $this->container ??= $this->createDefaultContainer();
145
146 9
        if ($this->container instanceof Container) {
147 8
            return $this->container->get(ContainerInterface::class);
0 ignored issues
show
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

147
            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...
148
        }
149
150 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...
151
    }
152
153 8
    final protected function getConfiguration(string $name): ?array
154
    {
155 8
        $config = $this->getConfig();
156 8
        return $config->has($name) ? $config->get($name) : null;
157
    }
158
159
    /**
160
     * @throws ErrorException
161
     */
162 11
    private function createDefaultConfig(): Config
163
    {
164 11
        $paramsGroups = [$this->paramsGroup, ...$this->nestedParamsGroups];
165 11
        $eventsGroups = [$this->eventsGroup, ...$this->nestedEventsGroups];
166
167 11
        return new Config(
168 11
            new ConfigPaths($this->rootPath, $this->configDirectory, $this->vendorDirectory),
169 11
            $this->environment,
170 11
            [
171 11
                ReverseMerge::groups(...$eventsGroups),
0 ignored issues
show
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

171
                ReverseMerge::groups(/** @scrutinizer ignore-type */ ...$eventsGroups),
Loading history...
172 11
                RecursiveMerge::groups(...$paramsGroups, ...$eventsGroups),
0 ignored issues
show
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

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