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