Completed
Pull Request — master (#272)
by Gonçalo
03:57
created

Manager::parseExcludes()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
ccs 16
cts 16
cp 1
rs 8.439
cc 5
eloc 13
nc 8
nop 1
crap 5
1
<?php
2
3
/*
4
 * This file is part of the League\Fractal package.
5
 *
6
 * (c) Phil Sturgeon <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace League\Fractal;
13
14
use League\Fractal\Resource\ResourceInterface;
15
use League\Fractal\Serializer\DataArraySerializer;
16
use League\Fractal\Serializer\SerializerAbstract;
17
18
/**
19
 * Manager
20
 *
21
 * Not a wildly creative name, but the manager is what a Fractal user will interact
22
 * with the most. The manager has various configurable options, and allows users
23
 * to create the "root scope" easily.
24
 */
25
class Manager
26
{
27
    /**
28
     * Array of scope identifiers for resources to include.
29
     *
30
     * @var array
31
     */
32
    protected $requestedIncludes = [];
33
34
    /**
35
     * Array of scope identifiers for resources to exclude.
36
     *
37
     * @var array
38
     */
39
    protected $requestedExcludes = [];
40
41
    /**
42
     * Array containing modifiers as keys and an array value of params.
43
     *
44
     * @var array
45
     */
46
    protected $includeParams = [];
47
48
    /**
49
     * The character used to separate modifier parameters.
50
     *
51
     * @var string
52
     */
53
    protected $paramDelimiter = '|';
54
55
    /**
56
     * Upper limit to how many levels of included data are allowed.
57
     *
58
     * @var int
59
     */
60
    protected $recursionLimit = 10;
61
62
    /**
63
     * Serializer.
64
     *
65
     * @var SerializerAbstract
66
     */
67
    protected $serializer;
68
69
    /**
70
     * Create Data.
71
     *
72
     * Main method to kick this all off. Make a resource then pass it over, and use toArray()
73
     *
74
     * @param ResourceInterface $resource
75
     * @param string            $scopeIdentifier
76
     * @param Scope             $parentScopeInstance
77
     *
78
     * @return Scope
79
     */
80 32
    public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
81
    {
82 32
        $scopeInstance = new Scope($this, $resource, $scopeIdentifier);
83
84
        // Update scope history
85 32
        if ($parentScopeInstance !== null) {
86
            // This will be the new children list of parents (parents parents, plus the parent)
87 27
            $scopeArray = $parentScopeInstance->getParentScopes();
88 27
            $scopeArray[] = $parentScopeInstance->getScopeIdentifier();
89
90 27
            $scopeInstance->setParentScopes($scopeArray);
91 27
        }
92
93 32
        return $scopeInstance;
94
    }
95
96
    /**
97
     * Get Include Params.
98
     *
99
     * @param string $include
100
     *
101
     * @return \League\Fractal\ParamBag|null
102
     */
103 23
    public function getIncludeParams($include)
104
    {
105 23
        if (! isset($this->includeParams[$include])) {
106 21
            return;
107
        }
108
109 2
        $params = $this->includeParams[$include];
110
111 2
        return new ParamBag($params);
112
    }
113
114
    /**
115
     * Get Requested Includes.
116
     *
117
     * @return array
118
     */
119 34
    public function getRequestedIncludes()
120
    {
121 34
        return $this->requestedIncludes;
122
    }
123
124
    /**
125
     * Get Requested Excludes.
126
     *
127
     * @return array
128
     */
129 24
    public function getRequestedExcludes()
130
    {
131 24
        return $this->requestedExcludes;
132
    }
133
134
    /**
135
     * Get Serializer.
136
     *
137
     * @return SerializerAbstract
138
     */
139 39
    public function getSerializer()
140
    {
141 39
        if (! $this->serializer) {
142 7
            $this->setSerializer(new DataArraySerializer());
143 7
        }
144
145 39
        return $this->serializer;
146
    }
147
148
    /**
149
     * Parse Include String.
150
     *
151
     * @param array|string $includes Array or csv string of resources to include
152
     *
153
     * @return $this
154
     */
155 33
    public function parseIncludes($includes)
156
    {
157
        // Wipe these before we go again
158 33
        $this->requestedIncludes = $this->includeParams = [];
159
160 33
        if (is_string($includes)) {
161 27
            $includes = explode(',', $includes);
162 27
        }
163
164 33
        if (! is_array($includes)) {
165 2
            throw new \InvalidArgumentException(
166 2
                'The parseIncludes() method expects a string or an array. '.gettype($includes).' given'
167 2
            );
168
        }
169
170 31
        foreach ($includes as $include) {
171 31
            list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);
172
173
            // Trim it down to a cool level of recursion
174 31
            $includeName = $this->trimToAcceptableRecursionLevel($includeName);
175
176 31
            if (in_array($includeName, $this->requestedIncludes)) {
177 1
                continue;
178
            }
179 31
            $this->requestedIncludes[] = $includeName;
180
181
            // No Params? Bored
182 31
            if ($allModifiersStr === null) {
183 30
                continue;
184
            }
185
186
            // Matches multiple instances of 'something(foo|bar|baz)' in the string
187
            // I guess it ignores : so you could use anything, but probably don't do that
188 2
            preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);
189
190
            // [0] is full matched strings...
191 2
            $modifierCount = count($allModifiersArr[0]);
192
193 2
            $modifierArr = [];
194
195 2
            for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
196
                // [1] is the modifier
197 2
                $modifierName = $allModifiersArr[1][$modifierIt];
198
199
                // and [3] is delimited params
200 2
                $modifierParamStr = $allModifiersArr[3][$modifierIt];
201
202
                // Make modifier array key with an array of params as the value
203 2
                $modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
204 2
            }
205
206 2
            $this->includeParams[$includeName] = $modifierArr;
207 31
        }
