• 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 * RepeatedField and RepeatedFieldIter are used by generated protocol message
12 * classes to manipulate repeated fields.
13 */
14
15namespace Google\Protobuf\Internal;
16
17use Google\Protobuf\Internal\GPBType;
18use Google\Protobuf\Internal\GPBUtil;
19use Traversable;
20
21/**
22 * RepeatedField is used by generated protocol message classes to manipulate
23 * repeated fields. It can be used like native PHP array.
24 */
25class RepeatedField implements \ArrayAccess, \IteratorAggregate, \Countable
26{
27
28    /**
29     * @ignore
30     */
31    private $container;
32    /**
33     * @ignore
34     */
35    private $type;
36    /**
37     * @ignore
38     */
39    private $klass;
40    /**
41     * @ignore
42     */
43    private $legacy_klass;
44
45    /**
46     * Constructs an instance of RepeatedField.
47     *
48     * @param integer $type Type of the stored element.
49     * @param string $klass Message/Enum class name (message/enum fields only).
50     * @ignore
51     */
52    public function __construct($type, $klass = null)
53    {
54        $this->container = [];
55        $this->type = $type;
56        if ($this->type == GPBType::MESSAGE) {
57            $pool = DescriptorPool::getGeneratedPool();
58            $desc = $pool->getDescriptorByClassName($klass);
59            if ($desc == NULL) {
60                new $klass;  // No msg class instance has been created before.
61                $desc = $pool->getDescriptorByClassName($klass);
62            }
63            $this->klass = $desc->getClass();
64            $this->legacy_klass = $desc->getLegacyClass();
65        }
66    }
67
68    /**
69     * @ignore
70     */
71    public function getType()
72    {
73        return $this->type;
74    }
75
76    /**
77     * @ignore
78     */
79    public function getClass()
80    {
81        return $this->klass;
82    }
83
84    /**
85     * @ignore
86     */
87    public function getLegacyClass()
88    {
89        return $this->legacy_klass;
90    }
91
92    /**
93     * Return the element at the given index.
94     *
95     * This will also be called for: $ele = $arr[0]
96     *
97     * @param integer $offset The index of the element to be fetched.
98     * @return mixed The stored element at given index.
99     * @throws \ErrorException Invalid type for index.
100     * @throws \ErrorException Non-existing index.
101     * @todo need to add return type mixed (require update php version to 8.0)
102     */
103    #[\ReturnTypeWillChange]
104    public function offsetGet($offset)
105    {
106        return $this->container[$offset];
107    }
108
109    /**
110     * Assign the element at the given index.
111     *
112     * This will also be called for: $arr []= $ele and $arr[0] = ele
113     *
114     * @param int|null $offset The index of the element to be assigned.
115     * @param mixed $value The element to be assigned.
116     * @return void
117     * @throws \ErrorException Invalid type for index.
118     * @throws \ErrorException Non-existing index.
119     * @throws \ErrorException Incorrect type of the element.
120     * @todo need to add return type void (require update php version to 7.1)
121     */
122    #[\ReturnTypeWillChange]
123    public function offsetSet($offset, $value)
124    {
125        switch ($this->type) {
126            case GPBType::SFIXED32:
127            case GPBType::SINT32:
128            case GPBType::INT32:
129            case GPBType::ENUM:
130                GPBUtil::checkInt32($value);
131                break;
132            case GPBType::FIXED32:
133            case GPBType::UINT32:
134                GPBUtil::checkUint32($value);
135                break;
136            case GPBType::SFIXED64:
137            case GPBType::SINT64:
138            case GPBType::INT64:
139                GPBUtil::checkInt64($value);
140                break;
141            case GPBType::FIXED64:
142            case GPBType::UINT64:
143                GPBUtil::checkUint64($value);
144                break;
145            case GPBType::FLOAT:
146                GPBUtil::checkFloat($value);
147                break;
148            case GPBType::DOUBLE:
149                GPBUtil::checkDouble($value);
150                break;
151            case GPBType::BOOL:
152                GPBUtil::checkBool($value);
153                break;
154            case GPBType::BYTES:
155                GPBUtil::checkString($value, false);
156                break;
157            case GPBType::STRING:
158                GPBUtil::checkString($value, true);
159                break;
160            case GPBType::MESSAGE:
161                if (is_null($value)) {
162                    throw new \TypeError("RepeatedField element cannot be null.");
163                }
164                GPBUtil::checkMessage($value, $this->klass);
165                break;
166            default:
167                break;
168        }
169        if (is_null($offset)) {
170            $this->container[] = $value;
171        } else {
172            $count = count($this->container);
173            if (!is_numeric($offset) || $offset < 0 || $offset >= $count) {
174                trigger_error(
175                    "Cannot modify element at the given index",
176                    E_USER_ERROR);
177                return;
178            }
179            $this->container[$offset] = $value;
180        }
181    }
182
183    /**
184     * Remove the element at the given index.
185     *
186     * This will also be called for: unset($arr)
187     *
188     * @param integer $offset The index of the element to be removed.
189     * @return void
190     * @throws \ErrorException Invalid type for index.
191     * @throws \ErrorException The element to be removed is not at the end of the
192     * RepeatedField.
193     * @todo need to add return type void (require update php version to 7.1)
194     */
195    #[\ReturnTypeWillChange]
196    public function offsetUnset($offset)
197    {
198        $count = count($this->container);
199        if (!is_numeric($offset) || $count === 0 || $offset < 0 || $offset >= $count) {
200            trigger_error(
201                "Cannot remove element at the given index",
202                E_USER_ERROR);
203            return;
204        }
205        array_splice($this->container, $offset, 1);
206    }
207
208    /**
209     * Check the existence of the element at the given index.
210     *
211     * This will also be called for: isset($arr)
212     *
213     * @param integer $offset The index of the element to be removed.
214     * @return bool True if the element at the given offset exists.
215     * @throws \ErrorException Invalid type for index.
216     */
217    public function offsetExists($offset): bool
218    {
219        return isset($this->container[$offset]);
220    }
221
222    /**
223     * @ignore
224     */
225    public function getIterator(): Traversable
226    {
227        return new RepeatedFieldIter($this->container);
228    }
229
230    /**
231     * Return the number of stored elements.
232     *
233     * This will also be called for: count($arr)
234     *
235     * @return integer The number of stored elements.
236     */
237    public function count(): int
238    {
239        return count($this->container);
240    }
241
242    public function __debugInfo()
243    {
244        return array_map(
245            function ($item) {
246                if ($item instanceof Message || $item instanceof RepeatedField) {
247                    return $item->__debugInfo();
248                }
249                return $item;
250            },
251            iterator_to_array($this)
252        );
253    }
254}
255