mirror of
https://github.com/n3w/helpers-functions-cli.git
synced 2025-12-19 12:43:22 +00:00
Compare commits
19 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cedd316946 | ||
|
|
1992fee96b | ||
|
|
b9a3034788 | ||
|
|
33090aee1f | ||
|
|
630efc99d6 | ||
|
|
593410a68b | ||
|
|
3ccfe2477f | ||
|
|
feb7956bb0 | ||
|
|
38b75f4d8e | ||
|
|
ace7c68ed6 | ||
|
|
0807b58647 | ||
|
|
f605849ee0 | ||
|
|
2274998ed6 | ||
|
|
558ea1ba2c | ||
|
|
9f9e6cb397 | ||
|
|
4c33ccfc23 | ||
|
|
5de5730739 | ||
|
|
075370c3be | ||
|
|
f40e14845e |
9 changed files with 503 additions and 91 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Force LF
|
||||||
|
* text eol=lf
|
||||||
41
.php-commitizen.php
Normal file
41
.php-commitizen.php
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'type' => [
|
||||||
|
'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,
|
||||||
|
],
|
||||||
|
];
|
||||||
59
.php-cs-fixer.dist.php
Normal file
59
.php-cs-fixer.dist.php
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
$header = <<<EOF
|
||||||
|
This file is part of the "PHP Helpers: Command-line Functions" repository.
|
||||||
|
|
||||||
|
Copyright 2019-2021 Alannah Kearney <hi@alannahkearney.com>
|
||||||
|
|
||||||
|
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,
|
||||||
|
])
|
||||||
|
;
|
||||||
44
CHANGELOG.md
44
CHANGELOG.md
|
|
@ -3,15 +3,47 @@
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
**View all [Unreleased][] changes here**
|
## [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
|
||||||
|
- Added `RunCommandFailedException` exception
|
||||||
|
- Added `pointybeard/helpers-exceptions-readabletrace` package
|
||||||
|
|
||||||
|
## [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
|
||||||
|
- 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
|
||||||
|
|
||||||
|
## [1.1.5][]
|
||||||
|
#### Changed
|
||||||
|
- Updated to work with `pointybeard/helpers-cli-input` v1.1.x
|
||||||
|
|
||||||
## [1.1.4][]
|
## [1.1.4][]
|
||||||
#### Changed
|
#### 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][]
|
## [1.1.3][]
|
||||||
#### Changed
|
#### 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
|
- Added `pointybeard/helpers-cli-colour` composer package
|
||||||
|
|
||||||
## [1.1.2][]
|
## [1.1.2][]
|
||||||
|
|
@ -34,7 +66,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
#### Added
|
#### Added
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
||||||
[Unreleased]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.4...integration
|
[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
|
||||||
|
[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.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.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.2]: https://github.com/pointybeard/helpers-functions-cli/compare/1.1.1...1.1.2
|
||||||
|
|
|
||||||
2
LICENCE
2
LICENCE
|
|
@ -3,7 +3,7 @@ unless otherwise specified, released under the MIT licence as follows:
|
||||||
|
|
||||||
----- begin license block -----
|
----- begin license block -----
|
||||||
|
|
||||||
Copyright 2019 Alannah Kearney
|
Copyright 2019-2021 Alannah Kearney
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
109
README.md
109
README.md
|
|
@ -1,7 +1,7 @@
|
||||||
# PHP Helpers: Command-line Functions
|
# PHP Helpers: Command-line Functions
|
||||||
|
|
||||||
- Version: v1.1.4
|
- Version: v1.1.10
|
||||||
- Date: May 24 2019
|
- Date: July 29 2021
|
||||||
- [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md)
|
- [Release notes](https://github.com/pointybeard/helpers-functions-cli/blob/master/CHANGELOG.md)
|
||||||
- [GitHub repository](https://github.com/pointybeard/helpers-functions-cli)
|
- [GitHub repository](https://github.com/pointybeard/helpers-functions-cli)
|
||||||
|
|
||||||
|
|
@ -28,11 +28,15 @@ This library is a collection convenience function for command-line tasks. They a
|
||||||
|
|
||||||
The following functions are provided:
|
The following functions are provided:
|
||||||
|
|
||||||
- `can_invoke_bash() : bool`
|
- `run_command()`
|
||||||
- `is_su() : bool`
|
- `which()`
|
||||||
- `usage(string $name, Cli\Input\InputCollection $collection) : string`
|
- `can_invoke_bash()`
|
||||||
- `manpage(string $name, string $version, string $description, Input\InputCollection $collection, $foregroundColour=Colour\Colour::FG_DEFAULT, $headingColour=Colour\Colour::FG_WHITE, array $additional=[]): string`
|
- `is_su()`
|
||||||
- `get_window_size(): array`
|
- `run_command()`
|
||||||
|
- `usage()`
|
||||||
|
- `manpage()`
|
||||||
|
- `get_window_size()`
|
||||||
|
- `display_error_and_exit()`
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
|
|
@ -46,6 +50,9 @@ use pointybeard\Helpers\Cli\Input;
|
||||||
use pointybeard\Helpers\Cli\Colour\Colour;
|
use pointybeard\Helpers\Cli\Colour\Colour;
|
||||||
use pointybeard\Helpers\Functions\Cli;
|
use pointybeard\Helpers\Functions\Cli;
|
||||||
|
|
||||||
|
var_dump(Cli\which("ls"));
|
||||||
|
// string(11) "/usr/bin/ls"
|
||||||
|
|
||||||
var_dump(Cli\can_invoke_bash());
|
var_dump(Cli\can_invoke_bash());
|
||||||
// bool(true)
|
// bool(true)
|
||||||
|
|
||||||
|
|
@ -58,59 +65,89 @@ var_dump(Cli\get_window_size());
|
||||||
// 'lines' => string(2) "68"
|
// '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(
|
echo Cli\manpage(
|
||||||
'test',
|
'test',
|
||||||
'1.0.0',
|
'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',
|
'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())
|
(new Input\InputCollection())
|
||||||
->append(new Input\Types\Argument(
|
->add(
|
||||||
'action',
|
Input\InputTypeFactory::build('Argument')
|
||||||
Input\AbstractInputType::FLAG_REQUIRED,
|
->name('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'
|
->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(new Input\Types\Option(
|
)
|
||||||
'v',
|
->add(
|
||||||
null,
|
Input\InputTypeFactory::build('IncrementingFlag')
|
||||||
Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_TYPE_INCREMENTING,
|
->name('v')
|
||||||
'verbosity level. -v (errors only), -vv (warnings and errors), -vvv (everything).',
|
->flags(Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_TYPE_INCREMENTING)
|
||||||
null,
|
->description('verbosity level. -v (errors only), -vv (warnings and errors), -vvv (everything).')
|
||||||
0
|
->validator(new Input\Validator(
|
||||||
))
|
function (Input\AbstractInputType $input, Input\AbstractInputHandler $context) {
|
||||||
->append(new Input\Types\Option(
|
// Make sure verbosity level never goes above 3
|
||||||
'd',
|
return min(3, (int) $context->find('v'));
|
||||||
'data',
|
}
|
||||||
Input\AbstractInputType::FLAG_OPTIONAL | Input\AbstractInputType::FLAG_VALUE_REQUIRED,
|
))
|
||||||
'Path to the input JSON data.'
|
)
|
||||||
)),
|
->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')
|
||||||
|
)
|
||||||
|
->add(
|
||||||
|
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_GREEN,
|
||||||
Colour::FG_WHITE,
|
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;
|
).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...
|
// Usage: test [OPTIONS]... ACTION...
|
||||||
//
|
//
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// ACTION The name of the action to perform. This is an
|
// ACTION The name of the action to perform. This is an intentionally very
|
||||||
// intentionally very long argument description so we can check
|
// long argument description so we can check that word wrapping is
|
||||||
// that word wrapping is working correctly
|
// working correctly
|
||||||
//
|
//
|
||||||
// Options:
|
// Options:
|
||||||
// -v verbosity level. -v (errors only), -vv
|
// -v verbosity level. -v (errors only), -vv (warnings and errors),
|
||||||
// (warnings and errors), -vvv (everything).
|
// -vvv (everything).
|
||||||
|
// -P Port to use for all connections.
|
||||||
// -d, --data=VALUE Path to the input JSON data.
|
// -d, --data=VALUE Path to the input JSON data.
|
||||||
//
|
//
|
||||||
// Examples:
|
// Examples:
|
||||||
// php -f test.php -- import -vvv -d test.json
|
// 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
|
## 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),
|
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.
|
||||||
or better yet, fork the library and submit a pull request.
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "pointybeard/helpers-functions-cli",
|
"name": "pointybeard/helpers-functions-cli",
|
||||||
"version": "1.1.4",
|
|
||||||
"description": "A collection of functions relating to the command-line",
|
"description": "A collection of functions relating to the command-line",
|
||||||
"homepage": "https://github.com/pointybeard/helpers-functions-cli",
|
"homepage": "https://github.com/pointybeard/helpers-functions-cli",
|
||||||
"license": "MIT",
|
"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": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Alannah Kearney",
|
"name": "Alannah Kearney",
|
||||||
|
|
@ -14,22 +18,34 @@
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.2",
|
"php": ">=7.2",
|
||||||
"pointybeard/helpers-cli-input": "~1",
|
"pointybeard/helpers-cli-input": "~1.2.0",
|
||||||
"pointybeard/helpers-cli-colour": "~1",
|
"pointybeard/helpers-cli-colour": "~1.0",
|
||||||
"pointybeard/helpers-functions-strings": "~1.1",
|
"pointybeard/helpers-functions-strings": "~1.1",
|
||||||
"pointybeard/helpers-functions-flags": "~1"
|
"pointybeard/helpers-functions-flags": "~1.0",
|
||||||
|
"pointybeard/helpers-functions-arrays": "~1.0",
|
||||||
|
"pointybeard/helpers-functions-debug": "~1.0",
|
||||||
|
"pointybeard/helpers-exceptions-readabletrace": "~1.0.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^8"
|
"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"
|
||||||
},
|
},
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/pointybeard/helpers-functions-cli/issues",
|
|
||||||
"wiki": "https://github.com/pointybeard/helpers-functions-cli/wiki"
|
|
||||||
},
|
|
||||||
"minimum-stability": "stable",
|
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"pointybeard\\Helpers\\Functions\\": "src/"
|
||||||
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src/Cli/Cli.php"
|
"src/Cli/Cli.php"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"tidy": "php-cs-fixer fix -v --using-cache=no",
|
||||||
|
"tidyDry": "@tidy --dry-run",
|
||||||
|
"test": [
|
||||||
|
"parallel-lint . --exclude vendor"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
261
src/Cli/Cli.php
261
src/Cli/Cli.php
|
|
@ -2,13 +2,117 @@
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the "PHP Helpers: Command-line Functions" repository.
|
||||||
|
*
|
||||||
|
* Copyright 2019-2021 Alannah Kearney <hi@alannahkearney.com>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENCE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
namespace pointybeard\Helpers\Functions\Cli;
|
namespace pointybeard\Helpers\Functions\Cli;
|
||||||
|
|
||||||
use pointybeard\Helpers\Cli\Input;
|
use Exception;
|
||||||
use pointybeard\Helpers\Cli\Colour;
|
use pointybeard\Helpers\Cli\Colour;
|
||||||
|
use pointybeard\Helpers\Cli\Input;
|
||||||
|
use pointybeard\Helpers\Functions\Arrays;
|
||||||
|
use pointybeard\Helpers\Functions\Debug;
|
||||||
use pointybeard\Helpers\Functions\Flags;
|
use pointybeard\Helpers\Functions\Flags;
|
||||||
use pointybeard\Helpers\Functions\Strings;
|
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.
|
* Checks if bash can be invoked.
|
||||||
*
|
*
|
||||||
|
|
@ -38,31 +142,7 @@ if (!function_exists(__NAMESPACE__.'is_su')) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!function_exists(__NAMESPACE__.'usage')) {
|
/*
|
||||||
function usage(string $name, Input\InputCollection $collection): string
|
|
||||||
{
|
|
||||||
$arguments = [];
|
|
||||||
foreach ($collection->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 ? '...' : ''
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses tput to find out the size of the window (columns and lines)
|
* Uses tput to find out the size of the window (columns and lines)
|
||||||
* @return array an array containing exactly 2 items: 'cols' and 'lines'
|
* @return array an array containing exactly 2 items: 'cols' and 'lines'
|
||||||
*/
|
*/
|
||||||
|
|
@ -76,16 +156,40 @@ if (!function_exists(__NAMESPACE__.'get_window_size')) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function_exists(__NAMESPACE__.'usage')) {
|
||||||
|
function usage(string $name, Input\InputCollection $collection): string
|
||||||
|
{
|
||||||
|
$arguments = [];
|
||||||
|
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()) ||
|
||||||
|
!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')) {
|
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
|
// 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);
|
return Colour\Colour::colourise($input, $headingColour);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convienence function for wrapping input in a specified colour
|
// 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);
|
return Colour\Colour::colourise($input, $foregroundColour);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -102,32 +206,109 @@ if (!function_exists(__NAMESPACE__.'manpage')) {
|
||||||
$arguments = [];
|
$arguments = [];
|
||||||
$options = [];
|
$options = [];
|
||||||
|
|
||||||
foreach ($collection->getArguments() as $a) {
|
foreach ($collection->getItemsByType('Argument') as $a) {
|
||||||
$arguments[] = (string) $a;
|
$arguments[] = (string) $a;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($collection->getOptions() as $o) {
|
foreach ($collection->getItemsExcludeByType('Argument') as $o) {
|
||||||
$options[] = (string) $o;
|
$options[] = (string) $o;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the arguments, if there are any.
|
// Add the arguments, if there are any.
|
||||||
if(false === empty($arguments)){
|
if (false === empty($arguments)) {
|
||||||
$sections[] = $heading("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.
|
// Add the options, if there are any.
|
||||||
if(false === empty($options)){
|
if (false === empty($options)) {
|
||||||
$sections[] = $heading("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
|
// 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[] = $heading("{$name}:");
|
||||||
$sections[] = $colourise($contents) . PHP_EOL;
|
$sections[] = $colourise($contents).PHP_EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode($sections, PHP_EOL);
|
return implode(PHP_EOL, $sections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!function_exists(__NAMESPACE__."\display_error_and_exit")) {
|
||||||
|
function display_error_and_exit($message, $heading = 'Error', $background = Colour\Colour::BG_RED, ?array $trace = null): void
|
||||||
|
{
|
||||||
|
$padCharacter = ' ';
|
||||||
|
$paddingBufferSize = 0.15; // 15%
|
||||||
|
$minimumWindowWidth = 40;
|
||||||
|
$edgePaddingLength = 5;
|
||||||
|
$edgePadding = str_repeat($padCharacter, $edgePaddingLength);
|
||||||
|
|
||||||
|
// Convenience function for adding the background to a line.
|
||||||
|
$add_background = function (string $string, bool $bold = false) use ($edgePadding, $background): string {
|
||||||
|
$string = $edgePadding.$string.$edgePadding;
|
||||||
|
|
||||||
|
return Colour\Colour::colourise(
|
||||||
|
$string,
|
||||||
|
(
|
||||||
|
true == $bold
|
||||||
|
? Colour\Colour::FG_WHITE
|
||||||
|
: Colour\Colour::FG_DEFAULT
|
||||||
|
),
|
||||||
|
$background
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
$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);
|
||||||
|
|
||||||
|
// Wrap everything in red
|
||||||
|
for ($ii = 0; $ii < count($message); ++$ii) {
|
||||||
|
$message[$ii] = $add_background(Strings\mb_str_pad(
|
||||||
|
$message[$ii],
|
||||||
|
mb_strlen($heading),
|
||||||
|
$padCharacter,
|
||||||
|
\STR_PAD_RIGHT
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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%s",
|
||||||
|
$emptyLine,
|
||||||
|
$add_background($heading, true),
|
||||||
|
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))
|
||||||
|
: ''
|
||||||
|
);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
40
src/Cli/Exceptions/RunCommandFailedException.php
Normal file
40
src/Cli/Exceptions/RunCommandFailedException.php
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the "PHP Helpers: Command-line Functions" repository.
|
||||||
|
*
|
||||||
|
* Copyright 2019-2021 Alannah Kearney <hi@alannahkearney.com>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
class RunCommandFailedException extends ReadableTrace\ReadableTraceException
|
||||||
|
{
|
||||||
|
private $command;
|
||||||
|
|
||||||
|
private $error;
|
||||||
|
|
||||||
|
public function __construct(string $command, string $error, int $code = 0, \Exception $previous = null)
|
||||||
|
{
|
||||||
|
$this->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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue