1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace hanneskod\readmetester\Config; |
6
|
|
|
|
7
|
|
|
final class ConfigManager |
8
|
|
|
{ |
9
|
|
|
/** @var array<string, mixed> */ |
10
|
|
|
private array $configs = []; |
11
|
|
|
|
12
|
|
|
/** @var array<RepositoryInterface> */ |
13
|
|
|
private array $repositories = []; |
14
|
|
|
|
15
|
|
|
/** @var array<Suite> */ |
16
|
|
|
private array $suites = []; |
17
|
|
|
|
18
|
|
|
public function __construct(RepositoryInterface ...$repos) |
19
|
|
|
{ |
20
|
|
|
foreach ($repos as $repo) { |
21
|
|
|
$this->loadRepository($repo); |
22
|
|
|
} |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
public function loadRepository(RepositoryInterface $repository): void |
26
|
|
|
{ |
27
|
|
|
// Reset configs to trigger rebuild |
28
|
|
|
$this->configs = []; |
29
|
|
|
|
30
|
|
|
$this->repositories[] = $repository; |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
/** @return iterable<string> */ |
34
|
|
|
public function getLoadedRepositoryNames(): iterable |
35
|
|
|
{ |
36
|
|
|
foreach ($this->repositories as $repository) { |
37
|
|
|
if ($repository->getRepositoryName()) { |
38
|
|
|
yield $repository->getRepositoryName(); |
39
|
|
|
} |
40
|
|
|
} |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
public function getBootstrap(): string |
44
|
|
|
{ |
45
|
|
|
$bootstrap = $this->readConfig(Configs::BOOTSTRAP); |
46
|
|
|
|
47
|
|
|
if ($bootstrap && (!is_file($bootstrap) || !is_readable($bootstrap))) { |
48
|
|
|
throw new \RuntimeException("Unable to load bootstrap: $bootstrap"); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
return $bootstrap; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
/** @return iterable<string> */ |
55
|
|
|
public function getSubscribers(): iterable |
56
|
|
|
{ |
57
|
|
|
yield from $this->readConfigList(Configs::SUBSCRIBERS); |
58
|
|
|
yield Configs::expand(Configs::OUTPUT_ID, $this->readConfig(Configs::OUTPUT)); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
public function getSuite(string $name): Suite |
62
|
|
|
{ |
63
|
|
|
foreach ($this->getUnfilteredSuites() as $suite) { |
64
|
|
|
if ($suite->getSuiteName() == $name) { |
65
|
|
|
return $suite; |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
throw new \RuntimeException("Unknown suite $name"); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** @return iterable<Suite> */ |
73
|
|
|
public function getAllSuites(): iterable |
74
|
|
|
{ |
75
|
|
|
foreach ($this->getUnfilteredSuites() as $suite) { |
76
|
|
|
if ($suite->isActive()) { |
77
|
|
|
yield $suite; |
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** @return iterable<Suite> */ |
83
|
|
|
private function getUnfilteredSuites(): iterable |
84
|
|
|
{ |
85
|
|
|
$this->rebuildConfigsIfOutdated(); |
86
|
|
|
yield from $this->suites; |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
private function readConfig(string $name): string |
90
|
|
|
{ |
91
|
|
|
$this->rebuildConfigsIfOutdated(); |
92
|
|
|
|
93
|
|
|
$value = $this->configs[$name] ?? ''; |
94
|
|
|
|
95
|
|
|
if (!is_scalar($value)) { |
96
|
|
|
throw new \RuntimeException("Configuration for '$name invalid."); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
return (string)$value; |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
/** @return array<string> */ |
103
|
|
|
private function readConfigList(string $name): array |
104
|
|
|
{ |
105
|
|
|
$this->rebuildConfigsIfOutdated(); |
106
|
|
|
|
107
|
|
|
$list = $this->configs[$name] ?? []; |
108
|
|
|
|
109
|
|
|
if (!is_array($list)) { |
110
|
|
|
throw new \RuntimeException("Configuration list for '$name' invalid."); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
$values = []; |
114
|
|
|
|
115
|
|
|
foreach ($list as $key => $value) { |
116
|
|
|
if (!is_scalar($value)) { |
117
|
|
|
throw new \RuntimeException(sprintf("Configuration for '%s[%s]' missing.", $name, $key)); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
$values[] = (string)$value; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
return $values; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
private function rebuildConfigsIfOutdated(): void |
127
|
|
|
{ |
128
|
|
|
// Only rebuild if neccesary |
129
|
|
|
if ($this->configs) { |
|
|
|
|
130
|
|
|
return; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
// Base configs |
134
|
|
|
$configs = [ |
135
|
|
|
Configs::SUITES => [], |
136
|
|
|
Configs::CLI => [], |
137
|
|
|
]; |
138
|
|
|
|
139
|
|
|
$defaults = []; |
140
|
|
|
|
141
|
|
|
// Merge with repositories |
142
|
|
|
foreach ($this->repositories as $repository) { |
143
|
|
|
$configs = array_merge($configs, $repository->getConfigs()); |
144
|
|
|
|
145
|
|
|
// Merge defaults separatly to account for multiple default definitions |
146
|
|
|
$defaults = array_merge( |
147
|
|
|
$defaults, |
148
|
|
|
(array)($repository->getConfigs()[Configs::DEFAULTS] ?? []) |
149
|
|
|
); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
// Create default suite if none exists |
153
|
|
|
if (empty($configs[Configs::SUITES])) { |
154
|
|
|
$configs[Configs::SUITES][Configs::DEFAULT_SUITE_NAME] = []; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
// Build suite objects |
158
|
|
|
$this->suites = []; |
159
|
|
|
|
160
|
|
|
foreach ($configs[Configs::SUITES] as $name => $suite) { |
161
|
|
|
// Merge suite with default configs |
162
|
|
|
$suite = array_merge($defaults, (array)$suite); |
163
|
|
|
|
164
|
|
|
// Merge suite with cli configs |
165
|
|
|
$suite = array_merge($suite, (array)$configs[Configs::CLI]); |
166
|
|
|
|
167
|
|
|
// Build suite |
168
|
|
|
$this->suites[] = new Suite( |
169
|
|
|
name: (string)$name, |
170
|
|
|
active: (bool)($suite[Configs::ACTIVE] ?? true), |
171
|
|
|
inputLanguage: (string)($suite[Configs::INPUT_LANGUAGE] ?? ''), |
172
|
|
|
runner: (string)($suite[Configs::RUNNER] ?? ''), |
173
|
|
|
includePaths: (array)($suite[Configs::INCLUDE_PATHS] ?? []), |
174
|
|
|
excludePaths: (array)($suite[Configs::EXCLUDE_PATHS] ?? []), |
175
|
|
|
fileExtensions: (array)($suite[Configs::FILE_EXTENSIONS] ?? []), |
176
|
|
|
stopOnFailure: (bool)($suite[Configs::STOP_ON_FAILURE] ?? false), |
177
|
|
|
globalAttributes: (array)($suite[Configs::GLOBAL_ATTRIBUTES] ?? []), |
178
|
|
|
filter: (string)($suite[Configs::FILTER] ?? ''), |
179
|
|
|
readFromStdin: (bool)($suite[Configs::STDIN] ?? false), |
180
|
|
|
); |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
// Store configs |
184
|
|
|
$this->configs = array_merge($configs, $configs[Configs::CLI]); |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.