1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* @package Transform |
5
|
|
|
* @author Flipbox Factory |
6
|
|
|
* @copyright Copyright (c) 2017, Flipbox Digital |
7
|
|
|
* @link https://github.com/flipbox/transform/releases/latest |
8
|
|
|
* @license https://github.com/flipbox/transform/blob/master/LICENSE |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace flipbox\transform; |
12
|
|
|
|
13
|
|
|
use flipbox\transform\resources\CollectionInterface; |
14
|
|
|
use flipbox\transform\resources\ItemInterface; |
15
|
|
|
use flipbox\transform\resources\ResourceInterface; |
16
|
|
|
use flipbox\transform\transformers\TransformerInterface; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @package flipbox\transform |
20
|
|
|
* @author Flipbox Factory <[email protected]> |
21
|
|
|
* @since 1.0.0 |
22
|
|
|
*/ |
23
|
|
|
class Scope |
24
|
|
|
{ |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* @var string |
28
|
|
|
*/ |
29
|
|
|
protected $scopeIdentifier; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var Factory |
33
|
|
|
*/ |
34
|
|
|
protected $factory; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var ResourceInterface |
38
|
|
|
*/ |
39
|
|
|
protected $resource; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var array |
43
|
|
|
*/ |
44
|
|
|
protected $parentScopes = []; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* Scope constructor. |
48
|
|
|
* @param Factory $factory |
49
|
|
|
* @param ResourceInterface $resource |
50
|
|
|
* @param string|null $scopeIdentifier |
51
|
|
|
* @param array $parentScopes |
52
|
|
|
*/ |
53
|
|
|
public function __construct(Factory $factory, ResourceInterface $resource, string $scopeIdentifier = null, array $parentScopes = []) |
54
|
|
|
{ |
55
|
|
|
$this->factory = $factory; |
56
|
|
|
$this->resource = $resource; |
57
|
|
|
$this->scopeIdentifier = $scopeIdentifier; |
58
|
|
|
$this->parentScopes = $parentScopes; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @return ResourceInterface |
63
|
|
|
*/ |
64
|
|
|
public function getResource(): ResourceInterface |
65
|
|
|
{ |
66
|
|
|
return $this->resource; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Getter for manager. |
71
|
|
|
* |
72
|
|
|
* @return Factory |
73
|
|
|
*/ |
74
|
|
|
public function getFactory(): Factory |
75
|
|
|
{ |
76
|
|
|
return $this->factory; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @param $key |
81
|
|
|
* @return ParamBag |
82
|
|
|
*/ |
83
|
|
|
public function getParams(string $key = null): ParamBag |
84
|
|
|
{ |
85
|
|
|
return $this->getFactory()->getParams( |
86
|
|
|
$this->getIdentifier($key) |
87
|
|
|
); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* Get the current identifier. |
92
|
|
|
* |
93
|
|
|
* @return string|null |
94
|
|
|
*/ |
95
|
|
|
public function getScopeIdentifier() |
96
|
|
|
{ |
97
|
|
|
return $this->scopeIdentifier; |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Get the unique identifier for this scope. |
102
|
|
|
* |
103
|
|
|
* @param string $appendIdentifier |
104
|
|
|
* |
105
|
|
|
* @return string |
106
|
|
|
*/ |
107
|
|
|
public function getIdentifier(string $appendIdentifier = null): string |
108
|
|
|
{ |
109
|
|
|
return implode( |
110
|
|
|
'.', |
111
|
|
|
array_filter(array_merge( |
112
|
|
|
$this->parentScopes, |
113
|
|
|
[ |
114
|
|
|
$this->scopeIdentifier, |
115
|
|
|
$appendIdentifier |
116
|
|
|
] |
117
|
|
|
)) |
118
|
|
|
); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* Getter for parentScopes. |
123
|
|
|
* |
124
|
|
|
* @return array |
125
|
|
|
*/ |
126
|
|
|
public function getParentScopes(): array |
127
|
|
|
{ |
128
|
|
|
return $this->parentScopes; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Is Requested. |
133
|
|
|
* |
134
|
|
|
* Check if - in relation to the current scope - this specific segment is allowed. |
135
|
|
|
* That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current |
136
|
|
|
* scope is a then c is not allowed, even if it is there and potentially transformable. |
137
|
|
|
* |
138
|
|
|
* @internal |
139
|
|
|
* |
140
|
|
|
* @param string $checkScopeSegment |
141
|
|
|
* |
142
|
|
|
* @return bool Returns the new number of elements in the array. |
143
|
|
|
*/ |
144
|
|
|
public function isRequested($checkScopeSegment): bool |
145
|
|
|
{ |
146
|
|
|
return in_array( |
147
|
|
|
$this->_scopeString($checkScopeSegment), |
148
|
|
|
$this->factory->getIncludes() |
149
|
|
|
); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* Is Excluded. |
154
|
|
|
* |
155
|
|
|
* Check if - in relation to the current scope - this specific segment should |
156
|
|
|
* be excluded. That means, if a.b.c is excluded and the current scope is a.b, |
157
|
|
|
* then c will not be allowed in the transformation whether it appears in |
158
|
|
|
* the list of default or available, requested includes. |
159
|
|
|
* |
160
|
|
|
* @internal |
161
|
|
|
* |
162
|
|
|
* @param string $checkScopeSegment |
163
|
|
|
* |
164
|
|
|
* @return bool |
165
|
|
|
*/ |
166
|
|
|
public function isExcluded($checkScopeSegment): bool |
167
|
|
|
{ |
168
|
|
|
return in_array( |
169
|
|
|
$this->_scopeString($checkScopeSegment), |
170
|
|
|
$this->factory->getExcludes() |
171
|
|
|
); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Convert the current data for this scope to an array. |
176
|
|
|
* |
177
|
|
|
* @return array|null |
178
|
|
|
*/ |
179
|
|
|
public function transform() |
180
|
|
|
{ |
181
|
|
|
|
182
|
|
|
$resource = $this->getResource(); |
183
|
|
|
$transformer = $resource->getTransformer(); |
184
|
|
|
$data = $resource->getData(); |
185
|
|
|
|
186
|
|
|
$transformedData = []; |
187
|
|
|
|
188
|
|
|
if ($resource instanceof ItemInterface) { |
189
|
|
|
|
190
|
|
|
$transformedData = $this->fireTransformer($transformer, $data); |
191
|
|
|
|
192
|
|
|
} elseif ($resource instanceof CollectionInterface) { |
193
|
|
|
|
194
|
|
|
foreach ($data as $value) { |
195
|
|
|
|
196
|
|
|
$transformedData[] = $this->fireTransformer($transformer, $value); |
197
|
|
|
|
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
} else { |
201
|
|
|
|
202
|
|
|
$transformedData = null; |
203
|
|
|
|
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
return $transformedData; |
207
|
|
|
|
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Fire the main transformer. |
212
|
|
|
* |
213
|
|
|
* @internal |
214
|
|
|
* |
215
|
|
|
* @param TransformerInterface|callable $transformer |
216
|
|
|
* @param mixed $data |
217
|
|
|
* |
218
|
|
|
* @return array |
219
|
|
|
*/ |
220
|
|
|
protected function fireTransformer(callable $transformer, $data): array |
221
|
|
|
{ |
222
|
|
|
|
223
|
|
|
$includedData = []; |
224
|
|
|
|
225
|
|
|
// Transform data |
226
|
|
|
$transformedData = $this->parseValue($transformer, $data); |
227
|
|
|
|
228
|
|
|
// Bail now |
229
|
|
|
if (null === $transformedData) { |
230
|
|
|
return $includedData; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
// Conform to array |
234
|
|
|
if (!is_array($transformedData)) { |
235
|
|
|
$transformedData = [$transformedData]; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
foreach ($transformedData as $key => $val) { |
239
|
|
|
|
240
|
|
|
if (!$this->includeValue($transformer, $key)) { |
241
|
|
|
continue; |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
$includedData[$key] = $this->parseValue($val, $data, $key); |
245
|
|
|
|
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
// Return only the requested fields |
249
|
|
|
$includedData = $this->filterFields($includedData); |
250
|
|
|
|
251
|
|
|
return $includedData; |
252
|
|
|
|
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* @param callable $transformer |
257
|
|
|
* @param string $key |
258
|
|
|
* @return bool |
259
|
|
|
*/ |
260
|
|
|
protected function includeValue(callable $transformer, string $key): bool |
261
|
|
|
{ |
262
|
|
|
|
263
|
|
|
// Ignore optional (that have not been explicitly requested) |
264
|
|
|
if ($transformer instanceof TransformerInterface && in_array($key, $transformer->getIncludes(), true) && !$this->isRequested($key)) { |
265
|
|
|
return false; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
// Ignore excludes |
269
|
|
|
if ($this->isExcluded($key)) { |
270
|
|
|
return false; |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
return true; |
274
|
|
|
|
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* @param $val |
279
|
|
|
* @param $data |
280
|
|
|
* @param string|null $key |
281
|
|
|
* @return array|string|null |
282
|
|
|
*/ |
283
|
|
|
protected function parseValue($val, $data, string $key = null) |
284
|
|
|
{ |
285
|
|
|
|
286
|
|
|
// Nesting |
287
|
|
|
if ($val instanceof ResourceInterface) { |
288
|
|
|
|
289
|
|
|
return $this->getFactory()->transform($val, $key, $this); |
|
|
|
|
290
|
|
|
|
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
if (is_callable($val)) { |
294
|
|
|
|
295
|
|
|
return call_user_func_array($val, [$data, $this, $key]); |
296
|
|
|
|
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
return $val; |
300
|
|
|
|
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Check, if this is the root scope. |
305
|
|
|
* |
306
|
|
|
* @return bool |
307
|
|
|
*/ |
308
|
|
|
protected function isRootScope(): bool |
309
|
|
|
{ |
310
|
|
|
return empty($this->parentScopes); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* Filter the provided data with the requested filter fields for |
315
|
|
|
* the scope resource |
316
|
|
|
* |
317
|
|
|
* @internal |
318
|
|
|
* |
319
|
|
|
* @param array $data |
320
|
|
|
* |
321
|
|
|
* @return array |
322
|
|
|
*/ |
323
|
|
|
protected function filterFields(array $data): array |
324
|
|
|
{ |
325
|
|
|
|
326
|
|
|
$fields = $this->getFilterFields(); |
327
|
|
|
|
328
|
|
|
if ($fields === null) { |
329
|
|
|
return $data; |
330
|
|
|
} |
331
|
|
|
|
332
|
|
|
return array_intersect_key( |
333
|
|
|
$data, |
334
|
|
|
array_flip( |
335
|
|
|
iterator_to_array($fields) |
336
|
|
|
) |
337
|
|
|
); |
338
|
|
|
|
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Return the requested filter fields for the scope resource |
343
|
|
|
* |
344
|
|
|
* @internal |
345
|
|
|
* |
346
|
|
|
* @return ParamBag|null |
347
|
|
|
*/ |
348
|
|
|
protected function getFilterFields() |
349
|
|
|
{ |
350
|
|
|
return $this->factory->getField( |
351
|
|
|
$this->getScopeIdentifier() |
352
|
|
|
); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
/** |
356
|
|
|
* @param string $checkScopeSegment |
357
|
|
|
* @return string |
358
|
|
|
*/ |
359
|
|
|
private function _scopeString(string $checkScopeSegment): string |
360
|
|
|
{ |
361
|
|
|
|
362
|
|
|
if ($this->parentScopes) { |
|
|
|
|
363
|
|
|
$scopeArray = array_slice($this->parentScopes, 1); |
364
|
|
|
array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment); |
365
|
|
|
} else { |
366
|
|
|
$scopeArray = [$checkScopeSegment]; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
return implode('.', (array)$scopeArray); |
370
|
|
|
|
371
|
|
|
} |
372
|
|
|
|
373
|
|
|
} |
374
|
|
|
|
This check looks at variables that have been passed in as parameters and are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.