Skip to content

Commit eb79a60

Browse files
committed
More sophisticated CLI example using clue/commander
1 parent 8d50f2c commit eb79a60

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,8 @@ MIT
564564

565565
* If you want to learn more about processing streams of data, refer to the documentation of
566566
the underlying [react/stream](https://github.com/reactphp/stream) component.
567+
* If you build an interactive CLI tool that reads a command line from STDIN, you
568+
may want to use [clue/arguments](https://github.com/clue/php-arguments) in
569+
order to split this string up into its individual arguments and then use
570+
[clue/commander](https://github.com/clue/php-commander) to route to registered
571+
commands and their required arguments.

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,9 @@
2222
},
2323
"autoload": {
2424
"psr-4": { "Clue\\React\\Stdio\\": "src/" }
25+
},
26+
"require-dev": {
27+
"clue/commander": "^1.2",
28+
"clue/arguments": "^2.0"
2529
}
2630
}

examples/03-commander.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
use Clue\React\Stdio\Stdio;
4+
use Clue\Arguments;
5+
use Clue\Commander\Router;
6+
use Clue\Commander\NoRouteFoundException;
7+
8+
require __DIR__ . '/../vendor/autoload.php';
9+
10+
$loop = React\EventLoop\Factory::create();
11+
12+
$stdio = new Stdio($loop);
13+
$readline = $stdio->getReadline();
14+
$readline->setPrompt('> ');
15+
16+
// limit history to HISTSIZE env
17+
$limit = getenv('HISTSIZE');
18+
if ($limit === '' || $limit < 0) {
19+
// empty string or negative value means unlimited
20+
$readline->limitHistory(null);
21+
} elseif ($limit !== false) {
22+
// apply any other value if given
23+
$readline->limitHistory($limit);
24+
}
25+
26+
// register all available commands and their arguments
27+
$router = new Router();
28+
$router->add('exit | quit', function() use ($stdio) {
29+
$stdio->end();
30+
});
31+
$router->add('help', function () use ($stdio) {
32+
$stdio->writeln('Use TAB-completion or use "exit"');
33+
});
34+
$router->add('(echo | print) <words>...', function (array $args) use ($stdio) {
35+
$stdio->writeln(implode(' ', $args['words']));
36+
});
37+
$router->add('printf <format> <args>...', function (array $args) use ($stdio) {
38+
$stdio->writeln(vsprintf($args['format'],$args['args']));
39+
});
40+
41+
// autocomplete the following commands (at offset=0/1 only)
42+
$readline->setAutocomplete(function ($_, $offset) {
43+
return $offset > 1 ? array() : array('exit', 'quit', 'help', 'echo', 'print', 'printf');
44+
});
45+
46+
$stdio->writeln('Welcome to this interactive demo');
47+
48+
// react to commands the user entered
49+
$stdio->on('line', function ($line) use ($router, $stdio, $readline) {
50+
// add all lines from input to history
51+
// skip empty line and duplicate of previous line
52+
$all = $readline->listHistory();
53+
if (trim($line) !== '' && $line !== end($all)) {
54+
$readline->addHistory($line);
55+
}
56+
57+
try {
58+
$args = Arguments\split($line);
59+
} catch (Arguments\UnclosedQuotesException $e) {
60+
$stdio->writeln('Error: Invalid command syntax (unclosed quotes)');
61+
return;
62+
}
63+
64+
// skip empty lines
65+
if (!$args) {
66+
return;
67+
}
68+
69+
try {
70+
$router->handleArgs($args);
71+
} catch (NoRouteFoundException $e) {
72+
$stdio->writeln('Error: Invalid command usage');
73+
}
74+
});
75+
76+
$loop->run();

0 commit comments

Comments
 (0)