Transform   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 329
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 29
lcom 2
cbo 3
dl 0
loc 329
ccs 0
cts 129
cp 0
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getParams() 0 5 2
A getIncludes() 0 4 1
B setIncludes() 0 59 7
A getExcludes() 0 4 1
A setExcludes() 0 26 5
A setFields() 0 11 2
A getFields() 0 4 1
A getField() 0 6 2
A item() 0 10 1
A collection() 0 15 2
A autoIncludeParents() 0 18 3
A trimToAcceptableRecursionLevel() 0 4 1
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\ObjectHelper;
13
14
/**
15
 * @author Flipbox Factory <[email protected]>
16
 * @since 3.0.0
17
 */
18
class Transform
19
{
20
    /**
21
     * The character used to separate modifier parameters.
22
     *
23
     * @var string
24
     */
25
    public $paramDelimiter = '|';
26
27
    /**
28
     * Upper limit to how many levels of included data are allowed.
29
     *
30
     * @var int
31
     */
32
    public $recursionLimit = 10;
33
34
    /**
35
     * Scope identifiers that resources can optionally include.
36
     *
37
     * @var array
38
     */
39
    protected $includes = [];
40
41
    /**
42
     * Scope identifiers that resources must exclude.
43
     *
44
     * @var array
45
     */
46
    protected $excludes = [];
47
48
    /**
49
     * Scope identifiers that resources must return.
50
     *
51
     * @var array
52
     */
53
    protected $fields = [];
54
55
    /**
56
     * Array containing modifiers as keys and an array value of params.
57
     *
58
     * @var array
59
     */
60
    protected $params = [];
61
62
    /**
63
     * @param array $config
64
     */
65
    public function __construct(array $config = [])
66
    {
67
        ObjectHelper::configure($this, $config);
68
    }
69
70
    /**
71
     * @param string $include
72
     *
73
     * @return ParamBag
74
     */
75
    public function getParams($include): ParamBag
76
    {
77
        $params = isset($this->params[$include]) ? $this->params[$include] : [];
78
        return new ParamBag($params);
79
    }
80
81
    /*******************************************
82
     * INCLUDES
83
     *******************************************/
84
85
    /**
86
     * Get Requested Includes.
87
     *
88
     * @return array
89
     */
90
    public function getIncludes(): array
91
    {
92
        return $this->includes;
93
    }
94
95
    /**
96
     * Parse Include String.
97
     *
98
     * @param array|string $includes Array or csv string of resources to include
99
     *
100
     * @return $this
101
     */
102
    public function setIncludes($includes)
103
    {
104
        // Wipe these before we go again
105
        $this->includes = $this->params = [];
106
107
        if (is_string($includes)) {
108
            $includes = explode(',', $includes);
109
        }
110
111
        if (!is_array($includes)) {
112
            throw new \InvalidArgumentException(
113
                'The parseIncludes() method expects a string or an array. ' . gettype($includes) . ' given'
114
            );
115
        }
116
117
        foreach ($includes as $include) {
118
            list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);
119
120
            // Trim it down to a cool level of recursion
121
            $includeName = $this->trimToAcceptableRecursionLevel($includeName);
122
123
            if (in_array($includeName, $this->includes)) {
124
                continue;
125
            }
126
            $this->includes[] = $includeName;
127
128
            // No Params? Bored
129
            if ($allModifiersStr === null) {
130
                continue;
131
            }
132
133
            // Matches multiple instances of 'something(foo|bar|baz)' in the string
134
            // I guess it ignores : so you could use anything, but probably don't do that
135
            preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);
136
137
            // [0] is full matched strings...
138
            $modifierCount = count($allModifiersArr[0]);
139
140
            $modifierArr = [];
141
142
            for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
143
                // [1] is the modifier
144
                $modifierName = $allModifiersArr[1][$modifierIt];
145
146
                // and [3] is delimited params
147
                $modifierParamStr = $allModifiersArr[3][$modifierIt];
148
149
                // Make modifier array key with an array of params as the value
150
                $modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
151
            }
152
153
            $this->params[$includeName] = $modifierArr;
154
        }
155
156
        // This should be optional and public someday, but without it includes would never show up
157
        $this->autoIncludeParents();
158
159
        return $this;
160
    }
