Completed
Pull Request — master (#306)
by Benoît
03:13
created

Manager   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 303
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 4

Test Coverage

Coverage 100%

Importance

Changes 12
Bugs 4 Features 2
Metric Value
wmc 28
c 12
b 4
f 2
lcom 3
cbo 4
dl 0
loc 303
ccs 87
cts 87
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 2
A createData() 0 8 2
A getIncludeParams() 0 10 2
A getRequestedIncludes() 0 4 1
A getRequestedExcludes() 0 4 1
A getSerializer() 0 8 2
B parseIncludes() 0 59 7
B parseExcludes() 0 26 5
A setRecursionLimit() 0 6 1
A setSerializer() 0 6 1
A autoIncludeParents() 0 18 3
A trimToAcceptableRecursionLevel() 0 4 1
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
     * Factory used to create new configured scopes.
71
     *
72
     * @var ScopeFactoryInterface
73
     */
74
    private $scopeFactory;
75
76 65
    public function __construct(ScopeFactoryInterface $scopeFactory = null)
77
    {
78 65
        $this->scopeFactory = $scopeFactory ?: new ScopeFactory();
79 65
    }
80
81
    /**
82
     * Create Data.
83
     *
84
     * Main method to kick this all off. Make a resource then pass it over, and use toArray()
85
     *
86
     * @param ResourceInterface $resource
87
     * @param string            $scopeIdentifier
88
     * @param Scope             $parentScopeInstance
89
     *
90
     * @return Scope
91
     */
92 32
    public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
93
    {
94 32
        if ($parentScopeInstance !== null) {
95 27
            return $this->scopeFactory->createChildScopeFor($this, $parentScopeInstance, $resource, $scopeIdentifier);
96
        }
97
98 5
        return $this->scopeFactory->createScopeFor($this, $resource, $scopeIdentifier);
99
    }
100
101
    /**
102
     * Get Include Params.
103
     *
104
     * @param string $include
105
     *
106
     * @return \League\Fractal\ParamBag|null
107
     */
108 23
    public function getIncludeParams($include)
109
    {
110 23
        if (! isset($this->includeParams[$include])) {
111 21
            return;
112
        }
113
114 2
        $params = $this->includeParams[$include];
115
116 2
        return new ParamBag($params);
117
    }
118
119
    /**
120
     * Get Requested Includes.
121
     *
122
     * @return array
123
     */
124 34
    public function getRequestedIncludes()
125
    {
126 34
        return $this->requestedIncludes;
127
    }
128
129
    /**
130
     * Get Requested Excludes.
131
     *
132
     * @return array
133
     */
134 24
    public function getRequestedExcludes()
135
    {
136 24
        return $this->requestedExcludes;
137
    }
138
139
    /**
140
     * Get Serializer.
141
     *
142
     * @return SerializerAbstract
143
     */
144 41
    public function getSerializer()
145
    {
146 41
        if (! $this->serializer) {
147 7
            $this->setSerializer(new DataArraySerializer());
148 7
        }
149
150 41
        return $this->serializer;
151
    }
152
153
    /**
154
     * Parse Include String.
155
     *
156
     * @param array|string $includes Array or csv string of resources to include
157
     *
158
     * @return $this
159
     */
160 35
    public function parseIncludes($includes)
161
    {
162
        // Wipe these before we go again
163 35
        $this->requestedIncludes = $this->includeParams = [];
164
165 35
        if (is_string($includes)) {
166 29
            $includes = explode(',', $includes);
167 29
        }
168
169 35
        if (! is_array($includes)) {
170 2
            throw new \InvalidArgumentException(
171 2
                'The parseIncludes() method expects a string or an array. '.gettype($includes).' given'
172 2
            );
173
        }
174
175 33
        foreach ($includes as $include) {
176 33
            list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);
177
178
            // Trim it down to a cool level of recursion
179 33
            $includeName = $this->trimToAcceptableRecursionLevel($includeName);
180
181 33
            if (in_array($includeName, $this->requestedIncludes)) {
182 1
                continue;
183
            }
184 33
            $this->requestedIncludes[] = $includeName;
185
186
            // No Params? Bored
187 33
            if ($allModifiersStr === null) {
188 32
                continue;
189
            }
190
191
            // Matches multiple instances of 'something(foo|bar|baz)' in the string
192
            // I guess it ignores : so you could use anything, but probably don't do that
193 2
            preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);
194
195
            // [0] is full matched strings...
196 2
            $modifierCount = count($allModifiersArr[0]);
197
198 2
            $modifierArr = [];
199
200 2
            for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
201
                // [1] is the modifier
202 2
                $modifierName = $allModifiersArr[1][$modifierIt];
203
204
                // and [3] is delimited params
205 2
                $modifierParamStr = $allModifiersArr[3][$modifierIt];
206
207
                // Make modifier array key with an array of params as the value
208 2
                $modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
209 2
            }
