Skip to content

Commit 5126681

Browse files
committed
table formatter class and colored help
The table formatter was made more felxible with dynamic column widths and moved to it's own class. The generated help is now colored, too.
1 parent 0e8e20d commit 5126681

File tree

6 files changed

+494
-57
lines changed

6 files changed

+494
-57
lines changed

examples/complex.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace splitbrain\phpcli\examples;
4+
5+
require __DIR__ . '/../vendor/autoload.php';
6+
7+
use splitbrain\phpcli\CLI;
8+
use splitbrain\phpcli\Options;
9+
10+
class Complex extends CLI
11+
{
12+
13+
/**
14+
* Register options and arguments on the given $options object
15+
*
16+
* @param Options $options
17+
* @return void
18+
*/
19+
protected function setup(Options $options)
20+
{
21+
$options->setHelp('This example sets up additional subcommands using their own options');
22+
$options->registerOption('longflag', 'This is a global flag that applies to all subcommands', 'l');
23+
24+
$options->registerCommand('foo', 'The foo command');
25+
$options->registerCommand('bar', 'The bar command');
26+
27+
$options->registerOption('someflag', 'This is a flag only valid for the foo command', 's', false, 'foo');
28+
$options->registerArgument('file', 'This argument is only required for the foo command', true, 'foo');
29+
30+
$options->registerOption('load', 'Another flag only for the bar command, requiring an argument', 'l', 'input', 'bar');
31+
32+
}
33+
34+
/**
35+
* Your main program
36+
*
37+
* Arguments and options have been parsed when this is run
38+
*
39+
* @param Options $options
40+
* @return void
41+
*/
42+
protected function main(Options $options)
43+
{
44+
45+
switch ($options->getCmd()) {
46+
case 'foo':
47+
$this->success('The foo command was called');
48+
break;
49+
case 'bar':
50+
$this->success('The bar command was called');
51+
break;
52+
default:
53+
$this->error('No known command was called, we show the default help instead:');
54+
echo $options->help();
55+
exit;
56+
}
57+
58+
$this->info('$options->getArgs():');
59+
var_dump($options->getArgs());
60+
61+
}
62+
}
63+
64+
$cli = new Complex();
65+
$cli->run();

examples/simple.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ protected function setup(Options $options)
2121
$options->setHelp('This is a simple example, not using any subcommands');
2222
$options->registerOption('longflag', 'A flag that can also be set with a short option', 'l');
2323
$options->registerOption('file', 'This option expects an argument.', 'f', 'filename');
24+
$options->registerArgument('argument', 'Arguments can be required or optional. This one is optional', false);
2425
}
2526

