diff --git a/docs/api/app.md b/docs/api/app.md index ca82526..554b78e 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -400,8 +400,8 @@ like this: // … ``` -Likewise, you can disable writing an access log by passing an absolute path to -`/dev/null` (Unix) or `nul` (Windows) like this: +Likewise, you can disable writing an access log by passing `/dev/null` (Unix) or +`nul` (Windows) like this: ```php title="public/index.php" DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : __DIR__ . '\\nul' + 'accesslog' => DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : 'nul', FrameworkX\AccessLogHandler::class => fn(string $accesslog) => new FrameworkX\AccessLogHandler($accesslog), ]); diff --git a/src/AccessLogHandler.php b/src/AccessLogHandler.php index 56ed811..80fd017 100644 --- a/src/AccessLogHandler.php +++ b/src/AccessLogHandler.php @@ -21,8 +21,8 @@ class AccessLogHandler private $hasHighResolution; /** - * @param ?string $path (optional) absolute log file path or will log to console output by default - * @throws \InvalidArgumentException if given `$path` is not an absolute file path + * @param ?string $path (optional) logs to console output by default, otherwise can be an absolute log file path, a `nul` path on Windows or a `php://` stream + * @throws \InvalidArgumentException if given `$path` is not an absolute file path, `nul` on Windows or `php://` stream * @throws \RuntimeException if given `$path` can not be opened in append mode */ public function __construct(?string $path = null) diff --git a/src/Io/LogStreamHandler.php b/src/Io/LogStreamHandler.php index 7d53b9a..d0d98d2 100644 --- a/src/Io/LogStreamHandler.php +++ b/src/Io/LogStreamHandler.php @@ -11,8 +11,8 @@ class LogStreamHandler private $stream; /** - * @param string $path absolute log file path - * @throws \InvalidArgumentException if given `$path` is not an absolute file path + * @param string $path absolute log file path, a `nul` path on Windows or a `php://` stream + * @throws \InvalidArgumentException if given `$path` is not an absolute file path, `nul` on Windows or `php://` stream * @throws \RuntimeException if given `$path` can not be opened in append mode */ public function __construct(string $path) @@ -90,6 +90,6 @@ public function log(string $message): void private function isAbsolutePath(string $path): bool { - return \DIRECTORY_SEPARATOR !== '\\' ? \substr($path, 0, 1) === '/' : (bool) \preg_match('#^[A-Z]:[/\\\\]#i', $path); + return \DIRECTORY_SEPARATOR !== '\\' ? \substr($path, 0, 1) === '/' : (bool) \preg_match('#^(?:[A-Z]:[/\\\\]|nul$)#i', $path); } } diff --git a/tests/AccessLogHandlerTest.php b/tests/AccessLogHandlerTest.php index f3bfeb5..80c55c1 100644 --- a/tests/AccessLogHandlerTest.php +++ b/tests/AccessLogHandlerTest.php @@ -122,7 +122,7 @@ public function testInvokeWithPathToExistingFileWillAppendLogMessage(): void */ public function testInvokeWithDevNullWritesNothing(): void { - $handler = new AccessLogHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : __DIR__ . '\\nul'); + $handler = new AccessLogHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : 'nul'); $request = new ServerRequest('GET', 'http://localhost:8080/users', [], '', '1.1', ['REMOTE_ADDR' => '127.0.0.1']); $response = new Response(200, [], "Hello\n"); diff --git a/tests/AppMiddlewareTest.php b/tests/AppMiddlewareTest.php index 0312554..242b070 100644 --- a/tests/AppMiddlewareTest.php +++ b/tests/AppMiddlewareTest.php @@ -648,7 +648,7 @@ public function testInvokeWithGlobalMiddlewareReturnsResponseWhenGlobalMiddlewar private function createAppWithoutLogger(...$middleware): App { return new App( - new AccessLogHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : __DIR__ . '\\nul'), + new AccessLogHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : 'nul'), new ErrorHandler(), ...$middleware ); diff --git a/tests/AppTest.php b/tests/AppTest.php index fee95df..5a0bb66 100644 --- a/tests/AppTest.php +++ b/tests/AppTest.php @@ -1997,7 +1997,7 @@ public function testInvokeWithMatchingRouteReturnsInternalServerErrorResponseWhe private function createAppWithoutLogger(callable ...$middleware): App { return new App( - new AccessLogHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : __DIR__ . '\\nul'), + new AccessLogHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : 'nul'), new ErrorHandler(), ...$middleware ); diff --git a/tests/Io/LogStreamHandlerTest.php b/tests/Io/LogStreamHandlerTest.php index 8c773e7..241e514 100644 --- a/tests/Io/LogStreamHandlerTest.php +++ b/tests/Io/LogStreamHandlerTest.php @@ -85,14 +85,23 @@ public static function provideFilesystemPaths(): \Generator ]; yield [ 'nul', + DIRECTORY_SEPARATOR === '\\' + ]; + yield [ + 'NUL', + DIRECTORY_SEPARATOR === '\\' + ]; + yield [ + 'nul.txt', false ]; yield [ '\\\\.\\nul', false ]; - if (DIRECTORY_SEPARATOR === '\\') { + if (DIRECTORY_SEPARATOR === '\\' && @fopen(__DIR__ . '\\nul', 'a') !== false) { // valid path on Windows, but we don't want to write here on Unix + // not supported on newer Windows versions due to https://github.com/php/php-src/security/advisories/GHSA-9f67-6fw4-hpfp yield [ __DIR__ . DIRECTORY_SEPARATOR . 'nul', true @@ -286,7 +295,7 @@ public function testLogWithPathToExistingFileWillAppendLogMessageWithCurrentDate */ public function testLogWithDevNullWritesNothing(): void { - $logger = new LogStreamHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : __DIR__ . '\\nul'); + $logger = new LogStreamHandler(DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : 'nul'); $logger->log('Hello'); } diff --git a/tests/integration/public/index.php b/tests/integration/public/index.php index 1fceb3a..f89f7b4 100644 --- a/tests/integration/public/index.php +++ b/tests/integration/public/index.php @@ -29,7 +29,7 @@ function asleep(float $s): PromiseInterface $container = new FrameworkX\Container([ FrameworkX\AccessLogHandler::class => function (?string $X_EXPERIMENTAL_RUNNER = null) { // log to /dev/null when running in experimental runner mode to avoid cluttering output - return new FrameworkX\AccessLogHandler($X_EXPERIMENTAL_RUNNER !== null ? (DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : __DIR__ . '\\nul') : null); + return new FrameworkX\AccessLogHandler($X_EXPERIMENTAL_RUNNER !== null ? (DIRECTORY_SEPARATOR !== '\\' ? '/dev/null' : 'nul') : null); } ]);