protected function decodeMessage(Protobuf\Message $message, $data) { // Get message descriptor $descriptor = Protobuf::getRegistry()->getDescriptor($message); // Split the index in UTF8 characters preg_match_all('/./u', $data[0], $chars); $chars = $chars[0]; for ($i = 1; $i < count($data); $i++) { $k = $this->c2i($chars[$i - 1]) - 48; $v = $data[$i]; $field = $descriptor->getField($k); if (NULL === $field) { // Unknown $unknown = new PhpArray\Unknown($k, gettype($v), $v); $message->addUnknown($unknown); continue; } $name = $field->getName(); if ($field->getType() === Protobuf::TYPE_MESSAGE) { $nested = $field->getReference(); if ($field->isRepeated()) { foreach ($v as $kk => $vv) { $v[$kk] = $this->decodeMessage(new $nested(), $vv); } $message->initValue($name, $v); } else { $obj = $this->decodeMessage(new $nested(), $v); $message->initValue($name, $obj); } } else { $message->initValue($name, $v); } } return $message; }
protected function decodeMessage(Protobuf\Message $message, $data) { // Get message descriptor $descriptor = Protobuf::getRegistry()->getDescriptor($message); foreach ($data as $key => $v) { // Get the field by tag number or name $field = $this->useTagNumber ? $descriptor->getField($key) : $descriptor->getFieldByName($key); // Unknown field found if (!$field) { $unknown = new PhpArray\Unknown($key, gettype($v), $v); $message->addUnknown($unknown); continue; } if ($field->isRepeated()) { // Make sure the value is an array of values $v = is_array($v) && is_int(key($v)) ? $v : array($v); foreach ($v as $k => $vv) { $v[$k] = $this->filterValue($vv, $field); } } else { $v = $this->filterValue($v, $field); } $message->_set($field->getNumber(), $v); } return $message; }
protected function decodeMessage(Protobuf\MessageInterface $message, $data) { // Get message descriptor $descriptor = Protobuf::getRegistry()->getDescriptor($message); $isLazy = $this->getOption('lazy'); $useTagNumber = $this->getOption('tags'); foreach ($data as $key => $v) { // Get the field by tag number or name $field = $useTagNumber ? $descriptor->getField($key) : $descriptor->getFieldByName($key); // Unknown field found if (!$field) { $unknown = new PhpArray\Unknown($key, gettype($v), $v); $message->addUnknown($unknown); continue; } if ($field->isRepeated()) { // Make sure the value is an array of values $v = is_array($v) && (empty($v) || is_int(key($v))) ? $v : array($v); // If we are packing lazy values use a LazyRepeat as container if ($isLazy && $field->getType() === Protobuf::TYPE_MESSAGE) { $v = new Protobuf\LazyRepeat($v); $v->codec = $this; $v->descriptor = $field; } else { foreach ($v as $k => $vv) { $v[$k] = $this->filterValue($vv, $field); } } } else { $v = $this->filterValue($v, $field); } $message->initValue($field->getName(), $v); } return $message; }
/** * @param string $data */ public function __construct($data = null) { // Cache the descriptor instance $this->_descriptor = Protobuf::getRegistry()->getDescriptor($this); // Assign default values to extensions foreach ($this->_descriptor->getFields() as $f) { if ($f->isExtension() && $f->hasDefault()) { $this->_extensions[$f->getName()] = $f->getDefault(); } } if (NULL !== $data) { $this->parse($data); } }
protected function encodeMessage(Protobuf\MessageInterface $message, $level = 0) { $descriptor = Protobuf::getRegistry()->getDescriptor($message); $strict = $this->getOption('strict'); $indent = str_repeat(' ', $level); $data = ''; foreach ($descriptor->getFields() as $tag => $field) { $empty = !isset($message[$tag]); if ($strict && $empty && $field->isRequired() && !$field->hasDefault()) { throw new \UnexpectedValueException('Message ' . $descriptor->getName() . '\'s field tag ' . $tag . '(' . $field->getName() . ') is required but has no value'); } if ($empty && !$field->hasDefault()) { continue; } $name = $field->getName(); $value = $message[$tag]; if ($value === NULL) { continue; } if ($field->isRepeated()) { foreach ($value as $val) { // Skip nullified repeated values if (NULL === $val) { continue; } else { if ($field->getType() !== Protobuf::TYPE_MESSAGE) { $data .= $indent . $name . ': ' . json_encode($val) . "\n"; } else { $data .= $indent . $name . " {\n"; $data .= $this->encodeMessage($val, $level + 1); $data .= $indent . "}\n"; } } } } else { if ($field->getType() === Protobuf::TYPE_MESSAGE) { $data .= $indent . $name . " {\n"; $data .= $this->encodeMessage($value, $level + 1); $data .= $indent . "}\n"; } else { $data .= $indent . $name . ': ' . json_encode($value) . "\n"; } } } return $data; }
/** * @param mixed $data */ public function __construct($data = null) { // Cache the descriptor instance for this class if (!static::$__descriptor) { static::$__descriptor = Protobuf::getRegistry()->getDescriptor($this); } // Alias the descriptor to this object instance since it's faster to access $this->_descriptor = static::$__descriptor; // BACKWARDS COMPATIBILITY: Unset public properties $publicfields = get_object_vars($this); foreach ($this->_descriptor->getFields() as $field) { $name = $field->name; if (array_key_exists($name, $publicfields)) { //trigger_error('DESTROYING PUBLIC FIELD: ' . $name); unset($this->{$name}); } } if (NULL !== $data) { $this->parse($data); } }
/** * @param \DrSlump\Protobuf\MessageInterface $message * @param string $data * @return \DrSlump\Protobuf\MessageInterface */ protected function _decodeMessage(\DrSlump\Protobuf\MessageInterface $message, $data) { $descriptor = Protobuf::getRegistry()->getDescriptor($message); $name = $descriptor->getName(); $res = $this->_describe($descriptor); $ret = \protobuf_decode($res, $data); // In non lazy mode we just pass the returned array thru the PhpArray codec if (!$this->getOption('lazy')) { if (!$this->_codec) { $this->_codec = new PhpArray(); $this->_codec->setOption('lazy', false); } return $this->_codec->decode($message, $ret); } // In lazy mode we need to walk thru the fields to convert message strings // to LazyValue / LazyRepeat foreach ($descriptor->getFields() as $field) { $name = $field->getName(); if (!isset($ret[$name])) { continue; } $value = $ret[$name]; if ($field->getType() === Protobuf::TYPE_MESSAGE) { if ($field->getRule() === Protobuf::RULE_REPEATED) { $value = new Protobuf\LazyRepeat($value); $value->codec = $this; $value->descriptor = $field; } else { $lazy = new Protobuf\LazyValue(); $lazy->codec = $this; $lazy->descriptor = $field; $lazy->value = $value; $value = $lazy; } } $message->initValue($name, $value); } return $message; }
protected function encodeMessage(Protobuf\Message $message) { $writer = new NativeWriter(); // Get message descriptor $descriptor = Protobuf::getRegistry()->getDescriptor($message); $strict = $this->getOption('strict'); foreach ($descriptor->getFields() as $tag => $field) { $empty = !isset($message[$tag]); if ($strict && $empty && $field->isRequired() && !$field->hasDefault()) { throw new \UnexpectedValueException('Message ' . get_class($message) . '\'s field tag ' . $tag . '(' . $field->getName() . ') is required but has no value'); } // Skip unknown fields if ($empty && !$field->hasDefault()) { continue; } $type = $field->getType(); $wire = $field->isPacked() ? self::WIRE_LENGTH : $this->getWireType($type, null); // Compute key with tag number and wire type $key = $tag << 3 | $wire; $value = $message[$tag]; if (NULL === $value) { continue; } if ($field->isRepeated()) { // Packed fields are encoded as a length-delimited stream containing // the concatenated encoding of each value. if ($field->isPacked() && !empty($value)) { $subwriter = new NativeWriter(); foreach ($value as $val) { $this->encodeSimpleType($subwriter, $type, $val); } $data = $subwriter->getBytes(); $writer->varint($key); $writer->varint(strlen($data)); $writer->write($data); } else { foreach ($value as $val) { // Skip nullified repeated values if (NULL === $val) { continue; } else { if ($type !== Protobuf::TYPE_MESSAGE) { $writer->varint($key); $this->encodeSimpleType($writer, $type, $val); } else { $writer->varint($key); $data = $this->encodeMessage($val); $writer->varint(strlen($data)); $writer->write($data); } } } } } else { if ($type !== Protobuf::TYPE_MESSAGE) { $writer->varint($key); $this->encodeSimpleType($writer, $type, $value); } else { $writer->varint($key); $data = $this->encodeMessage($value); $writer->varint(strlen($data)); $writer->write($data); } } } return $writer->getBytes(); }
/** * @param \DrSlump\Protobuf\Codec\Binary\Reader $reader * @param \DrSlump\Protobuf\Message $message * @param int $length * @return \DrSlump\Protobuf\Message */ protected function decodeMessage($reader, \DrSlump\Protobuf\Message $message, $length = NULL) { /** @var $message \DrSlump\Protobuf\Message */ /** @var $descriptor \DrSlump\Protobuf\Descriptor */ // Get message descriptor $descriptor = Protobuf::getRegistry()->getDescriptor($message); // Cache locally the message fields $fields = $descriptor->getFields(); // Calculate the maximum offset if we have defined a length $limit = !is_null($length) ? $reader->pos() + $length : NULL; $pos = $reader->pos(); // Keep reading until we reach the end or the limit while ($limit === NULL && !$reader->eof() || $limit !== NULL && $reader->pos() < $limit) { // Get initial varint with tag number and wire type $key = $reader->varint(); if ($reader->eof()) { break; } $wire = $key & 0x7; $tag = $key >> 3; // Find the matching field for the tag number if (!isset($fields[$tag])) { $data = $this->decodeUnknown($reader, $wire); $unknown = new Binary\Unknown($tag, $wire, $data); $message->addUnknown($unknown); continue; } $field = $fields[$tag]; $type = $field->getType(); // Check if we are dealing with a packed stream, we cannot rely on the packed // flag of the message since we cannot be certain if the creator of the message // was using it. if ($wire === self::WIRE_LENGTH && $field->isRepeated() && self::$PACKABLE[$type]) { $len = $reader->varint(); $until = $reader->pos() + $len; $wire = $this->getWireType($type); while ($reader->pos() < $until) { $item = $this->decodeSimpleType($reader, $type, $wire); $message->_add($tag, $item); } } else { // Assert wire and type match $this->assertWireType($wire, $type); // Check if it's a sub-message if ($type === Protobuf::TYPE_MESSAGE) { $submessage = $field->getReference(); $submessage = new $submessage(); $len = $reader->varint(); $value = $this->decodeMessage($reader, $submessage, $len); } else { $value = $this->decodeSimpleType($reader, $type, $wire); } // Support non-packed repeated fields if ($field->isRepeated()) { $message->_add($tag, $value); } else { $message->_set($tag, $value); } } } return $message; }