PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/src/Converter.php

https://gitlab.com/xibalba/ocelote
PHP | 322 lines | 171 code | 39 blank | 112 comment | 77 complexity | 6d76a34839d1a8f8480d61fa49c119ec MD5 | raw file
  1. <?php
  2. /**
  3. * @copyright 2014 - 2019 Xibalba Lab.
  4. * @license http://opensource.org/licenses/bsd-license.php
  5. * @link https://gitlab.com/xibalba/ocelote
  6. */
  7. namespace xibalba\ocelote;
  8. use \JsonSerializable;
  9. use \Serializable;
  10. use \SimpleXmlElement;
  11. /**
  12. * A class that handles the detection and conversion of certain resource formats / content types into other formats.
  13. * The current formats are supported: XML, JSON, Array, Object, Serialized
  14. *
  15. * @author Yeshua Rodas <yrodas@upnfm.edu.hn> ☭
  16. * @package alpaca\ocelote
  17. */
  18. class Converter {
  19. /** Disregard XML attributes and only return the value */
  20. const XML_NONE = 0;
  21. /** Merge attributes and the value into a single dimension; the values key will be "value" */
  22. const XML_MERGE = 1;
  23. /** Group the attributes into a key "attributes" and the value into a key of "value" */
  24. const XML_GROUP = 2;
  25. /** Attributes will only be returned */
  26. const XML_ATTRIBS = 3;
  27. /**
  28. * Autobox a value by type casting it.
  29. *
  30. * @param mixed $value
  31. * @return mixed
  32. */
  33. public static function autobox($value) {
  34. if (is_numeric($value)) {
  35. if (strpos($value, '.') !== false) return (float) $value;
  36. else return (int) $value;
  37. } else if (is_bool($value)) return (bool) $value;
  38. else if ($value === 'true' || $value === 'false') return ($value === 'true');
  39. return (string) $value;
  40. }
  41. /**
  42. * Unbox values by type casting to a string equivalent.
  43. *
  44. * @param mixed $value
  45. * @return string
  46. */
  47. public static function unbox($value) : string {
  48. if (is_bool($value)) return $value ? 'true' : 'false';
  49. return (string) $value;
  50. }
  51. /**
  52. * Turn an object into an array. Alternative to array_map magic.
  53. *
  54. * @param object|array $object
  55. * @return array
  56. */
  57. public static function buildArray($object) : array {
  58. $array = [];
  59. foreach ($object as $key => $value) {
  60. if (is_object($value) || is_array($value)) $array[$key] = static::buildArray($value);
  61. else $array[$key] = static::autobox($value);
  62. }
  63. return $array;
  64. }
  65. /**
  66. * Turn an array into an object. Alternative to array_map magic.
  67. *
  68. * @param array|object $array
  69. * @return object
  70. */
  71. public static function buildObject($array) : \stdClass {
  72. $obj = new \stdClass();
  73. foreach ($array as $key => $value) {
  74. if (is_array($value) || is_object($value)) $obj->{$key} = static::buildObject($value);
  75. else $obj->{$key} = static::autobox($value);
  76. }
  77. return $obj;
  78. }
  79. /**
  80. * Turn an array into an XML document. Alternative to array_map magic.
  81. *
  82. * @param \SimpleXMLElement $xml
  83. * @param array $array
  84. * @return \SimpleXMLElement
  85. */
  86. public static function buildXml(SimpleXMLElement &$xml, $array) : \SimpleXMLElement {
  87. if (is_array($array)) {
  88. foreach ($array as $key => $value) {
  89. // XML_NONE
  90. if (!is_array($value)) {
  91. $xml->addChild($key, static::unbox($value));
  92. continue;
  93. }
  94. // Multiple nodes of the same name
  95. if (Checker::isIndexed($value)) {
  96. foreach ($value as $kValue) {
  97. if (is_array($kValue)) static::buildXml($xml, [$key => $kValue]);
  98. else $xml->addChild($key, static::unbox($kValue));
  99. }
  100. }
  101. // XML_GROUP
  102. else if (isset($value['attributes'])) {
  103. if (!isset($value['value'])) $value['value'] = null;
  104. if (is_array($value['value'])) {
  105. $node = $xml->addChild($key);
  106. static::buildXml($node, $value['value']);
  107. } else $node = $xml->addChild($key, static::unbox($value['value']));
  108. if (!empty($value['attributes'])) {
  109. foreach ($value['attributes'] as $aKey => $aValue) {
  110. $node->addAttribute($aKey, static::unbox($aValue));
  111. }
  112. }
  113. }
  114. // XML_MERGE
  115. else if (isset($value['value'])) {
  116. $node = $xml->addChild($key, $value['value']);
  117. unset($value['value']);
  118. if (!empty($value)) {
  119. foreach ($value as $aKey => $aValue) {
  120. if (is_array($aValue)) static::buildXml($node, array($aKey => $aValue));
  121. else $node->addAttribute($aKey, static::unbox($aValue));
  122. }
  123. }
  124. }
  125. // XML_ATTRIBS
  126. else {
  127. $node = $xml->addChild($key);
  128. if (!empty($value)) {
  129. foreach ($value as $aKey => $aValue) {
  130. if (is_array($aValue)) static::buildXml($node, array($aKey => $aValue));
  131. else $node->addChild($aKey, static::unbox($aValue));
  132. }
  133. }
  134. }
  135. }
  136. }
  137. return $xml;
  138. }
  139. /**
  140. * Transforms a resource into an array.
  141. *
  142. * @param mixed $resource
  143. * @param bool $recursive
  144. * @return array
  145. */
  146. public static function toArray($resource, $recursive = false) : array {
  147. if (Checker::isArray($resource)) return $recursive ? static::buildArray($resource) : $resource;
  148. else if (Checker::isObject($resource)) return static::buildArray($resource);
  149. else if (Checker::isJson($resource)) $resource = json_decode($resource, true);
  150. else if (Checker::isSerialized($resource)) $resource = unserialize($resource);
  151. else if (Checker::isXml($resource)) $resource = static::xmlToArray(simplexml_load_string($resource));
  152. else if (Checker::isBag($resource)) return $resource->toArray();
  153. return (array) $resource;
  154. }
  155. /**
  156. * Transform a resource into an array and put in into a ocelote Bag instance.
  157. *
  158. * @param mixed $resource
  159. * @param bool $recursive
  160. * @return ocelote\Bag
  161. */
  162. public static function toBag($resource, $recursive = false) : Bag {
  163. $bag = new Bag();
  164. $bag->add(static::toArray($resource, $recursive));
  165. return $bag;
  166. }
  167. /**
  168. * Transforms a resource into a JSON object.
  169. *
  170. * @param mixed $resource
  171. * @param int $options
  172. * @return string
  173. */
  174. public static function toJson($resource, $options = 0) : string {
  175. if (Checker::isJson($resource)) return $resource;
  176. else if (Checker::isObject($resource)) $resource = static::buildArray($resource);
  177. else if (Checker::isXml($resource)) $resource = static::xmlToArray(simplexml_load_string($resource));
  178. else if (Checker::isSerialized($resource)) $resource = unserialize($resource);
  179. else if (Checker::isBag($resource)) return json_encode($resource->toArray(), $options);
  180. return json_encode($resource, $options);
  181. }
  182. /**
  183. * Transforms a resource into an object.
  184. *
  185. * @param mixed $resource
  186. * @param bool $recursive
  187. * @return object
  188. */
  189. public static function toObject($resource, $recursive = false) : \stdClass {
  190. if (Checker::isObject($resource)) {
  191. if (!$recursive) return $resource;
  192. }
  193. else if (Checker::isJson($resource)) $resource = json_decode($resource, true);
  194. else if (Checker::isSerialized($resource)) $resource = unserialize($resource);
  195. else if (Checker::isXml($resource)) $resource = static::xmlToArray(simplexml_load_string($resource));
  196. return static::buildObject($resource);
  197. }
  198. /**
  199. * Transforms a resource into a serialized form.
  200. *
  201. * @param mixed $resource
  202. * @return string
  203. */
  204. public static function toSerialize($resource) : string {
  205. if ($resource instanceof Serializable) {
  206. // pass-through
  207. } else $resource = static::toArray($resource);
  208. return serialize($resource);
  209. }
  210. /**
  211. * Transforms a resource into an XML document.
  212. *
  213. * @param mixed $resource
  214. * @param string $root
  215. * @return string
  216. */
  217. public static function toXml($resource, $root = 'root') : string {
  218. if ($array = static::toArray($resource, true)) {
  219. $xml = simplexml_load_string('<?xml version="1.0" encoding="utf-8"?><' . $root . '></' . $root . '>');
  220. $response = static::buildXml($xml, $array);
  221. return trim($response->asXML());
  222. }
  223. return null;
  224. }
  225. /**
  226. * Transform a Query String into an Array.
  227. * Bassically this method is a wrapper to `parse_str()` funcion.
  228. *
  229. * @see http://php.net/manual/en/function.parse-str.php `parse_str()` official documentation.
  230. *
  231. * @param string $str Query string to parse.
  232. * @return array The array result.
  233. */
  234. public static function queryStringToArray(string $str) : array {
  235. $result = [];
  236. parse_str($str, $result);
  237. return $result;
  238. }
  239. /**
  240. * Convert a SimpleXML object into an array.
  241. *
  242. * @param SimpleXMLElement $xml
  243. * @param int $format
  244. * @return array
  245. */
  246. public static function xmlToArray(SimpleXMLElement $xml, $format = self::XML_GROUP) {
  247. if (count($xml->children()) <= 0) return static::autobox((string) $xml);
  248. $array = [];
  249. /** @type SimpleXMLElement $node */
  250. foreach ($xml->children() as $element => $node) {
  251. $data = [];
  252. $children = $node->children();
  253. if (!isset($array[$element])) $array[$element] = '';
  254. if (!$node->attributes() || $format === static::XML_NONE) $data = static::xmlToArray($node, $format);
  255. else {
  256. switch ($format) {
  257. case static::XML_GROUP:
  258. $data = [
  259. 'value' => static::autobox((string) $node),
  260. 'attributes' => []
  261. ];
  262. if (count($children) > 0) $data['value'] = static::xmlToArray($node, $format);
  263. foreach ($node->attributes() as $attr => $value) {
  264. $data['attributes'][$attr] = static::autobox((string) $value);
  265. }
  266. break;
  267. case static::XML_MERGE:
  268. if (count($children) > 0) $data = $data + static::xmlToArray($node, $format);
  269. else $data['value'] = static::autobox((string) $node);
  270. /* fall-through */
  271. case static::XML_ATTRIBS:
  272. foreach ($node->attributes() as $attr => $value) {
  273. $data[$attr] = static::autobox((string) $value);
  274. }
  275. break;
  276. }
  277. }
  278. if (count($xml->{$element}) > 1) $array[$element][] = $data;
  279. else $array[$element] = $data;
  280. }
  281. return $array;
  282. }
  283. }