-
-
Notifications
You must be signed in to change notification settings - Fork 0
Refractor for better maintability #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Refactored DownloadComponent and UploadComponent to validate and cache storage manager instances, with improved error handling. Enhanced file name sanitization and file content loading in PathUtils and StoredFile, including better MIME type detection and exception handling. Updated UploadedFileDecorator for broader PSR-7 compatibility and improved option access. Refined BunnyStorageManager and LocalStorageManager for safer path handling, error reporting, and more robust upload logic. Adjusted StorageManager and StorageManagerInterface for more flexible configuration access and clearer type hints.
Bump MayMeowHQ/composer-run-action to v8.4 in CI and add a PHPUnit test job. Update composer.json to use vendor/bin/phpunit for the test script. Remove the fixture listener from phpunit.xml.dist. Adjust PathUtilsTest assertions to match updated file name sanitization behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This pull request enhances the file upload plugin with comprehensive test coverage, improved error handling, and better code maintainability. The changes introduce unit tests for core components, improve input validation and error handling across storage managers and components, and add CI automation for running tests.
- Added unit tests for
UploadedFileDecorator,StoredFile,PathUtils, and component classes - Enhanced error handling in
BunnyStorageManagerwith proper resource cleanup and validation - Improved input validation in
UploadComponentandDownloadComponentwith proper error messages
Reviewed Changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/TestCase/File/UploadedFileDecoratorTest.php | New test suite for UploadedFileDecorator validating option retrieval and filename fallback behavior |
| tests/TestCase/File/StoredFileTest.php | New tests for StoredFile verifying content caching and error handling for missing files |
| tests/TestCase/File/PathUtilsTest.php | New tests for PathUtils filename sanitization with various edge cases |
| tests/TestCase/Controller/Component/UploadComponentTest.php | Enhanced test coverage with typed properties and comprehensive error scenario tests |
| tests/TestCase/Controller/Component/TestStorageManager.php | New test helper classes for mocking storage managers in component tests |
| tests/TestCase/Controller/Component/DownloadComponentTest.php | Added test coverage for DownloadComponent with error handling tests |
| src/Storage/StorageManagerInterface.php | Updated documentation for the put() method parameter |
| src/Storage/StorageManager.php | Enhanced getConfig() method to support nullable keys and mixed return types |
| src/Storage/LocalStorageManager.php | Improved path normalization to handle empty paths and directory separators consistently |
| src/Storage/BunnyStorageManager.php | Enhanced error handling with proper resource cleanup, validation checks, and improved URL composition |
| src/File/UploadedFileDecorator.php | Changed to use UploadedFileInterface for better flexibility and improved get() method with default values |
| src/File/StoredFile.php | Added content caching, improved error handling with custom exception, and refactored MIME type detection |
| src/File/PathUtils.php | Added handling for filenames without extensions |
| src/Controller/Component/UploadComponent.php | Added input validation, proper error messages, and storage manager caching |
| src/Controller/Component/DownloadComponent.php | Added comprehensive error handling and storage manager validation |
| phpunit.xml.dist | Removed deprecated PHPUnit listener configuration |
| composer.json | Added test script for running PHPUnit tests |
| .github/workflows/ci.yml | Updated GitHub Actions workflow version and added automated test execution |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $storagePath = (string)$this->getConfig('storagePath'); | ||
| $normalizedPath = rtrim($storagePath, DIRECTORY_SEPARATOR . '/'); | ||
|
|
||
| if ($normalizedPath !== '') { | ||
| $normalizedPath .= DIRECTORY_SEPARATOR; | ||
| } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The path normalization logic (lines 23-28 and 50-55) is duplicated in both the put() and pull() methods. Consider extracting this into a private method like normalizeStoragePath(string $path): string to improve maintainability and reduce code duplication.
| - uses: actions/checkout@v2 | ||
| - name: Composer run action PHPC_CS | ||
| uses: MayMeowHQ/composer-run-action@v3 | ||
| uses: MayMeowHQ/composer-run-action@v8.4 |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version v8.4 for this GitHub Action is unusual. GitHub Actions typically use either major version tags (e.g., v3, v8) or full semantic versions (e.g., v3.2.1). Please verify that v8.4 is a valid and existing tag in the MayMeowHQ/composer-run-action repository.
| - uses: actions/checkout@v2 | ||
| - name: Composer run action PHPStan | ||
| uses: MayMeowHQ/composer-run-action@v3 | ||
| uses: MayMeowHQ/composer-run-action@v8.4 |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version v8.4 for this GitHub Action is unusual. GitHub Actions typically use either major version tags (e.g., v3, v8) or full semantic versions (e.g., v3.2.1). Please verify that v8.4 is a valid and existing tag in the MayMeowHQ/composer-run-action repository.
| steps: | ||
| - uses: actions/checkout@v2 | ||
| - name: Composer run action PHPUnit | ||
| uses: MayMeowHQ/[email protected] |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The version v8.4 for this GitHub Action is unusual. GitHub Actions typically use either major version tags (e.g., v3, v8) or full semantic versions (e.g., v3.2.1). Please verify that v8.4 is a valid and existing tag in the MayMeowHQ/composer-run-action repository.
| $size = ftell($stream); | ||
| rewind($stream); | ||
|
|
||
| $uploadedFile = new UploadedFile($stream, $size ?: null, UPLOAD_ERR_OK, $fileName, $mimeType); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The expression $size ?: null on line 141 can be simplified to $size !== false ? $size : null or handle the false case explicitly for better clarity, since ftell() returns int|false.
| $uploadedFile = new UploadedFile($stream, $size ?: null, UPLOAD_ERR_OK, $fileName, $mimeType); | |
| $uploadedFile = new UploadedFile($stream, $size !== false ? $size : null, UPLOAD_ERR_OK, $fileName, $mimeType); |
| public function testGetFileReturnsDecorator(): void | ||
| { | ||
| TestStorageManager::reset(); | ||
| $controller = $this->createControllerWithUpload('example.txt', 'plain/text'); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The MIME type 'plain/text' is incorrect. It should be 'text/plain' following the standard format of type/subtype.
| $controller = $this->createControllerWithUpload('example.txt', 'plain/text'); | |
| $controller = $this->createControllerWithUpload('example.txt', 'text/plain'); |
| return $fileName; | ||
| } | ||
|
|
||
| return $this->originalData->getClientFilename(); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The getClientFilename() method from PSR-7's UploadedFileInterface returns string|null, but the getFileName() method has a return type of string. This could cause a type error if getClientFilename() returns null. Consider handling the null case explicitly, e.g., return $this->originalData->getClientFilename() ?? '';
| return $this->originalData->getClientFilename(); | |
| return $this->originalData->getClientFilename() ?? ''; |
| $size = ftell($stream); | ||
| rewind($stream); | ||
|
|
||
| return new UploadedFile($stream, $size ?: null, UPLOAD_ERR_OK, $fileName, 'text/plain'); |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The expression $size ?: null can be simplified to $size !== false ? $size : null or just handle the false case explicitly for better clarity, since ftell() returns int|false.
| return new UploadedFile($stream, $size ?: null, UPLOAD_ERR_OK, $fileName, 'text/plain'); | |
| return new UploadedFile($stream, $size !== false ? $size : null, UPLOAD_ERR_OK, $fileName, 'text/plain'); |
| protected function _getStorageManager(): StorageManagerInterface | ||
| { | ||
| if ($this->storageManager instanceof StorageManagerInterface) { | ||
| return $this->storageManager; | ||
| } | ||
|
|
||
| $sm = $this->getConfig('managerClass'); | ||
|
|
||
| return new $sm($this->getConfig()); | ||
| if (!is_string($sm) || $sm === '') { | ||
| throw new HttpException('Storage manager class is not configured.'); | ||
| } | ||
|
|
||
| if (!class_exists($sm)) { | ||
| throw new HttpException(sprintf('Storage manager class "%s" does not exist.', $sm)); | ||
| } | ||
|
|
||
| $storageManager = new $sm($this->getConfig()); | ||
|
|
||
| if (!$storageManager instanceof StorageManagerInterface) { | ||
| throw new HttpException(sprintf('Storage manager must implement %s.', StorageManagerInterface::class)); | ||
| } | ||
|
|
||
| $this->storageManager = $storageManager; | ||
|
|
||
| return $this->storageManager; | ||
| } |
Copilot
AI
Nov 10, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _getStorageManager() method (lines 57-82) is duplicated in both UploadComponent and DownloadComponent. Consider extracting this logic into a shared trait or base class to reduce code duplication and improve maintainability.
This pull request introduces significant improvements to file upload and download handling, error checking, and test coverage. It refactors the storage manager logic for better reliability, enhances error handling for file operations, and updates the CI workflow to include PHPUnit tests. Additionally, it makes several code quality improvements and bug fixes across the codebase.
File Storage and Upload/Download Improvements:
DownloadComponentandUploadComponentto cache the storage manager instance, add stricter error handling for misconfiguration, and validate the storage manager class and interface implementation. Now throwsHttpExceptionfor misconfiguration or missing classes. (src/Controller/Component/DownloadComponent.php[1] [2];src/Controller/Component/UploadComponent.php[3] [4]BunnyStorageManagerto add detailed error checking for cURL operations, validate stream URIs, and provide clearer error messages when file uploads fail. Also refactored URL and hostname composition for better reliability. (src/Storage/BunnyStorageManager.php[1] [2]LocalStorageManagerto avoid path errors and ensure consistent file handling. (src/Storage/LocalStorageManager.php[1] [2]StoredFileto cache file content and MIME type, and throw a custom exception if file content cannot be loaded. (src/File/StoredFile.phpsrc/File/StoredFile.phpR6-R58)API and Interface Adjustments:
StorageManagerInterfaceand related implementations for stricter type safety and improved method signatures. (src/Storage/StorageManagerInterface.php[1]src/Storage/StorageManager.php[2]UploadedFileDecoratorto use the PSR-7UploadedFileInterfaceand improved option handling with default values. (src/File/UploadedFileDecorator.php[1] [2]Testing and CI Enhancements:
DownloadComponent, including tests for correct file retrieval and error cases when misconfigured. (tests/TestCase/Controller/Component/DownloadComponentTest.phptests/TestCase/Controller/Component/DownloadComponentTest.phpL4-R81)composer-run-actionand added a PHPUnit test job. Also added atestscript tocomposer.json. (.github/workflows/ci.yml[1] [2];composer.json[3]phpunit.xml.distfor cleaner test configuration. (phpunit.xml.distphpunit.xml.distL20-L28)Bug Fixes and Utilities:
PathUtils::fileNameSanitizeto handle empty extensions correctly. (src/File/PathUtils.phpsrc/File/PathUtils.phpR17-R20)These changes collectively improve the reliability, maintainability, and testability of the file upload/download subsystem.