1
|
|
|
<?php |
2
|
|
|
namespace Wandu\Config; |
3
|
|
|
|
4
|
|
|
use InvalidArgumentException; |
5
|
|
|
use Wandu\Config\Contracts\Config as ConfigContract; |
6
|
|
|
use Wandu\Config\Contracts\Loader; |
7
|
|
|
use Wandu\Config\Exception\CannotLoadException; |
8
|
|
|
use Wandu\Config\Exception\NotAllowedMethodException; |
9
|
|
|
|
10
|
|
|
class Config implements ConfigContract |
11
|
|
|
{ |
12
|
|
|
/** @var array */ |
13
|
|
|
protected $items; |
14
|
|
|
|
15
|
|
|
/** @var \Wandu\Config\Contracts\Loader[] */ |
16
|
|
|
protected $loaders = []; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @param array $items |
20
|
|
|
*/ |
21
|
21 |
|
public function __construct(array $items = []) |
22
|
|
|
{ |
23
|
21 |
|
$this->items = $items; |
24
|
21 |
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @param \Wandu\Config\Contracts\Loader $loader |
28
|
|
|
*/ |
29
|
2 |
|
public function pushLoader(Loader $loader) |
30
|
|
|
{ |
31
|
2 |
|
$this->loaders[] = $loader; |
32
|
2 |
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @param string $path |
36
|
|
|
*/ |
37
|
2 |
|
public function load(string $path) |
38
|
|
|
{ |
39
|
2 |
|
foreach ($this->loaders as $loader) { |
40
|
2 |
|
if ($loader->test($path)) { |
41
|
2 |
|
$this->merge($loader->load($path)); |
|
|
|
|
42
|
2 |
|
return; |
43
|
|
|
} |
44
|
|
|
} |
45
|
|
|
throw new CannotLoadException($path); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @param array $appender |
50
|
|
|
*/ |
51
|
4 |
|
public function merge(array $appender) |
52
|
|
|
{ |
53
|
4 |
|
$this->items = $this->recursiveMerge($this->items, $appender); |
|
|
|
|
54
|
4 |
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @param mixed $origin |
58
|
|
|
* @param mixed $appender |
59
|
|
|
* @return mixed |
60
|
|
|
*/ |
61
|
4 |
|
private function recursiveMerge($origin, $appender) |
62
|
|
|
{ |
63
|
4 |
|
if (is_array($origin) |
64
|
4 |
|
&& array_values($origin) !== $origin |
65
|
4 |
|
&& is_array($appender) |
66
|
4 |
|
&& array_values($appender) !== $appender) { |
67
|
4 |
|
foreach ($appender as $key => $value) { |
68
|
4 |
|
if (isset($origin[$key])) { |
69
|
2 |
|
$origin[$key] = $this->recursiveMerge($origin[$key], $value); |
70
|
|
|
} else { |
71
|
4 |
|
$origin[$key] = $value; |
72
|
|
|
} |
73
|
|
|
} |
74
|
4 |
|
return $origin; |
75
|
|
|
} |
76
|
4 |
|
return $appender; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* {@inheritdoc} |
81
|
|
|
*/ |
82
|
4 |
|
public function toArray(): array |
83
|
|
|
{ |
84
|
4 |
|
return $this->items ?? []; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* {@inheritdoc} |
89
|
|
|
*/ |
90
|
1 |
|
public function has($name): bool |
91
|
|
|
{ |
92
|
1 |
|
if ($name === '') { |
93
|
|
|
return true; |
94
|
|
|
} |
95
|
1 |
|
$names = explode('.', $name); |
96
|
1 |
|
$dataToReturn = $this->items; |
97
|
1 |
|
while (count($names)) { |
98
|
1 |
|
$name = array_shift($names); |
99
|
1 |
|
if (!is_array($dataToReturn) || !array_key_exists($name, $dataToReturn)) { |
100
|
1 |
|
return false; |
101
|
|
|
} |
102
|
1 |
|
$dataToReturn = $dataToReturn[$name]; |
103
|
|
|
} |
104
|
1 |
|
return true; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* {@inheritdoc} |
109
|
|
|
*/ |
110
|
11 |
|
public function get($name, $default = null) |
111
|
|
|
{ |
112
|
11 |
|
if ($name === '') { |
113
|
1 |
|
return $this->items; |
114
|
|
|
} |
115
|
11 |
|
$names = explode('.', $name); |
116
|
11 |
|
$dataToReturn = $this->items; |
117
|
11 |
|
while (count($names)) { |
118
|
11 |
|
$name = array_shift($names); |
119
|
11 |
|
if (!is_array($dataToReturn) || !array_key_exists($name, $dataToReturn)) { |
120
|
7 |
|
return $default; |
121
|
|
|
} |
122
|
9 |
|
$dataToReturn = $dataToReturn[$name]; |
123
|
|
|
} |
124
|
9 |
|
return $dataToReturn; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* {@inheritdoc} |
129
|
|
|
*/ |
130
|
1 |
|
public function subset($name): ConfigContract |
131
|
|
|
{ |
132
|
1 |
|
$subset = $this->get($name); |
133
|
1 |
|
if (!is_array($subset)) { |
134
|
1 |
|
throw new InvalidArgumentException('subset must be an array.'); |
135
|
|
|
} |
136
|
1 |
|
return new static($subset); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* {@inheritdoc} |
141
|
|
|
*/ |
142
|
|
|
public function offsetExists($offset) |
143
|
|
|
{ |
144
|
|
|
return $this->has($offset); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* {@inheritdoc} |
149
|
|
|
*/ |
150
|
3 |
|
public function offsetGet($offset) |
151
|
|
|
{ |
152
|
3 |
|
if (strpos($offset, '||') !== false) { |
153
|
1 |
|
list($offset, $default) = explode('||', $offset); |
154
|
1 |
|
return $this->get($offset, $default); |
155
|
|
|
} |
156
|
3 |
|
return $this->get($offset); |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* {@inheritdoc} |
161
|
|
|
*/ |
162
|
1 |
|
public function offsetSet($offset, $value) |
163
|
|
|
{ |
164
|
1 |
|
throw new NotAllowedMethodException(__FUNCTION__, __CLASS__); |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
/** |
168
|
|
|
* {@inheritdoc} |
169
|
|
|
*/ |
170
|
1 |
|
public function offsetUnset($offset) |
171
|
|
|
{ |
172
|
1 |
|
throw new NotAllowedMethodException(__FUNCTION__, __CLASS__); |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.