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 12namespace Symfony\Component\Process\Pipes; 13 14use Symfony\Component\Process\Process; 15 16/** 17 * UnixPipes implementation uses unix pipes as handles. 18 * 19 * @author Romain Neutron <imprec@gmail.com> 20 * 21 * @internal 22 */ 23class UnixPipes extends AbstractPipes 24{ 25 private $ttyMode; 26 private $ptyMode; 27 private $haveReadSupport; 28 29 public function __construct(?bool $ttyMode, bool $ptyMode, $input, bool $haveReadSupport) 30 { 31 $this->ttyMode = $ttyMode; 32 $this->ptyMode = $ptyMode; 33 $this->haveReadSupport = $haveReadSupport; 34 35 parent::__construct($input); 36 } 37 38 public function __sleep(): array 39 { 40 throw new \BadMethodCallException('Cannot serialize '.__CLASS__); 41 } 42 43 public function __wakeup() 44 { 45 throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); 46 } 47 48 public function __destruct() 49 { 50 $this->close(); 51 } 52 53 /** 54 * {@inheritdoc} 55 */ 56 public function getDescriptors(): array 57 { 58 if (!$this->haveReadSupport) { 59 $nullstream = fopen('/dev/null', 'c'); 60 61 return [ 62 ['pipe', 'r'], 63 $nullstream, 64 $nullstream, 65 ]; 66 } 67 68 if ($this->ttyMode) { 69 return [ 70 ['file', '/dev/tty', 'r'], 71 ['file', '/dev/tty', 'w'], 72 ['file', '/dev/tty', 'w'], 73 ]; 74 } 75 76 if ($this->ptyMode && Process::isPtySupported()) { 77 return [ 78 ['pty'], 79 ['pty'], 80 ['pty'], 81 ]; 82 } 83 84 return [ 85 ['pipe', 'r'], 86 ['pipe', 'w'], // stdout 87 ['pipe', 'w'], // stderr 88 ]; 89 } 90 91 /** 92 * {@inheritdoc} 93 */ 94 public function getFiles(): array 95 { 96 return []; 97 } 98 99 /** 100 * {@inheritdoc} 101 */ 102 public function readAndWrite(bool $blocking, bool $close = false): array 103 { 104 $this->unblock(); 105 $w = $this->write(); 106 107 $read = $e = []; 108 $r = $this->pipes; 109 unset($r[0]); 110 111 // let's have a look if something changed in streams 112 set_error_handler([$this, 'handleError']); 113 if (($r || $w) && false === stream_select($r, $w, $e, 0, $blocking ? Process::TIMEOUT_PRECISION * 1E6 : 0)) { 114 restore_error_handler(); 115 // if a system call has been interrupted, forget about it, let's try again 116 // otherwise, an error occurred, let's reset pipes 117 if (!$this->hasSystemCallBeenInterrupted()) { 118 $this->pipes = []; 119 } 120 121 return $read; 122 } 123 restore_error_handler(); 124 125 foreach ($r as $pipe) { 126 // prior PHP 5.4 the array passed to stream_select is modified and 127 // lose key association, we have to find back the key 128 $read[$type = array_search($pipe, $this->pipes, true)] = ''; 129 130 do { 131 $data = @fread($pipe, self::CHUNK_SIZE); 132 $read[$type] .= $data; 133 } while (isset($data[0]) && ($close || isset($data[self::CHUNK_SIZE - 1]))); 134 135 if (!isset($read[$type][0])) { 136 unset($read[$type]); 137 } 138 139 if ($close && feof($pipe)) { 140 fclose($pipe); 141 unset($this->pipes[$type]); 142 } 143 } 144 145 return $read; 146 } 147 148 /** 149 * {@inheritdoc} 150 */ 151 public function haveReadSupport(): bool 152 { 153 return $this->haveReadSupport; 154 } 155 156 /** 157 * {@inheritdoc} 158 */ 159 public function areOpen(): bool 160 { 161 return (bool) $this->pipes; 162 } 163} 164