Add php protobuffer support for transition to GTFS-realtime
[busui.git] / lib / Protobuf-PHP / library / DrSlump / Protobuf / Compiler.php
blob:a/lib/Protobuf-PHP/library/DrSlump/Protobuf/Compiler.php -> blob:b/lib/Protobuf-PHP/library/DrSlump/Protobuf/Compiler.php
  <?php
   
  namespace DrSlump\Protobuf;
   
  // Load descriptor messages
  require_once __DIR__ . '/Compiler/protos/descriptor.pb.php';
  require_once __DIR__ . '/Compiler/protos/plugin.pb.php';
  require_once __DIR__ . '/Compiler/protos/php.pb.php';
  require_once __DIR__ . '/Compiler/protos/json.pb.php';
   
  use DrSlump\Protobuf;
  use google\protobuf as proto;
   
  class Compiler
  {
  /** @var bool */
  protected $verbose = false;
  /** @var array */
  protected $packages = array();
  /** @var \DrSlump\Protobuf\Compiler\CommentsParser */
  protected $comments;
  /** @var bool */
  protected $skipImported = false;
  /** @var array */
  protected $options = array();
  /** @var array */
  protected $protos = array();
   
  public function __construct($verbose = false)
  {
  $this->verbose = $verbose;
  $this->comments = new Compiler\CommentsParser();
  }
   
  public function stderr($str)
  {
  $str = str_replace("\n", PHP_EOL, $str);
  fputs(STDERR, $str . PHP_EOL);
  }
   
  public function notice($str)
  {
  if ($this->verbose) {
  $this->stderr('NOTICE: ' . $str);
  }
  }
   
  public function warning($str)
  {
  $this->stderr('WARNING: ' . $str);
  }
   
  protected function error($str)
  {
  $this->stderr('ERROR: ' . $str);
  }
   
  public function getPackages()
  {
  return $this->packages;
  }
   
  public function hasPackage($package)
  {
  return isset($this->packages[$package]);
  }
   
  public function getPackage($package)
  {
  return $this->packages[$package];
  }
   
  public function setPackage($package, $namespace)
  {
  $this->packages[$package] = $namespace;
  }
   
  public function getOption($option, $type = 'string')
  {
  $value = isset($this->options[$option])
  ? $this->options[$option]
  : null;
   
  switch ($type) {
  case 'bool':
  return filter_var($value, FILTER_VALIDATE_BOOLEAN);
  default:
  return $value;
  }
  }
   
  public function camelize($name)
  {
  return preg_replace_callback(
  '/_([a-z])/i',
  function($m){ return strtoupper($m[1]); },
  $name
  );
  }
   
  public function compile($data)
  {
  // Parse the request
  $req = new \google\protobuf\compiler\CodeGeneratorRequest($data);
   
  // Set default generator class
  $generator = __CLASS__ . '\PhpGenerator';
   
  // Reset comments parser
  $this->comments->reset();
  $parseComments = false;
   
  // Get plugin arguments
  if ($req->hasParameter()) {
  parse_str($req->getParameter(), $args);
  foreach ($args as $arg=>$val) {
  switch($arg){
  case 'verbose':
  $this->verbose = filter_var($val, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
  break;
  case 'json':
  $this->notice("Using ProtoJson generator");
  $generator = __CLASS__ . '\JsonGenerator';
  break;
  case 'comments':
  $parseComments = filter_var($val, FILTER_VALIDATE_BOOLEAN);
  break;
  case 'protos':
  $this->protos = $val;
  break;
  case 'skip-imported':
  $this->skipImported = filter_var($val, FILTER_VALIDATE_BOOLEAN);
  break;
  case 'options':
  $this->options = $val;
  break;
  default:
  $this->warning('Skipping unknown option ' . $arg);
  }
  }
  }
   
  // Parse comments if we're told to do so
  if ($parseComments) {
  if (empty($this->protos)) {
  throw new \RuntimeException('Unable to port comments if .proto files are not passed as argument');
  }
  foreach ($this->protos as $fname) {
  $src = file_get_contents($fname);
  if (FALSE === $src) {
  throw new \RuntimeException('Unable to parse file ' . $fname . ' for comments');
  }
  $this->comments->parse($src);
  }
  }
   
  /** @var $generator \DrSlump\Protobuf\Compiler\AbstractGenerator */
  $generator = new $generator($this);
   
  // Setup response object
  $resp = new \google\protobuf\compiler\CodeGeneratorResponse();
   
  // First iterate over all the protos to get a map of namespaces
  $this->packages = array();
  foreach($req->getProtoFileList() as $proto) {
  $package = $proto->getPackage();
  $namespace = $generator->getNamespace($proto);
  if (isset($this->packages[$package]) && $namespace !== $this->packages[$package]) {
  $this->warning("Package $package was already mapped to {$this->packages[$package]} but has now been overridden to $namespace");
  }
  $this->packages[$package] = $namespace;
  $this->notice("Mapping $package to $namespace");
  }
   
  // Get the list of files to generate
  $files = $req->getFileToGenerate();
   
  // Run each file
  foreach ($req->getProtoFileList() as $file) {
  // Only compile those given to generate, not the imported ones
  if ($this->skipImported && !in_array($file->getName(), $files)) {
  $this->notice('Skipping generation of imported file "' . $file->getName() . '"');
  continue;
  }
   
  $sources = $generator->generate($file);
  foreach ($sources as $source) {
  $this->notice('Generating "' . $source->getName() . '"');
  $resp->addFile($source);
  }
  }
   
  // Finally serialize the response object
  return $resp->serialize();
  }
   
  public function getComment($ident, $prefix = '')
  {
  if (!$this->comments->hasComment($ident)) {
  return null;
  }
   
  $comment = $this->comments->getComment($ident);
  if (0 < strlen($prefix)) {
  $comment = $prefix . str_replace("\n", "\n$prefix", $comment);
  }
   
  return $comment;
  }
  }