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