Collection::offsetSet()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
3
namespace Guzzle\Common;
4
5
use Guzzle\Common\Exception\InvalidArgumentException;
6
use Guzzle\Common\Exception\RuntimeException;
7
8
/**
9
 * Key value pair collection object
10
 */
11
class Collection implements \ArrayAccess, \IteratorAggregate, \Countable, ToArrayInterface
12
{
13
    /** @var array Data associated with the object. */
14
    protected $data;
15
16
    /**
17
     * @param array $data Associative array of data to set
18
     */
19
    public function __construct(array $data = array())
20
    {
21
        $this->data = $data;
22
    }
23
24
    /**
25
     * Create a new collection from an array, validate the keys, and add default values where missing
26
     *
27
     * @param array $config   Configuration values to apply.
28
     * @param array $defaults Default parameters
29
     * @param array $required Required parameter names
30
     *
31
     * @return self
32
     * @throws InvalidArgumentException if a parameter is missing
33
     */
34
    public static function fromConfig(array $config = array(), array $defaults = array(), array $required = array())
35
    {
36
        $data = $config + $defaults;
37
38
        if ($missing = array_diff($required, array_keys($data))) {
39
            throw new InvalidArgumentException('Config is missing the following keys: ' . implode(', ', $missing));
40
        }
41
42
        return new self($data);
43
    }
44
45
    public function count()
46
    {
47
        return count($this->data);
48
    }
49
50
    public function getIterator()
51
    {
52
        return new \ArrayIterator($this->data);
53
    }
54
55
    public function toArray()
56
    {
57
        return $this->data;
58
    }
59
60
    /**
61
     * Removes all key value pairs
62
     *
63
     * @return Collection
64
     */
65
    public function clear()
66
    {
67
        $this->data = array();
68
69
        return $this;
70
    }
71
72
    /**
73
     * Get all or a subset of matching key value pairs
74
     *
75
     * @param array $keys Pass an array of keys to retrieve only a subset of key value pairs
76
     *
77
     * @return array Returns an array of all matching key value pairs
78
     */
79
    public function getAll(array $keys = null)
80
    {
81
        return $keys ? array_intersect_key($this->data, array_flip($keys)) : $this->data;
82
    }
83
84
    /**
85
     * Get a specific key value.
86
     *
87
     * @param string $key Key to retrieve.
88
     *
89
     * @return mixed|null Value of the key or NULL
90
     */
91
    public function get($key)
92
    {
93
        return isset($this->data[$key]) ? $this->data[$key] : null;
94
    }
95
96
    /**
97
     * Set a key value pair
98
     *
99
     * @param string $key   Key to set
100
     * @param mixed  $value Value to set
101
     *
102
     * @return Collection Returns a reference to the object
103
     */
104
    public function set($key, $value)
105
    {
106
        $this->data[$key] = $value;
107
108
        return $this;
109
    }
110
111
    /**
112
     * Add a value to a key.  If a key of the same name has already been added, the key value will be converted into an
113
     * array and the new value will be pushed to the end of the array.
114
     *
115
     * @param string $key   Key to add
116
     * @param mixed  $value Value to add to the key
117
     *
118
     * @return Collection Returns a reference to the object.
119
     */
120
    public function add($key, $value)
121
    {
122
        if (!array_key_exists($key, $this->data)) {
123
            $this->data[$key] = $value;
124
        } elseif (is_array($this->data[$key])) {
125
            $this->data[$key][] = $value;
126
        } else {
127
            $this->data[$key] = array($this->data[$key], $value);
128
        }
129
130
        return $this;
131
    }
132
133
    /**
134
     * Remove a specific key value pair
135
     *
136
     * @param string $key A key to remove
137
     *
138
     * @return Collection
139
     */
140
    public function remove($key)
141
    {
142
        unset($this->data[$key]);
143
144
        return $this;
145
    }
146
147
    /**
148
     * Get all keys in the collection
149
     *
150
     * @return array
151
     */
152
    public function getKeys()
153
    {
154
        return array_keys($this->data);
155
    }
156
157
    /**
158
     * Returns whether or not the specified key is present.
159
     *
160
     * @param string $key The key for which to check the existence.
161
     *
162
     * @return bool
163
     */
164
    public function hasKey($key)
165
    {
166
        return array_key_exists($key, $this->data);
167
    }
168
169
    /**
170
     * Case insensitive search the keys in the collection
171
     *
172
     * @param string $key Key to search for
173
     *
174
     * @return bool|string Returns false if not found, otherwise returns the key
175
     */
176
    public function keySearch($key)
177
    {
178
        foreach (array_keys($this->data) as $k) {
179
            if (!strcasecmp($k, $key)) {
180
                return $k;
181
            }
182
        }
183
184
        return false;
185
    }
186
187
    /**
188
     * Checks if any keys contains a certain value
189
     *
190
     * @param string $value Value to search for
191
     *
192
     * @return mixed Returns the key if the value was found FALSE if the value was not found.
193
     */
194
    public function hasValue($value)
195
    {
196
        return array_search($value, $this->data);
197
    }
198
199
    /**
200
     * Replace the data of the object with the value of an array
201
     *
202
     * @param array $data Associative array of data
203
     *
204
     * @return Collection Returns a reference to the object
205
     */
206
    public function replace(array $data)
207
    {
208
        $this->data = $data;
209
210
        return $this;
211
    }
212
213
    /**
214
     * Add and merge in a Collection or array of key value pair data.
215
     *
216
     * @param Collection|array $data Associative array of key value pair data
217
     *
218
     * @return Collection Returns a reference to the object.
219
     */
220
    public function merge($data)
221
    {
222
        foreach ($data as $key => $value) {
223
            $this->add($key, $value);
224
        }
225
226
        return $this;
227
    }
228
229
    /**
230
     * Over write key value pairs in this collection with all of the data from an array or collection.
231
     *
232
     * @param array|\Traversable $data Values to override over this config
233
     *
234
     * @return self
235
     */
236
    public function overwriteWith($data)
237
    {
238
        if (is_array($data)) {
239
            $this->data = $data + $this->data;
240
        } elseif ($data instanceof Collection) {
241
            $this->data = $data->toArray() + $this->data;
242
        } else {
243
            foreach ($data as $key => $value) {
244
                $this->data[$key] = $value;
245
            }
246
        }
247
248
        return $this;
249
    }
250
251
    /**
252
     * Returns a Collection containing all the elements of the collection after applying the callback function to each
253
     * one. The Closure should accept three parameters: (string) $key, (string) $value, (array) $context and return a
254
     * modified value
255
     *
256
     * @param \Closure $closure Closure to apply
257
     * @param array    $context Context to pass to the closure
258
     * @param bool     $static  Set to TRUE to use the same class as the return rather than returning a Collection
259
     *
260
     * @return Collection
261
     */
262 View Code Duplication
    public function map(\Closure $closure, array $context = array(), $static = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
263
    {
264
        $collection = $static ? new static() : new self();
265
        foreach ($this as $key => $value) {
266
            $collection->add($key, $closure($key, $value, $context));
267
        }
268
269
        return $collection;
270
    }
271
272
    /**
273
     * Iterates over each key value pair in the collection passing them to the Closure. If the  Closure function returns
274
     * true, the current value from input is returned into the result Collection.  The Closure must accept three
275
     * parameters: (string) $key, (string) $value and return Boolean TRUE or FALSE for each value.
276
     *
277
     * @param \Closure $closure Closure evaluation function
278
     * @param bool     $static  Set to TRUE to use the same class as the return rather than returning a Collection
279
     *
280
     * @return Collection
281
     */
282 View Code Duplication
    public function filter(\Closure $closure, $static = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
283
    {
284
        $collection = ($static) ? new static() : new self();
285
        foreach ($this->data as $key => $value) {
286
            if ($closure($key, $value)) {
287
                $collection->add($key, $value);
288
            }
289
        }
290
291
        return $collection;
292
    }
293
294
    public function offsetExists($offset)
295
    {
296
        return isset($this->data[$offset]);
297
    }
298
299
    public function offsetGet($offset)
300
    {
301
        return isset($this->data[$offset]) ? $this->data[$offset] : null;
302
    }
303
304
    public function offsetSet($offset, $value)
305
    {
306
        $this->data[$offset] = $value;
307
    }
308
309
    public function offsetUnset($offset)
310
    {
311
        unset($this->data[$offset]);
312
    }
313
314
    /**
315
     * Set a value into a nested array key. Keys will be created as needed to set the value.
316
     *
317
     * @param string $path  Path to set
318
     * @param mixed  $value Value to set at the key
319
     *
320
     * @return self
321
     * @throws RuntimeException when trying to setPath using a nested path that travels through a scalar value
322
     */
323
    public function setPath($path, $value)
324
    {
325
        $current =& $this->data;
326
        $queue = explode('/', $path);
327
        while (null !== ($key = array_shift($queue))) {
328
            if (!is_array($current)) {
329
                throw new RuntimeException("Trying to setPath {$path}, but {$key} is set and is not an array");
330
            } elseif (!$queue) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $queue of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
331
                $current[$key] = $value;
332
            } elseif (isset($current[$key])) {
333
                $current =& $current[$key];
334
            } else {
335
                $current[$key] = array();
336
                $current =& $current[$key];
337
            }
338
        }
339
340
        return $this;
341
    }
342
343
    /**
344
     * Gets a value from the collection using an array path (e.g. foo/baz/bar would retrieve bar from two nested arrays)
345
     * Allows for wildcard searches which recursively combine matches up to the level at which the wildcard occurs. This
346
     * can be useful for accepting any key of a sub-array and combining matching keys from each diverging path.
347
     *
348
     * @param string $path      Path to traverse and retrieve a value from
349
     * @param string $separator Character used to add depth to the search
350
     * @param mixed  $data      Optional data to descend into (used when wildcards are encountered)
351
     *
352
     * @return mixed|null
353
     */
354
    public function getPath($path, $separator = '/', $data = null)
355
    {
356
        if ($data === null) {
357
            $data =& $this->data;
358
        }
359
360
        $path = is_array($path) ? $path : explode($separator, $path);
361
        while (null !== ($part = array_shift($path))) {
362
            if (!is_array($data)) {
363
                return null;
364
            } elseif (isset($data[$part])) {
365
                $data =& $data[$part];
366
            } elseif ($part != '*') {
367
                return null;
368
            } else {
369
                // Perform a wildcard search by diverging and merging paths
370
                $result = array();
371
                foreach ($data as $value) {
372
                    if (!$path) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $path of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
373
                        $result = array_merge_recursive($result, (array) $value);
374
                    } elseif (null !== ($test = $this->getPath($path, $separator, $value))) {
0 ignored issues
show
Documentation introduced by
$path is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
375
                        $result = array_merge_recursive($result, (array) $test);
376
                    }
377
                }
378
                return $result;
379
            }
380
        }
381
382
        return $data;
383
    }
384
385
    /**
386
     * Inject configuration settings into an input string
387
     *
388
     * @param string $input Input to inject
389
     *
390
     * @return string
391
     * @deprecated
392
     */
393
    public function inject($input)
394
    {
395
        Version::warn(__METHOD__ . ' is deprecated');
396
        $replace = array();
397
        foreach ($this->data as $key => $val) {
398
            $replace['{' . $key . '}'] = $val;
399
        }
400
401
        return strtr($input, $replace);
402
    }
403
}
404