Completed
Push — master ( 1586bf...4ce22d )
by Jean
02:08
created

Arrays::isAssociative()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
namespace JClaveau\Arrays;
3
4
/**
5
 *
6
 */
7
class Arrays
8
{
9
    /**
10
     * Taken from Kohana's Arr class.
11
     *
12
     * Tests if an array is associative or not.
13
     *
14
     *     // Returns TRUE
15
     *     Arr::isAssoc(array('username' => 'john.doe'));
16
     *
17
     *     // Returns FALSE
18
     *     Arr::isAssoc('foo', 'bar');
19
     *
20
     * @param   array   $array  array to check
21
     * @return  boolean
22
     */
23
    public static function isAssociative(array $array)
24
    {
25
        // Keys of the array
26
        $keys = array_keys($array);
27
28
        // If the array keys of the keys match the keys, then the array must
29
        // not be associative (e.g. the keys array looked like {0:0, 1:1...}).
30
        return array_keys($keys) !== $keys;
31
    }
32
33
    /**
34
     * Taken from Kohana's Arr class.
35
     *
36
     * Recursively merge two or more arrays. Values in an associative array
37
     * overwrite previous values with the same key. Values in an indexed array
38
     * are appended, but only when they do not already exist in the result.
39
     *
40
     * Note that this does not work the same as [array_merge_recursive](http://php.net/array_merge_recursive)!
41
     *
42
     *     $john = array('name' => 'john', 'children' => array('fred', 'paul', 'sally', 'jane'));
43
     *     $mary = array('name' => 'mary', 'children' => array('jane'));
44
     *
45
     *     // John and Mary are married, merge them together
46
     *     $john = Arr::merge($john, $mary);
47
     *
48
     *     // The output of $john will now be:
49
     *     array('name' => 'mary', 'children' => array('fred', 'paul', 'sally', 'jane'))
50
     *
51
     * @param   array  $array1      initial array
52
     * @param   array  $array2,...  array to merge
53
     * @return  array
54
     */
55
    public static function merge($array1, $array2)
56
    {
57
        if (self::isAssociative($array2))
58
        {
59
            foreach ($array2 as $key => $value)
60
            {
61
                if (is_array($value)
62
                    AND isset($array1[$key])
63
                    AND is_array($array1[$key])
64
                )
65
                {
66
                    $array1[$key] = self::merge($array1[$key], $value);
67
                }
68
                else
69
                {
70
                    $array1[$key] = $value;
71
                }
72
            }
73
        }
74
        else
75
        {
76
            foreach ($array2 as $value)
77
            {
78
                if ( ! in_array($value, $array1, TRUE))
79
                {
80
                    $array1[] = $value;
81
                }
82
            }
83
        }
84
85
        if (func_num_args() > 2)
86
        {
87
            foreach (array_slice(func_get_args(), 2) as $array2)
0 ignored issues
show
introduced by
$array2 is overwriting one of the parameters of this function.
Loading history...
88
            {
89
                if (self::isAssociative($array2))
90
                {
91
                    foreach ($array2 as $key => $value)
92
                    {
93
                        if (is_array($value)
94
                            AND isset($array1[$key])
95
                            AND is_array($array1[$key])
96
                        )
97
                        {
98
                            $array1[$key] = self::merge($array1[$key], $value);
99
                        }
100
                        else
101
                        {
102
                            $array1[$key] = $value;
103
                        }
104
                    }
105
                }
106
                else
107
                {
108
                    foreach ($array2 as $value)
109
                    {
110
                        if ( ! in_array($value, $array1, TRUE))
111
                        {
112
                            $array1[] = $value;
113
                        }
114
                    }
115
                }
116
            }
117
        }
118
119
        return $array1;
120
    }
121
122
    /**
123
     * Equivalent of array_merge_recursive with more options.
124
     *
125
     * @param array         $existing_row
126
     * @param array         $conflict_row
127
     * @param callable|null $merge_resolver
128
     * @param int           $max_depth
129
     *
130
     * + If exist only in conflict row => add
131
     * + If same continue
132
     * + If different merge as array
133
     */
134
    public static function mergeRecursiveCustom(
135
        array $existing_row,
136
        array $conflict_row,
137
        callable $merge_resolver=null,
138
        $max_depth=null
139
    ){
140
        foreach ($conflict_row as $column => $conflict_value) {
141
142
            // not existing in first array
143
            if (!isset($existing_row[$column])) {
144
                $existing_row[$column] = $conflict_value;
145
                continue;
146
            }
147
148
            $existing_value = $existing_row[$column];
149
150
            // two arrays so we recurse
151
            if (is_array($existing_value) && is_array($conflict_value)) {
152
153
                if ($max_depth === null || $max_depth > 0) {
154
                    $existing_row[$column] = static::mergeRecursiveCustom(
155
                        $existing_value,
156
                        $conflict_value,
157
                        $merge_resolver,
158
                        $max_depth - 1
159
                    );
160
                    continue;
161
                }
162
            }
163
164
            if ($merge_resolver) {
165
                $existing_row[$column] = call_user_func_array(
166
                    $merge_resolver,
167
                    [
168
                        $existing_value,
169
                        $conflict_value,
170
                        $column,
171
                    ]
172
                );
173
            }
174
            else {
175
                // same resolution as array_merge_recursive
176
                if (!is_array($existing_value)) {
177
                    $existing_row[$column] = [$existing_value];
178
                }
179
180
                // We store the new value with their previous ones
181
                $existing_row[$column][] = $conflict_value;
182
            }
183
        }
184
185
        return $existing_row;
186
    }
187
188
    /**
189
     * Merges two rows
190
     *
191
     * @param  array $existing_row
192
     * @param  array $conflict_row
193
     *
194
     * @return array
195
     */
196
    public static function mergePreservingDistincts(
197
        array $existing_row,
198
        array $conflict_row
199
    ){
200
        $merge = static::mergeRecursiveCustom(
201
            $existing_row,
202
            $conflict_row,
203
            function ($existing_value, $conflict_value, $column) {
1 ignored issue
show
Unused Code introduced by
The parameter $column 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

203
            function ($existing_value, $conflict_value, /** @scrutinizer ignore-unused */ $column) {

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...
204
205
                if ( ! $existing_value instanceof MergeBucket) {
206
                    $existing_value = MergeBucket::from()->push($existing_value);
207
                }
208
209
                // We store the new value with their previous ones
210
                if ( ! $conflict_value instanceof MergeBucket) {
211
                    $conflict_value = MergeBucket::from()->push($conflict_value);
212
                }
213
214
                foreach ($conflict_value->toArray() as $conflict_key => $conflict_entry) {
215
                    $existing_value->push($conflict_entry);
216
                }
217
218
                return $existing_value;
219
            },
220
            0
221
        );
222
223
        return $merge;
224
    }
225
226
    /**
227
     *
228
     */
229
    public static function cleanMergeBuckets($merged_row)
230
    {
231
        foreach ($merged_row as $entry => $values) {
232
            if ($values instanceof MergeBucket) {
233
                $out[ $entry ] = $values->toArray();
234
            }
235
        }
236
237
        return $out;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $out does not seem to be defined for all execution paths leading up to this point.
Loading history...
238
    }
239
240
    /**
241
     * This is the cleaning part of self::mergePreservingDistincts()
242
     *
243
     * @see mergePreservingDistincts()
244
     */
245
    public static function keepUniqueColumnValues(array $row, array $excluded_columns=[])
246
    {
247
        foreach ($row as $column => &$values) {
248
            if (!is_array($values))
249
                continue;
250
251
            if (in_array($column, $excluded_columns))
252
                continue;
253
254
            $values = array_unique($values);
255
            if (count($values) == 1)
256
                $values = $values[0];
257
        }
258
259
        return $row;
260
    }
261
262
    /**
263
     * Replacement of array_unique, keeping the first key.
264
     *
265
     * @param  $array
266
     * @return With unique values
0 ignored issues
show
Bug introduced by
The type JClaveau\Arrays\With was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
267
     *
268
     * @todo   Options to keep another key than the first one?
269
     */
270
    public static function unique($array)
271
    {
272
        if (! is_array($array) && ! $array instanceof \Traversable) {
273
            throw \InvalidArgumentEXception(
0 ignored issues
show
Bug introduced by
The function InvalidArgumentEXception was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

273
            throw /** @scrutinizer ignore-call */ \InvalidArgumentEXception(
Loading history...
274
                "\$array must be an array or a \Traversable instead of: \n"
275
                .var_export($array)
0 ignored issues
show
Bug introduced by
Are you sure the usage of var_export($array) is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
276
            );
277
        }
278
279
        $ids = [];
280
        foreach ($array as $key => $value) {
281
            if (is_scalar($value)) {
282
                $id = $value;
283
            }
284
            else {
285
                $id = serialize($value);
286
            }
287
288
            if (isset($ids[ $id ])) {
289
                unset($array[ $key ]);
290
                $ids[ $id ][] = $key;
291
                continue;
292
            }
293
294
            $ids[ $id ] = [$key];
295
        }
296
297
        return $array;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $array returns the type Traversable|array which is incompatible with the documented return type JClaveau\Arrays\With.
Loading history...
298
    }
299
300
    /**/
301
}
302