blob: 357f16d699f7588de63b95069ceb48201ee6c0b5 [file] [log] [blame]
<?php
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* 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(teboring): 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(teboring): 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(teboring): 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(teboring): 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;
}
}