Compare commits

..

12 commits

9 changed files with 339 additions and 49 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Force LF
* text eol=lf

41
.php-commitizen.php Normal file
View 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
View 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,
])
;

View file

@ -3,7 +3,24 @@
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.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
@ -22,11 +39,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 +66,9 @@ 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.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

View file

@ -3,7 +3,7 @@ unless otherwise specified, released under the MIT licence as follows:
----- 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
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,7 +1,7 @@
# PHP Helpers: Command-line Functions
- Version: v1.1.7
- Date: May 26 2019
- 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,8 +28,11 @@ 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()`
- `usage()`
- `manpage()`
- `get_window_size()`
@ -47,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)
@ -59,18 +65,31 @@ 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.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 +101,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 +122,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.
//
@ -128,8 +147,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

View file

@ -1,9 +1,13 @@
{
"name": "pointybeard/helpers-functions-cli",
"version": "1.1.7",
"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",
@ -14,24 +18,34 @@
],
"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",
"pointybeard/helpers-exceptions-readabletrace": "~1.0.0"
},
"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": {
"psr-4": {
"pointybeard\\Helpers\\Functions\\": "src/"
},
"files": [
"src/Cli/Cli.php"
]
},
"scripts": {
"tidy": "php-cs-fixer fix -v --using-cache=no",
"tidyDry": "@tidy --dry-run",
"test": [
"parallel-lint . --exclude vendor"
]
}
}

View file

@ -2,14 +2,116 @@
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;
use pointybeard\Helpers\Cli\Input;
use Exception;
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.
@ -67,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',
@ -108,25 +210,20 @@ if (!function_exists(__NAMESPACE__.'manpage')) {
$arguments[] = (string) $a;
}
foreach ($collection->getTypes() as $type) {
if ('Argument' == $type) {
continue;
}
foreach ($collection->getItemsByType($type) as $o) {
foreach ($collection->getItemsExcludeByType('Argument') 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;
$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
@ -135,7 +232,7 @@ if (!function_exists(__NAMESPACE__.'manpage')) {
$sections[] = $colourise($contents).PHP_EOL;
}
return implode($sections, PHP_EOL);
return implode(PHP_EOL, $sections);
}
}
@ -149,7 +246,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(
@ -206,7 +303,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))
: ''

View 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;
}
}