1<?php
2
3// Protocol Buffers - Google's data interchange format
4// Copyright 2008 Google Inc.  All rights reserved.
5// https://developers.google.com/protocol-buffers/
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11//     * Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//     * Redistributions in binary form must reproduce the above
14// copyright notice, this list of conditions and the following disclaimer
15// in the documentation and/or other materials provided with the
16// distribution.
17//     * Neither the name of Google Inc. nor the names of its
18// contributors may be used to endorse or promote products derived from
19// this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33/**
34 * RepeatedField and RepeatedFieldIter are used by generated protocol message
35 * classes to manipulate repeated fields.
36 */
37
38namespace Google\Protobuf\Internal;
39
40use Google\Protobuf\Internal\GPBType;
41use Google\Protobuf\Internal\GPBUtil;
42
43/**
44 * RepeatedField is used by generated protocol message classes to manipulate
45 * repeated fields. It can be used like native PHP array.
46 */
47class RepeatedField implements \ArrayAccess, \IteratorAggregate, \Countable
48{
49
50    /**
51     * @ignore
52     */
53    private $container;
54    /**
55     * @ignore
56     */
57    private $type;
58    /**
59     * @ignore
60     */
61    private $klass;
62    /**
63     * @ignore
64     */
65    private $legacy_klass;
66
67    /**
68     * Constructs an instance of RepeatedField.
69     *
70     * @param long $type Type of the stored element.
71     * @param string $klass Message/Enum class name (message/enum fields only).
72     * @ignore
73     */
74    public function __construct($type, $klass = null)
75    {
76        $this->container = [];
77        $this->type = $type;
78        if ($this->type == GPBType::MESSAGE) {
79            $pool = DescriptorPool::getGeneratedPool();
80            $desc = $pool->getDescriptorByClassName($klass);
81            if ($desc == NULL) {
82                new $klass;  // No msg class instance has been created before.
83                $desc = $pool->getDescriptorByClassName($klass);
84            }
85            $this->klass = $desc->getClass();
86            $this->legacy_klass = $desc->getLegacyClass();
87        }
88    }
89
90    /**
91     * @ignore
92     */
93    public function getType()
94    {
95        return $this->type;
96    }
97
98    /**
99     * @ignore
100     */
101    public function getClass()
102    {
103        return $this->klass;
104    }
105
106    /**
107     * @ignore
108     */
109    public function getLegacyClass()
110    {
111        return $this->legacy_klass;
112    }
113
114    /**
115     * Return the element at the given index.
116     *
117     * This will also be called for: $ele = $arr[0]
118     *
119     * @param long $offset The index of the element to be fetched.
120     * @return object The stored element at given index.
121     * @throws \ErrorException Invalid type for index.
122     * @throws \ErrorException Non-existing index.
123     */
124    public function offsetGet($offset)
125    {
126        return $this->container[$offset];
127    }
128
129    /**
130     * Assign the element at the given index.
131     *
132     * This will also be called for: $arr []= $ele and $arr[0] = ele
133     *
134     * @param long $offset The index of the element to be assigned.
135     * @param object $value The element to be assigned.
136     * @return void
137     * @throws \ErrorException Invalid type for index.
138     * @throws \ErrorException Non-existing index.
139     * @throws \ErrorException Incorrect type of the element.
140     */
141    public function offsetSet($offset, $value)
142    {
143        switch ($this->type) {
144            case GPBType::SFIXED32:
145            case GPBType::SINT32:
146            case GPBType::INT32:
147            case GPBType::ENUM:
148                GPBUtil::checkInt32($value);
149                break;
150            case GPBType::FIXED32:
151            case GPBType::UINT32:
152                GPBUtil::checkUint32($value);
153                break;
154            case GPBType::SFIXED64:
155            case GPBType::SINT64:
156            case GPBType::INT64:
157                GPBUtil::checkInt64($value);
158                break;
159            case GPBType::FIXED64:
160            case GPBType::UINT64:
161                GPBUtil::checkUint64($value);
162                break;
163            case GPBType::FLOAT:
164                GPBUtil::checkFloat($value);
165                break;
166            case GPBType::DOUBLE:
167                GPBUtil::checkDouble($value);
168                break;
169            case GPBType::BOOL:
170                GPBUtil::checkBool($value);
171                break;
172            case GPBType::BYTES:
173                GPBUtil::checkString($value, false);
174                break;
175            case GPBType::STRING:
176                GPBUtil::checkString($value, true);
177                break;
178            case GPBType::MESSAGE:
179                if (is_null($value)) {
180                  trigger_error("RepeatedField element cannot be null.",
181                                E_USER_ERROR);
182                }
183                GPBUtil::checkMessage($value, $this->klass);
184                break;
185            default:
186                break;
187        }
188        if (is_null($offset)) {
189            $this->container[] = $value;
190        } else {
191            $count = count($this->container);
192            if (!is_numeric($offset) || $offset < 0 || $offset >= $count) {
193                trigger_error(
194                    "Cannot modify element at the given index",
195                    E_USER_ERROR);
196                return;
197            }
198            $this->container[$offset] = $value;
199        }
200    }
201
202    /**
203     * Remove the element at the given index.
204     *
205     * This will also be called for: unset($arr)
206     *
207     * @param long $offset The index of the element to be removed.
208     * @return void
209     * @throws \ErrorException Invalid type for index.
210     * @throws \ErrorException The element to be removed is not at the end of the
211     * RepeatedField.
212     */
213    public function offsetUnset($offset)
214    {
215        $count = count($this->container);
216        if (!is_numeric($offset) || $count === 0 || $offset !== $count - 1) {
217            trigger_error(
218                "Cannot remove element at the given index",
219                E_USER_ERROR);
220            return;
221        }
222        array_pop($this->container);
223    }
224
225    /**
226     * Check the existence of the element at the given index.
227     *
228     * This will also be called for: isset($arr)
229     *
230     * @param long $offset The index of the element to be removed.
231     * @return bool True if the element at the given offset exists.
232     * @throws \ErrorException Invalid type for index.
233     */
234    public function offsetExists($offset)
235    {
236        return isset($this->container[$offset]);
237    }
238
239    /**
240     * @ignore
241     */
242    public function getIterator()
243    {
244        return new RepeatedFieldIter($this->container);
245    }
246
247    /**
248     * Return the number of stored elements.
249     *
250     * This will also be called for: count($arr)
251     *
252     * @return integer The number of stored elements.
253     */
254    public function count()
255    {
256        return count($this->container);
257    }
258}
259