• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5//
6// Use of this source code is governed by a BSD-style
7// license that can be found in the LICENSE file or at
8// https://developers.google.com/open-source/licenses/bsd
9
10/**
11 * MapField and MapFieldIter are used by generated protocol message classes to
12 * manipulate map fields.
13 */
14
15namespace Google\Protobuf\Internal;
16
17use Traversable;
18
19/**
20 * MapField is used by generated protocol message classes to manipulate map
21 * fields. It can be used like native PHP array.
22 */
23class MapField implements \ArrayAccess, \IteratorAggregate, \Countable
24{
25    /**
26     * @ignore
27     */
28    private $container;
29    /**
30     * @ignore
31     */
32    private $key_type;
33    /**
34     * @ignore
35     */
36    private $value_type;
37    /**
38     * @ignore
39     */
40    private $klass;
41    /**
42     * @ignore
43     */
44    private $legacy_klass;
45
46    /**
47     * Constructs an instance of MapField.
48     *
49     * @param long $key_type Type of the stored key element.
50     * @param long $value_type Type of the stored value element.
51     * @param string $klass Message/Enum class name of value instance
52     * (message/enum fields only).
53     * @ignore
54     */
55    public function __construct($key_type, $value_type, $klass = null)
56    {
57        $this->container = [];
58        $this->key_type = $key_type;
59        $this->value_type = $value_type;
60        $this->klass = $klass;
61
62        if ($this->value_type == GPBType::MESSAGE) {
63            $pool = DescriptorPool::getGeneratedPool();
64            $desc = $pool->getDescriptorByClassName($klass);
65            if ($desc == NULL) {
66                new $klass;  // No msg class instance has been created before.
67                $desc = $pool->getDescriptorByClassName($klass);
68            }
69            $this->klass = $desc->getClass();
70            $this->legacy_klass = $desc->getLegacyClass();
71        }
72    }
73
74    /**
75     * @ignore
76     */
77    public function getKeyType()
78    {
79        return $this->key_type;
80    }
81
82    /**
83     * @ignore
84     */
85    public function getValueType()
86    {
87        return $this->value_type;
88    }
89
90    /**
91     * @ignore
92     */
93    public function getValueClass()
94    {
95        return $this->klass;
96    }
97
98    /**
99     * @ignore
100     */
101    public function getLegacyValueClass()
102    {
103        return $this->legacy_klass;
104    }
105
106    /**
107     * Return the element at the given key.
108     *
109     * This will also be called for: $ele = $arr[$key]
110     *
111     * @param int|string $key The key of the element to be fetched.
112     * @return object The stored element at given key.
113     * @throws \ErrorException Invalid type for index.
114     * @throws \ErrorException Non-existing index.
115     * @todo need to add return type mixed (require update php version to 8.0)
116     */
117    #[\ReturnTypeWillChange]
118    public function offsetGet($key)
119    {
120        return $this->container[$key];
121    }
122
123    /**
124     * Assign the element at the given key.
125     *
126     * This will also be called for: $arr[$key] = $value
127     *
128     * @param int|string $key The key of the element to be fetched.
129     * @param object $value The element to be assigned.
130     * @return void
131     * @throws \ErrorException Invalid type for key.
132     * @throws \ErrorException Invalid type for value.
133     * @throws \ErrorException Non-existing key.
134     * @todo need to add return type void (require update php version to 7.1)
135     */
136    #[\ReturnTypeWillChange]
137    public function offsetSet($key, $value)
138    {
139        $this->checkKey($this->key_type, $key);
140
141        switch ($this->value_type) {
142            case GPBType::SFIXED32:
143            case GPBType::SINT32:
144            case GPBType::INT32:
145            case GPBType::ENUM:
146                GPBUtil::checkInt32($value);
147                break;
148            case GPBType::FIXED32:
149            case GPBType::UINT32:
150                GPBUtil::checkUint32($value);
151                break;
152            case GPBType::SFIXED64:
153            case GPBType::SINT64:
154            case GPBType::INT64:
155                GPBUtil::checkInt64($value);
156                break;
157            case GPBType::FIXED64:
158            case GPBType::UINT64:
159                GPBUtil::checkUint64($value);
160                break;
161            case GPBType::FLOAT:
162                GPBUtil::checkFloat($value);
163                break;
164            case GPBType::DOUBLE:
165                GPBUtil::checkDouble($value);
166                break;
167            case GPBType::BOOL:
168                GPBUtil::checkBool($value);
169                break;
170            case GPBType::STRING:
171                GPBUtil::checkString($value, true);
172                break;
173            case GPBType::MESSAGE:
174                if (is_null($value)) {
175                  trigger_error("Map element cannot be null.", E_USER_ERROR);
176                }
177                GPBUtil::checkMessage($value, $this->klass);
178                break;
179            default:
180                break;
181        }
182
183        $this->container[$key] = $value;
184    }
185
186    /**
187     * Remove the element at the given key.
188     *
189     * This will also be called for: unset($arr)
190     *
191     * @param int|string $key The key of the element to be removed.
192     * @return void
193     * @throws \ErrorException Invalid type for key.
194     * @todo need to add return type void (require update php version to 7.1)
195     */
196    #[\ReturnTypeWillChange]
197    public function offsetUnset($key)
198    {
199        $this->checkKey($this->key_type, $key);
200        unset($this->container[$key]);
201    }
202
203    /**
204     * Check the existence of the element at the given key.
205     *
206     * This will also be called for: isset($arr)
207     *
208     * @param int|string $key The key of the element to be removed.
209     * @return bool True if the element at the given key exists.
210     * @throws \ErrorException Invalid type for key.
211     */
212    public function offsetExists($key): bool
213    {
214        $this->checkKey($this->key_type, $key);
215        return isset($this->container[$key]);
216    }
217
218    /**
219     * @ignore
220     */
221    public function getIterator(): Traversable
222    {
223        return new MapFieldIter($this->container, $this->key_type);
224    }
225
226    /**
227     * Return the number of stored elements.
228     *
229     * This will also be called for: count($arr)
230     *
231     * @return integer The number of stored elements.
232     */
233    public function count(): int
234    {
235        return count($this->container);
236    }
237
238    /**
239     * @ignore
240     */
241    private function checkKey($key_type, &$key)
242    {
243        switch ($key_type) {
244            case GPBType::SFIXED32:
245            case GPBType::SINT32:
246            case GPBType::INT32:
247                GPBUtil::checkInt32($key);
248                break;
249            case GPBType::FIXED32:
250            case GPBType::UINT32:
251                GPBUtil::checkUint32($key);
252                break;
253            case GPBType::SFIXED64:
254            case GPBType::SINT64:
255            case GPBType::INT64:
256                GPBUtil::checkInt64($key);
257                break;
258            case GPBType::FIXED64:
259            case GPBType::UINT64:
260                GPBUtil::checkUint64($key);
261                break;
262            case GPBType::BOOL:
263                GPBUtil::checkBool($key);
264                break;
265            case GPBType::STRING:
266                GPBUtil::checkString($key, true);
267                break;
268            default:
269                trigger_error(
270                    "Given type cannot be map key.",
271                    E_USER_ERROR);
272                break;
273        }
274    }
275}
276