Scope   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 392
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 3
dl 0
loc 392
ccs 0
cts 218
cp 0
rs 9.68
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getTransform() 0 4 1
A getParams() 0 6 1
A getIdentifier() 0 13 1
A isRequested() 0 7 1
A isExcluded() 0 7 1
A includeValue() 0 17 5
A transform() 0 11 1
A prepareData() 0 18 6
A prepareValue() 0 12 3
A prepareResource() 0 13 1
A prepareCallable() 0 29 3
A prepareClosure() 0 21 1
A prepareTransformer() 0 21 1
A childScope() 0 11 1
A isRootScope() 0 4 1
A filterFields() 0 15 2
A getFilterFields() 0 6 1
A scopeString() 0 11 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\ArgumentHelper;
13
use Flipbox\Transform\Helpers\TransformerHelper;
14
use Flipbox\Transform\Resources\ResourceInterface;
15
use Flipbox\Transform\Transformers\TransformerInterface;
16
17
/**
18
 * @author Flipbox Factory <[email protected]>
19
 * @since 3.0.0
20
 */
21
class Scope
22
{
23
    /**
24
     * @var string
25
     */
26
    protected $scopeIdentifier;
27
28
    /**
29
     * @var Transform
30
     */
31
    protected $transform;
32
33
    /**
34
     * @var array
35
     */
36
    protected $parentScopes = [];
37
38
    /**
39
     * Scope constructor.
40
     * @param Transform $transform
41
     * @param string|null $scopeIdentifier
42
     * @param array $parentScopes
43
     */
44
    public function __construct(
45
        Transform $transform,
46
        string $scopeIdentifier = null,
47
        array $parentScopes = []
48
    ) {
49
        $this->transform = $transform;
50
        $this->scopeIdentifier = $scopeIdentifier;
51
        $this->parentScopes = $parentScopes;
52
    }
53
54
    /**
55
     * @return Transform
56
     */
57
    public function getTransform(): Transform
58
    {
59
        return $this->transform;
60
    }
61
62
    /**
63
     * @param $key
64
     * @return ParamBag
65
     */
66
    public function getParams(string $key = null): ParamBag
67
    {
68
        return $this->getTransform()->getParams(
69
            $this->getIdentifier($key)
70
        );
71
    }
72
73
74
    /**
75
     * Get the unique identifier for this scope.
76
     *
77
     * @param string $appendIdentifier
78
     *
79
     * @return string
80
     */
81
    public function getIdentifier(string $appendIdentifier = null): string
82
    {
83
        return implode(
84
            '.',
85
            array_filter(array_merge(
86
                $this->parentScopes,
87
                [
88
                    $this->scopeIdentifier,
89
                    $appendIdentifier
90
                ]
91
            ))
92
        );
93
    }
94
95
    /**
96
     * Is Requested.
97
     *
98
     * Check if - in relation to the current scope - this specific segment is allowed.
99
     * That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current
100
     * scope is a then c is not allowed, even if it is there and potentially transformable.
101
     *
102
     * @param string $checkScopeSegment
103
     *
104
     * @return bool Returns the new number of elements in the array.
105
     */
106
    public function isRequested($checkScopeSegment): bool
107
    {
108
        return in_array(
109
            $this->scopeString($checkScopeSegment),
110
            $this->transform->getIncludes()
111
        );
112
    }
113
114
    /**
115
     * Is Excluded.
116
     *
117
     * Check if - in relation to the current scope - this specific segment should
118
     * be excluded. That means, if a.b.c is excluded and the current scope is a.b,
119
     * then c will not be allowed in the transformation whether it appears in
120
     * the list of default or available, requested includes.
121
     *
122
     * @param string $checkScopeSegment
123
     *
124
     * @return bool
125
     */
126
    protected function isExcluded($checkScopeSegment): bool
127
    {
128
        return in_array(
129
            $this->scopeString($checkScopeSegment),
130
            $this->transform->getExcludes()
131
        );
132
    }
133
134
    /**
135
     * @param callable $transformer
136
     * @param string $key
137
     * @return bool
138
     */
139
    public function includeValue(callable $transformer, string $key): bool
140
    {
141
        // Ignore optional (that have not been explicitly requested)
142
        if ($transformer instanceof TransformerInterface &&
143
            TransformerHelper::inInclude($transformer, $key) &&
144
            !$this->isRequested($key)
145
        ) {
146
            return false;
147
        }
148
149
        // Ignore excludes
150
        if ($this->isExcluded($key)) {
151
            return false;
152
        }
153
154
        return true;
155
    }
156
157
    /**
158
     * @param callable $transformer
159
     * @param mixed $data
160
     * @param array $extra
161
     * @return mixed
162
     */
163
    public function transform(callable $transformer, $data, array $extra = [])
164
    {
165
        return $this->prepareValue(
166
            $transformer,
167
            null,
168
            array_merge(
169
                $extra,
170
                ['data' => $data]
171
            )
172
        );
173
    }
174
175
    /**
176
     * @param callable $transformer
177
     * @param array $data
178
     * @param string|null $key
179
     * @param array $params
180
     * @return array
181
     */
182
    public function prepareData(callable $transformer, array $data, string $key = null, array $params = []): array
183
    {
184
        foreach ($data as $k => $val) {
185
            $newKey = ($key ? $key . '.' : '') . $k;
186
            if (!$this->includeValue($transformer, $newKey)) {
187
                unset($data[$k]);
188
                continue;
189
            }
190
191
            if (is_array($val) && !is_callable($val)) {
192
                $data[$k] = $this->prepareData($transformer, $val, $newKey, $params);
193
            } else {
194
                $data[$k] = $this->prepareValue($val, $newKey, $params);
195
            }
196
        }
197
198
        return $this->filterFields($data);
199
    }
200
201
    /**
202
     * @param $value
203
     * @param string|null $key
204
     * @param array $params
205
     * @return mixed
206
     */
207
    protected function prepareValue($value, string $key = null, array $params = [])
208
    {
209
        if ($value instanceof ResourceInterface) {
210
            return $this->prepareResource($value, $key, $params);
211
        }
212
213
        if (TransformerHelper::isTransformable($value)) {
214
            return $this->prepareCallable($value, $key, $params);
215
        }
216
217
        return $value;
218
    }
219
220
    /**
221
     * @param ResourceInterface $transformer
222
     * @param string $key
223
     * @param array $params
224
     * @return mixed
225
     */
226
    protected function prepareResource(
227
        ResourceInterface $transformer,
228
        string $key,
229
        array $params = []
230
    ) {
231
        return call_user_func_array(
232
            $transformer,
233
            array_merge(
234
                [$this, $key],
235
                $params
236
            )
237
        );
238
    }
239
240
    /**
241
     * @param callable $callable
242
     * @param string|null $key
243
     * @param array $params
244
     * @return mixed
245
     */
246
    protected function prepareCallable(
247
        callable $callable,
248
        string $key = null,
249
        array $params = []
250
    ) {
251
        if ($callable instanceof TransformerInterface) {
252
            return $this->prepareTransformer($callable, $key, $params);
253
        }
254
255
        if (TransformerHelper::isClosure($callable)) {
256
            return $this->prepareClosure($callable, $key, $params);
257
        }
258
259
        $args = ArgumentHelper::callable(
260
            $callable,
261
            array_merge(
262
                $params,
263
                [
264
                    'scope' => $this,
265
                    'identifier' => $key
266
                ]
267
            )
268
        );
269
270
        return call_user_func_array(
271
            $callable,
272
            $args
273
        );
274
    }
275
276
    /**
277
     * @param \Closure $transformer
278
     * @param string|null $key
279
     * @param array $extra
280
     * @return mixed
281
     */
282
    protected function prepareClosure(
283
        \Closure $transformer,
284
        string $key = null,
285
        array $extra = []
286
    ) {
287
        $args = ArgumentHelper::closure(
288
            $transformer,
289
            array_merge(
290
                $extra,
291
                [
292
                    'scope' => $this,
293
                    'identifier' => $key
294
                ]
295
            )
296
        );
297
298
        return call_user_func_array(
299
            $transformer,
300
            $args
301
        );
302
    }
303
304
    /**
305
     * @param TransformerInterface $transformer
306
     * @param $data
307
     * @param string|null $key
308
     * @param array $extra
309
     * @return mixed
310
     */
311
    protected function prepareTransformer(
312
        TransformerInterface $transformer,
313
        string $key = null,
314
        array $extra = []
315
    ) {
316
        $args = ArgumentHelper::transformer(
317
            $transformer,
318
            array_merge(
319
                $extra,
320
                [
321
                    'scope' => $this,
322
                    'identifier' => $key
323
                ]
324
            )
325
        );
326
327
        return call_user_func_array(
328
            $transformer,
329
            $args
330
        );
331
    }
332
333
    /**
334
     * @param string $identifier
335
     * @return Scope
336
     */
337
    public function childScope(string $identifier): Scope
338
    {
339
        $parentScopes = $this->parentScopes;
340
        $parentScopes[] = $this->scopeIdentifier;
341
342
        return new static(
343
            $this->getTransform(),
344
            $identifier,
345
            $parentScopes
346
        );
347
    }
348
349
    /**
350
     * Check, if this is the root scope.
351
     *
352
     * @return bool
353
     */
354
    protected function isRootScope(): bool
355
    {
356
        return empty($this->parentScopes);
357
    }
358
359
    /**
360
     * Filter the provided data with the requested filter fields for
361
     * the scope resource
362
     *
363
     * @param array $data
364
     *
365
     * @return array
366
     */
367
    protected function filterFields(array $data): array
368
    {
369
        $fields = $this->getFilterFields();
370
371
        if ($fields === null) {
372
            return $data;
373
        }
374
375
        return array_intersect_key(
376
            $data,
377
            array_flip(
378
                iterator_to_array($fields)
379
            )
380
        );
381
    }
382
383
    /**
384
     * Return the requested filter fields for the scope resource
385
     *
386
     * @internal
387
     *
388
     * @return ParamBag|null
389
     */
390
    protected function getFilterFields()
391
    {
392
        return $this->transform->getField(
393
            $this->scopeIdentifier
394
        );
395
    }
396
397
    /**
398
     * @param string $checkScopeSegment
399
     * @return string
400
     */
401
    private function scopeString(string $checkScopeSegment): string
402
    {
403
        if (!empty($this->parentScopes)) {
404
            $scopeArray = array_slice($this->parentScopes, 1);
405
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
406
        } else {
407
            $scopeArray = [$checkScopeSegment];
408
        }
409
410
        return implode('.', (array)$scopeArray);
411
    }
412
}
413