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