Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Process\Pipes; Chris@0: Chris@0: use Symfony\Component\Process\Exception\InvalidArgumentException; Chris@0: Chris@0: /** Chris@0: * @author Romain Neutron Chris@0: * Chris@0: * @internal Chris@0: */ Chris@0: abstract class AbstractPipes implements PipesInterface Chris@0: { Chris@17: public $pipes = []; Chris@0: Chris@0: private $inputBuffer = ''; Chris@0: private $input; Chris@0: private $blocked = true; Chris@16: private $lastError; Chris@0: Chris@14: /** Chris@14: * @param resource|string|int|float|bool|\Iterator|null $input Chris@14: */ Chris@0: public function __construct($input) Chris@0: { Chris@17: if (\is_resource($input) || $input instanceof \Iterator) { Chris@0: $this->input = $input; Chris@17: } elseif (\is_string($input)) { Chris@0: $this->inputBuffer = $input; Chris@0: } else { Chris@0: $this->inputBuffer = (string) $input; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function close() Chris@0: { Chris@0: foreach ($this->pipes as $pipe) { Chris@0: fclose($pipe); Chris@0: } Chris@17: $this->pipes = []; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns true if a system call has been interrupted. Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@0: protected function hasSystemCallBeenInterrupted() Chris@0: { Chris@16: $lastError = $this->lastError; Chris@16: $this->lastError = null; Chris@0: Chris@0: // stream_select returns false when the `select` system call is interrupted by an incoming signal Chris@16: return null !== $lastError && false !== stripos($lastError, 'interrupted system call'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Unblocks streams. Chris@0: */ Chris@0: protected function unblock() Chris@0: { Chris@0: if (!$this->blocked) { Chris@0: return; Chris@0: } Chris@0: Chris@0: foreach ($this->pipes as $pipe) { Chris@0: stream_set_blocking($pipe, 0); Chris@0: } Chris@17: if (\is_resource($this->input)) { Chris@0: stream_set_blocking($this->input, 0); Chris@0: } Chris@0: Chris@0: $this->blocked = false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Writes input to stdin. Chris@0: * Chris@0: * @throws InvalidArgumentException When an input iterator yields a non supported value Chris@0: */ Chris@0: protected function write() Chris@0: { Chris@0: if (!isset($this->pipes[0])) { Chris@0: return; Chris@0: } Chris@0: $input = $this->input; Chris@0: Chris@0: if ($input instanceof \Iterator) { Chris@0: if (!$input->valid()) { Chris@0: $input = null; Chris@17: } elseif (\is_resource($input = $input->current())) { Chris@0: stream_set_blocking($input, 0); Chris@0: } elseif (!isset($this->inputBuffer[0])) { Chris@17: if (!\is_string($input)) { Chris@0: if (!is_scalar($input)) { Chris@17: throw new InvalidArgumentException(sprintf('%s yielded a value of type "%s", but only scalars and stream resources are supported', \get_class($this->input), \gettype($input))); Chris@0: } Chris@0: $input = (string) $input; Chris@0: } Chris@0: $this->inputBuffer = $input; Chris@0: $this->input->next(); Chris@0: $input = null; Chris@0: } else { Chris@0: $input = null; Chris@0: } Chris@0: } Chris@0: Chris@17: $r = $e = []; Chris@17: $w = [$this->pipes[0]]; Chris@0: Chris@0: // let's have a look if something changed in streams Chris@14: if (false === @stream_select($r, $w, $e, 0, 0)) { Chris@0: return; Chris@0: } Chris@0: Chris@0: foreach ($w as $stdin) { Chris@0: if (isset($this->inputBuffer[0])) { Chris@0: $written = fwrite($stdin, $this->inputBuffer); Chris@0: $this->inputBuffer = substr($this->inputBuffer, $written); Chris@0: if (isset($this->inputBuffer[0])) { Chris@17: return [$this->pipes[0]]; Chris@0: } Chris@0: } Chris@0: Chris@0: if ($input) { Chris@0: for (;;) { Chris@0: $data = fread($input, self::CHUNK_SIZE); Chris@0: if (!isset($data[0])) { Chris@0: break; Chris@0: } Chris@0: $written = fwrite($stdin, $data); Chris@0: $data = substr($data, $written); Chris@0: if (isset($data[0])) { Chris@0: $this->inputBuffer = $data; Chris@0: Chris@17: return [$this->pipes[0]]; Chris@0: } Chris@0: } Chris@0: if (feof($input)) { Chris@0: if ($this->input instanceof \Iterator) { Chris@0: $this->input->next(); Chris@0: } else { Chris@0: $this->input = null; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // no input to read on resource, buffer is empty Chris@0: if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) { Chris@0: $this->input = null; Chris@0: fclose($this->pipes[0]); Chris@0: unset($this->pipes[0]); Chris@0: } elseif (!$w) { Chris@17: return [$this->pipes[0]]; Chris@0: } Chris@0: } Chris@16: Chris@16: /** Chris@16: * @internal Chris@16: */ Chris@16: public function handleError($type, $msg) Chris@16: { Chris@16: $this->lastError = $msg; Chris@16: } Chris@0: }