diff --git a/src/CliCommand.php b/src/CliCommand.php index e42fc0d..9579c2d 100644 --- a/src/CliCommand.php +++ b/src/CliCommand.php @@ -66,12 +66,12 @@ public function progressBar( } $nestedLevel++; - $progressBar->setNextedLevel($nestedLevel); + $progressBar->setNestedLevel($nestedLevel); $progressBar->execute(); $nestedLevel--; - $progressBar->setNextedLevel($nestedLevel); + $progressBar->setNestedLevel($nestedLevel); return $progressBar; } diff --git a/src/CliHelper.php b/src/CliHelper.php index 7f3ce86..0cc7493 100644 --- a/src/CliHelper.php +++ b/src/CliHelper.php @@ -16,6 +16,7 @@ namespace JBZoo\Cli; +use JBZoo\Cli\OutputMods\AbstractOutputMode; use JBZoo\Utils\Env; use JBZoo\Utils\Str; @@ -24,6 +25,23 @@ class CliHelper { + private static ?AbstractOutputMode $outputMode = null; + + public static function arrayMergeRecursiveDistinct(array &$array1, array &$array2): array + { + $merged = $array1; + + foreach ($array2 as $key => &$value) { + if (\is_array($value) && isset($merged[$key]) && \is_array($merged[$key])) { + $merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value); + } else { + $merged[$key] = $value; + } + } + + return $merged; + } + public static function getRootPath(): string { $rootPath = \defined('JBZOO_PATH_ROOT') ? (string)JBZOO_PATH_ROOT : null; @@ -127,4 +145,20 @@ public static function renderExpectedValues(array $values): string return \rtrim($result); } + + public static function setInstance(AbstractOutputMode $outputMode): void + { + self::$outputMode = $outputMode; + } + + public static function getInstance(): AbstractOutputMode + { + $result = self::$outputMode; + + if ($result === null) { + throw new Exception('Output mode is not defined'); + } + + return $result; + } } diff --git a/src/OutputMods/AbstractOutputMode.php b/src/OutputMods/AbstractOutputMode.php index 445e541..6083e15 100644 --- a/src/OutputMods/AbstractOutputMode.php +++ b/src/OutputMods/AbstractOutputMode.php @@ -17,6 +17,7 @@ namespace JBZoo\Cli\OutputMods; use JBZoo\Cli\CliApplication; +use JBZoo\Cli\CliHelper; use JBZoo\Cli\OutLvl; use JBZoo\Cli\ProgressBars\AbstractProgressBar; use Monolog\Formatter\NormalizerFormatter; @@ -29,8 +30,6 @@ abstract class AbstractOutputMode { - protected static self $instance; - protected CliApplication $application; protected float $startTimer; @@ -41,8 +40,11 @@ abstract class AbstractOutputMode protected InputInterface $input; protected OutputInterface $output; - protected bool $catchMode = false; - protected array $caughtMessages = []; + + protected bool $catchMode = false; + protected array $caughtMessages = []; + + protected array $extraContext = []; private bool $outputHasErrors = false; @@ -69,7 +71,7 @@ public function __construct(InputInterface $input, OutputInterface $output, CliA $this->input = $input; $this->output = $output; - self::$instance = $this; + CliHelper::setInstance($this); } public function getStartTime(): float @@ -206,12 +208,27 @@ public function catchModeFinish(): array return $caughtMessages; } + public function appendExtraContext(array $context): void + { + $this->extraContext = CliHelper::arrayMergeRecursiveDistinct($this->extraContext, $context); + } + + public function getExtraContext(): array + { + return $this->extraContext; + } + + public function setExtraContext(array $context): void + { + $this->extraContext = $context; + } + /** * @deprecated */ public static function getInstance(): self { - return self::$instance; + return CliHelper::getInstance(); } /** @@ -271,7 +288,9 @@ protected function prepareMessages(iterable|float|int|bool|string|null $messages protected function prepareContext(array $context): array { - return (array)(new NormalizerFormatter())->normalizeValue($context); + $resultContext = \array_merge($this->getExtraContext(), $context); + + return (array)(new NormalizerFormatter())->normalizeValue($resultContext); } protected function markOutputHasErrors(bool $hasError = true): void diff --git a/src/OutputMods/Logstash.php b/src/OutputMods/Logstash.php index 174c5f6..d08eaeb 100644 --- a/src/OutputMods/Logstash.php +++ b/src/OutputMods/Logstash.php @@ -62,10 +62,8 @@ public function onExecBefore(): void 'process' => [ 'pid' => \getmypid(), 'executable' => $_SERVER['PHP_SELF'] ?? null, - 'args_count' => $_SERVER['argv'] ?? null, 'command_line' => $this->input->__toString(), 'process_command' => $this->input->getFirstArgument(), - 'args' => $this->input->getArguments() + $this->input->getOptions(), 'working_directory' => \getcwd(), ], ]); diff --git a/src/ProgressBars/AbstractProgressBar.php b/src/ProgressBars/AbstractProgressBar.php index e53babf..43ce9c5 100644 --- a/src/ProgressBars/AbstractProgressBar.php +++ b/src/ProgressBars/AbstractProgressBar.php @@ -97,14 +97,14 @@ public function onFinish(\Closure $callback): self return $this; } - public function setNextedLevel(int $nestedLevel): self + public function setNestedLevel(int $nestedLevel): self { $this->nestedLevel = $nestedLevel; return $this; } - public function getNextedLevel(): int + public function getNestedLevel(): int { return $this->nestedLevel; } diff --git a/src/ProgressBars/ProgressBarLight.php b/src/ProgressBars/ProgressBarLight.php index 320ea98..666a0a8 100644 --- a/src/ProgressBars/ProgressBarLight.php +++ b/src/ProgressBars/ProgressBarLight.php @@ -58,7 +58,7 @@ public function execute(): bool private function init(): bool { - $progresBarLevel = $this->getNextedLevel(); + $progresBarLevel = $this->getNestedLevel(); $levelPostfix = $progresBarLevel > 1 ? " Level: {$progresBarLevel}." : ''; if ($this->max <= 0) { diff --git a/src/ProgressBars/ProgressBarSymfony.php b/src/ProgressBars/ProgressBarSymfony.php index 6a5fd55..0286ad7 100644 --- a/src/ProgressBars/ProgressBarSymfony.php +++ b/src/ProgressBars/ProgressBarSymfony.php @@ -179,7 +179,7 @@ protected function buildTemplate(): string private function init(): bool { - $progresBarLevel = $this->getNextedLevel(); + $progresBarLevel = $this->getNestedLevel(); $levelPostfix = $progresBarLevel > 1 ? " Level: {$progresBarLevel}." : ''; if ($this->max <= 0) { diff --git a/src/functions.php b/src/functions.php index 9bcf708..bbf2594 100644 --- a/src/functions.php +++ b/src/functions.php @@ -16,15 +16,11 @@ namespace JBZoo\Cli; -use JBZoo\Cli\OutputMods\AbstractOutputMode; - /** * Shortcut method. * @param mixed $messages - * @psalm-suppress DeprecatedMethod - * @suppress PhanDeprecatedFunction */ function cli($messages = '', string $verboseLevel = OutLvl::DEFAULT): void { - AbstractOutputMode::getInstance()->_($messages, $verboseLevel); + CliHelper::getInstance()->_($messages, $verboseLevel); } diff --git a/tests/CliOutputLogstashTest.php b/tests/CliOutputLogstashTest.php index 7095a48..8803f9f 100644 --- a/tests/CliOutputLogstashTest.php +++ b/tests/CliOutputLogstashTest.php @@ -90,30 +90,10 @@ public function testFormatOfMessageVerboseFisrt(): void 'type' => 'string', ], 'process' => [ - 'pid' => 'integer', - 'executable' => 'string', - 'args_count' => ['string', 'string', 'string', 'string', 'string'], - 'command_line' => 'string', - 'process_command' => 'string', - 'args' => [ - 'command' => 'string', - 'exception' => 'NULL', - 'type-of-vars' => 'boolean', - 'no-progress' => 'boolean', - 'mute-errors' => 'boolean', - 'stdout-only' => 'boolean', - 'non-zero-on-error' => 'boolean', - 'timestamp' => 'boolean', - 'profile' => 'boolean', - 'output-mode' => 'string', - 'cron' => 'boolean', - 'help' => 'boolean', - 'quiet' => 'boolean', - 'verbose' => 'boolean', - 'version' => 'boolean', - 'ansi' => 'boolean', - 'no-interaction' => 'boolean', - ], + 'pid' => 'integer', + 'executable' => 'string', + 'command_line' => 'string', + 'process_command' => 'string', 'working_directory' => 'string', ], ], @@ -480,6 +460,31 @@ public function testMuteErrorsAndNonZeroOnError(): void Helper::assertLogstash(['CRITICAL', 'Command Exception: Some message'], $stdOutput[9]); } + public function testExtraContext(): void + { + $cmdResult = Helper::executeReal('test:output', [ + 'output-mode' => 'logstash', + 'extra-context' => null, + ]); + + isSame(0, $cmdResult->code); + isSame('', $cmdResult->err); + + $stdOutput = Helper::prepareLogstash($cmdResult->std); + + isSame(['bar' => 1], $stdOutput[0]->find('context.foo')); + isSame('line #1', $stdOutput[0]->find('context.zzz')); + Helper::assertLogstash(['NOTICE', 'Message with extra context #1'], $stdOutput[0]); + + isSame(['bar' => 2], $stdOutput[1]->find('context.foo')); + isSame('line #2', $stdOutput[1]->find('context.zzz')); + Helper::assertLogstash(['NOTICE', 'Message with extra context #2'], $stdOutput[1]); + + isSame(['bar' => 2, 'zzz' => 3], $stdOutput[2]->find('context.foo')); + isSame('line #3', $stdOutput[2]->find('context.zzz')); + Helper::assertLogstash(['NOTICE', 'Message with extra context #3'], $stdOutput[2]); + } + public function testCronAlias(): void { $cmdResult = Helper::executeReal('test:output', [ diff --git a/tests/CliOutputTextTest.php b/tests/CliOutputTextTest.php index 28e60e5..634765c 100644 --- a/tests/CliOutputTextTest.php +++ b/tests/CliOutputTextTest.php @@ -385,4 +385,37 @@ public function testCronModeAlias(): void isSame(\str_word_count($cmdResultAlias->std), \str_word_count($cmdResult->std)); isSame(\count(\explode("\n", $cmdResultAlias->std)), \count(\explode("\n", $cmdResult->std))); } + + public function testExtraContext(): void + { + $cmdResult = Helper::executeReal('test:output', [ + 'extra-context' => null, + ]); + + isSame(0, $cmdResult->code); + isSame('', $cmdResult->err); + isSame( + \implode("\n", [ + 'Message with extra context #1 {"foo":{"bar":1},"zzz":"line #1"}', + 'Message with extra context #2 {"foo":{"bar":2},"zzz":"line #2"}', + 'Message with extra context #3 {"foo":{"bar":2,"zzz":3},"zzz":"line #3"}', + ]), + $cmdResult->std, + ); + } + + public function testExtraContextCron(): void + { + $cmdResult = Helper::executeReal('test:output', [ + 'output-mode' => 'cron', + 'extra-context' => null, + ]); + + isSame(0, $cmdResult->code); + isSame('', $cmdResult->err); + + isContain('] Message with extra context #1 {"foo":{"bar":1},"zzz":"line #1"}', $cmdResult->std); + isContain('] Message with extra context #2 {"foo":{"bar":2},"zzz":"line #2"}', $cmdResult->std); + isContain('] Message with extra context #3 {"foo":{"bar":2,"zzz":3},"zzz":"line #3"}', $cmdResult->std); + } } diff --git a/tests/TestApp/Commands/TestOutput.php b/tests/TestApp/Commands/TestOutput.php index 5787747..1bb9226 100644 --- a/tests/TestApp/Commands/TestOutput.php +++ b/tests/TestApp/Commands/TestOutput.php @@ -17,6 +17,7 @@ namespace JBZoo\PHPUnit\TestApp\Commands; use JBZoo\Cli\CliCommand; +use JBZoo\Cli\CliHelper; use JBZoo\Cli\Exception; use JBZoo\Cli\OutLvl; use Symfony\Component\Console\Input\InputOption; @@ -33,13 +34,27 @@ protected function configure(): void $this ->setName('test:output') ->addOption('exception', null, InputOption::VALUE_OPTIONAL, 'Throw exception') - ->addOption('type-of-vars', null, InputOption::VALUE_NONE, 'Check type of vars'); + ->addOption('type-of-vars', null, InputOption::VALUE_NONE, 'Check type of vars') + ->addOption('extra-context', null, InputOption::VALUE_NONE, 'Check type of vars'); parent::configure(); } protected function executeAction(): int { + if ($this->getOptBool('extra-context')) { + CliHelper::getInstance()->appendExtraContext(['foo' => ['bar' => 1]]); + $this->_('Message with extra context #1', OutLvl::DEFAULT, ['zzz' => 'line #1']); + + CliHelper::getInstance()->appendExtraContext(['foo' => ['bar' => 2]]); + $this->_('Message with extra context #2', OutLvl::DEFAULT, ['zzz' => 'line #2']); + + CliHelper::getInstance()->appendExtraContext(['foo' => ['zzz' => 3]]); + $this->_('Message with extra context #3', OutLvl::DEFAULT, ['zzz' => 'line #3']); + + return 0; + } + if ($this->getOptBool('type-of-vars')) { $this->_(' '); $this->_(0);