vendor/symfony/src/Symfony/Component/Process/Process.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 
       
     3 /*
       
     4  * This file is part of the Symfony package.
       
     5  *
       
     6  * (c) Fabien Potencier <fabien@symfony.com>
       
     7  *
       
     8  * For the full copyright and license information, please view the LICENSE
       
     9  * file that was distributed with this source code.
       
    10  */
       
    11 
       
    12 namespace Symfony\Component\Process;
       
    13 
       
    14 /**
       
    15  * Process is a thin wrapper around proc_* functions to ease
       
    16  * start independent PHP processes.
       
    17  *
       
    18  * @author Fabien Potencier <fabien@symfony.com>
       
    19  *
       
    20  * @api
       
    21  */
       
    22 class Process
       
    23 {
       
    24     private $commandline;
       
    25     private $cwd;
       
    26     private $env;
       
    27     private $stdin;
       
    28     private $timeout;
       
    29     private $options;
       
    30     private $exitcode;
       
    31     private $status;
       
    32     private $stdout;
       
    33     private $stderr;
       
    34 
       
    35     /**
       
    36      * Constructor.
       
    37      *
       
    38      * @param string  $commandline The command line to run
       
    39      * @param string  $cwd         The working directory
       
    40      * @param array   $env         The environment variables
       
    41      * @param string  $stdin       The STDIN content
       
    42      * @param integer $timeout     The timeout in seconds
       
    43      * @param array   $options     An array of options for proc_open
       
    44      *
       
    45      * @throws \RuntimeException When proc_open is not installed
       
    46      *
       
    47      * @api
       
    48      */
       
    49     public function __construct($commandline, $cwd = null, array $env = null, $stdin = null, $timeout = 60, array $options = array())
       
    50     {
       
    51         if (!function_exists('proc_open')) {
       
    52             throw new \RuntimeException('The Process class relies on proc_open, which is not available on your PHP installation.');
       
    53         }
       
    54 
       
    55         $this->commandline = $commandline;
       
    56         $this->cwd = null === $cwd ? getcwd() : $cwd;
       
    57         if (null !== $env) {
       
    58             $this->env = array();
       
    59             foreach ($env as $key => $value) {
       
    60                 $this->env[(binary) $key] = (binary) $value;
       
    61             }
       
    62         } else {
       
    63             $this->env = null;
       
    64         }
       
    65         $this->stdin = $stdin;
       
    66         $this->timeout = $timeout;
       
    67         $this->options = array_merge(array('suppress_errors' => true, 'binary_pipes' => true, 'bypass_shell' => false), $options);
       
    68     }
       
    69 
       
    70     /**
       
    71      * Runs the process.
       
    72      *
       
    73      * The callback receives the type of output (out or err) and
       
    74      * some bytes from the output in real-time. It allows to have feedback
       
    75      * from the independent process during execution.
       
    76      *
       
    77      * The STDOUT and STDERR are also available after the process is finished
       
    78      * via the getOutput() and getErrorOutput() methods.
       
    79      *
       
    80      * @param Closure|string|array $callback A PHP callback to run whenever there is some
       
    81      *                                       output available on STDOUT or STDERR
       
    82      *
       
    83      * @return integer The exit status code
       
    84      *
       
    85      * @throws \RuntimeException When process can't be launch or is stopped
       
    86      *
       
    87      * @api
       
    88      */
       
    89     public function run($callback = null)
       
    90     {
       
    91         $this->stdout = '';
       
    92         $this->stderr = '';
       
    93         $that = $this;
       
    94         $callback = function ($type, $data) use ($that, $callback)
       
    95         {
       
    96             if ('out' == $type) {
       
    97                 $that->addOutput($data);
       
    98             } else {
       
    99                 $that->addErrorOutput($data);
       
   100             }
       
   101 
       
   102             if (null !== $callback) {
       
   103                 call_user_func($callback, $type, $data);
       
   104             }
       
   105         };
       
   106 
       
   107         $descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
       
   108 
       
   109         $process = proc_open($this->commandline, $descriptors, $pipes, $this->cwd, $this->env, $this->options);
       
   110 
       
   111         if (!is_resource($process)) {
       
   112             throw new \RuntimeException('Unable to launch a new process.');
       
   113         }
       
   114 
       
   115         foreach ($pipes as $pipe) {
       
   116             stream_set_blocking($pipe, false);
       
   117         }
       
   118 
       
   119         if (null === $this->stdin) {
       
   120             fclose($pipes[0]);
       
   121             $writePipes = null;
       
   122         } else {
       
   123             $writePipes = array($pipes[0]);
       
   124             $stdinLen = strlen($this->stdin);
       
   125             $stdinOffset = 0;
       
   126         }
       
   127         unset($pipes[0]);
       
   128 
       
   129         while ($pipes || $writePipes) {
       
   130             $r = $pipes;
       
   131             $w = $writePipes;
       
   132             $e = null;
       
   133 
       
   134             $n = @stream_select($r, $w, $e, $this->timeout);
       
   135 
       
   136             if (false === $n) {
       
   137                 break;
       
   138             } elseif ($n === 0) {
       
   139                 proc_terminate($process);
       
   140 
       
   141                 throw new \RuntimeException('The process timed out.');
       
   142             }
       
   143 
       
   144             if ($w) {
       
   145                 $written = fwrite($writePipes[0], (binary) substr($this->stdin, $stdinOffset), 8192);
       
   146                 if (false !== $written) {
       
   147                     $stdinOffset += $written;
       
   148                 }
       
   149                 if ($stdinOffset >= $stdinLen) {
       
   150                     fclose($writePipes[0]);
       
   151                     $writePipes = null;
       
   152                 }
       
   153             }
       
   154 
       
   155             foreach ($r as $pipe) {
       
   156                 $type = array_search($pipe, $pipes);
       
   157                 $data = fread($pipe, 8192);
       
   158                 if (strlen($data) > 0) {
       
   159                     call_user_func($callback, $type == 1 ? 'out' : 'err', $data);
       
   160                 }
       
   161                 if (false === $data || feof($pipe)) {
       
   162                     fclose($pipe);
       
   163                     unset($pipes[$type]);
       
   164                 }
       
   165             }
       
   166         }
       
   167 
       
   168         $this->status = proc_get_status($process);
       
   169 
       
   170         $time = 0;
       
   171         while (1 == $this->status['running'] && $time < 1000000) {
       
   172             $time += 1000;
       
   173             usleep(1000);
       
   174             $this->status = proc_get_status($process);
       
   175         }
       
   176 
       
   177         $exitcode = proc_close($process);
       
   178 
       
   179         if ($this->status['signaled']) {
       
   180             throw new \RuntimeException(sprintf('The process stopped because of a "%s" signal.', $this->status['stopsig']));
       
   181         }
       
   182 
       
   183         return $this->exitcode = $this->status['running'] ? $exitcode : $this->status['exitcode'];
       
   184     }
       
   185 
       
   186     /**
       
   187      * Returns the output of the process (STDOUT).
       
   188      *
       
   189      * This only returns the output if you have not supplied a callback
       
   190      * to the run() method.
       
   191      *
       
   192      * @return string The process output
       
   193      *
       
   194      * @api
       
   195      */
       
   196     public function getOutput()
       
   197     {
       
   198         return $this->stdout;
       
   199     }
       
   200 
       
   201     /**
       
   202      * Returns the error output of the process (STDERR).
       
   203      *
       
   204      * This only returns the error output if you have not supplied a callback
       
   205      * to the run() method.
       
   206      *
       
   207      * @return string The process error output
       
   208      *
       
   209      * @api
       
   210      */
       
   211     public function getErrorOutput()
       
   212     {
       
   213         return $this->stderr;
       
   214     }
       
   215 
       
   216     /**
       
   217      * Returns the exit code returned by the process.
       
   218      *
       
   219      * @return integer The exit status code
       
   220      *
       
   221      * @api
       
   222      */
       
   223     public function getExitCode()
       
   224     {
       
   225         return $this->exitcode;
       
   226     }
       
   227 
       
   228     /**
       
   229      * Checks if the process ended successfully.
       
   230      *
       
   231      * @return Boolean true if the process ended successfully, false otherwise
       
   232      *
       
   233      * @api
       
   234      */
       
   235     public function isSuccessful()
       
   236     {
       
   237         return 0 == $this->exitcode;
       
   238     }
       
   239 
       
   240     /**
       
   241      * Returns true if the child process has been terminated by an uncaught signal.
       
   242      *
       
   243      * It always returns false on Windows.
       
   244      *
       
   245      * @return Boolean
       
   246      *
       
   247      * @api
       
   248      */
       
   249     public function hasBeenSignaled()
       
   250     {
       
   251         return $this->status['signaled'];
       
   252     }
       
   253 
       
   254     /**
       
   255      * Returns the number of the signal that caused the child process to terminate its execution.
       
   256      *
       
   257      * It is only meaningful if hasBeenSignaled() returns true.
       
   258      *
       
   259      * @return integer
       
   260      *
       
   261      * @api
       
   262      */
       
   263     public function getTermSignal()
       
   264     {
       
   265         return $this->status['termsig'];
       
   266     }
       
   267 
       
   268     /**
       
   269      * Returns true if the child process has been stopped by a signal.
       
   270      *
       
   271      * It always returns false on Windows.
       
   272      *
       
   273      * @return Boolean
       
   274      *
       
   275      * @api
       
   276      */
       
   277     public function hasBeenStopped()
       
   278     {
       
   279         return $this->status['stopped'];
       
   280     }
       
   281 
       
   282     /**
       
   283      * Returns the number of the signal that caused the child process to stop its execution
       
   284      *
       
   285      * It is only meaningful if hasBeenStopped() returns true.
       
   286      *
       
   287      * @return integer
       
   288      *
       
   289      * @api
       
   290      */
       
   291     public function getStopSignal()
       
   292     {
       
   293         return $this->status['stopsig'];
       
   294     }
       
   295 
       
   296     public function addOutput($line)
       
   297     {
       
   298         $this->stdout .= $line;
       
   299     }
       
   300 
       
   301     public function addErrorOutput($line)
       
   302     {
       
   303         $this->stderr .= $line;
       
   304     }
       
   305 
       
   306     public function getCommandLine()
       
   307     {
       
   308         return $this->commandline;
       
   309     }
       
   310 
       
   311     public function setCommandLine($commandline)
       
   312     {
       
   313         $this->commandline = $commandline;
       
   314     }
       
   315 
       
   316     public function getTimeout()
       
   317     {
       
   318         return $this->timeout;
       
   319     }
       
   320 
       
   321     public function setTimeout($timeout)
       
   322     {
       
   323         $this->timeout = $timeout;
       
   324     }
       
   325 
       
   326     public function getWorkingDirectory()
       
   327     {
       
   328         return $this->cwd;
       
   329     }
       
   330 
       
   331     public function setWorkingDirectory($cwd)
       
   332     {
       
   333         $this->cwd = $cwd;
       
   334     }
       
   335 
       
   336     public function getEnv()
       
   337     {
       
   338         return $this->env;
       
   339     }
       
   340 
       
   341     public function setEnv(array $env)
       
   342     {
       
   343         $this->env = $env;
       
   344     }
       
   345 
       
   346     public function getStdin()
       
   347     {
       
   348         return $this->stdin;
       
   349     }
       
   350 
       
   351     public function setStdin($stdin)
       
   352     {
       
   353         $this->stdin = $stdin;
       
   354     }
       
   355 
       
   356     public function getOptions()
       
   357     {
       
   358         return $this->options;
       
   359     }
       
   360 
       
   361     public function setOptions(array $options)
       
   362     {
       
   363         $this->options = $options;
       
   364     }
       
   365 }