104fd306cSNickeau<?php 204fd306cSNickeau 304fd306cSNickeau/* 404fd306cSNickeau * This file is part of the Symfony package. 504fd306cSNickeau * 604fd306cSNickeau * (c) Fabien Potencier <fabien@symfony.com> 704fd306cSNickeau * 804fd306cSNickeau * For the full copyright and license information, please view the LICENSE 904fd306cSNickeau * file that was distributed with this source code. 1004fd306cSNickeau */ 1104fd306cSNickeau 1204fd306cSNickeaunamespace Symfony\Component\Process\Pipes; 1304fd306cSNickeau 1404fd306cSNickeauuse Symfony\Component\Process\Exception\RuntimeException; 1504fd306cSNickeauuse Symfony\Component\Process\Process; 1604fd306cSNickeau 1704fd306cSNickeau/** 1804fd306cSNickeau * WindowsPipes implementation uses temporary files as handles. 1904fd306cSNickeau * 2004fd306cSNickeau * @see https://bugs.php.net/51800 2104fd306cSNickeau * @see https://bugs.php.net/65650 2204fd306cSNickeau * 2304fd306cSNickeau * @author Romain Neutron <imprec@gmail.com> 2404fd306cSNickeau * 2504fd306cSNickeau * @internal 2604fd306cSNickeau */ 2704fd306cSNickeauclass WindowsPipes extends AbstractPipes 2804fd306cSNickeau{ 2904fd306cSNickeau private $files = []; 3004fd306cSNickeau private $fileHandles = []; 3104fd306cSNickeau private $lockHandles = []; 3204fd306cSNickeau private $readBytes = [ 3304fd306cSNickeau Process::STDOUT => 0, 3404fd306cSNickeau Process::STDERR => 0, 3504fd306cSNickeau ]; 3604fd306cSNickeau private $haveReadSupport; 3704fd306cSNickeau 3804fd306cSNickeau public function __construct($input, bool $haveReadSupport) 3904fd306cSNickeau { 4004fd306cSNickeau $this->haveReadSupport = $haveReadSupport; 4104fd306cSNickeau 4204fd306cSNickeau if ($this->haveReadSupport) { 4304fd306cSNickeau // Fix for PHP bug #51800: reading from STDOUT pipe hangs forever on Windows if the output is too big. 4404fd306cSNickeau // Workaround for this problem is to use temporary files instead of pipes on Windows platform. 4504fd306cSNickeau // 4604fd306cSNickeau // @see https://bugs.php.net/51800 4704fd306cSNickeau $pipes = [ 4804fd306cSNickeau Process::STDOUT => Process::OUT, 4904fd306cSNickeau Process::STDERR => Process::ERR, 5004fd306cSNickeau ]; 5104fd306cSNickeau $tmpDir = sys_get_temp_dir(); 5204fd306cSNickeau $lastError = 'unknown reason'; 5304fd306cSNickeau set_error_handler(function ($type, $msg) use (&$lastError) { $lastError = $msg; }); 5404fd306cSNickeau for ($i = 0;; ++$i) { 5504fd306cSNickeau foreach ($pipes as $pipe => $name) { 5604fd306cSNickeau $file = sprintf('%s\\sf_proc_%02X.%s', $tmpDir, $i, $name); 5704fd306cSNickeau 5804fd306cSNickeau if (!$h = fopen($file.'.lock', 'w')) { 5904fd306cSNickeau if (file_exists($file.'.lock')) { 6004fd306cSNickeau continue 2; 6104fd306cSNickeau } 6204fd306cSNickeau restore_error_handler(); 6304fd306cSNickeau throw new RuntimeException('A temporary file could not be opened to write the process output: '.$lastError); 6404fd306cSNickeau } 6504fd306cSNickeau if (!flock($h, \LOCK_EX | \LOCK_NB)) { 6604fd306cSNickeau continue 2; 6704fd306cSNickeau } 6804fd306cSNickeau if (isset($this->lockHandles[$pipe])) { 6904fd306cSNickeau flock($this->lockHandles[$pipe], \LOCK_UN); 7004fd306cSNickeau fclose($this->lockHandles[$pipe]); 7104fd306cSNickeau } 7204fd306cSNickeau $this->lockHandles[$pipe] = $h; 7304fd306cSNickeau 7404fd306cSNickeau if (!($h = fopen($file, 'w')) || !fclose($h) || !$h = fopen($file, 'r')) { 7504fd306cSNickeau flock($this->lockHandles[$pipe], \LOCK_UN); 7604fd306cSNickeau fclose($this->lockHandles[$pipe]); 7704fd306cSNickeau unset($this->lockHandles[$pipe]); 7804fd306cSNickeau continue 2; 7904fd306cSNickeau } 8004fd306cSNickeau $this->fileHandles[$pipe] = $h; 8104fd306cSNickeau $this->files[$pipe] = $file; 8204fd306cSNickeau } 8304fd306cSNickeau break; 8404fd306cSNickeau } 8504fd306cSNickeau restore_error_handler(); 8604fd306cSNickeau } 8704fd306cSNickeau 8804fd306cSNickeau parent::__construct($input); 8904fd306cSNickeau } 9004fd306cSNickeau 9104fd306cSNickeau public function __sleep(): array 9204fd306cSNickeau { 9304fd306cSNickeau throw new \BadMethodCallException('Cannot serialize '.__CLASS__); 9404fd306cSNickeau } 9504fd306cSNickeau 9604fd306cSNickeau public function __wakeup() 9704fd306cSNickeau { 9804fd306cSNickeau throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); 9904fd306cSNickeau } 10004fd306cSNickeau 10104fd306cSNickeau public function __destruct() 10204fd306cSNickeau { 10304fd306cSNickeau $this->close(); 10404fd306cSNickeau } 10504fd306cSNickeau 10604fd306cSNickeau /** 10704fd306cSNickeau * {@inheritdoc} 10804fd306cSNickeau */ 10904fd306cSNickeau public function getDescriptors(): array 11004fd306cSNickeau { 11104fd306cSNickeau if (!$this->haveReadSupport) { 11204fd306cSNickeau $nullstream = fopen('NUL', 'c'); 11304fd306cSNickeau 11404fd306cSNickeau return [ 11504fd306cSNickeau ['pipe', 'r'], 11604fd306cSNickeau $nullstream, 11704fd306cSNickeau $nullstream, 11804fd306cSNickeau ]; 11904fd306cSNickeau } 12004fd306cSNickeau 12104fd306cSNickeau // We're not using pipe on Windows platform as it hangs (https://bugs.php.net/51800) 12204fd306cSNickeau // We're not using file handles as it can produce corrupted output https://bugs.php.net/65650 12304fd306cSNickeau // So we redirect output within the commandline and pass the nul device to the process 12404fd306cSNickeau return [ 12504fd306cSNickeau ['pipe', 'r'], 12604fd306cSNickeau ['file', 'NUL', 'w'], 12704fd306cSNickeau ['file', 'NUL', 'w'], 12804fd306cSNickeau ]; 12904fd306cSNickeau } 13004fd306cSNickeau 13104fd306cSNickeau /** 13204fd306cSNickeau * {@inheritdoc} 13304fd306cSNickeau */ 13404fd306cSNickeau public function getFiles(): array 13504fd306cSNickeau { 13604fd306cSNickeau return $this->files; 13704fd306cSNickeau } 13804fd306cSNickeau 13904fd306cSNickeau /** 14004fd306cSNickeau * {@inheritdoc} 14104fd306cSNickeau */ 14204fd306cSNickeau public function readAndWrite(bool $blocking, bool $close = false): array 14304fd306cSNickeau { 14404fd306cSNickeau $this->unblock(); 14504fd306cSNickeau $w = $this->write(); 14604fd306cSNickeau $read = $r = $e = []; 14704fd306cSNickeau 14804fd306cSNickeau if ($blocking) { 14904fd306cSNickeau if ($w) { 15004fd306cSNickeau @stream_select($r, $w, $e, 0, Process::TIMEOUT_PRECISION * 1E6); 15104fd306cSNickeau } elseif ($this->fileHandles) { 152*83c68632SNico usleep((int) (Process::TIMEOUT_PRECISION * 1E6)); 15304fd306cSNickeau } 15404fd306cSNickeau } 15504fd306cSNickeau foreach ($this->fileHandles as $type => $fileHandle) { 15604fd306cSNickeau $data = stream_get_contents($fileHandle, -1, $this->readBytes[$type]); 15704fd306cSNickeau 15804fd306cSNickeau if (isset($data[0])) { 15904fd306cSNickeau $this->readBytes[$type] += \strlen($data); 16004fd306cSNickeau $read[$type] = $data; 16104fd306cSNickeau } 16204fd306cSNickeau if ($close) { 16304fd306cSNickeau ftruncate($fileHandle, 0); 16404fd306cSNickeau fclose($fileHandle); 16504fd306cSNickeau flock($this->lockHandles[$type], \LOCK_UN); 16604fd306cSNickeau fclose($this->lockHandles[$type]); 16704fd306cSNickeau unset($this->fileHandles[$type], $this->lockHandles[$type]); 16804fd306cSNickeau } 16904fd306cSNickeau } 17004fd306cSNickeau 17104fd306cSNickeau return $read; 17204fd306cSNickeau } 17304fd306cSNickeau 17404fd306cSNickeau /** 17504fd306cSNickeau * {@inheritdoc} 17604fd306cSNickeau */ 17704fd306cSNickeau public function haveReadSupport(): bool 17804fd306cSNickeau { 17904fd306cSNickeau return $this->haveReadSupport; 18004fd306cSNickeau } 18104fd306cSNickeau 18204fd306cSNickeau /** 18304fd306cSNickeau * {@inheritdoc} 18404fd306cSNickeau */ 18504fd306cSNickeau public function areOpen(): bool 18604fd306cSNickeau { 18704fd306cSNickeau return $this->pipes && $this->fileHandles; 18804fd306cSNickeau } 18904fd306cSNickeau 19004fd306cSNickeau /** 19104fd306cSNickeau * {@inheritdoc} 19204fd306cSNickeau */ 19304fd306cSNickeau public function close() 19404fd306cSNickeau { 19504fd306cSNickeau parent::close(); 19604fd306cSNickeau foreach ($this->fileHandles as $type => $handle) { 19704fd306cSNickeau ftruncate($handle, 0); 19804fd306cSNickeau fclose($handle); 19904fd306cSNickeau flock($this->lockHandles[$type], \LOCK_UN); 20004fd306cSNickeau fclose($this->lockHandles[$type]); 20104fd306cSNickeau } 20204fd306cSNickeau $this->fileHandles = $this->lockHandles = []; 20304fd306cSNickeau } 20404fd306cSNickeau} 205