Passed
Pull Request — 4.2 (#8186)
by
unknown
10:54 queued 03:46
created

ArrayLib::is_associative()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM;
4
5
use Generator;
6
7
/**
8
 * Library of static methods for manipulating arrays.
9
 */
10
class ArrayLib
11
{
12
13
    /**
14
     * Inverses the first and second level keys of an associative
15
     * array, keying the result by the second level, and combines
16
     * all first level entries within them.
17
     *
18
     * Before:
19
     * <example>
20
     * array(
21
     *    'row1' => array(
22
     *        'col1' =>'val1',
23
     *        'col2' => 'val2'
24
     *    ),
25
     *    'row2' => array(
26
     *        'col1' => 'val3',
27
     *        'col2' => 'val4'
28
     *    )
29
     * )
30
     * </example>
31
     *
32
     * After:
33
     * <example>
34
     * array(
35
     *    'col1' => array(
36
     *        'row1' => 'val1',
37
     *        'row2' => 'val3',
38
     *    ),
39
     *    'col2' => array(
40
     *        'row1' => 'val2',
41
     *        'row2' => 'val4',
42
     *    ),
43
     * )
44
     * </example>
45
     *
46
     * @param array $arr
47
     * @return array
48
     */
49
    public static function invert($arr)
50
    {
51
        if (!$arr) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arr 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...
52
            return [];
53
        }
54
55
        $result = array();
56
57
        foreach ($arr as $columnName => $column) {
58
            foreach ($column as $rowName => $cell) {
59
                $result[$rowName][$columnName] = $cell;
60
            }
61
        }
62
63
        return $result;
64
    }
65
66
    /**
67
     * Return an array where the keys are all equal to the values.
68
     *
69
     * @param $arr array
70
     * @return array
71
     */
72
    public static function valuekey($arr)
73
    {
74
        return array_combine($arr, $arr);
75
    }
76
77
    /**
78
     * Flattens a nested array to a one level array
79
     *
80
     * @param array $array
81
     * @return array
82
     */
83
    public static function array_values_recursive($array)
84
    {
85
        $valuesArray = [];
86
87
        array_walk_recursive($array, function($value, $key) use (&$valuesArray) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

87
        array_walk_recursive($array, function($value, /** @scrutinizer ignore-unused */ $key) use (&$valuesArray) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
88
            if (is_scalar($value)) {
89
                array_push($valuesArray, $value);
90
            }
91
        });
92
93
        return $valuesArray;
94
    }
95
96
    /**
97
     * Filter an array by keys (useful for only allowing certain form-input to
98
     * be saved).
99
     *
100
     * @param $arr array
101
     * @param $keys array
102
     *
103
     * @return array
104
     */
105
    public static function filter_keys($arr, $keys)
106
    {
107
        foreach ($arr as $key => $v) {
108
            if (!in_array($key, $keys)) {
109
                unset($arr[$key]);
110
            }
111
        }
112
113
        return $arr;
114
    }
115
116
    /**
117
     * Determines if an array is associative by checking for existing keys via
118
     * array_key_exists().
119
     *
120
     * @see http://nz.php.net/manual/en/function.is-array.php#121692
121
     *
122
     * @param array $array
123
     *
124
     * @return boolean
125
     */
126
    public static function is_associative($array)
127
    {
128
        $isAssociative = !empty($array)
129
            && is_array($array)
130
            && ($array !== array_values($array));
131
132
        return $isAssociative;
133
    }
134
135
    /**
136
     * Recursively searches an array $haystack for the value(s) $needle.
137
     *
138
     * Assumes that all values in $needle (if $needle is an array) are at
139
     * the SAME level, not spread across multiple dimensions of the $haystack.
140
     *
141
     * @param mixed $needle
142
     * @param array $haystack
143
     * @param boolean $strict
144
     *
145
     * @return boolean
146
     */
147
    public static function in_array_recursive($needle, $haystack, $strict = false)
148
    {
149
        if (!is_array($haystack)) {
0 ignored issues
show
introduced by
The condition is_array($haystack) is always true.
Loading history...
150
            return false;
151
        }
152
153
        if (in_array($needle, $haystack, $strict)) {
154
            return true;
155
        } else {
156
            foreach ($haystack as $obj) {
157
                if (self::in_array_recursive($needle, $obj, $strict)) {
158
                    return true;
159
                }
160
            }
161
        }
162
163
        return false;
164
    }
165
166
    /**
167
     * Similar to array_map, but recurses when arrays are encountered.
168
     *
169
     * Actually only one array argument is supported.
170
     *
171
     * @param $f callback to apply
172
     * @param $array array
173
     * @return array
174
     */
175
    public static function array_map_recursive($f, $array)
176
    {
177
        $applyOrRecurse = function ($v) use ($f) {
178
            return is_array($v) ? ArrayLib::array_map_recursive($f, $v) : call_user_func($f, $v);
179
        };
180
181
        return array_map($applyOrRecurse, $array);
182
    }
183
184
    /**
185
     * Recursively merges two or more arrays.
186
     *
187
     * Behaves similar to array_merge_recursive(), however it only merges
188
     * values when both are arrays rather than creating a new array with
189
     * both values, as the PHP version does. The same behaviour also occurs
190
     * with numeric keys, to match that of what PHP does to generate $_REQUEST.
191
     *
192
     * @param array $array
193
     *
194
     * @return array
195
     */
196
    public static function array_merge_recursive($array)
197
    {
198
        $arrays = func_get_args();
199
        $merged = array();
200
201
        if (count($arrays) == 1) {
202
            return $array;
203
        }
204
205
        while ($arrays) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $arrays 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...
206
            $array = array_shift($arrays);
207
208
            if (!is_array($array)) {
209
                trigger_error(
210
                    'SilverStripe\ORM\ArrayLib::array_merge_recursive() encountered a non array argument',
211
                    E_USER_WARNING
212
                );
213
                return [];
214
            }
215
216
            if (!$array) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $array 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...
217
                continue;
218
            }
219
220
            foreach ($array as $key => $value) {
221
                if (is_array($value) && array_key_exists($key, $merged) && is_array($merged[$key])) {
222
                    $merged[$key] = ArrayLib::array_merge_recursive($merged[$key], $value);
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
223
                } else {
224
                    $merged[$key] = $value;
225
                }
226
            }
227
        }
