protected function decodeMessage(Protobuf\MessageInterface $message, $data) { // Get message descriptor $descriptor = Protobuf\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\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; }
protected function decodeMessage(Protobuf\Message $message, $data) { // Get message descriptor $descriptor = Protobuf\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\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 encodeMessage(Protobuf\MessageInterface $message, $level = 0) { $descriptor = Protobuf\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'); } // skip not set values if ($empty) { continue; } $value = $message[$tag]; // don't send nulls or defaults over the wire if (NULL === $value || $field->hasDefault() && $field->getDefault() === $value) { continue; } $name = $field->getName(); if ($field->isRepeated()) { foreach ($value as $val) { // Skip nullified repeated values if (NULL === $val) { continue; } else { if ($field->getType() !== Protobuf\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\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 \DrSlump\Protobuf\MessageInterface $message * @param string $data * @return \DrSlump\Protobuf\MessageInterface */ protected function _decodeMessage(\DrSlump\Protobuf\MessageInterface $message, $data) { $descriptor = Protobuf\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\Protobuf::TYPE_MESSAGE) { if ($field->getRule() === Protobuf\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\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 not set values if ($empty) { continue; } $value = $message[$tag]; // don't send nulls or defaults over the wire if (NULL === $value || $field->hasDefault() && $field->getDefault() === $value) { 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; 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(mb_strlen($data, '8bit')); $writer->write($data); } else { foreach ($value as $val) { // Skip nullified repeated values if (NULL === $val) { continue; } else { if ($type !== Protobuf\Protobuf::TYPE_MESSAGE) { $writer->varint($key); $this->encodeSimpleType($writer, $type, $val); } else { $writer->varint($key); $data = $this->encodeMessage($val); $writer->varint(mb_strlen($data, '8bit')); $writer->write($data); } } } } } else { if ($type !== Protobuf\Protobuf::TYPE_MESSAGE) { $writer->varint($key); $this->encodeSimpleType($writer, $type, $value); } else { $writer->varint($key); $data = $this->encodeMessage($value); $writer->varint(mb_strlen($data, '8bit')); $writer->write($data); } } } return $writer->getBytes(); }