208
209
        // This should be optional and public someday, but without it includes would never show up
210 31
        $this->autoIncludeParents();
211
212 31
        return $this;
213
    }
214
215
    /**
216
     * Parse Exclude String.
217
     *
218
     * @param array|string $excludes Array or csv string of resources to exclude
219
     *
220
     * @return $this
221
     */
222 5
    public function parseExcludes($excludes)
223
    {
224 5
        $this->requestedExcludes = [];
225
226 5
        if (is_string($excludes)) {
227 2
            $excludes = explode(',', $excludes);
228 2
        }
229
230 5
        if (! is_array($excludes)) {
231 2
            throw new \InvalidArgumentException(
232 2
                'The parseExcludes() method expects a string or an array. '.gettype($excludes).' given'
233 2
            );
234
        }
235
236 3
        foreach ($excludes as $excludeName) {
237 3
            $excludeName = $this->trimToAcceptableRecursionLevel($excludeName);
238
239 3
            if (in_array($excludeName, $this->requestedExcludes)) {
240 1
                continue;
241
            }
242
243 3
            $this->requestedExcludes[] = $excludeName;
244 3
        }
245
246 3
        return $this;
247
    }
248
249
    /**
250
     * Set Recursion Limit.
251
     *
252
     * @param int $recursionLimit
253
     *
254
     * @return $this
255
     */
256 1
    public function setRecursionLimit($recursionLimit)
257
    {
258 1
        $this->recursionLimit = $recursionLimit;
259
260 1
        return $this;
261
    }
262
263
    /**
264
     * Set Serializer
265
     *
266
     * @param SerializerAbstract $serializer
267
     *
268
     * @return $this
269
     */
270 39
    public function setSerializer(SerializerAbstract $serializer)
271
    {
272 39
        $this->serializer = $serializer;
273
274 39
        return $this;
275
    }
276
277
    /**
278
     * Auto-include Parents
279
     *
280
     * Look at the requested includes and automatically include the parents if they
281
     * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
282
     *
283
     * @internal
284
     *
285
     * @return void
286
     */
287 31
    protected function autoIncludeParents()
288
    {
289 31
        $parsed = [];
290
291 31
        foreach ($this->requestedIncludes as $include) {
292 31
            $nested = explode('.', $include);
293
294 31
            $part = array_shift($nested);
295 31
            $parsed[] = $part;
296
297 31
            while (count($nested) > 0) {
298 7
                $part .= '.'.array_shift($nested);
299 7
                $parsed[] = $part;
300 7
            }
301 31
        }
302
303 31
        $this->requestedIncludes = array_values(array_unique($parsed));
304 31
    }
305
306
    /**
307
     * Trim to Acceptable Recursion Level
308
     *
309
     * Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased
310
     * by trains or whatever the hell that movie was about.
311
     *
312
     * @internal
313
     *
314
     * @param string $includeName
315
     *
316
     * @return string
317
     */
318 33
    protected function trimToAcceptableRecursionLevel($includeName)
319
    {
320 33
        return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
321
    }
322
}
323