228
229
        return $merged;
230
    }
231
232
    /**
233
     * Takes an multi dimension array and returns the flattened version.
234
     *
235
     * @param array $array
236
     * @param boolean $preserveKeys
237
     * @param array $out
238
     *
239
     * @return array
240
     */
241
    public static function flatten($array, $preserveKeys = true, &$out = array())
242
    {
243
        foreach ($array as $key => $child) {
244
            if (is_array($child)) {
245
                $out = self::flatten($child, $preserveKeys, $out);
246
            } else {
247
                if ($preserveKeys) {
248
                    $out[$key] = $child;
249
                } else {
250
                    $out[] = $child;
251
                }
252
            }
253
        }
254
255
        return $out;
256
    }
257
258
    /**
259
     * Iterate list, but allowing for modifications to the underlying list.
260
     * Items in $list will only be iterated exactly once for each key, and supports
261
     * items being removed or deleted.
262
     * List must be associative.
263
     *
264
     * @param array $list
265
     * @return Generator
266
     */
267
    public static function iterateVolatile(array &$list)
268
    {
269
        // Keyed by already-iterated items
270
        $iterated = [];
271
        // Get all items not yet iterated
272
        while ($items = array_diff_key($list, $iterated)) {
273
            // Yield all results
274
            foreach ($items as $key => $value) {
275
                // Skip items removed by a prior step
276
                if (array_key_exists($key, $list)) {
277
                    // Ensure we yield from the source list
278
                    $iterated[$key] = true;
279
                    yield $key => $list[$key];
280
                }
281
            }
282
        }
283
    }
284
}
285