Files
DomFramework/console.php

1021 lines
30 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
* Like readline, but all in PHP
* Manage the historical of the provided commands
* Allow the user to use arrow keys and the shortcuts Ctrl+arrow, Ctrl+L,
* Ctrl+U, Ctrl+W
* To not allow the stop of the program by Ctrl+C, you can add
* exec ("stty intr ^J");
* To update the window size when the terminal is resized, use after console
* instanciation :
* declare(ticks = 1);
* pcntl_signal (SIGWINCH, function () use ($console) {
* $console->updateTerminalSize ();
* });
*/
class console
{
// PROPERTIES
// {{{
/** Set the debug on if a filename is provided, or do not debug if false is
* provided
*/
//private $debug = "/tmp/debug";
private $debug = false;
/** 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, 3, 4, 6, 8, 9, 16, 18,
20, 21, 22, 23, 24, 25, 27,
127);
/** The history list in an array
*/
private $history = array ();
/** The history max size in entries
*/
private $historyMaxSize = 1000;
/** Set the completion keys. Not set by default
*/
private $completionKeys = false;
/** Set the function called when the completion char is called
*/
private $completionFunction = array ();
/** Set the width of the terminal in chars
*/
private $termWidth;
/** Store the last cursor position in the last readline
*/
private $cursorPos = 1;
// }}}
/** 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.
// 'intr ^J' allow to redefine the interruption from Ctrl+C to Ctrl+J. It
// allow to manage the Ctrl+C key to clean the entry
exec ("stty -echo -icanon min 1 time 0");
$this->updateTerminalSize ();
}
// }}}
/** Update the terminal size
*/
public function updateTerminalSize ()
// {{{
{
$termSize = exec ("stty size", $null, $rc);
if ($rc === 0)
{
list ($termHeight, $termWidth) = explode (" ", $termSize);
if (is_null ($termWidth) || is_bool ($termWidth))
$this->termWidth = 80;
else
$this->termWidth = intval ($termWidth);
}
else
$this->termWidth = 80;
}
// }}}
/** The destructor return the terminal to initial state
*/
public function __destruct ()
// {{{
{
exec ("stty $this->initSttyState");
$this->colorReset ();
$this->textUnderline (false);
$this->textBold (false);
}
// }}}
/** 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. Must be used before "readline" method because
* the provided message will not be deleted by readline process.
* @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
* The UTF8 chars are return as multiple length chars
* @return the pressed char
*/
public function getKey ()
// {{{
{
$char = fgetc (STDIN);
if ($char === chr (27))
{
$sequence = $char;
$char2 = fgetc (STDIN);
if (ord ($char2) === 27)
{
// Sequence of ESC ESC
}
elseif (ord ($char2) === 79)
{
// Start an ESC SS3 sequence
// Like F2 Key
$sequence .= $char2;
$char = fgetc (STDIN);
$sequence .= $char;
}
elseif (ord ($char2) === 91)
{
// Start an ESC CSI sequence. Do not display it, just return it.
// The ESC squences are used to communicate the cursor keys, associated
// with the Ctrl key, by example
// 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
$sequence .= $char2;
$char = fgetc (STDIN);
while (ord ($char) < 64 || ord ($char) > 126)
{
$sequence .= $char;
$char = fgetc (STDIN);
}
$sequence .= $char;
}
else
{
$this->consoleException ("Invalid ESC seq : ".ord($char2));
}
return $sequence;
}
// UTF Sequence :
// - char1 < 128 : One char (like ascii)
// - char1 >= 194 && char1 <= 223 : Two chars
// - char1 >= 224 && char1 <= 239 : Three chars
// - char1 >= 240 && char1 <= 244 : Four chars
if (ord ($char) < 128)
{
// One Char like ascii
}
elseif (ord ($char) >= 194 && ord ($char) <= 223)
{
// Two chars
$char .= fgetc (STDIN);
}
elseif (ord ($char) >= 224 && ord ($char) <= 239)
{
// Three chars
$char .= fgetc (STDIN).fgetc (STDIN);
}
elseif (ord ($char) >= 240 && ord ($char) <= 244)
{
// Four chars
$char .= fgetc (STDIN).fgetc (STDIN).fgetc (STDIN);
}
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.
* Manage correctely the backspace, the Ctrl+W to remove word...
* @param string $propo Preset the text for the user
* @param boolean|string $stopperChar The chars to stop the analysis and
* return the result
* @return The typed string
*/
public function readline ($propo = "", $stopperChar = false)
// {{{
{
// Gets can not delete chars before the call. Keep the prompt (if exists)
if (! is_string ($propo))
$this->consoleException ("Invalid proposition provided to readline");
$prompt = $this->lineContent;
$minLength = mb_strlen ($this->lineContent) + 1;
// Manage the history and a temporary buffer if the user has already type
// something before calling the history
$historyPos = count ($this->history);
$string = $propo;
echo $string;
$this->lineContent = $prompt.$string;
// The cursor position from last char of line.
$cursorPos = mb_strlen ($this->lineContent) + 1;
while (1)
{
$char = $this->getKey ();
if ($stopperChar === false && $char === "\n")
{
// End of process without stopperChars
$this->lineContent = "";
break;
}
if ($stopperChar !== false &&
in_array ($char, $this->mb_str_split ($stopperChar)))
{
// End of process with stopperChars
$this->lineContent = "";
break;
}
if ($this->completionKeys !== false &&
in_array ($char, $this->mb_str_split ($this->completionKeys)))
// Manage autocompletion
// {{{
{
$completeArr = call_user_func ($this->completionFunction, $string);
$isAssoc = is_array ($completeArr) &&
array_diff_key ($completeArr, array_keys (array_keys ($completeArr)));
if (is_array ($completeArr) && count ($completeArr) === 1)
{
if (substr ($string, -1) !== " ")
{
// Continuous word is unique : replace the last word by the proposal
$pos = mb_strrpos ($string, " ");
if ($pos === false)
$string = "";
else
{
$string = mb_substr ($string, 0, $pos +1);
}
}
else
{
// Next word is unique : add this to the string
}
if ($isAssoc)
$string .= key ($completeArr);
else
$string .= reset ($completeArr);
}
elseif (is_array ($completeArr) && count ($completeArr))
{
// Multiple answers : display them
// Manage if all the answers start by the same chars : add them to the
// $string
if ($isAssoc)
$possibilities = array_keys ($completeArr);
else
$possibilities = $completeArr;
$addChars = $this->shortestIdenticalValues ($possibilities);
if ($addChars !== "")
{
$pos = mb_strrpos ($string, " ");
if ($pos === false)
$string = $addChars;
else
{
$string = mb_substr ($string, 0, $pos + 1).$addChars;
}
}
if ($isAssoc)
{
// In associative array, the key is the possible answer to
// autocompletion, and the value is the helper message
// Get the largest key length to make a beautiful alignment
// Get the smaller key length to found a affined answer
$maxlen = 0;
foreach ($completeArr as $key => $val)
{
$maxlen = max ($maxlen, mb_strlen ($key));
}
// Display the keys and helpers in an aligned form
$maxlen = $maxlen + 5;
echo "\n";
foreach ($completeArr as $key => $val)
printf ("%-${maxlen}s %s\n", $key, $val);
}
else
{
echo "\n".implode ("\n", $completeArr)."\n";
}
}
if (is_array ($completeArr) && count ($completeArr))
{
$cursorPos = mb_strlen ($prompt.$string) + 1;
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
}
// }}}
elseif (ord ($char) === 3)
// Abort (Ctrl+C)
// {{{
{
$this->debug ("Abort Ctrl+C : ".ord ($char));
$this->lineContent = "";
$string = "";
$this->clearLine ();
echo "$prompt\n";
break;
}
// }}}
elseif (ord ($char) === 4)
// Logout (Ctrl+D)
// {{{
{
$this->debug ("Logout Ctrl+D : ".ord ($char));
$string = "exit\n";
$this->rewriteLine ($prompt.$string);
return $string;
}
// }}}
elseif (ord($char) === 12)
// Refresh page (Ctrl+L)
// {{{
{
$this->debug ("Refresh Ctrl+L : ".ord ($char));
echo "\033[2J\033[;H\033c";
$cursorPos = mb_strlen ($prompt.$string) + 1;
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
elseif (ord($char) === 21)
// Empty line from prompt to cursor (Ctrl+U)
// {{{
{
$this->debug ("Empty line from prompt to cursor Ctrl+U : ".ord ($char));
$string = mb_substr ($string, $cursorPos - $minLength);
$cursorPos = $minLength;
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
elseif (ord($char) === 23)
// Remove the last word (Ctrl+W)
// {{{
{
$this->debug ("Remove the last word Ctrl+W : ".ord ($char));
$tmp = mb_substr ($string, 0, $cursorPos - $minLength);
$end = mb_substr ($string, $cursorPos - $minLength);
$tmp = rtrim ($tmp);
$pos = mb_strrpos ($tmp, " ");
if ($pos !== false)
$pos++;
$string = mb_substr ($string, 0, $pos).$end;
$cursorPos = $minLength + $pos;
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
elseif (ord($char) === 127)
// Remove the previous char (Backspace)
// {{{
{
$this->debug ("Remove the previous char (Backspace) : ".ord ($char));
if ($cursorPos <= $minLength)
continue;
$strArr = $this->mb_str_split ($string);
$cursorPos--;
unset ($strArr[$cursorPos - $minLength]);
$string = implode ($strArr);
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
elseif (ord ($char{0}) === 27)
{
// ESC SEQUENCE
$sequence = "";
foreach (str_split ($char) as $key)
$sequence .= ord ($key)." ";
$this->debug ("ESC SEQUENCE : $sequence");
if ($char === chr (27).chr (91).chr (49).chr (59).chr (53).chr (67))
// Cursor right + Ctrl : cursor jump by word
// {{{
{
$this->debug ("Cursor right + Ctrl");
$tmp = mb_substr ($string, $cursorPos - $minLength);
$tmp = ltrim ($tmp);
$pos = strpos ($tmp, " ");
if ($pos !== false)
$cursorPos += $pos +1 ;
else
$cursorPos = mb_strlen ($prompt.$string) + 1;
$this->moveCursor ($cursorPos);
}
// }}}
elseif ($char === chr (27).chr (91).chr (49).chr (59).chr (53).chr (68))
// Cursor left + Ctrl : cursor jump by word
// {{{
{
$this->debug ("Cursor left + Ctrl");
$tmp = mb_substr ($string, 0, $cursorPos - $minLength);
$tmp = rtrim ($tmp);
$pos = strrpos ($tmp, " ");
if ($pos !== false) $pos++;
$cursorPos = $minLength + $pos;
$this->moveCursor ($cursorPos);
}
// }}}
elseif ($char === chr (27).chr (91).chr (65))
// Cursor up : display the previous history if defined
// {{{
{
$this->debug ("Cursor up");
if (! isset ($historyTmp))
{
$historyTmp = $string;
$historyTmpPos = $cursorPos;
}
if ($historyPos > 0)
{
$historyPos--;
$slice = array_slice ($this->history, $historyPos, 1);
$string = reset ($slice);
$cursorPos = mb_strlen ($prompt.$string) + 1;
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
}
// }}}
elseif ($char === chr (27).chr (91).chr (66))
// Cursor down : display the next history if defined
// {{{
{
$this->debug ("Cursor down");
if ($historyPos < count ($this->history) - 1)
{
$historyPos++;
$slice = array_slice ($this->history, $historyPos, 1);
$string = reset ($slice);
$cursorPos = mb_strlen ($prompt.$string) + 1;
}
elseif (isset ($historyTmp))
{
$string = $historyTmp;
$cursorPos = $historyTmpPos;
unset ($historyTmp);
}
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
elseif ($char === chr (27).chr (91).chr (67))
// Cursor right
// {{{
{
$this->debug ("Cursor right");
if ($cursorPos <= mb_strlen ($this->lineContent))
{
$cursorPos++;
$this->moveCursor ($cursorPos);
}
}
// }}}
elseif ($char === chr (27).chr (91).chr (68))
// Cursor left
// {{{
{
$this->debug ("Cursor left");
if ($cursorPos > $minLength)
{
$cursorPos--;
$this->moveCursor ($cursorPos);
}
}
// }}}
elseif ($char === chr (27).chr (91).chr (70))
// End key
// {{{
{
$this->debug ("End key");
$cursorPos = $minLength + mb_strlen ($string);
$this->moveCursor ($cursorPos);
}
// }}}
elseif ($char === chr (27).chr (91).chr (72))
// Home key
// {{{
{
$this->debug ("Home key");
$cursorPos = $minLength;
$this->moveCursor ($cursorPos);
}
// }}}
elseif ($char === chr (27).chr (91).chr (51).chr (126))
// Remove the char under the cursor (Delete)
// {{{
{
$this->debug ("Delete key");
if ($cursorPos > mb_strlen ($prompt.$string))
continue;
$strArr = $this->mb_str_split ($string);
unset ($strArr[$cursorPos - $minLength]);
$string = implode ($strArr);
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
}
elseif (in_array (ord ($char), $this->nonWriteableChar))
// Non writeable char : skip it
// {{{
{
$this->debug ("Non writeable char : ".ord ($char));
}
// }}}
else
// Normal char : Add it to the string
// {{{
{
$this->debug ("Normal char : ".ord ($char));
$strArr = $this->mb_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++;
$this->rewriteLine ($prompt.$string);
$this->moveCursor ($cursorPos);
}
// }}}
}
$this->debug ("End of readline '$string'");
return $string;
}
// }}}
/** Rewrite the line with the provided $text.
* Delete all the old data
* @param string $text The new text to use on line
*/
private function rewriteLine ($text)
// {{{
{
$this->debug ("Call rewriteLine ($text)");
if ($this->echoMode)
{
$this->clearLine ();
$this->lineContent = $text;
echo $this->lineContent;
$this->cursorPos = mb_strlen ($this->lineContent);
}
}
// }}}
/** Move the cursor on position $position. The first column is $cursorPos=1
* @param integer $cursorPos The new position on line
*/
private function moveCursor ($cursorPos)
// {{{
{
$this->debug ("Call moveCursor ($cursorPos)");
if ($cursorPos < 1)
$this->consoleException ("MoveCursor lesser than one : $cursorPos");
if ($this->echoMode)
{
$oldLength = mb_strlen ($this->lineContent);
// 1. Calculate on which line the cursor is positionned
$cursorLine = 1 + floor ((-1+$this->cursorPos) / $this->termWidth);
// 2. Return the cursor to the first line
for ($i = $cursorLine ; $i > 1 ; $i--)
echo chr (27).chr (91).chr (49).chr (65)."\r";
// 3. Down the cursor to the wanted line
$wantedLine = ceil ($cursorPos / $this->termWidth);
if ($wantedLine > 1)
{
for ($i = 1 ; $i < $wantedLine ; $i++)
echo "\r".chr (27).chr (91).chr (49).chr (66)."\r";
}
// 4. Move the cursor on the last line
$needMovePos = -1 + $cursorPos - ($wantedLine - 1) * $this->termWidth;
echo "\r".str_repeat (chr (27).chr (91).chr (67), $needMovePos);
$this->cursorPos = $cursorPos;
}
}
// }}}
/** Clear the existing line.
*/
public function clearLine ()
// {{{
{
$this->debug ("Call clearLine");
$oldLength = mb_strlen ($this->lineContent);
// 1. Calculate on which line the cursor is positionned
$cursorLine = 1 + floor ((-1+$this->cursorPos) / $this->termWidth);
$lastLines = 1 + floor ((1+$oldLength) / $this->termWidth);
$this->debug ("==> clearLine : oldLength=$oldLength, ".
"cursorLine=$cursorLine, lastLines=$lastLines");
for ($i = $cursorLine ; $i < $lastLines ; $i++)
{
$this->debug ("==> clearLine : go Down (i=$i<$lastLines)");
echo "\033[1B";
}
// 3. Remove the lines from lastLines to line 1
if ($lastLines > 1)
{
for ($i = $lastLines ; $i > 1 ; $i--)
{
$this->debug ("==> clearLine : Remove line up (i=$i<$lastLines)");
echo "\r\033[K\033[1A\r";
}
}
// 4. Clean the line 1
$this->debug ("==> clearLine : Remove line 1");
echo "\r\033[K";
$this->lineContent = "";
$this->cursorPos = 1;
}
// }}}
/** Clear all the screen and remove the scroll of the screen
*/
public function clearScreen ()
// {{{
{
echo "\033[2J\033[;H\033c";
}
// }}}
/** Call a specific function when a completion key is pressed
* The function must get the partial text as first parameter, and must return
* an array with the possibilities
* If only one possibility is returned, the console will be immediately
* updated.
* @param string|bool $completionKeys The list of the completion keys. False
* unset the method
* @param callable $completionFunction The function called when one of the
* completion keys is pressed.
*/
public function completeFunction ($completionKeys, $completionFunction)
// {{{
{
if (! is_string ($completionKeys) && ! is_boolean ($completionKeys))
$this->consoleException ("Can not set the completionKeys : not a string");
if ($completionKeys === true)
$this->consoleException ("Can not set the completionKeys : not false");
if (! is_callable ($completionFunction))
$this->consoleException ("Can not set the completionFunction : ".
"not a callable function");
$this->completionKeys = $completionKeys;
$this->completionFunction = $completionFunction;
}
// }}}
/** Get the actual history in memory
*/
public function getHistory ()
// {{{
{
return $this->history;
}
// }}}
/** Clear the history
* This method do NOT write the empty history on disk
*/
public function clearHistory ()
// {{{
{
$this->history = array ();
return $this;
}
// }}}
/** Write the history to disk.
* @param string $historyFile The history file where the history is stored
*/
public function writeHistory ($historyFile)
// {{{
{
if (file_exists ($historyFile))
{
if (! is_writeable ($historyFile))
$this->consoleException ("History file '$historyFile' ".
"is not writeable");
$history = file_get_contents ($historyFile);
if ($history === false)
$this->consoleException ("History file '$historyFile' can not be read");
$historyArr = explode ("\n", $history);
if (! isset ($historyArr[0]) || $historyArr[0] !== "__HISTORY__")
$this->consoleException ("History file '$historyFile' ".
"is not an history file : do not touch\n");
}
elseif (! file_exists (dirname ($historyFile)))
$this->consoleException ("History file '$historyFile' ".
"can not be created: parent directory doesn't exists");
elseif (! is_dir (dirname ($historyFile)))
$this->consoleException ("History file '$historyFile' ".
"can not be created: parent directory is not a directory");
file_put_contents ($historyFile, "__HISTORY__\n");
$history = "";
foreach ($this->history as $time => $command)
$history .= "$time $command\n";
file_put_contents ($historyFile, $history, FILE_APPEND|LOCK_EX);
return $this;
}
// }}}
/** Read the history from the disk
* If the file doesn't exists, return an empty array
* @param string $historyFile The history file where the history is stored
* @return the read history with timestamp as key and command as value
*/
public function readHistory ($historyFile)
// {{{
{
if (! file_exists ($historyFile))
{
$this->history = array ();
return array ();
}
if (! is_readable ($historyFile))
$this->consoleException ("History file '$historyFile' can not be read");
$history = file_get_contents ($historyFile);
if ($history === false)
$this->consoleException ("History file '$historyFile' can not be read");
$historyArr = explode ("\n", $history);
if (! isset ($historyArr[0]) || $historyArr[0] !== "__HISTORY__")
$this->consoleException ("History file '$historyFile' ".
"is not an history file : do not touch\n");
array_shift ($historyArr);
foreach ($historyArr as $line)
{
@list ($time, $command) = @explode (" ", $line, 2);
if ($time === null || $command === null || ! ctype_digit ($time))
continue;
$this->history[$time] = $command;
}
return $this->history;
}
// }}}
/** Add a new entry in history.
* The new line can not be empty : it is not stored, but without error
* This method do NOT write the history on disk : you must use writeHistory
* @param string The new entry to add in history
*/
public function addHistory ($line)
// {{{
{
if (! is_string ($line))
$this->consoleException ("Can not add line to history : ".
"it is not a string");
if (trim ($line) === "")
return $this;
$this->history[time()] = $line;
$this->history = array_slice ($this->history, -$this->historyMaxSize, null,
true);
return $this;
}
// }}}
/** Get/Set the maximum number of entries in the history
* If null, get the defined maximum number
* @param integer|null $historyMaxSize The maximum number of entries
*/
public function historyMaxSize ($historyMaxSize = null)
// {{{
{
if ($historyMaxSize === null)
return $this->historyMaxSize;
if (intval ($historyMaxSize) < 1)
$this->consoleException ("Can not set the historyMaxSize : ".
"negative value provided");
$this->historyMaxSize = intval ($historyMaxSize);
}
// }}}
/** Error management
* @param string $message The message to throw in the exception
*/
public function consoleException ($message)
// {{{
{
throw new \Exception ($message, 500);
}
// }}}
/** Set the text color
* @param integer $colorNum The color number to use
*/
public function colorText ($colorNum)
// {{{
{
if (! is_int ($colorNum))
$this->consoleException ("ColorNum provided to colorText is not an ".
"integer");
echo "\033[38;5;${colorNum}m";
}
// }}}
/** Set the background text color
* @param integer $colorNum The color number to use
*/
public function colorBackgroundText ($colorNum)
// {{{
{
if (! is_int ($colorNum))
$this->consoleException ("ColorNum provided to colorBackgroundText not ".
"an integer");
echo "\033[48;5;${colorNum}m";
}
// }}}
/** Reset the colors
*/
public function colorReset ()
// {{{
{
echo "\033[0m";
}
// }}}
/** Underline the text
* @param boolean $underline True to underline, false to remove the underline
*/
public function textUnderline ($underline)
// {{{
{
if ($underline === false)
$underline = 2;
else
$underline = "";
echo "\033[${underline}4m";
}
// }}}
/** Bold the text
* @param boolean $bold True to bold, false to remove the bold
*/
public function textBold ($bold)
// {{{
{
if ($bold === false)
$bold = 0;
else
$bold = 1;
echo "\033[${bold}m";
}
// }}}
/** Tokenize the provided line and aggragate if there is single or double
* quotes.
* Trim the spaces
* @param string $line The line to tokenize
* @return array The tokens
*/
static public function tokenize ($line)
// {{{
{
$tokens = array ();
$token = strtok (trim ($line),' ');
while ($token)
{
// find double quoted tokens
if ($token{0}=='"') { $token .= ' '.strtok('"').'"'; }
// find single quoted tokens
if ($token{0}=="'") { $token .= ' '.strtok("'")."'"; }
$tokens[] = $token;
$token = strtok(' ');
}
return $tokens;
}
// }}}
/** This function return an array with each char, but supports UTF-8
* @param string $string The string to explode
* @param integer $split_length The number of chars in each split
* @return array
*/
private function mb_str_split ($string, $split_length = 1)
// {{{
{
$res = array();
for ($i = 0; $i < mb_strlen ($string); $i += $split_length)
$res[] = mb_substr ($string, $i, $split_length);
return $res;
}
// }}}
/** This function debug the data
* @param mixed $data The data to store
*/
private function debug ($data)
// {{{
{
if ($this->debug === false)
return;
if (is_array ($data) || is_bool ($data))
$data = var_export ($data, true);
file_put_contents ($this->debug, date ("H:i:s")." $data\n", FILE_APPEND);
}
// }}}
/** Look in the array which first chars of each possibilites are identical.
* @param array $completeArr The values to examine
* @return string the identical chars
*/
private function shortestIdenticalValues ($completeArr)
// {{{
{
if (! is_array ($completeArr))
return "";
$minlen = 99999;
foreach ($completeArr as $val)
{
$minlen = min ($minlen, mb_strlen ($val));
}
$identicalString = "";
$sameCharLength = 1 ;
while ($sameCharLength <= $minlen)
{
$tmp = "";
foreach ($completeArr as $val)
{
$part = mb_substr ($val, 0, $sameCharLength);
if ($tmp == "")
$tmp = $part;
if ($tmp !== $part)
{
break 2;
}
}
$identicalString = $tmp;
$sameCharLength++;
}
return $identicalString;
}
// }}}
}