Created
January 20, 2015 01:25
-
-
Save gmazzap/a37fbe70a15f99655fe3 to your computer and use it in GitHub Desktop.
Recursively convert an array or a traversable object into a nested array. Optionally convert all "atomic" items to strings and optionally HTML-encode all strings.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php namespace GM; | |
use Traversable; | |
use JsonSerializable; | |
/** | |
* Stateless class that recursively convert an array or a traversable object into a nested array. | |
* Optionally convert all "atomic" items to strings and optionally HTML-encode all strings. | |
* Array and traversable objects are all recursively converted. | |
* Non-traversable objects are converted to array, in 1st available among following methods: | |
* - if a transformer callback is provided, than it's called with object as param | |
* - if a transformer class is provided, than its transform() method is called with object as param | |
* - if the object has a method toArray() it is called | |
* - if the object has a method asArray() it is called | |
* - if the object is an instance of JsonSerializable then jsonSerialize() is called | |
* - get_object_vars() is called | |
* The obtained array is recursively parsed. | |
* | |
* @author Giuseppe Mazzapica <[email protected]> | |
* @license http://opensource.org/licenses/MIT MIT | |
*/ | |
class Arraize | |
{ | |
const CAST = 64; | |
const ESCAPE = 128; | |
/** | |
* @param mixed $data Data to convert | |
* @param bool $escape Should strings in data be HTML-encoded? | |
* @param array $trasf Transformes: full qualified class names or callables | |
* @param bool $tostring Should all scalar items in data be casted to strings? | |
* @return array | |
*/ | |
public function run($data = [], $escape = false, array $trasf = [], $tostring = false) | |
{ | |
$collect = $this->collect($data, $this->flags($escape, $tostring), $trasf); | |
return empty($collect) ? [] : (array) $collect; | |
} | |
/** | |
* @param mixed $var | |
* @param int $flags | |
* @param array $trasf | |
* @return mixed | |
*/ | |
private function collect($var, $flags, $trasf) | |
{ | |
return $this->traversable($var) ? | |
$this->walk($var, $flags, $trasf) : | |
$this->atomic($var, $flags, $trasf); | |
} | |
/** | |
* @param mixed $var | |
* @param int $flags | |
* @param array $trasf | |
* @return mixed | |
*/ | |
private function atomic($var, $flags, $trasf) | |
{ | |
return is_object($var) ? | |
$this->collect($this->transform($var, get_class($var), $trasf), $flags, $trasf) : | |
$this->escape($var, $flags); | |
} | |
/** | |
* | |
* @param array|Traversable $var | |
* @param int $flags | |
* @param array $trasf | |
* @return array | |
*/ | |
private function walk($var, $flags, $trasf) | |
{ | |
$output = []; | |
foreach ($var as $index => $item) { | |
$output[$index] = $this->traversable($item) ? | |
$this->collect($item, $flags, $trasf) : | |
$this->atomic($item, $flags, $trasf); | |
} | |
return $output; | |
} | |
/** | |
* @param object $var | |
* @param string $class | |
* @param array $trasf | |
* @return array | |
*/ | |
private function transform($var, $class, $trasf) | |
{ | |
$cb = $this->trasformer(isset($trasf[trim($class, '\\')]) ? $trasf[$class] : false); | |
if (is_object($cb) && method_exists($cb, 'transform')) { | |
$cb = [$cb, 'transform']; | |
} | |
return is_callable($cb) ? $this->vars(call_user_func($cb, $var), false) : $this->convert($var); | |
} | |
/** | |
* @param callable|string|bool $trasformer | |
* @return callable|object|bool | |
*/ | |
private function trasformer($trasformer) | |
{ | |
if (! is_callable($trasformer)) { | |
$trasformer = is_string($trasformer) && class_exists($trasformer) ? | |
new $trasformer() : | |
false; | |
} | |
return $trasformer; | |
} | |
/** | |
* @param object $var | |
* @return array | |
*/ | |
private function convert($var) | |
{ | |
$toarray = array_reduce(['toArray', 'asArray'], function ($obj, $name) { | |
return is_object($obj) && method_exists($obj, $name) ? (array) $obj->$name() : $obj; | |
}, $var); | |
return is_array($toarray) ? $toarray : $this->vars($var, true); | |
} | |
/** | |
* @param object $var | |
* @param bool $try_json | |
* @return array | |
*/ | |
private function vars($var, $try_json) | |
{ | |
if ($try_json && $var instanceof JsonSerializable) { | |
$var = $var->jsonSerialize(); | |
} | |
return is_object($var) ? get_object_vars($var) : (array) $var; | |
} | |
/** | |
* @param bool $escape | |
* @param bool $tostring | |
* @return int | |
*/ | |
private function flags($escape = false, $tostring = false) | |
{ | |
return (empty($escape) ? 0 : self::ESCAPE) | (empty($tostring) ? 0 : self::CAST); | |
} | |
/** | |
* @param Traversable $var | |
* @return bool | |
*/ | |
private function traversable($var) | |
{ | |
return is_array($var) || $var instanceof Traversable; | |
} | |
/** | |
* @param mixed $var | |
* @param int $flags | |
* @return mixed | |
*/ | |
private function escape($var, $flags) | |
{ | |
return ($flags & self::ESCAPE) && is_string($var) ? | |
htmlspecialchars($var, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') : | |
$this->string($var, $flags); | |
} | |
/** | |
* @param mixed $var | |
* @param int $flags | |
* @return mixed | |
*/ | |
private function string($var, $flags) | |
{ | |
return ($flags & self::CAST) && ! is_string($var) ? | |
$this->escape((string) $var, $flags) : | |
$var; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment