Skip to content
Merged
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"scripts": {
"cs:diff": "php-cs-fixer fix --dry-run -v --diff --show-progress dots",
"cs:fix": "php-cs-fixer fix -v",
"test": "phpunit",
"test": "XDEBUG_MODE=coverage phpunit",
"psalm": "psalm"
},
"autoload": {
Expand Down
61 changes: 61 additions & 0 deletions src/Context/DefaultProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

declare(strict_types=1);

namespace RoadRunner\PsrLogger\Context;

use RoadRunner\PsrLogger\Context\ObjectProcessor\DateTimeProcessor;
use RoadRunner\PsrLogger\Context\ObjectProcessor\FallbackProcessor;
use RoadRunner\PsrLogger\Context\ObjectProcessor\StringableProcessor;
use RoadRunner\PsrLogger\Context\ObjectProcessor\ThrowableProcessor;

final class DefaultProcessor
{
/** @var list<ObjectProcessor> */
private array $processors = [];

public static function createDefault(): self
{
$self = new self();
$self->processors = [
new DateTimeProcessor(),
new StringableProcessor(),
new ThrowableProcessor(),
new FallbackProcessor(),
];
return $self;
}

/**
* Copy the current object and add Object Processors before existing ones.
*/
public function withObjectProcessors(ObjectProcessor ...$processors): self
{
$clone = clone $this;
$clone->processors = \array_merge(\array_values($processors), $clone->processors);
return $clone;
}

public function __invoke(mixed $value): mixed
{
if (\is_resource($value)) {
return \get_resource_type($value) . ' resource';
}

if (\is_array($value)) {
foreach ($value as &$v) {
$v = $this($v);
}
}
Comment on lines +63 to +67
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add explicit return after array processing.

Array processing modifies $value by reference but doesn't explicitly return. Execution falls through to line 51 (object check fails) and eventually returns on line 59. While this works, it's confusing and wastes cycles checking if an array is an object.

Apply this diff:

         if (\is_array($value)) {
             foreach ($value as &$v) {
                 $v = $this($v);
             }
+            return $value;
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (\is_array($value)) {
foreach ($value as &$v) {
$v = $this($v);
}
}
if (\is_array($value)) {
foreach ($value as &$v) {
$v = $this($v);
}
return $value;
}
🧰 Tools
🪛 GitHub Actions: static analysis

[error] 46-46: Psalm: MixedAssignment: Unable to determine the type that $v is being assigned to


[error] 47-47: Psalm: MixedAssignment: Unable to determine the type that $v is being assigned to

🤖 Prompt for AI Agents
In src/Context/DefaultProcessor.php around lines 45 to 49, the code processes
arrays by reference but does not explicitly return after transforming each
element, causing execution to continue and perform an unnecessary object check;
add an immediate return of the processed $value right after the foreach block
(i.e., return $value;) so the method exits once array handling is complete.


if (\is_object($value)) {
foreach ($this->processors as $processor) {
if ($processor->canProcess($value)) {
return $processor->process($value, $this);
}
}
}

return $value;
}
}
26 changes: 26 additions & 0 deletions src/Context/ObjectProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace RoadRunner\PsrLogger\Context;

/**
* Converts an object into a scalar or an arra for serializable logger context.
*
* @template T
*
* @internal
*/
interface ObjectProcessor
{
/**
* Check if this processor can handle the given value.
*/
public function canProcess(object $value): bool;

/**
* @param T $value
* @param callable(mixed): mixed $processor Function to process nested object values
*/
public function process(object $value, callable $processor): mixed;
}
30 changes: 30 additions & 0 deletions src/Context/ObjectProcessor/DateTimeProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace RoadRunner\PsrLogger\Context\ObjectProcessor;

use RoadRunner\PsrLogger\Context\ObjectProcessor;

/**
* Processor for DateTime objects.
*
* Converts DateTime and DateTimeImmutable objects to ISO 8601 format
* for consistent structured logging.
*
* @implements ObjectProcessor<\DateTimeInterface>
*
* @internal
*/
final class DateTimeProcessor implements ObjectProcessor
{
public function canProcess(object $value): bool
{
return $value instanceof \DateTimeInterface;
}

public function process(object $value, callable $processor): mixed
{
return $value->format(\DateTimeInterface::ATOM);
}
}
36 changes: 36 additions & 0 deletions src/Context/ObjectProcessor/FallbackProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);

namespace RoadRunner\PsrLogger\Context\ObjectProcessor;

use RoadRunner\PsrLogger\Context\ObjectProcessor;

/**
* Fallback processor for unknown objects.
*
* @internal
*
* @implements ObjectProcessor<object>
*/
final class FallbackProcessor implements ObjectProcessor
{
public function canProcess(object $value): bool
{
return true;
}

public function process(object $value, callable $processor): array
{
$result = ['@class' => $value::class] + \get_object_vars($value);
foreach ($result as $k => &$v) {
if ($v === $value) {
unset($result[$k]);
}

$v = $processor($v);
}
Comment on lines +26 to +33
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Skip processing self-references after unsetting them.

Line 27 tries to drop self-referential properties, but because the loop lacks a continue, the same reference is immediately re-processed on Line 31, which reintroduces the entry and recurses indefinitely. Add a continue; right after the unset() so self-references stay removed and the recursion guard actually works.

Apply this diff:

         foreach ($result as $k => &$v) {
             if ($v === $value) {
                 unset($result[$k]);
+                continue;
             }

             $v = $processor($v);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$result = ['@class' => $value::class] + \get_object_vars($value);
foreach ($result as $k => &$v) {
if ($v === $value) {
unset($result[$k]);
}
$v = $processor($v);
}
$result = ['@class' => $value::class] + \get_object_vars($value);
foreach ($result as $k => &$v) {
if ($v === $value) {
unset($result[$k]);
continue;
}
$v = $processor($v);
}
🧰 Tools
🪛 GitHub Actions: static analysis

[error] 26-26: MixedAssignment: Unable to determine the type that $v is being assigned to


[error] 31-31: MixedAssignment: Unable to determine the type that $v is being assigned to

🤖 Prompt for AI Agents
In src/Internal/ContextProcessor/FallbackProcessor.php around lines 25 to 32,
the foreach unsets self-referential properties but then continues to process the
same referenced variable, which re-adds it and causes infinite recursion; after
unsetting the entry add a `continue;` so the loop skips further processing of
that item and the self-reference remains removed, preserving the recursion
guard.


return $result;
Comment on lines +27 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Prevent re-processing after unsetting self-references; avoid by-ref pitfalls.

After unset($result[$k]), the loop immediately reuses $v, which can reintroduce the value and risks runaway recursion. Also, iterating by reference leaks $v beyond the loop. Refactor to key-based writes (no &$v), and skip once unset.

Apply:

-        foreach ($result as $k => &$v) {
-            if ($v === $value) {
-                unset($result[$k]);
-            }
-
-            $v = $processor($v);
-        }
+        foreach (\array_keys($result) as $k) {
+            $v = $result[$k];
+            if ($v === $value) {
+                unset($result[$k]);
+                continue;
+            }
+            $result[$k] = $processor($v);
+        }

This removes the reference, skips removed entries, and satisfies static analyzers.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
foreach ($result as $k => &$v) {
if ($v === $value) {
unset($result[$k]);
}
$v = $processor($v);
}
return $result;
foreach (\array_keys($result) as $k) {
$v = $result[$k];
if ($v === $value) {
unset($result[$k]);
continue;
}
$result[$k] = $processor($v);
}
return $result;
🧰 Tools
🪛 GitHub Actions: static analysis

[error] 26-26: MixedAssignment: Unable to determine the type that $v is being assigned to


[error] 31-31: MixedAssignment: Unable to determine the type that $v is being assigned to

🤖 Prompt for AI Agents
In src/Internal/ContextProcessor/FallbackProcessor.php around lines 26 to 34,
the foreach uses a by-reference loop and reuses $v after an unset which can
reintroduce values and leak the reference; change to a key-based loop (foreach
($result as $k => $v)) so you can unset($result[$k]) and immediately continue
when $v === $value, and then assign the processed result back to $result[$k]
(e.g. $result[$k] = $processor($v)); this removes the reference usage, prevents
re-processing of removed entries, and avoids leaking $v beyond the loop.

}
}
27 changes: 27 additions & 0 deletions src/Context/ObjectProcessor/StringableProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace RoadRunner\PsrLogger\Context\ObjectProcessor;

use RoadRunner\PsrLogger\Context\ObjectProcessor;

/**
* Converts Stringable objects to their string representation.
*
* @internal
*
* @implements ObjectProcessor<\Stringable>
*/
final class StringableProcessor implements ObjectProcessor
{
public function canProcess(object $value): bool
{
return $value instanceof \Stringable;
}

public function process(object $value, callable $processor): mixed
{
return (string) $value;
}
}
35 changes: 35 additions & 0 deletions src/Context/ObjectProcessor/ThrowableProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace RoadRunner\PsrLogger\Context\ObjectProcessor;

use RoadRunner\PsrLogger\Context\ObjectProcessor;

/**
* Converts exceptions to structured data containing class, message,
* code, file, line, and stack trace information.
*
* @internal
*
* @implements ObjectProcessor<\Throwable>
*/
final class ThrowableProcessor implements ObjectProcessor
{
public function canProcess(object $value): bool
{
return $value instanceof \Throwable;
}

public function process(object $value, callable $processor): array
{
return [
'class' => \get_class($value),
'message' => $value->getMessage(),
'code' => $value->getCode(),
'file' => $value->getFile(),
'line' => $value->getLine(),
'trace' => $value->getTraceAsString(),
];
}
}
21 changes: 13 additions & 8 deletions src/RpcLogger.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@
use Psr\Log\InvalidArgumentException as PsrInvalidArgumentException;
use RoadRunner\Logger\Logger as AppLogger;
use RoadRunner\Logger\LogLevel;
use RoadRunner\PsrLogger\Context\DefaultProcessor;

class RpcLogger implements LoggerInterface
{
use LoggerTrait;

private readonly AppLogger $logger;
private readonly \Closure $objectProcessor;

public function __construct(AppLogger $logger)
public function __construct(AppLogger $logger, ?callable $processor = null)
{
$this->logger = $logger;
$this->objectProcessor = ($processor ?? DefaultProcessor::createDefault())(...);
}

/**
Expand All @@ -32,23 +35,25 @@ public function __construct(AppLogger $logger)
public function log($level, \Stringable|string $message, array $context = []): void
{
$normalizedLevel = \strtolower(match (true) {
\is_string($level),
\is_string($level) => $level,
$level instanceof \Stringable => (string) $level,
$level instanceof \BackedEnum => (string) $level->value,
$level instanceof LogLevel => $level->name,
default => throw new PsrInvalidArgumentException('Invalid log level type provided.'),
});

/** @var array<string, mixed> $context */
// Process context data for structured logging using the processor manager
$processedContext = ($this->objectProcessor)($context);

Comment on lines +49 to +51
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Normalize context keys to strings and cast message once.

Fix Psalm MixedAssignment/MixedArgument and ensure AppLogger gets array<string,mixed>.

-        // Process context data for structured logging using the processor manager
-        $processedContext = ($this->objectProcessor)($context);
+        // Process context data for structured logging
+        /** @var array<array-key, mixed> $tmpContext */
+        $tmpContext = ($this->objectProcessor)($context);
+        /** @var array<string, mixed> $processedContext */
+        $processedContext = [];
+        foreach ($tmpContext as $k => $v) {
+            $processedContext[(string) $k] = $v;
+        }
+        $message = (string) $message;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Process context data for structured logging using the processor manager
$processedContext = ($this->objectProcessor)($context);
// Process context data for structured logging
/** @var array<array-key, mixed> $tmpContext */
$tmpContext = ($this->objectProcessor)($context);
/** @var array<string, mixed> $processedContext */
$processedContext = [];
foreach ($tmpContext as $k => $v) {
$processedContext[(string) $k] = $v;
}
$message = (string) $message;
🧰 Tools
🪛 GitHub Actions: static analysis

[error] 46-46: Psalm: MixedAssignment: Unable to determine the type that $processedContext is being assigned to

🤖 Prompt for AI Agents
In src/RpcLogger.php around lines 45-47, the processed context currently may
have non-string keys and mixed types which triggers Psalm
MixedAssignment/MixedArgument; cast the incoming $message to string once
up-front (e.g. $message = (string)$message) before any logging, then after
running the object processor normalize the context keys to strings and ensure
the value is an array<string,mixed> (use array_combine(array_map('strval',
array_keys($processedContext)), array_values($processedContext)) or equivalent
and fall back to an empty array on failure), and add a local docblock/type
assertion like /** @var array<string,mixed> $processedContext */ before passing
it to AppLogger so Psalm and the logger receive a proper array<string,mixed>.

match ($normalizedLevel) {
PsrLogLevel::EMERGENCY,
PsrLogLevel::ALERT,
PsrLogLevel::CRITICAL,
PsrLogLevel::ERROR => $this->logger->error($message, $context),
PsrLogLevel::WARNING => $this->logger->warning($message, $context),
PsrLogLevel::NOTICE, PsrLogLevel::INFO => $this->logger->info((string) $message, $context),
'log' => $this->logger->log((string) $message, $context),
PsrLogLevel::DEBUG => $this->logger->debug($message, $context),
PsrLogLevel::ERROR => $this->logger->error($message, $processedContext),
PsrLogLevel::WARNING => $this->logger->warning($message, $processedContext),
PsrLogLevel::NOTICE, PsrLogLevel::INFO => $this->logger->info((string) $message, $processedContext),
'log' => $this->logger->log((string) $message, $processedContext),
PsrLogLevel::DEBUG => $this->logger->debug($message, $processedContext),
Comment on lines 43 to +60
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify: is custom level 'log' intentionally supported as an input level?

PSR-3 levels don’t include 'log'. Keeping this branch is fine if RoadRunner’s Logger explicitly accepts a 'log' level alias; otherwise consider removing it to avoid accepting invalid levels.


🌐 Web query:

Does RoadRunner\Logger\Logger accept a 'log' level (as input) distinct from PSR-3 levels, or is 'log' only a generic method name? If not supported as a level, confirm recommended handling for unknown levels.

💡 Result:

RoadRunner\Logger\Logger does not accept a 'log' level as a distinct log level; 'log' is only the generic method name defined by PSR-3 for submitting logs at any severity. Only the standard PSR-3 levels (emergency, alert, critical, error, warning, notice, info, debug) are accepted; custom or unknown levels, such as a literal 'log' level, are not supported[6][4][10].

Supporting details:

  • PSR-3 Compliance: RoadRunner’s logging is designed to be PSR-3 compliant[1][5]. This means it follows the Psr\Log\LoggerInterface which defines the log($level, $message, array $context = []) method. The method named log is generic and allows you to specify any compliant log level as the first argument, but the levels themselves must conform to the eight PSR-3 levels[6].
  • Allowed Levels: Official RoadRunner documentation and its logger plugin describe supported levels for configuration and filtering as debug, info, warn, error, panic[4][10]. However, for PHP userland and the app-logger RPC plugin, only the standard PSR-3 levels are valid as input.
  • Purpose of log Method: The log method’s purpose is to allow for passing the level dynamically—it is not a substitute or equivalent for a 'log' level[6].
  • Handling Unknown Levels: The PSR-3 specification and common implementations (as seen in Monolog and others) require that if an unknown or unsupported level is provided, an InvalidArgumentException must be thrown[3][6]. Your implementation should therefore either:
    • Map only valid PSR-3 levels to the logger.
    • Throw an exception or otherwise reject unknown (non-PSR-3) levels.

Summary of best practice:
Use only the officially recognized PSR-3 log levels (emergency, alert, critical, error, warning, notice, info, debug). If an unknown level, such as 'log', is provided, reject it with a suitable exception (e.g., InvalidArgumentException) as per PSR-3 guidelines[3][6].

Additional context:

  • RoadRunner’s internal logging and config also allow for levels like 'panic' in addition to PSR-3, but these are for internal server-side config/transport and not exposed as input levels to the application-side logger[4][10].
  • If you need custom levels, you’ll need to map them to one of the supported levels before passing to RoadRunner\Logger\Logger, or log them as context data within a permitted level.

Citations:


Remove unsupported ‘log’ level and enforce PSR-3 compliance
The ‘log’ branch currently treats “log” as if it were a valid PSR-3 level, but RoadRunner’s PHP logger only recognizes the eight standard PSR-3 levels (emergency, alert, critical, error, warning, notice, info, debug). The log() method itself is a generic entry point, not a distinct level. Accepting “log” silently risks masking invalid input.

• Remove the 'log' => $this->logger->log((string) $message, $processedContext), arm from the match statement.
• Add a default arm that throws an InvalidArgumentException for any unrecognized level, e.g.:

default => throw new \Psr\Log\InvalidArgumentException(
    sprintf('Unsupported log level "%s". Valid levels: emergency, alert, critical, error, warning, notice, info, debug.', $normalizedLevel)
),

• Update or add tests to assert that providing an invalid level (including “log”) triggers the exception.

This change ensures that only valid PSR-3 levels are accepted and that any unknown level is explicitly rejected, in line with PSR-3 guidelines.

🤖 Prompt for AI Agents
In src/RpcLogger.php around lines 48 to 56, remove the 'log' match arm (which
treats "log" as a level) and replace the match with one that only handles the
eight PSR-3 levels; add a default arm that throws a
\Psr\Log\InvalidArgumentException with a message like 'Unsupported log level
"%s". Valid levels: emergency, alert, critical, error, warning, notice, info,
debug.' (using sprintf to include the invalid level); and add/update tests to
assert that passing an invalid level (including "log") causes this exception to
be thrown.

default => throw new PsrInvalidArgumentException("Invalid log level `$normalizedLevel` provided."),
Comment on lines +56 to 61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Potential TypeError: cast Stringable messages to string for all logger calls

AppLogger methods likely accept string messages. Passing a \Stringable without casting can throw a TypeError with strict types. Some branches already cast; others don’t.

Apply this diff to consistently cast:

-            PsrLogLevel::ERROR => $this->logger->error($message, $processedContext),
-            PsrLogLevel::WARNING => $this->logger->warning($message, $processedContext),
-            PsrLogLevel::NOTICE, PsrLogLevel::INFO => $this->logger->info((string) $message, $processedContext),
-            'log' => $this->logger->log((string) $message, $processedContext),
-            PsrLogLevel::DEBUG => $this->logger->debug($message, $processedContext),
+            PsrLogLevel::ERROR => $this->logger->error((string) $message, $processedContext),
+            PsrLogLevel::WARNING => $this->logger->warning((string) $message, $processedContext),
+            PsrLogLevel::NOTICE, PsrLogLevel::INFO => $this->logger->info((string) $message, $processedContext),
+            'log' => $this->logger->log((string) $message, $processedContext),
+            PsrLogLevel::DEBUG => $this->logger->debug((string) $message, $processedContext),

Optional follow-up: cast once after processing context to reduce repetition.

         // Process context data for structured logging using the processor manager
         $processedContext = $this->contextProcessor->processContext($context);
+        $message = (string) $message;

Then remove per-branch casts.

🤖 Prompt for AI Agents
In src/RpcLogger.php around lines 52 to 57, some logger calls pass a \Stringable
$message without casting which can cause a TypeError under strict types; ensure
the message is always a string by casting before use — either replace each
logger call to use (string) $message consistently in every branch or better:
after processing the context, assign $msg = (string) $message and use $msg in
all switch branches (then remove any per-branch casts).

};
}
Expand Down
91 changes: 91 additions & 0 deletions tests/Unit/Context/DefaultProcessorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

namespace Context;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix the namespace declaration.

The namespace Context is incomplete and doesn't follow PSR-4 standards. For a test located at tests/Unit/Context/DefaultProcessorTest.php, the namespace should be Tests\Unit\Context.

Apply this diff:

-namespace Context;
+namespace Tests\Unit\Context;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
namespace Context;
namespace Tests\Unit\Context;
🤖 Prompt for AI Agents
In tests/Unit/Context/DefaultProcessorTest.php around line 5, the namespace is
declared as "Context" which doesn't follow PSR-4 for this test location; change
the namespace declaration to "Tests\Unit\Context" so the class matches the PSR-4
path. Update the namespace line accordingly and ensure any use/imports or class
references within the file remain correct after the namespace change.


use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use RoadRunner\PsrLogger\Context\DefaultProcessor;
use RoadRunner\PsrLogger\Context\ObjectProcessor\BuiltInTypeProcessor;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove unused import.

BuiltInTypeProcessor is imported but never used and doesn't exist in the codebase.

Apply this diff:

 use RoadRunner\PsrLogger\Context\DefaultProcessor;
-use RoadRunner\PsrLogger\Context\ObjectProcessor\BuiltInTypeProcessor;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
use RoadRunner\PsrLogger\Context\ObjectProcessor\BuiltInTypeProcessor;
use RoadRunner\PsrLogger\Context\DefaultProcessor;
🤖 Prompt for AI Agents
In tests/Unit/Context/DefaultProcessorTest.php around line 11, the import "use
RoadRunner\PsrLogger\Context\ObjectProcessor\BuiltInTypeProcessor;" is unused
and refers to a non-existent class; remove this import line from the file so
only required imports remain, then run tests to ensure no other references to
BuiltInTypeProcessor exist.


#[CoversClass(DefaultProcessor::class)]
class DefaultProcessorTest extends TestCase
{
private DefaultProcessor $processor;

public static function builtInTypeValuesProvider(): array
{
return [
'string' => ['test string', 'test string'],
'integer' => [42, 42],
'float' => [3.14, 3.14],
'boolean true' => [true, true],
'boolean false' => [false, false],
'null' => [null, null],
'empty array' => [[], []],
'simple array' => [[1, 2, 3], [1, 2, 3]],
'associative array' => [['key' => 'value'], ['key' => 'value']],
'resource' => [\fopen('php://memory', 'r'), 'stream resource'],
];
}

#[DataProvider('builtInTypeValuesProvider')]
public function testCanProcessBuiltInTypes(mixed $value, mixed $expected): void
{
$this->assertSame($expected, ($this->processor)($value));
}

public function testProcessNull(): void
{
$recursiveProcessor = static fn($v) => $v;
$result = ($this->processor)(null, $recursiveProcessor);
$this->assertNull($result);
}

public function testProcessScalarValues(): void
{
$values = ['test string', 42, 3.14, true, false];
$recursiveProcessor = static fn($v) => $v;

foreach ($values as $value) {
$result = ($this->processor)($value, $recursiveProcessor);
$this->assertSame($value, $result);
}
}
Comment on lines +40 to +56
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect method signatures in tests.

DefaultProcessor::__invoke() accepts only one parameter (mixed $value), but lines 43 and 53 pass a second $recursiveProcessor parameter. These tests will fail.

Apply this diff:

     public function testProcessNull(): void
     {
-        $recursiveProcessor = static fn($v) => $v;
-        $result = ($this->processor)(null, $recursiveProcessor);
+        $result = ($this->processor)(null);
         $this->assertNull($result);
     }

     public function testProcessScalarValues(): void
     {
         $values = ['test string', 42, 3.14, true, false];
-        $recursiveProcessor = static fn($v) => $v;

         foreach ($values as $value) {
-            $result = ($this->processor)($value, $recursiveProcessor);
+            $result = ($this->processor)($value);
             $this->assertSame($value, $result);
         }
     }
🤖 Prompt for AI Agents
In tests/Unit/Context/DefaultProcessorTest.php around lines 40 to 56, the test
calls to ($this->processor) pass a second $recursiveProcessor argument but
DefaultProcessor::__invoke() only accepts one parameter; remove the second
argument from both calls so the invocation is ($this->processor)(null) and
($this->processor)($value), and delete or ignore the unused $recursiveProcessor
variable in those tests.


public function testProcessSimpleArray(): void
{
$array = [1, 2, 'three', true];
$recursiveProcessor = static fn($v) => $v; // Identity function for simple values

$result = ($this->processor)($array, $recursiveProcessor);

$this->assertSame([1, 2, 'three', true], $result);
}
Comment on lines +58 to +66
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix incorrect method signature in testProcessSimpleArray.

Same issue: line 63 passes two parameters but __invoke() only accepts one.

Apply this diff:

     public function testProcessSimpleArray(): void
     {
         $array = [1, 2, 'three', true];
-        $recursiveProcessor = static fn($v) => $v; // Identity function for simple values
-
-        $result = ($this->processor)($array, $recursiveProcessor);
+        $result = ($this->processor)($array);

         $this->assertSame([1, 2, 'three', true], $result);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function testProcessSimpleArray(): void
{
$array = [1, 2, 'three', true];
$recursiveProcessor = static fn($v) => $v; // Identity function for simple values
$result = ($this->processor)($array, $recursiveProcessor);
$this->assertSame([1, 2, 'three', true], $result);
}
public function testProcessSimpleArray(): void
{
$array = [1, 2, 'three', true];
$result = ($this->processor)($array);
$this->assertSame([1, 2, 'three', true], $result);
}
🤖 Prompt for AI Agents
In tests/Unit/Context/DefaultProcessorTest.php around lines 58 to 66, the test
calls ($this->processor)($array, $recursiveProcessor) even though the
processor's __invoke() accepts only one argument; remove the second parameter
and call the processor with a single argument — replace the invocation with
($this->processor)($array) so the test matches the method signature and still
asserts the identity behavior.


public function testProcessNestedArray(): void
{
$array = [
'level1' => [
'level2' => [
'value' => 'deep',
],
],
];

$result = ($this->processor)($array);

$this->assertArrayHasKey('level1', $result);
$this->assertIsArray($result['level1']);
$this->assertArrayHasKey('level2', $result['level1']);
$this->assertIsArray($result['level1']['level2']);
$this->assertSame('deep', $result['level1']['level2']['value']);
}

protected function setUp(): void
{
$this->processor = new DefaultProcessor();
}
}
Loading
Loading