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