diff --git a/src/Collector/ContainerInterfaceProxy.php b/src/Collector/ContainerInterfaceProxy.php index 66bd09cb..2c53f06b 100644 --- a/src/Collector/ContainerInterfaceProxy.php +++ b/src/Collector/ContainerInterfaceProxy.php @@ -9,6 +9,7 @@ use Psr\Container\ContainerInterface; use Yiisoft\Proxy\ProxyManager; use Yiisoft\Proxy\ProxyTrait; +use Yiisoft\Yii\Debug\ProxyDecoratedCalls; use function is_callable; use function is_object; @@ -16,6 +17,7 @@ final class ContainerInterfaceProxy implements ContainerInterface { + use ProxyDecoratedCalls; use ProxyLogTrait; use ProxyTrait; @@ -30,8 +32,10 @@ final class ContainerInterfaceProxy implements ContainerInterface private array $serviceProxy = []; - public function __construct(protected ContainerInterface $container, ContainerProxyConfig $config) - { + public function __construct( + protected ContainerInterface $decorated, + ContainerProxyConfig $config, + ) { $this->config = $config; $this->proxyManager = new ProxyManager($this->config->getProxyCachePath()); } @@ -53,7 +57,7 @@ public function get($id): mixed } catch (ContainerExceptionInterface $e) { $this->repeatError($e); } finally { - $this->logProxy(ContainerInterface::class, $this->container, 'get', [$id], $instance, $timeStart); + $this->logProxy(ContainerInterface::class, $this->decorated, 'get', [$id], $instance, $timeStart); } if ( @@ -79,7 +83,7 @@ private function getInstance(string $id): mixed return $this; } - return $this->container->get($id); + return $this->decorated->get($id); } private function isDecorated(string $service): bool @@ -196,11 +200,11 @@ public function has($id): bool $result = null; try { - $result = $this->container->has($id); + $result = $this->decorated->has($id); } catch (ContainerExceptionInterface $e) { $this->repeatError($e); } finally { - $this->logProxy(ContainerInterface::class, $this->container, 'has', [$id], $result, $timeStart); + $this->logProxy(ContainerInterface::class, $this->decorated, 'has', [$id], $result, $timeStart); } return (bool)$result; diff --git a/src/Collector/EventDispatcherInterfaceProxy.php b/src/Collector/EventDispatcherInterfaceProxy.php index 26061acd..5084a1d2 100644 --- a/src/Collector/EventDispatcherInterfaceProxy.php +++ b/src/Collector/EventDispatcherInterfaceProxy.php @@ -5,11 +5,14 @@ namespace Yiisoft\Yii\Debug\Collector; use Psr\EventDispatcher\EventDispatcherInterface; +use Yiisoft\Yii\Debug\ProxyDecoratedCalls; final class EventDispatcherInterfaceProxy implements EventDispatcherInterface { + use ProxyDecoratedCalls; + public function __construct( - private readonly EventDispatcherInterface $dispatcher, + private readonly EventDispatcherInterface $decorated, private readonly EventCollector $collector ) { } @@ -21,6 +24,6 @@ public function dispatch(object $event): object $this->collector->collect($event, $callStack['file'] . ':' . $callStack['line']); - return $this->dispatcher->dispatch($event); + return $this->decorated->dispatch($event); } } diff --git a/src/Collector/HttpClientInterfaceProxy.php b/src/Collector/HttpClientInterfaceProxy.php index 158180a7..a23bfb17 100644 --- a/src/Collector/HttpClientInterfaceProxy.php +++ b/src/Collector/HttpClientInterfaceProxy.php @@ -7,9 +7,12 @@ use Psr\Http\Client\ClientInterface; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; +use Yiisoft\Yii\Debug\ProxyDecoratedCalls; final class HttpClientInterfaceProxy implements ClientInterface { + use ProxyDecoratedCalls; + public function __construct( private readonly ClientInterface $decorated, private readonly HttpClientCollector $collector diff --git a/src/Collector/LoggerInterfaceProxy.php b/src/Collector/LoggerInterfaceProxy.php index 7576e33f..a3a72f16 100644 --- a/src/Collector/LoggerInterfaceProxy.php +++ b/src/Collector/LoggerInterfaceProxy.php @@ -7,11 +7,14 @@ use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; use Stringable; +use Yiisoft\Yii\Debug\ProxyDecoratedCalls; final class LoggerInterfaceProxy implements LoggerInterface { + use ProxyDecoratedCalls; + public function __construct( - private readonly LoggerInterface $logger, + private readonly LoggerInterface $decorated, private readonly LogCollector $collector ) { } @@ -26,7 +29,7 @@ public function emergency(string|Stringable $message, array $context = []): void $context, $callStack['file'] . ':' . $callStack['line'] ); - $this->logger->emergency($message, $context); + $this->decorated->emergency($message, $context); } public function alert(string|Stringable $message, array $context = []): void @@ -34,7 +37,7 @@ public function alert(string|Stringable $message, array $context = []): void $callStack = $this->getCallStack(); $this->collector->collect(LogLevel::ALERT, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->alert($message, $context); + $this->decorated->alert($message, $context); } public function critical(string|Stringable $message, array $context = []): void @@ -47,7 +50,7 @@ public function critical(string|Stringable $message, array $context = []): void $context, $callStack['file'] . ':' . $callStack['line'] ); - $this->logger->critical($message, $context); + $this->decorated->critical($message, $context); } public function error(string|Stringable $message, array $context = []): void @@ -55,7 +58,7 @@ public function error(string|Stringable $message, array $context = []): void $callStack = $this->getCallStack(); $this->collector->collect(LogLevel::ERROR, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->error($message, $context); + $this->decorated->error($message, $context); } public function warning(string|Stringable $message, array $context = []): void @@ -63,7 +66,7 @@ public function warning(string|Stringable $message, array $context = []): void $callStack = $this->getCallStack(); $this->collector->collect(LogLevel::WARNING, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->warning($message, $context); + $this->decorated->warning($message, $context); } public function notice(string|Stringable $message, array $context = []): void @@ -71,7 +74,7 @@ public function notice(string|Stringable $message, array $context = []): void $callStack = $this->getCallStack(); $this->collector->collect(LogLevel::NOTICE, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->notice($message, $context); + $this->decorated->notice($message, $context); } public function info(string|Stringable $message, array $context = []): void @@ -79,7 +82,7 @@ public function info(string|Stringable $message, array $context = []): void $callStack = $this->getCallStack(); $this->collector->collect(LogLevel::INFO, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->info($message, $context); + $this->decorated->info($message, $context); } public function debug(string|Stringable $message, array $context = []): void @@ -87,7 +90,7 @@ public function debug(string|Stringable $message, array $context = []): void $callStack = $this->getCallStack(); $this->collector->collect(LogLevel::DEBUG, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->debug($message, $context); + $this->decorated->debug($message, $context); } public function log(mixed $level, string|Stringable $message, array $context = []): void @@ -95,7 +98,7 @@ public function log(mixed $level, string|Stringable $message, array $context = [ $callStack = $this->getCallStack(); $this->collector->collect($level, $message, $context, $callStack['file'] . ':' . $callStack['line']); - $this->logger->log($level, $message, $context); + $this->decorated->log($level, $message, $context); } /** diff --git a/src/Collector/VarDumperHandlerInterfaceProxy.php b/src/Collector/VarDumperHandlerInterfaceProxy.php index 46a5ed51..46fe954e 100644 --- a/src/Collector/VarDumperHandlerInterfaceProxy.php +++ b/src/Collector/VarDumperHandlerInterfaceProxy.php @@ -5,9 +5,12 @@ namespace Yiisoft\Yii\Debug\Collector; use Yiisoft\VarDumper\HandlerInterface; +use Yiisoft\Yii\Debug\ProxyDecoratedCalls; final class VarDumperHandlerInterfaceProxy implements HandlerInterface { + use ProxyDecoratedCalls; + public function __construct( private readonly HandlerInterface $decorated, private readonly VarDumperCollector $collector, diff --git a/src/ProxyDecoratedCalls.php b/src/ProxyDecoratedCalls.php new file mode 100644 index 00000000..57ce12bb --- /dev/null +++ b/src/ProxyDecoratedCalls.php @@ -0,0 +1,26 @@ +decorated->$name = $value; + } + + public function __get(string $name) + { + return $this->decorated->$name; + } + + public function __call(string $name, array $arguments) + { + return $this->decorated->$name(...$arguments); + } +} diff --git a/tests/Unit/Collector/ContainerInterfaceProxyTest.php b/tests/Unit/Collector/ContainerInterfaceProxyTest.php index 26cdc590..c5b3a87c 100644 --- a/tests/Unit/Collector/ContainerInterfaceProxyTest.php +++ b/tests/Unit/Collector/ContainerInterfaceProxyTest.php @@ -349,6 +349,41 @@ public function test2(): void $this->assertSame('from tests', $implementation->getName()); } + public function testProxyDecoratedCall(): void + { + $container = new class () implements ContainerInterface { + public $var = null; + + public function getProxiedCall(): string + { + return 'ok'; + } + + public function setProxiedCall($args): mixed + { + return $args; + } + + public function get($id) + { + throw new class () extends Exception implements ContainerExceptionInterface { + }; + } + + public function has($id): bool + { + throw new class () extends Exception implements ContainerExceptionInterface { + }; + } + }; + $proxy = new ContainerInterfaceProxy($container, new ContainerProxyConfig()); + + $this->assertEquals('ok', $proxy->getProxiedCall()); + $this->assertEquals($args = [1, new stdClass(), 'string'], $proxy->setProxiedCall($args)); + $proxy->var = '123'; + $this->assertEquals('123', $proxy->var); + } + private function createConfig(int $logLevel = ContainerInterfaceProxy::LOG_ARGUMENTS): ContainerProxyConfig { return new ContainerProxyConfig( diff --git a/tests/Unit/Collector/EventDispatcherInterfaceProxyTest.php b/tests/Unit/Collector/EventDispatcherInterfaceProxyTest.php index 0f198909..4bedf0d9 100644 --- a/tests/Unit/Collector/EventDispatcherInterfaceProxyTest.php +++ b/tests/Unit/Collector/EventDispatcherInterfaceProxyTest.php @@ -32,4 +32,32 @@ public function testDispatch(): void $this->assertSame($event, $newEvent); $this->assertCount(1, $collector->getCollected()); } + + public function testProxyDecoratedCall(): void + { + $dispatcher = new class () implements EventDispatcherInterface { + public $var = null; + + public function getProxiedCall(): string + { + return 'ok'; + } + + public function setProxiedCall($args): mixed + { + return $args; + } + + public function dispatch(object $event) + { + } + }; + $collector = new EventCollector(new TimelineCollector()); + $proxy = new EventDispatcherInterfaceProxy($dispatcher, $collector); + + $this->assertEquals('ok', $proxy->getProxiedCall()); + $this->assertEquals($args = [1, new stdClass(), 'string'], $proxy->setProxiedCall($args)); + $proxy->var = '123'; + $this->assertEquals('123', $proxy->var); + } } diff --git a/tests/Unit/Collector/HttpClientInterfaceProxyTest.php b/tests/Unit/Collector/HttpClientInterfaceProxyTest.php index e6b1e96e..773c0537 100644 --- a/tests/Unit/Collector/HttpClientInterfaceProxyTest.php +++ b/tests/Unit/Collector/HttpClientInterfaceProxyTest.php @@ -8,6 +8,9 @@ use GuzzleHttp\Psr7\Response; use PHPUnit\Framework\TestCase; use Psr\Http\Client\ClientInterface; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; +use stdClass; use Yiisoft\Yii\Debug\Collector\HttpClientCollector; use Yiisoft\Yii\Debug\Collector\HttpClientInterfaceProxy; use Yiisoft\Yii\Debug\Collector\TimelineCollector; @@ -34,4 +37,32 @@ public function testSendRequest(): void $this->assertSame($newResponse, $response); $this->assertCount(1, $collector->getCollected()); } + + public function testProxyDecoratedCall(): void + { + $httpClient = new class () implements ClientInterface { + public $var = null; + + public function getProxiedCall(): string + { + return 'ok'; + } + + public function setProxiedCall($args): mixed + { + return $args; + } + + public function sendRequest(RequestInterface $request): ResponseInterface + { + } + }; + $collector = new HttpClientCollector(new TimelineCollector()); + $proxy = new HttpClientInterfaceProxy($httpClient, $collector); + + $this->assertEquals('ok', $proxy->getProxiedCall()); + $this->assertEquals($args = [1, new stdClass(), 'string'], $proxy->setProxiedCall($args)); + $proxy->var = '123'; + $this->assertEquals('123', $proxy->var); + } } diff --git a/tests/Unit/Collector/LoggerInterfaceProxyTest.php b/tests/Unit/Collector/LoggerInterfaceProxyTest.php index 76763b92..fdcf0bf6 100644 --- a/tests/Unit/Collector/LoggerInterfaceProxyTest.php +++ b/tests/Unit/Collector/LoggerInterfaceProxyTest.php @@ -7,7 +7,9 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; +use Psr\Log\LoggerTrait; use Psr\Log\LogLevel; +use stdClass; use Yiisoft\Yii\Debug\Collector\LogCollector; use Yiisoft\Yii\Debug\Collector\LoggerInterfaceProxy; @@ -17,11 +19,14 @@ final class LoggerInterfaceProxyTest extends TestCase public function testLogMethods(string $method, string $level, string $message, array $context): void { $logger = $this->createMock(LoggerInterface::class); + $logger + ->expects($this->once()) + ->method($method); $collector = $this->createMock(LogCollector::class); $collector ->expects($this->once()) ->method('collect') - ->with($level, $message, $context, __FILE__ . ':27'); + ->with($level, $message, $context, __FILE__ . ':32'); $proxy = new LoggerInterfaceProxy($logger, $collector); $proxy->$method($message, $context); @@ -31,11 +36,14 @@ public function testLogMethods(string $method, string $level, string $message, a public function testMethodLog($method, string $level, string $message, array $context): void { $logger = $this->createMock(LoggerInterface::class); + $logger + ->expects($this->once()) + ->method('log'); $collector = $this->createMock(LogCollector::class); $collector ->expects($this->once()) ->method('collect') - ->with($level, $message, $context, __FILE__ . ':41'); + ->with($level, $message, $context, __FILE__ . ':49'); $proxy = new LoggerInterfaceProxy($logger, $collector); $proxy->log($level, $message, $context); @@ -52,4 +60,34 @@ public static function logMethodsProvider(): iterable yield 'info' => ['info', LogLevel::INFO, 'message', ['context']]; yield 'warning' => ['warning', LogLevel::WARNING, 'message', ['context']]; } + + public function testProxyDecoratedCall(): void + { + $logger = new class () implements LoggerInterface { + use LoggerTrait; + + public $var = null; + + public function getProxiedCall(): string + { + return 'ok'; + } + + public function setProxiedCall($args): mixed + { + return $args; + } + + public function log($level, \Stringable|string $message, array $context = []): void + { + } + }; + $collector = $this->createMock(LogCollector::class); + $proxy = new LoggerInterfaceProxy($logger, $collector); + + $this->assertEquals('ok', $proxy->getProxiedCall()); + $this->assertEquals($args = [1, new stdClass(), 'string'], $proxy->setProxiedCall($args)); + $proxy->var = '123'; + $this->assertEquals('123', $proxy->var); + } } diff --git a/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php b/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php new file mode 100644 index 00000000..b8e7f3fa --- /dev/null +++ b/tests/Unit/Collector/VarDumperHandlerInterfaceProxyTest.php @@ -0,0 +1,77 @@ +createMock(HandlerInterface::class); + $handler + ->expects($this->once()) + ->method('handle'); + $timeline = new TimelineCollector(); + $timeline->startup(); + $collector = new VarDumperCollector($timeline); + $collector->startup(); + $proxy = new VarDumperHandlerInterfaceProxy($handler, $collector); + + $proxy->handle(true, 50, true); + + $this->assertEquals([ + [ + 'variable' => true, + 'line' => __FILE__ . ':28', + ], + ], $collector->getCollected()); + $this->assertEquals([ + 'var-dumper' => [ + 'total' => 1, + ], + ], $collector->getSummary()); + + $this->assertCount(1, $timeline->getCollected()); + + $event = $timeline->getCollected()[0]; + $this->assertEquals(1, $event[1]); + $this->assertEquals(VarDumperCollector::class, $event[2]); + $this->assertEquals([], $event[3]); + } + + public function testProxyDecoratedCall(): void + { + $handler = new class () implements HandlerInterface { + public $var = null; + + public function getProxiedCall(): string + { + return 'ok'; + } + + public function setProxiedCall($args): mixed + { + return $args; + } + + public function handle(mixed $variable, int $depth, bool $highlight = false): void + { + } + }; + $collector = new VarDumperCollector(new TimelineCollector()); + $proxy = new VarDumperHandlerInterfaceProxy($handler, $collector); + + $this->assertEquals('ok', $proxy->getProxiedCall()); + $this->assertEquals($args = [1, new stdClass(), 'string'], $proxy->setProxiedCall($args)); + $proxy->var = '123'; + $this->assertEquals('123', $proxy->var); + } +}