--- a/lib/Protobuf-PHP/library/DrSlump/Protobuf/Codec/PhpArray.php +++ b/lib/Protobuf-PHP/library/DrSlump/Protobuf/Codec/PhpArray.php @@ -1,1 +1,147 @@ +useTagNumber = $useIt; + } + + /** + * @param \DrSlump\Protobuf\Message $message + * @return array + */ + public function encode(Protobuf\Message $message) + { + return $this->encodeMessage($message); + } + + /** + * @param \DrSlump\Protobuf\Message $message + * @param array $data + * @return \DrSlump\Protobuf\Message + */ + public function decode(Protobuf\Message $message, $data) + { + return $this->decodeMessage($message, $data); + } + + protected function encodeMessage(Protobuf\Message $message) + { + $descriptor = Protobuf::getRegistry()->getDescriptor($message); + + $data = array(); + foreach ($descriptor->getFields() as $tag=>$field) { + + $empty = !$message->_has($tag); + if ($field->isRequired() && $empty) { + throw new \UnexpectedValueException( + 'Message ' . get_class($message) . '\'s field tag ' . $tag . '(' . $field->getName() . ') is required but has no value' + ); + } + + if ($empty) { + continue; + } + + $key = $this->useTagNumber ? $field->getNumber() : $field->getName(); + $v = $message->_get($tag); + + if ($field->isRepeated()) { + // Make sure the value is an array of values + $v = is_array($v) ? $v : array($v); + foreach ($v as $k=>$vv) { + $v[$k] = $this->filterValue($vv, $field); + } + } else { + $v = $this->filterValue($v, $field); + } + + $data[$key] = $v; + } + + return $data; + } + + 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 filterValue($value, Protobuf\Field $field) + { + switch ($field->getType()) { + case Protobuf::TYPE_MESSAGE: + // Tell apart encoding and decoding + if ($value instanceof Protobuf\Message) { + return $this->encodeMessage($value); + } else { + $nested = $field->getReference(); + return $this->decodeMessage(new $nested, $value); + } + case Protobuf::TYPE_BOOL: + return filter_var($value, FILTER_VALIDATE_BOOLEAN); + case Protobuf::TYPE_STRING: + case Protobuf::TYPE_BYTES: + return (string)$value; + case Protobuf::TYPE_FLOAT: + case Protobuf::TYPE_DOUBLE: + return filter_var($value, FILTER_VALIDATE_FLOAT); + // Assume the rest are ints + default: + return filter_var($value, FILTER_VALIDATE_INT); + } + } + + +} +