Completed
Push — master ( cdbde6...eaec0f )
by Lars
30:14
created

ArrayyAbstract::replace()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 3
crap 1
1
<?php
2
3
namespace Arrayy;
4
5
use Closure;
6
7
/**
8
 * Abstract Arrayy
9
 * Methods that apply to both objects and arrays.
10
 */
11
abstract class ArrayyAbstract
12
{
13
  /**
14
   * @var array
15
   */
16
  protected $array = array();
17
18
  ////////////////////////////////////////////////////////////////////
19
  ///////////////////////////// ANALYZE //////////////////////////////
20
  ////////////////////////////////////////////////////////////////////
21
22
  /**
23
   * Check if an array has a given key.
24
   *
25
   * @param mixed $key
26
   *
27
   * @return bool
28
   */
29 18
  public function has($key)
30
  {
31
    // Generate unique string to use as marker.
32 18
    $unFound = (string)uniqid('arrayy', true);
33
34 18
    return $this->get($key, $unFound) !== $unFound;
35
  }
36
37
  ////////////////////////////////////////////////////////////////////
38
  //////////////////////////// FETCH FROM ////////////////////////////
39
  ////////////////////////////////////////////////////////////////////
40
41
  /**
42
   * Get a value from an array (optional using dot-notation).
43
   *
44
   * @param string $key     The key to look for
45
   * @param mixed  $default Default value to fallback to
46
   * @param array  $array   The array to get from,
47
   *                        if it's set to "null" we use the current array from the class
48
   *
49
   * @return mixed
50
   */
51 31
  public function get($key, $default = null, $array = null)
52
  {
53 31
    if (is_array($array) === true) {
54 4
      $usedArray = $array;
55 4
    } else {
56 28
      $usedArray = $this->array;
57
    }
58
59 31
    if (null === $key) {
60 1
      return $usedArray;
61
    }
62
63 31
    if (isset($usedArray[$key])) {
64 22
      return $usedArray[$key];
65
    }
66
67
    // Crawl through array, get key according to object or not
68 16
    foreach (explode('.', $key) as $segment) {
69 16
      if (!isset($usedArray[$segment])) {
70 16
        return $default instanceof Closure ? $default() : $default;
71
      }
72
73
      $usedArray = $usedArray[$segment];
74
    }
75
76
    return $usedArray;
77
  }
78
79
  /**
80
   * Set a value in a array using dot notation.
81
   *
82
   * @param string $key   The key to set
83
   * @param mixed  $value Its value
84
   *
85
   * @return Arrayy
86
   */
87 14
  public function set($key, $value)
88
  {
89 14
    $this->internalSet($key, $value);
90
91 14
    return Arrayy::create($this->array);
92
  }
93
94
  /**
95
   * Get a value from a array and set it if it was not.
96
   *
97
   * WARNING: this method only set the value, if the $key is not already set
98
   *
99
   * @param string $key     The key
100
   * @param mixed  $default The default value to set if it isn't
101
   *
102
   * @return mixed
103
   */
104 9
  public function setAndGet($key, $default = null)
105
  {
106
    // If the key doesn't exist, set it
107 9
    if (!$this->has($key)) {
108 4
      $this->array = $this->set($key, $default)->getArray();
109 4
    }
110
111 9
    return $this->get($key);
112
  }
113
114
  /**
115
   * Remove a value from an array using dot notation.
116
   *
117
   * @param mixed $key
118
   *
119
   * @return mixed
120
   */
121 10
  public function remove($key)
122
  {
123
    // Recursive call
124 10
    if (is_array($key)) {
125
      foreach ($key as $k) {
126
        $this->internalRemove($k);
127
      }
128
129
      return $this->array;
130
    }
131
132 10
    $this->internalRemove($key);
133
134 10
    return $this->array;
135
  }
136
137
  /**
138
   * Filters an array of objects (or a numeric array of associative arrays) based on the value of a particular property
139
   * within that.
140
   *
141
   * @param        $property
142
   * @param        $value
143
   * @param string $comparisonOp
144
   *
145
   * @return array
146
   */
147 1
  public function filterBy($property, $value, $comparisonOp = null)
148
  {
149 1
    if (!$comparisonOp) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $comparisonOp of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
150 1
      $comparisonOp = is_array($value) ? 'contains' : 'eq';
151 1
    }
152
153
    $ops = array(
154
        'eq'          => function ($item, $prop, $value) {
155 1
          return $item[$prop] === $value;
156 1
        },
157
        'gt'          => function ($item, $prop, $value) {
158
          return $item[$prop] > $value;
159 1
        },
160
        'gte'         => function ($item, $prop, $value) {
161
          return $item[$prop] >= $value;
162 1
        },
163
        'lt'          => function ($item, $prop, $value) {
164 1
          return $item[$prop] < $value;
165 1
        },
166
        'lte'         => function ($item, $prop, $value) {
167
          return $item[$prop] <= $value;
168 1
        },
169
        'ne'          => function ($item, $prop, $value) {
170
          return $item[$prop] !== $value;
171 1
        },
172
        'contains'    => function ($item, $prop, $value) {
173 1
          return in_array($item[$prop], (array)$value, true);
174 1
        },
175
        'notContains' => function ($item, $prop, $value) {
176
          return !in_array($item[$prop], (array)$value, true);
177 1
        },
178
        'newer'       => function ($item, $prop, $value) {
179
          return strtotime($item[$prop]) > strtotime($value);
180 1
        },
181
        'older'       => function ($item, $prop, $value) {
182
          return strtotime($item[$prop]) < strtotime($value);
183 1
        },
184 1
    );
185
186 1
    $result = array_values(
187 1
        array_filter(
188
            (array)$this->array, function ($item) use (
189 1
            $property,
190 1
            $value,
191 1
            $ops,
192 1
            $comparisonOp
193
        ) {
194 1
          $item = (array)$item;
195 1
          $item[$property] = $this->get($property, array(), $item);
196
197 1
          return $ops[$comparisonOp]($item, $property, $value);
198
        }
199 1
        )
200 1
    );
201
202 1
    return $result;
203
  }
