Completed
Push — develop ( a379bf )
by Nate
01:48
created

Scope::prepareTransformer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 0
cts 26
cp 0
rs 9.472
c 0
b 0
f 0
cc 2
nc 2
nop 4
crap 6
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 array
162
     */
163
    public function transform(callable $transformer, $data, array $extra = []): array
164
    {
165
        return (array)$this->prepareTransformer($transformer, $data, null, $extra);
166
    }
167
168
    /**
169
     * @param callable $transformer
170
     * @param array $data
171
     * @param string|null $key
172
     * @param array $params
173
     * @return array
174
     */
175
    public function prepareData(callable $transformer, array $data, string $key = null, array $params = []): array
176
    {
177
        foreach ($data as $k => $val) {
178
            $newKey = ($key ? $key . '.' : '') . $k;
179
            if (!$this->includeValue($transformer, $newKey)) {
180
                unset($data[$k]);
181
                continue;
182
            }
183
184
            $data[$k] = $this->prepareValue($transformer, $val, $newKey, $params);
185
        }
186
187
        return $this->filterFields($data);
188
    }
189
190
    /**
191
     * @param callable $transformer
192
     * @param $value
193
     * @param string|null $key
194
     * @param array $params
195
     * @return mixed
196
     */
197
    protected function prepareValue(callable $transformer, $value, string $key = null, array $params = [])
198
    {
199
        if ($value instanceof ResourceInterface) {
200
            return $this->prepareResource($value, $key, $params);
201
        }
202
203
        if (TransformerHelper::isClosure($value)) {
204
            return $this->prepareClosure($value, $key, $params);
205
        }
206
207
        if (TransformerHelper::isTransformer($value)) {
208
            return $this->prepareTransformer($value, $key, $params);
0 ignored issues
show
Documentation introduced by
$params is of type array, but the function expects a null|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
209
        }
210
211
        if (is_array($value)) {
212
            return $this->prepareData($transformer, $value, $key, $params);
213
        }
214
215
        return $value;
216
    }
217
218
    /**
219
     * @param ResourceInterface $transformer
220
     * @param string $key
221
     * @param array $params
222
     * @return mixed
223
     */
224
    protected function prepareResource(
225
        ResourceInterface $transformer,
226
        string $key,
227
        array $params = []
228
    ) {
229
        return call_user_func_array(
230
            $transformer,
231
            array_merge(
232
                [$this, $key],
233
                $params
234
            )
235
        );
236
    }
237
238
    /**
239
     * @param \Closure $transformer
240
     * @param string|null $key
241
     * @param array $extra
242
     * @return mixed
243
     */
244
    protected function prepareClosure(\Closure $transformer, string $key = null, array $extra = [])
245
    {
246
        $args = ['scope' => $this, 'identifier' => $key];
247
248
        $args = ArgumentHelper::closure(
249
            $transformer,
250
            array_merge(
251
                $extra,
252
                $args
253
            )
254
        );
255
256
        return call_user_func_array(
257
            $transformer,
258
            array_merge(
259
                $args
260
            )
261
        );
262
    }
263
264
    /**
265
     * @param callable $transformer
266
     * @param $data
267
     * @param string|null $key
268
     * @param array $extra
269
     * @return mixed
270
     */
271
    protected function prepareTransformer(callable $transformer, $data, string $key = null, array $extra = [])
272
    {
273
        if ($transformer instanceof TransformerInterface) {
274
            $args = ArgumentHelper::callable(
275
                $transformer,
276
                array_merge(
277
                    $extra,
278
                    [
279
                        'scope' => $this,
280
                        'identifier' => $key
281
                    ]
282
                )
283
            );
284
285
            return call_user_func_array(
286
                $transformer,
287
                array_merge(
288
                    [$data, $this, $key],
289
                    $args
290
                )
291
            );
292
        }
293
294
        return call_user_func(
295
            $transformer,
296
            $data
297
        );
298
    }
299
300
    /**
301
     * @param string $identifier
302
     * @return Scope
303
     */
304
    public function childScope(string $identifier): Scope
305
    {
306
        $parentScopes = $this->parentScopes;
307
        $parentScopes[] = $this->scopeIdentifier;
308
309
        return new static(
310
            $this->getTransform(),
311
            $identifier,
312
            $parentScopes
313
        );
314
    }
315
316
    /**
317
     * Check, if this is the root scope.
318
     *
319
     * @return bool
320
     */
321
    protected function isRootScope(): bool
322
    {
323
        return empty($this->parentScopes);
324
    }
325
326
    /**
327
     * Filter the provided data with the requested filter fields for
328
     * the scope resource
329
     *
330
     * @param array $data
331
     *
332
     * @return array
333
     */
334
    protected function filterFields(array $data): array
335
    {
336
        $fields = $this->getFilterFields();
337
338
        if ($fields === null) {
339
            return $data;
340
        }
341
342
        return array_intersect_key(
343
            $data,
344
            array_flip(
345
                iterator_to_array($fields)
346
            )
347
        );
348
    }
349
350
    /**
351
     * Return the requested filter fields for the scope resource
352
     *
353
     * @internal
354
     *
355
     * @return ParamBag|null
356
     */
357
    protected function getFilterFields()
358
    {
359
        return $this->transform->getField(
360
            $this->scopeIdentifier
361
        );
362
    }
363
364
    /**
365
     * @param string $checkScopeSegment
366
     * @return string
367
     */
368
    private function scopeString(string $checkScopeSegment): string
369
    {
370
        if (!empty($this->parentScopes)) {
371
            $scopeArray = array_slice($this->parentScopes, 1);
372
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
373
        } else {
374
            $scopeArray = [$checkScopeSegment];
375
        }
376
377
        return implode('.', (array)$scopeArray);
378
    }
379
}
380