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

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
239
                    // Do nothing
240
                } elseif ($preserveKeys) {
241
                    $out[$key] = $value;
242
                } else {
243
                    $out[] = $value;
244
                }
245
            }
246
        );
247
248
        return $out;
249
    }
250
251
    /**
252
     * Iterate list, but allowing for modifications to the underlying list.
253
     * Items in $list will only be iterated exactly once for each key, and supports
254
     * items being removed or deleted.
255
     * List must be associative.
256
     *
257
     * @param array $list
258
     * @return Generator
259
     */
260
    public static function iterateVolatile(array &$list)
261
    {
262
        // Keyed by already-iterated items
263
        $iterated = [];
264
        // Get all items not yet iterated
265
        while ($items = array_diff_key($list, $iterated)) {
266
            // Yield all results
267
            foreach ($items as $key => $value) {
268
                // Skip items removed by a prior step
269
                if (array_key_exists($key, $list)) {
270
                    // Ensure we yield from the source list
271
                    $iterated[$key] = true;
272
                    yield $key => $list[$key];
273
                }
274
            }
275
        }
276
    }
277
}
278