204
205
  /**
206
   * find by ...
207
   *
208
   * @param        $property
209
   * @param        $value
210
   * @param string $comparisonOp
211
   *
212
   * @return array
213
   */
214
  public function findBy($property, $value, $comparisonOp = 'eq')
215
  {
216
    return $this->filterBy($property, $value, $comparisonOp);
217
  }
218
219
  ////////////////////////////////////////////////////////////////////
220
  ///////////////////////////// ANALYZE //////////////////////////////
221
  ////////////////////////////////////////////////////////////////////
222
223
  /**
224
   * Get all keys from the current array.
225
   *
226
   * @return array
227
   */
228 1
  public function keys()
229
  {
230 1
    return array_keys((array)$this->array);
231
  }
232
233
  /**
234
   * Get all values from a array.
235
   *
236
   * @return array
237
   */
238 1
  public function values()
239
  {
240 1
    return array_values((array)$this->array);
241
  }
242
243
  ////////////////////////////////////////////////////////////////////
244
  ////////////////////////////// ALTER ///////////////////////////////
245
  ////////////////////////////////////////////////////////////////////
246
247
  /**
248
   * Replace a key with a new key/value pair.
249
   *
250
   * @param $replace
251
   * @param $key
252
   * @param $value
253
   *
254
   * @return Arrayy
255
   */
256 1
  public function replace($replace, $key, $value)
257
  {
258 1
    $this->remove($replace);
259
260 1
    return $this->set($key, $value);
261
  }
262
263
  /**
264
   * Sort a array by value, by a closure or by a property
265
   * If the sorter is null, the array is sorted naturally.
266
   *
267
   * @param null   $sorter
268
   * @param string $direction
269
   *
270
   * @return array
271
   */
272 1
  public function sort($sorter = null, $direction = 'asc')
