From 3115919e80f13178789c2ff3bdce80e5dc20d352 Mon Sep 17 00:00:00 2001 From: Aashish Sharma Date: Tue, 28 Jul 2026 12:24:42 +0530 Subject: [PATCH 1/2] Enhance File DTO to prevent warnings for large base64 data and add corresponding unit test --- src/Files/DTO/File.php | 6 +++-- tests/unit/Files/DTO/FileTest.php | 37 +++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/Files/DTO/File.php b/src/Files/DTO/File.php index e17b7418..b90a15e6 100644 --- a/src/Files/DTO/File.php +++ b/src/Files/DTO/File.php @@ -100,8 +100,10 @@ private function detectAndProcessFile(string $file, ?string $providedMimeType): return; } - // Check if it's a local file path (before base64 check) - if (file_exists($file) && is_file($file)) { + // Check if it's a local file path (before base64 check). + // The length guard avoids calling file_exists() on over-length strings (e.g. base64 data), + // which would emit a warning containing the entire string. + if (strlen($file) <= PHP_MAXPATHLEN && file_exists($file) && is_file($file)) { $this->fileType = FileTypeEnum::inline(); $this->base64Data = $this->convertFileToBase64($file); $this->mimeType = $this->determineMimeType($providedMimeType, null, $file); diff --git a/tests/unit/Files/DTO/FileTest.php b/tests/unit/Files/DTO/FileTest.php index 9ac76659..681bc050 100644 --- a/tests/unit/Files/DTO/FileTest.php +++ b/tests/unit/Files/DTO/FileTest.php @@ -111,6 +111,43 @@ public function testCreateFromPlainBase64(): void $this->assertEquals($mimeType, $file->getMimeType()); } + /** + * Tests creating a File from base64 data longer than the maximum path length without a PHP warning. + * + * @return void + */ + public function testCreateFromLargePlainBase64DoesNotEmitWarning(): void + { + // Base64-encoded JPEG data, longer than PHP_MAXPATHLEN and starting with the "/9j/" prefix. + $rawImage = "\xFF\xD8\xFF" . str_repeat("\x00", PHP_MAXPATHLEN); + $base64Data = base64_encode($rawImage); + $mimeType = 'image/jpeg'; + + $this->assertStringStartsWith('/9j/', $base64Data); + $this->assertGreaterThan(PHP_MAXPATHLEN, strlen($base64Data)); + + $capturedWarning = null; + set_error_handler( + static function (int $errno, string $errstr) use (&$capturedWarning): bool { + $capturedWarning = $errstr; + + return true; + }, + E_WARNING + ); + + try { + $file = new File($base64Data, $mimeType); + } finally { + restore_error_handler(); + } + + $this->assertNull($capturedWarning, 'Constructing a File from large base64 data must not emit a warning.'); + $this->assertEquals(FileTypeEnum::inline(), $file->getFileType()); + $this->assertEquals($base64Data, $file->getBase64Data()); + $this->assertEquals($mimeType, $file->getMimeType()); + } + /** * Tests that plain base64 without MIME type throws exception. * From 18aed1b1b601e19cb3ee87c4112b769610949629 Mon Sep 17 00:00:00 2001 From: Aashish Sharma Date: Tue, 15 Sep 2026 18:15:40 +0530 Subject: [PATCH 2/2] fix: path length boundary and suppress filesystem probe warnings in File detection --- src/Files/DTO/File.php | 2 +- tests/unit/Files/DTO/FileTest.php | 67 ++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/Files/DTO/File.php b/src/Files/DTO/File.php index b90a15e6..dacf4972 100644 --- a/src/Files/DTO/File.php +++ b/src/Files/DTO/File.php @@ -103,7 +103,7 @@ private function detectAndProcessFile(string $file, ?string $providedMimeType): // Check if it's a local file path (before base64 check). // The length guard avoids calling file_exists() on over-length strings (e.g. base64 data), // which would emit a warning containing the entire string. - if (strlen($file) <= PHP_MAXPATHLEN && file_exists($file) && is_file($file)) { + if (strlen($file) < PHP_MAXPATHLEN && @file_exists($file) && @is_file($file)) { $this->fileType = FileTypeEnum::inline(); $this->base64Data = $this->convertFileToBase64($file); $this->mimeType = $this->determineMimeType($providedMimeType, null, $file); diff --git a/tests/unit/Files/DTO/FileTest.php b/tests/unit/Files/DTO/FileTest.php index 681bc050..76344294 100644 --- a/tests/unit/Files/DTO/FileTest.php +++ b/tests/unit/Files/DTO/FileTest.php @@ -129,7 +129,12 @@ public function testCreateFromLargePlainBase64DoesNotEmitWarning(): void $capturedWarning = null; set_error_handler( static function (int $errno, string $errstr) use (&$capturedWarning): bool { - $capturedWarning = $errstr; + // Ignore diagnostics suppressed with the @ operator, which the handler still + // receives. Masking is detected by testing $errno against error_reporting(): + // it is 0 when suppressed on PHP 7.4, and a mask excluding E_WARNING on PHP 8.0+. + if ((error_reporting() & $errno) !== 0) { + $capturedWarning = $errstr; + } return true; }, @@ -148,6 +153,66 @@ static function (int $errno, string $errstr) use (&$capturedWarning): bool { $this->assertEquals($mimeType, $file->getMimeType()); } + /** + * Tests creating a File from base64 data at exactly the PHP_MAXPATHLEN boundary without a PHP warning. + * + * @return void + */ + public function testCreateFromBoundaryLengthBase64DoesNotEmitWarning(): void + { + // Build a base64 string whose length is exactly PHP_MAXPATHLEN. + // Start with valid base64 chars and pad to the boundary. + $base64Data = str_pad('/9j/', PHP_MAXPATHLEN, 'A'); + $mimeType = 'image/jpeg'; + + $this->assertSame(PHP_MAXPATHLEN, strlen($base64Data)); + + $capturedWarning = null; + set_error_handler( + static function (int $errno, string $errstr) use (&$capturedWarning): bool { + if ((error_reporting() & $errno) !== 0) { + $capturedWarning = $errstr; + } + + return true; + }, + E_WARNING + ); + + try { + $file = new File($base64Data, $mimeType); + } finally { + restore_error_handler(); + } + + $this->assertNull( + $capturedWarning, + 'Constructing a File from boundary-length base64 data must not emit a warning.' + ); + $this->assertEquals(FileTypeEnum::inline(), $file->getFileType()); + $this->assertEquals($base64Data, $file->getBase64Data()); + $this->assertEquals($mimeType, $file->getMimeType()); + } + + /** + * Tests creating a File from a small plain base64 payload. + * + * @return void + */ + public function testCreateFromSmallPlainBase64(): void + { + $base64Data = base64_encode('small test payload'); + $mimeType = 'text/plain'; + + $this->assertLessThan(PHP_MAXPATHLEN, strlen($base64Data)); + + $file = new File($base64Data, $mimeType); + + $this->assertEquals(FileTypeEnum::inline(), $file->getFileType()); + $this->assertEquals($base64Data, $file->getBase64Data()); + $this->assertEquals($mimeType, $file->getMimeType()); + } + /** * Tests that plain base64 without MIME type throws exception. *