* @license http://www.gnu.org/licenses/ GPLv3
* @link http://ermshaus.org/2010/03/php-kaloa-spl-arrayobject See for
* examples and additional info
* @see ArrayObject
* @version 2010-03-10
*/
class Kaloa_Spl_ArrayObject extends ArrayObject
{
/**
* Adds possibility to pass multi-dimensional arrays to the constructor
*
* All arrays found among the values of the passed array will be transformed
* recursively to instances of Kaloa_Spl_ArrayObject.
*
* @param array $array Data array to initialize class with
*/
public function __construct(array $array)
{
foreach($array as $key => $value) {
if (is_array($value)) {
$array[$key] = new self($value);
}
}
parent::__construct($array);
}
/**
* Groups the array by one or more criteria defined via callback function
*
* Each element in the first dimension of the array is passed to the
* specified callback function and will be reordered in regard to the
* returned value. This can either be a string with the new key or an array
* with a stack of new keys. For an element $e, the callback
* return value array('a', 'b') translates to
* $newArray['a']['b'][] = $e;.
*
* Callback functions may take the element argument by reference and modify
* it during execution (e. g. to remove any fields that will be grouped by).
*
* @param callback $func Function to group by
* @return Kaloa_Spl_ArrayObject Provides fluent interface
*/
public function groupBy($func)
{
$ret = array();
$it = $this->getIterator();
while ($it->valid()) {
if (is_object($it->current())) {
$key = call_user_func($func, $it->current());
} else {
// Pass scalar values by reference, too
$value = $it->current();
$key = call_user_func_array($func, array(&$value));
$it->offsetSet($it->key(), $value);
unset($value);
}
if (is_array($key)) {
$ref = &$ret;
foreach ($key as $subkey) {
if (!array_key_exists($subkey, $ref)) {
$ref[$subkey] = array();
}
$ref = &$ref[$subkey];
}
$ref[] = $it->current();
} else {
$ret[$key][] = $it->current();
}
$it->next();
}
unset($ref);
$ret = new self($ret);
$this->exchangeArray($ret->getArrayCopy());
return $this;
}
/**
* Adds usort as an instance method
*
* @param callback $cmp_function Function to sort by
* @return boolean
*/
public function usort($cmp_function)
{
$tmp = $this->getArrayCopy();
$ret = usort($tmp, $cmp_function);
$tmp = new self($tmp);
$this->exchangeArray($tmp->getArrayCopy());
return $ret;
}
/**
* Recursively applies all provided sorting functions to their corresponding
* dimension of the array
*
* @param Kaloa_Spl_ArrayObject $a Represents the current dimension
* in the active array branch
* @param array $sortFuncs Holds the specified sorting
* function for each dimension
* @param int $depth Current dimension
* @param string $sortMode Possible values: 'a', 'k', ''
* (= uasort, uksort, usort)
*/
protected function _uxsortmRec(Kaloa_Spl_ArrayObject $a, array $sortFuncs,
$depth = 0, $sortMode = '')
{
$goOn = (count($sortFuncs) > $depth + 1);
$it = $a->getIterator();
while ($it->valid()) {
if (null !== $sortFuncs[$depth]) {
if ($sortMode == 'a') {
$it->current()->uasort($sortFuncs[$depth]);
} else if ($sortMode == 'k') {
$it->current()->uksort($sortFuncs[$depth]);
} else {
$it->current()->usort($sortFuncs[$depth]);
}
}
if ($goOn) {
$this->_uxsortmRec($it->current(), $sortFuncs, $depth + 1,
$sortMode);
}
$it->next();
}
}
/**
* Applies the first sorting function (if set) to the array's first
* dimension and starts the recursion to apply the other functions (if set)
*
* A sorting function is exactly the same as an usort callback. If you don't
* want to sort a specific dimension but one or more dimensions below it,
* pass null for each dimension that should be skipped.
* array(null, null, $func) would sort the third dimension but
* leave dimensions one and two untouched.
*
* @param array|callback $funcs Sorting function(s) to sort one or more
* dimensions of the array by
* @param string $sortMode Possible values: 'a', 'k', '' (= uasort,
* uksort, usort)
* @return Kaloa_Spl_ArrayObject Provides fluent interface
*/
protected function _uxsortm($funcs, $sortMode = '')
{
if (!is_array($funcs)) {
$funcs = array($funcs);
}
if (count($funcs) > 0) {
if (null !== $funcs[0]) {
if ($sortMode == 'a') {
$this->uasort($funcs[0]);
} else if ($sortMode == 'k') {
$this->uksort($funcs[0]);
} else {
$this->usort($funcs[0]);
}
}
if (count($funcs) > 1) {
$this->_uxsortmRec($this, $funcs, 1, $sortMode);
}
}
return $this;
}
/**
*
* @param array|callback $funcs
* @return Kaloa_Spl_ArrayObject Provides fluent interface
*/
public function usortm($funcs)
{
return $this->_uxsortm($funcs);
}
/**
*
*
* @param array|callback $funcs
* @return Kaloa_Spl_ArrayObject Provides fluent interface
*/
public function uasortm($funcs)
{
return $this->_uxsortm($funcs, 'a');
}
/**
*
* @param array|callback $funcs
* @return Kaloa_Spl_ArrayObject Provides fluent interface
*/
public function uksortm($funcs)
{
return $this->_uxsortm($funcs, 'k');
}
}