├── .travis.yml
├── LICENSE
├── README.md
├── composer.json
├── example.php
├── phpunit.xml.dist
├── src
└── ConsoleKit
│ ├── Colors.php
│ ├── Command.php
│ ├── Console.php
│ ├── ConsoleException.php
│ ├── DefaultOptionsParser.php
│ ├── EchoTextWriter.php
│ ├── FileSystem.php
│ ├── FormatedWriter.php
│ ├── Help.php
│ ├── HelpCommand.php
│ ├── OptionsParser.php
│ ├── StdTextWriter.php
│ ├── TextFormater.php
│ ├── TextWriter.php
│ ├── Utils.php
│ └── Widgets
│ ├── AbstractWidget.php
│ ├── Box.php
│ ├── Checklist.php
│ ├── Dialog.php
│ └── ProgressBar.php
└── tests
├── ConsoleKit
└── Tests
│ ├── ColorsTest.php
│ ├── ConsoleKitTestCase.php
│ ├── ConsoleTest.php
│ ├── DefautOptionsParserTest.php
│ ├── TestCommand.php
│ ├── TestStatic.php
│ ├── TestSubCommand.php
│ ├── TextFormaterTest.php
│ └── UtilsTest.php
└── bootstrap.php
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | php:
3 | - 5.3
4 | - 5.4
5 | script: phpunit
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2012 Maxime Bouroumeau-Fuseau
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ConsoleKit
2 |
3 | PHP 5.3+ library to create command line utilities.
4 |
5 | [](http://travis-ci.org/maximebf/ConsoleKit)
6 |
7 | ## Example
8 |
9 | In *cli.php*:
10 |
11 | writeln('hello world!', ConsoleKit\Colors::GREEN);
18 | }
19 | }
20 |
21 | $console = new ConsoleKit\Console();
22 | $console->addCommand('HelloCommand');
23 | $console->run();
24 |
25 | In the shell:
26 |
27 | $ php cli.php hello
28 | hello world!
29 |
30 | More examples in [example.php](https://github.com/maximebf/ConsoleKit/blob/master/example.php)
31 |
32 | ## Installation
33 |
34 | The easiest way to install ConsoleKit is using [Composer](https://github.com/composer/composer)
35 | with the following requirement:
36 |
37 | {
38 | "require": {
39 | "maximebf/consolekit": ">=1.0.0"
40 | }
41 | }
42 |
43 | Alternatively, you can [download the archive](https://github.com/maximebf/ConsoleKit/zipball/master)
44 | and add the src/ folder to PHP's include path:
45 |
46 | set_include_path('/path/to/src' . PATH_SEPARATOR . get_include_path());
47 |
48 | ConsoleKit does not provide an autoloader but follows the [PSR-0 convention](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md).
49 | You can use the following snippet to autoload ConsoleKit classes:
50 |
51 | spl_autoload_register(function($className) {
52 | if (substr($className, 0, 10) === 'ConsoleKit') {
53 | $filename = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\')) . '.php';
54 | require_once $filename;
55 | }
56 | });
57 |
58 | ## Usage
59 |
60 | ### Options parser
61 |
62 | The default options parser parses an argv-like array.
63 | Items can be of the form:
64 |
65 | - --key=value
66 | - --key
67 | - -a
68 | - -ab (equivalent of -a -b)
69 |
70 | When an option has no value, true will be used. If multiple key/value pairs
71 | with the same key are specified, the "key" value will be an array containing all the values.
72 | If "--" is detected, all folowing values will be treated as a single argument
73 |
74 | Example: the string "-a -bc --longopt --key=value arg1 arg2 -- --any text" will produce the following two arrays:
75 |
76 | $args = array('arg1', 'arg2', '--any text');
77 | $options = array('a' => true, 'b' => true, 'c' => true, 'longopt' => true, 'key' => 'value');
78 |
79 | ### Creating commands
80 |
81 | Any callbacks can be a command. It will receive three parameters: the
82 | arguments array, the options array and the console object.
83 |
84 | function my_command($args, $opts, $console) {
85 | $console->writeln("hello world!");
86 | }
87 |
88 | Commands can also be defined as classes. In this case, they must inherit from `ConsoleKit\Command`
89 | and override the `execute()` method.
90 |
91 | class MyCommand extends ConsoleKit\Command {
92 | public function execute(array $args, array $opts) {
93 | $this->writeln("hello world!");
94 | }
95 | }
96 |
97 | The `ConsoleKit\Command` class offers helper methods, check it out for more info.
98 |
99 | ### Registering commands
100 |
101 | Commands need to be registered in the console object using the `addCommand()` method (or `addCommands()`).
102 |
103 | $console = new ConsoleKit\Console();
104 | $console->addCommand('my_command'); // the my_command function
105 | $console->addCommand('MyCommand'); // the MyCommand class
106 | $console->addCommand(function() { echo 'hello!'; }, 'hello'); // using a closure
107 | // or:
108 | $console->addCommand('hello', function() { echo 'hello!'; }); // alternative when using a closure
109 |
110 | Notice that in the last example we have provided a second argument which is an alias for a command.
111 | As closures have no name, one must be specified.
112 |
113 | The command name for functions is the same as the function name with underscores replaced
114 | by dashes (ie. my\_command becomes my-command).
115 |
116 | The command name for command classes is the short class name without the `Command`
117 | suffix and "dashized" (ie. HelloWorldCommand becomes hello-world).
118 |
119 | ### Running
120 |
121 | Simply call the `run()` method of the console object
122 |
123 | $console->run();
124 | $console->run(array('custom arg1', 'custom arg2')); // overrides $_SERVER['argv']
125 |
126 | ### Automatic help generation
127 |
128 | The *help* command is automatically registered and provides help about available methods based on doc comments.
129 | Check out [example.php](https://github.com/maximebf/ConsoleKit/blob/master/example.php) for example of available tags
130 |
131 | $ php myscript.php help
132 |
133 | ## Formating text
134 |
135 | ### Colors
136 |
137 | The `ConsoleKit\Colors::colorize()` method provides an easy way to colorize a text.
138 | Colors are defined as either a string or an integer (through constants of the `Colors` class).
139 | Available colors: black, red, green, yellow, blue, magenta, cyan, white.
140 |
141 | Foreground colors are also available in a "bold" variant. Suffix the color name with "+bold" or use the OR bit operator with constants.
142 |
143 | echo Colors::colorize('my red text', Colors::RED);
144 | echo Colors::colorize('my red text', 'red');
145 |
146 | echo Colors::colorize('my red bold text', Colors::RED | Colors::BOLD);
147 | echo Colors::colorize('my red bold text', 'red+bold');
148 |
149 | echo Colors::colorize('my red text over yellow background', Colors::RED, Colors::YELLOW);
150 |
151 | ### TextFormater
152 |
153 | The `ConsoleKit\TextFormater` class allows you to format text using the following options:
154 |
155 | - indentation using `setIndent()` or the *indent* option
156 | - quoting using `setQuote()` or the *quote* option
157 | - foreground color using `setFgColor()` or the *fgcolor* option
158 | - background color using `setBgColor()` or the *bgcolor* option
159 |
160 | Options can be defined using `setOptions()` or as the first parameter of the constructor.
161 |
162 | $formater = new ConsoleKit\TextFormater(array('quote' => ' > '));
163 | echo $formater->format("hello!");
164 | // produces: " > hello"
165 |
166 | ## Widgets
167 |
168 | ### Dialog
169 |
170 | Used to interact with the user
171 |
172 | $dialog = new ConsoleKit\Widgets\Dialog($console);
173 | $name = $dialog->ask('What is your name?');
174 | if ($dialog->confirm('Are you sure?')) {
175 | $console->writeln("hello $name");
176 | }
177 |
178 | ### Box
179 |
180 | Wraps text in a box
181 |
182 | $box = new ConsoleKit\Widgets\Box($console, 'my text');
183 | $box->write();
184 |
185 | Produces:
186 |
187 | ********************************************
188 | * my text *
189 | ********************************************
190 |
191 | ### Progress bar
192 |
193 | Displays a progress bar
194 |
195 | $total = 100;
196 | $progress = new ConsoleKit\Widgets\ProgressBar($console, $total);
197 | for ($i = 0; $i < $total; $i++) {
198 | $progress->incr();
199 | usleep(10000);
200 | }
201 | $progress->stop();
202 |
203 |
204 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "maximebf/consolekit",
3 | "description": "Library to create command line utilities",
4 | "keywords": ["console", "shell", "cli", "commands"],
5 | "homepage": "https://github.com/maximebf/ConsoleKit",
6 | "type": "library",
7 | "license": "MIT",
8 | "authors": [{
9 | "name": "Maxime Bouroumeau-Fuseau",
10 | "email": "maxime.bouroumeau@gmail.com",
11 | "homepage": "http://maximebf.com"
12 | }],
13 | "require": {
14 | "php": ">=5.3.0"
15 | },
16 | "autoload": {
17 | "psr-0": {"ConsoleKit": "src/"}
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example.php:
--------------------------------------------------------------------------------
1 | writeln('hello world!', Colors::GREEN);
20 | }
21 | }
22 |
23 | /**
24 | * Says hello to someone
25 | *
26 | * @arg name The name of the person to say hello to
27 | * @opt color The color in which to print the text
28 | */
29 | class SayHelloCommand extends Command
30 | {
31 | public function execute(array $args, array $options = array())
32 | {
33 | $this->context(array('fgcolor' => Utils::get($options, 'color')), function($c) use ($args) {
34 | $c->writeln(sprintf('hello %s!', $args[0]));
35 | });
36 | }
37 | }
38 |
39 | /**
40 | * Commands to say something to someone!
41 | */
42 | class SayCommand extends Command
43 | {
44 | /**
45 | * Says hello to someone
46 | *
47 | * @arg name The name of the person to say hello to
48 | */
49 | public function executeHello(array $args, array $options = array())
50 | {
51 | $name = 'unknown';
52 | if (empty($args)) {
53 | $dialog = new Dialog($this->console);
54 | $name = $dialog->ask('What is your name?', $name);
55 | } else {
56 | $name = $args[0];
57 | }
58 | $this->writeln(sprintf('hello %s!', $name));
59 | }
60 |
61 | /**
62 | * Says hi to someone
63 | *
64 | * @arg name The name of the person to say hello to
65 | */
66 | public function executeHi(array $args, array $options = array())
67 | {
68 | $this->writeln(sprintf('hi %s!', $args[0]));
69 | }
70 | }
71 |
72 | /**
73 | * Displays a progress bar
74 | *
75 | * @opt total Number of iterations
76 | * @opt usleep Waiting time in microsecond between each iteration
77 | */
78 | function progress($args, $options, $console)
79 | {
80 | $total = isset($options['total']) ? $options['total'] : 100;
81 | $usleep = isset($options['usleep']) ? $options['usleep'] : 10000;
82 | $progress = new ProgressBar($console, $total);
83 | for ($i = 0; $i < $total; $i++) {
84 | $progress->incr();
85 | usleep($usleep);
86 | }
87 | $progress->stop();
88 | }
89 |
90 | $console = new Console(array(
91 | 'hello' => 'HelloWorldCommand',
92 | 'SayHelloCommand',
93 | 'SayCommand',
94 | 'progress'
95 | ));
96 |
97 | $console->run();
98 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
15 |
16 | ./tests/ConsoleKit/
17 |
18 |
19 |
20 |
21 |
22 | ./src/ConsoleKit/
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Colors.php:
--------------------------------------------------------------------------------
1 |
19 | * $text = Colors::colorize('hello world', Colors::RED)
20 | * $text = Colors::colorize('hello world', 'red')
21 | * $text = Colors::colorize('hello world', Colors::RED | Colors::BOLD)
22 | * $text = Colors::colorize('hello world', 'red+bold')
23 | * $text = Colors::red('hello world');
24 | *
25 | */
26 | class Colors
27 | {
28 | const RESET = "\033[0m";
29 |
30 | const BLACK = 1;
31 | const RED = 2;
32 | const GREEN = 4;
33 | const YELLOW = 8;
34 | const BLUE = 16;
35 | const MAGENTA = 32;
36 | const CYAN = 64;
37 | const WHITE = 128;
38 |
39 | const BOLD = 256;
40 | const UNDERSCORE = 512;
41 | const BLINK = 1024;
42 | const REVERSE = 2048;
43 | const CONCEAL = 4096;
44 |
45 | /** @var array */
46 | private static $colors = array(
47 | 'black' => self::BLACK,
48 | 'red' => self::RED,
49 | 'green' => self::GREEN,
50 | 'yellow' => self::YELLOW,
51 | 'blue' => self::BLUE,
52 | 'magenta' => self::MAGENTA,
53 | 'cyan' => self::CYAN,
54 | 'white' => self::WHITE
55 | );
56 |
57 | /** @var array */
58 | private static $options = array(
59 | 'bold' => self::BOLD,
60 | 'underscore' => self::UNDERSCORE,
61 | 'blink' => self::BLINK,
62 | 'reverse' => self::REVERSE,
63 | 'conceal' => self::CONCEAL
64 | );
65 |
66 | /** @var array */
67 | private static $codes = array(
68 | self::BLACK => 0,
69 | self::RED => 1,
70 | self::GREEN => 2,
71 | self::YELLOW => 3,
72 | self::BLUE => 4,
73 | self::MAGENTA => 5,
74 | self::CYAN => 6,
75 | self::WHITE => 7,
76 | self::BOLD => 1,
77 | self::UNDERSCORE => 4,
78 | self::BLINK => 5,
79 | self::REVERSE => 7,
80 | self::CONCEAL => 8
81 | );
82 |
83 | /**
84 | * Returns a colorized string
85 | *
86 | * @param string $text
87 | * @param string $fgcolor (a key from the $foregroundColors array)
88 | * @param string $bgcolor (a key from the $backgroundColors array)
89 | * @return string
90 | */
91 | public static function colorize($text, $fgcolor = null, $bgcolor = null)
92 | {
93 | $colors = '';
94 | if ($bgcolor) {
95 | $colors .= self::getBgColorString(self::getColorCode($bgcolor));
96 | }
97 | if ($fgcolor) {
98 | $colors .= self::getFgColorString(self::getColorCode($fgcolor));
99 | }
100 | if ($colors) {
101 | $text = $colors . $text . self::RESET;
102 | }
103 | return $text;
104 | }
105 |
106 | /**
107 | * Returns a text with each lines colorized independently
108 | *
109 | * @param string $text
110 | * @param string $fgcolor
111 | * @param string $bgcolor
112 | * @return string
113 | */
114 | public static function colorizeLines($text, $fgcolor = null, $bgcolor = null)
115 | {
116 | $lines = explode("\n", $text);
117 | foreach ($lines as &$line) {
118 | $line = self::colorize($line, $fgcolor, $bgcolor);
119 | }
120 | return implode("\n", $lines);
121 | }
122 |
123 | /**
124 | * Returns a color code
125 | *
126 | * $color can be a string with the color name, or one of the color constants.
127 | *
128 | * @param int|string $color
129 | * @param array $options
130 | * @return int
131 | */
132 | public static function getColorCode($color, $options = array())
133 | {
134 | $code = (int) $color;
135 | if (is_string($color)) {
136 | $options = array_merge(explode('+', strtolower($color)), $options);
137 | $color = array_shift($options);
138 | if (!isset(self::$colors[$color])) {
139 | throw new ConsoleException("Unknown color '$color'");
140 | }
141 | $code = self::$colors[$color];
142 | }
143 | foreach ($options as $opt) {
144 | $opt = strtolower($opt);
145 | if (!isset(self::$options[$opt])) {
146 | throw new ConsoleException("Unknown option '$color'");
147 | }
148 | $code = $code | self::$options[$opt];
149 | }
150 | return $code;
151 | }
152 |
153 | /**
154 | * Returns a foreground color string
155 | *
156 | * @param int $color
157 | * @return string
158 | */
159 | public static function getFgColorString($colorCode)
160 | {
161 | list($color, $options) = self::extractColorAndOptions($colorCode);
162 | $codes = array_filter(array_merge($options, array("3{$color}")));
163 | return sprintf("\033[%sm", implode(';', $codes));
164 | }
165 |
166 | /**
167 | * Returns a background color string
168 | *
169 | * @param int $color
170 | * @return string
171 | */
172 | public static function getBgColorString($colorCode)
173 | {
174 | list($color, $options) = self::extractColorAndOptions($colorCode);
175 | $codes = array_filter(array_merge($options, array("4{$color}")));
176 | return sprintf("\033[%sm", implode(';', $codes));
177 | }
178 |
179 | /**
180 | * Extracts the options and the color from a color code
181 | *
182 | * @param int $colorCode
183 | * @return array
184 | */
185 | private static function extractColorAndOptions($colorCode)
186 | {
187 | $options = array();
188 | foreach (self::$options as $name => $bit) {
189 | if (($colorCode & $bit) === $bit) {
190 | $options[] = self::$codes[$bit];
191 | $colorCode = $colorCode & ~$bit;
192 | }
193 | }
194 | if (!isset(self::$codes[$colorCode])) {
195 | throw new ConsoleException("Cannot parse color code");
196 | }
197 | return array(self::$codes[$colorCode], $options);
198 | }
199 |
200 | public static function __callStatic($method, $args)
201 | {
202 | return self::colorize($args[0], $method);
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Command.php:
--------------------------------------------------------------------------------
1 |
22 | * class MyCommand extends Command {
23 | * public function execute(array $args, array $options = array()) {
24 | * $this->writeln('hello world');
25 | * }
26 | * }
27 | *
28 | *
29 | * If "execute()" is not overriden, the execution will be forwarded to subcommand
30 | * methods. The subcommand name will be the first argument value and the associated
31 | * method must be prefixed with "execute".
32 | *
33 | *
34 | * class MyCommand extends Command {
35 | * public function executeSub1(array $args, array $options = array()) {
36 | * $this->writeln('hello world');
37 | * }
38 | * public function executeSub2(array $args, array $options = array()) {
39 | * $this->writeln('hello world');
40 | * }
41 | * }
42 | *
43 | *
44 | */
45 | abstract class Command
46 | {
47 | /** @var Console */
48 | protected $console;
49 |
50 | /** @var array */
51 | protected $defaultFormatOptions = array();
52 |
53 | /**
54 | * @param Console $console
55 | */
56 | public function __construct(Console $console)
57 | {
58 | $this->console = $console;
59 | }
60 |
61 | /**
62 | * @return Console
63 | */
64 | public function getConsole()
65 | {
66 | return $this->console;
67 | }
68 |
69 | /**
70 | * If not overriden, will execute the command specified
71 | * as the first argument
72 | *
73 | * Commands must be defined as methods named after the
74 | * command, prefixed with execute (eg. create -> executeCreate)
75 | *
76 | * @param array $args
77 | * @param array $options
78 | */
79 | public function execute(array $args, array $options = array())
80 | {
81 | if (!count($args)) {
82 | throw new ConsoleException("Missing subcommand name");
83 | }
84 |
85 | $command = ucfirst(Utils::camelize(array_shift($args)));
86 | $methodName = "execute$command";
87 | if (!method_exists($this, $methodName)) {
88 | throw new ConsoleException("Command '$command' does not exist");
89 | }
90 |
91 | $method = new ReflectionMethod($this, $methodName);
92 | $params = Utils::computeFuncParams($method, $args, $options);
93 | return $method->invokeArgs($this, $params);
94 | }
95 |
96 | /**
97 | * Formats text using a {@see TextFormater}
98 | *
99 | * @param string $text
100 | * @param int|array $formatOptions Either an array of options for TextFormater or a color code
101 | * @return string
102 | */
103 | public function format($text, $formatOptions = array())
104 | {
105 | if (!is_array($formatOptions)) {
106 | $formatOptions = array('fgcolor' => $formatOptions);
107 | }
108 | $formatOptions = array_merge($this->defaultFormatOptions, $formatOptions);
109 | $formater = new TextFormater($formatOptions);
110 | return $formater->format($text);
111 | }
112 |
113 | /**
114 | * Executes the closure with a {@see FormatedWriter} object as the first
115 | * argument, initialized with the $formatOptions array
116 | *
117 | *
118 | * $this->context(array('quote' => ' * '), function($f) {
119 | * $f->writeln('quoted text');
120 | * })
121 | *
122 | *
123 | * @param array $formatOptions
124 | * @param Closure $closure
125 | */
126 | public function context(array $formatOptions, Closure $closure)
127 | {
128 | $formater = new FormatedWriter($this->console, $formatOptions);
129 | return $closure($formater);
130 | }
131 |
132 | /**
133 | * Writes some text to the text writer
134 | *
135 | * @see format()
136 | * @param string $text
137 | * @param int|array $formatOptions
138 | * @param int $pipe
139 | * @return Command
140 | */
141 | public function write($text, $formatOptions = array(), $pipe = TextWriter::STDOUT)
142 | {
143 | $this->console->write($this->format($text, $formatOptions), $pipe);
144 | return $this;
145 | }
146 |
147 | /**
148 | * Writes a message in bold red to STDERR
149 | *
150 | * @param string $text
151 | * @return Command
152 | */
153 | public function writeerr($text)
154 | {
155 | return $this->write($text, Colors::RED | Colors::BOLD, TextWriter::STDERR);
156 | }
157 |
158 | /**
159 | * Writes a line of text
160 | *
161 | * @param string $text
162 | * @param int|array $formatOptions
163 | * @param int $pipe
164 | * @return Command
165 | */
166 | public function writeln($text, $formatOptions = array(), $pipe = TextWriter::STDOUT)
167 | {
168 | $this->console->writeln($this->format($text, $formatOptions), $pipe);
169 | return $this;
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Console.php:
--------------------------------------------------------------------------------
1 | optionsParser = $parser ?: new DefaultOptionsParser();
56 | $this->textWriter = $writer ?: new StdTextWriter();
57 | if ($this->helpCommandClass) {
58 | $this->addCommand($this->helpCommandClass, $this->helpCommand);
59 | $this->addCommands($commands);
60 | }
61 | }
62 |
63 | /**
64 | * @param OptionsParser $parser
65 | * @return Console
66 | */
67 | public function setOptionsParser(OptionsParser $parser)
68 | {
69 | $this->optionsParser = $parser;
70 | return $this;
71 | }
72 |
73 | /**
74 | * @return OptionsParser
75 | */
76 | public function getOptionsParser()
77 | {
78 | return $this->optionsParser;
79 | }
80 |
81 | /**
82 | * @param TextWriter $writer
83 | * @return Console
84 | */
85 | public function setTextWriter(TextWriter $writer)
86 | {
87 | $this->textWriter = $writer;
88 | return $this;
89 | }
90 |
91 | /**
92 | * @return TextWriter
93 | */
94 | public function getTextWriter()
95 | {
96 | return $this->textWriter;
97 | }
98 |
99 | /**
100 | * Sets whether to call exit(1) when an exception is caught
101 | *
102 | * @param bool $exit
103 | * @return Console
104 | */
105 | public function setExitOnException($exit = true)
106 | {
107 | $this->exitOnException = $exit;
108 | return $this;
109 | }
110 |
111 | /**
112 | * @return bool
113 | */
114 | public function exitsOnException()
115 | {
116 | return $this->exitOnException;
117 | }
118 |
119 | /**
120 | * Sets whether a detailed error message is displayed when exception are caught
121 | *
122 | * @param boolean $enable
123 | */
124 | public function setVerboseException($enable = true)
125 | {
126 | $this->verboseException = $enable;
127 | }
128 |
129 | /**
130 | * @return bool
131 | */
132 | public function areExceptionsVerbose()
133 | {
134 | return $this->verboseException;
135 | }
136 |
137 | /**
138 | * Adds multiple commands at once
139 | *
140 | * @see addCommand()
141 | * @param array $commands
142 | * @return Console
143 | */
144 | public function addCommands(array $commands)
145 | {
146 | foreach ($commands as $name => $command) {
147 | $this->addCommand($command, is_numeric($name) ? null : $name);
148 | }
149 | return $this;
150 | }
151 |
152 | /**
153 | * Registers a command
154 | *
155 | * @param callback $callback Associated class name, function name, Command instance or closure
156 | * @param string $alias Command name to be used in the shell
157 | * @param bool $default True to set the command as the default one
158 | * @return Console
159 | */
160 | public function addCommand($callback, $alias = null, $default = false)
161 | {
162 | if ($alias instanceof \Closure && is_string($callback)) {
163 | list($alias, $callback) = array($callback, $alias);
164 | }
165 | if (is_array($callback) && is_string($callback[0])) {
166 | $callback = implode('::', $callback);
167 | }
168 |
169 | $name = '';
170 | if (is_string($callback)) {
171 | $name = $callback;
172 | if (is_callable($callback)) {
173 | if (strpos($callback, '::') !== false) {
174 | list($classname, $methodname) = explode('::', $callback);
175 | $name = Utils::dashized($methodname);
176 | } else {
177 | $name = strtolower(trim(str_replace('_', '-', $name), '-'));
178 | }
179 | } else {
180 | if (substr($name, -7) === 'Command') {
181 | $name = substr($name, 0, -7);
182 | }
183 | $name = Utils::dashized(basename(str_replace('\\', '/', $name)));
184 | }
185 | } else if (is_object($callback) && !($callback instanceof Closure)) {
186 | $classname = get_class($callback);
187 | if (!($callback instanceof Command)) {
188 | throw new ConsoleException("'$classname' must inherit from 'ConsoleKit\Command'");
189 | }
190 | if (substr($classname, -7) === 'Command') {
191 | $classname = substr($classname, 0, -7);
192 | }
193 | $name = Utils::dashized(basename(str_replace('\\', '/', $classname)));
194 | } else if (!$alias) {
195 | throw new ConsoleException("Commands using closures must have an alias");
196 | }
197 |
198 | $name = $alias ?: $name;
199 | $this->commands[$name] = $callback;
200 | if ($default) {
201 | $this->defaultCommand = $name;
202 | }
203 | return $this;
204 | }
205 |
206 | /**
207 | * Registers commands from a directory
208 | *
209 | * @param string $dir
210 | * @param string $namespace
211 | * @param bool $includeFiles
212 | * @return Console
213 | */
214 | public function addCommandsFromDir($dir, $namespace = '', $includeFiles = false)
215 | {
216 | foreach (new DirectoryIterator($dir) as $file) {
217 | $filename = $file->getFilename();
218 | if ($file->isDir() || substr($filename, 0, 1) === '.' || strlen($filename) <= 11
219 | || strtolower(substr($filename, -11)) !== 'command.php') {
220 | continue;
221 | }
222 | if ($includeFiles) {
223 | include $file->getPathname();
224 | }
225 | $className = trim($namespace . '\\' . substr($filename, 0, -4), '\\');
226 | $this->addCommand($className);
227 | }
228 | return $this;
229 | }
230 |
231 | /**
232 | * @param string $name
233 | * @return bool
234 | */
235 | public function hasCommand($name)
236 | {
237 | return isset($this->commands[$name]);
238 | }
239 |
240 | /**
241 | * @param string $name
242 | * @return string
243 | */
244 | public function getCommand($name)
245 | {
246 | if (!isset($this->commands[$name])) {
247 | throw new ConsoleException("Command '$name' does not exist");
248 | }
249 | return $this->commands[$name];
250 | }
251 |
252 | /**
253 | * @return array
254 | */
255 | public function getCommands()
256 | {
257 | return $this->commands;
258 | }
259 |
260 | /**
261 | * @param string $name
262 | * @return Console
263 | */
264 | public function setDefaultCommand($name = null)
265 | {
266 | if ($name !== null && !isset($this->commands[$name])) {
267 | throw new ConsoleException("Command '$name' does not exist");
268 | }
269 | $this->defaultCommand = $name;
270 | }
271 |
272 | /**
273 | * @return string
274 | */
275 | public function getDefaultCommand()
276 | {
277 | return $this->defaultCommand;
278 | }
279 |
280 | /**
281 | * Turn off command parsing
282 | *
283 | * @param type $singleCommand
284 | * @return Console
285 | */
286 | public function setSingleCommand($singleCommand) {
287 | $this->singleCommand = $singleCommand;
288 | return $this;
289 | }
290 |
291 | /**
292 | * @param array $args
293 | * @return mixed Results of the command callback
294 | */
295 | public function run(array $argv = null)
296 | {
297 | try {
298 | if ($argv === null) {
299 | $argv = isset($_SERVER['argv']) ? array_slice($_SERVER['argv'], 1) : array();
300 | }
301 |
302 | list($args, $options) = $this->getOptionsParser()->parse($argv);
303 | if($this->defaultCommand && $this->singleCommand) {
304 | return $this->execute($this->defaultCommand, $args, $options);
305 | }
306 |
307 | if (!count($args)) {
308 | if ($this->defaultCommand) {
309 | $args[] = $this->defaultCommand;
310 | } else {
311 | $this->textWriter->writeln(Colors::red("Missing command name"));
312 | $args[] = $this->helpCommand;
313 | }
314 | }
315 |
316 | $command = array_shift($args);
317 | return $this->execute($command, $args, $options);
318 |
319 | } catch (\Exception $e) {
320 | $this->writeException($e);
321 | if ($this->exitOnException) {
322 | exit(1);
323 | }
324 | throw $e;
325 | }
326 | }
327 |
328 | /**
329 | * Executes a command
330 | *
331 | * @param string $command
332 | * @param array $args
333 | * @param array $options
334 | * @return mixed
335 | */
336 | public function execute($command = null, array $args = array(), array $options = array())
337 | {
338 | $command = $command ?: $this->defaultCommand;
339 | if (!isset($this->commands[$command])) {
340 | throw new ConsoleException("Command '$command' does not exist");
341 | }
342 |
343 | $callback = $this->commands[$command];
344 | if (is_callable($callback)) {
345 | $params = array($args, $options);
346 | if (is_string($callback)) {
347 | if (strpos($callback, '::') !== false) {
348 | list($classname, $methodname) = explode('::', $callback);
349 | $reflection = new ReflectionMethod($classname, $methodname);
350 | } else {
351 | $reflection = new ReflectionFunction($callback);
352 | }
353 | $params = Utils::computeFuncParams($reflection, $args, $options);
354 | }
355 | $params[] = $this;
356 | return call_user_func_array($callback, $params);
357 | }
358 |
359 | $method = new ReflectionMethod($callback, 'execute');
360 | $params = Utils::computeFuncParams($method, $args, $options);
361 | return $method->invokeArgs(new $callback($this), $params);
362 | }
363 |
364 | /**
365 | * Writes some text to the text writer
366 | *
367 | * @see TextWriter::write()
368 | * @param string $text
369 | * @param array $formatOptions
370 | * @return Console
371 | */
372 | public function write($text, $pipe = TextWriter::STDOUT)
373 | {
374 | $this->textWriter->write($text, $pipe);
375 | return $this;
376 | }
377 |
378 | /**
379 | * Writes a line of text
380 | *
381 | * @see TextWriter::writeln()
382 | * @param string $text
383 | * @param array $formatOptions
384 | * @return Console
385 | */
386 | public function writeln($text = '', $pipe = TextWriter::STDOUT)
387 | {
388 | $this->textWriter->writeln($text, $pipe);
389 | return $this;
390 | }
391 |
392 | /**
393 | * Writes an error message to stderr
394 | *
395 | * @param \Exception $e
396 | * @return Console
397 | */
398 | public function writeException(\Exception $e)
399 | {
400 | if ($this->verboseException) {
401 | $text = sprintf("[%s]\n%s\nIn %s at line %s\n%s",
402 | get_class($e),
403 | $e->getMessage(),
404 | $e->getFile(),
405 | $e->getLine(),
406 | $e->getTraceAsString()
407 | );
408 | } else {
409 | $text = sprintf("\n[%s]\n%s\n", get_class($e), $e->getMessage());
410 | }
411 |
412 | $box = new Widgets\Box($this->textWriter, $text, '');
413 | $out = Colors::colorizeLines($box, Colors::WHITE, Colors::RED);
414 | $out = TextFormater::apply($out, array('indent' => 2));
415 | $this->textWriter->writeln($out);
416 | return $this;
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/src/ConsoleKit/ConsoleException.php:
--------------------------------------------------------------------------------
1 | write("$text\n", $pipe);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/ConsoleKit/FileSystem.php:
--------------------------------------------------------------------------------
1 | textWriter = $writer;
29 | }
30 |
31 | /**
32 | * @param TextWriter $writer
33 | * @return FormatedWriter
34 | */
35 | public function setTextWriter(TextWriter $writer)
36 | {
37 | $this->textWriter = $writer;
38 | return $this;
39 | }
40 |
41 | /**
42 | * @return TextWriter
43 | */
44 | public function getTextWriter()
45 | {
46 | return $this->textWriter;
47 | }
48 |
49 | /**
50 | * Writes some text to the text writer
51 | *
52 | * @see TextWriter::write()
53 | * @param string $text
54 | * @param array $formatOptions
55 | * @return Command
56 | */
57 | public function write($text, $pipe = TextWriter::STDOUT)
58 | {
59 | $this->textWriter->write($this->format($text), $pipe);
60 | return $this;
61 | }
62 |
63 | /**
64 | * Writes a line of text
65 | *
66 | * @see TextWriter::writeln()
67 | * @param string $text
68 | * @param array $formatOptions
69 | * @return Command
70 | */
71 | public function writeln($text = '', $pipe = TextWriter::STDOUT)
72 | {
73 | $this->textWriter->writeln($this->format($text), $pipe);
74 | return $this;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Help.php:
--------------------------------------------------------------------------------
1 | getDocComment());
67 | }
68 |
69 | /**
70 | * Creates an Help object from a class subclassing Command
71 | *
72 | * @param string $name
73 | * @param string $subCommand
74 | * @return Help
75 | */
76 | public static function fromCommandClass($name, $subCommand = null)
77 | {
78 | $prefix = 'execute';
79 | $class = new ReflectionClass($name);
80 |
81 | if ($subCommand) {
82 | $method = $prefix . ucfirst(Utils::camelize($subCommand));
83 | if (!$class->hasMethod($method)) {
84 | throw new ConsoleException("Sub command '$subCommand' of '$name' does not exist");
85 | }
86 | return new Help($class->getMethod($method)->getDocComment());
87 | }
88 |
89 | $help = new Help($class->getDocComment());
90 | foreach ($class->getMethods() as $method) {
91 | if (strlen($method->getName()) > strlen($prefix) &&
92 | substr($method->getName(), 0, strlen($prefix)) === $prefix) {
93 | $help->subCommands[] = Utils::dashized(substr($method->getName(), strlen($prefix)));
94 | }
95 | }
96 | return $help;
97 | }
98 |
99 | /**
100 | * @param string $text
101 | */
102 | protected function __construct($text = '')
103 | {
104 | $this->text = $text;
105 | $this->parse();
106 | }
107 |
108 | protected function parse()
109 | {
110 | $this->usage = '';
111 | $this->args = array();
112 | $this->options = array();
113 | $this->flags = array();
114 |
115 | $lines = explode("\n", substr(trim($this->text), 2, -2));
116 | $lines = array_map(function($v) { return ltrim(trim($v), '* '); }, $lines);
117 |
118 | $desc = array();
119 | foreach ($lines as $line) {
120 | if (preg_match('/@usage (.+)$/', $line, $matches)) {
121 | $this->usage = $matches[1];
122 | } else if (preg_match('/@arg ([^\s]+)( (.*)|)$/', $line, $matches)) {
123 | $this->args[$matches[1]] = isset($matches[3]) ? $matches[3] : '';
124 | } else if (preg_match('/@opt ([a-zA-Z\-_0-9=]+)( (.*)|)$/', $line, $matches)) {
125 | $this->options[$matches[1]] = isset($matches[3]) ? $matches[3] : '';
126 | } else if (preg_match('/@flag ([a-zA-Z0-9])( (.*)|)$/', $line, $matches)) {
127 | $this->flags[$matches[1]] = isset($matches[3]) ? $matches[3] : '';
128 | } else if (!preg_match('/^@([a-zA-Z\-_0-9]+)(.*)$/', $line)) {
129 | $desc[] = $line;
130 | }
131 | }
132 |
133 | $this->description = trim(implode("\n", $desc), "\n ");
134 | }
135 |
136 | /**
137 | * @return string
138 | */
139 | public function getDescrition()
140 | {
141 | return $this->description;
142 | }
143 |
144 | /**
145 | * @return string
146 | */
147 | public function getUsage()
148 | {
149 | return $this->usage;
150 | }
151 |
152 | /**
153 | * @return array
154 | */
155 | public function getArgs()
156 | {
157 | return $this->args;
158 | }
159 |
160 | /**
161 | * @return array
162 | */
163 | public function getOptions()
164 | {
165 | return $this->options;
166 | }
167 |
168 | /**
169 | * @return array
170 | */
171 | public function getFlags()
172 | {
173 | return $this->flags;
174 | }
175 |
176 | /**
177 | * @return bool
178 | */
179 | public function hasSubCommands()
180 | {
181 | return !empty($this->subCommands);
182 | }
183 |
184 | /**
185 | * @return array
186 | */
187 | public function getSubCommands()
188 | {
189 | return $this->subCommands;
190 | }
191 |
192 | /**
193 | * @return string
194 | */
195 | public function render()
196 | {
197 | $output = "{$this->description}\n\n";
198 | if (!empty($this->usage)) {
199 | $output .= "Usage: {$this->usage}\n\n";
200 | }
201 | if (!empty($this->args)) {
202 | $output .= Colors::colorize("Arguments:\n", Colors::BLACK | Colors::BOLD);
203 | foreach ($this->args as $name => $desc) {
204 | $output .= sprintf(" %s\t%s\n", $name, $desc);
205 | }
206 | $output .= "\n";
207 | }
208 | if (!empty($this->options)) {
209 | $output .= Colors::colorize("Available options:\n", Colors::BLACK | Colors::BOLD);
210 | foreach ($this->options as $name => $desc) {
211 | $output .= sprintf(" --%s\t%s\n", $name, $desc);
212 | }
213 | $output .= "\n";
214 | }
215 | if (!empty($this->flags)) {
216 | $output .= Colors::colorize("Available flags:\n", Colors::BLACK | Colors::BOLD);
217 | foreach ($this->flags as $name => $desc) {
218 | $output .= sprintf(" -%s\t%s\n", $name, $desc);
219 | }
220 | $output .= "\n";
221 | }
222 | if (!empty($this->subCommands)) {
223 | $output .= Colors::colorize("Available sub commands:\n", Colors::BLACK | Colors::BOLD);
224 | foreach ($this->subCommands as $name) {
225 | $output .= " - $name\n";
226 | }
227 | $output .= "\n";
228 | }
229 | return trim($output, "\n ");
230 | }
231 |
232 | public function __toString()
233 | {
234 | return $this->render();
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/ConsoleKit/HelpCommand.php:
--------------------------------------------------------------------------------
1 | ' * '));
19 | $this->writeln('Available commands:', Colors::BLACK | Colors::BOLD);
20 | foreach ($this->console->getCommands() as $name => $fqdn) {
21 | if ($fqdn !== __CLASS__) {
22 | $this->writeln($formater->format($name));
23 | }
24 | }
25 | $scriptName = basename($_SERVER['SCRIPT_FILENAME']);
26 | $this->writeln("Use './$scriptName help command' for more info");
27 | } else {
28 | $commandFQDN = $this->console->getCommand($args[0]);
29 | $help = Help::fromFQDN($commandFQDN, Utils::get($args, 1));
30 | $this->writeln($help);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/ConsoleKit/OptionsParser.php:
--------------------------------------------------------------------------------
1 | write("$text\n", $pipe);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/ConsoleKit/TextFormater.php:
--------------------------------------------------------------------------------
1 | format($text);
47 | }
48 |
49 | /**
50 | * @param array $options
51 | */
52 | public function __construct(array $options = array())
53 | {
54 | $this->indentWidth = self::$defaultIndentWidth;
55 | $this->setOptions($options);
56 | }
57 |
58 | /**
59 | * Available options:
60 | * - indentWidth
61 | * - indent
62 | * - quote
63 | * - fgcolor
64 | * - bgcolor
65 | *
66 | * @param array $options
67 | * @return TextFormater
68 | */
69 | public function setOptions(array $options)
70 | {
71 | if (isset($options['indentWidth'])) {
72 | $this->setIndentWidth($options['indentWidth']);
73 | }
74 | if (isset($options['indent'])) {
75 | $this->setIndent($options['indent']);
76 | }
77 | if (isset($options['quote'])) {
78 | $this->setQuote($options['quote']);
79 | }
80 | if (isset($options['fgcolor'])) {
81 | $this->setFgColor($options['fgcolor']);
82 | }
83 | if (isset($options['bgcolor'])) {
84 | $this->setBgColor($options['bgcolor']);
85 | }
86 | return $this;
87 | }
88 |
89 | /**
90 | * @param int $indent
91 | * @return TextFormater
92 | */
93 | public function setIndentWidth($width)
94 | {
95 | $this->indentWidth = (int) $width;
96 | return $this;
97 | }
98 |
99 | /**
100 | * @return int
101 | */
102 | public function getIndentWidth()
103 | {
104 | return $this->indentWidth;
105 | }
106 |
107 | /**
108 | * @param int $indent
109 | * @return TextFormater
110 | */
111 | public function setIndent($indent)
112 | {
113 | $this->indent = (int) $indent;
114 | return $this;
115 | }
116 |
117 | /**
118 | * @return int
119 | */
120 | public function getIndent()
121 | {
122 | return $this->indent;
123 | }
124 |
125 | /**
126 | * @param string $quote
127 | * @return TextFormater
128 | */
129 | public function setQuote($quote)
130 | {
131 | $this->quote = $quote;
132 | return $this;
133 | }
134 |
135 | /**
136 | * @return string
137 | */
138 | public function getQuote()
139 | {
140 | return $this->quote;
141 | }
142 |
143 | /**
144 | * @param string $color
145 | * @return TextFormater
146 | */
147 | public function setFgColor($color)
148 | {
149 | $this->fgColor = $color;
150 | return $this;
151 | }
152 |
153 | /**
154 | * @return string
155 | */
156 | public function getFgColor()
157 | {
158 | return $this->fgColor;
159 | }
160 |
161 | /**
162 | * @param string $color
163 | * @return TextFormater
164 | */
165 | public function setBgColor($color)
166 | {
167 | $this->bgColor = $color;
168 | return $this;
169 | }
170 |
171 | /**
172 | * @return string
173 | */
174 | public function getBgColor()
175 | {
176 | return $this->bgColor;
177 | }
178 |
179 | /**
180 | * Formats $text according to the formater's options
181 | *
182 | * @param string $text
183 | */
184 | public function format($text)
185 | {
186 | $lines = explode("\n", $text);
187 | foreach ($lines as &$line) {
188 | $line = ((string) $this->quote)
189 | . str_repeat(' ', $this->indent * $this->indentWidth)
190 | . $line;
191 | }
192 | return Colors::colorize(implode("\n", $lines), $this->fgColor, $this->bgColor);
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/src/ConsoleKit/TextWriter.php:
--------------------------------------------------------------------------------
1 | getDocComment())) {
160 | return array($args, $options);
161 | }
162 |
163 | $nbRequiredParams = $reflection->getNumberOfRequiredParameters();
164 | if (count($args) < $nbRequiredParams) {
165 | throw new ConsoleException("Not enough parameters in '" . $reflection->getName() . "'");
166 | }
167 |
168 | $params = $args;
169 | if (count($args) > $nbRequiredParams) {
170 | $params = array_slice($args, 0, $nbRequiredParams);
171 | $args = array_slice($args, $nbRequiredParams);
172 | }
173 |
174 | foreach ($reflection->getParameters() as $param) {
175 | if ($param->isOptional() && substr($param->getName(), 0, 1) !== '_') {
176 | if (array_key_exists($param->getName(), $options)) {
177 | $params[] = $options[$param->getName()];
178 | unset($options[$param->getName()]);
179 | } else {
180 | $params[] = $param->getDefaultValue();
181 | }
182 | }
183 | }
184 |
185 | $params[] = $args;
186 | $params[] = $options;
187 | return $params;
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Widgets/AbstractWidget.php:
--------------------------------------------------------------------------------
1 | textWriter = $writer;
26 | }
27 |
28 | /**
29 | * @param TextWriter $writer
30 | * @return Dialog
31 | */
32 | public function setTextWriter(TextWriter $writer)
33 | {
34 | $this->textWriter = $writer;
35 | return $this;
36 | }
37 |
38 | /**
39 | * @return TextWriter
40 | */
41 | public function getTextWriter()
42 | {
43 | return $this->textWriter;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Widgets/Box.php:
--------------------------------------------------------------------------------
1 | textWriter = $writer;
34 | $this->text = $text;
35 | $this->lineCharacter = $lineCharacter;
36 | $this->padding = $padding;
37 | }
38 |
39 | /**
40 | * @param string text
41 | * @return Box
42 | */
43 | public function setText($text)
44 | {
45 | $this->text = $text;
46 | return $this;
47 | }
48 |
49 | /**
50 | * @return string
51 | */
52 | public function getText()
53 | {
54 | return $this->text;
55 | }
56 |
57 | /**
58 | * @param string lineCharacter
59 | * @return Box
60 | */
61 | public function setLineCharacter($lineCharacter)
62 | {
63 | $this->lineCharacter = $lineCharacter;
64 | return $this;
65 | }
66 |
67 | /**
68 | * @return string
69 | */
70 | public function getLineCharacter()
71 | {
72 | return $this->lineCharacter;
73 | }
74 |
75 | /**
76 | * @param int padding
77 | * @return Box
78 | */
79 | public function setPadding($padding)
80 | {
81 | $this->padding = $padding;
82 | return $this;
83 | }
84 |
85 | /**
86 | * @return int
87 | */
88 | public function getPadding()
89 | {
90 | return $this->padding;
91 | }
92 |
93 | /**
94 | * @return string
95 | */
96 | public function render()
97 | {
98 | $lines = explode("\n", $this->text);
99 | $maxWidth = 0;
100 | foreach ($lines as $line) {
101 | if (strlen($line) > $maxWidth) {
102 | $maxWidth = strlen($line);
103 | }
104 | }
105 |
106 | $maxWidth += $this->padding * 2 + 2;
107 | $c = $this->lineCharacter;
108 | $output = str_repeat($c, $maxWidth) . "\n";
109 | foreach ($lines as $line) {
110 | $delta = $maxWidth - (strlen($line) + 2 + $this->padding * 2);
111 | $output .= $c . str_repeat(' ', $this->padding) . $line
112 | . str_repeat(' ', $delta + $this->padding) . $c . "\n";
113 | }
114 | $output .= str_repeat($c, $maxWidth);
115 | return $output;
116 | }
117 |
118 | public function write()
119 | {
120 | if ($this->textWriter === null) {
121 | throw new ConsoleException('No TextWriter object specified');
122 | }
123 | $this->textWriter->write($this->render());
124 | return $this;
125 | }
126 |
127 | public function __toString()
128 | {
129 | return $this->render();
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Widgets/Checklist.php:
--------------------------------------------------------------------------------
1 |
19 | * $checklist = new ConsoleKit\Widgets\Checklist($console);
20 | * $checklist->run(array(
21 | * 'Creating directory structure' => function() {
22 | * // code
23 | * return $success; // bool
24 | * },
25 | * 'Downloading scripts' => function() {
26 | * // code
27 | * return $success; // bool
28 | * }
29 | * ));
30 | *
31 | *
32 | * Will print:
33 | *
34 | * Creating directory structure OK
35 | * Downloading scripts OK
36 | */
37 | class Checklist extends AbstractWidget
38 | {
39 | protected $maxMessageLength = 100;
40 |
41 | protected $successText = 'OK';
42 |
43 | protected $successColor = Colors::GREEN;
44 |
45 | protected $errorText = 'FAIL';
46 |
47 | protected $errorColor = Colors::RED;
48 |
49 | public function setMaxMessageLength($length)
50 | {
51 | $this->maxMessageLength = $length;
52 | }
53 |
54 | public function getMaxMessageLength()
55 | {
56 | return $this->maxMessageLength;
57 | }
58 |
59 | public function setSuccessText($text, $color = Colors::GREEN)
60 | {
61 | $this->successText = $text;
62 | $this->successColor = $color;
63 | }
64 |
65 | public function setErrorText($text, $color = Colors::RED)
66 | {
67 | $this->errorText = $text;
68 | $this->errorColor = $color;
69 | }
70 |
71 | public function run(array $steps)
72 | {
73 | $maxMessageLength = min(array_reduce(array_keys($steps), function($r, $i) {
74 | return max(strlen($i), $r);
75 | }, 0), $this->maxMessageLength);
76 |
77 | foreach ($steps as $message => $callback) {
78 | $this->step($message, $callback, $maxMessageLength);
79 | }
80 | }
81 |
82 | public function runArray($array, $message, $callback, $useKeyInMessage = false)
83 | {
84 | $steps = array();
85 | foreach ($array as $k => $v) {
86 | $steps[sprintf($message, $useKeyInMessage ? $k : $v)] = function() use ($k, $v) {
87 | return $callback($v, $k);
88 | };
89 | }
90 | return $this->run($steps);
91 | }
92 |
93 | public function step($message, $callback, $maxMessageLength = null)
94 | {
95 | $maxMessageLength = $maxMessageLength ?: $this->maxMessageLength;
96 | $this->textWriter->write(sprintf("%-${maxMessageLength}s", $message));
97 | if (call_user_func($callback)) {
98 | $this->textWriter->write(Colors::colorize($this->successText, $this->successColor));
99 | } else {
100 | $this->textWriter->write(Colors::colorize($this->errorText, $this->errorColor));
101 | }
102 | $this->textWriter->write("\n");
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Widgets/Dialog.php:
--------------------------------------------------------------------------------
1 | 30) {
28 | $defaultText = substr($default, 0, 30) . '...';
29 | }
30 | $text .= " [$defaultText]";
31 | }
32 | $this->textWriter->write("$text ");
33 | return trim(fgets(STDIN)) ?: $default;
34 | }
35 |
36 | /**
37 | * Writes $text (followed by the list of choices) and reads the user response.
38 | * Returns true if it matches $expected, false otherwise
39 | *
40 | *
41 | * if($dialog->confirm('Are you sure?')) { ... }
42 | * if($dialog->confirm('Your choice?', null, array('a', 'b', 'c'))) { ... }
43 | *
44 | *
45 | * @param string $text
46 | * @param string $expected
47 | * @param array $choices
48 | * @param string $default
49 | * @param string $errorMessage
50 | * @return bool
51 | */
52 | public function confirm($text, $expected = 'y', array $choices = array('Y', 'n'), $default = 'y', $errorMessage = 'Invalid choice')
53 | {
54 | $text = $text . ' [' . implode('/', $choices) . ']';
55 | $choices = array_map('strtolower', $choices);
56 | $expected = strtolower($expected);
57 | $default = strtolower($default);
58 | do {
59 | $input = strtolower($this->ask($text));
60 | if (in_array($input, $choices)) {
61 | return $input === $expected;
62 | } else if (empty($input) && !empty($default)) {
63 | return $default === $expected;
64 | }
65 | $this->textWriter->writeln($errorMessage);
66 | } while (true);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/ConsoleKit/Widgets/ProgressBar.php:
--------------------------------------------------------------------------------
1 |
20 | * $total = 100;
21 | * $progress = new ProgressBar($textWriter, $total);
22 | * for ($i = 0; $i < $total; $i++) {
23 | * $progress->incr();
24 | * usleep(10000);
25 | * }
26 | * $progress->stop();
27 | *
28 | */
29 | class ProgressBar extends AbstractWidget
30 | {
31 | /** @var int */
32 | protected $value = 0;
33 |
34 | /** @var int */
35 | protected $total = 0;
36 |
37 | /** @var int */
38 | protected $size = 0;
39 |
40 | /** @var bool */
41 | protected $showRemainingTime = true;
42 |
43 | /** @var int */
44 | protected $startTime;
45 |
46 | /**
47 | * @param TextWriter $writer
48 | * @param int $total
49 | * @param int $size
50 | */
51 | public function __construct(TextWriter $writer = null, $total = 100, $size = 50, $showRemainingTime = true)
52 | {
53 | $this->textWriter = $writer;
54 | $this->size = $size;
55 | $this->showRemainingTime = $showRemainingTime;
56 | $this->start($total);
57 | }
58 |
59 | /**
60 | * @param int $size
61 | * @return ProgressBar
62 | */
63 | public function setSize($size)
64 | {
65 | $this->size = $size;
66 | return $this;
67 | }
68 |
69 | /**
70 | * @return int
71 | */
72 | public function getSize()
73 | {
74 | return $this->size;
75 | }
76 |
77 | /**
78 | * @param bool $show
79 | */
80 | public function setShowRemainingTime($show = true)
81 | {
82 | $this->showRemainingTime = $show;
83 | }
84 |
85 | /**
86 | * @return bool
87 | */
88 | public function getShowRemainingTime()
89 | {
90 | return $this->showRemainingTime;
91 | }
92 |
93 | /**
94 | * @param number $value
95 | */
96 | public function setValue($value)
97 | {
98 | $this->value = $value;
99 | }
100 |
101 | /**
102 | * @return number
103 | */
104 | public function getValue()
105 | {
106 | return $this->value;
107 | }
108 |
109 | /**
110 | * @param int $total
111 | * @return ProgressBar
112 | */
113 | public function start($total = 100)
114 | {
115 | $this->value = 0;
116 | $this->total = $total;
117 | $this->startTime = time();
118 | return $this;
119 | }
120 |
121 | /**
122 | * Increments the value and calls {@see write()}
123 | *
124 | * @param int $increment
125 | * @return ProgressBar
126 | */
127 | public function incr($increment = 1)
128 | {
129 | $this->value += $increment;
130 | $this->write();
131 | return $this;
132 | }
133 |
134 | /**
135 | * Writes a new line
136 | *
137 | * @return ProgressBar
138 | */
139 | public function stop()
140 | {
141 | $this->textWriter->writeln();
142 | return $this;
143 | }
144 |
145 | /**
146 | * Generates the text to write for the current values
147 | *
148 | * @return string
149 | */
150 | public function render()
151 | {
152 | $percentage = (double) ($this->value / $this->total);
153 |
154 | $progress = floor($percentage * $this->size);
155 | $output = "\r[" . str_repeat('=', $progress);
156 | if ($progress < $this->size) {
157 | $output .= ">" . str_repeat(' ', $this->size - $progress);
158 | } else {
159 | $output .= '=';
160 | }
161 | $output .= sprintf('] %s%% %s/%s', round($percentage * 100, 0), $this->value, $this->total);
162 |
163 | if ($this->showRemainingTime) {
164 | $speed = (time() - $this->startTime) / $this->value;
165 | $remaining = number_format(round($speed * ($this->total - $this->value), 2), 2);
166 | $output .= " - $remaining sec remaining";
167 | }
168 |
169 | return $output;
170 | }
171 |
172 | /**
173 | * Writes the rendered progress bar to the text writer
174 | *
175 | * @return ProgressBar
176 | */
177 | public function write()
178 | {
179 | if ($this->textWriter === null) {
180 | throw new ConsoleException('No TextWriter object specified');
181 | }
182 | $this->textWriter->write($this->render());
183 | return $this;
184 | }
185 |
186 | public function __toString()
187 | {
188 | return $this->render();
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/ColorsTest.php:
--------------------------------------------------------------------------------
1 | assertEquals("\033[31mred\033[0m", Colors::colorize('red', Colors::RED));
12 | $this->assertEquals("\033[1;31mred\033[0m", Colors::colorize('red', Colors::RED | Colors::BOLD));
13 | $this->assertEquals("\033[43m\033[31mred\033[0m", Colors::colorize('red', Colors::RED, Colors::YELLOW));
14 | $this->assertEquals("\033[31mred\033[0m", Colors::red('red'));
15 | }
16 |
17 | public function testGetColorCode()
18 | {
19 | $this->assertEquals(Colors::RED, Colors::getColorCode(Colors::RED));
20 | $this->assertEquals(Colors::RED, Colors::getColorCode('red'));
21 | $this->assertEquals(Colors::GREEN, Colors::getColorCode('GREEN'));
22 | }
23 |
24 | public function testGetBoldColorCode()
25 | {
26 | $this->assertEquals(Colors::YELLOW | Colors::BOLD, Colors::getColorCode(Colors::YELLOW | Colors::BOLD));
27 | $this->assertEquals(Colors::RED | Colors::BOLD, Colors::getColorCode('red+bold'));
28 | $this->assertEquals(Colors::GREEN | Colors::BOLD, Colors::getColorCode('green', array('bold')));
29 | }
30 |
31 | /**
32 | * @expectedException ConsoleKit\ConsoleException
33 | * @expectedExceptionMessage Unknown color 'foobar'
34 | */
35 | public function testGetUnknownColorCode()
36 | {
37 | Colors::getColorCode('foobar');
38 | }
39 |
40 | public function testGetFgColorString()
41 | {
42 | $this->assertEquals("\033[34m", Colors::getFgColorString(Colors::BLUE));
43 | $this->assertEquals("\033[1;34m", Colors::getFgColorString(Colors::BLUE | Colors::BOLD));
44 | }
45 |
46 | public function testGetBgColorString()
47 | {
48 | $this->assertEquals("\033[45m", Colors::getBgColorString(Colors::MAGENTA));
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/ConsoleKitTestCase.php:
--------------------------------------------------------------------------------
1 | console = new Console();
15 | $this->console->setTextWriter(new EchoTextWriter());
16 | $this->console->setExitOnException(false);
17 | }
18 |
19 | public function testAddCommand()
20 | {
21 | $this->console->addCommand('ConsoleKit\Tests\TestCommand');
22 | $this->assertArrayHasKey('test', $this->console->getCommands());
23 | $this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test'));
24 |
25 | $this->console->addCommand('ConsoleKit\Tests\TestCommand', 'test-alias');
26 | $this->assertArrayHasKey('test-alias', $this->console->getCommands());
27 | $this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test-alias'));
28 |
29 | $this->console->addCommand('ConsoleKit\Tests\TestStatic::sayHello');
30 | $this->assertArrayHasKey('say-hello', $this->console->getCommands());
31 | $this->assertEquals('ConsoleKit\Tests\TestStatic::sayHello', $this->console->getCommand('say-hello'));
32 |
33 | $this->console->addCommand('var_dump');
34 | $this->assertArrayHasKey('var-dump', $this->console->getCommands());
35 | $this->assertEquals('var_dump', $this->console->getCommand('var-dump'));
36 |
37 | $this->console->addCommand(array(new TestCommand($this->console), 'execute'), 'test-callback');
38 | $this->assertArrayHasKey('test-callback', $this->console->getCommands());
39 | $this->assertInternalType('array', $this->console->getCommand('test-callback'));
40 |
41 | $this->console->addCommand(function($args, $opts) { echo 'hello!'; }, 'hello');
42 | $this->assertArrayHasKey('hello', $this->console->getCommands());
43 | $this->assertInstanceOf('Closure', $this->console->getCommand('hello'));
44 | }
45 |
46 | public function testAddCommands()
47 | {
48 | $this->console->addCommands(array(
49 | 'ConsoleKit\Tests\TestCommand',
50 | 'test-alias' => 'ConsoleKit\Tests\TestCommand'
51 | ));
52 |
53 | $this->assertArrayHasKey('test', $this->console->getCommands());
54 | $this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test'));
55 | $this->assertArrayHasKey('test-alias', $this->console->getCommands());
56 | $this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test-alias'));
57 | }
58 |
59 | public function testAddCommandsFromDir()
60 | {
61 | $this->console->addCommandsFromDir(__DIR__, 'ConsoleKit\Tests');
62 | $this->assertArrayHasKey('test', $this->console->getCommands());
63 | $this->assertEquals('ConsoleKit\Tests\TestCommand', $this->console->getCommand('test'));
64 | }
65 |
66 | public function testExecute()
67 | {
68 | $this->expectOutputString("hello unknown!\n");
69 | $this->console->addCommand('ConsoleKit\Tests\TestCommand');
70 | $this->console->execute('test');
71 | }
72 |
73 | public function testExecuteWithArgs()
74 | {
75 | $this->expectOutputString("hello foo bar!\n");
76 | $this->console->addCommand('ConsoleKit\Tests\TestCommand');
77 | $this->console->execute('test', array('foo', 'bar'));
78 | }
79 |
80 | public function testExecuteWithOption()
81 | {
82 | $this->expectOutputString("hello foobar!\n");
83 | $this->console->addCommand('ConsoleKit\Tests\TestCommand');
84 | $this->console->execute('test', array(), array('name' => 'foobar'));
85 | }
86 |
87 | public function testExecuteSubCommand()
88 | {
89 | $this->console->addCommand('ConsoleKit\Tests\TestSubCommand', 'test');
90 | $this->assertEquals('hello foobar!', $this->console->execute('test', array('say-hello', 'foobar')));
91 | $this->assertEquals('hi foobar!', $this->console->execute('test', array('say-hi', 'foobar')));
92 | }
93 |
94 | public function testExecuteFunction()
95 | {
96 | $this->expectOutputString("\033[31mhello foobar!\033[0m\n");
97 | $this->console->addCommand(function($args, $opts, $console) {
98 | $console->writeln(Colors::colorize(sprintf("hello %s!", $args[0]), $opts['color']));
99 | }, 'test');
100 | $this->console->addCommand('test2', function($args, $opts, $console) {
101 | return "success";
102 | });
103 | $this->console->execute('test', array('foobar'), array('color' => 'red'));
104 | $this->assertEquals("success", $this->console->execute('test2'));
105 | }
106 |
107 | public function testRun()
108 | {
109 | $this->expectOutputString("hello unknown!\n");
110 | $this->console->addCommand('ConsoleKit\Tests\TestCommand');
111 | $this->console->run(array('test'));
112 | }
113 |
114 | public function testDefaultCommand()
115 | {
116 | $this->expectOutputString("hello unknown!\n");
117 | $this->console->addCommand('ConsoleKit\Tests\TestCommand', null, true);
118 | $this->console->run(array());
119 | }
120 |
121 | public function testOneCommandWithArguments() {
122 | $this->expectOutputString("hello foobar!\n");
123 | $this->console->addCommand('ConsoleKit\Tests\TestCommand', null, true);
124 | $this->console->setSingleCommand(true);
125 | $this->console->run(array('foobar'));
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/DefautOptionsParserTest.php:
--------------------------------------------------------------------------------
1 | parse(array('arg1'));
14 | $this->assertContains('arg1', $args);
15 | $this->assertEmpty($options);
16 |
17 | list($args, $options) = $parser->parse(array('arg1', 'arg2'));
18 | $this->assertContains('arg1', $args);
19 | $this->assertContains('arg2', $args);
20 | $this->assertEmpty($options);
21 |
22 | list($args, $options) = $parser->parse(array('--foo', 'arg1'));
23 | $this->assertContains('arg1', $args);
24 | $this->assertArrayHasKey('foo', $options);
25 |
26 | list($args, $options) = $parser->parse(array('--foo=bar', '--foobar'));
27 | $this->assertCount(2, $options);
28 | $this->assertArrayHasKey('foo', $options);
29 | $this->assertEquals('bar', $options['foo']);
30 | $this->assertArrayHasKey('foobar', $options);
31 |
32 | list($args, $options) = $parser->parse(array('--foo=bar', '--foo=baz'));
33 | $this->assertArrayHasKey('foo', $options);
34 | $this->assertInternalType('array', $options['foo']);
35 | $this->assertContains('bar', $options['foo']);
36 | $this->assertContains('baz', $options['foo']);
37 |
38 | list($args, $options) = $parser->parse(array('-a', '-bc'));
39 | $this->assertCount(3, $options);
40 | $this->assertArrayHasKey('a', $options);
41 | $this->assertArrayHasKey('b', $options);
42 | $this->assertArrayHasKey('c', $options);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/TestCommand.php:
--------------------------------------------------------------------------------
1 | writeln(sprintf("hello %s!", $name));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/TestStatic.php:
--------------------------------------------------------------------------------
1 | writeln(sprintf("hello %s!", $name));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/TestSubCommand.php:
--------------------------------------------------------------------------------
1 | formater = new TextFormater();
12 | $this->formater->setOptions(array(
13 | 'indentWidth' => 4,
14 | 'indent' => 1,
15 | 'quote' => '>',
16 | 'fgcolor' => 'black',
17 | 'bgcolor' => 'white'
18 | ));
19 | }
20 |
21 | public function testSetOptions()
22 | {
23 | $this->assertEquals(4, $this->formater->getIndentWidth());
24 | $this->assertEquals(1, $this->formater->getIndent());
25 | $this->assertEquals('>', $this->formater->getQuote());
26 | $this->assertEquals('black', $this->formater->getFgColor());
27 | $this->assertEquals('white', $this->formater->getBgColor());
28 | }
29 |
30 | public function testFormat()
31 | {
32 | $expected = "\033[47m\033[30m> my text\033[0m";
33 | $this->assertEquals($expected, $this->formater->format('my text'));
34 | }
35 |
36 | public function testFormatMultiline()
37 | {
38 | $expected = "\033[47m\033[30m> line1\n> line2\033[0m";
39 | $this->assertEquals($expected, $this->formater->format("line1\nline2"));
40 | }
41 | }
--------------------------------------------------------------------------------
/tests/ConsoleKit/Tests/UtilsTest.php:
--------------------------------------------------------------------------------
1 | 'bar');
12 | $this->assertEquals('bar', Utils::get($data, 'foo'));
13 | $this->assertNull(Utils::get($data, 'unknown'));
14 | $this->assertEquals('default', Utils::get($data, 'unknown', 'default'));
15 | }
16 |
17 | public function testFind()
18 | {
19 | $this->assertEquals(realpath(__FILE__), Utils::find(basename(__FILE__), __DIR__));
20 | }
21 |
22 | public function testFilterFiles()
23 | {
24 | $this->assertEquals(array(__FILE__), Utils::filterFiles(array(__FILE__, 'not_existant_file')));
25 | }
26 |
27 | public function testJoin()
28 | {
29 | $path = "foo" . DIRECTORY_SEPARATOR . "bar";
30 | $this->assertEquals($path, Utils::join('foo', 'bar'));
31 | }
32 |
33 | public function testCamelize()
34 | {
35 | $this->assertEquals('fooBar', Utils::camelize('foo-bar'));
36 | }
37 |
38 | public function testDashized()
39 | {
40 | $this->assertEquals('foo-bar', Utils::dashized('fooBar'));
41 | }
42 | }
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |