Вход Регистрация
Файл: vendor/psy/psysh/src/Readline/Hoa/ConsoleProcessus.php
Строк: 728
<?php

/**
 * Hoa
 *
 *
 * @license
 *
 * New BSD License
 *
 * Copyright © 2007-2017, Hoa community. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Hoa nor the names of its contributors may be
 *       used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

namespace PsyReadlineHoa;

/**
 * Manipulate a processus as a stream.
 */
class ConsoleProcessus extends Stream implements StreamInStreamOutStreamPathable
{
    
/**
     * Signal: terminal line hangup (terminate process).
     */
    
const SIGHUP 1;

    
/**
     * Signal: interrupt program (terminate process).
     */
    
const SIGINT 2;

    
/**
     * Signal: quit program (create core image).
     */
    
const SIGQUIT 3;

    
/**
     * Signal: illegal instruction (create core image).
     */
    
const SIGILL 4;

    
/**
     * Signal: trace trap (create core image).
     */
    
const SIGTRAP 5;

    
/**
     * Signal: abort program, formerly SIGIOT (create core image).
     */
    
const SIGABRT 6;

    
/**
     * Signal: emulate instruction executed (create core image).
     */
    
const SIGEMT 7;

    
/**
     * Signal: floating-point exception (create core image).
     */
    
const SIGFPE 8;

    
/**
     * Signal: kill program (terminate process).
     */
    
const SIGKILL 9;

    
/**
     * Signal: bus error.
     */
    
const SIGBUS 10;

    
/**
     * Signal: segmentation violation (create core image).
     */
    
const SIGSEGV 11;

    
/**
     * Signal: non-existent system call invoked (create core image).
     */
    
const SIGSYS 12;

    
/**
     * Signal: write on a pipe with no reader (terminate process).
     */
    
const SIGPIPE 13;

    
/**
     * Signal: real-time timer expired (terminate process).
     */
    
const SIGALRM 14;

    
/**
     * Signal: software termination signal (terminate process).
     */
    
const SIGTERM 15;

    
/**
     * Signal: urgent condition present on socket (discard signal).
     */
    
const SIGURG 16;

    
/**
     * Signal: stop, cannot be caught or ignored  (stop proces).
     */
    
const SIGSTOP 17;

    
/**
     * Signal: stop signal generated from keyboard (stop process).
     */
    
const SIGTSTP 18;

    
/**
     * Signal: continue after stop (discard signal).
     */
    
const SIGCONT 19;

    
/**
     * Signal: child status has changed (discard signal).
     */
    
const SIGCHLD 20;

    
/**
     * Signal: background read attempted from control terminal (stop process).
     */
    
const SIGTTIN 21;

    
/**
     * Signal: background write attempted to control terminal (stop process).
     */
    
const SIGTTOU 22;

    
/**
     * Signal: I/O is possible on a descriptor, see fcntl(2) (discard signal).
     */
    
const SIGIO 23;

    
/**
     * Signal: cpu time limit exceeded, see setrlimit(2) (terminate process).
     */
    
const SIGXCPU 24;

    
/**
     * Signal: file size limit exceeded, see setrlimit(2) (terminate process).
     */
    
const SIGXFSZ 25;

    
/**
     * Signal: virtual time alarm, see setitimer(2) (terminate process).
     */
    
const SIGVTALRM 26;

    
/**
     * Signal: profiling timer alarm, see setitimer(2) (terminate process).
     */
    
const SIGPROF 27;

    
/**
     * Signal: Window size change (discard signal).
     */
    
const SIGWINCH 28;

    
/**
     * Signal: status request from keyboard (discard signal).
     */
    
const SIGINFO 29;

    
/**
     * Signal: User defined signal 1 (terminate process).
     */
    
const SIGUSR1 30;

    
/**
     * Signal: User defined signal 2 (terminate process).
     */
    
const SIGUSR2 31;

    
/**
     * Command name.
     */
    
protected $_command null;

    
/**
     * Command options (options => value, or input).
     */
    
protected $_options = [];

    
/**
     * Current working directory.
     */
    
protected $_cwd null;

    
/**
     * Environment.
     */
    
protected $_environment null;

    
/**
     * Timeout.
     */
    
protected $_timeout 30;

    
/**
     * Descriptor.
     */
    
protected $_descriptors = [
        
=> ['pipe''r'],
        
=> ['pipe''w'],
        
=> ['pipe''w'],
    ];

    
/**
     * Pipe descriptors of the processus.
     */
    
protected $_pipes null;

    
/**
     * Seekability of pipes.
     */
    
protected $_seekable = [];

    
/**
     * Start a processus.
     */
    
public function __construct(
        
string $command,
        array 
$options null,
        array 
$descriptors null,
        
string $cwd null,
        array 
$environment null,
        
int $timeout 30
    
) {
        
$this->setCommand($command);

        if (
null !== $options) {
            
$this->setOptions($options);
        }

        if (
null !== $descriptors) {
            
$this->_descriptors = [];

            foreach (
$descriptors as $descriptor => $nature) {
                if (isset(
$this->_descriptors[$descriptor])) {
                    throw new 
ConsoleException('Pipe descriptor %d already exists, cannot '.'redefine it.'0$descriptor);
                }

                
$this->_descriptors[$descriptor] = $nature;
            }
        }

        
$this->setCwd($cwd ?: getcwd());

        if (
null !== $environment) {
            
$this->setEnvironment($environment);
        }

        
$this->setTimeout($timeout);
        
parent::__construct($this->getCommandLine(), nulltrue);
        
$this->getListener()->addIds(['input''output''timeout''start''stop']);

        return;
    }

    
/**
     * Open the stream and return the associated resource.
     */
    
protected function &_open(string $streamNameStreamContext $context null)
    {
        
$out = @proc_open(
            
$streamName,
            
$this->_descriptors,
            
$this->_pipes,
            
$this->getCwd(),
            
$this->getEnvironment()
        );

        if (
false === $out) {
            throw new 
ConsoleException('Something wrong happen when running %s.'1$streamName);
        }

        return 
$out;
    }

    
/**
     * Close the current stream.
     */
    
protected function _close(): bool
    
{
        foreach (
$this->_pipes as $pipe) {
            @
fclose($pipe);
        }

        return (bool) @
proc_close($this->getStream());
    }

    
/**
     * Run the process and fire events (amongst start, stop, input, output and
     * timeout).
     * If an event returns false, it will close the current pipe.
     * For a simple run without firing events, use the $this->open() method.
     */
    
public function run()
    {
        if (
false === $this->isOpened()) {
            
$this->open();
        } else {
            
$this->_close();
            
$this->_setStream($this->_open(
                
$this->getStreamName(),
                
$this->getStreamContext()
            ));
        }

        
$this->getListener()->fire('start', new EventBucket());

        
$_read = [];
        
$_write = [];
        
$_except = [];

        foreach (
$this->_pipes as $p => $pipe) {
            switch (
$this->_descriptors[$p][1]) {
                case 
'r':
                    
stream_set_blocking($pipefalse);
                    
$_write[] = $pipe;

                    break;

                case 
'w':
                case 
'a':
                    
stream_set_blocking($pipetrue);
                    
$_read[] = $pipe;

                    break;
            }
        }

        while (
true) {
            foreach (
$_read as $i => $r) {
                if (
false === is_resource($r)) {
                    unset(
$_read[$i]);
                }
            }

            foreach (
$_write as $i => $w) {
                if (
false === is_resource($w)) {
                    unset(
$_write[$i]);
                }
            }

            foreach (
$_except as $i => $e) {
                if (
false === is_resource($e)) {
                    unset(
$_except[$i]);
                }
            }

            if (empty(
$_read) && empty($_write) && empty($_except)) {
                break;
            }

            
$read $_read;
            
$write $_write;
            
$except $_except;
            
$select stream_select($read$write$except$this->getTimeout());

            if (
=== $select) {
                
$this->getListener()->fire('timeout', new EventBucket());

                break;
            }

            foreach (
$read as $i => $_r) {
                
$pipe array_search($_r$this->_pipes);
                
$line $this->readLine($pipe);

                if (
false === $line) {
                    
$result = [false];
                } else {
                    
$result $this->getListener()->fire(
                        
'output',
                        new 
EventBucket([
                            
'pipe' => $pipe,
                            
'line' => $line,
                        ])
                    );
                }

                if (
true === feof($_r) || in_array(false$resulttrue)) {
                    
fclose($_r);
                    unset(
$_read[$i]);

                    break;
                }
            }

            foreach (
$write as $j => $_w) {
                
$result $this->getListener()->fire(
                    
'input',
                    new 
EventBucket([
                        
'pipe' => array_search($_w$this->_pipes),
                    ])
                );

                if (
true === feof($_w) || in_array(false$resulttrue)) {
                    
fclose($_w);
                    unset(
$_write[$j]);
                }
            }

            if (empty(
$_read)) {
                break;
            }
        }

        
$this->getListener()->fire('stop', new EventBucket());

        return;
    }

    
/**
     * Get pipe resource.
     */
    
protected function getPipe(int $pipe)
    {
        if (!isset(
$this->_pipes[$pipe])) {
            throw new 
ConsoleException('Pipe descriptor %d does not exist, cannot read from it.'2$pipe);
        }

        return 
$this->_pipes[$pipe];
    }

    
/**
     * Check if a pipe is seekable or not.
     */
    
protected function isPipeSeekable(int $pipe): bool
    
{
        if (!isset(
$this->_seekable[$pipe])) {
            
$_pipe $this->getPipe($pipe);
            
$data stream_get_meta_data($_pipe);
            
$this->_seekable[$pipe] = $data['seekable'];
        }

        return 
$this->_seekable[$pipe];
    }

    
/**
     * Test for end-of-file.
     */
    
public function eof(int $pipe 1): bool
    
{
        return 
feof($this->getPipe($pipe));
    }

    
/**
     * Read n characters.
     */
    
public function read(int $lengthint $pipe 1)
    {
        if (
$length) {
            throw new 
ConsoleException('Length must be greater than 0, given %d.'3$length);
        }

        return 
fread($this->getPipe($pipe), $length);
    }

    
/**
     * Alias of $this->read().
     */
    
public function readString(int $lengthint $pipe 1)
    {
        return 
$this->read($length$pipe);
    }

    
/**
     * Read a character.
     */
    
public function readCharacter(int $pipe 1)
    {
        return 
fgetc($this->getPipe($pipe));
    }

    
/**
     * Read a boolean.
     */
    
public function readBoolean(int $pipe 1)
    {
        return (bool) 
$this->read(1$pipe);
    }

    
/**
     * Read an integer.
     */
    
public function readInteger(int $length 1int $pipe 1)
    {
        return (int) 
$this->read($length$pipe);
    }

    
/**
     * Read a float.
     */
    
public function readFloat(int $length 1int $pipe 1)
    {
        return (float) 
$this->read($length$pipe);
    }

    
/**
     * Read an array.
     * Alias of the $this->scanf() method.
     */
    
public function readArray(string $format nullint $pipe 1)
    {
        return 
$this->scanf($format$pipe);
    }

    
/**
     * Read a line.
     */
    
public function readLine(int $pipe 1)
    {
        return 
stream_get_line($this->getPipe($pipe), << 15"n");
    }

    
/**
     * Read all, i.e. read as much as possible.
     */
    
public function readAll(int $offset = -1int $pipe 1)
    {
        
$_pipe $this->getPipe($pipe);

        if (
true === $this->isPipeSeekable($pipe)) {
            
$offset += ftell($_pipe);
        } else {
            
$offset = -1;
        }

        return 
stream_get_contents($_pipe, -1$offset);
    }

    
/**
     * Parse input from a stream according to a format.
     */
    
public function scanf(string $formatint $pipe 1): array
    {
        return 
fscanf($this->getPipe($pipe), $format);
    }

    
/**
     * Write n characters.
     */
    
public function write(string $stringint $lengthint $pipe 0)
    {
        if (
$length) {
            throw new 
ConsoleException('Length must be greater than 0, given %d.'4$length);
        }

        return 
fwrite($this->getPipe($pipe), $string$length);
    }

    
/**
     * Write a string.
     */
    
public function writeString(string $stringint $pipe 0)
    {
        
$string = (string) $string;

        return 
$this->write($stringstrlen($string), $pipe);
    }

    
/**
     * Write a character.
     */
    
public function writeCharacter(string $charint $pipe 0)
    {
        return 
$this->write((string) $char[0], 1$pipe);
    }

    
/**
     * Write a boolean.
     */
    
public function writeBoolean(bool $booleanint $pipe 0)
    {
        return 
$this->write((string) (bool) $boolean1$pipe);
    }

    
/**
     * Write an integer.
     */
    
public function writeInteger(int $integerint $pipe 0)
    {
        
$integer = (string) (int) $integer;

        return 
$this->write($integerstrlen($integer), $pipe);
    }

    
/**
     * Write a float.
     */
    
public function writeFloat(float $floatint $pipe 0)
    {
        
$float = (string) (float) $float;

        return 
$this->write($floatstrlen($float), $pipe);
    }

    
/**
     * Write an array.
     */
    
public function writeArray(array $arrayint $pipe 0)
    {
        
$array var_export($arraytrue);

        return 
$this->write($arraystrlen($array), $pipe);
    }

    
/**
     * Write a line.
     */
    
public function writeLine(string $lineint $pipe 0)
    {
        if (
false === $n strpos($line"n")) {
            return 
$this->write($line."n"strlen($line) + 1$pipe);
        }

        ++
$n;

        return 
$this->write(substr($line0$n), $n$pipe);
    }

    
/**
     * Write all, i.e. as much as possible.
     */
    
public function writeAll(string $stringint $pipe 0)
    {
        return 
$this->write($stringstrlen($string), $pipe);
    }

    
/**
     * Truncate a file to a given length.
     */
    
public function truncate(int $sizeint $pipe 0): bool
    
{
        return 
ftruncate($this->getPipe($pipe), $size);
    }

    
/**
     * Get filename component of path.
     */
    
public function getBasename(): string
    
{
        return 
basename($this->getCommand());
    }

    
/**
     * Get directory name component of path.
     */
    
public function getDirname(): string
    
{
        return 
dirname($this->getCommand());
    }

    
/**
     * Get status.
     */
    
public function getStatus(): array
    {
        return 
proc_get_status($this->getStream());
    }

    
/**
     * Get exit code (alias of $this->getStatus()['exitcode']);.
     */
    
public function getExitCode(): int
    
{
        
$handle $this->getStatus();

        return 
$handle['exitcode'];
    }

    
/**
     * Whether the processus have ended successfully.
     *
     * @return bool
     */
    
public function isSuccessful(): bool
    
{
        return 
=== $this->getExitCode();
    }

    
/**
     * Terminate the process.
     *
     * Valid signals are self::SIGHUP, SIGINT, SIGQUIT, SIGABRT, SIGKILL,
     * SIGALRM and SIGTERM.
     */
    
public function terminate(int $signal self::SIGTERM): bool
    
{
        return 
proc_terminate($this->getStream(), $signal);
    }

    
/**
     * Set command name.
     */
    
protected function setCommand(string $command)
    {
        
$old $this->_command;
        
$this->_command escapeshellcmd($command);

        return 
$old;
    }

    
/**
     * Get command name.
     */
    
public function getCommand()
    {
        return 
$this->_command;
    }

    
/**
     * Set command options.
     */
    
protected function setOptions(array $options): array
    {
        foreach (
$options as &$option) {
            
$option escapeshellarg($option);
        }

        
$old $this->_options;
        
$this->_options $options;

        return 
$old;
    }

    
/**
     * Get options.
     */
    
public function getOptions(): array
    {
        return 
$this->_options;
    }

    
/**
     * Get command-line.
     */
    
public function getCommandLine(): string
    
{
        
$out $this->getCommand();

        foreach (
$this->getOptions() as $key => $value) {
            if (!
is_int($key)) {
                
$out .= ' '.$key.'='.$value;
            } else {
                
$out .= ' '.$value;
            }
        }

        return 
$out;
    }

    
/**
     * Set current working directory of the process.
     */
    
protected function setCwd(string $cwd)
    {
        
$old $this->_cwd;
        
$this->_cwd $cwd;

        return 
$old;
    }

    
/**
     * Get current working directory of the process.
     */
    
public function getCwd(): string
    
{
        return 
$this->_cwd;
    }

    
/**
     * Set environment of the process.
     */
    
protected function setEnvironment(array $environment)
    {
        
$old $this->_environment;
        
$this->_environment $environment;

        return 
$old;
    }

    
/**
     * Get environment of the process.
     */
    
public function getEnvironment()
    {
        return 
$this->_environment;
    }

    
/**
     * Set timeout of the process.
     */
    
public function setTimeout(int $timeout)
    {
        
$old $this->_timeout;
        
$this->_timeout $timeout;

        return 
$old;
    }

    
/**
     * Get timeout of the process.
     */
    
public function getTimeout(): int
    
{
        return 
$this->_timeout;
    }

    
/**
     * Set process title.
     */
    
public static function setTitle(string $title)
    {
        
cli_set_process_title($title);
    }

    
/**
     * Get process title.
     */
    
public static function getTitle()
    {
        return 
cli_get_process_title();
    }

    
/**
     * Found the place of a binary.
     */
    
public static function locate(string $binary)
    {
        if (isset(
$_ENV['PATH'])) {
            
$separator ':';
            
$path = &$_ENV['PATH'];
        } elseif (isset(
$_SERVER['PATH'])) {
            
$separator ':';
            
$path = &$_SERVER['PATH'];
        } elseif (isset(
$_SERVER['Path'])) {
            
$separator ';';
            
$path = &$_SERVER['Path'];
        } else {
            return 
null;
        }

        foreach (
explode($separator$path) as $directory) {
            if (
true === file_exists($out $directory.DIRECTORY_SEPARATOR.$binary)) {
                return 
$out;
            }
        }

        return 
null;
    }

    
/**
     * Quick process execution.
     * Returns only the STDOUT.
     */
    
public static function execute(string $commandLinebool $escape true): string
    
{
        if (
true === $escape) {
            
$commandLine escapeshellcmd($commandLine);
        }

        return 
rtrim(shell_exec($commandLine) ?? '');
    }
}
Онлайн: 1
Реклама