2627
/**

src/CLI.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public function __construct($autocatch=true)
3131
set_exception_handler(array($this, 'fatal'));
3232
}
3333

34-
$this->options = new Options();
3534
$this->colors = new Colors();
35+
$this->options = new Options($this->colors);
3636
}
3737

3838
/**

src/Options.php

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Options
3838
*/
3939
public function __construct(Colors $colors = null)
4040
{
41-
if(!is_null($colors)) {
41+
if (!is_null($colors)) {
4242
$this->colors = $colors;
4343
} else {
4444
$this->colors = new Colors();
@@ -311,7 +311,8 @@ public function getCmd()
311311
*
312312
* @return array
313313
*/
314-
public function getArgs() {
314+
public function getArgs()
315+
{
315316
return $this->args;
316317
}
317318

@@ -322,42 +323,60 @@ public function getArgs() {
322323
*/
323324
public function help()
324325
{
326+
$tf = new TableFormatter($this->colors);
325327
$text = '';
326328

327329
$hascommands = (count($this->setup) > 1);
328330
foreach ($this->setup as $command => $config) {
329331
$hasopts = (bool)$this->setup[$command]['opts'];
330332
$hasargs = (bool)$this->setup[$command]['args'];
331333

334+
// usage or command syntax line
332335
if (!$command) {
333-
$text .= 'USAGE: ' . $this->bin;
336+
$text .= $this->colors->wrap('USAGE:', 'brown');
337+
$text .= "\n";
338+
$text .= ' ' . $this->bin;
339+
$mv = 2;
334340
} else {
335-
$text .= "\n$command";
341+
$text .= "\n";
342+
$text .= $this->colors->wrap(' ' . $command, 'purple');
343+
$mv = 4;
336344
}
337345

338346
if ($hasopts) {
339-
$text .= ' <OPTIONS>';
347+
$text .= ' ' . $this->colors->wrap('<OPTIONS>', 'green');
348+
}
349+
350+
if (!$command && $hascommands) {
351+
$text .= ' ' . $this->colors->wrap('<COMMAND> ...', 'purple');
340352
}
341353

342354
foreach ($this->setup[$command]['args'] as $arg) {
343-
if ($arg['required']) {
344-
$text .= ' <' . $arg['name'] . '>';
345-
} else {
346-
$text .= ' [<' . $arg['name'] . '>]';
355+
$out = $this->colors->wrap('<' . $arg['name'] . '>', 'cyan');
356+
357+
if (!$arg['required']) {
358+
$out = '[' . $out . ']';
347359
}
360+
$text .= ' ' . $out;
348361
}
349362
$text .= "\n";
350363

364+
// usage or command intro
351365
if ($this->setup[$command]['help']) {
352366
$text .= "\n";
353-
$text .= $this->tableFormat(
354-
array(2, 72),
367+
$text .= $tf->format(
368+
array($mv, '*'),
355369
array('', $this->setup[$command]['help'] . "\n")
356370
);
357371
}
358372

373+
// option description
359374
if ($hasopts) {
360-
$text .= "\n OPTIONS\n\n";
375+
if (!$command) {
376+
$text .= "\n";
377+
$text .= $this->colors->wrap('OPTIONS:', 'brown');
378+
}
379+
$text .= "\n";
361380
foreach ($this->setup[$command]['opts'] as $long => $opt) {
362381

363382
$name = '';
@@ -373,28 +392,43 @@ public function help()
373392
$name .= ' <' . $opt['needsarg'] . '>';
374393
}
375394

376-
$text .= $this->tableFormat(
377-
array(2, 20, 52),
378-
array('', $name, $opt['help'])
395+
$text .= $tf->format(
396+
array($mv, '30%', '*'),
397+
array('', $name, $opt['help']),
398+
array('', 'green', '')
379399
);
380400
$text .= "\n";
381401
}
382402
}
383403

404+
// argument description
384405
if ($hasargs) {
406+
if (!$command) {
407+
$text .= "\n";
408+
$text .= $this->colors->wrap('ARGUMENTS:', 'brown');
409+
}
385410
$text .= "\n";
386411
foreach ($this->setup[$command]['args'] as $arg) {
387412
$name = '<' . $arg['name'] . '>';
388413

389-
$text .= $this->tableFormat(
390-
array(2, 20, 52),
391-
array('', $name, $arg['help'])
414+
$text .= $tf->format(
415+
array($mv, '30%', '*'),
416+
array('', $name, $arg['help']),
417+
array('', 'cyan', '')
392418
);
393419
}
394420
}
395421

396-
if ($command == '' && $hascommands) {
397-
$text .= "\nThis tool accepts a command as first parameter as outlined below:\n";
422+
// head line and intro for following command documentation
423+
if (!$command && $hascommands) {
424+
$text .= "\n";
425+
$text .= $this->colors->wrap('COMMANDS:', 'brown');
426+
$text .= "\n";
427+
$text .= $tf->format(
428+
array($mv, '*'),
429+
array('', 'This tool accepts a command as first parameter as outlined below:')
430+
);
431+
$text .= "\n";
398432
}
399433
}
400434

@@ -425,41 +459,5 @@ private function readPHPArgv()
425459
}
426460
return $argv;
427461
}
428-
429-
/**
430-
* Displays text in multiple word wrapped columns
431-
*
432-
* @param int[] $widths list of column widths (in characters)
433-
* @param string[] $texts list of texts for each column
434-
* @return string
435-
*/
436-
private function tableFormat($widths, $texts)
437-
{
438-
$wrapped = array();
439-
$maxlen = 0;
440-
441-
foreach ($widths as $col => $width) {
442-
$wrapped[$col] = explode("\n", wordwrap($texts[$col], $width - 1, "\n", true)); // -1 char border
443-
$len = count($wrapped[$col]);
444-
if ($len > $maxlen) {
445-
$maxlen = $len;
446-
}
447-
448-
}
449-
450-
$out = '';
451-
for ($i = 0; $i < $maxlen; $i++) {
452-
foreach ($widths as $col => $width) {
453-
if (isset($wrapped[$col][$i])) {
454-
$val = $wrapped[$col][$i];
455-
} else {
456-
$val = '';
457-
}
458-
$out .= sprintf('%-' . $width . 's', $val);
459-
}
460-
$out .= "\n";
461-
}
462-
return $out;
463-
}
464462
}
465463

0 commit comments

Comments
 (0)