161
162
    /*******************************************
163
     * EXCLUDES
164
     *******************************************/
165
166
    /**
167
     * Get Requested Excludes.
168
     *
169
     * @return array
170
     */
171
    public function getExcludes(): array
172
    {
173
        return $this->excludes;
174
    }
175
176
    /**
177
     * Parse Exclude String.
178
     *
179
     * @param array|string $excludes Array or csv string of resources to exclude
180
     *
181
     * @return $this
182
     */
183
    public function setExcludes($excludes)
184
    {
185
        $this->excludes = [];
186
187
        if (is_string($excludes)) {
188
            $excludes = explode(',', $excludes);
189
        }
190
191
        if (!is_array($excludes)) {
192
            throw new \InvalidArgumentException(
193
                'The parseExcludes() method expects a string or an array. ' . gettype($excludes) . ' given'
194
            );
195
        }
196
197
        foreach ($excludes as $excludeName) {
198
            $excludeName = $this->trimToAcceptableRecursionLevel($excludeName);
199
200
            if (in_array($excludeName, $this->excludes)) {
201
                continue;
202
            }
203
204
            $this->excludes[] = $excludeName;
205
        }
206
207
        return $this;
208
    }
209
210
    /*******************************************
211
     * FIELDS
212
     *******************************************/
213
214
    /**
215
     * Parse field parameter.
216
     *
217
     * @param array $fields Array of fields to include. It must be an array
218
     *                         whose keys are resource types and values a string
219
     *                         of the fields to return, separated by a comma
220
     *
221
     * @return $this
222
     */
223
    public function setFields(array $fields)
224
    {
225
        $this->fields = [];
226
227
        foreach ($fields as $type => $field) {
228
            //Remove empty and repeated fields
229
            $this->fields[$type] = array_unique(array_filter(explode(',', $field)));
230
        }
231
232
        return $this;
233
    }
234
235
    /**
236
     * Get requested fields.
237
     *
238
     * @return array
239
     */
240
    public function getFields(): array
241
    {
242
        return $this->fields;
243
    }
244
245
    /**
246
     * Get field params for the specified type.
247
     *
248
     * @param string $type
249
     *
250
     * @return ParamBag|null
251
     */
252
    public function getField($type)
253
    {
254
        return !isset($this->fields[$type]) ?
255
            null :
256
            new ParamBag($this->fields[$type]);
257
    }
258
259
260
    /*******************************************
261
     * RESOURCES
262
     *******************************************/
263
264
    /**
265
     * @param callable $transformer
266
     * @param $data
267
     * @param array $extra
268
     * @return array
269
     */
270
    public function item(callable $transformer, $data, array $extra = []): array
271
    {
272
        $scope = new Scope($this);
273
274
        return $scope->transform(
275
            $transformer,
276
            $data,
277
            $extra
278
        );
279
    }
280
281
    /**
282
     * @param callable $transformer
283
     * @param $data
284
     * @param array $extra
285
     * @return mixed
286
     */
287
    public function collection(callable $transformer, array $data, array $extra = []): array
288
    {
289
        $items = [];
290
        $scope = new Scope($this);
291
292
        foreach ($data as $item) {
293
            $items[] = $scope->transform(
294
                $transformer,
295
                $item,
296
                $extra
297
            );
298
        }
299
300
        return $items;
301
    }
302
303
304
    /**
305
     * Auto-include Parents
306
     *
307
     * Look at the requested includes and automatically include the parents if they
308
     * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
309
     *
310
     * @internal
311
     *
312
     * @return void
313
     */
314
    protected function autoIncludeParents()
315
    {
316
        $parsed = [];
317
318
        foreach ($this->includes as $include) {
319
            $nested = explode('.', $include);
320
321
            $part = array_shift($nested);
322
            $parsed[] = $part;
323
324
            while (count($nested) > 0) {
325
                $part .= '.' . array_shift($nested);
326
                $parsed[] = $part;
327
            }
328
        }
329
330
        $this->includes = array_values(array_unique($parsed));
331
    }
332
333
    /**
334
     * Trim to Acceptable Recursion Level
335
     *
336
     * @internal
337
     *
338
     * @param string $includeName
339
     *
340
     * @return string
341
     */
342
    protected function trimToAcceptableRecursionLevel($includeName)
343
    {
344
        return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
345
    }
346
}
347