diff --git a/README.md b/README.md index 446eef3..49e4859 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,9 @@ pop-console * [Quickstart](#quickstart) * [Response Buffer](#response-buffer) * [Colors](#colors) +* [Lines](#lines) +* [Headers](#headers) +* [Alerts](#alert) * [Prompt](#prompt) * [Commands](#commands) * [Help Screen](#help-screen) @@ -44,7 +47,7 @@ Install `pop-console` using Composer. Or, require it in your composer.json file "require": { - "popphp/pop-console" : "^4.0.0" + "popphp/pop-console" : "^4.1.0" } [Top](#pop-console) @@ -61,8 +64,8 @@ a prepended header and appended footer. use Pop\Console\Console; $console = new Console(); -$console->setHeader('My Application'); -$console->setFooter('The End'); +$console->setHeader('My Application'); // Set a global header at the start of the script +$console->setFooter('The End'); // Set a global footer at the end of the script $console->append('Here is some console information.'); $console->append('Hope you enjoyed it!'); @@ -80,14 +83,15 @@ The above will output: The End ``` -### Enforcing a terminal width +### Console wrap and margin -The allowed text width can be enforced by passing the `$width` parameter to the constructor: +By default, the console object enforces a wrap width at 80 characters and provides a margin of 4 spaces for readability. +These values can be changed to whatever is needed for the application. ```php use Pop\Console\Console; -$console = new Console(40); +$console = new Console(40, 2); // wrap width of 40, margin of 2 spaces $console->append( 'Here is some console information. This is a really long string. It will have to wrap.' ); @@ -95,29 +99,9 @@ $console->send(); ``` ```text - Here is some console information. This - is a really long string. It will have to - wrap. -``` - -### Setting an indent - -By default, an indent of four spaces is set to provide a margin from the edge of the terminal. This can be adjusted -or turned off by passing it to the constructor: - -```php -use Pop\Console\Console; - -$console = new Console(40, ' '); -$console->append( - 'Here is some console information using a 2 space indent. It will have to wrap.' -); -$console->send(); -``` - -```text - Here is some console information using a - 2 space indent. It will have to wrap. + Here is some console information. This + is a really long string. It will have to + wrap. ``` [Top](#pop-console) @@ -145,9 +129,9 @@ $console->write( ); ``` -### Newlines and Indents +### Newline and Margin -By default, calling the `append()` or `write()` methods will produce the indent value at the beginning +By default, calling the `append()` or `write()` methods will produce the margin value at the beginning of the content and a newline at the end of the content. If this is not the desired behavior, boolean flags can be passed to control this: @@ -155,14 +139,10 @@ can be passed to control this: use Pop\Console\Console; $console = new Console(40); -$console->write('Here ', false); // No new line, but use indent -$console->write('is ', false, false); // No new line, no indent -$console->write('some ', false, false); // No new line, no indent -$console->write('content.', true, false); // Use new line, but no indent -``` - -```text - Here is some content. +$console->write('Here ', false); // No new line, but use margin +$console->write('is ', false, false); // No new line, no margin +$console->write('some ', false, false); // No new line, no margin +$console->write('content.', true, false); // Use new line, but no margin ``` [Top](#pop-console) @@ -171,12 +151,16 @@ Colors ------ On a console terminal that supports it, you can colorize text outputted to the console -with the ``colorize()`` method: +with the ``Pop\Console\Color::colorize()`` method: ```php -$console->append( +use Pop\Console\Console; +use Pop\Console\Color; + +$console = new Console(); +$console->write( 'Here is some ' . - $console->colorize('IMPORTANT', Console::BOLD_RED) . + Color::colorize('IMPORTANT', Color::BOLD_RED) . ' console information.' ); ``` @@ -192,7 +176,15 @@ Available color constants include: - MAGENTA - CYAN - WHITE -- GRAY +- BRIGHT_BLACK +- BRIGHT_RED +- BRIGHT_GREEN +- BRIGHT_YELLOW +- BRIGHT_BLUE +- BRIGHT_MAGENTA +- BRIGHT_CYAN +- BRIGHT_WHITE +- BOLD_BLACK - BOLD_RED - BOLD_GREEN - BOLD_YELLOW @@ -200,6 +192,103 @@ Available color constants include: - BOLD_MAGENTA - BOLD_CYAN - BOLD_WHITE +- BRIGHT_BOLD_BLACK +- BRIGHT_BOLD_RED +- BRIGHT_BOLD_GREEN +- BRIGHT_BOLD_YELLOW +- BRIGHT_BOLD_BLUE +- BRIGHT_BOLD_MAGENTA +- BRIGHT_BOLD_CYAN +- BRIGHT_BOLD_WHITE + +[Top](#pop-console) + +Lines +----- + +The `line()` method provides a way to print a horizontal line rule out to the terminal. The default +character for the line is a dash `-`, but any character can be passed into the method. + +```php +use Pop\Console\Console; + +$console = new Console(); +$console->line(); +``` + +It will default to the wrap width of the console object. If no wrap width is available, it will take on +the width of the terminal, unless a custom width is specified: + +```php +use Pop\Console\Console; + +$console = new Console(); +$console->line('=', 20); +``` + +```text + ==================== +``` + +[Top](#pop-console) + +Headers +------- + +The `header()` method provides a way to output a separate block of text with an underline emphasis: + +```php +use Pop\Console\Console; + +$console = new Console(80); +$console->header('Hello World'); +``` + +```text + Hello World + ----------- +``` + +The character, size and alignment can be controlled as well: + +```php +use Pop\Console\Console; + +$console = new Console(); +$console->header('Hello World', '=', 40, 'center'); +``` + +```text + Hello World + ======================================== +``` + +[Top](#pop-console) + +Alerts +------ + +Alerts are specially formatted boxes that provide style and enhancement to the user's experience +in regard to important information and notifications. + +```php +use Pop\Console\Console; + +$console = new Console(40); +$console->alertDanger('Hello World', 'auto'); +$console->alertWarning('Hello World', 'auto'); +$console->alertSuccess('Hello World', 'auto'); +$console->alertInfo('Hello World', 'auto'); +$console->alertPrimary('Hello World', 'auto'); +$console->alertSecondary('Hello World', 'auto'); +$console->alertDark('Hello World', 'auto'); +$console->alertLight('Hello World', 'auto'); +$console->alertBox('Hello World', '-', '|', 'auto'); +``` + +The above code will produce the following output to the console terminal: + +![Alerts](tests/tmp/alerts.png) [Top](#pop-console) @@ -245,6 +334,18 @@ $ ./app Your favorite letter is B. ``` +### Confirm + +The `confirm()` is a shorthand version of a prompt to ask if the user is sure they want to proceed, +else the application will exit: + +```php +use Pop\Console\Console; + +$console = new Console(); +$console->confirm(); +$console->write('The user said yes.'); +``` [Top](#pop-console) @@ -316,7 +417,7 @@ Let's take a look at the abstract constructor of the `pop-kettle` component. $this->console = $console; $this->console->setHelpColors( - Console::BOLD_CYAN, Console::BOLD_GREEN, Console::BOLD_MAGENTA + Color::BOLD_CYAN, Color::BOLD_GREEN, Color::BOLD_MAGENTA ); $this->console->addCommandsFromRoutes( $application->router()->getRouteMatch(), './kettle' diff --git a/composer.json b/composer.json index 58847f0..510020e 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ ], "require": { "php": ">=8.1.0", - "popphp/popphp": "^4.0.0" + "popphp/popphp": "^4.1.0" }, "require-dev": { "phpunit/phpunit": "^10.0.0" diff --git a/src/Color.php b/src/Color.php index faf126f..3df2570 100644 --- a/src/Color.php +++ b/src/Color.php @@ -21,7 +21,7 @@ * @author Nick Sagona, III * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) * @license http://www.popphp.org/license New BSD License - * @version 4.0.0 + * @version 4.1.0 */ class Color { diff --git a/src/Command.php b/src/Command.php index f96f8a0..78e62dc 100644 --- a/src/Command.php +++ b/src/Command.php @@ -21,7 +21,7 @@ * @author Nick Sagona, III * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) * @license http://www.popphp.org/license New BSD License - * @version 4.0.0 + * @version 4.1.0 */ class Command { @@ -158,4 +158,4 @@ public function __toString(): string return $this->name . ((null !== $this->params) ? ' ' . $this->params : null); } -} \ No newline at end of file +} diff --git a/src/Console.php b/src/Console.php index d5b01e4..c58d7ff 100644 --- a/src/Console.php +++ b/src/Console.php @@ -24,7 +24,7 @@ * @author Nick Sagona, III * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) * @license http://www.popphp.org/license New BSD License - * @version 4.0.0 + * @version 4.1.0 */ class Console { @@ -104,7 +104,7 @@ class Console /** * Instantiate a new console object * - * @param int $wrap + * @param ?int $wrap * @param int|string $margin */ public function __construct(?int $wrap = 80, int|string $margin = 4) @@ -204,11 +204,15 @@ public function setHeight(int $height): Console * Set the console header * * @param string $header + * @param bool $newline * @return Console */ - public function setHeader(string $header): Console + public function setHeader(string $header, bool $newline = true): Console { $this->header = $header; + if ($newline) { + $this->header .= PHP_EOL; + } return $this; } @@ -216,11 +220,15 @@ public function setHeader(string $header): Console * Set the console footer * * @param string $footer + * @param bool $newline * @return Console */ - public function setFooter(string $footer): Console + public function setFooter(string $footer, bool $newline = true): Console { $this->footer = $footer; + if ($newline) { + $this->footer = PHP_EOL . $this->footer; + } return $this; } @@ -314,6 +322,26 @@ public function getHeight(): int return $this->height; } + /** + * Check is console terminal supports color + * + * @return bool + */ + public function isColor(): bool + { + return (isset($_SERVER['TERM']) && (stripos($_SERVER['TERM'], 'color') !== false)); + } + + /** + * Check is console terminal is in a Windows environment + * + * @return bool + */ + public function isWindows(): bool + { + return (stripos(PHP_OS, 'win') === false); + } + /** * Has wrap * @@ -679,7 +707,7 @@ public function headerRight(string $string, string $char = '-', int|string|null } /** - * Print an alert box out to the console + * Print a colored alert box out to the console * * @param string $message * @param int $fg @@ -740,6 +768,73 @@ public function alert( return $this; } + /** + * Print a colorless alert outline box out to the console + * + * @param string $message + * @param string $h + * @param ?string $v + * @param int|string|null $size + * @param string $align + * @param int $innerPad + * @param bool $newline + * @return Console + */ + public function alertBox( + string $message, string $h = '-', ?string $v = '|', int|string|null $size = null, + string $align = 'center', int $innerPad = 4, bool $newline = true + ): Console + { + if ($size === null) { + if (!empty($this->wrap) && (strlen($message) > $this->wrap)) { + $size = $this->wrap; + } else if (!empty($this->width) && (strlen($message) > $this->width)) { + $size = $this->width - ($this->margin * 2); + } else { + $size = strlen($message) + ($innerPad * 2); + } + } else if ($size == 'auto') { + if (!empty($this->wrap)) { + $size = $this->wrap; + } else if (!empty($this->width)) { + $size = $this->width - ($this->margin * 2); + } + } + + $innerSize = $size - ($innerPad * 2); + $messageLines = []; + $lines = (strlen($message) > $innerSize) ? + explode(PHP_EOL, wordwrap($message, $innerSize, PHP_EOL)) : [$message]; + + foreach ($lines as $line) { + $pad = $this->calculatePad($line, $size, $align); + if ($align == 'center') { + $messageLines[] = str_repeat(' ', $pad) . $line . str_repeat(' ', ($size - strlen($line) - $pad)); + } else if ($align == 'left') { + $messageLines[] = str_repeat(' ', $innerPad) . $line . str_repeat(' ', ($size - strlen($line) - $pad - $innerPad)); + } else if ($align == 'right') { + $messageLines[] = str_repeat(' ', ($size - strlen($line) - $innerPad)) . $line . str_repeat(' ', $innerPad); + } + } + + echo PHP_EOL; + echo $this->getIndent() . str_repeat($h, $size) . PHP_EOL; + echo $this->getIndent() . $v . str_repeat(' ', $size - 2) . $v . PHP_EOL; + foreach ($messageLines as $messageLine) { + if (!empty($v) && str_starts_with($messageLine, ' ') && str_ends_with($messageLine, ' ')) { + $messageLine = $v . substr($messageLine, 1, -1) . $v; + } + echo $this->getIndent() . $messageLine . PHP_EOL; + } + echo $this->getIndent() . $v . str_repeat(' ', $size - 2) . $v . PHP_EOL; + echo $this->getIndent() . str_repeat($h, $size) . PHP_EOL; + if ($newline) { + echo PHP_EOL; + } + + return $this; + } + /** * Print a "danger" alert box out to the console * diff --git a/src/Exception.php b/src/Exception.php index 4b534ad..6619a85 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -21,6 +21,6 @@ * @author Nick Sagona, III * @copyright Copyright (c) 2009-2024 NOLA Interactive, LLC. (http://www.nolainteractive.com) * @license http://www.popphp.org/license New BSD License - * @version 4.0.0 + * @version 4.1.0 */ class Exception extends \Exception {} diff --git a/tests/ConsoleTest.php b/tests/ConsoleTest.php index 7c58039..678e6b7 100644 --- a/tests/ConsoleTest.php +++ b/tests/ConsoleTest.php @@ -27,6 +27,8 @@ public function testConstructor() $this->assertTrue($console->hasWrap()); $this->assertGreaterThan(0, $console->getWidth()); $this->assertGreaterThan(0, $console->getHeight()); + $this->assertIsBool($console->isColor()); + $this->assertIsBool($console->isWindows()); } public function testSetAndGetHeader() @@ -36,7 +38,7 @@ public function testSetAndGetHeader() $console->setWidth(160)->setHeight(50); } $console->setHeader('header'); - $this->assertEquals('header', $console->getHeader()); + $this->assertEquals('header' . PHP_EOL, $console->getHeader()); } public function testSetAndGetFooter() @@ -46,7 +48,7 @@ public function testSetAndGetFooter() $console->setWidth(160)->setHeight(50); } $console->setFooter('footer'); - $this->assertEquals('footer', $console->getFooter()); + $this->assertEquals(PHP_EOL . 'footer', $console->getFooter()); } public function testSetAndGetHeaderSent() @@ -592,6 +594,96 @@ public function testAlertLight() $this->assertTrue(str_contains($result, "\x1b[1;30m\x1b[47m Hello World \x1b[0m")); } + public function testAlertBox1() + { + $console = new Console(20); + if (!$console->hasWidth()) { + $console->setWidth(160)->setHeight(50); + } + + ob_start(); + $console->alertBox('Hello World'); + $result = ob_get_clean(); + + $this->assertTrue(str_contains($result, " -------------------")); + $this->assertTrue(str_contains($result, " | Hello World |")); + } + + public function testAlertBox2() + { + $console = new Console(20); + if (!$console->hasWidth()) { + $console->setWidth(160)->setHeight(50); + } + + ob_start(); + $console->alertBox('Hello World. This is a longer alert. This is a longer alert.'); + $result = ob_get_clean(); + + $this->assertTrue(str_contains($result, " -------------------")); + $this->assertTrue(str_contains($result, " | Hello World")); + } + + public function testAlertBox3() + { + $console = new Console(null); + if (!$console->hasWidth()) { + $console->setWidth(160)->setHeight(50); + } + + ob_start(); + $console->alertBox('Hello World. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert. This is a longer alert.'); + $result = ob_get_clean(); + + $this->assertTrue(str_contains($result, " -------------------")); + $this->assertTrue(str_contains($result, "Hello World")); + } + + public function testAlertBox4() + { + $console = new Console(80); + if (!$console->hasWidth()) { + $console->setWidth(160)->setHeight(50); + } + + ob_start(); + $console->alertBox('Hello World. This is a longer alert.', '-', '|', 'auto', 'center'); + $result = ob_get_clean(); + + $this->assertTrue(str_contains($result, "-------------------")); + $this->assertTrue(str_contains($result, "Hello World")); + } + + public function testAlertBox5() + { + $console = new Console(null); + if (!$console->hasWidth()) { + $console->setWidth(160)->setHeight(50); + } + + ob_start(); + $console->alertBox('Hello World. This is a longer alert.', '-', '|', 'auto', 'right'); + $result = ob_get_clean(); + + $this->assertTrue(str_contains($result, "-------------------")); + $this->assertTrue(str_contains($result, "Hello World")); + } + + public function testAlertBox6() + { + $console = new Console(null); + if (!$console->hasWidth()) { + $console->setWidth(160)->setHeight(50); + } + + ob_start(); + $console->alertBox('Hello World. This is a longer alert.', '-', '|', 'auto', 'left'); + $result = ob_get_clean(); + + $this->assertTrue(str_contains($result, "-------------------")); + $this->assertTrue(str_contains($result, "Hello World")); + } + public function testAppend() { $console = new Console(); diff --git a/tests/tmp/alerts.png b/tests/tmp/alerts.png new file mode 100644 index 0000000..07d1949 Binary files /dev/null and b/tests/tmp/alerts.png differ