Completed
Push — master ( 9a1097...9d1cce )
by Nate
04:06
created

Scope   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 291
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 2
dl 0
loc 291
ccs 0
cts 136
cp 0
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getTransform() 0 4 1
A getParams() 0 6 1
A getScopeIdentifier() 0 4 1
A getIdentifier() 0 13 1
A getParentScopes() 0 4 1
A isRequested() 0 7 1
A isExcluded() 0 7 1
B transform() 0 29 5
B includeValue() 0 17 5
A parseValue() 0 8 2
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\Transformers\TransformerInterface;
13
14
/**
15
 * @author Flipbox Factory <[email protected]>
16
 * @since 1.0.0
17
 */
18
class Scope
19
{
20
    /**
21
     * @var string
22
     */
23
    protected $scopeIdentifier;
24
25
    /**
26
     * @var Transform
27
     */
28
    protected $transform;
29
30
    /**
31
     * @var array
32
     */
33
    protected $parentScopes = [];
34
35
    /**
36
     * Scope constructor.
37
     * @param Transform $transform
38
     * @param string|null $scopeIdentifier
39
     * @param array $parentScopes
40
     */
41
    public function __construct(
42
        Transform $transform,
43
        string $scopeIdentifier = null,
44
        array $parentScopes = []
45
    ) {
46
        $this->transform = $transform;
47
        $this->scopeIdentifier = $scopeIdentifier;
48
        $this->parentScopes = $parentScopes;
49
    }
50
51
    /**
52
     * @return Transform
53
     */
54
    public function getTransform(): Transform
55
    {
56
        return $this->transform;
57
    }
58
59
    /**
60
     * @param $key
61
     * @return ParamBag
62
     */
63
    public function getParams(string $key = null): ParamBag
64
    {
65
        return $this->getTransform()->getParams(
66
            $this->getIdentifier($key)
67
        );
68
    }
69
70
    /**
71
     * Get the current identifier.
72
     *
73
     * @return string|null
74
     */
75
    public function getScopeIdentifier()
76
    {
77
        return $this->scopeIdentifier;
78
    }
79
80
    /**
81
     * Get the unique identifier for this scope.
82
     *
83
     * @param string $appendIdentifier
84
     *
85
     * @return string
86
     */
87
    public function getIdentifier(string $appendIdentifier = null): string
88
    {
89
        return implode(
90
            '.',
91
            array_filter(array_merge(
92
                $this->parentScopes,
93
                [
94
                    $this->scopeIdentifier,
95
                    $appendIdentifier
96
                ]
97
            ))
98
        );
99
    }
100
101
    /**
102
     * Getter for parentScopes.
103
     *
104
     * @return array
105
     */
106
    public function getParentScopes(): array
107
    {
108
        return $this->parentScopes;
109
    }
110
111
    /**
112
     * Is Requested.
113
     *
114
     * Check if - in relation to the current scope - this specific segment is allowed.
115
     * That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current
116
     * scope is a then c is not allowed, even if it is there and potentially transformable.
117
     *
118
     * @internal
119
     *
120
     * @param string $checkScopeSegment
121
     *
122
     * @return bool Returns the new number of elements in the array.
123
     */
124
    public function isRequested($checkScopeSegment): bool
125
    {
126
        return in_array(
127
            $this->scopeString($checkScopeSegment),
128
            $this->transform->getIncludes()
129
        );
130
    }
131
132
    /**
133
     * Is Excluded.
134
     *
135
     * Check if - in relation to the current scope - this specific segment should
136
     * be excluded. That means, if a.b.c is excluded and the current scope is a.b,
137
     * then c will not be allowed in the transformation whether it appears in
138
     * the list of default or available, requested includes.
139
     *
140
     * @internal
141
     *
142
     * @param string $checkScopeSegment
143
     *
144
     * @return bool
145
     */
146
    public function isExcluded($checkScopeSegment): bool
147
    {
148
        return in_array(
149
            $this->scopeString($checkScopeSegment),
150
            $this->transform->getExcludes()
151
        );
152
    }
153
154
    /**
155
     * @param TransformerInterface|callable $transformer
156
     * @param mixed $data
157
     * @return array
158
     */
159
    public function transform(callable $transformer, $data): array
160
    {
161
        $includedData = [];
162
163
        // Transform data
164
        $transformedData = $this->parseValue($transformer, $data);
165
166
        // Bail now
167
        if (null === $transformedData) {
168
            return $includedData;
169
        }
170
171
        // Conform to array
172
        if (!is_array($transformedData)) {
173
            $transformedData = [$transformedData];
174
        }
175
176
        foreach ($transformedData as $key => $val) {
177
            if (!$this->includeValue($transformer, $key)) {
178
                continue;
179
            }
180
            $includedData[$key] = $this->parseValue($val, $data, $key);
181
        }
182
183
        // Return only the requested fields
184
        $includedData = $this->filterFields($includedData);
185
186
        return $includedData;
187
    }
188
189
    /**
190
     * @param callable $transformer
191
     * @param string $key
192
     * @return bool
193
     */
194
    protected function includeValue(callable $transformer, string $key): bool
195
    {
196
        // Ignore optional (that have not been explicitly requested)
197
        if ($transformer instanceof TransformerInterface &&
198
            in_array($key, $transformer->getIncludes(), true) &&
199
            !$this->isRequested($key)
200
        ) {
201
            return false;
202
        }
203
204
        // Ignore excludes
205
        if ($this->isExcluded($key)) {
206
            return false;
207
        }
208
209
        return true;
210
    }
211
212
    /**
213
     * @param $val
214
     * @param $data
215
     * @param string|null $key
216
     * @return array|string|null
217
     */
218
    protected function parseValue($val, $data, string $key = null)
219
    {
220
        if (is_callable($val)) {
221
            return call_user_func_array($val, [$data, $this, $key]);
222
        }
223
224
        return $val;
225
    }
226
227
    /**
228
     * @param string $identifier
229
     * @return Scope
230
     */
231
    public function childScope(string $identifier): Scope
232
    {
233
        $parentScopes = $this->getParentScopes();
234
        $parentScopes[] = $this->getScopeIdentifier();
235
236
        return new Scope(
237
            $this->getTransform(),
238
            $identifier,
239
            $parentScopes
240
        );
241
    }
242
243
    /**
244
     * Check, if this is the root scope.
245
     *
246
     * @return bool
247
     */
248
    protected function isRootScope(): bool
249
    {
250
        return empty($this->parentScopes);
251
    }
252
253
    /**
254
     * Filter the provided data with the requested filter fields for
255
     * the scope resource
256
     *
257
     * @internal
258
     *
259
     * @param array $data
260
     *
261
     * @return array
262
     */
263
    protected function filterFields(array $data): array
264
    {
265
        $fields = $this->getFilterFields();
266
267
        if ($fields === null) {
268
            return $data;
269
        }
270
271
        return array_intersect_key(
272
            $data,
273
            array_flip(
274
                iterator_to_array($fields)
275
            )
276
        );
277
    }
278
279
    /**
280
     * Return the requested filter fields for the scope resource
281
     *
282
     * @internal
283
     *
284
     * @return ParamBag|null
285
     */
286
    protected function getFilterFields()
287
    {
288
        return $this->transform->getField(
289
            $this->getScopeIdentifier()
290
        );
291
    }
292
293
    /**
294
     * @param string $checkScopeSegment
295
     * @return string
296
     */
297
    private function scopeString(string $checkScopeSegment): string
298
    {
299
        if ($this->parentScopes) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->parentScopes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
300
            $scopeArray = array_slice($this->parentScopes, 1);
301
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
302
        } else {
303
            $scopeArray = [$checkScopeSegment];
304
        }
305
306
        return implode('.', (array)$scopeArray);
307
    }
308
}
309