From 52eaa4ee5a2bcb61102d6fa8f629972bac2678da Mon Sep 17 00:00:00 2001 From: Claudear <262350598+claudear@users.noreply.github.com> Date: Mon, 10 Aug 2026 07:41:16 +0000 Subject: [PATCH] Fix fatal Error on unpopulated Log required fields `Logger::addLog()` validates a log with `empty($log->getAction()) || ...`, but the required properties on `Log` are typed with no default. Reading one before it is set is a fatal PHP `Error` ("Typed property Utopia\Logger\Log::$action must not be accessed before initialization"), thrown from inside the getter before the guard's own `throw new Exception('Log is not ready to be pushed.')` can run. Because `Error` does not extend `Exception`, callers following the documented `@throws Exception` contract never catch it, so a log that is missing a field crashes the request it was meant to report on. Default `$type`, `$message`, `$version`, `$environment` and `$action` to an empty string so the existing `empty()` validation behaves as intended and the readable getters keep their `string` return contract. Co-Authored-By: Claude Opus 5 --- src/Logger/Log.php | 10 ++-- tests/unit/LoggerTest.php | 114 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 tests/unit/LoggerTest.php diff --git a/src/Logger/Log.php b/src/Logger/Log.php index 46f6abe..ca73045 100644 --- a/src/Logger/Log.php +++ b/src/Logger/Log.php @@ -30,27 +30,27 @@ class Log /** * @var string (required, for example 'Log::TYPE_INFO') */ - protected string $type; + protected string $type = ''; /** * @var string (required) */ - protected string $message; + protected string $message = ''; /** * @var string (required) */ - protected string $version; + protected string $version = ''; /** * @var string (required) */ - protected string $environment; + protected string $environment = ''; /** * @var string (required) */ - protected string $action; + protected string $action = ''; /** * @var array (optional) diff --git a/tests/unit/LoggerTest.php b/tests/unit/LoggerTest.php new file mode 100644 index 0000000..53e2820 --- /dev/null +++ b/tests/unit/LoggerTest.php @@ -0,0 +1,114 @@ +getAdapter()); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Log is not ready to be pushed.'); + + $logger->addLog(new Log()); + } + + /** + * A partially populated log (missing the action) must also be rejected + * with an Exception. + * + * @throws Exception + */ + public function testAddLogWithMissingActionThrowsException(): void + { + $logger = new Logger($this->getAdapter()); + + $log = new Log(); + $log->setType(Log::TYPE_ERROR); + $log->setMessage('Something went wrong'); + $log->setVersion('1.0.0'); + $log->setEnvironment(Log::ENVIRONMENT_PRODUCTION); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Log is not ready to be pushed.'); + + $logger->addLog($log); + } + + /** + * Reading a required field of a fresh log must not fatal. + */ + public function testUnsetRequiredFieldsAreEmptyStrings(): void + { + $log = new Log(); + + self::assertSame('', $log->getAction()); + self::assertSame('', $log->getType()); + self::assertSame('', $log->getMessage()); + self::assertSame('', $log->getVersion()); + self::assertSame('', $log->getEnvironment()); + } + + /** + * A fully populated log is pushed to the adapter. + * + * @throws Exception + */ + public function testAddLogWithCompleteLog(): void + { + $logger = new Logger($this->getAdapter()); + + $log = new Log(); + $log->setType(Log::TYPE_ERROR); + $log->setMessage('Something went wrong'); + $log->setVersion('1.0.0'); + $log->setEnvironment(Log::ENVIRONMENT_PRODUCTION); + $log->setAction('testAction'); + + self::assertEquals(200, $logger->addLog($log)); + } +}