xref: /plugin/combo/vendor/symfony/process/Pipes/WindowsPipes.php (revision 83c6863253ba0b92605aa8dceca974358d439aaa)
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