Compare commits

..

7 Commits

Author SHA1 Message Date
Dominique FOURNIER
42766d07a5 File: lockUN must remve the file from local stack to allow relocking 2022-04-19 11:06:43 +02:00
Dominique FOURNIER
ffd75a57d4 Console : allow to know if the TTY is enabled (or if the console is called from pipe) 2022-03-18 11:08:47 +01:00
Dominique FOURNIER
c4031b71f8 Console : manage if Console is called from a pipe (without tty available) 2022-03-18 10:57:12 +01:00
5ff8278ad0 .gitignore php-cs-fixer-cache 2022-03-14 21:44:12 +01:00
Dominique FOURNIER
3eaffe64f9 Console : Allow to delete char with \b too (ASCII 0x08) 2022-03-14 20:56:22 +01:00
Dominique FOURNIER
3b5a291de9 Add StateMachine support 2021-07-21 16:11:43 +02:00
Dominique FOURNIER
f474a92a13 Rest : do not try to load the output classes 2021-05-28 12:02:35 +02:00
5 changed files with 154 additions and 7 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@
/flags
/test
tags
.php-cs-fixer.cache

View File

@@ -89,13 +89,13 @@ class Console
{
if (! function_exists ("exec"))
throw $this->ConsoleException ("No exec support in PHP");
$this->initSttyState = exec ("stty -g");
$this->initSttyState = exec ("stty -g 2>/dev/null");
// 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");
exec ("stty -echo -icanon min 1 time 0 2>/dev/null");
$this->updateTerminalSize ();
}
// }}}
@@ -107,7 +107,7 @@ class Console
{
$this->termWidth = 80;
$this->termHeight = 25;
$termSize = exec ("stty size", $null, $rc);
$termSize = exec ("stty size 2>/dev/null", $null, $rc);
if ($rc === 0)
{
list ($termHeight, $termWidth) = explode (" ", $termSize);
@@ -128,6 +128,7 @@ class Console
public function __destruct ()
// {{{
{
if ($this->initSttyState !== "")
exec ("stty $this->initSttyState");
$this->colorReset ();
$this->textUnderline (false);
@@ -207,7 +208,7 @@ class Console
$char = fgetc (STDIN);
$sequence .= $char;
}
elseif (ord ($char2) === 91)
elseif (ord ($char2) === 91 || ord($char2) === 93)
{
// Start an ESC CSI sequence. Do not display it, just return it.
// The ESC squences are used to communicate the cursor keys, associated
@@ -412,6 +413,13 @@ class Console
$this->debug ("Autocompletion : end '$prompt.$string'");
}
// }}}
elseif (ord ($char) === 0)
// End of file
{
$this->debug("Empty File entry : ".ord($char));
$string = chr(0);
break;
}
elseif (ord ($char) === 3)
// Abort (Ctrl+C)
// {{{
@@ -473,7 +481,7 @@ class Console
$this->moveCursor ($cursorPos);
}
// }}}
elseif (ord($char) === 127)
elseif (ord($char) === 127 || ord($char) === 8)
// Remove the previous char (Backspace)
// {{{
{
@@ -985,6 +993,13 @@ class Console
}
// }}}
/** Return true if the TTY is enabled, or false if the program is called from pipe
*/
public function isTTY()
{
return !! $this->initSttyState;
}
/** Tokenize the provided line and aggragate if there is single or double
* quotes.
* Trim the spaces

View File

@@ -507,6 +507,7 @@ class File
{
$res = flock ($this->locks[$filename], LOCK_UN);
fclose ($this->locks[$filename]);
unset($this->locks[$filename]);
}
return $res;
}

View File

@@ -60,7 +60,6 @@ class Rest
$text = $http->codetext ($code);
header ($_SERVER["SERVER_PROTOCOL"]." $code $text");
$type = $this->chooseType ();
require_once ("domframework/output$type.php");
$constr = __NAMESPACE__."\\Output$type";
$method = "out";
$obj = new $constr ();

131
src/StateMachine.php Normal file
View File

@@ -0,0 +1,131 @@
<?php
/** DomFramework
* @package domframework
* @author Dominique Fournier <dominique@fournier38.fr>
* @license BSD
*/
namespace Domframework;
/**
* This class manage a state machine.
* Define the states and the associated transitions.
* The states and the transitions are methods
* The states are the actions.
* The transitions are tests : return false if the test do not match, or true
* if the test match. The state machine will skip the test if it doesn't match,
* and go to the define toStateName if the test return true.
* Officially, the transitions should not have priority. In practise, the
* transistions are tested in the added order.
*/
class StateMachine
{
private $states = array();
private $transitions = array();
private $debug = false;
public function addState(string $stateName, callable $methodName): self
{
$this->states[$stateName] = new StateMachineState($methodName);
return $this;
}
public function addTransition(string $transitionName, string $fromStateName, string $toStateName, callable $methodName): self
{
if (! key_exists($fromStateName, $this->states))
throw new \Exception("StateMachine can not add Transition from '$fromStateName' : state not defined", 404);
if (! key_exists($toStateName, $this->states))
throw new \Exception("StateMachine can not add Transition to '$toStateName' : state not defined", 404);
if (key_exists($transitionName, $this->transitions))
throw new \Exception("StateMachine can not add Transition '$transitionName' : transition already defined", 406);
$this->transitions[$transitionName] = new StateMachineTransition($fromStateName, $toStateName, $methodName);
return $this;
}
/**
* Start the state machine at the provided state
* Will run until no transition match, then return
*/
public function run(string $stateName)
{
if (! key_exists ($stateName, $this->states))
throw new \Exception("StateMachine can not runState '$stateName' : state not defined", 404);
while(1)
{
$this->debug("Start state '$stateName'");
$rc = $this->states[$stateName]->run();
$this->debug("End state '$stateName' with type '".gettype($rc)."'");
foreach($this->transitions as $transitionName => $transition)
{
if ($transition->getFromStateName() !== $stateName)
continue;
if ($transition->run() === false)
{
$this->debug("Look at transition '$transitionName' : Return FALSE (not match)");
continue;
}
$this->debug("Look at transition '$transitionName' : Match");
$stateName = $transition->getToStateName();
continue 2;
}
break;
}
return $rc;
}
private function debug(string $msg)
{
if ($this->debug === false)
return;
echo "$msg\n";
}
}
class StateMachineState
{
private $methodName;
public function __construct(callable $methodName)
{
$this->methodName = $methodName;
}
public function run()
{
return call_user_func($this->methodName);
}
}
class StateMachineTransition
{
private $fromStateName;
private $toStateName;
private $methodName;
public function __construct(string $fromStateName, string $toStateName, callable $methodName)
{
$this->fromStateName = $fromStateName;
$this->toStateName = $toStateName;
$this->methodName = $methodName;
}
public function getFromStateName(): string
{
return $this->fromStateName;
}
public function getToStateName(): string
{
return $this->toStateName;
}
public function getMethodName(): callable
{
return $this->methodName;
}
public function run()
{
return call_user_func($this->methodName);
}
}