From 7532bd04a01079635f99c26d4434bd9850a5ae89 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Mon, 20 May 2019 22:15:35 +1000 Subject: [PATCH 01/31] Added manpage() and usage() functions --- src/Cli/Cli.php | 89 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 8 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 955b42f..2e54036 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -1,8 +1,14 @@ getArguments() as $a) { + $arguments[] = strtoupper( + // Wrap with square brackets if it's not required + Flags\is_flag_set(Input\AbstractInputType::FLAG_OPTIONAL, $a->flags()) || + !Flags\is_flag_set(Input\AbstractInputType::FLAG_REQUIRED, $a->flags()) + ? "[{$a->name()}]" + : $a->name() + ); + } + $arguments = trim(implode($arguments, ' ')); + + return sprintf( + 'Usage: %s [OPTIONS]... %s%s', + $name, + $arguments, + strlen($arguments) > 0 ? '...' : '' + ); + } +} + +if (!function_exists(__NAMESPACE__.'manpage')) { + function manpage(string $name, string $version, string $description, string $example, Input\InputCollection $collection): string + { + $arguments = $options = []; + + foreach ($collection->getArguments() as $a) { + $arguments[] = (string) $a; + } + + foreach ($collection->getOptions() as $o) { + $options[] = (string) $o; + } + + $arguments = implode($arguments, PHP_EOL.' '); + $options = implode($options, PHP_EOL.' '); + + return sprintf( + '%s %s, %s +%s + +Mandatory values for long options are mandatory for short options too. + +Arguments: + %s + +Options: + %s + +Examples: + %s +', + $name, + $version, + Strings\utf8_wordwrap($description), + usage($name, $collection), + $arguments, + $options, + $example + ); } } From 2ec172677a89079a4a74bbc0eca9bf99bdcd06e5 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Mon, 20 May 2019 22:16:21 +1000 Subject: [PATCH 02/31] Added pointybeard/helpers-cli-input, pointybeard/helpers-functions-strings, and pointybeard/helpers-functions-flags packages. Requiring PHP 7.2 or greater. --- composer.json | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 36394bb..3923132 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.0.1", + "version": "1.1.0", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -13,11 +13,13 @@ } ], "require": { - "php": ">=5.6.6" + "php": ">=7.2", + "pointybeard/helpers-cli-input": "~1.0", + "pointybeard/helpers-functions-strings": "~1.0", + "pointybeard/helpers-functions-flags": "~1.0" }, "require-dev": { - "phpunit/phpunit": "^5", - "block8/php-docblock-checker": "~1.10" + "phpunit/phpunit": "^8" }, "support": { "issues": "https://github.com/pointybeard/helpers-functions-cli/issues", From 233fee602d64a537197626686b2bcf8cccb96e76 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Mon, 20 May 2019 22:16:37 +1000 Subject: [PATCH 03/31] Updated README and CHANGELOG for 1.1.0 --- CHANGELOG.md | 11 +++++++++- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73b41dd..f92e858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,17 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.0][] +#### Added +- Added `manpage` and `usage` functions + +#### Changed +- Requiring PHP 7.2 or greater +- Including `pointybeard/helpers-cli-input`, `pointybeard/helpers-functions-strings`, and `pointybeard/helpers-functions-flags` composer packages + ## 1.0.0 #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.0.0...integration +[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.0...integration +[1.1.0]: https://github.com/pointybeard/helpers-functions-cli/compare/1.0.0...1.1.0 diff --git a/README.md b/README.md index ea84c05..85a1a8a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.0.1 -- Date: May 09 2019 +- Version: v1.1.0 +- Date: May 20 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -9,7 +9,7 @@ A collection of functions relating to the command-line ## Installation -This library is installed via [Composer](http://getcomposer.org/). To install, use `composer require pointybeard/helpers-functions-cli` or add `"pointybeard/helpers-functions-cli": "~1.0"` to your `composer.json` file. +This library is installed via [Composer](http://getcomposer.org/). To install, use `composer require pointybeard/helpers-functions-cli` or add `"pointybeard/helpers-functions-cli": "~1.1"` to your `composer.json` file. And run composer to update your dependencies: @@ -18,9 +18,9 @@ And run composer to update your dependencies: ### Requirements -There are no particuar requirements for this library other than PHP 5.6 or greater. +This library makes use of the [PHP Helpers: Command-line Input and Input Type Handlers](https://github.com/pointybeard/helpers-cli-input), [PHP Helpers: Flag Functions](https://github.com/pointybeard/helpers-functions-flags) (`pointybeard/helpers-functions-flags`) and [PHP Helpers: String Functions](https://github.com/pointybeard/helpers-functions-strings) packages. They are installed automatically via composer. -To include all the [PHP Helpers](https://github.com/pointybeard/helpers) packages on your project, use `composer require pointybeard/helpers` or add `"pointybeard/helpers": "~1.0"` to your composer file. +To include all the [PHP Helpers](https://github.com/pointybeard/helpers) packages on your project, use `composer require pointybeard/helpers` or add `"pointybeard/helpers": "~1.1"` to your composer file. ## Usage @@ -30,14 +30,18 @@ The following functions are provided: - `can_invoke_bash() : bool` - `is_su() : bool` +- `usage(string $name, Cli\Input\InputCollection $collection) : string` +- `manpage(string $name, string $version, string $description, string $example, Cli\Input\InputCollection $collection): string` Example usage: ```php append(new Input\Types\Argument( + 'action', + Input\AbstractInputType::FLAG_REQUIRED, + 'The name of the action to perform' + )) + ->append(new Input\Types\Option( + 'v', + null, + Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_TYPE_INCREMENTING, + 'verbosity level. -v (errors only), -vv (warnings and errors), -vvv (everything).', + null, + 0 + )) + ->append(new Input\Types\Option( + 'd', + 'data', + Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED, + 'Path to the input JSON data' + )) +).PHP_EOL; + +// test 1.0.0, A simple test command +// Usage: test [OPTIONS]... ACTION... +// +// Mandatory values for long options are mandatory for short options too. +// +// Arguments: +// ACTION The name of the action to perform +// +// Options: +// -v verbosity level. -v (errors only), -vv (warnings +// and errors), -vvv (everything). +// -d, --data=VALUE Path to the input JSON data +// +// Examples: +// php -f test.php -- import -vvv -d test.json + ``` ## Support From 4985979992786e6a0ff363900f2d8b823b0e6b2a Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 23 May 2019 16:18:38 +1000 Subject: [PATCH 04/31] Added get_window_size() function --- src/Cli/Cli.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 2e54036..4a09e54 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -102,3 +102,13 @@ Examples: ); } } + +if (!function_exists(__NAMESPACE__.'get_window_size')) { + function get_window_size(): array + { + return [ + 'cols' => exec('tput cols'), + 'lines' => exec('tput lines'), + ]; + } +} From f520bc432379adacad6b8822e911f5873914318b Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 23 May 2019 16:20:05 +1000 Subject: [PATCH 05/31] Updated README and CHANGELOG for 1.1.1 --- CHANGELOG.md | 7 ++++++- README.md | 11 +++++++++-- composer.json | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f92e858..26d0a84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.1][] +#### Added +- Added `get_window_size()` function + ## [1.1.0][] #### Added - Added `manpage` and `usage` functions @@ -17,5 +21,6 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.0...integration +[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...integration +[1.1.1]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.0...1.1.1 [1.1.0]: https://github.com/pointybeard/helpers-functions-cli/compare/1.0.0...1.1.0 diff --git a/README.md b/README.md index 85a1a8a..69c140e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.0 -- Date: May 20 2019 +- Version: v1.1.1 +- Date: May 23 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -32,6 +32,7 @@ The following functions are provided: - `is_su() : bool` - `usage(string $name, Cli\Input\InputCollection $collection) : string` - `manpage(string $name, string $version, string $description, string $example, Cli\Input\InputCollection $collection): string` +- `get_window_size(): array` Example usage: @@ -50,6 +51,12 @@ var_dump(Cli\can_invoke_bash()); var_dump(Cli\is_su()); // bool(false) +var_dump(Cli\get_window_size()); +// array(2) { +// 'cols' => string(3) "103" +// 'lines' => string(2) "68" +// } + echo Cli\manpage( 'test', '1.0.0', diff --git a/composer.json b/composer.json index 3923132..427c8bb 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.0", + "version": "1.1.1", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", From 9b94e2dc5a21327c96fe2817876886f9c983184d Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 23 May 2019 17:11:59 +1000 Subject: [PATCH 06/31] Using latest version of pointybeard/helpers-functions-strings --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 427c8bb..ee69593 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.1", + "version": "1.1.2", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -15,7 +15,7 @@ "require": { "php": ">=7.2", "pointybeard/helpers-cli-input": "~1.0", - "pointybeard/helpers-functions-strings": "~1.0", + "pointybeard/helpers-functions-strings": "~1.1", "pointybeard/helpers-functions-flags": "~1.0" }, "require-dev": { From 3dd7d2efe1eba0a95b384c70ecb38dce39d51411 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 23 May 2019 17:12:13 +1000 Subject: [PATCH 07/31] Updated README and CHANGELOG for 1.1.2 --- CHANGELOG.md | 4 ++++ README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26d0a84..f3ef781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.2][] +#### Changed +- Using latest version of `pointybeard/helpers-functions-strings` + ## [1.1.1][] #### Added - Added `get_window_size()` function diff --git a/README.md b/README.md index 69c140e..9cec68b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.1 +- Version: v1.1.2 - Date: May 23 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) From 2387e2a8eaacb77b9798d71a72699d683c6c3dbc Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 13:18:04 +1000 Subject: [PATCH 08/31] Added pointybeard/helpers-cli-colour. Ensuring latest version of helper classes are used --- composer.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index ee69593..a1eb815 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.2", + "version": "1.1.3", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -14,9 +14,10 @@ ], "require": { "php": ">=7.2", - "pointybeard/helpers-cli-input": "~1.0", + "pointybeard/helpers-cli-input": "~1", + "pointybeard/helpers-cli-colour": "~1", "pointybeard/helpers-functions-strings": "~1.1", - "pointybeard/helpers-functions-flags": "~1.0" + "pointybeard/helpers-functions-flags": "~1" }, "require-dev": { "phpunit/phpunit": "^8" From cfc256347dd67edff2af83a1e5c51a06645c3233 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 13:34:46 +1000 Subject: [PATCH 09/31] Updated manpage() to include foregroundColour, headingColour, and additional arguments. Removed example argument in favour of including it inside additional. --- src/Cli/Cli.php | 53 ++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 4a09e54..c379a49 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace pointybeard\Helpers\Functions\Cli; use pointybeard\Helpers\Cli\Input; +use pointybeard\Helpers\Cli\Colour; use pointybeard\Helpers\Functions\Flags; use pointybeard\Helpers\Functions\Strings; @@ -62,7 +63,7 @@ if (!function_exists(__NAMESPACE__.'usage')) { } if (!function_exists(__NAMESPACE__.'manpage')) { - function manpage(string $name, string $version, string $description, string $example, Input\InputCollection $collection): string + function manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additional=[]): string { $arguments = $options = []; @@ -74,32 +75,38 @@ if (!function_exists(__NAMESPACE__.'manpage')) { $options[] = (string) $o; } - $arguments = implode($arguments, PHP_EOL.' '); - $options = implode($options, PHP_EOL.' '); + $arguments = implode($arguments, PHP_EOL); + $options = implode($options, PHP_EOL); - return sprintf( - '%s %s, %s -%s + // Convienence function for wrapping a heading with colour + $heading = function(string $input) use ($headingColour) { + return Colour\Colour::colourise($input, $headingColour); + }; -Mandatory values for long options are mandatory for short options too. + // Convienence function for wrapping input in a specified colour + $colourise = function(string $input) use ($foregroundColour) { + return Colour\Colour::colourise($input, $foregroundColour); + }; + $sections = [ + $colourise(sprintf( + "%s %s, %s\r\n%s\r\n", + $name, + $version, + $description, + usage($name, $collection) + )), + $heading('Arguments:'), + $colourise($arguments) . PHP_EOL, + $heading('Options:'), + $colourise($options) . PHP_EOL + ]; -Arguments: - %s + foreach($additional as $name => $contents) { + $sections[] = $heading("{$name}:"); + $sections[] = $colourise($contents) . PHP_EOL; + } -Options: - %s - -Examples: - %s -', - $name, - $version, - Strings\utf8_wordwrap($description), - usage($name, $collection), - $arguments, - $options, - $example - ); + return implode($sections, PHP_EOL); } } From 627406241e39ac6329e77fbb374235e792c10e0f Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 13:20:08 +1000 Subject: [PATCH 10/31] Updated README and CHANGELOG for 1.1.3 release --- CHANGELOG.md | 11 +++++++++-- README.md | 39 ++++++++++++++++++++++----------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3ef781..8cdbbdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,14 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.3][] +#### Changed +- Updated `manpage()` to include `foregroundColour`, `headingColour`, and `additional` arguments. Removed `example` argument in favour of including it inside `additional`. +- Added `pointybeard/helpers-cli-colour` composer package + ## [1.1.2][] #### Changed -- Using latest version of `pointybeard/helpers-functions-strings` +- Using latest version of `pointybeard/helpers-functions-strings` ## [1.1.1][] #### Added @@ -25,6 +30,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...integration +[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.3...integration +[1.1.3]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.2...1.1.3 +[1.1.2]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...1.1.2 [1.1.1]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.0...1.1.1 [1.1.0]: https://github.com/pointybeard/helpers-functions-cli/compare/1.0.0...1.1.0 diff --git a/README.md b/README.md index 9cec68b..b7338f2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.2 -- Date: May 23 2019 +- Version: v1.1.3 +- Date: May 24 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -18,9 +18,9 @@ And run composer to update your dependencies: ### Requirements -This library makes use of the [PHP Helpers: Command-line Input and Input Type Handlers](https://github.com/pointybeard/helpers-cli-input), [PHP Helpers: Flag Functions](https://github.com/pointybeard/helpers-functions-flags) (`pointybeard/helpers-functions-flags`) and [PHP Helpers: String Functions](https://github.com/pointybeard/helpers-functions-strings) packages. They are installed automatically via composer. +This library makes use of the [PHP Helpers: Command-line Input and Input Type Handlers](https://github.com/pointybeard/helpers-cli-input), [PHP Helpers: Flag Functions](https://github.com/pointybeard/helpers-functions-flags) (`pointybeard/helpers-functions-flags`), [PHP Helpers: Command-line Colour](https://github.com/pointybeard/helpers-cli-colours) (`pointybeard/helpers-cli-colours`) and [PHP Helpers: String Functions](https://github.com/pointybeard/helpers-functions-strings) packages. They are installed automatically via composer. -To include all the [PHP Helpers](https://github.com/pointybeard/helpers) packages on your project, use `composer require pointybeard/helpers` or add `"pointybeard/helpers": "~1.1"` to your composer file. +To include all the [PHP Helpers](https://github.com/pointybeard/helpers) packages on your project, use `composer require pointybeard/helpers` or add `"pointybeard/helpers": "~1"` to your composer file. ## Usage @@ -43,6 +43,7 @@ declare(strict_types=1); include __DIR__.'/vendor/autoload.php'; use pointybeard\Helpers\Cli\Input; +use pointybeard\Helpers\Cli\Colour\Colour; use pointybeard\Helpers\Functions\Cli; var_dump(Cli\can_invoke_bash()); @@ -60,13 +61,12 @@ var_dump(Cli\get_window_size()); echo Cli\manpage( 'test', '1.0.0', - 'A simple test command', - 'php -f test.php -- import -vvv -d test.json', + 'A simple test command with a really long description. This is an intentionally very long argument description so we can check that word wrapping is working correctly. It should wrap to the window', (new Input\InputCollection()) ->append(new Input\Types\Argument( 'action', Input\AbstractInputType::FLAG_REQUIRED, - 'The name of the action to perform' + 'The name of the action to perform. This is an intentionally very long argument description so we can check that word wrapping is working correctly' )) ->append(new Input\Types\Option( 'v', @@ -80,25 +80,30 @@ echo Cli\manpage( 'd', 'data', Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED, - 'Path to the input JSON data' - )) + 'Path to the input JSON data.' + )), + Colour::FG_GREEN, + Colour::FG_WHITE, + [ + 'Examples' => 'php -f test.php -- import -vvv -d test.json' + ] ).PHP_EOL; -// test 1.0.0, A simple test command +// test 1.0.0, A simple test command with a really long description. This is an intentionally very long argument description so we can check that word wrapping is working correctly. It should wrap to the window // Usage: test [OPTIONS]... ACTION... // -// Mandatory values for long options are mandatory for short options too. -// // Arguments: -// ACTION The name of the action to perform +// ACTION The name of the action to perform. This is an +// intentionally very long argument description so we can check +// that word wrapping is working correctly // // Options: -// -v verbosity level. -v (errors only), -vv (warnings -// and errors), -vvv (everything). -// -d, --data=VALUE Path to the input JSON data +// -v verbosity level. -v (errors only), -vv +// (warnings and errors), -vvv (everything). +// -d, --data=VALUE Path to the input JSON data. // // Examples: -// php -f test.php -- import -vvv -d test.json +// php -f test.php -- import -vvv -d test.json ``` From 3f07310dfeb5361ee30ff6284c0a2fc07da902a6 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 14:01:38 +1000 Subject: [PATCH 11/31] Refactoring of manpage() to hide 'Options' and/or 'Arguments' if there are none to show. --- src/Cli/Cli.php | 108 +++++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index c379a49..14e9454 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -62,54 +62,10 @@ if (!function_exists(__NAMESPACE__.'usage')) { } } -if (!function_exists(__NAMESPACE__.'manpage')) { - function manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additional=[]): string - { - $arguments = $options = []; - - foreach ($collection->getArguments() as $a) { - $arguments[] = (string) $a; - } - - foreach ($collection->getOptions() as $o) { - $options[] = (string) $o; - } - - $arguments = implode($arguments, PHP_EOL); - $options = implode($options, PHP_EOL); - - // Convienence function for wrapping a heading with colour - $heading = function(string $input) use ($headingColour) { - return Colour\Colour::colourise($input, $headingColour); - }; - - // Convienence function for wrapping input in a specified colour - $colourise = function(string $input) use ($foregroundColour) { - return Colour\Colour::colourise($input, $foregroundColour); - }; - $sections = [ - $colourise(sprintf( - "%s %s, %s\r\n%s\r\n", - $name, - $version, - $description, - usage($name, $collection) - )), - $heading('Arguments:'), - $colourise($arguments) . PHP_EOL, - $heading('Options:'), - $colourise($options) . PHP_EOL - ]; - - foreach($additional as $name => $contents) { - $sections[] = $heading("{$name}:"); - $sections[] = $colourise($contents) . PHP_EOL; - } - - return implode($sections, PHP_EOL); - } -} - +/** + * Uses tput to find out the size of the window (columns and lines) + * @return array an array containing exactly 2 items: 'cols' and 'lines' + */ if (!function_exists(__NAMESPACE__.'get_window_size')) { function get_window_size(): array { @@ -119,3 +75,59 @@ if (!function_exists(__NAMESPACE__.'get_window_size')) { ]; } } + +if (!function_exists(__NAMESPACE__.'manpage')) { + function manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additionalSections=[]): string + { + // Convienence function for wrapping a heading with colour + $heading = function(string $input) use ($headingColour) { + return Colour\Colour::colourise($input, $headingColour); + }; + + // Convienence function for wrapping input in a specified colour + $colourise = function(string $input) use ($foregroundColour) { + return Colour\Colour::colourise($input, $foregroundColour); + }; + + $sections = [ + $colourise(sprintf( + "%s %s, %s\r\n%s\r\n", + $name, + $version, + $description, + usage($name, $collection) + )), + ]; + + $arguments = []; + $options = []; + + foreach ($collection->getArguments() as $a) { + $arguments[] = (string) $a; + } + + foreach ($collection->getOptions() as $o) { + $options[] = (string) $o; + } + + // Add the arguments, if there are any. + if(false === empty($arguments)){ + $sections[] = $heading("Arguments:"); + $sections[] = $colourise(implode($arguments, PHP_EOL)) . PHP_EOL; + } + + // Add the options, if there are any. + if(false === empty($options)){ + $sections[] = $heading("Options:"); + $sections[] = $colourise(implode($options, PHP_EOL)) . PHP_EOL; + } + + // Iterate over all additional items and add them as new sections + foreach($additionalSections as $name => $contents) { + $sections[] = $heading("{$name}:"); + $sections[] = $colourise($contents) . PHP_EOL; + } + + return implode($sections, PHP_EOL); + } +} From 94801ba0e6a744873bab30f1a1da123cab01b004 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 14:02:21 +1000 Subject: [PATCH 12/31] Updated composer.json, README, and CHANGELOG for 1.1.4 release --- CHANGELOG.md | 7 ++++++- README.md | 4 ++-- composer.json | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cdbbdb..65dd31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.4][] +#### Changed +- Refactoring of `manpage()` to hide 'Options' and/or 'Arguments' if there are none to show. + ## [1.1.3][] #### Changed - Updated `manpage()` to include `foregroundColour`, `headingColour`, and `additional` arguments. Removed `example` argument in favour of including it inside `additional`. @@ -30,7 +34,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.3...integration +[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.4...integration +[1.1.4]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.3...1.1.4 [1.1.3]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.2...1.1.3 [1.1.2]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...1.1.2 [1.1.1]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.0...1.1.1 diff --git a/README.md b/README.md index b7338f2..4c17d04 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.3 +- Version: v1.1.4 - Date: May 24 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -31,7 +31,7 @@ The following functions are provided: - `can_invoke_bash() : bool` - `is_su() : bool` - `usage(string $name, Cli\Input\InputCollection $collection) : string` -- `manpage(string $name, string $version, string $description, string $example, Cli\Input\InputCollection $collection): string` +- `manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additional=[]): string` - `get_window_size(): array` Example usage: diff --git a/composer.json b/composer.json index a1eb815..13571fb 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.3", + "version": "1.1.4", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", From f40e14845e663b75b386074237eac28ccf7c967f Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 17:33:00 +1000 Subject: [PATCH 13/31] Updated to work with pointybeard/helpers-cli-input 1.1.x --- src/Cli/Cli.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 14e9454..00d1703 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -42,7 +42,7 @@ if (!function_exists(__NAMESPACE__.'usage')) { function usage(string $name, Input\InputCollection $collection): string { $arguments = []; - foreach ($collection->getArguments() as $a) { + foreach ($collection->getItemsByType('Argument') as $a) { $arguments[] = strtoupper( // Wrap with square brackets if it's not required Flags\is_flag_set(Input\AbstractInputType::FLAG_OPTIONAL, $a->flags()) || @@ -102,12 +102,17 @@ if (!function_exists(__NAMESPACE__.'manpage')) { $arguments = []; $options = []; - foreach ($collection->getArguments() as $a) { + foreach ($collection->getItemsByType("Argument") as $a) { $arguments[] = (string) $a; } - foreach ($collection->getOptions() as $o) { - $options[] = (string) $o; + foreach($collection->getTypes() as $type) { + if($type == 'Argument') { + continue; + } + foreach ($collection->getItemsByType($type) as $o) { + $options[] = (string) $o; + } } // Add the arguments, if there are any. From 075370c3be400161862e04ad18e3b9542cc4866a Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Fri, 24 May 2019 17:34:09 +1000 Subject: [PATCH 14/31] Updated README, CHANGELOG, and composer.json for 1.1.5 release --- CHANGELOG.md | 7 ++++++- README.md | 58 ++++++++++++++++++++++++++++++++------------------- composer.json | 4 ++-- 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65dd31b..98a333d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.5][] +#### Changed +- Updated to work with `pointybeard/helpers-cli-input` v1.1.x + ## [1.1.4][] #### Changed - Refactoring of `manpage()` to hide 'Options' and/or 'Arguments' if there are none to show. @@ -34,7 +38,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.4...integration +[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...integration +[1.1.5]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...1.1.5 [1.1.4]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.3...1.1.4 [1.1.3]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.2...1.1.3 [1.1.2]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...1.1.2 diff --git a/README.md b/README.md index 4c17d04..d861e1f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.4 +- Version: v1.1.5 - Date: May 24 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -60,32 +60,45 @@ var_dump(Cli\get_window_size()); echo Cli\manpage( 'test', - '1.0.0', + '1.0.1', 'A simple test command with a really long description. This is an intentionally very long argument description so we can check that word wrapping is working correctly. It should wrap to the window', (new Input\InputCollection()) - ->append(new Input\Types\Argument( - 'action', - Input\AbstractInputType::FLAG_REQUIRED, - 'The name of the action to perform. This is an intentionally very long argument description so we can check that word wrapping is working correctly' - )) - ->append(new Input\Types\Option( - 'v', - null, - Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_TYPE_INCREMENTING, - 'verbosity level. -v (errors only), -vv (warnings and errors), -vvv (everything).', - null, - 0 - )) - ->append(new Input\Types\Option( - 'd', - 'data', - Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED, - 'Path to the input JSON data.' - )), + ->append( + Input\InputTypeFactory::build('Argument') + ->name('action') + ->flags(Input\AbstractInputType::FLAG_REQUIRED) + ->description('The name of the action to perform. This is an intentionally very long argument description so we can check that word wrapping is working correctly') + ) + ->append( + Input\InputTypeFactory::build('IncrementingFlag') + ->name('v') + ->flags(Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_TYPE_INCREMENTING) + ->description('verbosity level. -v (errors only), -vv (warnings and errors), -vvv (everything).') + ->validator(new Input\Validator( + function (Input\AbstractInputType $input, Input\AbstractInputHandler $context) { + // Make sure verbosity level never goes above 3 + return min(3, (int)$context->find('v')); + } + )) + ) + ->append( + Input\InputTypeFactory::build('Option') + ->name('P') + ->flags(Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_OPTIONAL) + ->description('Port to use for all connections.') + ->default('3306') + ) + ->append( + Input\InputTypeFactory::build('LongOption') + ->name('data') + ->short('d') + ->flags(Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED) + ->description('Path to the input JSON data.') + ), Colour::FG_GREEN, Colour::FG_WHITE, [ - 'Examples' => 'php -f test.php -- import -vvv -d test.json' + 'Examples' => 'php -f test.php -- import -vvv -d test.json', ] ).PHP_EOL; @@ -100,6 +113,7 @@ echo Cli\manpage( // Options: // -v verbosity level. -v (errors only), -vv // (warnings and errors), -vvv (everything). +// -P Port to use for all connections. // -d, --data=VALUE Path to the input JSON data. // // Examples: diff --git a/composer.json b/composer.json index 13571fb..49423c6 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.4", + "version": "1.1.5", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -14,7 +14,7 @@ ], "require": { "php": ">=7.2", - "pointybeard/helpers-cli-input": "~1", + "pointybeard/helpers-cli-input": "~1.1", "pointybeard/helpers-cli-colour": "~1", "pointybeard/helpers-functions-strings": "~1.1", "pointybeard/helpers-functions-flags": "~1" From 5de5730739cbf63342d84699eb031334414a6f9a Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sat, 25 May 2019 14:27:28 +1000 Subject: [PATCH 15/31] Added display_error_and_exit function --- src/Cli/Cli.php | 149 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 121 insertions(+), 28 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 00d1703..dc70b7d 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -8,6 +8,7 @@ use pointybeard\Helpers\Cli\Input; use pointybeard\Helpers\Cli\Colour; use pointybeard\Helpers\Functions\Flags; use pointybeard\Helpers\Functions\Strings; +use pointybeard\Helpers\Functions\Arrays; /* * Checks if bash can be invoked. @@ -38,6 +39,20 @@ if (!function_exists(__NAMESPACE__.'is_su')) { } } +/* + * Uses tput to find out the size of the window (columns and lines) + * @return array an array containing exactly 2 items: 'cols' and 'lines' + */ +if (!function_exists(__NAMESPACE__.'get_window_size')) { + function get_window_size(): array + { + return [ + 'cols' => exec('tput cols'), + 'lines' => exec('tput lines'), + ]; + } +} + if (!function_exists(__NAMESPACE__.'usage')) { function usage(string $name, Input\InputCollection $collection): string { @@ -62,30 +77,16 @@ if (!function_exists(__NAMESPACE__.'usage')) { } } -/** - * Uses tput to find out the size of the window (columns and lines) - * @return array an array containing exactly 2 items: 'cols' and 'lines' - */ -if (!function_exists(__NAMESPACE__.'get_window_size')) { - function get_window_size(): array - { - return [ - 'cols' => exec('tput cols'), - 'lines' => exec('tput lines'), - ]; - } -} - if (!function_exists(__NAMESPACE__.'manpage')) { - function manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additionalSections=[]): string + function manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour = Colour\Colour::FG_DEFAULT, $headingColour = Colour\Colour::FG_WHITE, array $additionalSections = []): string { // Convienence function for wrapping a heading with colour - $heading = function(string $input) use ($headingColour) { + $heading = function (string $input) use ($headingColour) { return Colour\Colour::colourise($input, $headingColour); }; // Convienence function for wrapping input in a specified colour - $colourise = function(string $input) use ($foregroundColour) { + $colourise = function (string $input) use ($foregroundColour) { return Colour\Colour::colourise($input, $foregroundColour); }; @@ -102,12 +103,12 @@ if (!function_exists(__NAMESPACE__.'manpage')) { $arguments = []; $options = []; - foreach ($collection->getItemsByType("Argument") as $a) { + foreach ($collection->getItemsByType('Argument') as $a) { $arguments[] = (string) $a; } - foreach($collection->getTypes() as $type) { - if($type == 'Argument') { + foreach ($collection->getTypes() as $type) { + if ('Argument' == $type) { continue; } foreach ($collection->getItemsByType($type) as $o) { @@ -116,23 +117,115 @@ if (!function_exists(__NAMESPACE__.'manpage')) { } // Add the arguments, if there are any. - if(false === empty($arguments)){ - $sections[] = $heading("Arguments:"); - $sections[] = $colourise(implode($arguments, PHP_EOL)) . PHP_EOL; + if (false === empty($arguments)) { + $sections[] = $heading('Arguments:'); + $sections[] = $colourise(implode($arguments, PHP_EOL)).PHP_EOL; } // Add the options, if there are any. - if(false === empty($options)){ - $sections[] = $heading("Options:"); - $sections[] = $colourise(implode($options, PHP_EOL)) . PHP_EOL; + if (false === empty($options)) { + $sections[] = $heading('Options:'); + $sections[] = $colourise(implode($options, PHP_EOL)).PHP_EOL; } // Iterate over all additional items and add them as new sections - foreach($additionalSections as $name => $contents) { + foreach ($additionalSections as $name => $contents) { $sections[] = $heading("{$name}:"); - $sections[] = $colourise($contents) . PHP_EOL; + $sections[] = $colourise($contents).PHP_EOL; } return implode($sections, PHP_EOL); } } + +if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { + function display_error_and_exit($message, $heading = 'Error', $background = Colour\Colour::BG_RED): void + { + $padCharacter = ' '; + $paddingBufferSize = 0.15; // 15% + $minimumWindowWidth = 40; + $edgePaddingLength = 5; + $edgePadding = str_repeat($padCharacter, $edgePaddingLength); + + // Get the window dimensions but restrict width to minimum + // of $minimumWindowWidth + $window = get_window_size(); + $window['cols'] = max($minimumWindowWidth, $window['cols']); + + // This shrinks the total line length (derived by the window width) by + // $paddingBufferSize + $paddingBuffer = (int) ceil($window['cols'] * $paddingBufferSize); + + $lineLength = $window['cols'] - (2 * $edgePaddingLength) - $paddingBuffer; + + $add_background = function (string $string, bool $bold = false) use ($padCharacter, $edgePadding, $background): string { + $string = $edgePadding.$string.$edgePadding; + + return Colour\Colour::colourise( + $string, + ( + true == $bold + ? Colour\Colour::FG_WHITE + : Colour\Colour::FG_DEFAULT + ), + $background + ); + }; + + $emptyLine = $add_background(str_repeat($padCharacter, $lineLength), true); + $heading = Strings\mb_str_pad(trim($heading), $lineLength, $padCharacter, \STR_PAD_RIGHT); + + $message = Strings\utf8_wordwrap_array($message, $lineLength, PHP_EOL, true); + + // Remove surrounding whitespace + $message = array_map('trim', $message); + + // Remove empty elements from the array + $message = Arrays\array_remove_empty($message); + + // Reset array indicies + $message = array_values($message); + + // Check for a backtrace and get it's index if there is one. Trace + // will most likely have been provided by the + // Helpers\Exceptions\ReadableTrace\ReadableTraceException + $traceArrayIndex = array_search('Trace', $message); + if (false !== $traceArrayIndex) { + // Purely cosmetic; add a new line before the trace starts + $message[$traceArrayIndex] = PHP_EOL.$message[$traceArrayIndex]; + } + + // Wrap everything (except the trace) in red + for ($ii = 0; $ii < count($message); ++$ii) { + if (false !== $traceArrayIndex && $ii == $traceArrayIndex) { + break; + } + $message[$ii] = $add_background(Strings\mb_str_pad( + $message[$ii], + mb_strlen($heading), + $padCharacter, + \STR_PAD_RIGHT + )); + } + + // Add an empty red line before the trace (or at the end if there + // is no trace) + Arrays\array_insert_at_index( + $message, + false !== $traceArrayIndex + ? $traceArrayIndex + : count($message), + $emptyLine + ); + + // Print the error message, starting with an empty red line + printf( + "\r\n%s\r\n%s\r\n%s\r\n", + $emptyLine, + $add_background($heading, true), + implode($message, PHP_EOL) + ); + + exit(1); + } +} From 4c33ccfc234eb5edaffe2f1681cff3fe5333bced Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sat, 25 May 2019 14:29:34 +1000 Subject: [PATCH 16/31] Updated composer.json, README, and CHANGELOG for 1.1.6 release --- CHANGELOG.md | 9 +++++++-- README.md | 11 ++++++++--- composer.json | 5 +++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98a333d..6655c1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.6][] +#### Added +- Added `display_error_and_exit` function + ## [1.1.5][] #### Changed - Updated to work with `pointybeard/helpers-cli-input` v1.1.x @@ -38,8 +42,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...integration -[1.1.5]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...1.1.5 +[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.6...integration +[1.1.6]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...1.1.6 +[1.1.5]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.4...1.1.5 [1.1.4]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.3...1.1.4 [1.1.3]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.2...1.1.3 [1.1.2]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...1.1.2 diff --git a/README.md b/README.md index d861e1f..357e10a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.5 -- Date: May 24 2019 +- Version: v1.1.6 +- Date: May 25 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -33,6 +33,7 @@ The following functions are provided: - `usage(string $name, Cli\Input\InputCollection $collection) : string` - `manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additional=[]): string` - `get_window_size(): array` +- `display_error_and_exit($message, $heading = 'Error', $background = Colour\Colour::BG_RED): void` Example usage: @@ -77,7 +78,7 @@ echo Cli\manpage( ->validator(new Input\Validator( function (Input\AbstractInputType $input, Input\AbstractInputHandler $context) { // Make sure verbosity level never goes above 3 - return min(3, (int)$context->find('v')); + return min(3, (int) $context->find('v')); } )) ) @@ -119,6 +120,10 @@ echo Cli\manpage( // Examples: // php -f test.php -- import -vvv -d test.json +Cli\display_error_and_exit('Looks like something went wrong!', 'Fatal Error'); +// Fatal Error +// Looks like something went wrong! + ``` ## Support diff --git a/composer.json b/composer.json index 49423c6..9ee7234 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.5", + "version": "1.1.6", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -17,7 +17,8 @@ "pointybeard/helpers-cli-input": "~1.1", "pointybeard/helpers-cli-colour": "~1", "pointybeard/helpers-functions-strings": "~1.1", - "pointybeard/helpers-functions-flags": "~1" + "pointybeard/helpers-functions-flags": "~1", + "pointybeard/helpers-functions-arrays": "~1" }, "require-dev": { "phpunit/phpunit": "^8" From 9f9e6cb3975ec6ce65081ca90440bd9dff57d523 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sun, 26 May 2019 20:16:27 +1000 Subject: [PATCH 17/31] Added pointybeard/helpers-functions-debug package --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9ee7234..3ea5188 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "pointybeard/helpers-cli-colour": "~1", "pointybeard/helpers-functions-strings": "~1.1", "pointybeard/helpers-functions-flags": "~1", - "pointybeard/helpers-functions-arrays": "~1" + "pointybeard/helpers-functions-arrays": "~1", + "pointybeard/helpers-functions-debug": "~1" }, "require-dev": { "phpunit/phpunit": "^8" From 558ea1ba2cfdafe6b5977d81545fe5017c7655c5 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sun, 26 May 2019 20:19:25 +1000 Subject: [PATCH 18/31] Using readable_debug_backtrace (provided by pointybeard/helpers-functions-debug) to produce a trace if one is provided --- src/Cli/Cli.php | 58 +++++++++++++++++++------------------------------ 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index dc70b7d..472784b 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -9,6 +9,7 @@ use pointybeard\Helpers\Cli\Colour; use pointybeard\Helpers\Functions\Flags; use pointybeard\Helpers\Functions\Strings; use pointybeard\Helpers\Functions\Arrays; +use pointybeard\Helpers\Functions\Debug; /* * Checks if bash can be invoked. @@ -139,7 +140,7 @@ if (!function_exists(__NAMESPACE__.'manpage')) { } if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { - function display_error_and_exit($message, $heading = 'Error', $background = Colour\Colour::BG_RED): void + function display_error_and_exit($message, $heading = 'Error', $background = Colour\Colour::BG_RED, ?array $trace = null): void { $padCharacter = ' '; $paddingBufferSize = 0.15; // 15% @@ -147,17 +148,7 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { $edgePaddingLength = 5; $edgePadding = str_repeat($padCharacter, $edgePaddingLength); - // Get the window dimensions but restrict width to minimum - // of $minimumWindowWidth - $window = get_window_size(); - $window['cols'] = max($minimumWindowWidth, $window['cols']); - - // This shrinks the total line length (derived by the window width) by - // $paddingBufferSize - $paddingBuffer = (int) ceil($window['cols'] * $paddingBufferSize); - - $lineLength = $window['cols'] - (2 * $edgePaddingLength) - $paddingBuffer; - + // Convenience function for adding the background to a line. $add_background = function (string $string, bool $bold = false) use ($padCharacter, $edgePadding, $background): string { $string = $edgePadding.$string.$edgePadding; @@ -172,6 +163,17 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { ); }; + // Get the window dimensions but restrict width to minimum + // of $minimumWindowWidth + $window = get_window_size(); + $window['cols'] = max($minimumWindowWidth, $window['cols']); + + // This shrinks the total line length (derived by the window width) by + // $paddingBufferSize + $paddingBuffer = (int) ceil($window['cols'] * $paddingBufferSize); + + $lineLength = $window['cols'] - (2 * $edgePaddingLength) - $paddingBuffer; + $emptyLine = $add_background(str_repeat($padCharacter, $lineLength), true); $heading = Strings\mb_str_pad(trim($heading), $lineLength, $padCharacter, \STR_PAD_RIGHT); @@ -186,20 +188,8 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { // Reset array indicies $message = array_values($message); - // Check for a backtrace and get it's index if there is one. Trace - // will most likely have been provided by the - // Helpers\Exceptions\ReadableTrace\ReadableTraceException - $traceArrayIndex = array_search('Trace', $message); - if (false !== $traceArrayIndex) { - // Purely cosmetic; add a new line before the trace starts - $message[$traceArrayIndex] = PHP_EOL.$message[$traceArrayIndex]; - } - - // Wrap everything (except the trace) in red + // Wrap everything in red for ($ii = 0; $ii < count($message); ++$ii) { - if (false !== $traceArrayIndex && $ii == $traceArrayIndex) { - break; - } $message[$ii] = $add_background(Strings\mb_str_pad( $message[$ii], mb_strlen($heading), @@ -208,22 +198,18 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { )); } - // Add an empty red line before the trace (or at the end if there - // is no trace) - Arrays\array_insert_at_index( - $message, - false !== $traceArrayIndex - ? $traceArrayIndex - : count($message), - $emptyLine - ); + // Add an empty red line at the end + array_push($message, $emptyLine); // Print the error message, starting with an empty red line printf( - "\r\n%s\r\n%s\r\n%s\r\n", + "\r\n%s\r\n%s\r\n%s\r\n%s", $emptyLine, $add_background($heading, true), - implode($message, PHP_EOL) + implode($message, PHP_EOL), + !empty($trace) && count($trace) > 0 + ? PHP_EOL . sprintf("Trace\r\n==========\r\n%s\r\n", Debug\readable_debug_backtrace($trace)) + : '' ); exit(1); From 2274998ed63830518111a4793da29019fd78da54 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sun, 26 May 2019 20:22:01 +1000 Subject: [PATCH 19/31] Upated README, CHANGELOG, and composer.json for 1.1.7 release --- CHANGELOG.md | 7 +++++++ README.md | 16 ++++++++-------- composer.json | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6655c1c..145cbda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/). **View all [Unreleased][] changes here** +## [1.1.7][] +#### Added +- Added `pointybeard/helpers-functions-debug` package + +#### Changed +- Using `readable_debug_backtrace()` (provided by `pointybeard/helpers-functions-debug`) in `display_error_and_exit()` to produce a trace if one is provided + ## [1.1.6][] #### Added - Added `display_error_and_exit` function diff --git a/README.md b/README.md index 357e10a..ae774ec 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.6 -- Date: May 25 2019 +- Version: v1.1.7 +- Date: May 26 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -28,12 +28,12 @@ This library is a collection convenience function for command-line tasks. They a The following functions are provided: -- `can_invoke_bash() : bool` -- `is_su() : bool` -- `usage(string $name, Cli\Input\InputCollection $collection) : string` -- `manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additional=[]): string` -- `get_window_size(): array` -- `display_error_and_exit($message, $heading = 'Error', $background = Colour\Colour::BG_RED): void` +- `can_invoke_bash()` +- `is_su()` +- `usage()` +- `manpage()` +- `get_window_size()` +- `display_error_and_exit()` Example usage: diff --git a/composer.json b/composer.json index 3ea5188..f9951c2 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.6", + "version": "1.1.7", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", From f605849ee0f3300b52ba93a14ff41ddc6a148e4b Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sat, 1 Jun 2019 15:02:30 +1000 Subject: [PATCH 20/31] Updated manpage() to work with pointybeard/helpers-cli-input 1.2 --- src/Cli/Cli.php | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 472784b..f9d760b 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -108,13 +108,8 @@ if (!function_exists(__NAMESPACE__.'manpage')) { $arguments[] = (string) $a; } - foreach ($collection->getTypes() as $type) { - if ('Argument' == $type) { - continue; - } - foreach ($collection->getItemsByType($type) as $o) { - $options[] = (string) $o; - } + foreach ($collection->getItemsExcludeByType('Argument') as $o) { + $options[] = (string) $o; } // Add the arguments, if there are any. @@ -208,7 +203,7 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { $add_background($heading, true), implode($message, PHP_EOL), !empty($trace) && count($trace) > 0 - ? PHP_EOL . sprintf("Trace\r\n==========\r\n%s\r\n", Debug\readable_debug_backtrace($trace)) + ? PHP_EOL.sprintf("Trace\r\n==========\r\n%s\r\n", Debug\readable_debug_backtrace($trace)) : '' ); From 0807b586476873b6e5cea223205042f05a4c7849 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sat, 1 Jun 2019 23:02:15 +1000 Subject: [PATCH 21/31] Using v1.2.x of pointybeard/helpers-cli-input. Updated version constraints. Bumped version to 1.1.8 --- composer.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/composer.json b/composer.json index f9951c2..672eb27 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.7", + "version": "1.1.8", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -14,12 +14,12 @@ ], "require": { "php": ">=7.2", - "pointybeard/helpers-cli-input": "~1.1", - "pointybeard/helpers-cli-colour": "~1", + "pointybeard/helpers-cli-input": "~1.2.0", + "pointybeard/helpers-cli-colour": "~1.0", "pointybeard/helpers-functions-strings": "~1.1", - "pointybeard/helpers-functions-flags": "~1", - "pointybeard/helpers-functions-arrays": "~1", - "pointybeard/helpers-functions-debug": "~1" + "pointybeard/helpers-functions-flags": "~1.0", + "pointybeard/helpers-functions-arrays": "~1.0", + "pointybeard/helpers-functions-debug": "~1.0" }, "require-dev": { "phpunit/phpunit": "^8" From ace7c68ed6d65a71f6d9d9d7b1cc410ba41fb532 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sat, 1 Jun 2019 23:04:07 +1000 Subject: [PATCH 22/31] Updated README and CHANGELOG for 1.1.8 release --- CHANGELOG.md | 13 +++++++++---- README.md | 26 +++++++++++++------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 145cbda..d875479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). -**View all [Unreleased][] changes here** +## [1.1.8][] +#### Changed +- Updated `manpage()` to work with `pointybeard/helpers-cli-input` 1.2 +- Using v1.2.x of `pointybeard/helpers-cli-input` +- Updated version constraints in `composer.json` ## [1.1.7][] #### Added @@ -22,11 +26,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## [1.1.4][] #### Changed -- Refactoring of `manpage()` to hide 'Options' and/or 'Arguments' if there are none to show. +- Refactoring of `manpage()` to hide 'Options' and/or 'Arguments' if there are none to show ## [1.1.3][] #### Changed -- Updated `manpage()` to include `foregroundColour`, `headingColour`, and `additional` arguments. Removed `example` argument in favour of including it inside `additional`. +- Updated `manpage()` to include `foregroundColour`, `headingColour`, and `additional` arguments. Removed `example` argument in favour of including it inside `additional` - Added `pointybeard/helpers-cli-colour` composer package ## [1.1.2][] @@ -49,7 +53,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release -[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.6...integration +[1.1.8]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.7...1.1.8 +[1.1.7]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.6...1.1.7 [1.1.6]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...1.1.6 [1.1.5]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.4...1.1.5 [1.1.4]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.3...1.1.4 diff --git a/README.md b/README.md index ae774ec..3b79609 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.7 -- Date: May 26 2019 +- Version: v1.1.8 +- Date: June 01 2019 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -61,16 +61,16 @@ var_dump(Cli\get_window_size()); echo Cli\manpage( 'test', - '1.0.1', + '1.0.2', 'A simple test command with a really long description. This is an intentionally very long argument description so we can check that word wrapping is working correctly. It should wrap to the window', (new Input\InputCollection()) - ->append( + ->add( Input\InputTypeFactory::build('Argument') ->name('action') ->flags(Input\AbstractInputType::FLAG_REQUIRED) ->description('The name of the action to perform. This is an intentionally very long argument description so we can check that word wrapping is working correctly') ) - ->append( + ->add( Input\InputTypeFactory::build('IncrementingFlag') ->name('v') ->flags(Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_TYPE_INCREMENTING) @@ -82,14 +82,14 @@ echo Cli\manpage( } )) ) - ->append( + ->add( Input\InputTypeFactory::build('Option') ->name('P') ->flags(Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_OPTIONAL) ->description('Port to use for all connections.') ->default('3306') ) - ->append( + ->add( Input\InputTypeFactory::build('LongOption') ->name('data') ->short('d') @@ -103,17 +103,17 @@ echo Cli\manpage( ] ).PHP_EOL; -// test 1.0.0, A simple test command with a really long description. This is an intentionally very long argument description so we can check that word wrapping is working correctly. It should wrap to the window +// test 1.0.2, A simple test command with a really long description. This is an intentionally very long argument description so we can check that word wrapping is working correctly. It should wrap to the window // Usage: test [OPTIONS]... ACTION... // // Arguments: -// ACTION The name of the action to perform. This is an -// intentionally very long argument description so we can check -// that word wrapping is working correctly +// ACTION The name of the action to perform. This is an intentionally very +// long argument description so we can check that word wrapping is +// working correctly // // Options: -// -v verbosity level. -v (errors only), -vv -// (warnings and errors), -vvv (everything). +// -v verbosity level. -v (errors only), -vv (warnings and errors), +// -vvv (everything). // -P Port to use for all connections. // -d, --data=VALUE Path to the input JSON data. // From 38b75f4d8e060871afbc25357bd6ed2103e17d06 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 28 Nov 2019 03:18:16 +0000 Subject: [PATCH 23/31] Removed version number from composer.json. No longer requiring phpunit since currently not being used. --- composer.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/composer.json b/composer.json index 672eb27..63ec837 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,5 @@ { "name": "pointybeard/helpers-functions-cli", - "version": "1.1.8", "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", @@ -21,9 +20,6 @@ "pointybeard/helpers-functions-arrays": "~1.0", "pointybeard/helpers-functions-debug": "~1.0" }, - "require-dev": { - "phpunit/phpunit": "^8" - }, "support": { "issues": "https://github.com/pointybeard/helpers-functions-cli/issues", "wiki": "https://github.com/pointybeard/helpers-functions-cli/wiki" From feb7956bb014af6491e9e61f1f5b62eb9fd0145c Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Mon, 6 Apr 2020 17:23:18 +1000 Subject: [PATCH 24/31] Added helpers-exceptions-readabletrace package --- composer.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 63ec837..e61fcd0 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "pointybeard/helpers-functions-strings": "~1.1", "pointybeard/helpers-functions-flags": "~1.0", "pointybeard/helpers-functions-arrays": "~1.0", - "pointybeard/helpers-functions-debug": "~1.0" + "pointybeard/helpers-functions-debug": "~1.0", + "pointybeard/helpers-exceptions-readabletrace": "~1.0.0" }, "support": { "issues": "https://github.com/pointybeard/helpers-functions-cli/issues", @@ -26,6 +27,9 @@ }, "minimum-stability": "stable", "autoload": { + "psr-4": { + "pointybeard\\Helpers\\Functions\\": "src/" + }, "files": [ "src/Cli/Cli.php" ] From 3ccfe2477fce7af92b1a942d539d8876d35ddf13 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Mon, 6 Apr 2020 17:23:44 +1000 Subject: [PATCH 25/31] Added run_command() function and RunCommandFailedException exception --- src/Cli/Cli.php | 51 +++++++++++++++++++ .../Exceptions/RunCommandFailedException.php | 30 +++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/Cli/Exceptions/RunCommandFailedException.php diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index f9d760b..7b7231f 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -210,3 +210,54 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { exit(1); } } + +/* + * Uses proc_open() to run a command on the shell. Output and errors are captured + * and returned. If the command "fails" to run (i.e. return code is != 0), this + * function will throw an exception. + * + * Note that some commands will return a non-zero status code to signify, for + * example, no results found. This function is unable to tell the difference and + * will trigger an exception regardless. In this instance, It is advised to trap + * that exception and inspect both $stderr and $stdout to decide if it was + * actually due to failed command execution. + * + * @param string $command the full bash command to run + * @param string $stdout (optional) reference to capture output from STDOUT + * @param string $stderr (optional) reference to capture output from STDERR + * + * @throws RunCommandFailedException + */ +if (!function_exists(__NAMESPACE__.'\run_command')) { + function run_command(string $command, string &$stdout = null, string &$stderr = null): void + { + $pipes = null; + $return = null; + + $proc = proc_open( + $command, + [ + ['pipe', 'r'], // STDIN + ['pipe', 'w'], // STDOUT + ['pipe', 'w'], // STDERR + ], + $pipes, + getcwd(), + null + ); + + if (true == is_resource($proc)) { + $stdout = trim(stream_get_contents($pipes[1])); + $stderr = trim(stream_get_contents($pipes[2])); + + // Check the return code. If it's not 0, then the command failed. + if (0 != proc_close($proc)) { + throw new Exceptions\RunCommandFailedException($command, (string) $stderr); + } + } else { + // Something went horribly wrong with proc_open(). This should + // nearly never happen, but, accounting for it regardless. + throw new Exceptions\RunCommandFailedException($command, 'proc_open() returned FALSE'); + } + } +} diff --git a/src/Cli/Exceptions/RunCommandFailedException.php b/src/Cli/Exceptions/RunCommandFailedException.php new file mode 100644 index 0000000..492900f --- /dev/null +++ b/src/Cli/Exceptions/RunCommandFailedException.php @@ -0,0 +1,30 @@ +command = $command; + $this->error = $error; + parent::__construct("Failed to run command. Returned: {$error}", $code, $previous); + } + + public function getCommand(): string + { + return $this->command; + } + + public function getError(): string + { + return $this->error; + } +} From 593410a68b851470c2d02a7d47ce94911da9caff Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Mon, 6 Apr 2020 17:24:04 +1000 Subject: [PATCH 26/31] Updated README, CHANGELOG, and LICENCE for 1.1.9 release --- CHANGELOG.md | 7 +++++++ LICENCE | 2 +- README.md | 21 +++++++++++++++++---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d875479..12fd545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [1.1.9][] +#### Added +- Added `run_command()` function +- Added `RunCommandFailedException` exception +- Added `pointybeard/helpers-exceptions-readabletrace` package + ## [1.1.8][] #### Changed - Updated `manpage()` to work with `pointybeard/helpers-cli-input` 1.2 @@ -53,6 +59,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). #### Added - Initial release +[1.1.9]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.8...1.1.9 [1.1.8]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.7...1.1.8 [1.1.7]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.6...1.1.7 [1.1.6]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.5...1.1.6 diff --git a/LICENCE b/LICENCE index 7d77cd1..cb582d5 100644 --- a/LICENCE +++ b/LICENCE @@ -3,7 +3,7 @@ unless otherwise specified, released under the MIT licence as follows: ----- begin license block ----- -Copyright 2019 Alannah Kearney +Copyright 2019-2020 Alannah Kearney Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 3b79609..bf596c6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.8 -- Date: June 01 2019 +- Version: v1.1.9 +- Date: April 06 2020 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -30,6 +30,7 @@ The following functions are provided: - `can_invoke_bash()` - `is_su()` +- `run_command()` - `usage()` - `manpage()` - `get_window_size()` @@ -59,6 +60,19 @@ var_dump(Cli\get_window_size()); // 'lines' => string(2) "68" // } +Cli\run_command("date", $out); +var_dump($out); +// string(29) "Mon 6 Apr 17:20:29 AEST 2020" + +try{ + Cli\run_command("not -a --command", $out, $err); +} catch(Cli\Exceptions\RunCommandFailedException $ex) { + var_dump($ex->getMessage(), $ex->getCommand(), $ex->getError()); +} +// string(54) "Failed to run command. Returned: sh: 1: not: not found" +// string(16) "not -a --command" +// string(21) "sh: 1: not: not found" + echo Cli\manpage( 'test', '1.0.2', @@ -128,8 +142,7 @@ Cli\display_error_and_exit('Looks like something went wrong!', 'Fatal Error'); ## Support -If you believe you have found a bug, please report it using the [GitHub issue tracker](https://github.com/pointybeard/helpers-functions-cli/issues), -or better yet, fork the library and submit a pull request. +If you believe you have found a bug, please report it using the [GitHub issue tracker](https://github.com/pointybeard/helpers-functions-cli/issues), or better yet, fork the library and submit a pull request. ## Contributing From 630efc99d6b7cb6df8333cf0b569c6d4c2bf16d6 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Sat, 11 Apr 2020 18:51:09 +1000 Subject: [PATCH 27/31] Fixed call to implode() in display_error_and_exit() to avoid deprecation warning. --- src/Cli/Cli.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 7b7231f..dfa54fd 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -201,7 +201,7 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { "\r\n%s\r\n%s\r\n%s\r\n%s", $emptyLine, $add_background($heading, true), - implode($message, PHP_EOL), + implode(PHP_EOL, $message), !empty($trace) && count($trace) > 0 ? PHP_EOL.sprintf("Trace\r\n==========\r\n%s\r\n", Debug\readable_debug_backtrace($trace)) : '' From 33090aee1fa40cd5af916b89b922b3a17c038851 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 29 Jul 2021 14:36:51 +1000 Subject: [PATCH 28/31] feat: Add which() method and refactor run_command() --- src/Cli/Cli.php | 160 +++++++++++++++++++++++++++++++----------------- 1 file changed, 105 insertions(+), 55 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index dfa54fd..86dc180 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -2,14 +2,115 @@ declare(strict_types=1); +/* + * This file is part of the "PHP Helpers: Command-line Functions" repository. + * + * Copyright 2019-2021 Alannah Kearney + * + * For the full copyright and license information, please view the LICENCE + * file that was distributed with this source code. + */ + namespace pointybeard\Helpers\Functions\Cli; -use pointybeard\Helpers\Cli\Input; use pointybeard\Helpers\Cli\Colour; -use pointybeard\Helpers\Functions\Flags; -use pointybeard\Helpers\Functions\Strings; +use pointybeard\Helpers\Cli\Input; use pointybeard\Helpers\Functions\Arrays; use pointybeard\Helpers\Functions\Debug; +use pointybeard\Helpers\Functions\Flags; +use pointybeard\Helpers\Functions\Strings; + +/* + * Uses proc_open() to run a command on the shell. Output and errors are captured + * and returned. If the command "fails" to run (i.e. return code is != 0), this + * function will throw an exception. + * + * Note that some commands will return a non-zero status code to signify, for + * example, no results found. This function is unable to tell the difference and + * will trigger an exception regardless. In this instance, It is advised to trap + * that exception and inspect both $stderr and $stdout to decide if it was + * actually due to failed command execution. + * + * @param string $command the full bash command to run + * @param string $stdout (optional) reference to capture output from STDOUT + * @param string $stderr (optional) reference to capture output from STDERR + * #param string $exitCode (options) reference to capture the command exit code + * + * @throws RunCommandFailedException + */ +if (!function_exists(__NAMESPACE__.'\run_command')) { + function run_command(string $command, string &$stdout = null, string &$stderr = null, int &$exitCode = null): void + { + $pipes = null; + $exitCode = null; + + $proc = proc_open( + "{$command};echo $? >&3", + [ + 0 => ['pipe', 'r'], // STDIN + 1 => ['pipe', 'w'], // STDOUT + 2 => ['pipe', 'w'], // STDERR + 3 => ['pipe', 'w'], // Used to capture the exit code + ], + $pipes, + getcwd(), + null + ); + + // Close STDIN stream + fclose($pipes[0]); + + // (guard) proc_open failed to return a resource + if (false == is_resource($proc)) { + throw new Exceptions\RunCommandFailedException($command, 'proc_open() returned FALSE.'); + } + + // Get contents of STDOUT and close stream + $stdout = trim(stream_get_contents($pipes[1])); + fclose($pipes[1]); + + // Get contents od STDERR and close stream + $stderr = trim(stream_get_contents($pipes[2])); + fclose($pipes[2]); + + // Grab the exit code then close the stream + if (false == feof($pipes[3])) { + $exitCode = (int) trim(stream_get_contents($pipes[3])); + } + fclose($pipes[3]); + + // Close the process we created + proc_close($proc); + + // (guard) proc_close return indiciated a failure + if (0 != $exitCode) { + // There was some kind of error. Throw an exception. + // If STDERR is empty, in effort to give back something + // meaningful, grab contents of STDOUT instead + throw new Exceptions\RunCommandFailedException($command, true == empty(trim($stderr)) ? $stdout : $stderr); + } + } +} + +/* + * Returns the pathname for a specified command (or null if it cannot be found) + * + * @params $command the name of the command to look for + * + * @returns string|null + */ +if (!function_exists(__NAMESPACE__.'\which')) { + function which(string $command): ?string + { + try { + run_command("which {$command}", $output); + } catch (Exception $ex) { + $output = null; + } + + return $output; + } +} /* * Checks if bash can be invoked. @@ -144,7 +245,7 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { $edgePadding = str_repeat($padCharacter, $edgePaddingLength); // Convenience function for adding the background to a line. - $add_background = function (string $string, bool $bold = false) use ($padCharacter, $edgePadding, $background): string { + $add_background = function (string $string, bool $bold = false) use ($edgePadding, $background): string { $string = $edgePadding.$string.$edgePadding; return Colour\Colour::colourise( @@ -210,54 +311,3 @@ if (!function_exists(__NAMESPACE__."\display_error_and_exit")) { exit(1); } } - -/* - * Uses proc_open() to run a command on the shell. Output and errors are captured - * and returned. If the command "fails" to run (i.e. return code is != 0), this - * function will throw an exception. - * - * Note that some commands will return a non-zero status code to signify, for - * example, no results found. This function is unable to tell the difference and - * will trigger an exception regardless. In this instance, It is advised to trap - * that exception and inspect both $stderr and $stdout to decide if it was - * actually due to failed command execution. - * - * @param string $command the full bash command to run - * @param string $stdout (optional) reference to capture output from STDOUT - * @param string $stderr (optional) reference to capture output from STDERR - * - * @throws RunCommandFailedException - */ -if (!function_exists(__NAMESPACE__.'\run_command')) { - function run_command(string $command, string &$stdout = null, string &$stderr = null): void - { - $pipes = null; - $return = null; - - $proc = proc_open( - $command, - [ - ['pipe', 'r'], // STDIN - ['pipe', 'w'], // STDOUT - ['pipe', 'w'], // STDERR - ], - $pipes, - getcwd(), - null - ); - - if (true == is_resource($proc)) { - $stdout = trim(stream_get_contents($pipes[1])); - $stderr = trim(stream_get_contents($pipes[2])); - - // Check the return code. If it's not 0, then the command failed. - if (0 != proc_close($proc)) { - throw new Exceptions\RunCommandFailedException($command, (string) $stderr); - } - } else { - // Something went horribly wrong with proc_open(). This should - // nearly never happen, but, accounting for it regardless. - throw new Exceptions\RunCommandFailedException($command, 'proc_open() returned FALSE'); - } - } -} From b9a3034788e0083afe66c85d6d1978798799964e Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Thu, 29 Jul 2021 14:37:32 +1000 Subject: [PATCH 29/31] chore: Update README, LICENCE, and add code linting --- .gitattributes | 2 + .php-commitizen.php | 41 +++++++++++++ .php-cs-fixer.dist.php | 59 +++++++++++++++++++ CHANGELOG.md | 7 +++ LICENCE | 2 +- README.md | 9 ++- composer.json | 22 +++++-- .../Exceptions/RunCommandFailedException.php | 10 ++++ 8 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 .gitattributes create mode 100644 .php-commitizen.php create mode 100644 .php-cs-fixer.dist.php diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a3aa700 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Force LF +* text eol=lf diff --git a/.php-commitizen.php b/.php-commitizen.php new file mode 100644 index 0000000..8c77023 --- /dev/null +++ b/.php-commitizen.php @@ -0,0 +1,41 @@ + [ + 'lengthMin' => 3, + 'lengthMax' => 8, + 'acceptExtra' => false, + 'values' => [ + 'feat', + 'fix', + 'docs', + 'chore', + 'test', + 'refactor', + 'revert', + 'ci', + ] + ], + 'scope' => [ + 'lengthMin' => 0, + 'lengthMax' => 10, + 'acceptExtra' => true, + 'values' => [], + ], + 'description' => [ + 'lengthMin' => 1, + 'lengthMax' => 47, + ], + 'subject' => [ + 'lengthMin' => 1, + 'lengthMax' => 69, + ], + 'body' => [ + 'wrap' => 72, + ], + 'footer' => [ + 'wrap' => 72, + ], +]; diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..563c415 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,59 @@ + + +For the full copyright and license information, please view the LICENCE +file that was distributed with this source code. +EOF; + +return (new PhpCsFixer\Config()) + ->setUsingCache(true) + ->setRiskyAllowed(true) + ->setFinder( + (new PhpCsFixer\Finder()) + ->files() + ->name('*.php') + ->in(__DIR__) + ->exclude(__DIR__.'/vendor') + ) + ->setRules([ + '@PSR2' => true, + '@Symfony' => true, + 'is_null' => true, + 'blank_line_before_statement' => ['statements' => ['continue', 'declare', 'return', 'throw', 'try']], + 'cast_spaces' => ['space' => 'single'], + 'header_comment' => ['header' => $header], + 'include' => true, + 'class_attributes_separation' => ['elements' => ['const' => 'one', 'method' => 'one', 'property' => 'one']], + 'no_blank_lines_after_class_opening' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_blank_lines' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_trailing_comma_in_singleline_array' => true, + 'no_unused_imports' => true, + 'no_whitespace_in_blank_line' => true, + 'object_operator_without_whitespace' => true, + 'phpdoc_align' => true, + 'phpdoc_indent' => true, + 'phpdoc_no_access' => true, + 'phpdoc_no_package' => true, + 'phpdoc_order' => true, + 'phpdoc_scalar' => true, + 'phpdoc_trim' => true, + 'phpdoc_types' => true, + 'psr_autoloading' => true, + 'array_syntax' => ['syntax' => 'short'], + 'declare_strict_types' => true, + 'single_blank_line_before_namespace' => true, + 'standardize_not_equals' => true, + 'ternary_operator_spaces' => true, + 'trailing_comma_in_multiline' => true, + ]) +; diff --git a/CHANGELOG.md b/CHANGELOG.md index 12fd545..07d8b3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## [1.1.10][] +#### Added +- Added `which()` function + +#### Changed +- Refactor of `run_command()` function to return an exit code + ## [1.1.9][] #### Added - Added `run_command()` function diff --git a/LICENCE b/LICENCE index cb582d5..d91ed67 100644 --- a/LICENCE +++ b/LICENCE @@ -3,7 +3,7 @@ unless otherwise specified, released under the MIT licence as follows: ----- begin license block ----- -Copyright 2019-2020 Alannah Kearney +Copyright 2019-2021 Alannah Kearney Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index bf596c6..5de4990 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PHP Helpers: Command-line Functions -- Version: v1.1.9 -- Date: April 06 2020 +- Version: v1.1.10 +- Date: July 29 2021 - [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md) - [GitHub repository](https://github.com/pointybeard/helpers-functions-cli) @@ -28,6 +28,8 @@ This library is a collection convenience function for command-line tasks. They a The following functions are provided: +- `run_command()` +- `which()` - `can_invoke_bash()` - `is_su()` - `run_command()` @@ -48,6 +50,9 @@ use pointybeard\Helpers\Cli\Input; use pointybeard\Helpers\Cli\Colour\Colour; use pointybeard\Helpers\Functions\Cli; +var_dump(Cli\which("ls")); +// string(11) "/usr/bin/ls" + var_dump(Cli\can_invoke_bash()); // bool(true) diff --git a/composer.json b/composer.json index e61fcd0..87779c7 100644 --- a/composer.json +++ b/composer.json @@ -3,6 +3,11 @@ "description": "A collection of functions relating to the command-line", "homepage": "https://github.com/pointybeard/helpers-functions-cli", "license": "MIT", + "minimum-stability": "stable", + "support": { + "issues": "https://github.com/pointybeard/helpers-functions-cli/issues", + "wiki": "https://github.com/pointybeard/helpers-functions-cli/wiki" + }, "authors": [ { "name": "Alannah Kearney", @@ -21,11 +26,13 @@ "pointybeard/helpers-functions-debug": "~1.0", "pointybeard/helpers-exceptions-readabletrace": "~1.0.0" }, - "support": { - "issues": "https://github.com/pointybeard/helpers-functions-cli/issues", - "wiki": "https://github.com/pointybeard/helpers-functions-cli/wiki" + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.0", + "squizlabs/php_codesniffer": "^3.0", + "damianopetrungaro/php-commitizen": "^0.1.0", + "php-parallel-lint/php-parallel-lint": "^1.0", + "php-parallel-lint/php-console-highlighter": "^0.5.0" }, - "minimum-stability": "stable", "autoload": { "psr-4": { "pointybeard\\Helpers\\Functions\\": "src/" @@ -33,5 +40,12 @@ "files": [ "src/Cli/Cli.php" ] + }, + "scripts": { + "tidy": "php-cs-fixer fix -v --using-cache=no", + "tidyDry": "@tidy --dry-run", + "test": [ + "parallel-lint . --exclude vendor" + ] } } diff --git a/src/Cli/Exceptions/RunCommandFailedException.php b/src/Cli/Exceptions/RunCommandFailedException.php index 492900f..3665808 100644 --- a/src/Cli/Exceptions/RunCommandFailedException.php +++ b/src/Cli/Exceptions/RunCommandFailedException.php @@ -2,6 +2,15 @@ declare(strict_types=1); +/* + * This file is part of the "PHP Helpers: Command-line Functions" repository. + * + * Copyright 2019-2021 Alannah Kearney + * + * For the full copyright and license information, please view the LICENCE + * file that was distributed with this source code. + */ + namespace pointybeard\Helpers\Functions\Cli\Exceptions; use pointybeard\Helpers\Exceptions\ReadableTrace; @@ -9,6 +18,7 @@ use pointybeard\Helpers\Exceptions\ReadableTrace; class RunCommandFailedException extends ReadableTrace\ReadableTraceException { private $command; + private $error; public function __construct(string $command, string $error, int $code = 0, \Exception $previous = null) From 1992fee96b24e4a72f9980f0cbeaf475ff003e03 Mon Sep 17 00:00:00 2001 From: Alannah Kearney Date: Tue, 7 Sep 2021 11:39:39 +1000 Subject: [PATCH 30/31] fix: Add use clause for Exception so which() can catch RunCommandFailedException correctly --- src/Cli/Cli.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index 86dc180..cccb18f 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -13,6 +13,7 @@ declare(strict_types=1); namespace pointybeard\Helpers\Functions\Cli; +use Exception; use pointybeard\Helpers\Cli\Colour; use pointybeard\Helpers\Cli\Input; use pointybeard\Helpers\Functions\Arrays; From cedd316946da20bcca1766a0d0e1d53a1da53986 Mon Sep 17 00:00:00 2001 From: Norbert Wagner <5166607+n3w@users.noreply.github.com> Date: Thu, 23 Mar 2023 08:50:28 +0100 Subject: [PATCH 31/31] Passing the separator after the array is no longer supported --- src/Cli/Cli.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Cli/Cli.php b/src/Cli/Cli.php index cccb18f..79e4f9f 100644 --- a/src/Cli/Cli.php +++ b/src/Cli/Cli.php @@ -169,7 +169,7 @@ if (!function_exists(__NAMESPACE__.'usage')) { : $a->name() ); } - $arguments = trim(implode($arguments, ' ')); + $arguments = trim(implode(' ', $arguments)); return sprintf( 'Usage: %s [OPTIONS]... %s%s', @@ -217,13 +217,13 @@ if (!function_exists(__NAMESPACE__.'manpage')) { // Add the arguments, if there are any. if (false === empty($arguments)) { $sections[] = $heading('Arguments:'); - $sections[] = $colourise(implode($arguments, PHP_EOL)).PHP_EOL; + $sections[] = $colourise(implode(PHP_EOL, $arguments)).PHP_EOL; } // Add the options, if there are any. if (false === empty($options)) { $sections[] = $heading('Options:'); - $sections[] = $colourise(implode($options, PHP_EOL)).PHP_EOL; + $sections[] = $colourise(implode(PHP_EOL, $options)).PHP_EOL; } // Iterate over all additional items and add them as new sections @@ -232,7 +232,7 @@ if (!function_exists(__NAMESPACE__.'manpage')) { $sections[] = $colourise($contents).PHP_EOL; } - return implode($sections, PHP_EOL); + return implode(PHP_EOL, $sections); } }