Add php protobuffer support for transition to GTFS-realtime
[busui.git] / lib / Protobuf-PHP / library / DrSlump / Protobuf / Codec / PhpArray.php
blob:a/lib/Protobuf-PHP/library/DrSlump/Protobuf/Codec/PhpArray.php -> blob:b/lib/Protobuf-PHP/library/DrSlump/Protobuf/Codec/PhpArray.php
  <?php
   
  namespace DrSlump\Protobuf\Codec;
   
  use DrSlump\Protobuf;
   
  /**
  * This codec serializes and unserializes data from/to PHP associative
  * arrays, allowing it to be used as a base for an arbitrary number
  * of different serializations (json, yaml, ini, xml ...).
  *
  */
  class PhpArray implements Protobuf\CodecInterface
  {
  /** @var bool */
  protected $useTagNumber = false;
   
  /**
  * Tells the codec to expect the array keys to contain the
  * field's tag number instead of the name.
  *
  * @param bool $useIt
  */
  public function useTagNumberAsKey($useIt = true)
  {
  $this->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);
  }
  }
   
   
  }