ScopeTokenResolver::getScope()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 3
cts 4
cp 0.75
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
crap 2.0625
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
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
}