diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index f172154..500b4ab 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -160,7 +160,7 @@ jobs:
- name: "Run mutation tests with infection/infection"
env:
STRYKER_DASHBOARD_API_KEY: ${{ secrets.STRYKER_DASHBOARD_API_KEY }}
- run: "vendor/bin/infection --show-mutations --only-covered --min-covered-msi=79 --min-msi=79 --coverage=.build/coverage --logger-github --no-progress -vv"
+ run: "vendor/bin/infection --show-mutations --only-covered --min-covered-msi=97 --min-msi=97 --coverage=.build/coverage --logger-github --no-progress -vv"
# This is a meta job to avoid to have to constantly change the protection rules
# whenever we touch the matrix.
diff --git a/composer.json b/composer.json
index 347a262..10c9b21 100644
--- a/composer.json
+++ b/composer.json
@@ -32,7 +32,7 @@
"infection/infection": "^0.27.0",
"laminas/laminas-modulemanager": "^2.14.0",
"laminas/laminas-servicemanager": "^3.21.0",
- "mimmi20/coding-standard": "^5.0.3",
+ "mimmi20/coding-standard": "^5.0.5",
"nikic/php-parser": "^v4.15.5",
"phpstan/extension-installer": "^1.3.1",
"phpstan/phpstan": "^1.10.15",
diff --git a/phpcs.xml b/phpcs.xml
index aa9d8c0..0a3f6b7 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -15,6 +15,7 @@
+
diff --git a/src/StreamFormatter.php b/src/StreamFormatter.php
index 9dfc247..c79871f 100644
--- a/src/StreamFormatter.php
+++ b/src/StreamFormatter.php
@@ -12,7 +12,6 @@
namespace Mimmi20\Monolog\Formatter;
-use DateTimeImmutable;
use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\NormalizerFormatter;
use Monolog\Level;
@@ -28,16 +27,11 @@
use function array_keys;
use function count;
use function is_array;
-use function is_bool;
-use function is_iterable;
use function is_scalar;
use function is_string;
-use function mb_strpos;
use function str_repeat;
use function str_replace;
use function trim;
-use function ucfirst;
-use function var_export;
final class StreamFormatter extends NormalizerFormatter
{
@@ -128,10 +122,6 @@ public function setFormatter(LineFormatter $formatter): void
*/
public function format(LogRecord $record): string
{
- /** @var array<(array|scalar|null)>|scalar|null $vars */
- /** @phpstan-var array{message: string, context: array, level: Level, level_name: string, channel: string, datetime: DateTimeImmutable, extra: array} $vars */
- $vars = $this->normalizeRecord($record);
-
$message = $this->getFormatter()->format($record);
$levelName = Level::fromValue($record->level->value)->getName();
@@ -172,65 +162,8 @@ public function format(LogRecord $record): string
],
);
- foreach (['extra', 'context'] as $element) {
- if (empty($vars[$element]) || !is_iterable($vars[$element])) {
- continue;
- }
-
- $this->table->addRow(new TableSeparator());
- $this->table->addRow(
- [new TableCell(ucfirst($element), ['colspan' => self::SPAN_ALL_COLUMS])],
- );
- $this->table->addRow(new TableSeparator());
-
- foreach ($vars[$element] as $key => $value) {
- if (!is_string($key)) {
- continue;
- }
-
- if (
- is_array($record->{$element})
- && isset($record->{$element}[$key])
- && $record->{$element}[$key] instanceof Throwable
- ) {
- $exception = $record->{$element}[$key];
-
- $value = [
- 'Code' => $exception->getCode(),
- 'File' => $exception->getFile(),
- 'Line' => $exception->getLine(),
- 'Message' => $exception->getMessage(),
- 'Trace' => $exception->getTraceAsString(),
- 'Type' => $exception::class,
- ];
-
- $this->addFact($key, $value);
-
- $prev = $exception->getPrevious();
-
- if ($prev instanceof Throwable) {
- do {
- $value = [
- 'Code' => $prev->getCode(),
- 'File' => $prev->getFile(),
- 'Line' => $prev->getLine(),
- 'Message' => $prev->getMessage(),
- 'Trace' => $prev->getTraceAsString(),
- 'Type' => $prev::class,
- ];
-
- $this->addFact('previous Throwable', $value);
-
- $prev = $prev->getPrevious();
- } while ($prev instanceof Throwable);
- }
-
- continue;
- }
-
- $this->addFact($key, $value);
- }
- }
+ $this->addExtra($record->extra);
+ $this->addContext($record->context);
$this->table->render();
@@ -272,38 +205,98 @@ private function getFormatter(): LineFormatter
return $this->formatter;
}
- /** @throws RuntimeException if encoding fails and errors are not ignored */
- private function stringify(mixed $value): string
+ /**
+ * @param array $context
+ *
+ * @throws RuntimeException
+ */
+ private function addContext(array $context): void
{
- return $this->replaceNewlines($this->convertToString($value));
+ if ($context === []) {
+ return;
+ }
+
+ $this->table->addRow(new TableSeparator());
+ $this->table->addRow(
+ [new TableCell('Context', ['colspan' => self::SPAN_ALL_COLUMS])],
+ );
+ $this->table->addRow(new TableSeparator());
+
+ foreach ($context as $key => $value) {
+ if (!is_string($key)) {
+ continue;
+ }
+
+ $this->addFact($key, $this->normalize($value));
+ }
}
- /** @throws RuntimeException if encoding fails and errors are not ignored */
- private function convertToString(mixed $data): string
+ /**
+ * @param array $extra
+ *
+ * @throws RuntimeException
+ */
+ private function addExtra(array $extra): void
{
- if ($data === null || is_bool($data)) {
- return var_export($data, true);
+ if ($extra === []) {
+ return;
}
- if (is_scalar($data)) {
- return (string) $data;
- }
+ $this->table->addRow(new TableSeparator());
+ $this->table->addRow(
+ [new TableCell('Extra', ['colspan' => self::SPAN_ALL_COLUMS])],
+ );
+ $this->table->addRow(new TableSeparator());
- return $this->toJson($data, true);
+ foreach ($extra as $key => $value) {
+ if (!is_string($key)) {
+ continue;
+ }
+
+ if ($extra[$key] instanceof Throwable) {
+ $this->addThrowable($extra[$key]);
+
+ continue;
+ }
+
+ $this->addFact($key, $this->normalize($value));
+ }
}
- /** @throws void */
- private function replaceNewlines(string $str): string
+ /** @throws RuntimeException */
+ private function addThrowable(Throwable $exception): void
{
- if ($this->allowInlineLineBreaks) {
- if (mb_strpos($str, '{') === 0) {
- return str_replace(['\r', '\n'], ["\r", "\n"], $str);
- }
+ $value = [
+ 'Code' => $exception->getCode(),
+ 'File' => $exception->getFile(),
+ 'Line' => $exception->getLine(),
+ 'Message' => $exception->getMessage(),
+ 'Trace' => $exception->getTraceAsString(),
+ 'Type' => $exception::class,
+ ];
+
+ $this->addFact('Throwable', $value);
- return $str;
+ $prev = $exception->getPrevious();
+
+ if (!$prev instanceof Throwable) {
+ return;
}
- return str_replace(["\r\n", "\r", "\n"], ' ', $str);
+ do {
+ $value = [
+ 'Code' => $prev->getCode(),
+ 'File' => $prev->getFile(),
+ 'Line' => $prev->getLine(),
+ 'Message' => $prev->getMessage(),
+ 'Trace' => $prev->getTraceAsString(),
+ 'Type' => $prev::class,
+ ];
+
+ $this->addFact('previous Throwable', $value);
+
+ $prev = $prev->getPrevious();
+ } while ($prev instanceof Throwable);
}
/** @throws RuntimeException if encoding fails and errors are not ignored */
@@ -315,11 +308,7 @@ private function addFact(string $name, mixed $value): void
$rowspan = count($value);
foreach (array_keys($value) as $number => $key) {
- $cellValue = $value[$key];
-
- if (!is_string($cellValue)) {
- $cellValue = $this->stringify($cellValue);
- }
+ $cellValue = $this->stringify($value[$key]);
if ($number === 0) {
$this->table->addRow(
@@ -341,17 +330,17 @@ private function addFact(string $name, mixed $value): void
),
],
);
- } else {
- $this->table->addRow([new TableCell((string) $key), new TableCell($cellValue)]);
+
+ continue;
}
+
+ $this->table->addRow([new TableCell((string) $key), new TableCell($cellValue)]);
}
return;
}
- if (!is_string($value)) {
- $value = $this->stringify($value);
- }
+ $value = $this->stringify($value);
$this->table->addRow(
[
@@ -363,4 +352,50 @@ private function addFact(string $name, mixed $value): void
],
);
}
+
+ /** @throws RuntimeException if encoding fails and errors are not ignored */
+ private function stringify(mixed $value): string
+ {
+ return $this->replaceNewlines($this->convertToString($value));
+ }
+
+ /** @throws RuntimeException if encoding fails and errors are not ignored */
+ private function convertToString(mixed $data): string
+ {
+ if (is_string($data)) {
+ return $data;
+ }
+
+ if ($data === null) {
+ return 'null';
+ }
+
+ if ($data === true) {
+ return 'true';
+ }
+
+ if ($data === false) {
+ return 'false';
+ }
+
+ if (is_scalar($data)) {
+ return (string) $data;
+ }
+
+ return $this->toJson($data, true);
+ }
+
+ /** @throws void */
+ private function replaceNewlines(string $str): string
+ {
+ if ($this->allowInlineLineBreaks) {
+ return str_replace(
+ ['\\\\r\\\\n', '\\r\\n', '\\\\r', '\\r', '\\\\n', '\\n', "\r\n", "\r"],
+ "\n",
+ $str,
+ );
+ }
+
+ return str_replace(["\r\n", "\r", "\n"], ' ', $str);
+ }
}
diff --git a/tests/ConfigProviderTest.php b/tests/ConfigProviderTest.php
index 1f4d5f4..1ec24dc 100644
--- a/tests/ConfigProviderTest.php
+++ b/tests/ConfigProviderTest.php
@@ -50,4 +50,35 @@ public function testGetMonologFormatterConfig(): void
self::assertIsArray($factories);
self::assertCount(1, $factories);
}
+
+ /** @throws Exception */
+ public function testInvoke(): void
+ {
+ $config = ($this->provider)();
+ self::assertIsArray($config);
+ self::assertCount(1, $config);
+
+ self::assertArrayHasKey('monolog_formatters', $config);
+
+ $monologFormatterConfig = $config['monolog_formatters'];
+ self::assertIsArray($monologFormatterConfig);
+ self::assertCount(2, $monologFormatterConfig);
+
+ self::assertArrayNotHasKey('abstract_factories', $monologFormatterConfig);
+ self::assertArrayNotHasKey('delegators', $monologFormatterConfig);
+ self::assertArrayNotHasKey('initializers', $monologFormatterConfig);
+ self::assertArrayNotHasKey('invokables', $monologFormatterConfig);
+ self::assertArrayNotHasKey('services', $monologFormatterConfig);
+ self::assertArrayNotHasKey('shared', $monologFormatterConfig);
+
+ self::assertArrayHasKey('aliases', $monologFormatterConfig);
+ $aliases = $monologFormatterConfig['aliases'];
+ self::assertIsArray($aliases);
+ self::assertCount(1, $aliases);
+
+ self::assertArrayHasKey('factories', $monologFormatterConfig);
+ $factories = $monologFormatterConfig['factories'];
+ self::assertIsArray($factories);
+ self::assertCount(1, $factories);
+ }
}
diff --git a/tests/ModuleTest.php b/tests/ModuleTest.php
index 72b0fb3..3b8928f 100644
--- a/tests/ModuleTest.php
+++ b/tests/ModuleTest.php
@@ -28,5 +28,26 @@ public function testGetConfig(): void
self::assertIsArray($config);
self::assertCount(1, $config);
self::assertArrayHasKey('monolog_formatters', $config);
+
+ $monologFormatterConfig = $config['monolog_formatters'];
+ self::assertIsArray($monologFormatterConfig);
+ self::assertCount(2, $monologFormatterConfig);
+
+ self::assertArrayNotHasKey('abstract_factories', $monologFormatterConfig);
+ self::assertArrayNotHasKey('delegators', $monologFormatterConfig);
+ self::assertArrayNotHasKey('initializers', $monologFormatterConfig);
+ self::assertArrayNotHasKey('invokables', $monologFormatterConfig);
+ self::assertArrayNotHasKey('services', $monologFormatterConfig);
+ self::assertArrayNotHasKey('shared', $monologFormatterConfig);
+
+ self::assertArrayHasKey('aliases', $monologFormatterConfig);
+ $aliases = $monologFormatterConfig['aliases'];
+ self::assertIsArray($aliases);
+ self::assertCount(1, $aliases);
+
+ self::assertArrayHasKey('factories', $monologFormatterConfig);
+ $factories = $monologFormatterConfig['factories'];
+ self::assertIsArray($factories);
+ self::assertCount(1, $factories);
}
}
diff --git a/tests/StreamFormatterTest.php b/tests/StreamFormatterTest.php
index 580aa61..d0e3e1a 100644
--- a/tests/StreamFormatterTest.php
+++ b/tests/StreamFormatterTest.php
@@ -26,11 +26,15 @@
use RuntimeException;
use stdClass;
use Symfony\Component\Console\Helper\Table;
+use Symfony\Component\Console\Helper\TableCell;
+use Symfony\Component\Console\Helper\TableSeparator;
use Symfony\Component\Console\Output\BufferedOutput;
-use Symfony\Component\Console\Output\Output;
+use Symfony\Component\Console\Output\OutputInterface;
use UnexpectedValueException;
+use function assert;
use function file_put_contents;
+use function in_array;
use function str_repeat;
use function str_replace;
@@ -74,7 +78,7 @@ public function testConstructWithDefaults(): void
$table->expects(self::never())
->method('render');
- $formatter = new StreamFormatter($output, $table);
+ $formatter = new StreamFormatter(output: $output, table: $table);
self::assertSame(NormalizerFormatter::SIMPLE_DATE, $formatter->getDateFormat());
self::assertSame(9, $formatter->getMaxNormalizeDepth());
@@ -140,13 +144,13 @@ public function testConstructWithValues(): void
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- $format,
- $tableStyle,
- $dateFormat,
- true,
- false,
+ output: $output,
+ table: $table,
+ format: $format,
+ tableStyle: $tableStyle,
+ dateFormat: $dateFormat,
+ allowInlineLineBreaks: true,
+ includeStacktraces: false,
);
self::assertSame($dateFormat, $formatter->getDateFormat());
@@ -213,13 +217,13 @@ public function testConstructWithValues2(): void
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- $format,
- $tableStyle,
- $dateFormat,
- false,
- true,
+ output: $output,
+ table: $table,
+ format: $format,
+ tableStyle: $tableStyle,
+ dateFormat: $dateFormat,
+ allowInlineLineBreaks: false,
+ includeStacktraces: true,
);
self::assertSame($dateFormat, $formatter->getDateFormat());
@@ -286,13 +290,13 @@ public function testConstructWithValues3(): void
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- $format,
- $tableStyle,
- $dateFormat,
- false,
- false,
+ output: $output,
+ table: $table,
+ format: $format,
+ tableStyle: $tableStyle,
+ dateFormat: $dateFormat,
+ allowInlineLineBreaks: false,
+ includeStacktraces: false,
);
self::assertSame($dateFormat, $formatter->getDateFormat());
@@ -365,13 +369,13 @@ public function testConstructWithValues4(): void
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- $format,
- $tableStyle,
- $dateFormat,
- true,
- false,
+ output: $output,
+ table: $table,
+ format: $format,
+ tableStyle: $tableStyle,
+ dateFormat: $dateFormat,
+ allowInlineLineBreaks: true,
+ includeStacktraces: false,
);
self::assertSame($dateFormat, $formatter->getDateFormat());
@@ -420,6 +424,7 @@ public function testFormat(): void
$message = 'test message';
$channel = 'test-channel';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -429,15 +434,18 @@ public function testFormat(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->with()
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher, $message): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame($message, $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -460,17 +468,73 @@ public function testFormat(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(3))
- ->method('addRow');
+ $matcher = self::exactly(3);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
- $formatter = new StreamFormatter($output, $table);
+ $formatter = new StreamFormatter(output: $output, table: $table);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: [],
extra: [],
@@ -490,6 +554,7 @@ public function testFormat2(): void
$message = 'test message';
$channel = 'test-channel';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -499,14 +564,18 @@ public function testFormat2(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', 220), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher, $message): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame($message, $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -529,19 +598,106 @@ public function testFormat2(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(15))
- ->method('addRow');
+ $matcher = self::exactly(15);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 14 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
- $formatter = new StreamFormatter($output, $table);
+ $formatter = new StreamFormatter(output: $output, table: $table);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
- context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz']],
+ context: ['one' => null, 'two' => true, 0 => 'numeric-key', 'three' => false, 'four' => ['abc', 'xyz']],
extra: ['app' => 'test-app'],
);
@@ -559,6 +715,7 @@ public function testFormat3(): void
$message = 'test message';
$channel = 'test-channel';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -568,14 +725,18 @@ public function testFormat3(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', 220), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame('test message true test-app', $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -598,19 +759,110 @@ public function testFormat3(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(15))
- ->method('addRow');
+ $matcher = self::exactly(15);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 14 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
- $formatter = new StreamFormatter($output, $table, '%message% %context.two% %extra.app%');
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.two% %extra.app%',
+ );
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
- context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz']],
+ context: ['one' => null, 'two' => true, 0 => 'numeric-key', 'three' => false, 'four' => ['abc', 'xyz']],
extra: ['app' => 'test-app'],
);
@@ -628,6 +880,7 @@ public function testFormat4(): void
$message = 'test message';
$channel = 'test-channel';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -637,14 +890,18 @@ public function testFormat4(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', 220), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame('test message ["abc","xyz"] test-app', $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -667,19 +924,110 @@ public function testFormat4(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(15))
- ->method('addRow');
+ $matcher = self::exactly(15);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 14 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
- $formatter = new StreamFormatter($output, $table, '%message% %context.four% %extra.app%');
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.four% %extra.app%',
+ );
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
- context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz']],
+ context: ['one' => null, 'two' => true, 0 => 'numeric-key', 'three' => false, 'four' => ['abc', 'xyz']],
extra: ['app' => 'test-app'],
);
@@ -698,6 +1046,7 @@ public function testFormat5(): void
$channel = 'test-channel';
$datetime = new DateTimeImmutable('now');
$tableStyle = 'default';
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -707,14 +1056,18 @@ public function testFormat5(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', 220), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame('test message test test test-app', $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -737,26 +1090,113 @@ public function testFormat5(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(16))
- ->method('addRow');
+ $matcher = self::exactly(16);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 14 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.five% %extra.app%',
- $tableStyle,
- null,
- false,
+ output: $output,
+ table: $table,
+ format: '%message% %context.five% %extra.app%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: false,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
- context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
+ context: ['one' => null, 'two' => true, 0 => 'numeric-key', 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
extra: ['app' => 'test-app'],
);
@@ -775,6 +1215,7 @@ public function testFormat6(): void
$channel = 'test-channel';
$datetime = new DateTimeImmutable('now');
$tableStyle = 'default';
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -784,14 +1225,18 @@ public function testFormat6(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', 220), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame("test message test\ntest test-app", $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -814,27 +1259,114 @@ public function testFormat6(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(16))
- ->method('addRow');
+ $matcher = self::exactly(16);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 14 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.five% %extra.app%',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% %context.five% %extra.app%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
- extra: ['app' => 'test-app'],
+ extra: ['app' => 'test-app', 0 => 'numeric-key'],
);
$formatted = $formatter->format($record);
@@ -853,6 +1385,7 @@ public function testFormat7(): void
$tableStyle = 'default';
$datetime = new DateTimeImmutable('now');
$exception = new RuntimeException('error');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -862,14 +1395,21 @@ public function testFormat7(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', 220), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher, $exception): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame(
+ "test message test\ntest <[object] (RuntimeException(code: " . $exception->getCode() . '): error at ' . $exception->getFile() . ':' . $exception->getLine() . ')>',
+ $messages,
+ ),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -892,27 +1432,194 @@ public function testFormat7(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(22))
- ->method('addRow');
+ $matcher = self::exactly(23);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 15, 17], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 16 => self::assertCount(
+ 1,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ 8, 21 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 8) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 10) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 11) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 12) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 13) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 16) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ if ($matcher->numberOfInvocations() === 21) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('four', (string) $tableCell);
+
+ return $table;
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.five% <%extra.Exception%>',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% %context.five% <%extra.Exception%>',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
- extra: ['app' => 'test-app', 'Exception' => $exception],
+ extra: ['app' => 'test-app', 0 => 'numeric-key', 'Exception' => $exception, 'system' => 'test-system'],
);
$formatted = $formatter->format($record);
@@ -930,8 +1637,8 @@ public function testFormat8(): void
$channel = 'test-channel';
$tableStyle = 'default';
$datetime = new DateTimeImmutable('now');
-
- $exception = new RuntimeException('error');
+ $exception = new RuntimeException('error');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -941,14 +1648,18 @@ public function testFormat8(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame("test message test\ntest test-app", $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -971,27 +1682,184 @@ public function testFormat8(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(22))
- ->method('addRow');
+ $matcher = self::exactly(23);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 15, 17], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 16 => self::assertCount(
+ 1,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ 8, 21 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 8) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 10) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 11) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 12) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 13) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 16) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.five% %extra.app%',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% %context.five% %extra.app%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
- extra: ['app' => 'test-app', 'Exception' => $exception],
+ extra: ['app' => 'test-app', 0 => 'numeric-key', 'Exception' => $exception, 'system' => 'test-system'],
);
$formatted = $formatter->format($record);
@@ -1009,6 +1877,7 @@ public function testFormat9(): void
$channel = 'test-channel';
$tableStyle = 'default';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -1022,14 +1891,18 @@ public function testFormat9(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame("test message test\ntest test-app", $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -1052,27 +1925,320 @@ public function testFormat9(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(34))
- ->method('addRow');
- $table->expects(self::once())
- ->method('render');
+ $matcher = self::exactly(35);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 27, 29], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
- $formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.five% %extra.app%',
- $tableStyle,
- null,
- true,
- );
+ return $table;
+ }
- $record = new LogRecord(
- datetime: $datetime,
- channel: $channel,
- level: Level::Error,
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 28 => self::assertCount(
+ 1,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ 8, 14, 20, 33 => self::assertCount(
+ 3,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 8) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 10) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 11) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 12) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 13) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 14) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('previous Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 15) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 16) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 17) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 18) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 19) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 20) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('previous Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 21) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 22) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 23) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 24) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 25) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 28) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
+ $table->expects(self::once())
+ ->method('render');
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.five% %extra.app%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
+ );
+
+ $record = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level,
message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
- extra: ['app' => 'test-app', 'Exception' => $exception3],
+ extra: ['app' => 'test-app', 0 => 'numeric-key', 'Exception' => $exception3, 'system' => 'test-system'],
);
$formatted = $formatter->format($record);
@@ -1090,6 +2256,7 @@ public function testFormat10(): void
$channel = 'test-channel';
$tableStyle = 'default';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$exception1 = new RuntimeException('error');
$exception2 = new UnexpectedValueException('error', 4711, $exception1);
@@ -1103,14 +2270,21 @@ public function testFormat10(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame(
+ "test message context.one test\ntest test-app extra.Exception",
+ $messages,
+ ),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -1133,27 +2307,320 @@ public function testFormat10(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(34))
- ->method('addRow');
+ $matcher = self::exactly(35);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 27, 29], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 28 => self::assertCount(
+ 1,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ 8, 14, 20, 33 => self::assertCount(
+ 3,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 8) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 10) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 11) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 12) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 13) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 14) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('previous Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 15) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 16) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 17) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 18) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 19) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 20) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('previous Throwable', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('Code', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 21) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('File', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 22) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Line', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 23) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Message', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 24) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Trace', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 25) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Type', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 28) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% context.one %context.five% %extra.app% extra.Exception',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% context.one %context.five% %extra.app% extra.Exception',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
- extra: ['app' => 'test-app', 'Exception' => $exception3],
+ extra: ['app' => 'test-app', 0 => 'numeric-key', 'Exception' => $exception3, 'system' => 'test-system'],
);
$formatted = $formatter->format($record);
@@ -1171,6 +2638,7 @@ public function testFormat11(): void
$channel = 'test-channel';
$tableStyle = 'default';
$datetime = new DateTimeImmutable('now');
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -1180,14 +2648,21 @@ public function testFormat11(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame(
+ "test message NULL test\ntest test-app test-app",
+ $messages,
+ ),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -1210,24 +2685,110 @@ public function testFormat11(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(12))
- ->method('addRow');
+ $matcher = self::exactly(12);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: ['one' => null, 'five' => "test\ntest"],
extra: ['app' => 'test-app'],
@@ -1252,6 +2813,7 @@ public function testFormat12(): void
$stdClass = new stdClass();
$stdClass->a = $channel;
$stdClass->b = $message;
+ $level = Level::Error;
$expected = 'rendered-content';
@@ -1261,14 +2823,18 @@ public function testFormat12(): void
$output->expects(self::exactly(2))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected);
- $output->expects(self::exactly(5))
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher, $formattedMessage): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame($formattedMessage, $messages),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -1291,24 +2857,111 @@ public function testFormat12(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(13))
- ->method('addRow');
+ $matcher = self::exactly(13);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 13 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::once())
->method('render');
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
context: ['one' => null, 'five' => "test\ntest", 'six' => $stdClass],
extra: ['app' => 'test-app'],
@@ -1343,6 +2996,7 @@ public function testFormat13(): void
$stdClass = new stdClass();
$stdClass->a = $channel;
$stdClass->b = $message;
+ $level = Level::Error;
$expected = '==============================================================================================================================================================================================================================================================================
@@ -1361,10 +3015,14 @@ public function testFormat13(): void
+----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Context |
+----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| one | NULL |
+| one | null |
+| two | true |
+| three | false |
+| four | 42 |
| five | test |
| | test |
| six | stdClass | {"a":"test-channel","b":"test message"} |
+| seven | 47.11 |
+----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
';
@@ -1373,20 +3031,20 @@ public function testFormat13(): void
$table = new Table($output);
$formatter = new StreamFormatter(
- $output,
- $table,
- '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
- $tableStyle,
- null,
- true,
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
);
$record = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level,
message: $message,
- context: ['one' => null, 'five' => "test\ntest", 'six' => $stdClass],
+ context: ['one' => null, 'two' => true, 'three' => false, 'four' => 42, 'five' => "test\ntest", 'six' => $stdClass, 'seven' => 47.11],
extra: ['app' => 'test-app'],
);
@@ -1412,37 +3070,576 @@ public function testFormat13(): void
* @throws Exception
* @throws RuntimeException
*/
- public function testFormatBatch(): void
+ public function testFormat14(): void
{
- $message = 'test message';
- $channel = 'test-channel';
- $tableStyle = StreamFormatter::BOX_STYLE;
- $datetime = new DateTimeImmutable('now');
+ $message = ' test message ';
+ $channel = 'test-channel';
+ $tableStyle = 'default';
+ $datetime = new DateTimeImmutable('now');
+ $formattedMessage = 'this is a formatted message';
+ $stdClass = new stdClass();
+ $stdClass->a = $channel;
+ $stdClass->b = $message;
+ $level = Level::Error;
- $expected1 = 'rendered-content-1';
- $expected2 = 'rendered-content-2';
- $expected3 = 'rendered-content-3';
+ $expected = '==============================================================================================================================================================================================================================================================================
- $record1 = new LogRecord(
- datetime: $datetime,
- channel: $channel,
- level: Level::Error,
- message: $message,
- context: [],
- extra: [],
- );
- $record2 = new LogRecord(
- datetime: $datetime,
- channel: $channel,
- level: Level::Error,
- message: $message,
+this is a formatted message
+
++----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| General Info |
+| Time | ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' |
+| Level | ERROR |
++----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Extra |
++----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| app | test-app |
++----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Context |
++----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| one | null |
+| five | test |
+| | test |
+| six | stdClass | {"a":"test-channel","b":" test message "} |
++----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+';
+
+ $output = new BufferedOutput();
+ $table = new Table($output);
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
+ );
+
+ $record = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level,
+ message: $message,
+ context: ['one' => null, 'five' => "test\ntest", 'six' => $stdClass],
+ extra: ['app' => 'test-app'],
+ );
+
+ $lineFormatter = $this->getMockBuilder(LineFormatter::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $lineFormatter->expects(self::once())
+ ->method('format')
+ ->with($record)
+ ->willReturn($formattedMessage);
+
+ $formatter->setFormatter($lineFormatter);
+
+ $formatted = $formatter->format($record);
+
+ self::assertSame(
+ str_replace("\r\n", "\n", $expected),
+ str_replace("\r\n", "\n", $formatted),
+ );
+ }
+
+ /**
+ * @throws Exception
+ * @throws RuntimeException
+ */
+ public function testFormat15(): void
+ {
+ $message1 = 'test message\rtest message 2\ntest message 3\r\ntest message 4';
+ $message2 = 'test message 5\rtest message 6\ntest message 7\r\ntest message 8';
+ $message3 = "test1\ntest2\rtest3\r\ntest4";
+ $channel = 'test-channel';
+ $tableStyle = 'default';
+ $datetime = new DateTimeImmutable('now');
+ $formattedMessage = 'this is a formatted message';
+ $stdClass = new stdClass();
+ $stdClass->a = $channel;
+ $stdClass->b = $message1;
+ $level = Level::Error;
+ $appName = 'test-app';
+
+ $expected = 'rendered-content';
+
+ $output = $this->getMockBuilder(BufferedOutput::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $output->expects(self::exactly(2))
+ ->method('fetch')
+ ->willReturnOnConsecutiveCalls('', $expected);
+ $matcher = self::exactly(5);
+ $output->expects($matcher)
+ ->method('writeln')
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher, $formattedMessage): void {
+ match ($matcher->numberOfInvocations()) {
+ 1 => self::assertSame(str_repeat('=', StreamFormatter::FULL_WIDTH), $messages),
+ 2, 4, 5 => self::assertSame('', $messages),
+ default => self::assertSame($formattedMessage, $messages),
+ };
+ },
+ );
+
+ $table = $this->getMockBuilder(Table::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $table->expects(self::once())
+ ->method('setStyle')
+ ->with($tableStyle)
+ ->willReturnSelf();
+ $table->expects(self::exactly(3))
+ ->method('setColumnMaxWidth')
+ ->willReturnSelf();
+ $table->expects(self::once())
+ ->method('setColumnWidths')
+ ->with(
+ [StreamFormatter::WIDTH_FIRST_COLUMN, StreamFormatter::WIDTH_SECOND_COLUMN, StreamFormatter::WIDTH_THIRD_COLUMN],
+ )
+ ->willReturnSelf();
+ $table->expects(self::once())
+ ->method('setRows')
+ ->with([])
+ ->willReturnSelf();
+ $matcher = self::exactly(14);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level, $message2, $message3, $appName): Table {
+ if (in_array($matcher->numberOfInvocations(), [4, 6, 8, 10], true)) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 5, 9 => self::assertCount(1, $row, (string) $matcher->numberOfInvocations()),
+ 13 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if ($matcher->numberOfInvocations() === 1) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('General Info', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 2) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Time', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('Level', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($level->getName(), (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 5) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Extra', (string) $tableCell);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 7) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('app', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame($appName, (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 9) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell);
+ self::assertSame('Context', (string) $tableCell);
+ }
+
+ if ($matcher->numberOfInvocations() === 11) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('one', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('null', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 12) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('five', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ str_replace(
+ ['\\\\r\\\\n', '\\r\\n', '\\\\r', '\\r', '\\\\n', '\\n', "\r\n", "\r"],
+ "\n",
+ $message3,
+ ),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 13) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('six', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame('stdClass', (string) $tableCell2);
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 14) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell1);
+ self::assertSame('seven', (string) $tableCell1);
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(TableCell::class, $tableCell2);
+ self::assertSame(
+ str_replace(
+ ['\\\\r\\\\n', '\\r\\n', '\\\\r', '\\r', '\\\\n', '\\n', "\r\n", "\r"],
+ "\n",
+ $message2,
+ ),
+ (string) $tableCell2,
+ );
+
+ return $table;
+ }
+
+ return $table;
+ },
+ );
+ $table->expects(self::once())
+ ->method('render');
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
+ );
+
+ $record = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level,
+ message: $message1,
+ context: ['one' => null, 'five' => $message3, 'six' => $stdClass, 'seven' => $message2],
+ extra: ['app' => $appName],
+ );
+
+ $lineFormatter = $this->getMockBuilder(LineFormatter::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $lineFormatter->expects(self::once())
+ ->method('format')
+ ->with($record)
+ ->willReturn($formattedMessage);
+
+ $formatter->setFormatter($lineFormatter);
+
+ $formatted = $formatter->format($record);
+
+ self::assertSame($expected, $formatted);
+ }
+
+ /**
+ * @throws Exception
+ * @throws RuntimeException
+ */
+ public function testFormat16(): void
+ {
+ $message1 = 'test message\rtest message 2\ntest message 3\r\ntest message 4';
+ $message2 = 'test message 5\rtest message 6\ntest message 7\r\ntest message 8';
+ $message3 = "test1\ntest2\rtest3\r\ntest4";
+ $channel = 'test-channel';
+ $tableStyle = 'default';
+ $datetime = new DateTimeImmutable('now');
+ $formattedMessage = 'this is a formatted message';
+ $stdClass = new stdClass();
+ $stdClass->a = $channel;
+ $stdClass->b = $message1;
+ $level = Level::Error;
+ $appName = 'test-app';
+
+ $expected = <<format(NormalizerFormatter::SIMPLE_DATE)} |
+ | Level | ERROR |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | Extra |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | app | test-app |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | Context |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | one | null |
+ | five | test1 test2 test3 test4 |
+ | six | stdClass | {"a":"test-channel","b":"test message\\\\rtest message 2\\\\ntest message 3\\\\r\\\\ntest message 4"} |
+ | seven | test message 5\\rtest message 6\\ntest mes |
+ | | sage 7\\r\\ntest message 8 |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+ TXT;
+
+ $output = new BufferedOutput();
+ $table = new Table($output);
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: false,
+ );
+
+ $record = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level,
+ message: $message1,
+ context: ['one' => null, 'five' => $message3, 'six' => $stdClass, 'seven' => $message2],
+ extra: ['app' => $appName],
+ );
+
+ $lineFormatter = $this->getMockBuilder(LineFormatter::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $lineFormatter->expects(self::once())
+ ->method('format')
+ ->with($record)
+ ->willReturn($formattedMessage);
+
+ $formatter->setFormatter($lineFormatter);
+
+ $formatted = $formatter->format($record);
+
+ self::assertSame(
+ str_replace(["\r\n", "\r"], "\n", $expected),
+ str_replace(["\r\n", "\r"], "\n", $formatted),
+ );
+ }
+
+ /**
+ * @throws Exception
+ * @throws RuntimeException
+ */
+ public function testFormat17(): void
+ {
+ $message1 = 'test message\rtest message 2\ntest message 3\r\ntest message 4';
+ $message2 = 'test message 5\rtest message 6\ntest message 7\r\ntest message 8';
+ $message3 = "test1\ntest2\rtest3\r\ntest4";
+ $channel = 'test-channel';
+ $tableStyle = 'default';
+ $datetime = new DateTimeImmutable('now');
+ $formattedMessage = 'this is a formatted message';
+ $stdClass = new stdClass();
+ $stdClass->a = $channel;
+ $stdClass->b = $message1;
+ $level = Level::Error;
+ $appName = 'test-app';
+
+ $expected = <<format(NormalizerFormatter::SIMPLE_DATE)} |
+ | Level | ERROR |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | Extra |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | app | test-app |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | Context |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+ | one | null |
+ | five | test1 |
+ | | test2 |
+ | | test3 |
+ | | test4 |
+ | six | stdClass | {"a":"test-channel","b":"test message |
+ | | | test message 2 |
+ | | | test message 3 |
+ | | | test message 4"} |
+ | seven | test message 5 |
+ | | test message 6 |
+ | | test message 7 |
+ | | test message 8 |
+ +----------------------+----------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+ TXT;
+
+ $output = new BufferedOutput();
+ $table = new Table($output);
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: '%message% %context.one% %context.five% %context% %extra.app% %extra.app% %extra%',
+ tableStyle: $tableStyle,
+ dateFormat: null,
+ allowInlineLineBreaks: true,
+ );
+
+ $record = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level,
+ message: $message1,
+ context: ['one' => null, 'five' => $message3, 'six' => $stdClass, 'seven' => $message2],
+ extra: ['app' => $appName],
+ );
+
+ $lineFormatter = $this->getMockBuilder(LineFormatter::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $lineFormatter->expects(self::once())
+ ->method('format')
+ ->with($record)
+ ->willReturn($formattedMessage);
+
+ $formatter->setFormatter($lineFormatter);
+
+ $formatted = $formatter->format($record);
+
+ self::assertSame(
+ str_replace(["\r\n", "\r"], "\n", $expected),
+ str_replace(["\r\n", "\r"], "\n", $formatted),
+ );
+ }
+
+ /**
+ * @throws Exception
+ * @throws RuntimeException
+ */
+ public function testFormatBatch(): void
+ {
+ $message = 'test message';
+ $channel = 'test-channel';
+ $tableStyle = StreamFormatter::BOX_STYLE;
+ $datetime = new DateTimeImmutable('now');
+ $level1 = Level::Error;
+ $level2 = Level::Error;
+ $level3 = Level::Error;
+
+ $expected1 = 'rendered-content-1';
+ $expected2 = 'rendered-content-2';
+ $expected3 = 'rendered-content-3';
+
+ $record1 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level1,
+ message: $message,
+ context: [],
+ extra: [],
+ );
+ $record2 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: $level2,
+ message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz']],
extra: ['app' => 'test-app'],
);
$record3 = new LogRecord(
datetime: $datetime,
channel: $channel,
- level: Level::Error,
+ level: $level3,
message: $message,
context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz'], 'five' => "test\ntest"],
extra: ['app' => 'test-app'],
@@ -1454,14 +3651,30 @@ public function testFormatBatch(): void
$output->expects(self::exactly(6))
->method('fetch')
->willReturnOnConsecutiveCalls('', $expected1, '', $expected2, '', $expected3);
- $output->expects(self::exactly(15))
+ $matcher = self::exactly(15);
+ $output->expects($matcher)
->method('writeln')
- ->willReturnMap(
- [
- [str_repeat('=', StreamFormatter::FULL_WIDTH), Output::OUTPUT_NORMAL, null],
- ['', Output::OUTPUT_NORMAL, null],
- [$message, Output::OUTPUT_NORMAL, null],
- ],
+ ->willReturnCallback(
+ /** @phpcsSuppress SlevomatCodingStandard.Functions.UnusedParameter.UnusedParameter */
+ static function (string | iterable $messages, int $options = OutputInterface::OUTPUT_NORMAL) use ($matcher, $message): void {
+ match ($matcher->numberOfInvocations()) {
+ 1, 6, 11 => self::assertSame(
+ str_repeat('=', StreamFormatter::FULL_WIDTH),
+ $messages,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ 2, 4, 5, 7, 9, 10, 12, 14, 15 => self::assertSame(
+ '',
+ $messages,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ default => self::assertSame(
+ $message,
+ $messages,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ };
+ },
);
$table = $this->getMockBuilder(Table::class)
@@ -1484,12 +3697,240 @@ public function testFormatBatch(): void
->method('setRows')
->with([])
->willReturnSelf();
- $table->expects(self::exactly(34))
- ->method('addRow');
+ $matcher = self::exactly(34);
+ $table->expects($matcher)
+ ->method('addRow')
+ ->willReturnCallback(
+ static function (TableSeparator | array $row) use ($matcher, $table, $datetime, $level1, $level2, $level3): Table {
+ if (
+ in_array(
+ $matcher->numberOfInvocations(),
+ [7, 9, 11, 13, 22, 24, 26, 28],
+ true,
+ )
+ ) {
+ self::assertInstanceOf(
+ TableSeparator::class,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ self::assertIsArray($row, (string) $matcher->numberOfInvocations());
+
+ match ($matcher->numberOfInvocations()) {
+ 1, 4, 8, 12, 19, 23, 27 => self::assertCount(
+ 1,
+ $row,
+ (string) $matcher->numberOfInvocations(),
+ ),
+ 17, 32 => self::assertCount(3, $row, (string) $matcher->numberOfInvocations()),
+ default => self::assertCount(2, $row, (string) $matcher->numberOfInvocations()),
+ };
+
+ if (
+ $matcher->numberOfInvocations() === 1
+ || $matcher->numberOfInvocations() === 4
+ || $matcher->numberOfInvocations() === 19
+ ) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'General Info',
+ (string) $tableCell,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ if (
+ $matcher->numberOfInvocations() === 2
+ || $matcher->numberOfInvocations() === 5
+ || $matcher->numberOfInvocations() === 20
+ ) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'Time',
+ (string) $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ $datetime->format(NormalizerFormatter::SIMPLE_DATE),
+ (string) $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 3) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'Level',
+ (string) $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ $level1->getName(),
+ (string) $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 6) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'Level',
+ (string) $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ $level2->getName(),
+ (string) $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ if ($matcher->numberOfInvocations() === 21) {
+ $tableCell1 = $row[0];
+ assert($tableCell1 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'Level',
+ (string) $tableCell1,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ $tableCell2 = $row[1];
+ assert($tableCell2 instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ $level3->getName(),
+ (string) $tableCell2,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ if (
+ $matcher->numberOfInvocations() === 8
+ || $matcher->numberOfInvocations() === 23
+ ) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'Extra',
+ (string) $tableCell,
+ (string) $matcher->numberOfInvocations(),
+ );
+
+ return $table;
+ }
+
+ if (
+ $matcher->numberOfInvocations() === 12
+ || $matcher->numberOfInvocations() === 27
+ ) {
+ $tableCell = $row[0];
+ assert($tableCell instanceof TableCell);
+
+ self::assertInstanceOf(
+ TableCell::class,
+ $tableCell,
+ (string) $matcher->numberOfInvocations(),
+ );
+ self::assertSame(
+ 'Context',
+ (string) $tableCell,
+ (string) $matcher->numberOfInvocations(),
+ );
+ }
+
+ return $table;
+ },
+ );
$table->expects(self::exactly(3))
->method('render');
- $formatter = new StreamFormatter($output, $table);
+ $formatter = new StreamFormatter(output: $output, table: $table);
$formatted = $formatter->formatBatch([$record1, $record2, $record3]);
@@ -1537,7 +3978,7 @@ public function testFormatBatch2(): void
├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Context │
├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
-│ one │ NULL │
+│ one │ null │
│ two │ true │
│ three │ false │
│ four │ 0 │ abc │
@@ -1562,13 +4003,12 @@ public function testFormatBatch2(): void
├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Context │
├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
-│ one │ NULL │
+│ one │ null │
│ two │ true │
│ three │ false │
│ four │ 0 │ abc │
│ │ 1 │ xyz │
-│ five │ test │
-│ │ test │
+│ five │ test test │
└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
';
@@ -1601,7 +4041,228 @@ public function testFormatBatch2(): void
$output = new BufferedOutput();
$table = new Table($output);
- $formatter = new StreamFormatter($output, $table, null, $tableStyle);
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: null,
+ tableStyle: $tableStyle,
+ );
+
+ $formatted = $formatter->formatBatch([$record1, $record2, $record3]);
+
+ file_put_contents('output.txt', $formatted);
+
+ self::assertSame(
+ str_replace("\r\n", "\n", $expected1 . $expected2 . $expected3),
+ str_replace("\r\n", "\n", $formatted),
+ );
+ }
+
+ /**
+ * @throws Exception
+ * @throws RuntimeException
+ */
+ public function testFormatBatch3(): void
+ {
+ $message = 'test message';
+ $channel = 'test-channel';
+ $tableStyle = StreamFormatter::BOX_STYLE;
+ $datetime = new DateTimeImmutable('now');
+
+ $expected1 = '==============================================================================================================================================================================================================================================================================
+
+test message
+
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ General Info │
+│ Time │ ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' │
+│ Level │ ERROR │
+└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+';
+ $expected2 = '==============================================================================================================================================================================================================================================================================
+
+test message
+
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ General Info │
+│ Time │ ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' │
+│ Level │ ERROR │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Context │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ one │ null │
+│ two │ true │
+│ three │ false │
+│ four │ 0 │ abc │
+│ │ 1 │ xyz │
+└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+';
+ $expected3 = '==============================================================================================================================================================================================================================================================================
+
+test message
+
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ General Info │
+│ Time │ ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' │
+│ Level │ ERROR │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Extra │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ app │ test-app │
+└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+';
+
+ $record1 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: Level::Error,
+ message: $message,
+ context: [],
+ extra: [],
+ );
+ $record2 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: Level::Error,
+ message: $message,
+ context: ['one' => null, 'two' => true, 'three' => false, 'four' => ['abc', 'xyz']],
+ extra: [],
+ );
+ $record3 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: Level::Error,
+ message: $message,
+ context: [],
+ extra: ['app' => 'test-app'],
+ );
+
+ $output = new BufferedOutput();
+ $table = new Table($output);
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: null,
+ tableStyle: $tableStyle,
+ );
+
+ $formatted = $formatter->formatBatch([$record1, $record2, $record3]);
+
+ file_put_contents('output.txt', $formatted);
+
+ self::assertSame(
+ str_replace("\r\n", "\n", $expected1 . $expected2 . $expected3),
+ str_replace("\r\n", "\n", $formatted),
+ );
+ }
+
+ /**
+ * @throws Exception
+ * @throws RuntimeException
+ */
+ public function testFormatBatch4(): void
+ {
+ $message = ' test message ';
+ $channel = 'test-channel';
+ $tableStyle = StreamFormatter::BOX_STYLE;
+ $datetime = new DateTimeImmutable('now');
+
+ $expected1 = '==============================================================================================================================================================================================================================================================================
+
+test message
+
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ General Info │
+│ Time │ ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' │
+│ Level │ ERROR │
+└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+';
+ $expected2 = '==============================================================================================================================================================================================================================================================================
+
+test message
+
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ General Info │
+│ Time │ ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' │
+│ Level │ ERROR │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Context │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ one │ null │
+│ two │ true │
+│ three │ false │
+│ four five │ 0 │ abc │
+│ │ 1 │ xyz │
+└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+';
+ $expected3 = '==============================================================================================================================================================================================================================================================================
+
+test message
+
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ General Info │
+│ Time │ ' . $datetime->format(
+ NormalizerFormatter::SIMPLE_DATE,
+ ) . ' │
+│ Level │ ERROR │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ Extra │
+├──────────────────────┼──────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
+│ app │ test-app │
+└──────────────────────┴──────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
+
+';
+
+ $record1 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: Level::Error,
+ message: $message,
+ context: [],
+ extra: [],
+ );
+ $record2 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: Level::Error,
+ message: $message,
+ context: ['one' => null, 'two' => true, 'three' => false, ' four_five ' => ['abc', 'xyz']],
+ extra: [],
+ );
+ $record3 = new LogRecord(
+ datetime: $datetime,
+ channel: $channel,
+ level: Level::Error,
+ message: $message,
+ context: [],
+ extra: ['app' => 'test-app'],
+ );
+
+ $output = new BufferedOutput();
+ $table = new Table($output);
+
+ $formatter = new StreamFormatter(
+ output: $output,
+ table: $table,
+ format: null,
+ tableStyle: $tableStyle,
+ );
$formatted = $formatter->formatBatch([$record1, $record2, $record3]);