ArrayObject::_uxsortmRec()   B
last analyzed

Complexity

Conditions 6
Paths 9

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 6

Importance

Changes 4
Bugs 0 Features 2
Metric Value
c 4
b 0
f 2
dl 0
loc 32
ccs 22
cts 22
cp 1
rs 8.439
cc 6
eloc 22
nc 9
nop 4
crap 6
1
<?php
2
3
/*
4
 * This file is part of the kaloa/util package.
5
 *
6
 * For full copyright and license information, please view the LICENSE file
7
 * that was distributed with this source code.
8
 */
9
10
namespace Kaloa\Util;
11
12
use ArrayObject as CoreArrayObject;
13
14
/**
15
 * Adds grouping and multi-dimensional sorting functions to ArrayObject
16
 *
17
 * See: http://www.ermshaus.org/2010/03/php-kaloa-spl-arrayobject
18
 *
19
 * Please note: This class isn't the most efficient way to perform the
20
 * implemented operations. A more low-level approach to grouping will be
21
 * considerably faster and use less resources, sorting operations should be done
22
 * in the DMBS (if applicable).
23
 */
24
final class ArrayObject extends CoreArrayObject
25
{
26
    /**
27
     * Adds possibility to pass multi-dimensional arrays to the constructor
28
     *
29
     * All arrays found among the values of the passed array will be transformed
30
     * recursively to instances of ArrayObject.
31
     *
32
     * @param array $array Data array to initialize class with
33
     */
34 6
    public function __construct(array $array)
35
    {
36 6
        foreach ($array as $key => $value) {
37 6
            if (is_array($value)) {
38 5
                $array[$key] = new self($value);
39 5
            }
40 6
        }
41
42 6
        parent::__construct($array);
43 6
    }
44
45
    /**
46
     * Groups the array by one or more criteria defined via callback function
47
     *
48
     * Each element in the first dimension of the array is passed to the
49
     * specified callback function and will be reordered in regard to the
50
     * returned value. This can either be a string with the new key or an array
51
     * with a stack of new keys. For an element <var>$e</var>, the callback
52
     * return value <var>array('a', 'b')</var> translates to
53
     * <var>$newArray['a']['b'][] = $e;</var>.
54
     *
55
     * Callback functions may take the element argument by reference and modify
56
     * it during execution (e. g. to remove any fields that will be grouped by).
57
     *
58
     * @param  callback $func Function to group by
59
     * @return ArrayObject Provides fluent interface
60
     */
61 4
    public function groupBy($func)
62
    {
63 4
        $ret = array();
64 4
        $it  = $this->getIterator();
65
66 4
        while ($it->valid()) {
67
68 4
            if (is_object($it->current())) {
69 3
                $key = call_user_func($func, $it->current());
70 3
            } else {
71
                // Pass scalar values by reference, too
72 1
                $value = $it->current();
73 1
                $key = call_user_func_array($func, array(&$value));
74 1
                $it->offsetSet($it->key(), $value);
75 1
                unset($value);
76
            }
77
78 4
            if (is_array($key)) {
79 3
                $ref = &$ret;
80
81 3
                foreach ($key as $subkey) {
82 3
                    if (!array_key_exists($subkey, $ref)) {
83 3
                        $ref[$subkey] = array();
84 3
                    }
85 3
                    $ref = &$ref[$subkey];
86 3
                }
87 3
                $ref[] = $it->current();
88 3
            } else {
89 1
                $ret[$key][] = $it->current();
90
            }
91
92 4
            $it->next();
93 4
        }
94 4
        unset($ref);
95
96 4
        $ret = new self($ret);
97 4
        $this->exchangeArray($ret->getArrayCopy());
98
99 4
        return $this;
100
    }
101
102
    /**
103
     * Adds usort as an instance method
104
     *
105
     * @param callback $cmp_function Function to sort by
106
     * @return boolean
107
     */
108 1
    public function usort($cmp_function)
109
    {
110 1
        $tmp = $this->getArrayCopy();
111 1
        $ret = usort($tmp, $cmp_function);
112
113 1
        $tmp = new self($tmp);
114 1
        $this->exchangeArray($tmp->getArrayCopy());
115
116 1
        return $ret;
117
    }
118
119
    /**
120
     *
121
     * @param  array|callback $funcs
122
     * @return ArrayObject Provides fluent interface
123
     */
124 1
    public function usortm($funcs)
125
    {
126 1
        return $this->_uxsortm($funcs);
127
    }
128
129
    /**
130
     *
131
     *
132
     * @param  array|callback $funcs
133
     * @return ArrayObject Provides fluent interface
134
     */
135 1
    public function uasortm($funcs)
136
    {
137 1
        return $this->_uxsortm($funcs, 'a');
138
    }
139
140
    /**
141
     *
142
     * @param  array|callback $funcs
143
     * @return ArrayObject Provides fluent interface
144
     */
145 1
    public function uksortm($funcs)
146
    {
147 1
        return $this->_uxsortm($funcs, 'k');
148
    }
149
150
    /**
151
     * Returns the multi-dimensional array structure with all instances of
152
     * ArrayObject transformed to standard PHP arrays
153
     *
154
     * @return array Flattened array
155
     */
156 3
    public function getArrayCopyRec()
157
    {
158 3
        $ret = array();
159 3
        $it  = $this->getIterator();
160
161 3
        while ($it->valid()) {
162 3
            if ($it->current() instanceof self) {
163 2
                $ret[$it->key()] = $it->current()->getArrayCopyRec();
164 2
            } else {
165 3
                $ret[$it->key()] = $it->current();
166
            }
167
168 3
            $it->next();
169 3
        }
170
171 3
        return $ret;
172
    }
173
174
    /**
175
     * Recursively applies all provided sorting functions to their corresponding
176
     * dimension of the array
177
     *
178
     * @param ArrayObject $a         Represents the current dimension
179
     *        in the active array branch
180
     * @param array                 $sortFuncs Holds the specified sorting
181
     *        function for each dimension
182
     * @param int                   $depth     Current dimension
183
     * @param string                $sortMode  Possible values: 'a', 'k', ''
184
     *        (= uasort, uksort, usort)
185
     */
186 1
    private function _uxsortmRec(
187
        ArrayObject $a,
188
        array $sortFuncs,
189
        $depth = 0,
190
        $sortMode = ''
191
    ) {
192 1
        $goOn = (count($sortFuncs) > $depth + 1);
193 1
        $it   = $a->getIterator();
194
195 1
        while ($it->valid()) {
196 1
            if (null !== $sortFuncs[$depth]) {
197 1
                if ($sortMode == 'a') {
198 1
                    $it->current()->uasort($sortFuncs[$depth]);
199 1
                } else if ($sortMode == 'k') {
200 1
                    $it->current()->uksort($sortFuncs[$depth]);
201 1
                } else {
202 1
                    $it->current()->usort($sortFuncs[$depth]);
203
                }
204 1
            }
205
206 1
            if ($goOn) {
207 1
                $this->_uxsortmRec(
208 1
                    $it->current(),
209 1
                    $sortFuncs,
210 1
                    $depth + 1,
211
                    $sortMode
212 1
                );
213 1
            }
214
215 1
            $it->next();
216 1
        }
217 1
    }
218
219
    /**
220
     * Applies the first sorting function (if set) to the array's first
221
     * dimension and starts the recursion to apply the other functions (if set)
222
     *
223
     * A sorting function is exactly the same as an usort callback. If you don't
224
     * want to sort a specific dimension but one or more dimensions below it,
225
     * pass <var>null</var> for each dimension that should be skipped.
226
     * <var>array(null, null, $func)</var> would sort the third dimension but
227
     * leave dimensions one and two untouched.
228
     *
229
     * @param  array|callback $funcs    Sorting function(s) to sort one or more
230
     *         dimensions of the array by
231
     * @param  string         $sortMode Possible values: 'a', 'k', '' (= uasort,
232
     *         uksort, usort)
233
     * @return ArrayObject Provides fluent interface
234
     */
235 1
    private function _uxsortm($funcs, $sortMode = '')
236
    {
237 1
        if (!is_array($funcs)) {
238 1
            $funcs = array($funcs);
239 1
        }
240
241 1
        if (count($funcs) > 0) {
242 1
            if (null !== $funcs[0]) {
243 1
                if ($sortMode == 'a') {
244 1
                    $this->uasort($funcs[0]);
245 1
                } else if ($sortMode == 'k') {
246 1
                    $this->uksort($funcs[0]);
247 1
                } else {
248 1
                    $this->usort($funcs[0]);
249
                }
250 1
            }
251
252 1
            if (count($funcs) > 1) {
253 1
                $this->_uxsortmRec($this, $funcs, 1, $sortMode);
254 1
            }
255 1
        }
256
257 1
        return $this;
258
    }
259
}
260