210
211 2
            $this->includeParams[$includeName] = $modifierArr;
212 33
        }
213
214
        // This should be optional and public someday, but without it includes would never show up
215 33
        $this->autoIncludeParents();
216
217 33
        return $this;
218
    }
219
220
    /**
221
     * Parse Exclude String.
222
     *
223
     * @param array|string $excludes Array or csv string of resources to exclude
224
     *
225
     * @return $this
226
     */
227 5
    public function parseExcludes($excludes)
228
    {
229 5
        $this->requestedExcludes = [];
230
231 5
        if (is_string($excludes)) {
232 2
            $excludes = explode(',', $excludes);
233 2
        }
234
235 5
        if (! is_array($excludes)) {
236 2
            throw new \InvalidArgumentException(
237 2
                'The parseExcludes() method expects a string or an array. '.gettype($excludes).' given'
238 2
            );
239
        }
240
241 3
        foreach ($excludes as $excludeName) {
242 3
            $excludeName = $this->trimToAcceptableRecursionLevel($excludeName);
243
244 3
            if (in_array($excludeName, $this->requestedExcludes)) {
245 1
                continue;
246
            }
247
248 3
            $this->requestedExcludes[] = $excludeName;
249 3
        }
250
251 3
        return $this;
252
    }
253
254
    /**
255
     * Set Recursion Limit.
256
     *
257
     * @param int $recursionLimit
258
     *
259
     * @return $this
260
     */
261 1
    public function setRecursionLimit($recursionLimit)
262
    {
263 1
        $this->recursionLimit = $recursionLimit;
264
265 1
        return $this;
266
    }
267
268
    /**
269
     * Set Serializer
270
     *
271
     * @param SerializerAbstract $serializer
272
     *
273
     * @return $this
274
     */
275 41
    public function setSerializer(SerializerAbstract $serializer)
276
    {
277 41
        $this->serializer = $serializer;
278
279 41
        return $this;
280
    }
281
282
    /**
283
     * Auto-include Parents
284
     *
285
     * Look at the requested includes and automatically include the parents if they
286
     * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
287
     *
288
     * @internal
289
     *
290
     * @return void
291
     */
292 33
    protected function autoIncludeParents()
293
    {
294 33
        $parsed = [];
295
296 33
        foreach ($this->requestedIncludes as $include) {
297 33
            $nested = explode('.', $include);
298
299 33
            $part = array_shift($nested);
300 33
            $parsed[] = $part;
301
302 33
            while (count($nested) > 0) {
303 7
                $part .= '.'.array_shift($nested);
304 7
                $parsed[] = $part;
305 7
            }
306 33
        }
307
308 33
        $this->requestedIncludes = array_values(array_unique($parsed));
309 33
    }
310
311
    /**
312
     * Trim to Acceptable Recursion Level
313
     *
314
     * Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased
315
     * by trains or whatever the hell that movie was about.
316
     *
317
     * @internal
318
     *
319
     * @param string $includeName
320
     *
321
     * @return string
322
     */
323 35
    protected function trimToAcceptableRecursionLevel($includeName)
324
    {
325 35
        return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
326
    }
327
}
328