273
  {
274 1
    $array = (array)$this->array;
275
276
    // Get correct PHP constant for direction
277 1
    $direction = strtolower($direction);
278
279 1
    if ($direction === 'desc') {
280 1
      $directionType = SORT_DESC;
281 1
    } else {
282 1
      $directionType = SORT_ASC;
283
    }
284
285
    // Transform all values into their results
286 1
    if ($sorter) {
287 1
      $arrayy = new Arrayy($array);
288
289 1
      $results = $arrayy->each(
290 1
          function ($value) use ($sorter) {
291 1
            return is_callable($sorter) ? $sorter($value) : $this->get($sorter, null, $value);
292
          }
293 1
      );
294 1
    } else {
295 1
      $results = $array;
296
    }
297
298
    // Sort by the results and replace by original values
299 1
    array_multisort($results, $directionType, SORT_REGULAR, $array);
300
301 1
    return $array;
302
  }
303
304
  /**
305
   * Group values from a array according to the results of a closure.
306
   *
307
   * @param string $grouper a callable function name
308
   * @param bool   $saveKeys
309
   *
310
   * @return array
311
   */
312 3
  public function group($grouper, $saveKeys = false)
313
  {
314 3
    $array = (array)$this->array;
315 3
    $result = array();
316
317
    // Iterate over values, group by property/results from closure
318 3
    foreach ($array as $key => $value) {
319 3
      $groupKey = is_callable($grouper) ? $grouper($value, $key) : $this->get($grouper, null, $value);
320 3
      $newValue = $this->get($groupKey, null, $result);
321
322
      // Add to results
323 3
      if ($groupKey !== null) {
324 2
        if ($saveKeys) {
325 1
          $result[$groupKey] = $newValue;
326 1
          $result[$groupKey][$key] = $value;
327 1
        } else {
328 1
          $result[$groupKey] = $newValue;
329 1
          $result[$groupKey][] = $value;
330
        }
331 2
      }
332
333 3
    }
334
335 3
    return $result;
336
  }
337
338
  ////////////////////////////////////////////////////////////////////
339
  ////////////////////////////// HELPERS /////////////////////////////
340
  ////////////////////////////////////////////////////////////////////
341
342
  /**
343
   * Internal mechanic of set method.
344
   *
345
   * @param string $key
346
   * @param mixed  $value
347
   *
348
   * @return mixed
349
   */
350 14
  protected function internalSet($key, $value)
351
  {
352 14
    if (null === $key) {
353
      /** @noinspection OneTimeUseVariablesInspection */
354
      $array = $value;
355
356
      return $array;
357
    }
358
359
    // Explode the keys
360 14
    $keys = explode('.', $key);
361
362
    // Crawl through the keys
363 14 View Code Duplication
    while (count($keys) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
364
      $key = array_shift($keys);
365
366
      $this->array[$key] = $this->get(array(), null, $key);
0 ignored issues
show
Documentation introduced by
array() 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...
367
      $this->array = &$this->array[$key];
368
    }
369
370
    // Bind final tree on the array
371 14
    $key = array_shift($keys);
372
373 14
    $this->array[$key] = $value;
374 14
  }
375
376
  /**
377
   * Internal mechanics of remove method.
378
   *
379
   * @param $key
380
   *
381
   * @return boolean
382
   */
383 10
  protected function internalRemove($key)
384
  {
385
    // Explode keys
386 10
    $keys = explode('.', $key);
387
388
    // Crawl though the keys
389 10 View Code Duplication
    while (count($keys) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
390
      $key = array_shift($keys);
391
392
      if (!$this->has($key)) {
393
        return false;
394
      }
395
396
      $this->array = &$this->array[$key];
397
    }
398
399 10
    $key = array_shift($keys);
400
401 10
    unset($this->array[$key]);
402
403 10
    return true;
404
  }
405
406
  /**
407
   * Given a list, and an iteratee function that returns
408
   * a key for each element in the list (or a property name),
409
   * returns an object with an index of each item.
410
   * Just like groupBy, but for when you know your keys are unique.
411
   *
412
   * @param mixed $key
413
   *
414
   * @return array
415
   */
416 3
  public function indexBy($key)
417
  {
418 3
    $results = array();
419
420 3
    foreach ($this->array as $a) {
421 3
      if (isset($a[$key])) {
422 2
        $results[$a[$key]] = $a;
423 2
      }
424 3
    }
425
426 3
    return $results;
427
  }
428
}
429