Add php protobuffer support for transition to GTFS-realtime
[busui.git] / lib / Protobuf-PHP / library / DrSlump / Protobuf / Compiler / Cli.php
blob:a/lib/Protobuf-PHP/library/DrSlump/Protobuf/Compiler/Cli.php -> blob:b/lib/Protobuf-PHP/library/DrSlump/Protobuf/Compiler/Cli.php
  <?php
   
  namespace DrSlump\Protobuf\Compiler;
   
  require_once 'Console/CommandLine.php';
   
  use DrSlump\Protobuf;
   
  class Cli
  {
  public static function run($pluginExecutable)
  {
  // Open STDIN in non-blocking mode
  $fp = fopen('php://stdin', 'rb');
  stream_set_blocking($fp, FALSE);
   
  // Loop until STDIN is closed or we've waited too long for data
  $cnt = 0;
  $stdin = '';
  while (!feof($fp) && $cnt++ < 10) {
  // give protoc some time to feed the data
  usleep(10000);
  // read the bytes
  $bytes = fread($fp, 1024);
  if (strlen($bytes)) {
  $cnt = 0;
  $stdin .= $bytes;
  }
  }
   
  // If no input was given we launch protoc from here
  if (0 === strlen($stdin)) {
  self::runProtoc($pluginExecutable);
  exit(0);
  }
   
  // We have data from stdin so compile it
  try {
  // Create a compiler interface
  $comp = new Protobuf\Compiler();
  echo $comp->compile($stdin);
  exit(0);
  } catch(\Exception $e) {
  fputs(STDERR, 'ERROR: ' . $e->getMessage());
  fputs(STDERR, $e->getTraceAsString());
  exit(255);
  }
  }
   
  public static function runProtoc($pluginExecutable)
  {
  $result = self::parseArguments();
   
  $protocBin = $result->options['protoc'];
   
  // Check if protoc is available
  exec("$protocBin --version", $output, $return);
   
  if (0 !== $return && 1 !== $return) {
  fputs(STDERR, "ERROR: Unable to find the protoc command.". PHP_EOL);
  fputs(STDERR, " Please make sure it's installed and available in the path." . PHP_EOL);
  exit(1);
  }
   
  if (!preg_match('/[0-9\.]+/', $output[0], $m)) {
  fputs(STDERR, "ERROR: Unable to get protoc command version.". PHP_EOL);
  fputs(STDERR, " Please make sure it's installed and available in the path." . PHP_EOL);
  exit(1);
  }
   
  if (version_compare($m[0], '2.3.0') < 0) {
  fputs(STDERR, "ERROR: The protoc command in your system is too old." . PHP_EOL);
  fputs(STDERR, " Minimum version required is 2.3.0 but found {$m[0]}." . PHP_EOL);
  exit(1);
  }
   
  $cmd[] = $protocBin;
  $cmd[] = '--plugin=protoc-gen-php=' . escapeshellarg($pluginExecutable);
   
  // Include paths
  $cmd[] = '--proto_path=' . escapeshellarg(__DIR__ . DIRECTORY_SEPARATOR . 'protos');
  if (!empty($result->options['include'])) {
  foreach($result->options['include'] as $include) {
  $include = realpath($include);
  $cmd[] = '--proto_path=' . escapeshellarg($include);
  }
  }
   
  // Convert proto files to absolute paths
  $protos = array();
  foreach ($result->args['protos'] as $proto) {
  $realpath = realpath($proto);
  if (FALSE === $realpath) {
  fputs(STDERR, "ERROR: File '$proto' does not exists");
  exit(1);
  }
   
  $protos[] = $realpath;
  }
   
  // Protoc will pass custom arguments to the plugin if they are given
  // before a colon character. ie: --php_out="foo=bar:/path/to/plugin"
  // We make use of it to pass arguments encoded as an URI query string
   
  $args = array();
  if ($result->options['comments']) {
  $args['comments'] = 1;
  // Protos are only needed for comments right now
  $args['protos'] = $protos;
  }
  if ($result->options['verbose']) {
  $args['verbose'] = 1;
  }
  if ($result->options['json']) {
  $args['json'] = 1;
  }
  if ($result->options['skipImported']) {
  $args['skip-imported'] = 1;
  }
  if ($result->options['define']) {
  $args['options'] = array();
  foreach($result->options['define'] as $define) {
  $parts = explode('=', $define);
  $parts = array_filter(array_map('trim', $parts));
  if (count($parts) === 1) {
  $parts[1] = 1;
  }
  $args['options'][$parts[0]] = $parts[1];
  }
  }
   
  $cmd[] = '--php_out=' .
  escapeshellarg(
  http_build_query($args, '', '&') .
  ':' .
  $result->options['out']
  );
   
  // Add the chosen proto files to generate
  foreach ($protos as $proto) {
  $cmd[] = escapeshellarg($proto);
  }
   
  $cmdStr = implode(' ', $cmd);
   
  // Run command with stderr redirected to stdout
  passthru($cmdStr . ' 2>&1', $return);
   
  if ($return !== 0) {
  fputs(STDERR, PHP_EOL);
  fputs(STDERR, 'ERROR: protoc exited with an error (' . $return . ') when executed with: ' . PHP_EOL);
  fputs(STDERR, ' ' . implode(" \\\n ", $cmd) . PHP_EOL);
  exit($return);
  }
  }
   
   
  public static function parseArguments()
  {
  $main = new \Console_CommandLine();
   
  $main->addOption('out', array(
  'short_name' => '-o',
  'long_name' => '--out',
  'action' => 'StoreString',
  'description' => 'destination directory for generated files',
  'default' => './',
  ));
   
  $main->addOption('include', array(
  'short_name' => '-i',
  'long_name' => '--include',
  'action' => 'StoreArray',
  'description' => 'define an include path (can be repeated)',
  'multiple' => true,
  ));
   
   
  $main->addOption('json', array(
  'short_name' => '-j',
  'long_name' => '--json',
  'action' => 'StoreTrue',
  'description' => 'turn on ProtoJson Javascript file generation',
  ));
   
  $main->addOption('protoc', array(
  'long_name' => '--protoc',
  'action' => 'StoreString',
  'default' => 'protoc',
  'description' => 'protoc compiler executable path',
  ));
   
  $main->addOption('skipImported', array(
  'long_name' => '--skip-imported',
  'action' => 'StoreTrue',
  'default' => false,
  'description' => 'do not generate imported proto files',
  ));
   
  $main->addOption('comments', array(
  'long_name' => '--comments',
  'action' => 'StoreTrue',
  'description' => 'port .proto comments to generated code',
  ));
   
  $main->addOption('define', array(
  'short_name' => '-D',
  'long_name' => '--define',
  'action' => 'StoreArray',
  'multiple' => true,
  'description' => 'define a generator option (ie: -Dmultifile -Dsuffix=pb.php)',
  ));
   
  $main->addOption('verbose', array(
  'short_name' => '-v',
  'long_name' => '--verbose',
  'action' => 'StoreTrue',
  'description' => 'turn on verbose output',
  ));
   
   
  $main->addArgument('protos', array(
  'multiple' => true,
  'description' => 'proto files',
  ));
   
  try {
  echo 'Protobuf-PHP ' . Protobuf::VERSION . ' by Ivan -DrSlump- Montes' . PHP_EOL . PHP_EOL;
  $result = $main->parse();
  return $result;
  } catch (\Exception $e) {
  $main->displayError($e->getMessage());
  exit(1);
  }
  }
   
  }