Completed
Push — master ( ac0544...a10425 )
by Nate
11:46 queued 42s
created

Scope::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 0
cts 9
cp 0
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 3
crap 2
1
<?php
2
3
/**
4
 * @author    Flipbox Factory
5
 * @copyright Copyright (c) 2017, Flipbox Digital
6
 * @link      https://github.com/flipbox/transform/releases/latest
7
 * @license   https://github.com/flipbox/transform/blob/master/LICENSE
8
 */
9
10
namespace Flipbox\Transform;
11
12
use Flipbox\Transform\Helpers\TransformerHelper;
13
use Flipbox\Transform\Traits\ExtraParameterTrait;
14
use Flipbox\Transform\Transformers\TransformerInterface;
15
use InvalidArgumentException;
16
use ReflectionMethod;
17
use ReflectionParameter;
18
19
/**
20
 * @author Flipbox Factory <[email protected]>
21
 * @since 1.0.0
22
 */
23
class Scope
24
{
25
    use ExtraParameterTrait;
26
27
    /**
28
     * @var string
29
     */
30
    protected $scopeIdentifier;
31
32
    /**
33
     * @var Transform
34
     */
35
    protected $transform;
36
37
    /**
38
     * @var array
39
     */
40
    protected $parentScopes = [];
41
42
    /**
43
     * Scope constructor.
44
     * @param Transform $transform
45
     * @param string|null $scopeIdentifier
46
     * @param array $parentScopes
47
     */
48
    public function __construct(
49
        Transform $transform,
50
        string $scopeIdentifier = null,
51
        array $parentScopes = []
52
    ) {
53
        $this->transform = $transform;
54
        $this->scopeIdentifier = $scopeIdentifier;
55
        $this->parentScopes = $parentScopes;
56
    }
57
58
    /**
59
     * @return Transform
60
     */
61
    public function getTransform(): Transform
62
    {
63
        return $this->transform;
64
    }
65
66
    /**
67
     * @param $key
68
     * @return ParamBag
69
     */
70
    public function getParams(string $key = null): ParamBag
71
    {
72
        return $this->getTransform()->getParams(
73
            $this->getIdentifier($key)
74
        );
75
    }
76
77
    /**
78
     * Get the current identifier.
79
     *
80
     * @return string|null
81
     */
82
    public function getScopeIdentifier()
83
    {
84
        return $this->scopeIdentifier;
85
    }
86
87
    /**
88
     * Get the unique identifier for this scope.
89
     *
90
     * @param string $appendIdentifier
91
     *
92
     * @return string
93
     */
94
    public function getIdentifier(string $appendIdentifier = null): string
95
    {
96
        return implode(
97
            '.',
98
            array_filter(array_merge(
99
                $this->parentScopes,
100
                [
101
                    $this->scopeIdentifier,
102
                    $appendIdentifier
103
                ]
104
            ))
105
        );
106
    }
107
108
    /**
109
     * Getter for parentScopes.
110
     *
111
     * @return array
112
     */
113
    public function getParentScopes(): array
114
    {
115
        return $this->parentScopes;
116
    }
117
118
    /**
119
     * Is Requested.
120
     *
121
     * Check if - in relation to the current scope - this specific segment is allowed.
122
     * That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current
123
     * scope is a then c is not allowed, even if it is there and potentially transformable.
124
     *
125
     * @internal
126
     *
127
     * @param string $checkScopeSegment
128
     *
129
     * @return bool Returns the new number of elements in the array.
130
     */
131
    public function isRequested($checkScopeSegment): bool
132
    {
133
        return in_array(
134
            $this->scopeString($checkScopeSegment),
135
            $this->transform->getIncludes()
136
        );
137
    }
138
139
    /**
140
     * Is Excluded.
141
     *
142
     * Check if - in relation to the current scope - this specific segment should
143
     * be excluded. That means, if a.b.c is excluded and the current scope is a.b,
144
     * then c will not be allowed in the transformation whether it appears in
145
     * the list of default or available, requested includes.
146
     *
147
     * @internal
148
     *
149
     * @param string $checkScopeSegment
150
     *
151
     * @return bool
152
     */
153
    public function isExcluded($checkScopeSegment): bool
154
    {
155
        return in_array(
156
            $this->scopeString($checkScopeSegment),
157
            $this->transform->getExcludes()
158
        );
159
    }
160
161
    /**
162
     * @param TransformerInterface|callable $transformer
163
     * @param mixed $data
164
     * @param array $extra
165
     * @return mixed
166
     */
167
    public function transform(callable $transformer, $data, array $extra = [])
168
    {
169
        return $this->parseTransformer($transformer, $data, null, $extra);
170
    }
171
172
    /**
173
     * @param callable $transformer
174
     * @param string $key
175
     * @return bool
176
     */
177
    public function includeValue(callable $transformer, string $key): bool
178
    {
179
        // Ignore optional (that have not been explicitly requested)
180
        if ($transformer instanceof TransformerInterface &&
181
            TransformerHelper::inInclude($transformer, $key) &&
182
            !$this->isRequested($key)
183
        ) {
184
            return false;
185
        }
186
187
        // Ignore excludes
188
        if ($this->isExcluded($key)) {
189
            return false;
190
        }
191
192
        return true;
193
    }
194
195
    /**
196
     * @param $val
197
     * @param $data
198
     * @param string|null $key
199
     * @return mixed
200
     *
201
     * @deprecated It is recommended one uses static::parseNestedValue
202
     */
203
    public function parseValue($val, $data, string $key = null)
204
    {
205
        if (TransformerHelper::isTransformer($val)) {
206
            return $this->parseTransformer($val, $data, $key);
207
        }
208
209
        return $val;
210
    }
211
212
    /**
213
     * @param callable $transformer
214
     * @param $val
215
     * @param $data
216
     * @param string|null $key
217
     * @return mixed
218
     */
219
    public function parseNestedValue(callable $transformer, $val, $data, string $key = null)
220
    {
221
        if (TransformerHelper::isTransformer($val)) {
222
            return $this->parseTransformer($val, $data, $key);
223
        }
224
225
        if (is_array($val)) {
226
            foreach ($val as $k => $v) {
227
                $newKey = $key . '.' . $k;
228
229
                if (!$this->includeValue($transformer, $newKey)) {
230
                    unset($val[$k]);
231
                    continue;
232
                }
233
234
                $val[$k] = $this->parseNestedValue($transformer, $v, $val, $newKey);
235
            }
236
        }
237
238
        return $val;
239
    }
240
241
    /**
242
     * @param callable $transformer
243
     * @param $data
244
     * @param string|null $key
245
     * @param array $extra
246
     * @return mixed
247
     */
248
    public function parseTransformer(callable $transformer, $data, string $key = null, array $extra = [])
249
    {
250
        $args = [$data, $this, $key];
251
252
        if (!empty($extra)) {
253
            try {
254
                $args = array_merge(
255
                    $args,
256
                    $this->validParams($transformer, $extra)
257
                );
258
            } catch (\ReflectionException $e) {
259
                // Sorry
260
            }
261
        }
262
263
        return call_user_func_array($transformer, $args);
264
    }
265
266
    /**
267
     * @param string $identifier
268
     * @return Scope
269
     */
270
    public function childScope(string $identifier): Scope
271
    {
272
        $parentScopes = $this->getParentScopes();
273
        $parentScopes[] = $this->getScopeIdentifier();
274
275
        return new static(
276
            $this->getTransform(),
277
            $identifier,
278
            $parentScopes
279
        );
280
    }
281
282
    /**
283
     * Check, if this is the root scope.
284
     *
285
     * @return bool
286
     */
287
    protected function isRootScope(): bool
288
    {
289
        return empty($this->parentScopes);
290
    }
291
292
    /**
293
     * Filter the provided data with the requested filter fields for
294
     * the scope resource
295
     *
296
     * @param array $data
297
     *
298
     * @return array
299
     */
300
    public function filterFields(array $data): array
301
    {
302
        $fields = $this->getFilterFields();
303
304
        if ($fields === null) {
305
            return $data;
306
        }
307
308
        return array_intersect_key(
309
            $data,
310
            array_flip(
311
                iterator_to_array($fields)
312
            )
313
        );
314
    }
315
316
    /**
317
     * Return the requested filter fields for the scope resource
318
     *
319
     * @internal
320
     *
321
     * @return ParamBag|null
322
     */
323
    protected function getFilterFields()
324
    {
325
        return $this->transform->getField(
326
            $this->getScopeIdentifier()
327
        );
328
    }
329
330
    /**
331
     * @param string $checkScopeSegment
332
     * @return string
333
     */
334
    private function scopeString(string $checkScopeSegment): string
335
    {
336
        if (!empty($this->parentScopes)) {
337
            $scopeArray = array_slice($this->parentScopes, 1);
338
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
339
        } else {
340
            $scopeArray = [$checkScopeSegment];
341
        }
342
343
        return implode('.', (array)$scopeArray);
344
    }
345
}
346