1*04fd306cSNickeau<?php 2*04fd306cSNickeau 3*04fd306cSNickeau/* 4*04fd306cSNickeau * This file is part of the Symfony package. 5*04fd306cSNickeau * 6*04fd306cSNickeau * (c) Fabien Potencier <fabien@symfony.com> 7*04fd306cSNickeau * 8*04fd306cSNickeau * For the full copyright and license information, please view the LICENSE 9*04fd306cSNickeau * file that was distributed with this source code. 10*04fd306cSNickeau */ 11*04fd306cSNickeau 12*04fd306cSNickeaunamespace Symfony\Component\Process\Pipes; 13*04fd306cSNickeau 14*04fd306cSNickeauuse Symfony\Component\Process\Exception\InvalidArgumentException; 15*04fd306cSNickeau 16*04fd306cSNickeau/** 17*04fd306cSNickeau * @author Romain Neutron <imprec@gmail.com> 18*04fd306cSNickeau * 19*04fd306cSNickeau * @internal 20*04fd306cSNickeau */ 21*04fd306cSNickeauabstract class AbstractPipes implements PipesInterface 22*04fd306cSNickeau{ 23*04fd306cSNickeau public $pipes = []; 24*04fd306cSNickeau 25*04fd306cSNickeau private $inputBuffer = ''; 26*04fd306cSNickeau private $input; 27*04fd306cSNickeau private $blocked = true; 28*04fd306cSNickeau private $lastError; 29*04fd306cSNickeau 30*04fd306cSNickeau /** 31*04fd306cSNickeau * @param resource|string|int|float|bool|\Iterator|null $input 32*04fd306cSNickeau */ 33*04fd306cSNickeau public function __construct($input) 34*04fd306cSNickeau { 35*04fd306cSNickeau if (\is_resource($input) || $input instanceof \Iterator) { 36*04fd306cSNickeau $this->input = $input; 37*04fd306cSNickeau } elseif (\is_string($input)) { 38*04fd306cSNickeau $this->inputBuffer = $input; 39*04fd306cSNickeau } else { 40*04fd306cSNickeau $this->inputBuffer = (string) $input; 41*04fd306cSNickeau } 42*04fd306cSNickeau } 43*04fd306cSNickeau 44*04fd306cSNickeau /** 45*04fd306cSNickeau * {@inheritdoc} 46*04fd306cSNickeau */ 47*04fd306cSNickeau public function close() 48*04fd306cSNickeau { 49*04fd306cSNickeau foreach ($this->pipes as $pipe) { 50*04fd306cSNickeau if (\is_resource($pipe)) { 51*04fd306cSNickeau fclose($pipe); 52*04fd306cSNickeau } 53*04fd306cSNickeau } 54*04fd306cSNickeau $this->pipes = []; 55*04fd306cSNickeau } 56*04fd306cSNickeau 57*04fd306cSNickeau /** 58*04fd306cSNickeau * Returns true if a system call has been interrupted. 59*04fd306cSNickeau */ 60*04fd306cSNickeau protected function hasSystemCallBeenInterrupted(): bool 61*04fd306cSNickeau { 62*04fd306cSNickeau $lastError = $this->lastError; 63*04fd306cSNickeau $this->lastError = null; 64*04fd306cSNickeau 65*04fd306cSNickeau // stream_select returns false when the `select` system call is interrupted by an incoming signal 66*04fd306cSNickeau return null !== $lastError && false !== stripos($lastError, 'interrupted system call'); 67*04fd306cSNickeau } 68*04fd306cSNickeau 69*04fd306cSNickeau /** 70*04fd306cSNickeau * Unblocks streams. 71*04fd306cSNickeau */ 72*04fd306cSNickeau protected function unblock() 73*04fd306cSNickeau { 74*04fd306cSNickeau if (!$this->blocked) { 75*04fd306cSNickeau return; 76*04fd306cSNickeau } 77*04fd306cSNickeau 78*04fd306cSNickeau foreach ($this->pipes as $pipe) { 79*04fd306cSNickeau stream_set_blocking($pipe, 0); 80*04fd306cSNickeau } 81*04fd306cSNickeau if (\is_resource($this->input)) { 82*04fd306cSNickeau stream_set_blocking($this->input, 0); 83*04fd306cSNickeau } 84*04fd306cSNickeau 85*04fd306cSNickeau $this->blocked = false; 86*04fd306cSNickeau } 87*04fd306cSNickeau 88*04fd306cSNickeau /** 89*04fd306cSNickeau * Writes input to stdin. 90*04fd306cSNickeau * 91*04fd306cSNickeau * @throws InvalidArgumentException When an input iterator yields a non supported value 92*04fd306cSNickeau */ 93*04fd306cSNickeau protected function write(): ?array 94*04fd306cSNickeau { 95*04fd306cSNickeau if (!isset($this->pipes[0])) { 96*04fd306cSNickeau return null; 97*04fd306cSNickeau } 98*04fd306cSNickeau $input = $this->input; 99*04fd306cSNickeau 100*04fd306cSNickeau if ($input instanceof \Iterator) { 101*04fd306cSNickeau if (!$input->valid()) { 102*04fd306cSNickeau $input = null; 103*04fd306cSNickeau } elseif (\is_resource($input = $input->current())) { 104*04fd306cSNickeau stream_set_blocking($input, 0); 105*04fd306cSNickeau } elseif (!isset($this->inputBuffer[0])) { 106*04fd306cSNickeau if (!\is_string($input)) { 107*04fd306cSNickeau if (!is_scalar($input)) { 108*04fd306cSNickeau throw new InvalidArgumentException(sprintf('"%s" yielded a value of type "%s", but only scalars and stream resources are supported.', get_debug_type($this->input), get_debug_type($input))); 109*04fd306cSNickeau } 110*04fd306cSNickeau $input = (string) $input; 111*04fd306cSNickeau } 112*04fd306cSNickeau $this->inputBuffer = $input; 113*04fd306cSNickeau $this->input->next(); 114*04fd306cSNickeau $input = null; 115*04fd306cSNickeau } else { 116*04fd306cSNickeau $input = null; 117*04fd306cSNickeau } 118*04fd306cSNickeau } 119*04fd306cSNickeau 120*04fd306cSNickeau $r = $e = []; 121*04fd306cSNickeau $w = [$this->pipes[0]]; 122*04fd306cSNickeau 123*04fd306cSNickeau // let's have a look if something changed in streams 124*04fd306cSNickeau if (false === @stream_select($r, $w, $e, 0, 0)) { 125*04fd306cSNickeau return null; 126*04fd306cSNickeau } 127*04fd306cSNickeau 128*04fd306cSNickeau foreach ($w as $stdin) { 129*04fd306cSNickeau if (isset($this->inputBuffer[0])) { 130*04fd306cSNickeau $written = fwrite($stdin, $this->inputBuffer); 131*04fd306cSNickeau $this->inputBuffer = substr($this->inputBuffer, $written); 132*04fd306cSNickeau if (isset($this->inputBuffer[0])) { 133*04fd306cSNickeau return [$this->pipes[0]]; 134*04fd306cSNickeau } 135*04fd306cSNickeau } 136*04fd306cSNickeau 137*04fd306cSNickeau if ($input) { 138*04fd306cSNickeau while (true) { 139*04fd306cSNickeau $data = fread($input, self::CHUNK_SIZE); 140*04fd306cSNickeau if (!isset($data[0])) { 141*04fd306cSNickeau break; 142*04fd306cSNickeau } 143*04fd306cSNickeau $written = fwrite($stdin, $data); 144*04fd306cSNickeau $data = substr($data, $written); 145*04fd306cSNickeau if (isset($data[0])) { 146*04fd306cSNickeau $this->inputBuffer = $data; 147*04fd306cSNickeau 148*04fd306cSNickeau return [$this->pipes[0]]; 149*04fd306cSNickeau } 150*04fd306cSNickeau } 151*04fd306cSNickeau if (feof($input)) { 152*04fd306cSNickeau if ($this->input instanceof \Iterator) { 153*04fd306cSNickeau $this->input->next(); 154*04fd306cSNickeau } else { 155*04fd306cSNickeau $this->input = null; 156*04fd306cSNickeau } 157*04fd306cSNickeau } 158*04fd306cSNickeau } 159*04fd306cSNickeau } 160*04fd306cSNickeau 161*04fd306cSNickeau // no input to read on resource, buffer is empty 162*04fd306cSNickeau if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) { 163*04fd306cSNickeau $this->input = null; 164*04fd306cSNickeau fclose($this->pipes[0]); 165*04fd306cSNickeau unset($this->pipes[0]); 166*04fd306cSNickeau } elseif (!$w) { 167*04fd306cSNickeau return [$this->pipes[0]]; 168*04fd306cSNickeau } 169*04fd306cSNickeau 170*04fd306cSNickeau return null; 171*04fd306cSNickeau } 172*04fd306cSNickeau 173*04fd306cSNickeau /** 174*04fd306cSNickeau * @internal 175*04fd306cSNickeau */ 176*04fd306cSNickeau public function handleError(int $type, string $msg) 177*04fd306cSNickeau { 178*04fd306cSNickeau $this->lastError = $msg; 179*04fd306cSNickeau } 180*04fd306cSNickeau} 181