desc = $desc; return; } $pool = DescriptorPool::getGeneratedPool(); $this->desc = $pool->getDescriptorByClassName(get_class($this)); 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"); } } } } 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 writeOneof($number, $value) { $field = $this->desc->getFieldByNumber($number); $oneof = $this->desc->getOneofDecl()[$field->getOneofIndex()]; $oneof_name = $oneof->getName(); $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 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: echo "GROUP\xA"; trigger_error("Not implemented.", E_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; $field_type = $field->getType(); $value_format = GPBWire::UNKNOWN; if (GPBWire::getTagWireType($tag) === GPBWire::getWireType($field_type)) { $value_format = GPBWire::NORMAL_FORMAT; } elseif ($field->isPackable() && GPBWire::getTagWireType($tag) === GPBWire::WIRETYPE_LENGTH_DELIMITED) { $value_format = GPBWire::PACKED_FORMAT; } if ($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->$getter()[] = $value; } $input->popLimit($limit); return; } else { return false; } if ($field->isMap()) { $getter = $field->getGetter(); $this->$getter()[$value->getKey()] = $value->getValue(); } else if ($field->isRepeated()) { $getter = $field->getGetter(); $this->$getter()[] = $value; } else { $setter = $field->getSetter(); $this->$setter($value); } } /** * Clear all containing fields. * @return null. */ public function clear() { 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); } } } } } /** * 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 overritten. * Singular/Oneof sub-messages are recursively merged. All overritten * 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->$getter()[$key] = $copy; } else { $this->$getter()[$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->$getter()[] = $copy; } else { $this->$getter()[] = $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 InputStream($data); $this->parseFromStream($input); } /** * @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); // Check whether we retrieved a known field if ($field === NULL) { continue; } $this->parseFieldFromStream($tag, $input, $field); } } /** * @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)) { 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 */ public function serializeToStream(&$output) { $fields = $this->desc->getField(); foreach ($fields as $field) { if (!$this->serializeFieldToStream($output, $field)) { return false; } } return true; } /** * Serialize the message to string. * @return string Serialized binary protobuf data. */ public function serializeToString() { $output = new OutputStream($this->byteSize()); $this->serializeToStream($output); return $output->getData(); } /** * @ignore */ private function existField($field) { $oneof_index = $field->getOneofIndex(); if ($oneof_index !== -1) { $oneof = $this->desc->getOneofDecl()[$oneof_index]; $oneof_name = $oneof->getName(); return $this->$oneof_name->getNumber() === $field->getNumber(); } $getter = $field->getGetter(); $value = $this->$getter(); return $value !== $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 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; $data_size += $this->fieldDataOnlyByteSize($key_field, $key); $data_size += $this->fieldDataOnlyByteSize( $value_field, $value); $data_size += GPBWire::tagSize($key_field); $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 */ public function byteSize() { $size = 0; $fields = $this->desc->getField(); foreach ($fields as $field) { $size += $this->fieldByteSize($field); } return $size; } }