Files
DomFramework/console.php
2018-06-08 14:44:13 +00:00

341 lines
10 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/** DomFramework
* @package domframework
* @author Dominique Fournier <dominique@fournier38.fr>
*/
/** Allow to manage a linux Console to have a minimal but working text interface
* When using this class, you must use the $console::echo method and not
* display directely on screen
*/
class console
{
// PROPERTIES
// {{{
/** Save the initial stty value
*/
private $initSttyState;
/** Line Content
*/
private $lineContent = "";
/** If true, display each char the user has pressed (echo mode)
*/
private $echoMode = true;
/** List of non printable chars in decimal. The non printable chars are not
* displayed but are correctely captured
*/
private $nonWriteableChar = array (1, 2, 4, 6, 8, 9, 16, 18,
20, 21, 22, 23, 24, 25,
127);
// }}}
/** The constructor init the console.
* Check if we have the rights to execute, if wa have in cli...
*/
public function __construct ()
// {{{
{
if (! function_exists ("exec"))
throw $this->ConsoleException ("No exec support in PHP");
$this->initSttyState = exec ("stty -g");
// Set the terminal to return the value each time a key is pressed.
// Do not display anything, so we don't see the characters when the user is
// deleting.
exec ("stty -echo -icanon min 1 time 0");
}
// }}}
/** The destructor return the terminal to initial state
*/
public function __destruct ()
// {{{
{
exec ("stty $this->initSttyState");
}
// }}}
/** Each time a key is pressed by the user, display the value on screen (echo)
*/
public function setEcho ()
// {{{
{
$this->echoMode = true;
}
// }}}
/** Each time a key is pressed by the user, DO NOT display the value on screen
* (echo disabled)
*/
public function unsetEcho ()
// {{{
{
$this->echoMode = false;
}
// }}}
/** Display a text on screen
* @param string $message The message to display
*/
public function echo ($message)
// {{{
{
echo $message;
$this->lineContent .= $message;
}
// }}}
/** Wait one valid character from the user.
* The non printable chars are not displayed, nor returned
* The ESC Sequences are skipped
* @return the pressed char
*/
public function getc ()
// {{{
{
$char = $this->getKey ();
while (in_array (ord ($char), $this->nonWriteableChar))
$char = $this->getKey ();
return $char;
}
// }}}
/** Wait one key pressed by the user. If the key pressed is an ESC sequence,
* return this sequence
* The non printable chars are not displayed, but are correctely returned
* @return the pressed char
*/
public function getKey ()
// {{{
{
$char = fgetc (STDIN);
if ($char === chr (27))
{
$sequence = $char;
// Start an ESC CSI sequence. Do not display it, just return it, complete.
// ESC [ is followed by any number (including none) of "parameter bytes"
// in the range 0x300x3F (ASCII 09:;<=>?), then by any number of
// "intermediate bytes" in the range 0x200x2F (ASCII space and
// !"#$%&'()*+,-./), then finally by a single "final byte" in the range
// 0x400x7E (ASCII @AZ[\]^_`az{|}~).[14]:5.4
$char = fgetc (STDIN);
if ($char !== chr (91))
$this->consoleException ("Invalid ESC CSI sequence provided");
$sequence .= $char;
$char = fgetc (STDIN);
while (ord ($char) < 64 || ord ($char) > 126)
{
$sequence .= $char;
$char = fgetc (STDIN);
}
$sequence .= $char;
return $sequence;
}
if ($this->echoMode && ! in_array (ord ($char), $this->nonWriteableChar))
echo $char;
return $char;
}
// }}}
/** Get the line of characters pressed by the user and return the result.
* Stop when the user valid by \n or one of the stoppers chars. If the
* stoppers chars are set, the last char of the returned string is the
* stopper char. If not set, the method do not return \n at the end.
* Manage correctely the backspace, the Ctrl+W to remove word...
* @param string $stopperChar The stopper chars.
* @return The typed string
*/
public function gets ($stopperChar = false)
// {{{
{
// Gets can not delete chars before the call. Keep the prompt (if exists)
$prompt = $this->lineContent;
$minLength = strlen ($this->lineContent);
// The cursor position from first char of line.
$cursorPos = $minLength;
$string = "";
while (1)
{
$char = $this->getKey ();
if ($stopperChar === false && $char === "\n")
{
// End of process without stopperChars
$this->lineContent = "";
break;
}
if ($stopperChar !== false && in_array ($char, str_split ($stopperChar)))
{
// End of process with stopperChars
$this->lineContent = "";
$string .= $char;
break;
}
elseif (ord($char) === 21)
{
// Empty line next to prompt (Ctrl+U)
// {{{
$string = substr ($string, $cursorPos - $minLength);
echo "\r".str_repeat (" ", strlen ($this->lineContent))."\r";
$this->lineContent = $prompt.$string;
echo $this->lineContent;
$cursorPos = $minLength;
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
// }}}
}
elseif (ord($char) === 23)
{
// Remove the last word (Ctrl+W)
// TODO : Cursor management !!
$tmp = substr ($string, 0, $cursorPos - $minLength);
$end = substr ($string, $cursorPos - $minLength);
$tmp = rtrim ($tmp);
$pos = strrpos ($tmp, " ");
$string = substr ($string, 0, $pos).$end;
$cursorPos = $minLength + $pos;
if ($this->echoMode)
{
echo "\r".str_repeat (" ", strlen ($this->lineContent))."\r";
$this->lineContent = $prompt.$string;
echo $this->lineContent;
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
}
}
elseif (ord($char) === 127)
{
// Remove the previous char (Backspace)
// {{{
if (strlen ($this->lineContent) < $minLength + 1)
continue;
if ($cursorPos < $minLength +1)
continue;
$cursorPos--;
$strArr = str_split ($string);
unset ($strArr[$cursorPos - $minLength]);
$string = implode ($strArr);
if ($this->echoMode)
{
echo "\r".str_repeat (" ", strlen ($this->lineContent))."\r";
$this->lineContent = $prompt.$string;
echo $this->lineContent;
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
}
// }}}
}
elseif (ord ($char{0}) === 27)
{
// ESC SEQUENCE
/*file_put_contents ("/tmp/debug", "SEQ CHAR=", FILE_APPEND);
foreach (str_split ($char) as $key)
file_put_contents ("/tmp/debug", ord ($key)." ", FILE_APPEND);
file_put_contents ("/tmp/debug", "\n", FILE_APPEND);*/
if ($char === chr (27).chr (91).chr (67))
{
// Cursor right
// {{{
if ($cursorPos < strlen ($this->lineContent))
{
echo $char;
$cursorPos++;
}
// }}}
}
elseif ($char === chr (27).chr (91).chr (68))
{
// Cursor left
// {{{
if ($cursorPos > $minLength)
{
echo $char;
$cursorPos--;
}
// }}}
}
elseif ($char === chr (27).chr (91).chr (70))
{
// End key
// {{{
$cursorPos = $minLength + strlen ($string);
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
// }}}
}
elseif ($char === chr (27).chr (91).chr (72))
{
// Home key
// {{{
$cursorPos = $minLength;
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
// }}}
}
elseif ($char === chr (27).chr (91).chr (51).chr (126))
{
// Remove the char under the cursor (Delete)
// {{{
if ($cursorPos >= strlen ($this->lineContent))
continue;
$cursorPos;
$strArr = str_split ($string);
unset ($strArr[$cursorPos - $minLength]);
$string = implode ($strArr);
if ($this->echoMode)
{
echo "\r".str_repeat (" ", strlen ($this->lineContent))."\r";
$this->lineContent = $prompt.$string;
echo $this->lineContent;
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
}
// }}}
}
}
else
{
// Normal char : Add it to the string
// {{{
$strArr = str_split ($string);
$firstArr = array_slice ($strArr, 0, $cursorPos - $minLength);
$lastArr = array_slice ($strArr, $cursorPos - $minLength);
$insertArr = array ($char);
$strArr = array_merge ($firstArr, $insertArr, $lastArr);
$string = implode ($strArr);
$cursorPos++;
echo "\r";
$this->lineContent = $prompt.$string;
echo $this->lineContent;
// Move the cursor on the correct position
echo "\r".str_repeat (chr (27).chr (91).chr (67), $cursorPos);
// }}}
}
}
return $string;
}
// }}}
/** Clear the existing line
*/
public function clearLine ()
// {{{
{
echo "\r".str_repeat (" ", strlen ($this->lineContent))."\r";
$this->lineContent = "";
}
// }}}
/** Error management
* @param string $message The message to throw in the exception
*/
public function consoleException ($message)
// {{{
{
throw new \Exception ($message, 500);
}
// }}}
}