Includes::set()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 2
c 0
b 0
f 0
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Rexlabs\Smokescreen\Includes;
4
5
use Rexlabs\Smokescreen\Exception\ParseIncludesException;
6
7
/**
8
 * Container object for managing include keys and optional mapped parameters.
9
 */
10
class Includes
11
{
12
    /** @var array */
13
    protected $keys;
14
15
    /** @var array */
16
    protected $params = [];
17
18
    /**
19
     * Provide a list of keys, and optional parameters for those keys.
20
     *
21
     * @param array $keys
22
     * @param array $params
23
     */
24 28
    public function __construct(array $keys = [], array $params = [])
25
    {
26 28
        $this->set($keys);
27 28
        $this->params = $params;
28
    }
29
30
    /**
31
     * @param array $keys
32
     *
33
     * @return $this
34
     */
35 28
    public function set(array $keys)
36
    {
37 28
        $this->keys = $this->expandKeys($keys);
38
39 28
        return $this;
40
    }
41
42
    /**
43
     * Provide a list of keys, so that depth-keys are expanded to include parents
44
     * Given a two keys of [user.id, photo.url], this method will return [user, photo, user.id, photo.url].
45
     *
46
     * @param array $keys
47
     *
48
     * @return array
49
     */
50 28
    protected function expandKeys(array $keys): array
51
    {
52 28
        $allKeys = [];
53
54
        // Search all keys that contain the '.', and add their parents all the way up.
55 28
        foreach ($keys as $key) {
56 18
            $allKeys[] = $key;
57 18
            while (($dot = \strrpos($key, '.')) !== false) {
58 10
                $key = \substr($key, 0, $dot);
59 10
                $allKeys[] = $key;
60
            }
61
        }
62
63
        // We probably created some dupes ...
64 28
        $allKeys = \array_unique($allKeys);
65
66 28
        return $allKeys;
67
    }
68
69
    /**
70
     * Add one or more keys.
71
     * Duplicate keys will be silently ignored.
72
     *
73
     * @param string|array|mixed $keys
74
     *
75
     * @return $this
76
     */
77 2
    public function add($keys)
78
    {
79 2
        foreach ($this->expandKeys((array) $keys) as $key) {
80 2
            if (!$this->has($key)) {
81 2
                $this->keys[] = $key;
82
            }
83
        }
84
85 2
        return $this;
86
    }
87
88
    /**
89
     * Determine if a given key exists.
90
     *
91
     * @param $key
92
     *
93
     * @return bool
94
     */
95 5
    public function has($key): bool
96
    {
97 5
        return \in_array($key, $this->keys(), true);
98
    }
99
100
    /**
101
     * Get an array of all keys, note this will include expanded keys (parents).
102
     *
103
     * @return array
104
     */
105 24
    public function keys(): array
106
    {
107 24
        return $this->keys;
108
    }
109
110
    /**
111
     * Returns true if any keys have been set.
112
     *
113
     * @return bool
114
     */
115 2
    public function hasKeys(): bool
116
    {
117 2
        return \count($this->keys) > 0;
118
    }
119
120
    /**
121
     * Remove one or more keys.
122
     *
123
     * @param array|string $keys
124
     */
125 1
    public function remove($keys)
126
    {
127 1
        $keys = (array) $keys; // Cast all the things
128 1
        $this->keys = array_filter($this->keys(), function ($key) use ($keys) {
129 1
            foreach ($keys as $remove) {
130 1
                if (preg_match('/^'.preg_quote($remove, '/').'(\..+)?$/', $key)) {
131
                    // Keys and descendant keys will be removed
132 1
                    return false;
133
                }
134
            }
135
136 1
            return true;
137 1
        });
138
    }
139
140
    /**
141
     * Only keys that don't descend (eg. don't contain a dot).
142
     *
143
     * @return array
144
     */
145 16
    public function baseKeys(): array
146
    {
147 16
        return \array_values(\array_filter($this->keys(), function ($key) {
148 9
            return \strpos($key, '.') === false;
149 16
        }));
150
    }
151
152
    /**
153
     * Return the params associative array.
154
     *
155
     * @return array
156
     */
157 1
    public function params(): array
158
    {
159 1
        return $this->params;
160
    }
161
162
    /**
163
     * Returns true if any parameters have been set.
164
     *
165
     * @return bool
166
     */
167 1
    public function hasParams(): bool
168
    {
169 1
        return \count($this->params) > 0;
170
    }
171
172
    /**
173
     * An array of parameters indexed by key.
174
     *
175
     * @param array $params
176
     *
177
     * @throws ParseIncludesException
178
     *
179
     * @return $this
180
     */
181 16
    public function setParams(array $params)
182
    {
183
        // Is this an associative array?
184 16
        if (!empty($params)) {
185 3
            if (\count(array_filter(array_keys($params), '\is_string')) < 1) {
186 1
                throw new ParseIncludesException('Parameters must be an associative array indexed by key');
187
            }
188
        }
189 16
        $this->params = $params;
190
191 16
        return $this;
192
    }
193
194
    /**
195
     * Get the parameters for the given key
196
     * An empty array will be returned for non-matched keys.
197
     *
198
     * @param string $key
199
     *
200
     * @return array
201
     */
202 1
    public function paramsFor(string $key): array
203
    {
204 1
        $params = [];
205 1
        if (isset($this->params[$key])) {
206 1
            $params = $this->params[$key];
207
        }
208
209 1
        return $params;
210
    }
211
212
    /**
213
     * All keys and params will be reset.
214
     *
215
     * @return $this
216
     */
217 1
    public function reset()
218
    {
219 1
        $this->keys = [];
220 1
        $this->params = [];
221
222 1
        return $this;
223
    }
224
225
    /**
226
     * Returns a new Includes object containing all keys spliced below the given parent key,
227
     * as well as any mapped params.
228
     *
229
     * @param string $parentKey
230
     *
231
     * @return static
232
     *
233
     * @see Includes::descendantsOf()
234
     * @see Includes::allParamsFor()
235
     */
236 7
    public function splice($parentKey)
237
    {
238
        // Get all the descendant keys
239 7
        $keys = $this->descendantsOf($parentKey);
240
241
        // Cool, we have a list of keys, now let's get the associated params
242 7
        $params = $this->allParamsFor($keys); // That was hard
243
244 7
        return new static(
245 7
            $keys,
246 7
            $params
247 7
        );
248
    }
249
250
    /**
251
     * Given a parent key, return all the descendant keys (without the parent prefix)
252
     * Example: Given key is "user", the current keys are ["user.id", "user.photos", "user.photos.id"]
253
     * The result will be: ["id", "photos", "photos.id"].
254
     *
255
     * @param string $parentKey
256
     *
257
     * @return array
258
     */
259 7
    public function descendantsOf(string $parentKey): array
260
    {
261
        // First get child keys of the given parent key
262 7
        $keys = [];
263 7
        foreach ($this->keys() as $key) {
264 7
            if (strpos($key, "{$parentKey}.") === 0) {
265
                // Found a match, chop off the parent key
266 3
                $keys[] = preg_replace(
267 3
                    '/^'.preg_quote($parentKey.'.', '/').'/', // Starts with parent
268 3
                    '', // Remove it
269 3
                    $key
270 3
                );
271
            }
272
        }
273
274 7
        return $keys;
275
    }
276
277
    /**
278
     * Get the parameters for the given keys as an associative array indexed by key
279
     * An empty array will be returned if there are no matched keys.
280
     *
281
     * @param array $keys
282
     *
283
     * @return array
284
     */
285 7
    public function allParamsFor(array $keys): array
286
    {
287 7
        $params = [];
288 7
        foreach ($keys as $key) {
289 3
            if (isset($this->params[$key])) {
290
                $params[$key] = $this->params[$key];
291
            }
292
        }
293
294 7
        return $params;
295
    }
296
}
297