1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | <?php namespace DrSlump\Protobuf\Codec; use DrSlump\Protobuf; /** * This codec serializes and unserializes from/to Xml documents * where the elements represent the field's name. * * It makes use of the PhpArray codec to do the heavy work to just * take care of converting the array to/from XML. */ class Xml extends PhpArray implements Protobuf\CodecInterface { /** @var bool */ protected $dom = false; /** @var string */ protected $root; /** * @param array $options */ public function __construct(array $options = array()) { foreach ($options as $option=>$value) { $this->setOption($option, $value); } } /** * @throws \InvalidArgumentException * @param string $option * @param mixed $value * @return void */ public function setOption($option, $value) { switch (strtolower($option)) { case 'root': $this->root = $value; break; case 'dom': $this->dom = (bool)$value; break; default: throw new \InvalidArgumentException('Unknown option ' . $option); } } /** * @param \DrSlump\Protobuf\Message $message * @return string | \SimpleXMLElement */ public function encode(Protobuf\Message $message) { // Generate an associative array $data = $this->encodeMessage($message); // Build an XML representation $root = $this->root ?: str_replace('\\', '_', get_class($message)); $root = new \SimpleXMLElement('<?xml version="1.0"?><' . $root . '></' . $root . '>'); $this->arrayToXml($root, $data); return $this->dom ? $root : $root->asXML(); } /** * @param \SimpleXMLElement $elem * @param array $data * @param null $label */ protected function arrayToXml(\SimpleXMLElement $elem, array $data, $label = null) { foreach ($data as $k=>$v) { if (is_array($v)) { // Detect nested messages if (!is_int(key($v))) { $child = $elem->addChild($label ?: $k); $this->arrayToXml($child, $v); // Lists are forced a fixed label } else { $this->arrayToXml($elem, $v, $k); } } else { $elem->addChild($label ?: $k, $v); } } } /** * @param \DrSlump\Protobuf\Message $message * @param string | \SimpleXMLElement $xml * @return \DrSlump\Protobuf\Message */ public function decode(Protobuf\Message $message, $xml) { if (is_string($xml)) { $xml = new \SimpleXMLElement($xml); } // Build an associative array from the XML $data = $this->xmlToArray($xml); // Generate a message from the data return $this->decodeMessage($message, $data); } /** * @param \SimpleXMLElement $elem * @return array */ protected function xmlToArray(\SimpleXMLElement $elem) { $data = array(); foreach ($elem->children() as $child) { if (count($child->children())) { $value = $this->xmlToArray($child); } else { $value = (string)$child; } $name = $child->getName(); if (isset($data[$name])) { // If not yet a "list" array if (!is_array($data[$name]) || !is_int(key($data[$name]))) { $data[$name] = array($data[$name]); } $data[$name][] = $value; } else { $data[$name] = $value; } } return $data; } } |