xref: /plugin/combo/vendor/symfony/process/Pipes/AbstractPipes.php (revision 83c6863253ba0b92605aa8dceca974358d439aaa)
104fd306cSNickeau<?php
204fd306cSNickeau
304fd306cSNickeau/*
404fd306cSNickeau * This file is part of the Symfony package.
504fd306cSNickeau *
604fd306cSNickeau * (c) Fabien Potencier <fabien@symfony.com>
704fd306cSNickeau *
804fd306cSNickeau * For the full copyright and license information, please view the LICENSE
904fd306cSNickeau * file that was distributed with this source code.
1004fd306cSNickeau */
1104fd306cSNickeau
1204fd306cSNickeaunamespace Symfony\Component\Process\Pipes;
1304fd306cSNickeau
1404fd306cSNickeauuse Symfony\Component\Process\Exception\InvalidArgumentException;
1504fd306cSNickeau
1604fd306cSNickeau/**
1704fd306cSNickeau * @author Romain Neutron <imprec@gmail.com>
1804fd306cSNickeau *
1904fd306cSNickeau * @internal
2004fd306cSNickeau */
2104fd306cSNickeauabstract class AbstractPipes implements PipesInterface
2204fd306cSNickeau{
2304fd306cSNickeau    public $pipes = [];
2404fd306cSNickeau
2504fd306cSNickeau    private $inputBuffer = '';
2604fd306cSNickeau    private $input;
2704fd306cSNickeau    private $blocked = true;
2804fd306cSNickeau    private $lastError;
2904fd306cSNickeau
3004fd306cSNickeau    /**
3104fd306cSNickeau     * @param resource|string|int|float|bool|\Iterator|null $input
3204fd306cSNickeau     */
3304fd306cSNickeau    public function __construct($input)
3404fd306cSNickeau    {
3504fd306cSNickeau        if (\is_resource($input) || $input instanceof \Iterator) {
3604fd306cSNickeau            $this->input = $input;
3704fd306cSNickeau        } elseif (\is_string($input)) {
3804fd306cSNickeau            $this->inputBuffer = $input;
3904fd306cSNickeau        } else {
4004fd306cSNickeau            $this->inputBuffer = (string) $input;
4104fd306cSNickeau        }
4204fd306cSNickeau    }
4304fd306cSNickeau
4404fd306cSNickeau    /**
4504fd306cSNickeau     * {@inheritdoc}
4604fd306cSNickeau     */
4704fd306cSNickeau    public function close()
4804fd306cSNickeau    {
4904fd306cSNickeau        foreach ($this->pipes as $pipe) {
5004fd306cSNickeau            if (\is_resource($pipe)) {
5104fd306cSNickeau                fclose($pipe);
5204fd306cSNickeau            }
5304fd306cSNickeau        }
5404fd306cSNickeau        $this->pipes = [];
5504fd306cSNickeau    }
5604fd306cSNickeau
5704fd306cSNickeau    /**
5804fd306cSNickeau     * Returns true if a system call has been interrupted.
5904fd306cSNickeau     */
6004fd306cSNickeau    protected function hasSystemCallBeenInterrupted(): bool
6104fd306cSNickeau    {
6204fd306cSNickeau        $lastError = $this->lastError;
6304fd306cSNickeau        $this->lastError = null;
6404fd306cSNickeau
6504fd306cSNickeau        // stream_select returns false when the `select` system call is interrupted by an incoming signal
6604fd306cSNickeau        return null !== $lastError && false !== stripos($lastError, 'interrupted system call');
6704fd306cSNickeau    }
6804fd306cSNickeau
6904fd306cSNickeau    /**
7004fd306cSNickeau     * Unblocks streams.
7104fd306cSNickeau     */
7204fd306cSNickeau    protected function unblock()
7304fd306cSNickeau    {
7404fd306cSNickeau        if (!$this->blocked) {
7504fd306cSNickeau            return;
7604fd306cSNickeau        }
7704fd306cSNickeau
7804fd306cSNickeau        foreach ($this->pipes as $pipe) {
7904fd306cSNickeau            stream_set_blocking($pipe, 0);
8004fd306cSNickeau        }
8104fd306cSNickeau        if (\is_resource($this->input)) {
8204fd306cSNickeau            stream_set_blocking($this->input, 0);
8304fd306cSNickeau        }
8404fd306cSNickeau
8504fd306cSNickeau        $this->blocked = false;
8604fd306cSNickeau    }
8704fd306cSNickeau
8804fd306cSNickeau    /**
8904fd306cSNickeau     * Writes input to stdin.
9004fd306cSNickeau     *
9104fd306cSNickeau     * @throws InvalidArgumentException When an input iterator yields a non supported value
9204fd306cSNickeau     */
9304fd306cSNickeau    protected function write(): ?array
9404fd306cSNickeau    {
9504fd306cSNickeau        if (!isset($this->pipes[0])) {
9604fd306cSNickeau            return null;
9704fd306cSNickeau        }
9804fd306cSNickeau        $input = $this->input;
9904fd306cSNickeau
10004fd306cSNickeau        if ($input instanceof \Iterator) {
10104fd306cSNickeau            if (!$input->valid()) {
10204fd306cSNickeau                $input = null;
10304fd306cSNickeau            } elseif (\is_resource($input = $input->current())) {
10404fd306cSNickeau                stream_set_blocking($input, 0);
10504fd306cSNickeau            } elseif (!isset($this->inputBuffer[0])) {
10604fd306cSNickeau                if (!\is_string($input)) {
107*83c68632SNico                    if (!\is_scalar($input)) {
10804fd306cSNickeau                        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)));
10904fd306cSNickeau                    }
11004fd306cSNickeau                    $input = (string) $input;
11104fd306cSNickeau                }
11204fd306cSNickeau                $this->inputBuffer = $input;
11304fd306cSNickeau                $this->input->next();
11404fd306cSNickeau                $input = null;
11504fd306cSNickeau            } else {
11604fd306cSNickeau                $input = null;
11704fd306cSNickeau            }
11804fd306cSNickeau        }
11904fd306cSNickeau
12004fd306cSNickeau        $r = $e = [];
12104fd306cSNickeau        $w = [$this->pipes[0]];
12204fd306cSNickeau
12304fd306cSNickeau        // let's have a look if something changed in streams
12404fd306cSNickeau        if (false === @stream_select($r, $w, $e, 0, 0)) {
12504fd306cSNickeau            return null;
12604fd306cSNickeau        }
12704fd306cSNickeau
12804fd306cSNickeau        foreach ($w as $stdin) {
12904fd306cSNickeau            if (isset($this->inputBuffer[0])) {
13004fd306cSNickeau                $written = fwrite($stdin, $this->inputBuffer);
13104fd306cSNickeau                $this->inputBuffer = substr($this->inputBuffer, $written);
13204fd306cSNickeau                if (isset($this->inputBuffer[0])) {
13304fd306cSNickeau                    return [$this->pipes[0]];
13404fd306cSNickeau                }
13504fd306cSNickeau            }
13604fd306cSNickeau
13704fd306cSNickeau            if ($input) {
13804fd306cSNickeau                while (true) {
13904fd306cSNickeau                    $data = fread($input, self::CHUNK_SIZE);
14004fd306cSNickeau                    if (!isset($data[0])) {
14104fd306cSNickeau                        break;
14204fd306cSNickeau                    }
14304fd306cSNickeau                    $written = fwrite($stdin, $data);
14404fd306cSNickeau                    $data = substr($data, $written);
14504fd306cSNickeau                    if (isset($data[0])) {
14604fd306cSNickeau                        $this->inputBuffer = $data;
14704fd306cSNickeau
14804fd306cSNickeau                        return [$this->pipes[0]];
14904fd306cSNickeau                    }
15004fd306cSNickeau                }
15104fd306cSNickeau                if (feof($input)) {
15204fd306cSNickeau                    if ($this->input instanceof \Iterator) {
15304fd306cSNickeau                        $this->input->next();
15404fd306cSNickeau                    } else {
15504fd306cSNickeau                        $this->input = null;
15604fd306cSNickeau                    }
15704fd306cSNickeau                }
15804fd306cSNickeau            }
15904fd306cSNickeau        }
16004fd306cSNickeau
16104fd306cSNickeau        // no input to read on resource, buffer is empty
16204fd306cSNickeau        if (!isset($this->inputBuffer[0]) && !($this->input instanceof \Iterator ? $this->input->valid() : $this->input)) {
16304fd306cSNickeau            $this->input = null;
16404fd306cSNickeau            fclose($this->pipes[0]);
16504fd306cSNickeau            unset($this->pipes[0]);
16604fd306cSNickeau        } elseif (!$w) {
16704fd306cSNickeau            return [$this->pipes[0]];
16804fd306cSNickeau        }
16904fd306cSNickeau
17004fd306cSNickeau        return null;
17104fd306cSNickeau    }
17204fd306cSNickeau
17304fd306cSNickeau    /**
17404fd306cSNickeau     * @internal
17504fd306cSNickeau     */
17604fd306cSNickeau    public function handleError(int $type, string $msg)
17704fd306cSNickeau    {
17804fd306cSNickeau        $this->lastError = $msg;
17904fd306cSNickeau    }
18004fd306cSNickeau}
181