scratch – Blame information for rev
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
120 | office | 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 | |||
12 | namespace Symfony\Component\Process\Pipes; |
||
13 | |||
14 | use Symfony\Component\Process\Process; |
||
15 | use Symfony\Component\Process\Exception\RuntimeException; |
||
16 | |||
17 | /** |
||
18 | * WindowsPipes implementation uses temporary files as handles. |
||
19 | * |
||
20 | * @see https://bugs.php.net/bug.php?id=51800 |
||
21 | * @see https://bugs.php.net/bug.php?id=65650 |
||
22 | * |
||
23 | * @author Romain Neutron <imprec@gmail.com> |
||
24 | * |
||
25 | * @internal |
||
26 | */ |
||
27 | class WindowsPipes extends AbstractPipes |
||
28 | { |
||
29 | /** @var array */ |
||
30 | private $files = array(); |
||
31 | /** @var array */ |
||
32 | private $fileHandles = array(); |
||
33 | /** @var array */ |
||
34 | private $readBytes = array( |
||
35 | Process::STDOUT => 0, |
||
36 | Process::STDERR => 0, |
||
37 | ); |
||
38 | /** @var bool */ |
||
39 | private $haveReadSupport; |
||
40 | |||
41 | public function __construct($input, $haveReadSupport) |
||
42 | { |
||
43 | $this->haveReadSupport = (bool) $haveReadSupport; |
||
44 | |||
45 | if ($this->haveReadSupport) { |
||
46 | // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. |
||
47 | // Workaround for this problem is to use temporary files instead of pipes on Windows platform. |
||
48 | // |
||
49 | // @see https://bugs.php.net/bug.php?id=51800 |
||
50 | $pipes = array( |
||
51 | Process::STDOUT => Process::OUT, |
||
52 | Process::STDERR => Process::ERR, |
||
53 | ); |
||
54 | $tmpCheck = false; |
||
55 | $tmpDir = sys_get_temp_dir(); |
||
56 | $lastError = 'unknown reason'; |
||
57 | set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); |
||
58 | for ($i = 0;; ++$i) { |
||
59 | foreach ($pipes as $pipe => $name) { |
||
60 | $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); |
||
61 | if (file_exists($file) && !unlink($file)) { |
||
62 | continue 2; |
||
63 | } |
||
64 | $h = fopen($file, 'xb'); |
||
65 | if (!$h) { |
||
66 | $error = $lastError; |
||
67 | if ($tmpCheck || $tmpCheck = unlink(tempnam(false, 'sf_check_'))) { |
||
68 | continue; |
||
69 | } |
||
70 | restore_error_handler(); |
||
71 | throw new RuntimeException(sprintf('A temporary file could not be opened to write the process output: %s', $error)); |
||
72 | } |
||
73 | if (!$h || !$this->fileHandles[$pipe] = fopen($file, 'rb')) { |
||
74 | continue 2; |
||
75 | } |
||
76 | if (isset($this->files[$pipe])) { |
||
77 | unlink($this->files[$pipe]); |
||
78 | } |
||
79 | $this->files[$pipe] = $file; |
||
80 | } |
||
81 | break; |
||
82 | } |
||
83 | restore_error_handler(); |
||
84 | } |
||
85 | |||
86 | parent::__construct($input); |
||
87 | } |
||
88 | |||
89 | public function __destruct() |
||
90 | { |
||
91 | $this->close(); |
||
92 | $this->removeFiles(); |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * {@inheritdoc} |
||
97 | */ |
||
98 | public function getDescriptors() |
||
99 | { |
||
100 | if (!$this->haveReadSupport) { |
||
101 | $nullstream = fopen('NUL', 'c'); |
||
102 | |||
103 | return array( |
||
104 | array('pipe', 'r'), |
||
105 | $nullstream, |
||
106 | $nullstream, |
||
107 | ); |
||
108 | } |
||
109 | |||
110 | // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/bug.php?id=51800) |
||
111 | // We're not using file handles as it can produce corrupted output https://bugs.php.net/bug.php?id=65650 |
||
112 | // So we redirect output within the commandline and pass the nul device to the process |
||
113 | return array( |
||
114 | array('pipe', 'r'), |
||
115 | array('file', 'NUL', 'w'), |
||
116 | array('file', 'NUL', 'w'), |
||
117 | ); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * {@inheritdoc} |
||
122 | */ |
||
123 | public function getFiles() |
||
124 | { |
||
125 | return $this->files; |
||
126 | } |
||
127 | |||
128 | /** |
||
129 | * {@inheritdoc} |
||
130 | */ |
||
131 | public function readAndWrite($blocking, $close = false) |
||
132 | { |
||
133 | $this->unblock(); |
||
134 | $w = $this->write(); |
||
135 | $read = $r = $e = array(); |
||
136 | |||
137 | if ($blocking) { |
||
138 | if ($w) { |
||
139 | @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6); |
||
140 | } elseif ($this->fileHandles) { |
||
141 | usleep(Process::TIMEOUT_PRECISION * 1E6); |
||
142 | } |
||
143 | } |
||
144 | foreach ($this->fileHandles as $type => $fileHandle) { |
||
145 | $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]); |
||
146 | |||
147 | if (isset($data[0])) { |
||
148 | $this->readBytes[$type] += strlen($data); |
||
149 | $read[$type] = $data; |
||
150 | } |
||
151 | if ($close) { |
||
152 | fclose($fileHandle); |
||
153 | unset($this->fileHandles[$type]); |
||
154 | } |
||
155 | } |
||
156 | |||
157 | return $read; |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * {@inheritdoc} |
||
162 | */ |
||
163 | public function haveReadSupport() |
||
164 | { |
||
165 | return $this->haveReadSupport; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * {@inheritdoc} |
||
170 | */ |
||
171 | public function areOpen() |
||
172 | { |
||
173 | return $this->pipes && $this->fileHandles; |
||
174 | } |
||
175 | |||
176 | /** |
||
177 | * {@inheritdoc} |
||
178 | */ |
||
179 | public function close() |
||
180 | { |
||
181 | parent::close(); |
||
182 | foreach ($this->fileHandles as $handle) { |
||
183 | fclose($handle); |
||
184 | } |
||
185 | $this->fileHandles = array(); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Removes temporary files. |
||
190 | */ |
||
191 | private function removeFiles() |
||
192 | { |
||
193 | foreach ($this->files as $filename) { |
||
194 | if (file_exists($filename)) { |
||
195 | @unlink($filename); |
||
196 | } |
||
197 | } |
||
198 | $this->files = array(); |
||
199 | } |
||
200 | } |