Add php protobuffer support for transition to GTFS-realtime
[busui.git] / lib / Protobuf-PHP / library / DrSlump / Protobuf / Codec / Xml.php
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;
    }
 
}