From 4debc7dc50b3e172c63ccf839a7851d5d47f0553 Mon Sep 17 00:00:00 2001 From: Rayan Levert Date: Sat, 17 Aug 2024 07:14:04 +0200 Subject: [PATCH 1/6] Arguments\Option : added Enum cases for all defined options for an Argument #16 --- src/Arguments/Argument.php | 63 ++++++++------------------------ src/Arguments/Option.php | 57 +++++++++++++++++++++++++++++ tests/Arguments/ArgumentTest.php | 13 ++----- 3 files changed, 76 insertions(+), 57 deletions(-) create mode 100644 src/Arguments/Option.php diff --git a/src/Arguments/Argument.php b/src/Arguments/Argument.php index 4a43d7f..cf667ca 100755 --- a/src/Arguments/Argument.php +++ b/src/Arguments/Argument.php @@ -2,11 +2,7 @@ namespace RayanLevert\Cli\Arguments; -use function array_key_exists; -use function is_string; use function is_bool; -use function is_double; -use function is_int; use function gettype; use function is_numeric; use function implode; @@ -35,64 +31,35 @@ class Argument private bool $hasBeenHandled = false; /** - * Creates an argument with a name and differents options + * Creates an argument with a name and different options * - * @param array $options - * - description (string) Description of the argument - * - defaultValue (float|int|string) Default value if the argument is not handled - * - required (bool) If the argument must be present and parsed - * - castTo (string) PHP type - If the argument has a type other than string, its value will be casted - * - noValue (bool) If a prefixed argument doesn't need a value -> boolean cast - * - prefix (string) Short prefix (-u) - * - longPrefix (string) Long prefix (--user) + * @param array $options See Arguments\Option cases for more informations * - * @throws \RayanLevert\Cli\Arguments\Exception If options are incompatible or incorrectes + * @throws \RayanLevert\Cli\Arguments\Exception If options are incompatible or incorrect */ final public function __construct(protected readonly string $name, array $options = []) { - if (array_key_exists('description', $options) && is_string($options['description'])) { - $this->description = $options['description']; - } - - if (array_key_exists('required', $options) && is_bool($options['required'])) { - $this->isRequired = $options['required']; - } - - if (array_key_exists('noValue', $options) && is_bool($options['noValue'])) { - $this->noValue = $options['noValue']; - } - - if (array_key_exists('prefix', $options) && is_string($options['prefix'])) { - $this->prefix = $options['prefix']; - } + foreach ($options as $name => $value) { + if (!($option = Option::tryFrom($name)) || !$option->verifiesType($value)) { + continue; + } - if (array_key_exists('longPrefix', $options) && is_string($options['longPrefix'])) { - $this->longPrefix = $options['longPrefix']; + $this->{$option->getPhpProperty()} = $value; } - if (array_key_exists('castTo', $options) && is_string($options['castTo'])) { - $this->castTo = match ($options['castTo']) { + if ($this->castTo) { + $this->castTo = match ($this->castTo) { 'int', 'integer' => 'integer', 'bool', 'boolean' => throw new Exception('castTo cannot be of type bool, use the option "noValue"'), 'double', 'float' => 'double', 'string' => 'string', - default => throw new Exception($options['castTo'] . ' is not a native PHP type') + default => throw new Exception($this->castTo . ' is not a native PHP type') }; } - if (array_key_exists('defaultValue', $options)) { - $defaultValue = $options['defaultValue']; - - if (!is_string($defaultValue) && !is_double($defaultValue) && !is_int($defaultValue)) { - throw new Exception('Default value must be of type float, integer or string'); - } - - $this->defaultValue = $defaultValue; - - // Asserts the default value type is the same as the castTo option - if (gettype($this->defaultValue) !== $this->castTo) { - throw new Exception("Default value is not the same type as castTo option ({$this->castTo})"); - } + // Asserts the default value type is the same as the castTo option + if ($this->defaultValue && gettype($this->defaultValue) !== $this->castTo) { + throw new Exception("Default value is not the same type as castTo option ({$this->castTo})"); } if (($this->noValue || $this->isRequired) && $this->defaultValue) { @@ -137,7 +104,7 @@ public function setValueParsed(bool|string $value): void return; } - // Thorws an exception if the value is not of casted type + // Throws an exception if the value is not of casted type if ($this->castTo === 'integer') { if (!is_numeric($value)) { throw new ParseException("Argument {$this->name} is not a numeric string (must cast to integer)"); diff --git a/src/Arguments/Option.php b/src/Arguments/Option.php new file mode 100644 index 0000000..5602af4 --- /dev/null +++ b/src/Arguments/Option.php @@ -0,0 +1,57 @@ + verifies only its presence by a boolean status (ex: --help) */ + case NO_VALUE = 'noValue'; + + /** (string) Short prefix (-u=) */ + case PREFIX = 'prefix'; + + /** (string) Long prefix (--user=) */ + case LONG_PREFIX = 'longPrefix'; + + /** (string) PHP type - If the argument has a type other than string -> its value will be casted */ + case CAST_TO = 'castTo'; + + /** (string|int|float) Default value if the argument is not handled */ + case DEFAULT_VALUE = 'defaultValue'; + + /** From a value, verifies the type the option must require */ + public function verifiesType(mixed $value): bool + { + return match ($this) { + self::DESCRIPTION => is_string($value), + self::REQUIRED => is_bool($value), + self::NO_VALUE => is_bool($value), + self::PREFIX => is_string($value), + self::LONG_PREFIX => is_string($value), + self::CAST_TO => is_string($value), + self::DEFAULT_VALUE => is_string($value) || is_int($value) || is_float($value) + }; + } + + /** Returns the php property for the Arguments\Argument class */ + public function getPhpProperty(): string + { + return match ($this) { + self::REQUIRED => 'isRequired', + default => $this->value + }; + } +} diff --git a/tests/Arguments/ArgumentTest.php b/tests/Arguments/ArgumentTest.php index 0b01110..8eaecb2 100755 --- a/tests/Arguments/ArgumentTest.php +++ b/tests/Arguments/ArgumentTest.php @@ -157,16 +157,11 @@ public function testDefaultValue(): void $oArgument = new Argument('test'); $this->assertNull($oArgument->getDefaultValue()); - // we recover every incorrect PHP type = throws an exception + // we recover every incorrect PHP type = does not assign the value foreach ([true, false, [], new \stdClass(), fopen(__FILE__, 'r')] as $incorrectValue) { - try { - $oArgument = new Argument('test', ['defaultValue' => $incorrectValue]); - - $this->fail('expected exception pour la valeur ' . var_export($incorrectValue, true)); - } catch (\Exception $e) { - $this->assertSame('Default value must be of type float, integer or string', $e->getMessage()); - $this->assertInstanceOf(Exception::class, $e); - } + $oArgument = new Argument('test', ['defaultValue' => $incorrectValue]); + + $this->assertNull($oArgument->getDefaultValue()); } // castTo string and defaultValue string OK From d2f757a1a8fedff9a6f201015cd1c8b0df30214d Mon Sep 17 00:00:00 2001 From: Rayan Levert Date: Sat, 17 Aug 2024 07:21:00 +0200 Subject: [PATCH 2/6] Docker : only php8.3 version in the repository + using php-cli and last version of composer --- README.md | 202 ------------------------------------------ docker-compose.yml | 18 +--- docker/8.1/Dockerfile | 24 ----- docker/8.2/Dockerfile | 24 ----- docker/8.3/Dockerfile | 4 +- 5 files changed, 3 insertions(+), 269 deletions(-) delete mode 100644 README.md delete mode 100644 docker/8.1/Dockerfile delete mode 100644 docker/8.2/Dockerfile diff --git a/README.md b/README.md deleted file mode 100644 index caa4a2f..0000000 --- a/README.md +++ /dev/null @@ -1,202 +0,0 @@ -## Dependency-free command line interface (CLI) handling arguments and easily personalizing output in the PHP userland - -[![Packagist Version](https://img.shields.io/packagist/v/rayanlevert/command-line-interface)](https://packagist.org/packages/rayanlevert/command-line-interface) -[![PHP from Packagist](https://img.shields.io/packagist/php-v/rayanlevert/command-line-interface)](https://packagist.org/packages/rayanlevert/command-line-interface) -[![codecov](https://codecov.io/gh/rayanlevert/command-line-interface/branch/main/graph/badge.svg)](https://codecov.io/gh/rayanlevert/command-line-interface) - -### **RayanLevert\Cli\Arguments\Argument** defines what is an Argument -An argument has a name and different options and can only be of type `integer`, `double` ou `string` (if the option `noValue` is used, it will be `bool`) - -```php -new \RayanLevert\Cli\Arguments\Argument(string $name, array $options = []) -``` - -``` -- description (string) Description of the argument -- defaultValue (float|int|string) Default value if the argument is not handled -- required (bool) If the argument must be present and parsed -- castTo (string) PHP type - If the argument has a type other than string, its value will be casted -- noValue (bool) If a prefixed argument doesn't need a value -> boolean cast -- prefix (string) Short prefix (-u) -- longPrefix (string) Long prefix (--user) -``` - -A `RayanLevert\Cli\Arguments\Exception` can be thrown if options are not compliant (see `__construct()`) - -### **RayanLevert\Cli\Arguments** is a collection of Argument capable of parsing values from `argv` (array of strings) - -```php -new \RayanLevert\Cli\Arguments(\RayanLevert\Cli\Arguments\Argument ...$oArguments) -``` - -> Required arguments must be declared first, before not required ones - -#### Recovery of parsed values is done via the method `parse(string ...$arguments): void` -> To parse arguments from the actual CLI, use `parse(...$argv)` (with declaring `global $argv;` if you are not in the global scope) - -Associates each parsed value to its Argument in the collection - -The parsed value of an argument is recoverable by `::get(string $argumentName)` - -By default, `NULL` is returned; `integer`, `float` or `string` can be returned if the argument has been parsed and option `castTo` has been set - -- If castTo is `integer` or `float`, the value must be a numeric string (asserts with `is_numeric()`) -- Si castTo is `string` (by default), the value will be the one parsed - -### Implementation -```php -$oArguments = new Arguments(new Argument('arg1')); -$oArguments->get('arg1') // NULL - -$oArguments = new Arguments(new Argument('arg1', ['defaultValue' => 'test'])); -$oArguments->get('arg1') // test - -$oArguments = new Arguments(new Argument('arg1', ['castTo' => 'float', 'defaultValue' => 14.3])); -$oArguments->get('arg1') // 14.3 - -$oArguments = new Arguments(new Argument('arg1', ['required' => true])); -$oArguments->parse(); // ParseException arg1 is required -$oArguments->parse('test'); // OK $oArguments->get('arg1') = test - -// Parsing optional arguments -$oArguments = new Arguments(new Argument('arg1'), new Argument('arg2')); -$oArguments->parse('test1'); // $oArguments->get('arg1') = test1, $oArguments->get('arg1') = NULL -$oArguments->parse('test1', 'test2'); // $oArguments->get('arg1') = test1, $oArguments->get('arg1') = test2 - -// Parsing prefixed arguments -$oArguments = new Arguments(new Argument('arg1', ['prefix' => 'a', 'longPrefix' => 'arg'])); -$oArguments->parse('-a=testValue'); // $oArguments->get('arg1') = testValue -$oArguments->parse('-a="test Value"'); // $oArguments->get('arg1') = test Value -$oArguments->parse('--arg=testValue'); // $oArguments->get('arg1') = testValue -$oArguments->parse('--arg="test Value"'); // $oArguments->get('arg1') = test Value -``` - -### Multiple methods are available - -- Adds an argument - `set(Argument $oArgument): void` -- Removes one - `remove(string $argumentName): void` -- Returns the number of arguments `count(): int` -- Prints a clean display about the informations of arguments `printArguments(): void` - ``` - Required arguments: - arg1 (type: string) - arg2 (type: integer) - - Optional arguments: - arg3 --arg3=arg3 (type: integer) - arg4 -arg4 - ``` - -## Personalizes the command line interface by changing the color and formatting displayed text - -### **RayanLevert\Cli\Style** is a class that only has static methods - -3 enumerations are available to stylize the output: - -- `RayanLevert\Cli\Style\Background`: Background colors -- `RayanLevert\Cli\Style\Foreground`: Text colors -- `RayanLevert\Cli\Style\Attributes`: Text attributes - -2 main methods are used to display formatted text - -```php -/** - * Prints a string of a background color, text color and/or an attribute -*/ -public static function inline(string $string, Background $bg = null, Foreground $fg = null, Attribute $at = null): void; - -/** - * Prints a string and breaks a line of a background color, text color and/or an attribute -*/ -public static function outline(string $string, Background $bg = null, Foreground $fg = null, Attribute $at = null): void; -``` - -And one to return the formatted string instead of printing it -```php -public static function stylize(string $string, Background $bg = null, Foreground $fg = null, Attribute $at = null): string; -``` - -Other useful methods are available: - -```php -/** - * Prints a formatted string thanks to its tags of ANSI codes (Foreground, Background and Attribute) - * - * Useful is you want to use multiple styles in one single method call - * - * Tags to use are in the three enumerations thanks to the 'tryFromTag' method -*/ -public static function tag(string $tag): void - -==================================== -。◕‿◕。 This is a title 。◕‿◕。 -==================================== -public static function title(string $title): void; - ---- Flanked message ---\n -public static function flank(string $message, string $char = '-', int $length = 3): void; - - (◍•﹏•) Warning message\n -public static function warning(string $message): void; // colored text in yellow - - (◍•﹏•) Error message\n -public static function error(string $message): void; // colored text in red - -public static function red(string $message): void; // displays a red colored text and breaks a line -public static function green(string $message): void; // displays a green colored text and breaks a line -public static function yellow(string $message): void; // displays a yellow colored text and breaks a line - -// Displays according to a boolean status, a red or green text colored message and breaks a line -public static function outlineWithBool(bool $status, string $ifTrue, string $ifFalse, string $toPrecede = ''): void; - -// Prints the details of an exception in red + its trace in white -public static function exception(\Exception $e, bool $withoutTrace = false): void; -``` - -### **RayanLevert\Cli\ProgressBar displays progression output through a progress bar** - -```php -/** - * @param int $max Maximum value of iterations - * @param int $numberOfSymbols Number of symbols added after each iteration -*/ -$oProgressBar = new ProgressBar(int $max, int $numberOfSymbols = 50); - -/** - * @param string $title Title to add above the progress bar - * @param Style\Foreground $fg Text color -*/ -$oProgressBar->setTitle(string $title = '', Style\Foreground $fg = Style\Foreground::BLUE); - -/** - * Starts the progress bar (or restarts it, if not breaks two lines) -*/ -$oProgressBar->start(); - -/** - * Advances the progress bar of `$toAdvance` iterations updating the progression -*/ -$oProgressBar->advance(int $toAdvance = 1); - -// Finishes the progress bar (advances to the max value) -$oProgressBar->finish(); -``` - -### Simple implementation - -```php -// 10 is the max value -> a new symbol every new iteration -$oProgressBar = new ProgressBar(10); -$oProgressBar->start('My progress bar'); - -// Advances to 1 each iteration -foreach (range(1, 10) as $range) { - $oProgressBar->advance(); -} - - My progress bar - 1 / 10 [# ] - 2 / 10 [## ] - ... - 10 / 10 [##########] -``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 3fd85dc..e9ffe0e 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,21 +1,5 @@ -version: '2' - services: - cli-8.1: - build: docker/8.1 - restart: 'no' - working_dir: /app - volumes: - - .:/app - - cli-8.2: - build: docker/8.2 - restart: 'no' - working_dir: /app - volumes: - - .:/app - - cli-8.3: + cli: build: docker/8.3 restart: 'no' working_dir: /app diff --git a/docker/8.1/Dockerfile b/docker/8.1/Dockerfile deleted file mode 100644 index 424b37f..0000000 --- a/docker/8.1/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM composer:2.6.5 AS composer -FROM php:8.1-fpm - -LABEL maintainer="Rayan Levert " - -# Installing packages needed -RUN apt-get update -y && \ - apt-get install -y \ - git \ - zip - -# Enabling xdebug -RUN pecl install xdebug && docker-php-ext-enable xdebug - -# Creates the app directory -RUN mkdir /app - -# Volumes -VOLUME ["/app"] - -# Composer -COPY --from=composer /usr/bin/composer /usr/local/bin/composer - -CMD ["php-fpm"] \ No newline at end of file diff --git a/docker/8.2/Dockerfile b/docker/8.2/Dockerfile deleted file mode 100644 index a45b0c4..0000000 --- a/docker/8.2/Dockerfile +++ /dev/null @@ -1,24 +0,0 @@ -FROM composer:2.6.5 AS composer -FROM php:8.2-fpm - -LABEL maintainer="Rayan Levert " - -# Installing packages needed -RUN apt-get update -y && \ - apt-get install -y \ - git \ - zip - -# Enabling xdebug -RUN pecl install xdebug && docker-php-ext-enable xdebug - -# Creates the app directory -RUN mkdir /app - -# Volumes -VOLUME ["/app"] - -# Composer -COPY --from=composer /usr/bin/composer /usr/local/bin/composer - -CMD ["php-fpm"] \ No newline at end of file diff --git a/docker/8.3/Dockerfile b/docker/8.3/Dockerfile index 94ec0f8..6f4c8cf 100644 --- a/docker/8.3/Dockerfile +++ b/docker/8.3/Dockerfile @@ -1,5 +1,5 @@ -FROM composer:2.6.5 AS composer -FROM php:8.3-fpm +FROM composer:2.7.7 AS composer +FROM php:8.3-cli LABEL maintainer="Rayan Levert " From bcb556094c2d7496a68c6278456dbf8355c7135a Mon Sep 17 00:00:00 2001 From: Rayan Levert Date: Sat, 17 Aug 2024 07:22:34 +0200 Subject: [PATCH 3/6] Arguments\Argument is not a final class anymore --- src/Arguments/Argument.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Arguments/Argument.php b/src/Arguments/Argument.php index cf667ca..e93c8df 100755 --- a/src/Arguments/Argument.php +++ b/src/Arguments/Argument.php @@ -37,7 +37,7 @@ class Argument * * @throws \RayanLevert\Cli\Arguments\Exception If options are incompatible or incorrect */ - final public function __construct(protected readonly string $name, array $options = []) + public function __construct(protected readonly string $name, array $options = []) { foreach ($options as $name => $value) { if (!($option = Option::tryFrom($name)) || !$option->verifiesType($value)) { From 9f94a31c79dfaa01273a3ffab0f38e6126acf087 Mon Sep 17 00:00:00 2001 From: Rayan Levert Date: Sat, 17 Aug 2024 07:26:32 +0200 Subject: [PATCH 4/6] Arguments : getIterator yields instead of an useless ArrayIterator --- src/Arguments.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Arguments.php b/src/Arguments.php index 3c0de32..18f9b74 100755 --- a/src/Arguments.php +++ b/src/Arguments.php @@ -23,7 +23,7 @@ * * @implements \IteratorAggregate */ -class Arguments implements \IteratorAggregate +class Arguments implements \IteratorAggregate, \Countable { /** * @var array @@ -43,11 +43,11 @@ public function __construct(Argument ...$oArguments) } /** - * @return \Traversable + * @return \Generator */ - public function getIterator(): \Traversable + public function getIterator(): \Generator { - return new \ArrayIterator($this->data); + yield from $this->data; } /** @@ -121,7 +121,7 @@ public function parse(string ...$arguments): void } // Loops to recover required arguments and set their values - foreach ($this->data as $name => $oArgument) { + foreach ($this as $name => $oArgument) { if (!$oArgument->isRequired()) { continue; } @@ -171,7 +171,7 @@ public function getRequired(): self $oSelf = new self(); - foreach ($this->data as $oArgument) { + foreach ($this as $oArgument) { if ($oArgument->isRequired()) { $oSelf->set($oArgument); } @@ -215,7 +215,7 @@ public function printArguments(): void */ protected function getByShortPrefix(string $name): ?Argument { - foreach ($this->data as $oArgument) { + foreach ($this as $oArgument) { if ($oArgument->getPrefix() === $name) { return $oArgument; } @@ -229,7 +229,7 @@ protected function getByShortPrefix(string $name): ?Argument */ protected function getByLongPrefix(string $name): ?Argument { - foreach ($this->data as $oArgument) { + foreach ($this as $oArgument) { if ($oArgument->getLongPrefix() === $name) { return $oArgument; } @@ -245,7 +245,7 @@ protected function getNotHandled(): self { $oSelf = new self(); - foreach ($this->data as $oArgument) { + foreach ($this as $oArgument) { if (!$oArgument->hasBeenHandled()) { $oSelf->set($oArgument); } @@ -269,7 +269,7 @@ protected function assertOrderRequired(): void { $argHasNotRequired = false; - foreach ($this->data as $name => $oArgument) { + foreach ($this as $name => $oArgument) { if ($oArgument->getPrefix() || $oArgument->getLongPrefix()) { continue; } From 46138bdd7ec7bf4d3976b8605d5bd9daedd0f243 Mon Sep 17 00:00:00 2001 From: Rayan Levert Date: Sat, 17 Aug 2024 07:45:36 +0200 Subject: [PATCH 5/6] Adding support for php8.4 (Implicitly nullable parameter declarations deprecated) --- .github/workflows/{ci.yml => ci.yaml} | 2 +- src/Arguments/Option.php | 2 +- src/ProgressBar.php | 4 ++-- src/Style.php | 20 +++++++++++--------- 4 files changed, 15 insertions(+), 13 deletions(-) rename .github/workflows/{ci.yml => ci.yaml} (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yaml similarity index 99% rename from .github/workflows/ci.yml rename to .github/workflows/ci.yaml index 6fbf700..719716e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yaml @@ -1,4 +1,4 @@ -# Inspired by Sebastian Bergmann's phpunit action workflow https://github.com/sebastianbergmann/phpunit/blob/main/.github/workflows/ci.yml +# Inspired by Sebastian Bergmann's phpunit action workflow https://github.com/sebastianbergmann/phpunit/blob/main/.github/workflows/ci.yaml name: "CI" diff --git a/src/Arguments/Option.php b/src/Arguments/Option.php index 5602af4..332c514 100644 --- a/src/Arguments/Option.php +++ b/src/Arguments/Option.php @@ -46,7 +46,7 @@ public function verifiesType(mixed $value): bool }; } - /** Returns the php property for the Arguments\Argument class */ + /** Returns the PHP property for the Arguments\Argument class */ public function getPhpProperty(): string { return match ($this) { diff --git a/src/ProgressBar.php b/src/ProgressBar.php index 8848a46..b664235 100644 --- a/src/ProgressBar.php +++ b/src/ProgressBar.php @@ -259,7 +259,7 @@ public function getCurrent(): int private function printTime(): void { // Time color by its total time - $time = Style::stylize($this->getFormattedTime($this->totalTime), fg: match (true) { + $time = Style::stylize(self::getFormattedTime($this->totalTime), fg: match (true) { $this->totalTime <= 500 => Foreground::GREEN, $this->totalTime <= 2000 => Foreground::YELLOW, default => Foreground::RED @@ -267,7 +267,7 @@ private function printTime(): void // Allocated memory by its usage $memoryUsage = memory_get_usage(true); - $memory = Style::stylize($this->getFormattedMemory($memoryUsage), fg: match (true) { + $memory = Style::stylize(self::getFormattedMemory($memoryUsage), fg: match (true) { $memoryUsage <= 268435456 => Foreground::LIGHT_GREEN, // 256MB $memoryUsage <= 536870912 => Foreground::YELLOW, // 512MB default => Foreground::RED diff --git a/src/Style.php b/src/Style.php index 91ac0d6..a844bbf 100755 --- a/src/Style.php +++ b/src/Style.php @@ -7,6 +7,8 @@ use function sprintf; use function get_class; use function preg_match_all; +use function substr; +use function trigger_error; use function get_called_class; use function str_replace; @@ -24,9 +26,9 @@ class Style */ public static function inline( string $string, - Style\Background $bg = null, - Style\Foreground $fg = null, - Style\Attribute $at = null + ?Style\Background $bg = null, + ?Style\Foreground $fg = null, + ?Style\Attribute $at = null ): void { print self::stylize($string, $bg, $fg, $at); } @@ -36,9 +38,9 @@ public static function inline( */ public static function outline( string $string, - Style\Background $bg = null, - Style\Foreground $fg = null, - Style\Attribute $at = null + ?Style\Background $bg = null, + ?Style\Foreground $fg = null, + ?Style\Attribute $at = null ): void { print self::stylize($string, $bg, $fg, $at) . PHP_EOL; } @@ -202,9 +204,9 @@ public static function tag(string $tag): void */ public static function stylize( string $string, - Style\Background $bg = null, - Style\Foreground $fg = null, - Style\Attribute $at = null + ?Style\Background $bg = null, + ?Style\Foreground $fg = null, + ?Style\Attribute $at = null ): string { if (!$bg && !$fg && !$at) { return $string; From b15cc2701236eb8449d0488ce17dcc2691a0d57b Mon Sep 17 00:00:00 2001 From: Rayan Levert Date: Sat, 17 Aug 2024 07:46:56 +0200 Subject: [PATCH 6/6] ci.yaml : adding php8.4 --- .github/workflows/ci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 719716e..56163e6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -97,6 +97,7 @@ jobs: - "8.1" - "8.2" - "8.3" + - "8.4" steps: - name: Checkout