Hoa central
Processus.php
Go to the documentation of this file.
1 <?php
2 
37 namespace Hoa\Console;
38 
39 use Hoa\Core;
40 use Hoa\Stream;
41 
50 class Processus
51  extends Stream
52  implements Stream\IStream\In,
55 {
61  const SIGHUP = 1;
62 
68  const SIGINT = 2;
69 
75  const SIGQUIT = 3;
76 
82  const SIGILL = 4;
83 
89  const SIGTRAP = 5;
90 
96  const SIGABRT = 6;
97 
103  const SIGEMT = 7;
104 
110  const SIGFPE = 8;
111 
117  const SIGKILL = 9;
118 
124  const SIGBUS = 10;
125 
131  const SIGSEGV = 11;
132 
138  const SIGSYS = 12;
139 
145  const SIGPIPE = 13;
146 
152  const SIGALRM = 14;
153 
159  const SIGTERM = 15;
160 
166  const SIGURG = 16;
167 
173  const SIGSTOP = 17;
174 
180  const SIGTSTP = 18;
181 
187  const SIGCONT = 19;
188 
194  const SIGCHLD = 20;
195 
201  const SIGTTIN = 21;
202 
208  const SIGTTOU = 22;
209 
215  const SIGIO = 23;
216 
222  const SIGXCPU = 24;
223 
229  const SIGXFSZ = 25;
230 
236  const SIGVTALRM = 26;
237 
243  const SIGPROF = 27;
244 
250  const SIGWINCH = 28;
251 
257  const SIGINFO = 29;
258 
264  const SIGUSR1 = 30;
265 
271  const SIGUSR2 = 31;
272 
278  protected $_command = null;
279 
285  protected $_options = [];
286 
292  protected $_cwd = null;
293 
299  protected $_environment = null;
300 
306  protected $_timeout = 30;
307 
313  protected $_descriptors = [
314  0 => ['pipe', 'r'],
315  1 => ['pipe', 'w'],
316  2 => ['pipe', 'w']
317  ];
318 
324  protected $_pipes = null;
325 
331  protected $_seekable = [];
332 
333 
334 
348  public function __construct(
349  $command,
350  Array $options = null,
351  Array $descriptors = null,
352  $cwd = null,
353  Array $environment = null,
354  $timeout = 30
355  ) {
356  $this->setCommand($command);
357 
358  if (null !== $options) {
359  $this->setOptions($options);
360  }
361 
362  if (null !== $descriptors) {
363  $this->_descriptors = [];
364 
365  foreach ($descriptors as $descriptor => $nature) {
366  if (isset($this->_descriptors[$descriptor])) {
367  throw new Exception(
368  'Pipe descriptor %d already exists, cannot ' .
369  'redefine it.',
370  0,
371  $descriptor
372  );
373  }
374 
375  $this->_descriptors[$descriptor] = $nature;
376  }
377  }
378 
379  $this->setCwd($cwd ?: getcwd());
380 
381  if (null !== $environment) {
382  $this->setEnvironment($environment);
383  }
384 
385  $this->setTimeout($timeout);
386  parent::__construct($this->getCommandLine(), null, true);
387  $this->_on->addIds(['input', 'output', 'timeout', 'start', 'stop']);
388 
389  return;
390  }
391 
400  protected function &_open($streamName, Stream\Context $context = null)
401  {
402  $out = @proc_open(
403  $streamName,
404  $this->_descriptors,
405  $this->_pipes,
406  $this->getCwd(),
407  $this->getEnvironment()
408  );
409 
410  if (false === $out) {
411  throw new Exception(
412  'Something wrong happen when running %s.',
413  1,
414  $streamName
415  );
416  }
417 
418  return $out;
419  }
420 
426  protected function _close()
427  {
428  foreach ($this->_pipes as $pipe) {
429  @fclose($pipe);
430  }
431 
432  return @proc_close($this->getStream());
433  }
434 
443  public function run()
444  {
445  if (false === $this->isOpened()) {
446  $this->open();
447  } else {
448  $this->_close();
449  $this->_setStream($this->_open(
450  $this->getStreamName(),
451  $this->getStreamContext()
452  ));
453  }
454 
455  $this->_on->fire('start', new Core\Event\Bucket());
456 
457  $_read = [];
458  $_write = [];
459  $_except = [];
460 
461  foreach ($this->_pipes as $p => $pipe) {
462  switch ($this->_descriptors[$p][1]) {
463  case 'r':
464  stream_set_blocking($pipe, false);
465  $_write[] = $pipe;
466 
467  break;
468 
469  case 'w':
470  case 'a':
471  stream_set_blocking($pipe, true);
472  $_read[] = $pipe;
473 
474  break;
475  }
476  }
477 
478  while (true) {
479  foreach ($_read as $i => $r) {
480  if (false === is_resource($r)) {
481  unset($_read[$i]);
482  }
483  }
484 
485  foreach ($_write as $i => $w) {
486  if (false === is_resource($w)) {
487  unset($_write[$i]);
488  }
489  }
490 
491  foreach ($_except as $i => $e) {
492  if (false === is_resource($e)) {
493  unset($_except[$i]);
494  }
495  }
496 
497  if (empty($_read) && empty($_write) && empty($_except)) {
498  break;
499  }
500 
501  $read = $_read;
502  $write = $_write;
503  $except = $_except;
504  $select = stream_select($read, $write, $except, $this->getTimeout());
505 
506  if (0 === $select) {
507  $this->_on->fire('timeout', new Core\Event\Bucket());
508 
509  break;
510  }
511 
512  foreach ($read as $i => $_r) {
513  $pipe = array_search($_r, $this->_pipes);
514  $line = $this->readLine($pipe);
515 
516  if (false === $line) {
517  $result = [false];
518  } else {
519  $result = $this->_on->fire(
520  'output',
521  new Core\Event\Bucket([
522  'pipe' => $pipe,
523  'line' => $line
524  ])
525  );
526  }
527 
528  if (true === feof($_r) || in_array(false, $result, true)) {
529  fclose($_r);
530  unset($_read[$i]);
531 
532  break;
533  }
534  }
535 
536  foreach ($write as $j => $_w) {
537  $result = $this->_on->fire(
538  'input',
539  new Core\Event\Bucket([
540  'pipe' => array_search($_w, $this->_pipes)
541  ])
542  );
543 
544  if (true === feof($_w) || in_array(false, $result, true)) {
545  fclose($_w);
546  unset($_write[$j]);
547  }
548  }
549 
550  if (empty($_read)) {
551  break;
552  }
553  }
554 
555  $this->_on->fire('stop', new Core\Event\Bucket());
556 
557  return;
558  }
559 
567  protected function getPipe($pipe)
568  {
569  if (!isset($this->_pipes[$pipe])) {
570  throw new Exception(
571  'Pipe descriptor %d does not exist, cannot read from it.',
572  2,
573  $pipe
574  );
575  }
576 
577  return $this->_pipes[$pipe];
578  }
579 
586  protected function isPipeSeekable($pipe)
587  {
588  if (!isset($this->_seekable[$pipe])) {
589  $_pipe = $this->getPipe($pipe);
590  $data = stream_get_meta_data($_pipe);
591  $this->_seekable[$pipe] = $data['seekable'];
592  }
593 
594  return $this->_seekable[$pipe];
595  }
596 
603  public function eof($pipe = 1)
604  {
605  return feof($this->getPipe($pipe));
606  }
607 
616  public function read($length, $pipe = 1)
617  {
618  if (0 > $length) {
619  throw new Exception(
620  'Length must be greater than 0, given %d.',
621  3,
622  $length
623  );
624  }
625 
626  return fread($this->getPipe($pipe), $length);
627  }
628 
636  public function readString($length, $pipe = 1)
637  {
638  return $this->read($length, $pipe);
639  }
640 
647  public function readCharacter($pipe = 1)
648  {
649  return fgetc($this->getPipe($pipe));
650  }
651 
658  public function readBoolean($pipe = 1)
659  {
660  return (bool) $this->read(1, $pipe);
661  }
662 
670  public function readInteger($length = 1, $pipe = 1)
671  {
672  return (int) $this->read($length, $pipe);
673  }
674 
682  public function readFloat($length = 1, $pipe = 1)
683  {
684  return (float) $this->read($length, $pipe);
685  }
686 
695  public function readArray($format = null, $pipe = 1)
696  {
697  return $this->scanf($format, $pipe);
698  }
699 
706  public function readLine($pipe = 1)
707  {
708  return stream_get_line($this->getPipe($pipe), 1 << 15, "\n");
709  }
710 
718  public function readAll($offset = -1, $pipe = 1)
719  {
720  $_pipe = $this->getPipe($pipe);
721 
722  if (true === $this->isPipeSeekable($pipe)) {
723  $offset += ftell($_pipe);
724  } else {
725  $offset = -1;
726  }
727 
728  return stream_get_contents($_pipe, -1, $offset);
729  }
730 
738  public function scanf($format, $pipe = 1)
739  {
740  return fscanf($this->getPipe($pipe), $format);
741  }
742 
752  public function write($string, $length, $pipe = 0)
753  {
754  if (0 > $length) {
755  throw new Exception(
756  'Length must be greater than 0, given %d.',
757  4,
758  $length
759  );
760  }
761 
762  return fwrite($this->getPipe($pipe), $string, $length);
763  }
764 
772  public function writeString($string, $pipe = 0)
773  {
774  $string = (string) $string;
775 
776  return $this->write($string, strlen($string), $pipe);
777  }
778 
786  public function writeCharacter($char, $pipe = 0)
787  {
788  return $this->write((string) $char[0], 1, $pipe);
789  }
790 
798  public function writeBoolean($boolean, $pipe = 0)
799  {
800  return $this->write((string) (bool) $boolean, 1, $pipe);
801  }
802 
810  public function writeInteger($integer, $pipe = 0)
811  {
812  $integer = (string) (int) $integer;
813 
814  return $this->write($integer, strlen($integer), $pipe);
815  }
816 
824  public function writeFloat($float, $pipe = 0)
825  {
826  $float = (string) (float) $float;
827 
828  return $this->write($float, strlen($float), $pipe);
829  }
830 
838  public function writeArray(Array $array, $pipe = 0)
839  {
840  $array = var_export($array, true);
841 
842  return $this->write($array, strlen($array), $pipe);
843  }
844 
852  public function writeLine($line, $pipe = 0)
853  {
854  if (false === $n = strpos($line, "\n")) {
855  return $this->write($line . "\n", strlen($line) + 1, $pipe);
856  }
857 
858  ++$n;
859 
860  return $this->write(substr($line, 0, $n), $n, $pipe);
861  }
862 
870  public function writeAll($string, $pipe = 0)
871  {
872  return $this->write($string, strlen($string), $pipe);
873  }
874 
882  public function truncate($size, $pipe = 0)
883  {
884  return ftruncate($this->getPipe($pipe), $size);
885  }
886 
892  public function getBasename()
893  {
894  return basename($this->getCommand());
895  }
896 
902  public function getDirname()
903  {
904  return dirname($this->getCommand());
905  }
906 
912  public function getStatus()
913  {
914  return proc_get_status($this->getStream());
915  }
916 
922  public function getExitCode()
923  {
924  $handle = $this->getStatus();
925 
926  return $handle['exitcode'];
927  }
928 
934  public function isSuccessful()
935  {
936  return 0 === $this->getExitCode();
937  }
938 
947  public function terminate($signal = self::SIGTERM)
948  {
949  return proc_terminate($this->getStream(), $signal);
950  }
951 
958  protected function setCommand($command)
959  {
960  $old = $this->_command;
961  $this->_command = escapeshellcmd($command);
962 
963  return $old;
964  }
965 
971  public function getCommand()
972  {
973  return $this->_command;
974  }
975 
982  protected function setOptions(Array $options)
983  {
984  foreach ($options as &$option) {
985  $option = escapeshellarg($option);
986  }
987 
988  $old = $this->_options;
989  $this->_options = $options;
990 
991  return $old;
992  }
993 
999  public function getOptions()
1000  {
1001  return $this->_options;
1002  }
1003 
1009  public function getCommandLine()
1010  {
1011  $out = $this->getCommand();
1012 
1013  foreach ($this->getOptions() as $key => $value) {
1014  if (!is_int($key)) {
1015  $out .= ' ' . $key . '=' . $value;
1016  } else {
1017  $out .= ' ' . $value;
1018  }
1019  }
1020 
1021  return $out;
1022  }
1023 
1030  protected function setCwd($cwd)
1031  {
1032  $old = $this->_cwd;
1033  $this->_cwd = $cwd;
1034 
1035  return $old;
1036  }
1037 
1043  public function getCwd()
1044  {
1045  return $this->_cwd;
1046  }
1047 
1054  protected function setEnvironment(Array $environment)
1055  {
1056  $old = $this->_environment;
1057  $this->_environment = $environment;
1058 
1059  return $old;
1060  }
1061 
1067  public function getEnvironment()
1068  {
1069  return $this->_environment;
1070  }
1071 
1078  public function setTimeout($timeout)
1079  {
1080  $old = $this->_timeout;
1081  $this->_timeout = $timeout;
1082 
1083  return $old;
1084  }
1085 
1091  public function getTimeout()
1092  {
1093  return $this->_timeout;
1094  }
1095 
1102  public static function setTitle($title)
1103  {
1104  if (PHP_VERSION_ID < 50500) {
1105  return;
1106  }
1107 
1108  cli_set_process_title($title);
1109 
1110  return;
1111  }
1112 
1118  public static function getTitle()
1119  {
1120  if (PHP_VERSION_ID < 50500) {
1121  return null;
1122  }
1123 
1124  return cli_get_process_title();
1125  }
1126 
1133  public static function locate($binary)
1134  {
1135  if (isset($_ENV['PATH'])) {
1136  $separator = ':';
1137  $path = &$_ENV['PATH'];
1138  } elseif (isset($_SERVER['PATH'])) {
1139  $separator = ':';
1140  $path = &$_SERVER['PATH'];
1141  } elseif (isset($_SERVER['Path'])) {
1142  $separator = ';';
1143  $path = &$_SERVER['Path'];
1144  } else {
1145  return null;
1146  }
1147 
1148  foreach (explode($separator, $path) as $directory) {
1149  if (true === file_exists($out = $directory . DS . $binary)) {
1150  return $out;
1151  }
1152  }
1153 
1154  return null;
1155  }
1156 
1165  public static function execute($commandLine, $escape = true)
1166  {
1167  if (true === $escape) {
1168  $commandLine = escapeshellcmd($commandLine);
1169  }
1170 
1171  return rtrim(shell_exec($commandLine));
1172  }
1173 }
readFloat($length=1, $pipe=1)
Definition: Processus.php:682
readInteger($length=1, $pipe=1)
Definition: Processus.php:670
read($length, $pipe=1)
Definition: Processus.php:616
writeArray(Array $array, $pipe=0)
Definition: Processus.php:838
readString($length, $pipe=1)
Definition: Processus.php:636
truncate($size, $pipe=0)
Definition: Processus.php:882
readArray($format=null, $pipe=1)
Definition: Processus.php:695
writeBoolean($boolean, $pipe=0)
Definition: Processus.php:798
__construct($command, Array $options=null, Array $descriptors=null, $cwd=null, Array $environment=null, $timeout=30)
Definition: Processus.php:348
setOptions(Array $options)
Definition: Processus.php:982
static execute($commandLine, $escape=true)
Definition: Processus.php:1165
terminate($signal=self::SIGTERM)
Definition: Processus.php:947
writeFloat($float, $pipe=0)
Definition: Processus.php:824
write($string, $length, $pipe=0)
Definition: Processus.php:752
_setStream($stream)
Definition: Stream.php:399
writeAll($string, $pipe=0)
Definition: Processus.php:870
static locate($binary)
Definition: Processus.php:1133
scanf($format, $pipe=1)
Definition: Processus.php:738
writeInteger($integer, $pipe=0)
Definition: Processus.php:810
writeCharacter($char, $pipe=0)
Definition: Processus.php:786
writeString($string, $pipe=0)
Definition: Processus.php:772
static setTitle($title)
Definition: Processus.php:1102
writeLine($line, $pipe=0)
Definition: Processus.php:852
& _open($streamName, Stream\Context $context=null)
Definition: Processus.php:400
setEnvironment(Array $environment)
Definition: Processus.php:1054
readAll($offset=-1, $pipe=1)
Definition: Processus.php:718