Procházet zdrojové kódy

[Process] added the component

Fabien Potencier před 15 roky
rodič
revize
4933ddd6a8

+ 91 - 0
src/Symfony/Components/Process/PhpProcess.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace Symfony\Components\Process;
+
+/*
+ * This file is part of the symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * PhpProcess runs a PHP script in a forked process.
+ *
+ * @package    Symfony
+ * @subpackage Components_Process
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class PhpProcess extends Process
+{
+  /**
+   * Constructor.
+   *
+   * @param string  $script  The PHP script to run (as a string)
+   * @param string  $cwd     The working directory
+   * @param array   $env     The environment variables
+   * @param integer $timeout The timeout in seconds
+   * @param array   $options An array of options for proc_open
+   */
+  public function __construct($script, $cwd = null, array $env = array(), $timeout = 60, array $options = array())
+  {
+    parent::__construct(null, $cwd, $env, $script, $timeout, $options);
+  }
+
+  /**
+   * Sets the path to the PHP binary to use.
+   */
+  public function setPhpBinary($php)
+  {
+    $this->commandline = $php;
+  }
+
+  /**
+   * Forks and run the process.
+   *
+   * @param Closure|string|array $callback A PHP callback to run whenever there is some
+   *                                       output available on STDOUT or STDERR
+   *
+   * @return integer The exit status code
+   */
+  public function run($callback)
+  {
+    if (null === $this->commandline)
+    {
+      $this->commandline = $this->getPhpBinary();
+    }
+
+    parent::run($callback);
+  }
+
+  /**
+   * Returns the PHP binary path.
+   *
+   * return string The PHP binary path
+   */
+  static public function getPhpBinary()
+  {
+    if (getenv('PHP_PATH'))
+    {
+      if (!is_executable($php = getenv('PHP_PATH')))
+      {
+        throw new \RuntimeException('The defined PHP_PATH environment variable is not a valid PHP executable.');
+      }
+
+      return $php;
+    }
+
+    $suffixes = DIRECTORY_SEPARATOR == '\\' ? (getenv('PATHEXT') ? explode(PATH_SEPARATOR, getenv('PATHEXT')) : array('.exe', '.bat', '.cmd', '.com')) : array('');
+    foreach ($suffixes as $suffix)
+    {
+      if (is_executable($php = PHP_BINDIR.DIRECTORY_SEPARATOR.'php'.$suffix))
+      {
+        return $php;
+      }
+    }
+
+    throw new \RuntimeException("Unable to find the PHP executable.");
+  }
+}

+ 209 - 0
src/Symfony/Components/Process/Process.php

@@ -0,0 +1,209 @@
+<?php
+
+namespace Symfony\Components\Process;
+
+/*
+ * This file is part of the symfony package.
+ *
+ * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * Process is a thin wrapper around proc_* functions to ease
+ * the forking processes from PHP.
+ *
+ * @package    Symfony
+ * @subpackage Components_Process
+ * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
+ */
+class Process
+{
+  protected $commandline;
+  protected $cwd;
+  protected $env;
+  protected $stdin;
+  protected $timeout;
+  protected $options;
+  protected $exitcode;
+  protected $status;
+
+  /**
+   * Constructor.
+   *
+   * @param string  $commandline The command line to run
+   * @param string  $cwd         The working directory
+   * @param array   $env         The environment variables
+   * @param string  $stdin       The STDIN content
+   * @param integer $timeout     The timeout in seconds
+   * @param array   $options     An array of options for proc_open
+   */
+  public function __construct($commandline, $cwd, array $env = array(), $stdin = null, $timeout = 60, array $options = array())
+  {
+    if (!function_exists('proc_open'))
+    {
+      throw new \RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
+    }
+
+    $this->commandline = $commandline;
+    $this->cwd = null === $cwd ? getcwd() : $cwd;
+    $this->env = array();
+    foreach ($env as $key => $value)
+    {
+      $this->env[(binary) $key] = (binary) $value;
+    }
+    $this->stdin = $stdin;
+    $this->timeout = $timeout;
+    $this->options = array_merge($options, array('suppress_errors' => true, 'binary_pipes' => true));
+  }
+
+  /**
+   * Forks and run the process.
+   *
+   * @param Closure|string|array $callback A PHP callback to run whenever there is some
+   *                                       output available on STDOUT or STDERR
+   *
+   * @return integer The exit status code
+   */
+  public function run($callback)
+  {
+    $descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
+
+    $proccess = proc_open($this->commandline, $descriptors, $pipes, $this->cwd, $this->env, $this->options);
+
+    stream_set_blocking($pipes[1], false);
+    stream_set_blocking($pipes[2], false);
+
+    if (!is_resource($proccess))
+    {
+      throw new \RuntimeException('Unable to launch a new process.');
+    }
+
+    if (!is_null($this->stdin))
+    {
+      fwrite($pipes[0], (binary) $this->stdin);
+    }
+    fclose($pipes[0]);
+
+    while (true)
+    {
+      $r = $pipes;
+      $w = null;
+      $e = null;
+
+      $n = @stream_select($r, $w, $e, $this->timeout);
+
+      if ($n === false)
+      {
+        break;
+      }
+      elseif ($n === 0)
+      {
+        proc_terminate($proccess);
+
+        throw new \RuntimeException('The process timed out.');
+      }
+      elseif ($n > 0)
+      {
+        $called = false;
+
+        while (true)
+        {
+          $c = false;
+          if ($line = (binary) fgets($pipes[1], 1024))
+          {
+            $called = $c = true;
+            call_user_func($callback, 'out', $line);
+          }
+
+          if ($line = fgets($pipes[2], 1024))
+          {
+            $called = $c = true;
+            call_user_func($callback, 'err', $line);
+          }
+
+          if (!$c)
+          {
+            break;
+          }
+        }
+
+        if (!$called)
+        {
+          break;
+        }
+      }
+    }
+
+    $this->status = proc_get_status($proccess);
+
+    proc_close($proccess);
+
+    if ($this->status['signaled'])
+    {
+      throw new \RuntimeException(sprintf('The process stopped because of a "%s" signal.', $this->status['stopsig']));
+    }
+
+    return $this->exitcode = $this->status['exitcode'];
+  }
+
+  /**
+   * Returns the exit code returned by the process.
+   *
+   * @return integer The exit status code
+   */
+  public function getExitCode()
+  {
+    return $this->exitcode;
+  }
+
+  /**
+   * Returns true if the child process has been terminated by an uncaught signal.
+   *
+   * It always returns false on Windows.
+   *
+   * @return Boolean
+   */
+  public function hasBeenSignaled()
+  {
+    return $this->status['signaled'];
+  }
+
+  /**
+   * Returns the number of the signal that caused the child process to terminate its execution.
+   *
+   * It is only meaningful if hasBeenSignaled() returns true.
+   *
+   * @return integer
+   */
+  public function getTermSignal()
+  {
+    return $this->status['termsig'];
+  }
+
+  /**
+   * Returns true if the child process has been stopped by a signal.
+   *
+   * It always returns false on Windows.
+   *
+   * @return Boolean
+   */
+  public function hasBeenStopped()
+  {
+    return $this->status['stopped'];
+  }
+
+  /**
+   * Returns the number of the signal that caused the child process to stop its execution
+   *
+   * It is only meaningful if hasBeenStopped() returns true.
+   *
+   * @return integer
+   */
+  public function getStopSignal()
+  {
+    return $this->status['stopsig'];
+  }
+}