Completed
Push — master ( 198b3f...28f717 )
by Nate
02:18
created

Transform::setExcludes()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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