|
@@ -0,0 +1,2017 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+// Protocol Buffers - Google's data interchange format
|
|
|
+// Copyright 2008 Google Inc. All rights reserved.
|
|
|
+//
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file or at
|
|
|
+// https://developers.google.com/open-source/licenses/bsd
|
|
|
+
|
|
|
+/**
|
|
|
+ * Defines Message, the parent class extended by all protocol message classes.
|
|
|
+ */
|
|
|
+
|
|
|
+namespace Google\Protobuf\Internal;
|
|
|
+
|
|
|
+use Google\Protobuf\Internal\CodedInputStream;
|
|
|
+use Google\Protobuf\Internal\CodedOutputStream;
|
|
|
+use Google\Protobuf\Internal\DescriptorPool;
|
|
|
+use Google\Protobuf\Internal\GPBLabel;
|
|
|
+use Google\Protobuf\Internal\GPBType;
|
|
|
+use Google\Protobuf\Internal\GPBWire;
|
|
|
+use Google\Protobuf\Internal\MapEntry;
|
|
|
+use Google\Protobuf\Internal\RepeatedField;
|
|
|
+use Google\Protobuf\ListValue;
|
|
|
+use Google\Protobuf\Value;
|
|
|
+use Google\Protobuf\Struct;
|
|
|
+use Google\Protobuf\NullValue;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Parent class of all proto messages. Users should not instantiate this class
|
|
|
+ * or extend this class or its child classes by their own. See the comment of
|
|
|
+ * specific functions for more details.
|
|
|
+ */
|
|
|
+#[\AllowDynamicProperties]
|
|
|
+class Message
|
|
|
+{
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private $desc;
|
|
|
+ private $unknown = "";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function __construct($data = NULL)
|
|
|
+ {
|
|
|
+ // MapEntry message is shared by all types of map fields, whose
|
|
|
+ // descriptors are different from each other. Thus, we cannot find a
|
|
|
+ // specific descriptor from the descriptor pool.
|
|
|
+ if ($this instanceof MapEntry) {
|
|
|
+ $this->initWithDescriptor($data);
|
|
|
+ } else {
|
|
|
+ $this->initWithGeneratedPool();
|
|
|
+ if (is_array($data)) {
|
|
|
+ $this->mergeFromArray($data);
|
|
|
+ } else if (!empty($data)) {
|
|
|
+ throw new \InvalidArgumentException(
|
|
|
+ 'Message constructor must be an array or null.'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function initWithGeneratedPool()
|
|
|
+ {
|
|
|
+ $pool = DescriptorPool::getGeneratedPool();
|
|
|
+ $this->desc = $pool->getDescriptorByClassName(get_class($this));
|
|
|
+ if (is_null($this->desc)) {
|
|
|
+ throw new \InvalidArgumentException(
|
|
|
+ get_class($this) ." is not found in descriptor pool. " .
|
|
|
+ 'Only generated classes may derive from Message.');
|
|
|
+ }
|
|
|
+ foreach ($this->desc->getField() as $field) {
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $message_type = $field->getMessageType();
|
|
|
+ $key_field = $message_type->getFieldByNumber(1);
|
|
|
+ $value_field = $message_type->getFieldByNumber(2);
|
|
|
+ switch ($value_field->getType()) {
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ case GPBType::GROUP:
|
|
|
+ $map_field = new MapField(
|
|
|
+ $key_field->getType(),
|
|
|
+ $value_field->getType(),
|
|
|
+ $value_field->getMessageType()->getClass());
|
|
|
+ $this->$setter($map_field);
|
|
|
+ break;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ $map_field = new MapField(
|
|
|
+ $key_field->getType(),
|
|
|
+ $value_field->getType(),
|
|
|
+ $value_field->getEnumType()->getClass());
|
|
|
+ $this->$setter($map_field);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ $map_field = new MapField(
|
|
|
+ $key_field->getType(),
|
|
|
+ $value_field->getType());
|
|
|
+ $this->$setter($map_field);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else if ($field->getLabel() === GPBLabel::REPEATED) {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ case GPBType::GROUP:
|
|
|
+ $repeated_field = new RepeatedField(
|
|
|
+ $field->getType(),
|
|
|
+ $field->getMessageType()->getClass());
|
|
|
+ $this->$setter($repeated_field);
|
|
|
+ break;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ $repeated_field = new RepeatedField(
|
|
|
+ $field->getType(),
|
|
|
+ $field->getEnumType()->getClass());
|
|
|
+ $this->$setter($repeated_field);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ $repeated_field = new RepeatedField($field->getType());
|
|
|
+ $this->$setter($repeated_field);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else if ($field->getOneofIndex() !== -1) {
|
|
|
+ $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
|
|
|
+ $oneof_name = $oneof->getName();
|
|
|
+ $this->$oneof_name = new OneofField($oneof);
|
|
|
+ } else if ($field->getLabel() === GPBLabel::OPTIONAL &&
|
|
|
+ PHP_INT_SIZE == 4) {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::UINT64:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ $this->$setter("0");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function initWithDescriptor(Descriptor $desc)
|
|
|
+ {
|
|
|
+ $this->desc = $desc;
|
|
|
+ foreach ($desc->getField() as $field) {
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ $defaultValue = $this->defaultValue($field);
|
|
|
+ $this->$setter($defaultValue);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function readWrapperValue($member)
|
|
|
+ {
|
|
|
+ $field = $this->desc->getFieldByName($member);
|
|
|
+ $oneof_index = $field->getOneofIndex();
|
|
|
+ if ($oneof_index === -1) {
|
|
|
+ $wrapper = $this->$member;
|
|
|
+ } else {
|
|
|
+ $wrapper = $this->readOneof($field->getNumber());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_null($wrapper)) {
|
|
|
+ return NULL;
|
|
|
+ } else {
|
|
|
+ return $wrapper->getValue();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function writeWrapperValue($member, $value)
|
|
|
+ {
|
|
|
+ $field = $this->desc->getFieldByName($member);
|
|
|
+ $wrapped_value = $value;
|
|
|
+ if (!is_null($value)) {
|
|
|
+ $desc = $field->getMessageType();
|
|
|
+ $klass = $desc->getClass();
|
|
|
+ $wrapped_value = new $klass;
|
|
|
+ $wrapped_value->setValue($value);
|
|
|
+ }
|
|
|
+
|
|
|
+ $oneof_index = $field->getOneofIndex();
|
|
|
+ if ($oneof_index === -1) {
|
|
|
+ $this->$member = $wrapped_value;
|
|
|
+ } else {
|
|
|
+ $this->writeOneof($field->getNumber(), $wrapped_value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function readOneof($number)
|
|
|
+ {
|
|
|
+ $field = $this->desc->getFieldByNumber($number);
|
|
|
+ $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
|
|
|
+ $oneof_name = $oneof->getName();
|
|
|
+ $oneof_field = $this->$oneof_name;
|
|
|
+ if ($number === $oneof_field->getNumber()) {
|
|
|
+ return $oneof_field->getValue();
|
|
|
+ } else {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function hasOneof($number)
|
|
|
+ {
|
|
|
+ $field = $this->desc->getFieldByNumber($number);
|
|
|
+ $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
|
|
|
+ $oneof_name = $oneof->getName();
|
|
|
+ $oneof_field = $this->$oneof_name;
|
|
|
+ return $number === $oneof_field->getNumber();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function writeOneof($number, $value)
|
|
|
+ {
|
|
|
+ $field = $this->desc->getFieldByNumber($number);
|
|
|
+ $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
|
|
|
+ $oneof_name = $oneof->getName();
|
|
|
+ if ($value === null) {
|
|
|
+ $this->$oneof_name = new OneofField($oneof);
|
|
|
+ } else {
|
|
|
+ $oneof_field = $this->$oneof_name;
|
|
|
+ $oneof_field->setValue($value);
|
|
|
+ $oneof_field->setFieldName($field->getName());
|
|
|
+ $oneof_field->setNumber($number);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function whichOneof($oneof_name)
|
|
|
+ {
|
|
|
+ $oneof_field = $this->$oneof_name;
|
|
|
+ $number = $oneof_field->getNumber();
|
|
|
+ if ($number == 0) {
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ $field = $this->desc->getFieldByNumber($number);
|
|
|
+ return $field->getName();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function defaultValue($field)
|
|
|
+ {
|
|
|
+ $value = null;
|
|
|
+
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::DOUBLE:
|
|
|
+ case GPBType::FLOAT:
|
|
|
+ return 0.0;
|
|
|
+ case GPBType::UINT32:
|
|
|
+ case GPBType::INT32:
|
|
|
+ case GPBType::FIXED32:
|
|
|
+ case GPBType::SFIXED32:
|
|
|
+ case GPBType::SINT32:
|
|
|
+ case GPBType::ENUM:
|
|
|
+ return 0;
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::UINT64:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ if (PHP_INT_SIZE === 4) {
|
|
|
+ return '0';
|
|
|
+ } else {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ case GPBType::BOOL:
|
|
|
+ return false;
|
|
|
+ case GPBType::STRING:
|
|
|
+ case GPBType::BYTES:
|
|
|
+ return "";
|
|
|
+ case GPBType::GROUP:
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ return null;
|
|
|
+ default:
|
|
|
+ user_error("Unsupported type.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function skipField($input, $tag)
|
|
|
+ {
|
|
|
+ $number = GPBWire::getTagFieldNumber($tag);
|
|
|
+ if ($number === 0) {
|
|
|
+ throw new GPBDecodeException("Illegal field number zero.");
|
|
|
+ }
|
|
|
+
|
|
|
+ $start = $input->current();
|
|
|
+ switch (GPBWire::getTagWireType($tag)) {
|
|
|
+ case GPBWireType::VARINT:
|
|
|
+ $uint64 = 0;
|
|
|
+ if (!$input->readVarint64($uint64)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside varint.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBWireType::FIXED64:
|
|
|
+ $uint64 = 0;
|
|
|
+ if (!$input->readLittleEndian64($uint64)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside fixed64.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBWireType::FIXED32:
|
|
|
+ $uint32 = 0;
|
|
|
+ if (!$input->readLittleEndian32($uint32)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside fixed32.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBWireType::LENGTH_DELIMITED:
|
|
|
+ $length = 0;
|
|
|
+ if (!$input->readVarint32($length)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside length.");
|
|
|
+ }
|
|
|
+ $data = NULL;
|
|
|
+ if (!$input->readRaw($length, $data)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside length delimited data.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBWireType::START_GROUP:
|
|
|
+ case GPBWireType::END_GROUP:
|
|
|
+ throw new GPBDecodeException("Unexpected wire type.");
|
|
|
+ default:
|
|
|
+ throw new GPBDecodeException("Unexpected wire type.");
|
|
|
+ }
|
|
|
+ $end = $input->current();
|
|
|
+
|
|
|
+ $bytes = str_repeat(chr(0), CodedOutputStream::MAX_VARINT64_BYTES);
|
|
|
+ $size = CodedOutputStream::writeVarintToArray($tag, $bytes, true);
|
|
|
+ $this->unknown .= substr($bytes, 0, $size) . $input->substr($start, $end);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private static function parseFieldFromStreamNoTag($input, $field, &$value)
|
|
|
+ {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::DOUBLE:
|
|
|
+ if (!GPBWire::readDouble($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside double field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::FLOAT:
|
|
|
+ if (!GPBWire::readFloat($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside float field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::INT64:
|
|
|
+ if (!GPBWire::readInt64($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside int64 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::UINT64:
|
|
|
+ if (!GPBWire::readUint64($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside uint64 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::INT32:
|
|
|
+ if (!GPBWire::readInt32($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside int32 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ if (!GPBWire::readFixed64($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside fixed64 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::FIXED32:
|
|
|
+ if (!GPBWire::readFixed32($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside fixed32 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::BOOL:
|
|
|
+ if (!GPBWire::readBool($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside bool field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::STRING:
|
|
|
+ // TODO: Add utf-8 check.
|
|
|
+ if (!GPBWire::readString($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside string field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::GROUP:
|
|
|
+ trigger_error("Not implemented.", E_USER_ERROR);
|
|
|
+ break;
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $value = new MapEntry($field->getMessageType());
|
|
|
+ } else {
|
|
|
+ $klass = $field->getMessageType()->getClass();
|
|
|
+ $value = new $klass;
|
|
|
+ }
|
|
|
+ if (!GPBWire::readMessage($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside message.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::BYTES:
|
|
|
+ if (!GPBWire::readString($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside bytes field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::UINT32:
|
|
|
+ if (!GPBWire::readUint32($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside uint32 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ // TODO: Check unknown enum value.
|
|
|
+ if (!GPBWire::readInt32($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside enum field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::SFIXED32:
|
|
|
+ if (!GPBWire::readSfixed32($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside sfixed32 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ if (!GPBWire::readSfixed64($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside sfixed64 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::SINT32:
|
|
|
+ if (!GPBWire::readSint32($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside sint32 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::SINT64:
|
|
|
+ if (!GPBWire::readSint64($input, $value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside sint64 field.");
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ user_error("Unsupported type.");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function parseFieldFromStream($tag, $input, $field)
|
|
|
+ {
|
|
|
+ $value = null;
|
|
|
+
|
|
|
+ if (is_null($field)) {
|
|
|
+ $value_format = GPBWire::UNKNOWN;
|
|
|
+ } elseif (GPBWire::getTagWireType($tag) ===
|
|
|
+ GPBWire::getWireType($field->getType())) {
|
|
|
+ $value_format = GPBWire::NORMAL_FORMAT;
|
|
|
+ } elseif ($field->isPackable() &&
|
|
|
+ GPBWire::getTagWireType($tag) ===
|
|
|
+ GPBWire::WIRETYPE_LENGTH_DELIMITED) {
|
|
|
+ $value_format = GPBWire::PACKED_FORMAT;
|
|
|
+ } else {
|
|
|
+ // the wire type doesn't match. Put it in our unknown field set.
|
|
|
+ $value_format = GPBWire::UNKNOWN;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($value_format === GPBWire::UNKNOWN) {
|
|
|
+ $this->skipField($input, $tag);
|
|
|
+ return;
|
|
|
+ } elseif ($value_format === GPBWire::NORMAL_FORMAT) {
|
|
|
+ self::parseFieldFromStreamNoTag($input, $field, $value);
|
|
|
+ } elseif ($value_format === GPBWire::PACKED_FORMAT) {
|
|
|
+ $length = 0;
|
|
|
+ if (!GPBWire::readInt32($input, $length)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Unexpected EOF inside packed length.");
|
|
|
+ }
|
|
|
+ $limit = $input->pushLimit($length);
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ while ($input->bytesUntilLimit() > 0) {
|
|
|
+ self::parseFieldFromStreamNoTag($input, $field, $value);
|
|
|
+ $this->appendHelper($field, $value);
|
|
|
+ }
|
|
|
+ $input->popLimit($limit);
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $this->kvUpdateHelper($field, $value->getKey(), $value->getValue());
|
|
|
+ } else if ($field->isRepeated()) {
|
|
|
+ $this->appendHelper($field, $value);
|
|
|
+ } else {
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ $this->$setter($value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clear all containing fields.
|
|
|
+ * @return null
|
|
|
+ */
|
|
|
+ public function clear()
|
|
|
+ {
|
|
|
+ $this->unknown = "";
|
|
|
+ foreach ($this->desc->getField() as $field) {
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $message_type = $field->getMessageType();
|
|
|
+ $key_field = $message_type->getFieldByNumber(1);
|
|
|
+ $value_field = $message_type->getFieldByNumber(2);
|
|
|
+ switch ($value_field->getType()) {
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ case GPBType::GROUP:
|
|
|
+ $map_field = new MapField(
|
|
|
+ $key_field->getType(),
|
|
|
+ $value_field->getType(),
|
|
|
+ $value_field->getMessageType()->getClass());
|
|
|
+ $this->$setter($map_field);
|
|
|
+ break;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ $map_field = new MapField(
|
|
|
+ $key_field->getType(),
|
|
|
+ $value_field->getType(),
|
|
|
+ $value_field->getEnumType()->getClass());
|
|
|
+ $this->$setter($map_field);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ $map_field = new MapField(
|
|
|
+ $key_field->getType(),
|
|
|
+ $value_field->getType());
|
|
|
+ $this->$setter($map_field);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else if ($field->getLabel() === GPBLabel::REPEATED) {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ case GPBType::GROUP:
|
|
|
+ $repeated_field = new RepeatedField(
|
|
|
+ $field->getType(),
|
|
|
+ $field->getMessageType()->getClass());
|
|
|
+ $this->$setter($repeated_field);
|
|
|
+ break;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ $repeated_field = new RepeatedField(
|
|
|
+ $field->getType(),
|
|
|
+ $field->getEnumType()->getClass());
|
|
|
+ $this->$setter($repeated_field);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ $repeated_field = new RepeatedField($field->getType());
|
|
|
+ $this->$setter($repeated_field);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } else if ($field->getOneofIndex() !== -1) {
|
|
|
+ $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
|
|
|
+ $oneof_name = $oneof->getName();
|
|
|
+ $this->$oneof_name = new OneofField($oneof);
|
|
|
+ } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::DOUBLE :
|
|
|
+ case GPBType::FLOAT :
|
|
|
+ $this->$setter(0.0);
|
|
|
+ break;
|
|
|
+ case GPBType::INT32 :
|
|
|
+ case GPBType::FIXED32 :
|
|
|
+ case GPBType::UINT32 :
|
|
|
+ case GPBType::SFIXED32 :
|
|
|
+ case GPBType::SINT32 :
|
|
|
+ case GPBType::ENUM :
|
|
|
+ $this->$setter(0);
|
|
|
+ break;
|
|
|
+ case GPBType::BOOL :
|
|
|
+ $this->$setter(false);
|
|
|
+ break;
|
|
|
+ case GPBType::STRING :
|
|
|
+ case GPBType::BYTES :
|
|
|
+ $this->$setter("");
|
|
|
+ break;
|
|
|
+ case GPBType::GROUP :
|
|
|
+ case GPBType::MESSAGE :
|
|
|
+ $null = null;
|
|
|
+ $this->$setter($null);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (PHP_INT_SIZE == 4) {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::UINT64:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ $this->$setter("0");
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::UINT64:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ $this->$setter(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clear all unknown fields previously parsed.
|
|
|
+ * @return null
|
|
|
+ */
|
|
|
+ public function discardUnknownFields()
|
|
|
+ {
|
|
|
+ $this->unknown = "";
|
|
|
+ foreach ($this->desc->getField() as $field) {
|
|
|
+ if ($field->getType() != GPBType::MESSAGE) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $value_field = $field->getMessageType()->getFieldByNumber(2);
|
|
|
+ if ($value_field->getType() != GPBType::MESSAGE) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $map = $this->$getter();
|
|
|
+ foreach ($map as $key => $value) {
|
|
|
+ $value->discardUnknownFields();
|
|
|
+ }
|
|
|
+ } else if ($field->getLabel() === GPBLabel::REPEATED) {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $arr = $this->$getter();
|
|
|
+ foreach ($arr as $sub) {
|
|
|
+ $sub->discardUnknownFields();
|
|
|
+ }
|
|
|
+ } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $sub = $this->$getter();
|
|
|
+ if (!is_null($sub)) {
|
|
|
+ $sub->discardUnknownFields();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Merges the contents of the specified message into current message.
|
|
|
+ *
|
|
|
+ * This method merges the contents of the specified message into the
|
|
|
+ * current message. Singular fields that are set in the specified message
|
|
|
+ * overwrite the corresponding fields in the current message. Repeated
|
|
|
+ * fields are appended. Map fields key-value pairs are overwritten.
|
|
|
+ * Singular/Oneof sub-messages are recursively merged. All overwritten
|
|
|
+ * sub-messages are deep-copied.
|
|
|
+ *
|
|
|
+ * @param object $msg Protobuf message to be merged from.
|
|
|
+ * @return null
|
|
|
+ */
|
|
|
+ public function mergeFrom($msg)
|
|
|
+ {
|
|
|
+ if (get_class($this) !== get_class($msg)) {
|
|
|
+ user_error("Cannot merge messages with different class.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($this->desc->getField() as $field) {
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ if ($field->isMap()) {
|
|
|
+ if (count($msg->$getter()) != 0) {
|
|
|
+ $value_field = $field->getMessageType()->getFieldByNumber(2);
|
|
|
+ foreach ($msg->$getter() as $key => $value) {
|
|
|
+ if ($value_field->getType() == GPBType::MESSAGE) {
|
|
|
+ $klass = $value_field->getMessageType()->getClass();
|
|
|
+ $copy = new $klass;
|
|
|
+ $copy->mergeFrom($value);
|
|
|
+
|
|
|
+ $this->kvUpdateHelper($field, $key, $copy);
|
|
|
+ } else {
|
|
|
+ $this->kvUpdateHelper($field, $key, $value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if ($field->getLabel() === GPBLabel::REPEATED) {
|
|
|
+ if (count($msg->$getter()) != 0) {
|
|
|
+ foreach ($msg->$getter() as $tmp) {
|
|
|
+ if ($field->getType() == GPBType::MESSAGE) {
|
|
|
+ $klass = $field->getMessageType()->getClass();
|
|
|
+ $copy = new $klass;
|
|
|
+ $copy->mergeFrom($tmp);
|
|
|
+ $this->appendHelper($field, $copy);
|
|
|
+ } else {
|
|
|
+ $this->appendHelper($field, $tmp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if ($field->getLabel() === GPBLabel::OPTIONAL) {
|
|
|
+ if($msg->$getter() !== $this->defaultValue($field)) {
|
|
|
+ $tmp = $msg->$getter();
|
|
|
+ if ($field->getType() == GPBType::MESSAGE) {
|
|
|
+ if (is_null($this->$getter())) {
|
|
|
+ $klass = $field->getMessageType()->getClass();
|
|
|
+ $new_msg = new $klass;
|
|
|
+ $this->$setter($new_msg);
|
|
|
+ }
|
|
|
+ $this->$getter()->mergeFrom($tmp);
|
|
|
+ } else {
|
|
|
+ $this->$setter($tmp);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parses a protocol buffer contained in a string.
|
|
|
+ *
|
|
|
+ * This function takes a string in the (non-human-readable) binary wire
|
|
|
+ * format, matching the encoding output by serializeToString().
|
|
|
+ * See mergeFrom() for merging behavior, if the field is already set in the
|
|
|
+ * specified message.
|
|
|
+ *
|
|
|
+ * @param string $data Binary protobuf data.
|
|
|
+ * @return null
|
|
|
+ * @throws \Exception Invalid data.
|
|
|
+ */
|
|
|
+ public function mergeFromString($data)
|
|
|
+ {
|
|
|
+ $input = new CodedInputStream($data);
|
|
|
+ $this->parseFromStream($input);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Parses a json string to protobuf message.
|
|
|
+ *
|
|
|
+ * This function takes a string in the json wire format, matching the
|
|
|
+ * encoding output by serializeToJsonString().
|
|
|
+ * See mergeFrom() for merging behavior, if the field is already set in the
|
|
|
+ * specified message.
|
|
|
+ *
|
|
|
+ * @param string $data Json protobuf data.
|
|
|
+ * @param bool $ignore_unknown
|
|
|
+ * @return null
|
|
|
+ * @throws \Exception Invalid data.
|
|
|
+ */
|
|
|
+ public function mergeFromJsonString($data, $ignore_unknown = false)
|
|
|
+ {
|
|
|
+ $input = new RawInputStream($data);
|
|
|
+ $this->parseFromJsonStream($input, $ignore_unknown);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function parseFromStream($input)
|
|
|
+ {
|
|
|
+ while (true) {
|
|
|
+ $tag = $input->readTag();
|
|
|
+ // End of input. This is a valid place to end, so return true.
|
|
|
+ if ($tag === 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ $number = GPBWire::getTagFieldNumber($tag);
|
|
|
+ $field = $this->desc->getFieldByNumber($number);
|
|
|
+
|
|
|
+ $this->parseFieldFromStream($tag, $input, $field);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private function convertJsonValueToProtoValue(
|
|
|
+ $value,
|
|
|
+ $field,
|
|
|
+ $ignore_unknown,
|
|
|
+ $is_map_key = false)
|
|
|
+ {
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ $klass = $field->getMessageType()->getClass();
|
|
|
+ $submsg = new $klass;
|
|
|
+
|
|
|
+ if (is_a($submsg, "Google\Protobuf\Duration")) {
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ } else if (!is_string($value)) {
|
|
|
+ throw new GPBDecodeException("Expect string.");
|
|
|
+ }
|
|
|
+ return GPBUtil::parseDuration($value);
|
|
|
+ } else if ($field->isTimestamp()) {
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ } else if (!is_string($value)) {
|
|
|
+ throw new GPBDecodeException("Expect string.");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $timestamp = GPBUtil::parseTimestamp($value);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid RFC 3339 timestamp: ".$e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ $submsg->setSeconds($timestamp->getSeconds());
|
|
|
+ $submsg->setNanos($timestamp->getNanos());
|
|
|
+ } else if (is_a($submsg, "Google\Protobuf\FieldMask")) {
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return GPBUtil::parseFieldMask($value);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid FieldMask: ".$e->getMessage());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (is_null($value) &&
|
|
|
+ !is_a($submsg, "Google\Protobuf\Value")) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (GPBUtil::hasSpecialJsonMapping($submsg)) {
|
|
|
+ } elseif (!is_object($value) && !is_array($value)) {
|
|
|
+ throw new GPBDecodeException("Expect message.");
|
|
|
+ }
|
|
|
+ $submsg->mergeFromJsonArray($value, $ignore_unknown);
|
|
|
+ }
|
|
|
+ return $submsg;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (is_integer($value)) {
|
|
|
+ return $value;
|
|
|
+ }
|
|
|
+ $enum_value = $field->getEnumType()->getValueByName($value);
|
|
|
+ if (!is_null($enum_value)) {
|
|
|
+ return $enum_value->getNumber();
|
|
|
+ } else if ($ignore_unknown) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ } else {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Enum field only accepts integer or enum value name");
|
|
|
+ }
|
|
|
+ case GPBType::STRING:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (is_numeric($value)) {
|
|
|
+ return strval($value);
|
|
|
+ }
|
|
|
+ if (!is_string($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "String field only accepts string value");
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ case GPBType::BYTES:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (!is_string($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Byte field only accepts string value");
|
|
|
+ }
|
|
|
+ $proto_value = base64_decode($value, true);
|
|
|
+ if ($proto_value === false) {
|
|
|
+ throw new GPBDecodeException("Invalid base64 characters");
|
|
|
+ }
|
|
|
+ return $proto_value;
|
|
|
+ case GPBType::BOOL:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if ($is_map_key) {
|
|
|
+ if ($value === "true") {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ if ($value === "false") {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Bool field only accepts bool value");
|
|
|
+ }
|
|
|
+ if (!is_bool($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Bool field only accepts bool value");
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ case GPBType::FLOAT:
|
|
|
+ case GPBType::DOUBLE:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if ($value === "Infinity") {
|
|
|
+ return INF;
|
|
|
+ }
|
|
|
+ if ($value === "-Infinity") {
|
|
|
+ return -INF;
|
|
|
+ }
|
|
|
+ if ($value === "NaN") {
|
|
|
+ return NAN;
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ case GPBType::INT32:
|
|
|
+ case GPBType::SINT32:
|
|
|
+ case GPBType::SFIXED32:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (!is_numeric($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int32 field");
|
|
|
+ }
|
|
|
+ if (is_string($value) && trim($value) !== $value) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int32 field");
|
|
|
+ }
|
|
|
+ if (bccomp($value, "2147483647") > 0) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Int32 too large");
|
|
|
+ }
|
|
|
+ if (bccomp($value, "-2147483648") < 0) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Int32 too small");
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ case GPBType::UINT32:
|
|
|
+ case GPBType::FIXED32:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (!is_numeric($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for uint32 field");
|
|
|
+ }
|
|
|
+ if (is_string($value) && trim($value) !== $value) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int32 field");
|
|
|
+ }
|
|
|
+ if (bccomp($value, 4294967295) > 0) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Uint32 too large");
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (!is_numeric($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int64 field");
|
|
|
+ }
|
|
|
+ if (is_string($value) && trim($value) !== $value) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int64 field");
|
|
|
+ }
|
|
|
+ if (bccomp($value, "9223372036854775807") > 0) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Int64 too large");
|
|
|
+ }
|
|
|
+ if (bccomp($value, "-9223372036854775808") < 0) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Int64 too small");
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ case GPBType::UINT64:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ if (is_null($value)) {
|
|
|
+ return $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ if (!is_numeric($value)) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int64 field");
|
|
|
+ }
|
|
|
+ if (is_string($value) && trim($value) !== $value) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Invalid data type for int64 field");
|
|
|
+ }
|
|
|
+ if (bccomp($value, "18446744073709551615") > 0) {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Uint64 too large");
|
|
|
+ }
|
|
|
+ if (bccomp($value, "9223372036854775807") > 0) {
|
|
|
+ $value = bcsub($value, "18446744073709551616");
|
|
|
+ }
|
|
|
+ return $value;
|
|
|
+ default:
|
|
|
+ return $value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Populates the message from a user-supplied PHP array. Array keys
|
|
|
+ * correspond to Message properties and nested message properties.
|
|
|
+ *
|
|
|
+ * Example:
|
|
|
+ * ```
|
|
|
+ * $message->mergeFromArray([
|
|
|
+ * 'name' => 'This is a message name',
|
|
|
+ * 'interval' => [
|
|
|
+ * 'startTime' => time() - 60,
|
|
|
+ * 'endTime' => time(),
|
|
|
+ * ]
|
|
|
+ * ]);
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * This method will trigger an error if it is passed data that cannot
|
|
|
+ * be converted to the correct type. For example, a StringValue field
|
|
|
+ * must receive data that is either a string or a StringValue object.
|
|
|
+ *
|
|
|
+ * @param array $array An array containing message properties and values.
|
|
|
+ * @return null
|
|
|
+ */
|
|
|
+ protected function mergeFromArray(array $array)
|
|
|
+ {
|
|
|
+ // Just call the setters for the field names
|
|
|
+ foreach ($array as $key => $value) {
|
|
|
+ $field = $this->desc->getFieldByName($key);
|
|
|
+ if (is_null($field)) {
|
|
|
+ throw new \UnexpectedValueException(
|
|
|
+ 'Invalid message property: ' . $key);
|
|
|
+ }
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $valueField = $field->getMessageType()->getFieldByName('value');
|
|
|
+ if (!is_null($valueField) && $valueField->isWrapperType()) {
|
|
|
+ self::normalizeArrayElementsToMessageType($value, $valueField->getMessageType()->getClass());
|
|
|
+ }
|
|
|
+ } elseif ($field->isWrapperType()) {
|
|
|
+ $class = $field->getMessageType()->getClass();
|
|
|
+ if ($field->isRepeated()) {
|
|
|
+ self::normalizeArrayElementsToMessageType($value, $class);
|
|
|
+ } else {
|
|
|
+ self::normalizeToMessageType($value, $class);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $this->$setter($value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tries to normalize the elements in $value into a provided protobuf
|
|
|
+ * wrapper type $class. If $value is any type other than array, we do
|
|
|
+ * not do any conversion, and instead rely on the existing protobuf
|
|
|
+ * type checking. If $value is an array, we process each element and
|
|
|
+ * try to convert it to an instance of $class.
|
|
|
+ *
|
|
|
+ * @param mixed $value The array of values to normalize.
|
|
|
+ * @param string $class The expected wrapper class name
|
|
|
+ */
|
|
|
+ private static function normalizeArrayElementsToMessageType(&$value, $class)
|
|
|
+ {
|
|
|
+ if (!is_array($value)) {
|
|
|
+ // In the case that $value is not an array, we do not want to
|
|
|
+ // attempt any conversion. Note that this includes the cases
|
|
|
+ // when $value is a RepeatedField of MapField. In those cases,
|
|
|
+ // we do not need to convert the elements, as they should
|
|
|
+ // already be the correct types.
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ // Normalize each element in the array.
|
|
|
+ foreach ($value as $key => &$elementValue) {
|
|
|
+ self::normalizeToMessageType($elementValue, $class);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tries to normalize $value into a provided protobuf wrapper type $class.
|
|
|
+ * If $value is any type other than an object, we attempt to construct an
|
|
|
+ * instance of $class and assign $value to it using the setValue method
|
|
|
+ * shared by all wrapper types.
|
|
|
+ *
|
|
|
+ * This method will raise an error if it receives a type that cannot be
|
|
|
+ * assigned to the wrapper type via setValue.
|
|
|
+ *
|
|
|
+ * @param mixed $value The value to normalize.
|
|
|
+ * @param string $class The expected wrapper class name
|
|
|
+ */
|
|
|
+ private static function normalizeToMessageType(&$value, $class)
|
|
|
+ {
|
|
|
+ if (is_null($value) || is_object($value)) {
|
|
|
+ // This handles the case that $value is an instance of $class. We
|
|
|
+ // choose not to do any more strict checking here, relying on the
|
|
|
+ // existing type checking done by GPBUtil.
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ // Try to instantiate $class and set the value
|
|
|
+ try {
|
|
|
+ $msg = new $class;
|
|
|
+ $msg->setValue($value);
|
|
|
+ $value = $msg;
|
|
|
+ return;
|
|
|
+ } catch (\Exception $exception) {
|
|
|
+ trigger_error(
|
|
|
+ "Error normalizing value to type '$class': " . $exception->getMessage(),
|
|
|
+ E_USER_ERROR
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected function mergeFromJsonArray($array, $ignore_unknown)
|
|
|
+ {
|
|
|
+ if (is_a($this, "Google\Protobuf\Any")) {
|
|
|
+ $this->clear();
|
|
|
+ $this->setTypeUrl($array["@type"]);
|
|
|
+ $msg = $this->unpack();
|
|
|
+ if (GPBUtil::hasSpecialJsonMapping($msg)) {
|
|
|
+ $msg->mergeFromJsonArray($array["value"], $ignore_unknown);
|
|
|
+ } else {
|
|
|
+ unset($array["@type"]);
|
|
|
+ $msg->mergeFromJsonArray($array, $ignore_unknown);
|
|
|
+ }
|
|
|
+ $this->setValue($msg->serializeToString());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\DoubleValue") ||
|
|
|
+ is_a($this, "Google\Protobuf\FloatValue") ||
|
|
|
+ is_a($this, "Google\Protobuf\Int64Value") ||
|
|
|
+ is_a($this, "Google\Protobuf\UInt64Value") ||
|
|
|
+ is_a($this, "Google\Protobuf\Int32Value") ||
|
|
|
+ is_a($this, "Google\Protobuf\UInt32Value") ||
|
|
|
+ is_a($this, "Google\Protobuf\BoolValue") ||
|
|
|
+ is_a($this, "Google\Protobuf\StringValue")) {
|
|
|
+ $this->setValue($array);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\BytesValue")) {
|
|
|
+ $this->setValue(base64_decode($array));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\Duration")) {
|
|
|
+ $this->mergeFrom(GPBUtil::parseDuration($array));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\FieldMask")) {
|
|
|
+ $this->mergeFrom(GPBUtil::parseFieldMask($array));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\Timestamp")) {
|
|
|
+ $this->mergeFrom(GPBUtil::parseTimestamp($array));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\Struct")) {
|
|
|
+ $fields = $this->getFields();
|
|
|
+ foreach($array as $key => $value) {
|
|
|
+ $v = new Value();
|
|
|
+ $v->mergeFromJsonArray($value, $ignore_unknown);
|
|
|
+ $fields[$key] = $v;
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (is_a($this, "Google\Protobuf\Value")) {
|
|
|
+ if (is_bool($array)) {
|
|
|
+ $this->setBoolValue($array);
|
|
|
+ } elseif (is_string($array)) {
|
|
|
+ $this->setStringValue($array);
|
|
|
+ } elseif (is_null($array)) {
|
|
|
+ $this->setNullValue(0);
|
|
|
+ } elseif (is_double($array) || is_integer($array)) {
|
|
|
+ $this->setNumberValue($array);
|
|
|
+ } elseif (is_array($array)) {
|
|
|
+ if (array_values($array) !== $array) {
|
|
|
+ // Associative array
|
|
|
+ $struct_value = $this->getStructValue();
|
|
|
+ if (is_null($struct_value)) {
|
|
|
+ $struct_value = new Struct();
|
|
|
+ $this->setStructValue($struct_value);
|
|
|
+ }
|
|
|
+ foreach ($array as $key => $v) {
|
|
|
+ $value = new Value();
|
|
|
+ $value->mergeFromJsonArray($v, $ignore_unknown);
|
|
|
+ $values = $struct_value->getFields();
|
|
|
+ $values[$key]= $value;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Array
|
|
|
+ $list_value = $this->getListValue();
|
|
|
+ if (is_null($list_value)) {
|
|
|
+ $list_value = new ListValue();
|
|
|
+ $this->setListValue($list_value);
|
|
|
+ }
|
|
|
+ foreach ($array as $v) {
|
|
|
+ $value = new Value();
|
|
|
+ $value->mergeFromJsonArray($v, $ignore_unknown);
|
|
|
+ $values = $list_value->getValues();
|
|
|
+ $values[]= $value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new GPBDecodeException("Invalid type for Value.");
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $this->mergeFromArrayJsonImpl($array, $ignore_unknown);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function mergeFromArrayJsonImpl($array, $ignore_unknown)
|
|
|
+ {
|
|
|
+ foreach ($array as $key => $value) {
|
|
|
+ $field = $this->desc->getFieldByJsonName($key);
|
|
|
+ if (is_null($field)) {
|
|
|
+ $field = $this->desc->getFieldByName($key);
|
|
|
+ if (is_null($field)) {
|
|
|
+ if ($ignore_unknown) {
|
|
|
+ continue;
|
|
|
+ } else {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ $key . ' is unknown.'
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ($field->isMap()) {
|
|
|
+ if (is_null($value)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $key_field = $field->getMessageType()->getFieldByNumber(1);
|
|
|
+ $value_field = $field->getMessageType()->getFieldByNumber(2);
|
|
|
+ foreach ($value as $tmp_key => $tmp_value) {
|
|
|
+ if (is_null($tmp_value)) {
|
|
|
+ throw new \Exception(
|
|
|
+ "Map value field element cannot be null.");
|
|
|
+ }
|
|
|
+ $proto_key = $this->convertJsonValueToProtoValue(
|
|
|
+ $tmp_key,
|
|
|
+ $key_field,
|
|
|
+ $ignore_unknown,
|
|
|
+ true);
|
|
|
+ $proto_value = $this->convertJsonValueToProtoValue(
|
|
|
+ $tmp_value,
|
|
|
+ $value_field,
|
|
|
+ $ignore_unknown);
|
|
|
+ self::kvUpdateHelper($field, $proto_key, $proto_value);
|
|
|
+ }
|
|
|
+ } else if ($field->isRepeated()) {
|
|
|
+ if (is_null($value)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ foreach ($value as $tmp) {
|
|
|
+ if (is_null($tmp)) {
|
|
|
+ throw new \Exception(
|
|
|
+ "Repeated field elements cannot be null.");
|
|
|
+ }
|
|
|
+ $proto_value = $this->convertJsonValueToProtoValue(
|
|
|
+ $tmp,
|
|
|
+ $field,
|
|
|
+ $ignore_unknown);
|
|
|
+ self::appendHelper($field, $proto_value);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $setter = $field->getSetter();
|
|
|
+ $proto_value = $this->convertJsonValueToProtoValue(
|
|
|
+ $value,
|
|
|
+ $field,
|
|
|
+ $ignore_unknown);
|
|
|
+ if ($field->getType() === GPBType::MESSAGE) {
|
|
|
+ if (is_null($proto_value)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $submsg = $this->$getter();
|
|
|
+ if (!is_null($submsg)) {
|
|
|
+ $submsg->mergeFrom($proto_value);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $this->$setter($proto_value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function parseFromJsonStream($input, $ignore_unknown)
|
|
|
+ {
|
|
|
+ $array = json_decode($input->getData(), true, 512, JSON_BIGINT_AS_STRING);
|
|
|
+ if ($this instanceof \Google\Protobuf\ListValue) {
|
|
|
+ $array = ["values"=>$array];
|
|
|
+ }
|
|
|
+ if (is_null($array)) {
|
|
|
+ if ($this instanceof \Google\Protobuf\Value) {
|
|
|
+ $this->setNullValue(\Google\Protobuf\NullValue::NULL_VALUE);
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ throw new GPBDecodeException(
|
|
|
+ "Cannot decode json string: " . $input->getData());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $this->mergeFromJsonArray($array, $ignore_unknown);
|
|
|
+ } catch (\Exception $e) {
|
|
|
+ throw new GPBDecodeException($e->getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function serializeSingularFieldToStream($field, &$output)
|
|
|
+ {
|
|
|
+ if (!$this->existField($field)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $value = $this->$getter();
|
|
|
+ if (!GPBWire::serializeFieldToStream($value, $field, true, $output)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function serializeRepeatedFieldToStream($field, &$output)
|
|
|
+ {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count === 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ $packed = $field->getPacked();
|
|
|
+ if ($packed) {
|
|
|
+ if (!GPBWire::writeTag(
|
|
|
+ $output,
|
|
|
+ GPBWire::makeTag($field->getNumber(), GPBType::STRING))) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $size = 0;
|
|
|
+ foreach ($values as $value) {
|
|
|
+ $size += $this->fieldDataOnlyByteSize($field, $value);
|
|
|
+ }
|
|
|
+ if (!$output->writeVarint32($size, true)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($values as $value) {
|
|
|
+ if (!GPBWire::serializeFieldToStream(
|
|
|
+ $value,
|
|
|
+ $field,
|
|
|
+ !$packed,
|
|
|
+ $output)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function serializeMapFieldToStream($field, $output)
|
|
|
+ {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count === 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach ($values as $key => $value) {
|
|
|
+ $map_entry = new MapEntry($field->getMessageType());
|
|
|
+ $map_entry->setKey($key);
|
|
|
+ $map_entry->setValue($value);
|
|
|
+ if (!GPBWire::serializeFieldToStream(
|
|
|
+ $map_entry,
|
|
|
+ $field,
|
|
|
+ true,
|
|
|
+ $output)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function serializeFieldToStream(&$output, $field)
|
|
|
+ {
|
|
|
+ if ($field->isMap()) {
|
|
|
+ return $this->serializeMapFieldToStream($field, $output);
|
|
|
+ } elseif ($field->isRepeated()) {
|
|
|
+ return $this->serializeRepeatedFieldToStream($field, $output);
|
|
|
+ } else {
|
|
|
+ return $this->serializeSingularFieldToStream($field, $output);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function serializeFieldToJsonStream(&$output, $field)
|
|
|
+ {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ return GPBJsonWire::serializeFieldToStream(
|
|
|
+ $values, $field, $output, !GPBUtil::hasSpecialJsonMapping($this));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function serializeToStream(&$output)
|
|
|
+ {
|
|
|
+ $fields = $this->desc->getField();
|
|
|
+ foreach ($fields as $field) {
|
|
|
+ if (!$this->serializeFieldToStream($output, $field)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $output->writeRaw($this->unknown, strlen($this->unknown));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function serializeToJsonStream(&$output)
|
|
|
+ {
|
|
|
+ if (is_a($this, 'Google\Protobuf\Any')) {
|
|
|
+ $output->writeRaw("{", 1);
|
|
|
+ $type_field = $this->desc->getFieldByNumber(1);
|
|
|
+ $value_msg = $this->unpack();
|
|
|
+
|
|
|
+ // Serialize type url.
|
|
|
+ $output->writeRaw("\"@type\":", 8);
|
|
|
+ $output->writeRaw("\"", 1);
|
|
|
+ $output->writeRaw($this->getTypeUrl(), strlen($this->getTypeUrl()));
|
|
|
+ $output->writeRaw("\"", 1);
|
|
|
+
|
|
|
+ // Serialize value
|
|
|
+ if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
|
|
|
+ $output->writeRaw(",\"value\":", 9);
|
|
|
+ $value_msg->serializeToJsonStream($output);
|
|
|
+ } else {
|
|
|
+ $value_fields = $value_msg->desc->getField();
|
|
|
+ foreach ($value_fields as $field) {
|
|
|
+ if ($value_msg->existField($field)) {
|
|
|
+ $output->writeRaw(",", 1);
|
|
|
+ if (!$value_msg->serializeFieldToJsonStream($output, $field)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $output->writeRaw("}", 1);
|
|
|
+ } elseif (is_a($this, 'Google\Protobuf\FieldMask')) {
|
|
|
+ $field_mask = GPBUtil::formatFieldMask($this);
|
|
|
+ $output->writeRaw("\"", 1);
|
|
|
+ $output->writeRaw($field_mask, strlen($field_mask));
|
|
|
+ $output->writeRaw("\"", 1);
|
|
|
+ } elseif (is_a($this, 'Google\Protobuf\Duration')) {
|
|
|
+ $duration = GPBUtil::formatDuration($this) . "s";
|
|
|
+ $output->writeRaw("\"", 1);
|
|
|
+ $output->writeRaw($duration, strlen($duration));
|
|
|
+ $output->writeRaw("\"", 1);
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
|
|
|
+ $timestamp = GPBUtil::formatTimestamp($this);
|
|
|
+ $timestamp = json_encode($timestamp);
|
|
|
+ $output->writeRaw($timestamp, strlen($timestamp));
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\ListValue') {
|
|
|
+ $field = $this->desc->getField()[1];
|
|
|
+ if (!$this->existField($field)) {
|
|
|
+ $output->writeRaw("[]", 2);
|
|
|
+ } else {
|
|
|
+ if (!$this->serializeFieldToJsonStream($output, $field)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\Struct') {
|
|
|
+ $field = $this->desc->getField()[1];
|
|
|
+ if (!$this->existField($field)) {
|
|
|
+ $output->writeRaw("{}", 2);
|
|
|
+ } else {
|
|
|
+ if (!$this->serializeFieldToJsonStream($output, $field)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
|
|
|
+ $output->writeRaw("{", 1);
|
|
|
+ }
|
|
|
+ $fields = $this->desc->getField();
|
|
|
+ $first = true;
|
|
|
+ foreach ($fields as $field) {
|
|
|
+ if ($this->existField($field) ||
|
|
|
+ GPBUtil::hasJsonValue($this)) {
|
|
|
+ if ($first) {
|
|
|
+ $first = false;
|
|
|
+ } else {
|
|
|
+ $output->writeRaw(",", 1);
|
|
|
+ }
|
|
|
+ if (!$this->serializeFieldToJsonStream($output, $field)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
|
|
|
+ $output->writeRaw("}", 1);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Serialize the message to string.
|
|
|
+ * @return string Serialized binary protobuf data.
|
|
|
+ */
|
|
|
+ public function serializeToString()
|
|
|
+ {
|
|
|
+ $output = new CodedOutputStream($this->byteSize());
|
|
|
+ $this->serializeToStream($output);
|
|
|
+ return $output->getData();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Serialize the message to json string.
|
|
|
+ * @return string Serialized json protobuf data.
|
|
|
+ */
|
|
|
+ public function serializeToJsonString()
|
|
|
+ {
|
|
|
+ $output = new CodedOutputStream($this->jsonByteSize());
|
|
|
+ $this->serializeToJsonStream($output);
|
|
|
+ return $output->getData();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function existField($field)
|
|
|
+ {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $hazzer = "has" . substr($getter, 3);
|
|
|
+
|
|
|
+ if (method_exists($this, $hazzer)) {
|
|
|
+ return $this->$hazzer();
|
|
|
+ } else if ($field->getOneofIndex() !== -1) {
|
|
|
+ // For old generated code, which does not have hazzers for oneof
|
|
|
+ // fields.
|
|
|
+ $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()];
|
|
|
+ $oneof_name = $oneof->getName();
|
|
|
+ return $this->$oneof_name->getNumber() === $field->getNumber();
|
|
|
+ }
|
|
|
+
|
|
|
+ $values = $this->$getter();
|
|
|
+ if ($field->isMap()) {
|
|
|
+ return count($values) !== 0;
|
|
|
+ } elseif ($field->isRepeated()) {
|
|
|
+ return count($values) !== 0;
|
|
|
+ } else {
|
|
|
+ return $values !== $this->defaultValue($field);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function repeatedFieldDataOnlyByteSize($field)
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count !== 0) {
|
|
|
+ $size += $count * GPBWire::tagSize($field);
|
|
|
+ foreach ($values as $value) {
|
|
|
+ $size += $this->singularFieldDataOnlyByteSize($field);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function fieldDataOnlyByteSize($field, $value)
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::BOOL:
|
|
|
+ $size += 1;
|
|
|
+ break;
|
|
|
+ case GPBType::FLOAT:
|
|
|
+ case GPBType::FIXED32:
|
|
|
+ case GPBType::SFIXED32:
|
|
|
+ $size += 4;
|
|
|
+ break;
|
|
|
+ case GPBType::DOUBLE:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ $size += 8;
|
|
|
+ break;
|
|
|
+ case GPBType::INT32:
|
|
|
+ case GPBType::ENUM:
|
|
|
+ $size += GPBWire::varint32Size($value, true);
|
|
|
+ break;
|
|
|
+ case GPBType::UINT32:
|
|
|
+ $size += GPBWire::varint32Size($value);
|
|
|
+ break;
|
|
|
+ case GPBType::UINT64:
|
|
|
+ case GPBType::INT64:
|
|
|
+ $size += GPBWire::varint64Size($value);
|
|
|
+ break;
|
|
|
+ case GPBType::SINT32:
|
|
|
+ $size += GPBWire::sint32Size($value);
|
|
|
+ break;
|
|
|
+ case GPBType::SINT64:
|
|
|
+ $size += GPBWire::sint64Size($value);
|
|
|
+ break;
|
|
|
+ case GPBType::STRING:
|
|
|
+ case GPBType::BYTES:
|
|
|
+ $size += strlen($value);
|
|
|
+ $size += GPBWire::varint32Size($size);
|
|
|
+ break;
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ $size += $value->byteSize();
|
|
|
+ $size += GPBWire::varint32Size($size);
|
|
|
+ break;
|
|
|
+ case GPBType::GROUP:
|
|
|
+ // TODO: Add support.
|
|
|
+ user_error("Unsupported type.");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ user_error("Unsupported type.");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function fieldDataOnlyJsonByteSize($field, $value)
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+
|
|
|
+ switch ($field->getType()) {
|
|
|
+ case GPBType::SFIXED32:
|
|
|
+ case GPBType::SINT32:
|
|
|
+ case GPBType::INT32:
|
|
|
+ $size += strlen(strval($value));
|
|
|
+ break;
|
|
|
+ case GPBType::FIXED32:
|
|
|
+ case GPBType::UINT32:
|
|
|
+ if ($value < 0) {
|
|
|
+ $value = bcadd($value, "4294967296");
|
|
|
+ }
|
|
|
+ $size += strlen(strval($value));
|
|
|
+ break;
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::UINT64:
|
|
|
+ if ($value < 0) {
|
|
|
+ $value = bcadd($value, "18446744073709551616");
|
|
|
+ }
|
|
|
+ // Intentional fall through.
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ $size += 2; // size for ""
|
|
|
+ $size += strlen(strval($value));
|
|
|
+ break;
|
|
|
+ case GPBType::FLOAT:
|
|
|
+ if (is_nan($value)) {
|
|
|
+ $size += strlen("NaN") + 2;
|
|
|
+ } elseif ($value === INF) {
|
|
|
+ $size += strlen("Infinity") + 2;
|
|
|
+ } elseif ($value === -INF) {
|
|
|
+ $size += strlen("-Infinity") + 2;
|
|
|
+ } else {
|
|
|
+ $size += strlen(sprintf("%.8g", $value));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::DOUBLE:
|
|
|
+ if (is_nan($value)) {
|
|
|
+ $size += strlen("NaN") + 2;
|
|
|
+ } elseif ($value === INF) {
|
|
|
+ $size += strlen("Infinity") + 2;
|
|
|
+ } elseif ($value === -INF) {
|
|
|
+ $size += strlen("-Infinity") + 2;
|
|
|
+ } else {
|
|
|
+ $size += strlen(sprintf("%.17g", $value));
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::ENUM:
|
|
|
+ $enum_desc = $field->getEnumType();
|
|
|
+ if ($enum_desc->getClass() === "Google\Protobuf\NullValue") {
|
|
|
+ $size += 4;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ $enum_value_desc = $enum_desc->getValueByNumber($value);
|
|
|
+ if (!is_null($enum_value_desc)) {
|
|
|
+ $size += 2; // size for ""
|
|
|
+ $size += strlen($enum_value_desc->getName());
|
|
|
+ } else {
|
|
|
+ $str_value = strval($value);
|
|
|
+ $size += strlen($str_value);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::BOOL:
|
|
|
+ if ($value) {
|
|
|
+ $size += 4;
|
|
|
+ } else {
|
|
|
+ $size += 5;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case GPBType::STRING:
|
|
|
+ $value = json_encode($value, JSON_UNESCAPED_UNICODE);
|
|
|
+ $size += strlen($value);
|
|
|
+ break;
|
|
|
+ case GPBType::BYTES:
|
|
|
+ # if (is_a($this, "Google\Protobuf\BytesValue")) {
|
|
|
+ # $size += strlen(json_encode($value));
|
|
|
+ # } else {
|
|
|
+ # $size += strlen(base64_encode($value));
|
|
|
+ # $size += 2; // size for \"\"
|
|
|
+ # }
|
|
|
+ $size += strlen(base64_encode($value));
|
|
|
+ $size += 2; // size for \"\"
|
|
|
+ break;
|
|
|
+ case GPBType::MESSAGE:
|
|
|
+ $size += $value->jsonByteSize();
|
|
|
+ break;
|
|
|
+# case GPBType::GROUP:
|
|
|
+# // TODO: Add support.
|
|
|
+# user_error("Unsupported type.");
|
|
|
+# break;
|
|
|
+ default:
|
|
|
+ user_error("Unsupported type " . $field->getType());
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function fieldByteSize($field)
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count !== 0) {
|
|
|
+ $size += $count * GPBWire::tagSize($field);
|
|
|
+ $message_type = $field->getMessageType();
|
|
|
+ $key_field = $message_type->getFieldByNumber(1);
|
|
|
+ $value_field = $message_type->getFieldByNumber(2);
|
|
|
+ foreach ($values as $key => $value) {
|
|
|
+ $data_size = 0;
|
|
|
+ if ($key != $this->defaultValue($key_field)) {
|
|
|
+ $data_size += $this->fieldDataOnlyByteSize(
|
|
|
+ $key_field,
|
|
|
+ $key);
|
|
|
+ $data_size += GPBWire::tagSize($key_field);
|
|
|
+ }
|
|
|
+ if ($value != $this->defaultValue($value_field)) {
|
|
|
+ $data_size += $this->fieldDataOnlyByteSize(
|
|
|
+ $value_field,
|
|
|
+ $value);
|
|
|
+ $data_size += GPBWire::tagSize($value_field);
|
|
|
+ }
|
|
|
+ $size += GPBWire::varint32Size($data_size) + $data_size;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif ($field->isRepeated()) {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count !== 0) {
|
|
|
+ if ($field->getPacked()) {
|
|
|
+ $data_size = 0;
|
|
|
+ foreach ($values as $value) {
|
|
|
+ $data_size += $this->fieldDataOnlyByteSize($field, $value);
|
|
|
+ }
|
|
|
+ $size += GPBWire::tagSize($field);
|
|
|
+ $size += GPBWire::varint32Size($data_size);
|
|
|
+ $size += $data_size;
|
|
|
+ } else {
|
|
|
+ $size += $count * GPBWire::tagSize($field);
|
|
|
+ foreach ($values as $value) {
|
|
|
+ $size += $this->fieldDataOnlyByteSize($field, $value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif ($this->existField($field)) {
|
|
|
+ $size += GPBWire::tagSize($field);
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $value = $this->$getter();
|
|
|
+ $size += $this->fieldDataOnlyByteSize($field, $value);
|
|
|
+ }
|
|
|
+ return $size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ private function fieldJsonByteSize($field)
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+
|
|
|
+ if ($field->isMap()) {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count !== 0) {
|
|
|
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
|
|
|
+ $size += 3; // size for "\"\":".
|
|
|
+ $size += strlen($field->getJsonName()); // size for field name
|
|
|
+ }
|
|
|
+ $size += 2; // size for "{}".
|
|
|
+ $size += $count - 1; // size for commas
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $map_entry = $field->getMessageType();
|
|
|
+ $key_field = $map_entry->getFieldByNumber(1);
|
|
|
+ $value_field = $map_entry->getFieldByNumber(2);
|
|
|
+ switch ($key_field->getType()) {
|
|
|
+ case GPBType::STRING:
|
|
|
+ case GPBType::SFIXED64:
|
|
|
+ case GPBType::INT64:
|
|
|
+ case GPBType::SINT64:
|
|
|
+ case GPBType::FIXED64:
|
|
|
+ case GPBType::UINT64:
|
|
|
+ $additional_quote = false;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ $additional_quote = true;
|
|
|
+ }
|
|
|
+ foreach ($values as $key => $value) {
|
|
|
+ if ($additional_quote) {
|
|
|
+ $size += 2; // size for ""
|
|
|
+ }
|
|
|
+ $size += $this->fieldDataOnlyJsonByteSize($key_field, $key);
|
|
|
+ $size += $this->fieldDataOnlyJsonByteSize($value_field, $value);
|
|
|
+ $size += 1; // size for :
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif ($field->isRepeated()) {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $values = $this->$getter();
|
|
|
+ $count = count($values);
|
|
|
+ if ($count !== 0) {
|
|
|
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
|
|
|
+ $size += 3; // size for "\"\":".
|
|
|
+ $size += strlen($field->getJsonName()); // size for field name
|
|
|
+ }
|
|
|
+ $size += 2; // size for "[]".
|
|
|
+ $size += $count - 1; // size for commas
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ foreach ($values as $value) {
|
|
|
+ $size += $this->fieldDataOnlyJsonByteSize($field, $value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif ($this->existField($field) || GPBUtil::hasJsonValue($this)) {
|
|
|
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
|
|
|
+ $size += 3; // size for "\"\":".
|
|
|
+ $size += strlen($field->getJsonName()); // size for field name
|
|
|
+ }
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $value = $this->$getter();
|
|
|
+ $size += $this->fieldDataOnlyJsonByteSize($field, $value);
|
|
|
+ }
|
|
|
+ return $size;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function byteSize()
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+
|
|
|
+ $fields = $this->desc->getField();
|
|
|
+ foreach ($fields as $field) {
|
|
|
+ $size += $this->fieldByteSize($field);
|
|
|
+ }
|
|
|
+ $size += strlen($this->unknown);
|
|
|
+ return $size;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function appendHelper($field, $append_value)
|
|
|
+ {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $setter = $field->getSetter();
|
|
|
+
|
|
|
+ $field_arr_value = $this->$getter();
|
|
|
+ $field_arr_value[] = $append_value;
|
|
|
+
|
|
|
+ if (!is_object($field_arr_value)) {
|
|
|
+ $this->$setter($field_arr_value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private function kvUpdateHelper($field, $update_key, $update_value)
|
|
|
+ {
|
|
|
+ $getter = $field->getGetter();
|
|
|
+ $setter = $field->getSetter();
|
|
|
+
|
|
|
+ $field_arr_value = $this->$getter();
|
|
|
+ $field_arr_value[$update_key] = $update_value;
|
|
|
+
|
|
|
+ if (!is_object($field_arr_value)) {
|
|
|
+ $this->$setter($field_arr_value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @ignore
|
|
|
+ */
|
|
|
+ public function jsonByteSize()
|
|
|
+ {
|
|
|
+ $size = 0;
|
|
|
+ if (is_a($this, 'Google\Protobuf\Any')) {
|
|
|
+ // Size for "{}".
|
|
|
+ $size += 2;
|
|
|
+
|
|
|
+ // Size for "\"@type\":".
|
|
|
+ $size += 8;
|
|
|
+
|
|
|
+ // Size for url. +2 for "" /.
|
|
|
+ $size += strlen($this->getTypeUrl()) + 2;
|
|
|
+
|
|
|
+ $value_msg = $this->unpack();
|
|
|
+ if (GPBUtil::hasSpecialJsonMapping($value_msg)) {
|
|
|
+ // Size for "\",value\":".
|
|
|
+ $size += 9;
|
|
|
+ $size += $value_msg->jsonByteSize();
|
|
|
+ } else {
|
|
|
+ $value_size = $value_msg->jsonByteSize();
|
|
|
+ // size === 2 it's empty message {} which is not serialized inside any
|
|
|
+ if ($value_size !== 2) {
|
|
|
+ // Size for value. +1 for comma, -2 for "{}".
|
|
|
+ $size += $value_size -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\FieldMask') {
|
|
|
+ $field_mask = GPBUtil::formatFieldMask($this);
|
|
|
+ $size += strlen($field_mask) + 2; // 2 for ""
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\Duration') {
|
|
|
+ $duration = GPBUtil::formatDuration($this) . "s";
|
|
|
+ $size += strlen($duration) + 2; // 2 for ""
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\Timestamp') {
|
|
|
+ $timestamp = GPBUtil::formatTimestamp($this);
|
|
|
+ $timestamp = json_encode($timestamp);
|
|
|
+ $size += strlen($timestamp);
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\ListValue') {
|
|
|
+ $field = $this->desc->getField()[1];
|
|
|
+ if ($this->existField($field)) {
|
|
|
+ $field_size = $this->fieldJsonByteSize($field);
|
|
|
+ $size += $field_size;
|
|
|
+ } else {
|
|
|
+ // Size for "[]".
|
|
|
+ $size += 2;
|
|
|
+ }
|
|
|
+ } elseif (get_class($this) === 'Google\Protobuf\Struct') {
|
|
|
+ $field = $this->desc->getField()[1];
|
|
|
+ if ($this->existField($field)) {
|
|
|
+ $field_size = $this->fieldJsonByteSize($field);
|
|
|
+ $size += $field_size;
|
|
|
+ } else {
|
|
|
+ // Size for "{}".
|
|
|
+ $size += 2;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!GPBUtil::hasSpecialJsonMapping($this)) {
|
|
|
+ // Size for "{}".
|
|
|
+ $size += 2;
|
|
|
+ }
|
|
|
+
|
|
|
+ $fields = $this->desc->getField();
|
|
|
+ $count = 0;
|
|
|
+ foreach ($fields as $field) {
|
|
|
+ $field_size = $this->fieldJsonByteSize($field);
|
|
|
+ $size += $field_size;
|
|
|
+ if ($field_size != 0) {
|
|
|
+ $count++;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // size for comma
|
|
|
+ $size += $count > 0 ? ($count - 1) : 0;
|
|
|
+ }
|
|
|
+ return $size;
|
|
|
+ }
|
|
|
+}
|