Completed
Push — master ( d84682...296916 )
by Lars
02:12
created

ArrayyAbstract::has()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
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 7
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
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 3
      $usedArray = $array;
55 3
    } else {
56 29
      $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 Arrayy
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 1
            (array)$this->array,
189 1
            function ($item) use (
190 1
                $property,
191 1
                $value,
192 1
                $ops,
193 1
                $comparisonOp
194
            ) {
195 1
              $item = (array)$item;
196 1
              $itemArrayy = new Arrayy($item);
197 1
              $item[$property] = $itemArrayy->get($property, array());
198
199 1
              return $ops[$comparisonOp]($item, $property, $value);
200
            }
201 1
        )
202 1
    );
203
204 1
    return Arrayy::create($result);
205
  }
206
207
  /**
208
   * find by ...
209
   *
210
   * @param        $property
211
   * @param        $value
212
   * @param string $comparisonOp
213
   *
214
   * @return Arrayy
215
   */
216
  public function findBy($property, $value, $comparisonOp = 'eq')
217
  {
218
    $return = $this->filterBy($property, $value, $comparisonOp);
219
220
    return Arrayy::create($return);
0 ignored issues
show
Documentation introduced by
$return is of type object<Arrayy\Arrayy>, but the function expects a array.

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...
221
  }
222
223
  ////////////////////////////////////////////////////////////////////
224
  ///////////////////////////// ANALYZE //////////////////////////////
225
  ////////////////////////////////////////////////////////////////////
226
227
  /**
228
   * Get all keys from the current array.
229
   *
230
   * @return Arrayy
231
   */
232 1
  public function keys()
233
  {
234 1
    $return = array_keys((array)$this->array);
235
236 1
    return Arrayy::create($return);
237
  }
238
239
  /**
240
   * Get all values from a array.
241
   *
242
   * @return Arrayy
243
   */
244 1
  public function values()
245
  {
246 1
    $return = array_values((array)$this->array);
247
248 1
    return Arrayy::create($return);
249
  }
250
251
  ////////////////////////////////////////////////////////////////////
252
  ////////////////////////////// ALTER ///////////////////////////////
253
  ////////////////////////////////////////////////////////////////////
254
255
  /**
256
   * Replace a key with a new key/value pair.
257
   *
258
   * @param $replace
259
   * @param $key
260
   * @param $value
261
   *
262
   * @return Arrayy
263
   */
264 1
  public function replace($replace, $key, $value)
265
  {
266 1
    $this->remove($replace);
267
268 1
    return $this->set($key, $value);
269
  }
270
271
  /**
272
   * Group values from a array according to the results of a closure.
273
   *
274
   * @param string $grouper a callable function name
275
   * @param bool   $saveKeys
276
   *
277
   * @return Arrayy
278
   */
279 3
  public function group($grouper, $saveKeys = false)
280
  {
281 3
    $array = (array)$this->array;
282 3
    $result = array();
283
284
    // Iterate over values, group by property/results from closure
285 3
    foreach ($array as $key => $value) {
286 3
      $groupKey = is_callable($grouper) ? $grouper($value, $key) : $this->get($grouper, null, $value);
287 3
      $newValue = $this->get($groupKey, null, $result);
288
289
      // Add to results
290 3
      if ($groupKey !== null) {
291 2
        if ($saveKeys) {
292 1
          $result[$groupKey] = $newValue;
293 1
          $result[$groupKey][$key] = $value;
294 1
        } else {
295 1
          $result[$groupKey] = $newValue;
296 1
          $result[$groupKey][] = $value;
297
        }
298 2
      }
299
300 3
    }
301
302 3
    return Arrayy::create($result);
303
  }
304
305
  ////////////////////////////////////////////////////////////////////
306
  ////////////////////////////// HELPERS /////////////////////////////
307
  ////////////////////////////////////////////////////////////////////
308
309
  /**
310
   * Internal mechanic of set method.
311
   *
312
   * @param string $key
313
   * @param mixed  $value
314
   *
315
   * @return mixed
316
   */
317 14
  protected function internalSet($key, $value)
318
  {
319 14
    if (null === $key) {
320
      /** @noinspection OneTimeUseVariablesInspection */
321
      $array = $value;
322
323
      return $array;
324
    }
325
326
    // Explode the keys
327 14
    $keys = explode('.', $key);
328
329
    // Crawl through the keys
330 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...
331
      $key = array_shift($keys);
332
333
      $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...
334
      $this->array = &$this->array[$key];
335
    }
336
337
    // Bind final tree on the array
338 14
    $key = array_shift($keys);
339
340 14
    $this->array[$key] = $value;
341 14
  }
342
343
  /**
344
   * Internal mechanics of remove method.
345
   *
346
   * @param $key
347
   *
348
   * @return boolean
349
   */
350 10
  protected function internalRemove($key)
351
  {
352
    // Explode keys
353 10
    $keys = explode('.', $key);
354
355
    // Crawl though the keys
356 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...
357
      $key = array_shift($keys);
358
359
      if (!$this->has($key)) {
360
        return false;
361
      }
362
363
      $this->array = &$this->array[$key];
364
    }
365
366 10
    $key = array_shift($keys);
367
368 10
    unset($this->array[$key]);
369
370 10
    return true;
371
  }
372
373
  /**
374
   * Given a list, and an iteratee function that returns
375
   * a key for each element in the list (or a property name),
376
   * returns an object with an index of each item.
377
   * Just like groupBy, but for when you know your keys are unique.
378
   *
379
   * @param mixed $key
380
   *
381
   * @return Arrayy
382
   */
383 3
  public function indexBy($key)
384
  {
385 3
    $results = array();
386
387 3
    foreach ($this->array as $a) {
388 3
      if (isset($a[$key])) {
389 2
        $results[$a[$key]] = $a;
390 2
      }
391 3
    }
392
393 3
    return Arrayy::create($results);
394
  }
395
}
396