|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace ConfigToken\TokenResolver\Types; |
|
4
|
|
|
|
|
5
|
|
|
use ConfigToken\TokenResolver\Exception\OutOfScopeException; |
|
6
|
|
|
use ConfigToken\TokenResolver\Exception\ScopeTokenValueSerializationException; |
|
7
|
|
|
use ConfigToken\TokenResolver\Exception\TokenFormatException; |
|
8
|
|
|
use ConfigToken\TokenResolver\Exception\UnknownTokenException; |
|
9
|
|
|
use ConfigToken\TokenResolver\ScopeTokenValueSerializerInterface; |
|
10
|
|
|
|
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* Class ScopeTokenResolver |
|
14
|
|
|
* |
|
15
|
|
|
* Resolve tokens based on a scope represented by an associative array with string keys. |
|
16
|
|
|
* The token format is: $scopeName$scopeNameDelimiter$scopeLevel1[$scopeLevelDelimiter$scopeLevelN]] |
|
17
|
|
|
* |
|
18
|
|
|
* Examples: |
|
19
|
|
|
* json:firstLevel.secondLevel |
|
20
|
|
|
* (the scope path is: firstLevel.secondLevel) |
|
21
|
|
|
* - scopeTokenName = 'json' |
|
22
|
|
|
* - scopeTokenNameDelimiter = ':' |
|
23
|
|
|
* - scopeLevelDelimiter = '.' |
|
24
|
|
|
* |
|
25
|
|
|
* will result in the value: 'exampleValue' |
|
26
|
|
|
* if the scope array is: |
|
27
|
|
|
* scope = array( |
|
28
|
|
|
* 'firstLevel' => array( |
|
29
|
|
|
* 'secondLevel' => 'exampleValue' |
|
30
|
|
|
* ) |
|
31
|
|
|
* ) |
|
32
|
|
|
* |
|
33
|
|
|
* or will result in the value: "{'exampleValue': 5}" |
|
34
|
|
|
* if the scope array is: |
|
35
|
|
|
* scope = array( |
|
36
|
|
|
* 'firstLevel' => array( |
|
37
|
|
|
* 'secondLevel' => array( |
|
38
|
|
|
* 'exampleValue' => 5 |
|
39
|
|
|
* ) |
|
40
|
|
|
* ) |
|
41
|
|
|
* ) |
|
42
|
|
|
* and the serializer is JsonScopeTokenValueSerializer |
|
43
|
|
|
* |
|
44
|
|
|
* @package ConfigToken\Library\TokenResolver |
|
45
|
|
|
*/ |
|
46
|
|
|
class ScopeTokenResolver extends AbstractTokenResolver |
|
47
|
|
|
{ |
|
48
|
|
|
/** @var string */ |
|
49
|
|
|
protected $scopeTokenName; |
|
50
|
|
|
|
|
51
|
|
|
/** @var array */ |
|
52
|
|
|
protected $scope = array(); |
|
53
|
|
|
|
|
54
|
|
|
/** @var string */ |
|
55
|
|
|
protected $scopeTokenNameDelimiter = ':'; |
|
56
|
|
|
|
|
57
|
|
|
/** @var string */ |
|
58
|
|
|
protected $scopeLevelDelimiter = '.'; |
|
59
|
|
|
|
|
60
|
|
|
/** @var boolean */ |
|
61
|
|
|
protected $ignoreOutOfScope; |
|
62
|
|
|
|
|
63
|
|
|
/** @var ScopeTokenValueSerializerInterface */ |
|
64
|
|
|
protected $serializer; |
|
65
|
|
|
|
|
66
|
|
|
|
|
67
|
8 |
|
public function __construct($scopeTokenName = null, $scope = null, $ignoreOutOfScope = False) |
|
68
|
|
|
{ |
|
69
|
8 |
|
$this->setScopeTokenName($scopeTokenName); |
|
70
|
8 |
|
$this->setIgnoreOutOfScope($ignoreOutOfScope); |
|
71
|
|
|
|
|
72
|
8 |
|
if (isset($scope)) { |
|
73
|
7 |
|
$this->setScope($scope); |
|
74
|
7 |
|
} |
|
75
|
8 |
|
} |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* Get the token resolver type identifier. |
|
79
|
|
|
* |
|
80
|
|
|
* @return string |
|
81
|
|
|
*/ |
|
82
|
1 |
|
public static function getType() |
|
83
|
|
|
{ |
|
84
|
1 |
|
return self::getBaseType(); |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Get the token resolver base type identifier. |
|
89
|
|
|
* |
|
90
|
|
|
* @return string |
|
91
|
|
|
*/ |
|
92
|
7 |
|
public static function getBaseType() |
|
93
|
|
|
{ |
|
94
|
7 |
|
return 'scope'; |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
/** |
|
98
|
|
|
* Check if the scope token name is set. |
|
99
|
|
|
* |
|
100
|
|
|
* @return boolean |
|
101
|
|
|
*/ |
|
102
|
|
|
public function hasScopeTokenName() |
|
103
|
|
|
{ |
|
104
|
|
|
return isset($this->scopeTokenName); |
|
105
|
|
|
} |
|
106
|
|
|
|
|
107
|
|
|
/** |
|
108
|
|
|
* Set the scope token name. |
|
109
|
|
|
* |
|
110
|
|
|
* @param string $value |
|
111
|
|
|
* @return $this |
|
112
|
|
|
*/ |
|
113
|
8 |
|
public function setScopeTokenName($value) |
|
114
|
|
|
{ |
|
115
|
8 |
|
$this->scopeTokenName = $value; |
|
116
|
8 |
|
return $this; |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
/** |
|
120
|
|
|
* Get scope token name. |
|
121
|
|
|
* |
|
122
|
|
|
* @return string|null |
|
123
|
|
|
*/ |
|
124
|
1 |
|
public function getScopeTokenName() |
|
125
|
|
|
{ |
|
126
|
1 |
|
return $this->scopeTokenName; |
|
127
|
|
|
} |
|
128
|
|
|
|
|
129
|
|
|
/** |
|
130
|
|
|
* Check if scope was set. |
|
131
|
|
|
* |
|
132
|
|
|
* @return boolean |
|
133
|
|
|
*/ |
|
134
|
5 |
|
public function hasScope() |
|
135
|
|
|
{ |
|
136
|
5 |
|
return isset($this->scope) && (count($this->scope) > 0); |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
/** |
|
140
|
|
|
* Get scope. |
|
141
|
|
|
* |
|
142
|
|
|
* @return array|null |
|
143
|
|
|
*/ |
|
144
|
1 |
|
public function getScope() |
|
145
|
|
|
{ |
|
146
|
1 |
|
if (!$this->hasScope()) { |
|
147
|
|
|
return null; |
|
148
|
|
|
} |
|
149
|
1 |
|
return $this->scope; |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
/** |
|
153
|
|
|
* Set scope. |
|
154
|
|
|
* |
|
155
|
|
|
* @param array $value The new value. |
|
156
|
|
|
* @return $this |
|
157
|
|
|
*/ |
|
158
|
7 |
|
public function setScope($value) |
|
159
|
|
|
{ |
|
160
|
7 |
|
$this->scope = $value; |
|
161
|
7 |
|
return $this; |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
/** |
|
165
|
|
|
* Get scope token name delimiter. |
|
166
|
|
|
* |
|
167
|
|
|
* @return string |
|
168
|
|
|
*/ |
|
169
|
1 |
|
public function getScopeTokenNameDelimiter() |
|
170
|
|
|
{ |
|
171
|
1 |
|
return $this->scopeTokenNameDelimiter; |
|
172
|
|
|
} |
|
173
|
|
|
|
|
174
|
|
|
/** |
|
175
|
|
|
* Set scope token name delimiter. |
|
176
|
|
|
* |
|
177
|
|
|
* @param string $value The new value. |
|
178
|
|
|
* @return $this |
|
179
|
|
|
*/ |
|
180
|
1 |
|
public function setScopeTokenNameDelimiter($value) |
|
181
|
|
|
{ |
|
182
|
1 |
|
$this->scopeTokenNameDelimiter = $value; |
|
183
|
1 |
|
return $this; |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
/** |
|
187
|
|
|
* Get scope level delimiter. |
|
188
|
|
|
* |
|
189
|
|
|
* @return string |
|
190
|
|
|
*/ |
|
191
|
1 |
|
public function getScopeLevelDelimiter() |
|
192
|
|
|
{ |
|
193
|
1 |
|
return $this->scopeLevelDelimiter; |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
/** |
|
197
|
|
|
* Set scope level delimiter. |
|
198
|
|
|
* |
|
199
|
|
|
* @param string $value The new value. |
|
200
|
|
|
* @return $this |
|
201
|
|
|
*/ |
|
202
|
1 |
|
public function setScopeLevelDelimiter($value) |
|
203
|
|
|
{ |
|
204
|
1 |
|
$this->scopeLevelDelimiter = $value; |
|
205
|
1 |
|
return $this; |
|
206
|
|
|
} |
|
207
|
|
|
|
|
208
|
|
|
/** |
|
209
|
|
|
* Get ignore out of scope flag. |
|
210
|
|
|
* |
|
211
|
|
|
* @return boolean |
|
212
|
|
|
*/ |
|
213
|
1 |
|
public function getIgnoreOutOfScope() |
|
214
|
|
|
{ |
|
215
|
1 |
|
return $this->ignoreOutOfScope; |
|
216
|
|
|
} |
|
217
|
|
|
|
|
218
|
|
|
/** |
|
219
|
|
|
* Set ignore out of scope flag. |
|
220
|
|
|
* |
|
221
|
|
|
* @param boolean $value The new value. |
|
222
|
|
|
* @return $this |
|
223
|
|
|
*/ |
|
224
|
8 |
|
public function setIgnoreOutOfScope($value) |
|
225
|
|
|
{ |
|
226
|
8 |
|
$this->ignoreOutOfScope = $value; |
|
227
|
8 |
|
return $this; |
|
228
|
|
|
} |
|
229
|
|
|
|
|
230
|
|
|
/** |
|
231
|
|
|
* Check if scope token value serializer was set. |
|
232
|
|
|
* |
|
233
|
|
|
* @return boolean |
|
234
|
|
|
*/ |
|
235
|
4 |
|
public function hasSerializer() |
|
236
|
|
|
{ |
|
237
|
4 |
|
return isset($this->serializer); |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
/** |
|
241
|
|
|
* Get scope token value serializer. |
|
242
|
|
|
* |
|
243
|
|
|
* @return ScopeTokenValueSerializerInterface|null |
|
244
|
|
|
*/ |
|
245
|
1 |
|
public function getSerializer() |
|
246
|
|
|
{ |
|
247
|
1 |
|
if (!$this->hasSerializer()) { |
|
248
|
|
|
return null; |
|
249
|
|
|
} |
|
250
|
1 |
|
return $this->serializer; |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
/** |
|
254
|
|
|
* Set scope token value serializer. |
|
255
|
|
|
* |
|
256
|
|
|
* @param ScopeTokenValueSerializerInterface $value The new value. |
|
257
|
|
|
* @return $this |
|
258
|
|
|
*/ |
|
259
|
4 |
|
public function setSerializer($value) |
|
260
|
|
|
{ |
|
261
|
4 |
|
$this->serializer = $value; |
|
262
|
4 |
|
return $this; |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
/** |
|
266
|
|
|
* Get the value at the given tree path from the scope. |
|
267
|
|
|
* |
|
268
|
|
|
* @param string $pathStr |
|
269
|
|
|
* @return mixed |
|
270
|
|
|
* @throws \Exception |
|
271
|
|
|
* @throws OutOfScopeException |
|
272
|
|
|
*/ |
|
273
|
5 |
|
protected function getFromScopeByPath($pathStr) |
|
274
|
|
|
{ |
|
275
|
5 |
|
if (!$this->hasScope()) { |
|
276
|
|
|
throw new \Exception(sprintf('No scope was set for the %s resolver.', get_called_class())); |
|
277
|
|
|
} |
|
278
|
5 |
|
$path = explode($this->scopeLevelDelimiter, $pathStr); |
|
279
|
5 |
View Code Duplication |
while (count($path) > 0) { |
|
|
|
|
|
|
280
|
5 |
|
$k = count($path)-1; |
|
281
|
5 |
|
if (trim($path[$k]) == '') { |
|
282
|
1 |
|
unset($path[$k]); |
|
283
|
1 |
|
} else { |
|
284
|
5 |
|
break; |
|
285
|
|
|
} |
|
286
|
1 |
|
} |
|
287
|
5 |
|
$scopePtr = &$this->scope; |
|
288
|
5 |
|
foreach ($path as $leaf) { |
|
289
|
5 |
|
if (array_key_exists($leaf, $scopePtr)) { |
|
290
|
4 |
|
$scopePtr = &$scopePtr[$leaf]; |
|
291
|
4 |
|
} else { |
|
292
|
2 |
|
throw new OutOfScopeException( |
|
293
|
2 |
|
sprintf( |
|
294
|
2 |
|
'The path "%s" is outside of the scope set for %s: %s', |
|
295
|
2 |
|
$pathStr, |
|
296
|
2 |
|
get_called_class(), |
|
297
|
2 |
|
json_encode($this->scope) |
|
298
|
2 |
|
) |
|
299
|
2 |
|
); |
|
300
|
|
|
} |
|
301
|
4 |
|
} |
|
302
|
4 |
|
return $scopePtr; |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* Check if the token value with the given name is registered. |
|
307
|
|
|
* |
|
308
|
|
|
* @param string $tokenName The identifier of the token value. |
|
309
|
|
|
* @return boolean |
|
310
|
|
|
*/ |
|
311
|
1 |
|
public function isTokenValueRegistered($tokenName) |
|
312
|
|
|
{ |
|
313
|
1 |
|
$t = explode($this->scopeTokenNameDelimiter, $tokenName); |
|
314
|
1 |
|
$token = $t[0]; |
|
315
|
1 |
|
if (($token !== $this->scopeTokenName) || (count($t) != 2) || (!$this->hasScope())) { |
|
316
|
1 |
|
return false; |
|
317
|
|
|
} |
|
318
|
|
|
try { |
|
319
|
1 |
|
$this->getFromScopeByPath($t[1]); |
|
320
|
1 |
|
} catch (\Exception $e) { |
|
321
|
1 |
|
return false; |
|
322
|
|
|
} |
|
323
|
1 |
|
return true; |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
/** |
|
327
|
|
|
* Get the value for the given token. |
|
328
|
|
|
* |
|
329
|
|
|
* @param string $tokenName The name of the token to be resolved to a value. |
|
330
|
|
|
* @param boolean|null $ignoreUnknownTokens If True, passing an unresolvable token will not cause an exception. |
|
331
|
|
|
* @param string|null $defaultValue The value returned if the token is not found and set to ignore unknown tokens. |
|
332
|
|
|
* @throws OutOfScopeException |
|
333
|
|
|
* If the path portion of the token name does not exist in the given scope and |
|
334
|
|
|
* not set to ignore unknown tokens. |
|
335
|
|
|
* @throws ScopeTokenValueSerializationException |
|
336
|
|
|
* If the value at the specified path was not properly serialized to string or no serializer was set. |
|
337
|
|
|
* @throws TokenFormatException |
|
338
|
|
|
* If no path was specified. |
|
339
|
|
|
* @throws UnknownTokenException |
|
340
|
|
|
* If the name portion of the token name does not match the scope token name and |
|
341
|
|
|
* not set to ignore unknown tokens. |
|
342
|
|
|
* @throws \Exception |
|
343
|
|
|
* @return null|string |
|
344
|
|
|
*/ |
|
345
|
7 |
|
public function getTokenValue($tokenName, $ignoreUnknownTokens = null, $defaultValue = null) |
|
346
|
|
|
{ |
|
347
|
7 |
|
if (is_null($ignoreUnknownTokens)) { |
|
348
|
|
|
$ignoreUnknownTokens = $this->getIgnoreUnknownTokens(); |
|
349
|
|
|
} |
|
350
|
7 |
|
$t = explode($this->scopeTokenNameDelimiter, $tokenName); |
|
351
|
7 |
|
$token = $t[0]; |
|
352
|
7 |
|
if ($token !== $this->scopeTokenName) { |
|
353
|
1 |
|
if ($ignoreUnknownTokens) { |
|
354
|
|
|
return $defaultValue; |
|
355
|
|
|
} |
|
356
|
1 |
|
throw new UnknownTokenException($token, $tokenName); |
|
357
|
|
|
} |
|
358
|
6 |
View Code Duplication |
while (count($t) > 0) { |
|
|
|
|
|
|
359
|
6 |
|
$k = count($t) - 1; |
|
360
|
6 |
|
if (trim($t[$k]) == '') { |
|
361
|
1 |
|
unset($t[$k]); |
|
362
|
1 |
|
} else { |
|
363
|
6 |
|
break; |
|
364
|
|
|
} |
|
365
|
1 |
|
} |
|
366
|
6 |
|
if (count($t) != 2) { |
|
367
|
1 |
|
throw new TokenFormatException(sprintf('No path specified for scope reference token "%s".', $tokenName)); |
|
368
|
|
|
} |
|
369
|
|
|
try { |
|
370
|
5 |
|
$scopeValue = $this->getFromScopeByPath($t[1]); |
|
371
|
5 |
|
} catch (OutOfScopeException $e) { |
|
372
|
1 |
|
$scopeValue = $defaultValue; |
|
373
|
1 |
|
if (!$this->ignoreOutOfScope) { |
|
374
|
1 |
|
throw $e; |
|
375
|
|
|
} |
|
376
|
|
|
if (!$this->ignoreUnknownTokens) { |
|
377
|
|
|
throw new UnknownTokenException($tokenName); |
|
378
|
|
|
} |
|
379
|
|
|
} |
|
380
|
|
|
|
|
381
|
4 |
|
if ($this->hasSerializer()) { |
|
382
|
3 |
|
$result = $this->serializer->getSerializedValue($scopeValue); |
|
383
|
2 |
|
if (gettype($result) == 'string') { |
|
384
|
2 |
|
return $result; |
|
385
|
|
|
} |
|
386
|
1 |
|
throw new ScopeTokenValueSerializationException( |
|
387
|
1 |
|
sprintf( |
|
388
|
1 |
|
'Value for scope reference token "%s" of %s was not properly serialized. (%s)', |
|
389
|
1 |
|
$tokenName, |
|
390
|
1 |
|
get_called_class(), |
|
391
|
1 |
|
'Serializer: ' . ($this->hasSerializer() ? get_class($this->serializer) : 'not set') |
|
392
|
1 |
|
) |
|
393
|
1 |
|
); |
|
394
|
|
|
} else { |
|
395
|
1 |
|
if (is_string($scopeValue)) { |
|
396
|
1 |
|
return $scopeValue; |
|
397
|
|
|
} |
|
398
|
1 |
|
if (is_int($scopeValue)) { |
|
399
|
|
|
return sprintf('%d', $scopeValue); |
|
400
|
|
|
} |
|
401
|
1 |
|
if (is_float($scopeValue)) { |
|
402
|
|
|
return sprintf('%.5f', $scopeValue); |
|
403
|
|
|
} |
|
404
|
1 |
|
throw new ScopeTokenValueSerializationException( |
|
405
|
1 |
|
sprintf( |
|
406
|
1 |
|
'Value of type "%s" for scope reference token "%s" of %s cannot be converted to string. (Serializer not set)', |
|
407
|
1 |
|
gettype($scopeValue), |
|
408
|
1 |
|
$tokenName, |
|
409
|
1 |
|
get_called_class() |
|
410
|
1 |
|
) |
|
411
|
1 |
|
); |
|
412
|
|
|
} |
|
413
|
|
|
} |
|
414
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.