Test Failed
Push — master ( 8e4667...0df70f )
by Jodie
02:22
created

Includes::hasParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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