Code cleanup, better examples, tasks added
This commit is contained in:
parent
4cd5cc2620
commit
809c04abfa
25
CHANGELOG.md
25
CHANGELOG.md
@ -5,6 +5,25 @@ Changelog and Upgrade Instructions
|
|||||||
|
|
||||||
Major changes:
|
Major changes:
|
||||||
|
|
||||||
* The `Shell::EV_*` constants have been renamed to `Shell::EVT_*`.
|
* The `Shell::EV_*` constants have been renamed to `Shell::EVT_*`.
|
||||||
* The `configure` method has been removed.
|
* The `configure` method has been removed. If you really need to
|
||||||
* Events are now dispatched using the *NoccyLabs/TinyEvent* library.
|
extend the `Shell` class, use the constructor to configure your
|
||||||
|
shell (see *examples/timers.php*)
|
||||||
|
* Events are now dispatched using the *NoccyLabs/TinyEvent* library.
|
||||||
|
The main difference here is in how the event object received. The
|
||||||
|
shell instance is now passed in the object, and you can provide any
|
||||||
|
userdata to be passed on to the handler when you add the listener.
|
||||||
|
* The event `Shell::EVT_BAD_COMMAND` will be fired if a command can
|
||||||
|
not be found, assuming `Context->execute()` does not accept the
|
||||||
|
command. (see *examples/commandevents.php*)
|
||||||
|
* When calling `Context->addCommand()` you can now use both `help`
|
||||||
|
and `descr` as the last argument to provide the description text
|
||||||
|
for the command. This change is intended to make `addCommand` and
|
||||||
|
the doccomment `@descr` more similar.
|
||||||
|
|
||||||
|
New features:
|
||||||
|
|
||||||
|
* Tasks can now be added and removed from the shell. Tasks will receive
|
||||||
|
a call to their `update()` method once per main loop, until they are
|
||||||
|
removed or return false from the `isValid()` method. Tasks need to
|
||||||
|
implement the `TaskInterface` interface. (see *examples/tasks.php*)
|
@ -30,7 +30,7 @@ $myShell->setPrompt("test>");
|
|||||||
$ctx = $myShell->createContext("root");
|
$ctx = $myShell->createContext("root");
|
||||||
$ctx->addCommand("hello", function () {
|
$ctx->addCommand("hello", function () {
|
||||||
echo "Hello World!\n";
|
echo "Hello World!\n";
|
||||||
});
|
}, [ 'descr'=>'Say hello' ]);
|
||||||
|
|
||||||
// Run the shell
|
// Run the shell
|
||||||
$myShell->run();
|
$myShell->run();
|
||||||
|
44
examples/commandevents.php
Normal file
44
examples/commandevents.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This example demonstrates how to use events to catch commands that
|
||||||
|
* have not been handled by any context or builtin.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__."/../vendor/autoload.php";
|
||||||
|
|
||||||
|
use NoccyLabs\Shell\Shell;
|
||||||
|
use NoccyLabs\Shell\Style;
|
||||||
|
use NoccyLabs\Shell\Context;
|
||||||
|
|
||||||
|
$myShell = new Shell();
|
||||||
|
|
||||||
|
// Add a listeners for various command aspects
|
||||||
|
$myShell->addListener(Shell::EVT_BAD_COMMAND, function ($e) {
|
||||||
|
echo "EVT_BAD_COMMAND:\n";
|
||||||
|
printf("| command: %s\n", $e->command);
|
||||||
|
printf("|_args: %s\n", join(" ",$e->args));
|
||||||
|
});
|
||||||
|
$myShell->addListener(Shell::EVT_BEFORE_COMMAND, function ($e) {
|
||||||
|
echo "EVT_BEFORE_COMMAND:\n";
|
||||||
|
printf("| command: %s\n", $e->command);
|
||||||
|
printf("|_args: %s\n", join(" ",$e->args));
|
||||||
|
});
|
||||||
|
$myShell->addListener(Shell::EVT_AFTER_COMMAND, function ($e) {
|
||||||
|
echo "EVT_AFTER_COMMAND:\n";
|
||||||
|
printf("| command: %s\n", $e->command);
|
||||||
|
printf("| args: %s\n", join(" ",$e->args));
|
||||||
|
printf("|_type: %s\n", $e->type);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the initial prompt, not really needed.
|
||||||
|
$myShell->setPrompt("test>");
|
||||||
|
|
||||||
|
// Create an anonymous context and add a command
|
||||||
|
$ctx = $myShell->createContext("root");
|
||||||
|
$ctx->addCommand("hello", function () {
|
||||||
|
echo "Hello World!\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run the shell
|
||||||
|
$myShell->run();
|
60
examples/tasks.php
Normal file
60
examples/tasks.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This example demonstrates how to use events to catch commands that
|
||||||
|
* have not been handled by any context or builtin.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__."/../vendor/autoload.php";
|
||||||
|
|
||||||
|
use NoccyLabs\Shell\Shell;
|
||||||
|
use NoccyLabs\Shell\Style;
|
||||||
|
use NoccyLabs\Shell\Context;
|
||||||
|
use NoccyLabs\Shell\TaskInterface;
|
||||||
|
|
||||||
|
class MyTask implements TaskInterface
|
||||||
|
{
|
||||||
|
protected $start;
|
||||||
|
protected $last;
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->start = time();
|
||||||
|
}
|
||||||
|
public function update()
|
||||||
|
{
|
||||||
|
if ($this->last < time()) {
|
||||||
|
echo date("H:i:s")."\n";
|
||||||
|
$this->last = time();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public function isValid()
|
||||||
|
{
|
||||||
|
return (time() - $this->start < 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$myShell = new Shell();
|
||||||
|
|
||||||
|
$myShell->addListener(Shell::EVT_TASK_CREATED, function ($e) {
|
||||||
|
echo "EVT_TASK_CREATED:\n";
|
||||||
|
printf("|_task: %s\n", get_class($e->task));
|
||||||
|
});
|
||||||
|
$myShell->addListener(Shell::EVT_TASK_DESTROYED, function ($e) {
|
||||||
|
echo "EVT_TASK_DESTROYED:\n";
|
||||||
|
printf("|_task: %s\n", get_class($e->task));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$myShell->addTask(new MyTask());
|
||||||
|
|
||||||
|
// Set the initial prompt, not really needed.
|
||||||
|
$myShell->setPrompt("test>");
|
||||||
|
|
||||||
|
// Create an anonymous context and add a command
|
||||||
|
$ctx = $myShell->createContext("root");
|
||||||
|
$ctx->addCommand("hello", function () {
|
||||||
|
echo "Hello World!\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run the shell
|
||||||
|
$myShell->run();
|
@ -132,6 +132,7 @@ class Context
|
|||||||
$info = $this->commandInfo[$command];
|
$info = $this->commandInfo[$command];
|
||||||
$args = array_key_exists("args",$info)?$info['args']:"";
|
$args = array_key_exists("args",$info)?$info['args']:"";
|
||||||
$help = array_key_exists("help",$info)?$info['help']:"";
|
$help = array_key_exists("help",$info)?$info['help']:"";
|
||||||
|
$help = $help?:(array_key_exists("descr",$info)?$info['descr']:"");
|
||||||
$ret[trim("{$command} {$args}")] = $help;
|
$ret[trim("{$command} {$args}")] = $help;
|
||||||
}
|
}
|
||||||
return $ret;
|
return $ret;
|
||||||
|
@ -13,12 +13,14 @@ class Shell
|
|||||||
const EVT_UPDATE_PROMPT = "shell.prompt"; // called to update the prompt
|
const EVT_UPDATE_PROMPT = "shell.prompt"; // called to update the prompt
|
||||||
const EVT_BEFORE_COMMAND = "shell.command.before"; // before a command is executed
|
const EVT_BEFORE_COMMAND = "shell.command.before"; // before a command is executed
|
||||||
const EVT_AFTER_COMMAND = "shell.command.after"; // after a command is executed
|
const EVT_AFTER_COMMAND = "shell.command.after"; // after a command is executed
|
||||||
const EVT_NO_COMMAND = "shell.command.missing"; // no such command found
|
const EVT_BAD_COMMAND = "shell.command.bad"; // no such command found
|
||||||
const EVT_CONTEXT_CHANGED = "shell.context"; // a new context is activated
|
const EVT_CONTEXT_CHANGED = "shell.context"; // a new context is activated
|
||||||
const EVT_SHELL_START = "shell.start"; // the shell is about to start
|
const EVT_SHELL_START = "shell.start"; // the shell is about to start
|
||||||
const EVT_SHELL_STOP = "shell.stop"; // the shell is about to exit
|
const EVT_SHELL_STOP = "shell.stop"; // the shell is about to exit
|
||||||
const EVT_SHELL_ABORT = "shell.abort"; // the shell was aborted (ctrl-c)
|
const EVT_SHELL_ABORT = "shell.abort"; // the shell was aborted (ctrl-c)
|
||||||
const EVT_SHELL_ESCAPE = "shell.escape"; // escape key pressed
|
const EVT_SHELL_ESCAPE = "shell.escape"; // escape key pressed
|
||||||
|
const EVT_TASK_CREATED = "task.created"; // a task was created
|
||||||
|
const EVT_TASK_DESTROYED = "task.destryed"; // a task was removed or invalidated
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var LineRead The lineread instance
|
* @var LineRead The lineread instance
|
||||||
@ -37,7 +39,7 @@ class Shell
|
|||||||
*/
|
*/
|
||||||
protected $timers = [];
|
protected $timers = [];
|
||||||
/**
|
/**
|
||||||
* @var object[] Running subtasks
|
* @var TaskInterface[] Created tasks
|
||||||
*/
|
*/
|
||||||
protected $tasks = [];
|
protected $tasks = [];
|
||||||
/**
|
/**
|
||||||
@ -186,6 +188,37 @@ class Shell
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a task to be update():d in the main loop.
|
||||||
|
*
|
||||||
|
* @param TaskInterface $task The task to add
|
||||||
|
*/
|
||||||
|
public function addTask(TaskInterface $task)
|
||||||
|
{
|
||||||
|
if ($this->dispatchEvent(self::EVT_TASK_CREATED, [
|
||||||
|
'task' => $task
|
||||||
|
])->isPropagationStopped()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$this->tasks[] = $task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a previously added task. This can also be done by the task returning
|
||||||
|
* false from its isValid() method.
|
||||||
|
*
|
||||||
|
* @param TaskInterface $task The task to remove
|
||||||
|
*/
|
||||||
|
public function removeTask(TaskInterface $task)
|
||||||
|
{
|
||||||
|
$this->tasks = array_filter($this->tasks, function ($item) use ($task) {
|
||||||
|
return ($item != $task);
|
||||||
|
});
|
||||||
|
$this->dispatchEvent(self::EVT_TASK_DESTROYED, [
|
||||||
|
'task' => $task
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the prompt text
|
* Set the prompt text
|
||||||
*
|
*
|
||||||
@ -268,7 +301,19 @@ class Shell
|
|||||||
*/
|
*/
|
||||||
public function executeCommand($command, ...$args)
|
public function executeCommand($command, ...$args)
|
||||||
{
|
{
|
||||||
|
if ($this->dispatchEvent(self::EVT_BEFORE_COMMAND, [
|
||||||
|
'command' => $command,
|
||||||
|
'args' => $args
|
||||||
|
])->isPropagationStopped()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->executeBuiltin($command, ...$args)) {
|
if ($this->executeBuiltin($command, ...$args)) {
|
||||||
|
$this->dispatchEvent(self::EVT_AFTER_COMMAND, [
|
||||||
|
'command' => $command,
|
||||||
|
'args' => $args,
|
||||||
|
'type' => 'builtin'
|
||||||
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,11 +323,31 @@ class Shell
|
|||||||
if ($ret instanceof Context) {
|
if ($ret instanceof Context) {
|
||||||
$this->pushContext($ret);
|
$this->pushContext($ret);
|
||||||
}
|
}
|
||||||
|
$this->dispatchEvent(self::EVT_AFTER_COMMAND, [
|
||||||
|
'command' => $command,
|
||||||
|
'args' => $args,
|
||||||
|
'type' => 'command'
|
||||||
|
]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call 'execute' on the current context
|
// Call 'execute' on the current context
|
||||||
if ($this->context->execute($command, ...$args)) {
|
if ($this->context->execute($command, ...$args)) {
|
||||||
|
$this->dispatchEvent(self::EVT_AFTER_COMMAND, [
|
||||||
|
'command' => $command,
|
||||||
|
'args' => $args,
|
||||||
|
'type' => 'execute'
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire the EVT_BAD_COMMAND event and return if the event propagation
|
||||||
|
// has been stopped.
|
||||||
|
$evt = $this->dispatchEvent(self::EVT_BAD_COMMAND, [
|
||||||
|
'command'=>$command,
|
||||||
|
'args'=>$args
|
||||||
|
]);
|
||||||
|
if ($evt->isPropagationStopped()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,12 +395,12 @@ class Shell
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ksort($ghelp);
|
ksort($ghelp);
|
||||||
//printf("Commands in current context:\n");
|
printf("Commands in current context:\n");
|
||||||
foreach ($help as $command=>$info) {
|
foreach ($help as $command=>$info) {
|
||||||
printf(" %-20s %s\n", $command, $info);
|
printf(" %-20s %s\n", $command, $info);
|
||||||
}
|
}
|
||||||
if (count($ghelp)) {
|
if (count($ghelp)) {
|
||||||
//printf("\nImported from parent contexts:\n");
|
printf("\nImported from parent contexts:\n");
|
||||||
foreach ($ghelp as $command=>$info) {
|
foreach ($ghelp as $command=>$info) {
|
||||||
printf(" %-20s %s\n", $command, $info);
|
printf(" %-20s %s\n", $command, $info);
|
||||||
}
|
}
|
||||||
@ -411,8 +476,14 @@ class Shell
|
|||||||
foreach ($this->timers as $timer) {
|
foreach ($this->timers as $timer) {
|
||||||
$timer->update();
|
$timer->update();
|
||||||
}
|
}
|
||||||
foreach ($this->tasks as $task) {
|
foreach ($this->tasks as $taskidx=>$task) {
|
||||||
$task->update();
|
$task->update();
|
||||||
|
if (!$task->isValid()) {
|
||||||
|
$this->dispatchEvent(self::EVT_TASK_DESTROYED, [
|
||||||
|
'task' => $task
|
||||||
|
]);
|
||||||
|
unset($this->tasks[$taskidx]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($buffer) {
|
if ($buffer) {
|
||||||
$this->executeBuffer($buffer);
|
$this->executeBuffer($buffer);
|
||||||
@ -423,11 +494,13 @@ class Shell
|
|||||||
if (trim($output)) {
|
if (trim($output)) {
|
||||||
$this->lineReader->erase();
|
$this->lineReader->erase();
|
||||||
echo rtrim($output)."\n";
|
echo rtrim($output)."\n";
|
||||||
$this->lineReader->redraw();
|
if ($this->running)
|
||||||
|
$this->lineReader->redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->context) {
|
if (!$this->context) {
|
||||||
$this->stop();
|
$this->stop();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($buffer) {
|
if ($buffer) {
|
||||||
@ -456,6 +529,7 @@ class Shell
|
|||||||
$data['context'] = $this->context;
|
$data['context'] = $this->context;
|
||||||
$event = new Event($type, $data);
|
$event = new Event($type, $data);
|
||||||
$this->emitEvent($type, $event);
|
$this->emitEvent($type, $event);
|
||||||
|
return $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
10
lib/TaskInterface.php
Normal file
10
lib/TaskInterface.php
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace NoccyLabs\Shell;
|
||||||
|
|
||||||
|
interface TaskInterface
|
||||||
|
{
|
||||||
|
public function update();
|
||||||
|
|
||||||
|
public function isValid();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user