Add php protobuffer support for transition to GTFS-realtime
[busui.git] / lib / Protobuf-PHP / library / DrSlump / Protobuf / Codec / PhpArray.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
140
141
142
143
144
145
146
147
<?php
 
namespace DrSlump\Protobuf\Codec;
 
use DrSlump\Protobuf;
 
/**
 * This codec serializes and unserializes data from/to PHP associative
 * arrays, allowing it to be used as a base for an arbitrary number
 * of different serializations (json, yaml, ini, xml ...).
 *
 */
class PhpArray implements Protobuf\CodecInterface
{
    /** @var bool */
    protected $useTagNumber = false;
 
    /**
     * Tells the codec to expect the array keys to contain the
     * field's tag number instead of the name.
     *
     * @param bool $useIt
     */
    public function useTagNumberAsKey($useIt = true)
    {
        $this->useTagNumber = $useIt;
    }
 
    /**
     * @param \DrSlump\Protobuf\Message $message
     * @return array
     */
    public function encode(Protobuf\Message $message)
    {
        return $this->encodeMessage($message);
    }
 
    /**
     * @param \DrSlump\Protobuf\Message $message
     * @param array $data
     * @return \DrSlump\Protobuf\Message
     */
    public function decode(Protobuf\Message $message, $data)
    {
        return $this->decodeMessage($message, $data);
    }
 
    protected function encodeMessage(Protobuf\Message $message)
    {
        $descriptor = Protobuf::getRegistry()->getDescriptor($message);
 
        $data = array();
        foreach ($descriptor->getFields() as $tag=>$field) {
 
            $empty = !$message->_has($tag);
            if ($field->isRequired() && $empty) {
                throw new \UnexpectedValueException(
                    'Message ' . get_class($message) . '\'s field tag ' . $tag . '(' . $field->getName() . ') is required but has no value'
                );
            }
 
            if ($empty) {
                continue;
            }
 
            $key = $this->useTagNumber ? $field->getNumber() : $field->getName();
            $v = $message->_get($tag);
 
            if ($field->isRepeated()) {
                // Make sure the value is an array of values
                $v = is_array($v) ? $v : array($v);
                foreach ($v as $k=>$vv) {
                    $v[$k] = $this->filterValue($vv, $field);
                }
            } else {
                $v = $this->filterValue($v, $field);
            }
 
            $data[$key] = $v;
        }
 
        return $data;
    }
 
    protected function decodeMessage(Protobuf\Message $message, $data)
    {
        // Get message descriptor
        $descriptor = Protobuf::getRegistry()->getDescriptor($message);
 
        foreach ($data as $key=>$v) {
 
            // Get the field by tag number or name
            $field = $this->useTagNumber
                   ? $descriptor->getField($key)
                   : $descriptor->getFieldByName($key);
 
            // Unknown field found
            if (!$field) {
                $unknown = new PhpArray\Unknown($key, gettype($v), $v);
                $message->addUnknown($unknown);
                continue;
            }
 
            if ($field->isRepeated()) {
                // Make sure the value is an array of values
                $v = is_array($v) && is_int(key($v)) ? $v : array($v);
                foreach ($v as $k=>$vv) {
                    $v[$k] = $this->filterValue($vv, $field);
                }
            } else {
                $v = $this->filterValue($v, $field);
            }
 
            $message->_set($field->getNumber(), $v);
        }
 
        return $message;
    }
 
    protected function filterValue($value, Protobuf\Field $field)
    {
        switch ($field->getType()) {
            case Protobuf::TYPE_MESSAGE:
                // Tell apart encoding and decoding
                if ($value instanceof Protobuf\Message) {
                    return $this->encodeMessage($value);
                } else {
                    $nested = $field->getReference();
                    return $this->decodeMessage(new $nested, $value);
                }
            case Protobuf::TYPE_BOOL:
                return filter_var($value, FILTER_VALIDATE_BOOLEAN);
            case Protobuf::TYPE_STRING:
            case Protobuf::TYPE_BYTES:
                return (string)$value;
            case Protobuf::TYPE_FLOAT:
            case Protobuf::TYPE_DOUBLE:
                return filter_var($value, FILTER_VALIDATE_FLOAT);
            // Assume the rest are ints
            default:
                return filter_var($value, FILTER_VALIDATE_INT);
        }
    }
 
 
}