xref: /plugin/combo/vendor/symfony/process/Pipes/AbstractPipes.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
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