Added added third-party library 'php-school/terminal'
This commit is contained in:
parent
b633f16694
commit
f8db35135e
11 changed files with 859 additions and 0 deletions
19
src/ncc/ThirdParty/php-school/terminal/Exception/NotInteractiveTerminal.php
vendored
Normal file
19
src/ncc/ThirdParty/php-school/terminal/Exception/NotInteractiveTerminal.php
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal\Exception;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class NotInteractiveTerminal extends \RuntimeException
|
||||
{
|
||||
public static function inputNotInteractive() : self
|
||||
{
|
||||
return new self('Input stream is not interactive (non TTY)');
|
||||
}
|
||||
|
||||
public static function outputNotInteractive() : self
|
||||
{
|
||||
return new self('Output stream is not interactive (non TTY)');
|
||||
}
|
||||
}
|
40
src/ncc/ThirdParty/php-school/terminal/IO/BufferedOutput.php
vendored
Normal file
40
src/ncc/ThirdParty/php-school/terminal/IO/BufferedOutput.php
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal\IO;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class BufferedOutput implements OutputStream
|
||||
{
|
||||
private $buffer = '';
|
||||
|
||||
public function write(string $buffer): void
|
||||
{
|
||||
$this->buffer .= $buffer;
|
||||
}
|
||||
|
||||
public function fetch(bool $clean = true) : string
|
||||
{
|
||||
$buffer = $this->buffer;
|
||||
|
||||
if ($clean) {
|
||||
$this->buffer = '';
|
||||
}
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
return $this->fetch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the stream is connected to an interactive terminal
|
||||
*/
|
||||
public function isInteractive() : bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
20
src/ncc/ThirdParty/php-school/terminal/IO/InputStream.php
vendored
Normal file
20
src/ncc/ThirdParty/php-school/terminal/IO/InputStream.php
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal\IO;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
interface InputStream
|
||||
{
|
||||
/**
|
||||
* Callback should be called with the number of bytes requested
|
||||
* when ready.
|
||||
*/
|
||||
public function read(int $numBytes, callable $callback) : void;
|
||||
|
||||
/**
|
||||
* Whether the stream is connected to an interactive terminal
|
||||
*/
|
||||
public function isInteractive() : bool;
|
||||
}
|
19
src/ncc/ThirdParty/php-school/terminal/IO/OutputStream.php
vendored
Normal file
19
src/ncc/ThirdParty/php-school/terminal/IO/OutputStream.php
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal\IO;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
interface OutputStream
|
||||
{
|
||||
/**
|
||||
* Write the buffer to the stream
|
||||
*/
|
||||
public function write(string $buffer) : void;
|
||||
|
||||
/**
|
||||
* Whether the stream is connected to an interactive terminal
|
||||
*/
|
||||
public function isInteractive() : bool;
|
||||
}
|
47
src/ncc/ThirdParty/php-school/terminal/IO/ResourceInputStream.php
vendored
Normal file
47
src/ncc/ThirdParty/php-school/terminal/IO/ResourceInputStream.php
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal\IO;
|
||||
|
||||
use function is_resource;
|
||||
use function get_resource_type;
|
||||
use function stream_get_meta_data;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class ResourceInputStream implements InputStream
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
public function __construct($stream = STDIN)
|
||||
{
|
||||
if (!is_resource($stream) || get_resource_type($stream) !== 'stream') {
|
||||
throw new \InvalidArgumentException('Expected a valid stream');
|
||||
}
|
||||
|
||||
$meta = stream_get_meta_data($stream);
|
||||
if (strpos($meta['mode'], 'r') === false && strpos($meta['mode'], '+') === false) {
|
||||
throw new \InvalidArgumentException('Expected a readable stream');
|
||||
}
|
||||
|
||||
$this->stream = $stream;
|
||||
}
|
||||
|
||||
public function read(int $numBytes, callable $callback) : void
|
||||
{
|
||||
$buffer = fread($this->stream, $numBytes);
|
||||
$callback($buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the stream is connected to an interactive terminal
|
||||
*/
|
||||
public function isInteractive() : bool
|
||||
{
|
||||
return posix_isatty($this->stream);
|
||||
}
|
||||
}
|
46
src/ncc/ThirdParty/php-school/terminal/IO/ResourceOutputStream.php
vendored
Normal file
46
src/ncc/ThirdParty/php-school/terminal/IO/ResourceOutputStream.php
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal\IO;
|
||||
|
||||
use function is_resource;
|
||||
use function get_resource_type;
|
||||
use function stream_get_meta_data;
|
||||
use function strpos;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class ResourceOutputStream implements OutputStream
|
||||
{
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
private $stream;
|
||||
|
||||
public function __construct($stream = STDOUT)
|
||||
{
|
||||
if (!is_resource($stream) || get_resource_type($stream) !== 'stream') {
|
||||
throw new \InvalidArgumentException('Expected a valid stream');
|
||||
}
|
||||
|
||||
$meta = stream_get_meta_data($stream);
|
||||
if (strpos($meta['mode'], 'r') !== false && strpos($meta['mode'], '+') === false) {
|
||||
throw new \InvalidArgumentException('Expected a writable stream');
|
||||
}
|
||||
|
||||
$this->stream = $stream;
|
||||
}
|
||||
|
||||
public function write(string $buffer): void
|
||||
{
|
||||
fwrite($this->stream, $buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the stream is connected to an interactive terminal
|
||||
*/
|
||||
public function isInteractive() : bool
|
||||
{
|
||||
return posix_isatty($this->stream);
|
||||
}
|
||||
}
|
137
src/ncc/ThirdParty/php-school/terminal/InputCharacter.php
vendored
Normal file
137
src/ncc/ThirdParty/php-school/terminal/InputCharacter.php
vendored
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal;
|
||||
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class InputCharacter
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $data;
|
||||
|
||||
public const UP = 'UP';
|
||||
public const DOWN = 'DOWN';
|
||||
public const RIGHT = 'RIGHT';
|
||||
public const LEFT = 'LEFT';
|
||||
public const CTRLA = 'CTRLA';
|
||||
public const CTRLB = 'CTRLB';
|
||||
public const CTRLE = 'CTRLE';
|
||||
public const CTRLF = 'CTRLF';
|
||||
public const BACKSPACE = 'BACKSPACE';
|
||||
public const CTRLW = 'CTRLW';
|
||||
public const ENTER = 'ENTER';
|
||||
public const TAB = 'TAB';
|
||||
public const ESC = 'ESC';
|
||||
|
||||
private static $controls = [
|
||||
"\033[A" => self::UP,
|
||||
"\033[B" => self::DOWN,
|
||||
"\033[C" => self::RIGHT,
|
||||
"\033[D" => self::LEFT,
|
||||
"\033OA" => self::UP,
|
||||
"\033OB" => self::DOWN,
|
||||
"\033OC" => self::RIGHT,
|
||||
"\033OD" => self::LEFT,
|
||||
"\001" => self::CTRLA,
|
||||
"\002" => self::CTRLB,
|
||||
"\005" => self::CTRLE,
|
||||
"\006" => self::CTRLF,
|
||||
"\010" => self::BACKSPACE,
|
||||
"\177" => self::BACKSPACE,
|
||||
"\027" => self::CTRLW,
|
||||
"\n" => self::ENTER,
|
||||
"\t" => self::TAB,
|
||||
"\e" => self::ESC,
|
||||
];
|
||||
|
||||
public function __construct(string $data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function isHandledControl() : bool
|
||||
{
|
||||
return isset(static::$controls[$this->data]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this character a control sequence?
|
||||
*/
|
||||
public function isControl() : bool
|
||||
{
|
||||
return preg_match('/[\x00-\x1F\x7F]/', $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this character a normal character?
|
||||
*/
|
||||
public function isNotControl() : bool
|
||||
{
|
||||
return ! $this->isControl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw character or control sequence
|
||||
*/
|
||||
public function get() : string
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual control name that this sequence represents.
|
||||
* One of the class constants. Eg. self::UP.
|
||||
*
|
||||
* Throws an exception if the character is not actually a control sequence
|
||||
*/
|
||||
public function getControl() : string
|
||||
{
|
||||
if (!isset(static::$controls[$this->data])) {
|
||||
throw new \RuntimeException(sprintf('Character "%s" is not a control', $this->data));
|
||||
}
|
||||
|
||||
return static::$controls[$this->data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw character or control sequence
|
||||
*/
|
||||
public function __toString() : string
|
||||
{
|
||||
return $this->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the given control name exist? eg self::UP.
|
||||
*/
|
||||
public static function controlExists(string $controlName) : bool
|
||||
{
|
||||
return in_array($controlName, static::$controls, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the available control names
|
||||
*/
|
||||
public static function getControls() : array
|
||||
{
|
||||
return array_values(array_unique(static::$controls));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a instance from a given control name. Throws an exception if the
|
||||
* control name does not exist.
|
||||
*/
|
||||
public static function fromControlName(string $controlName) : self
|
||||
{
|
||||
if (!static::controlExists($controlName)) {
|
||||
throw new \InvalidArgumentException(sprintf('Control "%s" does not exist', $controlName));
|
||||
}
|
||||
|
||||
return new static(array_search($controlName, static::$controls, true));
|
||||
}
|
||||
}
|
79
src/ncc/ThirdParty/php-school/terminal/NonCanonicalReader.php
vendored
Normal file
79
src/ncc/ThirdParty/php-school/terminal/NonCanonicalReader.php
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal;
|
||||
|
||||
/**
|
||||
* This class takes a terminal and disabled canonical mode. It reads the input
|
||||
* and returns characters and control sequences as `InputCharacters` as soon
|
||||
* as they are read - character by character.
|
||||
*
|
||||
* On destruct canonical mode will be enabled if it was when in it was constructed.
|
||||
*
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class NonCanonicalReader
|
||||
{
|
||||
/**
|
||||
* @var Terminal
|
||||
*/
|
||||
private $terminal;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $wasCanonicalModeEnabled;
|
||||
|
||||
/**
|
||||
* Map of characters to controls.
|
||||
* Eg map 'w' to the up control.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $mappings = [];
|
||||
|
||||
public function __construct(Terminal $terminal)
|
||||
{
|
||||
$this->terminal = $terminal;
|
||||
$this->wasCanonicalModeEnabled = $terminal->isCanonicalMode();
|
||||
$this->terminal->disableCanonicalMode();
|
||||
}
|
||||
|
||||
public function addControlMapping(string $character, string $mapToControl) : void
|
||||
{
|
||||
if (!InputCharacter::controlExists($mapToControl)) {
|
||||
throw new \InvalidArgumentException(sprintf('Control "%s" does not exist', $mapToControl));
|
||||
}
|
||||
|
||||
$this->mappings[$character] = $mapToControl;
|
||||
}
|
||||
|
||||
public function addControlMappings(array $mappings) : void
|
||||
{
|
||||
foreach ($mappings as $character => $mapToControl) {
|
||||
$this->addControlMapping($character, $mapToControl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be ran with the terminal canonical mode disabled.
|
||||
*
|
||||
* @return InputCharacter
|
||||
*/
|
||||
public function readCharacter() : InputCharacter
|
||||
{
|
||||
$char = $this->terminal->read(4);
|
||||
|
||||
if (isset($this->mappings[$char])) {
|
||||
return InputCharacter::fromControlName($this->mappings[$char]);
|
||||
}
|
||||
|
||||
return new InputCharacter($char);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->wasCanonicalModeEnabled) {
|
||||
$this->terminal->enableCanonicalMode();
|
||||
}
|
||||
}
|
||||
}
|
32
src/ncc/ThirdParty/php-school/terminal/README.md
vendored
Normal file
32
src/ncc/ThirdParty/php-school/terminal/README.md
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<h1 align="center">Terminal Utility</h1>
|
||||
|
||||
<p align="center">
|
||||
Small utility to help provide a simple, consise API for terminal interaction
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/php-school/terminal" title="Build Status" target="_blank">
|
||||
<img src="https://img.shields.io/travis/php-school/terminal/master.svg?style=flat-square&label=Linux" />
|
||||
</a
|
||||
<a href="https://codecov.io/github/php-school/terminal" title="Coverage Status" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/php-school/terminal.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://scrutinizer-ci.com/g/php-school/terminal/" title="Scrutinizer Code Quality" target="_blank">
|
||||
<img src="https://img.shields.io/scrutinizer/g/php-school/terminal.svg?style=flat-square" />
|
||||
</a>
|
||||
<a href="https://phpschool-team.slack.com/messages">
|
||||
<img src="https://phpschool.herokuapp.com/badge.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
composer require php-school/terminal
|
||||
```
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Docs
|
133
src/ncc/ThirdParty/php-school/terminal/Terminal.php
vendored
Normal file
133
src/ncc/ThirdParty/php-school/terminal/Terminal.php
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal;
|
||||
|
||||
/**
|
||||
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
interface Terminal
|
||||
{
|
||||
/**
|
||||
* Get the available width of the terminal
|
||||
*/
|
||||
public function getWidth() : int;
|
||||
|
||||
/**
|
||||
* Get the available height of the terminal
|
||||
*/
|
||||
public function getHeight() : int;
|
||||
|
||||
/**
|
||||
* Get the number of colours the terminal supports (1, 8, 256, true colours)
|
||||
*/
|
||||
public function getColourSupport() : int;
|
||||
|
||||
/**
|
||||
* Disables echoing every character back to the terminal. This means
|
||||
* we do not have to clear the line when reading.
|
||||
*/
|
||||
public function disableEchoBack() : void;
|
||||
|
||||
/**
|
||||
* Enable echoing back every character input to the terminal.
|
||||
*/
|
||||
public function enableEchoBack() : void;
|
||||
|
||||
/**
|
||||
* Is echo back mode enabled
|
||||
*/
|
||||
public function isEchoBack() : bool;
|
||||
|
||||
/**
|
||||
* Disable canonical input (allow each key press for reading, rather than the whole line)
|
||||
*
|
||||
* @see https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html
|
||||
*/
|
||||
public function disableCanonicalMode() : void;
|
||||
|
||||
/**
|
||||
* Enable canonical input - read input by line
|
||||
*
|
||||
* @see https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html
|
||||
*/
|
||||
public function enableCanonicalMode() : void;
|
||||
|
||||
/**
|
||||
* Is canonical mode enabled or not
|
||||
*/
|
||||
public function isCanonicalMode() : bool;
|
||||
|
||||
/**
|
||||
* Check if the Input & Output streams are interactive. Eg - they are
|
||||
* connected to a terminal.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInteractive() : bool;
|
||||
|
||||
/**
|
||||
* Restore the terminals original configuration
|
||||
*/
|
||||
public function restoreOriginalConfiguration() : void;
|
||||
|
||||
/**
|
||||
* Test whether terminal supports colour output
|
||||
*/
|
||||
public function supportsColour() : bool;
|
||||
|
||||
/**
|
||||
* Clear the terminal window
|
||||
*/
|
||||
public function clear() : void;
|
||||
|
||||
/**
|
||||
* Clear the current cursors line
|
||||
*/
|
||||
public function clearLine() : void;
|
||||
|
||||
/**
|
||||
* Erase screen from the current line down to the bottom of the screen
|
||||
*/
|
||||
public function clearDown() : void;
|
||||
|
||||
/**
|
||||
* Clean the whole console without jumping the window
|
||||
*/
|
||||
public function clean() : void;
|
||||
|
||||
/**
|
||||
* Enable cursor display
|
||||
*/
|
||||
public function enableCursor() : void;
|
||||
|
||||
/**
|
||||
* Disable cursor display
|
||||
*/
|
||||
public function disableCursor() : void;
|
||||
|
||||
/**
|
||||
* Move the cursor to the top left of the window
|
||||
*/
|
||||
public function moveCursorToTop() : void;
|
||||
|
||||
/**
|
||||
* Move the cursor to the start of a specific row
|
||||
*/
|
||||
public function moveCursorToRow(int $rowNumber) : void;
|
||||
|
||||
/**
|
||||
* Move the cursor to a specific column
|
||||
*/
|
||||
public function moveCursorToColumn(int $columnNumber) : void;
|
||||
|
||||
/**
|
||||
* Read from the input stream
|
||||
*/
|
||||
public function read(int $bytes) : string;
|
||||
|
||||
/**
|
||||
* Write to the output stream
|
||||
*/
|
||||
public function write(string $buffer) : void;
|
||||
}
|
287
src/ncc/ThirdParty/php-school/terminal/UnixTerminal.php
vendored
Normal file
287
src/ncc/ThirdParty/php-school/terminal/UnixTerminal.php
vendored
Normal file
|
@ -0,0 +1,287 @@
|
|||
<?php
|
||||
|
||||
namespace PhpSchool\Terminal;
|
||||
|
||||
use PhpSchool\Terminal\Exception\NotInteractiveTerminal;
|
||||
use PhpSchool\Terminal\IO\InputStream;
|
||||
use PhpSchool\Terminal\IO\OutputStream;
|
||||
|
||||
/**
|
||||
* @author Michael Woodward <mikeymike.mw@gmail.com>
|
||||
* @author Aydin Hassan <aydin@hotmail.co.uk>
|
||||
*/
|
||||
class UnixTerminal implements Terminal
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isCanonical;
|
||||
|
||||
/**
|
||||
* Whether terminal echo back is enabled or not.
|
||||
* Eg. user key presses and the terminal immediately shows it.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private $echoBack = true;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $height;
|
||||
|
||||
/**
|
||||
* @var int;
|
||||
*/
|
||||
private $colourSupport;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $originalConfiguration;
|
||||
|
||||
/**
|
||||
* @var InputStream
|
||||
*/
|
||||
private $input;
|
||||
|
||||
/**
|
||||
* @var OutputStream
|
||||
*/
|
||||
private $output;
|
||||
|
||||
public function __construct(InputStream $input, OutputStream $output)
|
||||
{
|
||||
$this->getOriginalConfiguration();
|
||||
$this->getOriginalCanonicalMode();
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
}
|
||||
|
||||
private function getOriginalCanonicalMode() : void
|
||||
{
|
||||
exec('stty -a', $output);
|
||||
$this->isCanonical = (strpos(implode("\n", $output), ' icanon') !== false);
|
||||
}
|
||||
|
||||
public function getWidth() : int
|
||||
{
|
||||
return $this->width ?: $this->width = (int) exec('tput cols');
|
||||
}
|
||||
|
||||
public function getHeight() : int
|
||||
{
|
||||
return $this->height ?: $this->height = (int) exec('tput lines');
|
||||
}
|
||||
|
||||
public function getColourSupport() : int
|
||||
{
|
||||
return $this->colourSupport ?: $this->colourSupport = (int) exec('tput colors');
|
||||
}
|
||||
|
||||
private function getOriginalConfiguration() : string
|
||||
{
|
||||
return $this->originalConfiguration ?: $this->originalConfiguration = exec('stty -g');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables echoing every character back to the terminal. This means
|
||||
* we do not have to clear the line when reading.
|
||||
*/
|
||||
public function disableEchoBack() : void
|
||||
{
|
||||
exec('stty -echo');
|
||||
$this->echoBack = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable echoing back every character input to the terminal.
|
||||
*/
|
||||
public function enableEchoBack() : void
|
||||
{
|
||||
exec('stty echo');
|
||||
$this->echoBack = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is echo back mode enabled
|
||||
*/
|
||||
public function isEchoBack() : bool
|
||||
{
|
||||
return $this->echoBack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable canonical input (allow each key press for reading, rather than the whole line)
|
||||
*
|
||||
* @see https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html
|
||||
*/
|
||||
public function disableCanonicalMode() : void
|
||||
{
|
||||
if ($this->isCanonical) {
|
||||
exec('stty -icanon');
|
||||
$this->isCanonical = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable canonical input - read input by line
|
||||
*
|
||||
* @see https://www.gnu.org/software/libc/manual/html_node/Canonical-or-Not.html
|
||||
*/
|
||||
public function enableCanonicalMode() : void
|
||||
{
|
||||
if (!$this->isCanonical) {
|
||||
exec('stty icanon');
|
||||
$this->isCanonical = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is canonical mode enabled or not
|
||||
*/
|
||||
public function isCanonicalMode() : bool
|
||||
{
|
||||
return $this->isCanonical;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the original terminal configuration
|
||||
*/
|
||||
public function restoreOriginalConfiguration() : void
|
||||
{
|
||||
exec('stty ' . $this->getOriginalConfiguration());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Input & Output streams are interactive. Eg - they are
|
||||
* connected to a terminal.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInteractive() : bool
|
||||
{
|
||||
return $this->input->isInteractive() && $this->output->isInteractive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that both the Input & Output streams are interactive. Throw
|
||||
* `NotInteractiveTerminal` if not.
|
||||
*/
|
||||
public function mustBeInteractive() : void
|
||||
{
|
||||
if (!$this->input->isInteractive()) {
|
||||
throw NotInteractiveTerminal::inputNotInteractive();
|
||||
}
|
||||
|
||||
if (!$this->output->isInteractive()) {
|
||||
throw NotInteractiveTerminal::outputNotInteractive();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/symfony/Console/blob/master/Output/StreamOutput.php#L95-L102
|
||||
*/
|
||||
public function supportsColour() : bool
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return false !== getenv('ANSICON') || 'ON' === getenv('ConEmuANSI') || 'xterm' === getenv('TERM');
|
||||
}
|
||||
|
||||
return $this->isInteractive();
|
||||
}
|
||||
|
||||
public function clear() : void
|
||||
{
|
||||
$this->output->write("\033[2J");
|
||||
}
|
||||
|
||||
public function clearLine() : void
|
||||
{
|
||||
$this->output->write("\033[2K");
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase screen from the current line down to the bottom of the screen
|
||||
*/
|
||||
public function clearDown() : void
|
||||
{
|
||||
$this->output->write("\033[J");
|
||||
}
|
||||
|
||||
public function clean() : void
|
||||
{
|
||||
foreach (range(0, $this->getHeight()) as $rowNum) {
|
||||
$this->moveCursorToRow($rowNum);
|
||||
$this->clearLine();
|
||||
}
|
||||
}
|
||||
|
||||
public function enableCursor() : void
|
||||
{
|
||||
$this->output->write("\033[?25h");
|
||||
}
|
||||
|
||||
public function disableCursor() : void
|
||||
{
|
||||
$this->output->write("\033[?25l");
|
||||
}
|
||||
|
||||
public function moveCursorToTop() : void
|
||||
{
|
||||
$this->output->write("\033[H");
|
||||
}
|
||||
|
||||
public function moveCursorToRow(int $rowNumber) : void
|
||||
{
|
||||
$this->output->write(sprintf("\033[%d;0H", $rowNumber));
|
||||
}
|
||||
|
||||
public function moveCursorToColumn(int $column) : void
|
||||
{
|
||||
$this->output->write(sprintf("\033[%dC", $column));
|
||||
}
|
||||
|
||||
public function showSecondaryScreen() : void
|
||||
{
|
||||
$this->output->write("\033[?47h");
|
||||
}
|
||||
|
||||
public function showPrimaryScreen() : void
|
||||
{
|
||||
$this->output->write("\033[?47l");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read bytes from the input stream
|
||||
*/
|
||||
public function read(int $bytes): string
|
||||
{
|
||||
$buffer = '';
|
||||
$this->input->read($bytes, function ($data) use (&$buffer) {
|
||||
$buffer .= $data;
|
||||
});
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the output stream
|
||||
*/
|
||||
public function write(string $buffer): void
|
||||
{
|
||||
$this->output->write($buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the original terminal configuration on shutdown.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->restoreOriginalConfiguration();
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue