From bbb7c69cf9d59b7db290a64c15a48d949bda7ac3 Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Tue, 14 Apr 2026 20:55:32 +0600 Subject: [PATCH 1/3] File --- README.md | 17 +++ docs/capabilities.rst | 16 +++ docs/file-facade.rst | 97 +++++++++++++++ docs/index.rst | 1 + docs/installation.rst | 8 +- docs/overview.rst | 11 +- docs/quickstart.rst | 4 +- src/File.php | 199 +++++++++++++++++++++++++++++++ tests/Feature/FileFacadeTest.php | 116 ++++++++++++++++++ 9 files changed, 459 insertions(+), 10 deletions(-) create mode 100644 docs/file-facade.rst create mode 100644 src/File.php create mode 100644 tests/Feature/FileFacadeTest.php diff --git a/README.md b/README.md index f4930c3..9ffab47 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,7 @@ Requirements: - Filesystem operations across core modules. - Mount support with scheme paths (`name://path`) and default filesystem support for relative paths. - Config-driven storage bootstrap via `StorageFactory` for local/custom/adapter-based filesystems. +- Unified entry facade via `Infocyph\Pathwise\File` for file/dir/processors/storage/tooling. - Advanced file APIs: checksum verification, visibility controls, URL passthrough (`publicUrl`, `temporaryUrl`). - Directory automation: sync with diff report, recursive copy/move/delete, mounted-path ZIP/unzip bridging. - Upload/download pipelines: chunked/resumable uploads, validation profiles (image/video/document), extension allow/deny controls, strict MIME/signature checks, upload-id safety validation, malware-scan hook, secure download metadata + range handling. @@ -137,6 +138,22 @@ StorageFactory::mount('tenant', [ ]); ``` +## **Unified File Facade** + +Use a single entry point when you want fewer direct class imports. + +```php +use Infocyph\Pathwise\File; + +$entry = File::at('/tmp/example.txt'); +$entry->file()->create('hello')->append("\nworld"); + +$upload = File::upload(); +$download = File::download(); + +File::mountStorage('assets', ['driver' => 'local', 'root' => '/srv/assets']); +``` + ## **FileManager** The `FileManager` module provides classes for handling files, including reading, writing, compressing and general file operations. diff --git a/docs/capabilities.rst b/docs/capabilities.rst index e031cc1..24109a1 100644 --- a/docs/capabilities.rst +++ b/docs/capabilities.rst @@ -14,6 +14,21 @@ Pathwise combines two layers: Primary Modules --------------- +Unified Facade (``Infocyph\Pathwise\File``) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Class: + +* ``File`` + +What you get: + +* One path-bound entry to ``FileOperations``, ``DirectoryOperations``, + ``SafeFileReader``, ``SafeFileWriter`` and ``FileCompression``. +* Static gateways for ``UploadProcessor``, ``DownloadProcessor``, + ``StorageFactory``, ``PolicyEngine``, ``FileJobQueue``, ``AuditTrail``, + ``RetentionManager``, ``ChecksumIndexer`` and ``FileWatcher``. + File IO (``Infocyph\Pathwise\FileManager``) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -122,5 +137,6 @@ Optional: What to Read Next ----------------- +* ``file-facade`` for unified usage style. * ``quickstart`` for first-use examples. * ``recipes`` for end-to-end flows. diff --git a/docs/file-facade.rst b/docs/file-facade.rst new file mode 100644 index 0000000..5089933 --- /dev/null +++ b/docs/file-facade.rst @@ -0,0 +1,97 @@ +Unified File Facade +=================== + +Namespace: ``Infocyph\Pathwise`` + +Pathwise provides ``File`` as a convenience facade when you want one entry +point instead of importing many classes directly. + +Use this when: + +* you want path-bound access to file/directory/compression/read/write APIs +* you want static gateways for upload/download/storage/policy/queue/audit/etc. + +Keep direct classes when: + +* you prefer explicit class-level imports for large codebases +* you need very focused dependencies per module + +Path-Bound Access +----------------- + +.. code-block:: php + + use Infocyph\Pathwise\File; + + $entry = File::at('/tmp/demo.txt'); + + $entry->file()->create('hello')->append("\nworld"); + + $reader = $entry->reader(); + foreach ($reader->line() as $line) { + // ... + } + + $writer = $entry->writer(true); + $writer->line('tail'); + $writer->close(); + + $metadata = $entry->metadata(); + +Directory + Compression via Same Entry +-------------------------------------- + +.. code-block:: php + + use Infocyph\Pathwise\File; + + File::at('/tmp/source')->directory()->create(); + + File::at('/tmp/archive.zip') + ->compression(true) + ->compress('/tmp/source') + ->save(); + +Static Gateways +--------------- + +.. code-block:: php + + use Infocyph\Pathwise\File; + + $upload = File::upload(); + $download = File::download(); + $policy = File::policy(); + $queue = File::queue('/tmp/jobs.json'); + $audit = File::audit('/tmp/audit.jsonl'); + +Storage from Facade +------------------- + +``File`` delegates storage creation/mounting to ``StorageFactory``. + +.. code-block:: php + + use Infocyph\Pathwise\File; + + File::mountStorage('assets', [ + 'driver' => 'local', + 'root' => '/srv/storage/assets', + ]); + + // For other adapters, pass adapter/constructor config: + // File::mountStorage('s3', ['driver' => 's3', 'adapter' => $adapter]); + +Operational Tooling from Facade +------------------------------- + +Available helpers: + +* ``File::retain(...)`` -> ``RetentionManager`` +* ``File::index(...)`` / ``File::duplicates(...)`` / ``File::deduplicate(...)`` -> ``ChecksumIndexer`` +* ``File::snapshot(...)`` / ``File::diffSnapshots(...)`` / ``File::watch(...)`` -> ``FileWatcher`` + +See also: + +* ``storage-adapters`` for adapter bootstrap +* ``upload-processing`` and ``download-processing`` for stream workflows diff --git a/docs/index.rst b/docs/index.rst index 03b1ebb..67390cb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -13,6 +13,7 @@ queue/audit tooling, and operational helpers. installation capabilities storage-adapters + file-facade quickstart recipes file-manager diff --git a/docs/installation.rst b/docs/installation.rst index 787e571..879cd90 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -41,12 +41,12 @@ See ``storage-adapters`` for setup patterns. Where to Use First ------------------ -If you are evaluating Pathwise, start with ``FileOperations`` and -``DirectoryOperations``. They cover the largest set of day-to-day file tasks. +If you are evaluating Pathwise, start with the unified ``File`` facade, then +drop down to direct module classes as needed. .. code-block:: php - use Infocyph\Pathwise\FileManager\FileOperations; + use Infocyph\Pathwise\File; - $ops = new FileOperations('/tmp/example.txt'); + $ops = File::at('/tmp/example.txt')->file(); $ops->create('initial')->update('updated'); diff --git a/docs/overview.rst b/docs/overview.rst index 9ca78dc..1c1dafa 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -15,6 +15,7 @@ It focuses on three layers: Main namespaces: +* ``Infocyph\Pathwise`` (unified ``File`` facade) * ``Infocyph\Pathwise\FileManager`` * ``Infocyph\Pathwise\DirectoryManager`` * ``Infocyph\Pathwise\StreamHandler`` @@ -36,19 +37,21 @@ Quick Start .. code-block:: php - use Infocyph\Pathwise\FileManager\FileOperations; - use Infocyph\Pathwise\DirectoryManager\DirectoryOperations; + use Infocyph\Pathwise\File; - (new FileOperations('/tmp/demo.txt')) + File::at('/tmp/demo.txt') + ->file() ->create('hello') ->append("\nworld"); - $report = (new DirectoryOperations('/tmp/source')) + $report = File::at('/tmp/source') + ->directory() ->syncTo('/tmp/backup', deleteOrphans: true); Read Next --------- * ``capabilities`` for the full module map. +* ``file-facade`` for the unified entry style. * ``quickstart`` for copy/paste examples. * ``recipes`` for end-to-end workflow patterns. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index ee06ae4..149ae23 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -15,9 +15,9 @@ This quickstart shows the fastest way to understand what Pathwise can do. .. code-block:: php - use Infocyph\Pathwise\FileManager\FileOperations; + use Infocyph\Pathwise\File; - $file = new FileOperations('/tmp/example.txt'); + $file = File::at('/tmp/example.txt')->file(); $file->create("v1\n") ->append("v2\n") ->writeAndVerify("v3\n", 'sha256'); diff --git a/src/File.php b/src/File.php new file mode 100644 index 0000000..0e96c21 --- /dev/null +++ b/src/File.php @@ -0,0 +1,199 @@ +path = PathHelper::normalize($path); + } + + public static function at(string $path): self + { + return new self($path); + } + + public static function audit(string $logFilePath): AuditTrail + { + return new AuditTrail($logFilePath); + } + + /** + * @param array $config + */ + public static function createFilesystem(array $config): FilesystemOperator + { + return StorageFactory::createFilesystem($config); + } + + /** + * @return array{linked: array, skipped: array} + */ + public static function deduplicate(string $directory, string $algorithm = 'sha256'): array + { + return ChecksumIndexer::deduplicateWithHardLinks($directory, $algorithm); + } + + /** + * @return array{created: array, modified: array, deleted: array} + */ + public static function diffSnapshots(array $previousSnapshot, array $currentSnapshot): array + { + return FileWatcher::diff($previousSnapshot, $currentSnapshot); + } + + public static function download(): DownloadProcessor + { + return new DownloadProcessor(); + } + + /** + * @return array> + */ + public static function duplicates(string $directory, string $algorithm = 'sha256'): array + { + return ChecksumIndexer::findDuplicates($directory, $algorithm); + } + + public static function from(string $path): self + { + return self::at($path); + } + + /** + * @return array> checksum => paths + */ + public static function index(string $directory, string $algorithm = 'sha256'): array + { + return ChecksumIndexer::buildIndex($directory, $algorithm); + } + + /** + * @param array $config + */ + public static function mountStorage(string $name, array $config): FilesystemOperator + { + return StorageFactory::mount($name, $config); + } + + /** + * @param array> $mounts + */ + public static function mountStorages(array $mounts): void + { + StorageFactory::mountMany($mounts); + } + + public static function policy(): PolicyEngine + { + return new PolicyEngine(); + } + + public static function queue(string $queueFilePath): FileJobQueue + { + return new FileJobQueue($queueFilePath); + } + + /** + * @return array{deleted: array, kept: array} + */ + public static function retain( + string $directory, + ?int $keepLast = null, + ?int $maxAgeDays = null, + string $sortBy = 'mtime', + ): array { + return RetentionManager::apply($directory, $keepLast, $maxAgeDays, $sortBy); + } + + /** + * @return array + */ + public static function snapshot(string $path, bool $recursive = true): array + { + return FileWatcher::snapshot($path, $recursive); + } + + public static function upload(): UploadProcessor + { + return new UploadProcessor(); + } + + /** + * @return array + */ + public static function watch( + string $path, + callable $onChange, + int $durationSeconds = 5, + int $intervalMilliseconds = 500, + bool $recursive = true, + ): array { + return FileWatcher::watch($path, $onChange, $durationSeconds, $intervalMilliseconds, $recursive); + } + + public function compression(bool $create = false): FileCompression + { + return new FileCompression($this->path, $create); + } + + public function directory(): DirectoryOperations + { + return new DirectoryOperations($this->path); + } + + public function exists(): bool + { + return FlysystemHelper::has($this->path); + } + + public function file(): FileOperations + { + return new FileOperations($this->path); + } + + public function metadata(bool $humanReadableSize = false): ?array + { + return MetadataHelper::getAllMetadata($this->path, $humanReadableSize); + } + + public function mimeType(): ?string + { + return MetadataHelper::getMimeType($this->path); + } + + public function path(): string + { + return $this->path; + } + + public function reader(string $mode = 'r', bool $exclusiveLock = false): SafeFileReader + { + return new SafeFileReader($this->path, $mode, $exclusiveLock); + } + + public function writer(bool $append = false): SafeFileWriter + { + return new SafeFileWriter($this->path, $append); + } +} diff --git a/tests/Feature/FileFacadeTest.php b/tests/Feature/FileFacadeTest.php new file mode 100644 index 0000000..9976b1a --- /dev/null +++ b/tests/Feature/FileFacadeTest.php @@ -0,0 +1,116 @@ +workspace = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('pathwise_file_facade_', true); + mkdir($this->workspace, 0755, true); +}); + +afterEach(function () { + FlysystemHelper::reset(); + StorageFactory::clearDrivers(); + + if (!is_dir($this->workspace)) { + return; + } + + deleteDirectory($this->workspace); +}); + +test('it provides path-bound file accessors', function () { + $filePath = $this->workspace . DIRECTORY_SEPARATOR . 'sample.txt'; + $entry = File::at($filePath); + + $entry->file()->create("line-1\n"); + + $writer = $entry->writer(true); + $writer->line('line-2'); + $writer->close(); + + $content = $entry->file()->read(); + $lines = iterator_to_array($entry->reader()->line()); + + expect($entry->exists())->toBeTrue() + ->and($entry->path())->toBe($filePath) + ->and($content)->toContain("line-1\n") + ->and($content)->toContain('line-2') + ->and(trim((string) ($lines[0] ?? '')))->toBe('line-1') + ->and(trim((string) ($lines[1] ?? '')))->toBe('line-2') + ->and($entry->metadata())->toBeArray(); +}); + +test('it provides path-bound directory and compression accessors', function () { + $sourceDir = $this->workspace . DIRECTORY_SEPARATOR . 'source'; + mkdir($sourceDir, 0755, true); + file_put_contents($sourceDir . DIRECTORY_SEPARATOR . 'a.txt', 'A'); + + $zipPath = $this->workspace . DIRECTORY_SEPARATOR . 'archive.zip'; + $extractDir = $this->workspace . DIRECTORY_SEPARATOR . 'extract'; + + File::at($sourceDir)->directory()->create(); + File::at($zipPath)->compression(true)->compress($sourceDir)->save(); + File::at($zipPath)->compression()->decompress($extractDir)->save(); + + expect(FlysystemHelper::fileExists($zipPath))->toBeTrue() + ->and(FlysystemHelper::fileExists($extractDir . DIRECTORY_SEPARATOR . 'a.txt'))->toBeTrue() + ->and(FlysystemHelper::read($extractDir . DIRECTORY_SEPARATOR . 'a.txt'))->toBe('A'); +}); + +test('it provides static gateways for processors policy storage and ops tooling', function () { + $root = $this->workspace . DIRECTORY_SEPARATOR . 'storage'; + mkdir($root, 0755, true); + + File::mountStorage('facade', [ + 'driver' => 'local', + 'root' => $root, + ]); + + FlysystemHelper::write('facade://data/file.txt', 'hello'); + + $upload = File::upload(); + $download = File::download(); + $policy = File::policy()->allow('*', '*'); + + $queueFile = $this->workspace . DIRECTORY_SEPARATOR . 'queue' . DIRECTORY_SEPARATOR . 'jobs.json'; + $queue = File::queue($queueFile); + $queue->enqueue('example', ['id' => 1], 10); + $stats = $queue->stats(); + + $auditFile = $this->workspace . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'audit.jsonl'; + $audit = File::audit($auditFile); + $audit->log('facade.test', ['ok' => true]); + + $watchPath = $this->workspace . DIRECTORY_SEPARATOR . 'watch.txt'; + file_put_contents($watchPath, 'v1'); + $snapshotA = File::snapshot($watchPath); + file_put_contents($watchPath, 'v22'); + $snapshotB = File::snapshot($watchPath); + $diff = File::diffSnapshots($snapshotA, $snapshotB); + + $dupDir = $this->workspace . DIRECTORY_SEPARATOR . 'dups'; + mkdir($dupDir, 0755, true); + file_put_contents($dupDir . DIRECTORY_SEPARATOR . 'a.txt', 'dup'); + file_put_contents($dupDir . DIRECTORY_SEPARATOR . 'b.txt', 'dup'); + $index = File::index($dupDir); + $duplicates = File::duplicates($dupDir); + + $retention = File::retain($this->workspace . DIRECTORY_SEPARATOR . 'empty-retention'); + + expect($upload)->toBeInstanceOf(\Infocyph\Pathwise\StreamHandler\UploadProcessor::class) + ->and($download)->toBeInstanceOf(\Infocyph\Pathwise\StreamHandler\DownloadProcessor::class) + ->and($policy->isAllowed('read', 'anything'))->toBeTrue() + ->and(FlysystemHelper::read('facade://data/file.txt'))->toBe('hello') + ->and($stats['pending'])->toBe(1) + ->and(FlysystemHelper::fileExists($auditFile))->toBeTrue() + ->and($diff['modified'])->toContain($watchPath) + ->and($index)->not->toBeEmpty() + ->and($duplicates)->not->toBeEmpty() + ->and($retention)->toBe(['deleted' => [], 'kept' => []]); + + FlysystemHelper::unmount('facade'); +}); From d49c68481c7fb08a01dd05b71587730b215dc0b0 Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Tue, 14 Apr 2026 21:20:19 +0600 Subject: [PATCH 2/3] docblock --- src/DirectoryManager/DirectoryOperations.php | 24 +++ src/File.php | 94 ++++++++++++ src/FileManager/FileCompression.php | 16 ++ src/FileManager/FileOperations.php | 89 +++++++++++ src/FileManager/SafeFileWriter.php | 15 ++ src/Native/NativeCommandRunner.php | 6 + src/Native/NativeOperationsAdapter.php | 15 ++ src/Observability/AuditTrail.php | 11 ++ src/Queue/FileJobQueue.php | 13 ++ src/Security/PolicyEngine.php | 32 ++++ src/Storage/StorageFactory.php | 35 ++++- src/StreamHandler/DownloadProcessor.php | 30 ++++ src/StreamHandler/UploadProcessor.php | 5 + src/Utils/FlysystemHelper.php | 145 ++++++++++++++++++ .../Ownership/FallbackOwnershipResolver.php | 12 ++ .../Ownership/OwnershipResolverFactory.php | 5 + .../Ownership/PosixOwnershipResolver.php | 12 ++ .../Ownership/WindowsOwnershipResolver.php | 12 ++ src/Utils/PathHelper.php | 6 + 19 files changed, 576 insertions(+), 1 deletion(-) diff --git a/src/DirectoryManager/DirectoryOperations.php b/src/DirectoryManager/DirectoryOperations.php index 94560ba..c30ce20 100644 --- a/src/DirectoryManager/DirectoryOperations.php +++ b/src/DirectoryManager/DirectoryOperations.php @@ -303,6 +303,12 @@ public function listContents(bool $detailed = false, ?callable $filter = null): return $contents; } + /** + * List directory contents as a DirectoryListing object. + * + * @param bool $deep Whether to list contents recursively. Defaults to true. + * @return DirectoryListing The directory listing. + */ public function listContentsListing(bool $deep = true): DirectoryListing { return FlysystemHelper::listContentsListing($this->path, $deep); @@ -363,6 +369,12 @@ public function move(string $destination): bool return true; } + /** + * Set the execution strategy for directory operations. + * + * @param ExecutionStrategy $executionStrategy The execution strategy to use. + * @return self This instance for method chaining. + */ public function setExecutionStrategy(ExecutionStrategy $executionStrategy): self { $this->executionStrategy = $executionStrategy; @@ -385,6 +397,12 @@ public function setPermissions(int $permissions): bool return chmod($this->path, $permissions); } + /** + * Set the visibility of the directory. + * + * @param string $visibility The visibility to set (e.g., 'public' or 'private'). + * @return self This instance for method chaining. + */ public function setVisibility(string $visibility): self { FlysystemHelper::setVisibility($this->path, $visibility); @@ -486,6 +504,12 @@ public function unzip(string $source): bool } } + /** + * Get the visibility of the directory. + * + * @return string|null The visibility, or null if not available. + * @throws DirectoryOperationException If the directory does not exist. + */ public function visibility(): ?string { if (!FlysystemHelper::directoryExists($this->path)) { diff --git a/src/File.php b/src/File.php index 0e96c21..2fe9d0c 100644 --- a/src/File.php +++ b/src/File.php @@ -23,16 +23,33 @@ final class File { + /** + * Constructor to initialize the file path. + * + * @param string $path The path to the file or directory. + */ public function __construct(private string $path) { $this->path = PathHelper::normalize($path); } + /** + * Create a new instance at the given path. + * + * @param string $path The path to the file or directory. + * @return self A new File instance. + */ public static function at(string $path): self { return new self($path); } + /** + * Create an audit trail logger. + * + * @param string $logFilePath The path to the log file. + * @return AuditTrail The audit trail instance. + */ public static function audit(string $logFilePath): AuditTrail { return new AuditTrail($logFilePath); @@ -62,6 +79,11 @@ public static function diffSnapshots(array $previousSnapshot, array $currentSnap return FileWatcher::diff($previousSnapshot, $currentSnapshot); } + /** + * Create a download processor for secure file downloads. + * + * @return DownloadProcessor The download processor instance. + */ public static function download(): DownloadProcessor { return new DownloadProcessor(); @@ -75,6 +97,12 @@ public static function duplicates(string $directory, string $algorithm = 'sha256 return ChecksumIndexer::findDuplicates($directory, $algorithm); } + /** + * Alias for at() - create a new instance at the given path. + * + * @param string $path The path to the file or directory. + * @return self A new File instance. + */ public static function from(string $path): self { return self::at($path); @@ -104,11 +132,22 @@ public static function mountStorages(array $mounts): void StorageFactory::mountMany($mounts); } + /** + * Create a policy engine for access control. + * + * @return PolicyEngine The policy engine instance. + */ public static function policy(): PolicyEngine { return new PolicyEngine(); } + /** + * Create a file-based job queue. + * + * @param string $queueFilePath The path to the queue file. + * @return FileJobQueue The job queue instance. + */ public static function queue(string $queueFilePath): FileJobQueue { return new FileJobQueue($queueFilePath); @@ -134,6 +173,11 @@ public static function snapshot(string $path, bool $recursive = true): array return FileWatcher::snapshot($path, $recursive); } + /** + * Create an upload processor for secure file uploads. + * + * @return UploadProcessor The upload processor instance. + */ public static function upload(): UploadProcessor { return new UploadProcessor(); @@ -152,46 +196,96 @@ public static function watch( return FileWatcher::watch($path, $onChange, $durationSeconds, $intervalMilliseconds, $recursive); } + /** + * Get a file compression handler for this path. + * + * @param bool $create If true, create a new ZIP archive if it doesn't exist. + * @return FileCompression The file compression instance. + */ public function compression(bool $create = false): FileCompression { return new FileCompression($this->path, $create); } + /** + * Get a directory operations handler for this path. + * + * @return DirectoryOperations The directory operations instance. + */ public function directory(): DirectoryOperations { return new DirectoryOperations($this->path); } + /** + * Check if the file or directory exists. + * + * @return bool True if the path exists, false otherwise. + */ public function exists(): bool { return FlysystemHelper::has($this->path); } + /** + * Get a file operations handler for this path. + * + * @return FileOperations The file operations instance. + */ public function file(): FileOperations { return new FileOperations($this->path); } + /** + * Get metadata for this file or directory. + * + * @param bool $humanReadableSize If true, return size in human-readable format. + * @return array|null The metadata array, or null if the path doesn't exist. + */ public function metadata(bool $humanReadableSize = false): ?array { return MetadataHelper::getAllMetadata($this->path, $humanReadableSize); } + /** + * Get the MIME type of this file. + * + * @return string|null The MIME type, or null if not a file. + */ public function mimeType(): ?string { return MetadataHelper::getMimeType($this->path); } + /** + * Get the normalized path. + * + * @return string The normalized path. + */ public function path(): string { return $this->path; } + /** + * Get a safe file reader for this path. + * + * @param string $mode The file mode to open with. Defaults to 'r'. + * @param bool $exclusiveLock If true, acquire an exclusive lock. + * @return SafeFileReader The file reader instance. + */ public function reader(string $mode = 'r', bool $exclusiveLock = false): SafeFileReader { return new SafeFileReader($this->path, $mode, $exclusiveLock); } + /** + * Get a safe file writer for this path. + * + * @param bool $append If true, append to existing file. Defaults to false. + * @return SafeFileWriter The file writer instance. + */ public function writer(bool $append = false): SafeFileWriter { return new SafeFileWriter($this->path, $append); diff --git a/src/FileManager/FileCompression.php b/src/FileManager/FileCompression.php index 3aaa41a..477fd8f 100644 --- a/src/FileManager/FileCompression.php +++ b/src/FileManager/FileCompression.php @@ -422,6 +422,12 @@ public function setEncryptionAlgorithm(int $algorithm): self return $this; } + /** + * Set the execution strategy for compression operations. + * + * @param ExecutionStrategy $executionStrategy The execution strategy to use. + * @return self This instance for method chaining. + */ public function setExecutionStrategy(ExecutionStrategy $executionStrategy): self { $this->executionStrategy = $executionStrategy; @@ -431,6 +437,10 @@ public function setExecutionStrategy(ExecutionStrategy $executionStrategy): self /** * Configure include/exclude glob patterns used during compression. + * + * @param array $includePatterns Patterns to include in compression. + * @param array $excludePatterns Patterns to exclude from compression. + * @return self This instance for method chaining. */ public function setGlobPatterns(array $includePatterns = [], array $excludePatterns = []): self { @@ -442,6 +452,9 @@ public function setGlobPatterns(array $includePatterns = [], array $excludePatte /** * Configure ignore file names (e.g. .gitignore, .pathwiseignore) read from source root. + * + * @param array $ignoreFileNames Array of ignore file names. + * @return self This instance for method chaining. */ public function setIgnoreFileNames(array $ignoreFileNames): self { @@ -481,6 +494,9 @@ public function setPassword(string $password): self /** * Register a progress callback for compress/decompress operations. + * + * @param callable $progressCallback Callback receiving progress array with operation, path, current, and total. + * @return self This instance for method chaining. */ public function setProgressCallback(callable $progressCallback): self { diff --git a/src/FileManager/FileOperations.php b/src/FileManager/FileOperations.php index 607e255..4b78c4c 100644 --- a/src/FileManager/FileOperations.php +++ b/src/FileManager/FileOperations.php @@ -51,6 +51,11 @@ public function append(string $content): self return $this; } + /** + * Begin a transaction for atomic file operations. + * + * @return self This instance for method chaining. + */ public function beginTransaction(): self { $this->transactionActive = true; @@ -59,6 +64,11 @@ public function beginTransaction(): self return $this; } + /** + * Commit the current transaction. + * + * @return self This instance for method chaining. + */ public function commitTransaction(): self { $this->transactionActive = false; @@ -253,6 +263,13 @@ public function openWithLock(bool $exclusive = true, int $timeout = 0): self return $this; } + /** + * Get the public URL for this file. + * + * @param array $config Additional configuration for URL generation. + * @return string The public URL. + * @throws FileNotFoundException If the file does not exist. + */ public function publicUrl(array $config = []): string { if (!$this->exists()) { @@ -273,6 +290,12 @@ public function read(): string return FlysystemHelper::read($this->filePath); } + /** + * Read the file as a stream. + * + * @return mixed The file stream resource. + * @throws FileNotFoundException If the file is not readable. + */ public function readStream(): mixed { $this->isReadable(); @@ -304,6 +327,11 @@ public function rename(string $newPath): self return $this; } + /** + * Rollback the current transaction, reverting all changes. + * + * @return self This instance for method chaining. + */ public function rollbackTransaction(): self { for ($i = count($this->rollbackActions) - 1; $i >= 0; $i--) { @@ -339,6 +367,12 @@ public function searchContent(string $searchTerm): array return $output; } + /** + * Set the audit trail for logging operations. + * + * @param AuditTrail $auditTrail The audit trail instance. + * @return self This instance for method chaining. + */ public function setAuditTrail(AuditTrail $auditTrail): self { $this->auditTrail = $auditTrail; @@ -346,6 +380,12 @@ public function setAuditTrail(AuditTrail $auditTrail): self return $this; } + /** + * Set the execution strategy for file operations. + * + * @param ExecutionStrategy $executionStrategy The execution strategy to use. + * @return self This instance for method chaining. + */ public function setExecutionStrategy(ExecutionStrategy $executionStrategy): self { $this->executionStrategy = $executionStrategy; @@ -401,6 +441,12 @@ public function setPermissions(int $permissions): self return $this; } + /** + * Set the policy engine for access control. + * + * @param PolicyEngine $policyEngine The policy engine instance. + * @return self This instance for method chaining. + */ public function setPolicyEngine(PolicyEngine $policyEngine): self { $this->policyEngine = $policyEngine; @@ -408,6 +454,12 @@ public function setPolicyEngine(PolicyEngine $policyEngine): self return $this; } + /** + * Set the visibility of the file. + * + * @param string $visibility The visibility to set (e.g., 'public' or 'private'). + * @return self This instance for method chaining. + */ public function setVisibility(string $visibility): self { $this->assertPolicy('set-visibility', $this->filePath, ['visibility' => $visibility]); @@ -417,6 +469,14 @@ public function setVisibility(string $visibility): self return $this; } + /** + * Get a temporary URL for this file. + * + * @param DateTimeInterface $expiresAt The expiration time for the URL. + * @param array $config Additional configuration for URL generation. + * @return string The temporary URL. + * @throws FileNotFoundException If the file does not exist. + */ public function temporaryUrl(DateTimeInterface $expiresAt, array $config = []): string { if (!$this->exists()) { @@ -426,6 +486,13 @@ public function temporaryUrl(DateTimeInterface $expiresAt, array $config = []): return FlysystemHelper::temporaryUrl($this->filePath, $expiresAt, $config); } + /** + * Execute a callback within a transaction. + * + * @param callable $callback The callback to execute. Receives this instance as argument. + * @return mixed The result of the callback. + * @throws \Throwable Re-throws any exception after rollback. + */ public function transaction(callable $callback): mixed { $this->beginTransaction(); @@ -468,6 +535,15 @@ public function update(string $content): self return $this; } + /** + * Verify the file's checksum against an expected value. + * + * @param string $expectedChecksum The expected checksum. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return bool True if the checksum matches, false otherwise. + * @throws FileAccessException If the algorithm is not supported. + * @throws FileNotFoundException If the file does not exist. + */ public function verifyChecksum(string $expectedChecksum, string $algorithm = 'sha256'): bool { if (!in_array($algorithm, hash_algos(), true)) { @@ -482,6 +558,12 @@ public function verifyChecksum(string $expectedChecksum, string $algorithm = 'sh return is_string($fileHash) && hash_equals($expectedChecksum, $fileHash); } + /** + * Get the visibility of the file. + * + * @return string|null The visibility, or null if not available. + * @throws FileNotFoundException If the file does not exist. + */ public function visibility(): ?string { if (!$this->exists()) { @@ -509,6 +591,13 @@ public function writeAndVerify(string $content, string $algorithm = 'sha256'): s return $this; } + /** + * Write to the file from a stream. + * + * @param mixed $stream The stream resource to write from. + * @param array $config Additional configuration for the write operation. + * @return self This instance for method chaining. + */ public function writeStream(mixed $stream, array $config = []): self { $this->assertPolicy('write-stream', $this->filePath); diff --git a/src/FileManager/SafeFileWriter.php b/src/FileManager/SafeFileWriter.php index 4af7af1..0a7d19e 100644 --- a/src/FileManager/SafeFileWriter.php +++ b/src/FileManager/SafeFileWriter.php @@ -145,6 +145,13 @@ public function count(): int return $this->writeCount; } + /** + * Enable or disable atomic write mode. + * + * @param bool $enabled If true, enable atomic writes. + * @return self This instance for method chaining. + * @throws FileAccessException If atomic mode is enabled in append mode. + */ public function enableAtomicWrite(bool $enabled = true): self { if ($enabled && $this->append) { @@ -307,6 +314,14 @@ public function unlock(): void $this->isLocked = false; } + /** + * Verify the file's checksum against an expected value. + * + * @param string $expectedChecksum The expected checksum. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return bool True if the checksum matches, false otherwise. + * @throws Exception If the algorithm is not supported. + */ public function verifyChecksum(string $expectedChecksum, string $algorithm = 'sha256'): bool { if (!in_array($algorithm, hash_algos(), true)) { diff --git a/src/Native/NativeCommandRunner.php b/src/Native/NativeCommandRunner.php index 48ef00c..2588e71 100644 --- a/src/Native/NativeCommandRunner.php +++ b/src/Native/NativeCommandRunner.php @@ -4,6 +4,12 @@ final class NativeCommandRunner { + /** + * Check if a command exists on the system. + * + * @param string $command The command to check. + * @return bool True if the command exists, false otherwise. + */ public static function commandExists(string $command): bool { $lookup = PHP_OS_FAMILY === 'Windows' diff --git a/src/Native/NativeOperationsAdapter.php b/src/Native/NativeOperationsAdapter.php index 74fa294..027da88 100644 --- a/src/Native/NativeOperationsAdapter.php +++ b/src/Native/NativeOperationsAdapter.php @@ -6,6 +6,11 @@ final class NativeOperationsAdapter { + /** + * Check if native compression commands are available. + * + * @return bool True if native compression is available. + */ public static function canUseNativeCompression(): bool { if (PHP_OS_FAMILY === 'Windows') { @@ -14,6 +19,11 @@ public static function canUseNativeCompression(): bool return NativeCommandRunner::commandExists('zip') && NativeCommandRunner::commandExists('unzip'); } + /** + * Check if native directory copy commands are available. + * + * @return bool True if native directory copy is available. + */ public static function canUseNativeDirectoryCopy(): bool { if (PHP_OS_FAMILY === 'Windows') { @@ -23,6 +33,11 @@ public static function canUseNativeDirectoryCopy(): bool return NativeCommandRunner::commandExists('rsync'); } + /** + * Check if native file copy commands are available. + * + * @return bool True if native file copy is available. + */ public static function canUseNativeFileCopy(): bool { if (PHP_OS_FAMILY === 'Windows') { diff --git a/src/Observability/AuditTrail.php b/src/Observability/AuditTrail.php index b52d9bc..fddfeae 100644 --- a/src/Observability/AuditTrail.php +++ b/src/Observability/AuditTrail.php @@ -16,11 +16,22 @@ public function __construct(private string $logFilePath) } } + /** + * Get the log file path. + * + * @return string The normalized log file path. + */ public function getLogFilePath(): string { return PathHelper::normalize($this->logFilePath); } + /** + * Log an operation with context. + * + * @param string $operation The operation name. + * @param array $context Additional context data. + */ public function log(string $operation, array $context = []): void { $record = [ diff --git a/src/Queue/FileJobQueue.php b/src/Queue/FileJobQueue.php index abff493..248d50d 100644 --- a/src/Queue/FileJobQueue.php +++ b/src/Queue/FileJobQueue.php @@ -18,6 +18,14 @@ public function __construct(private string $queueFilePath) } } + /** + * Add a job to the queue. + * + * @param string $type The job type. + * @param array $payload The job payload data. + * @param int $priority The job priority (higher is more important). + * @return string The job ID. + */ public function enqueue(string $type, array $payload = [], int $priority = 0): string { $jobId = uniqid('job_', true); @@ -84,6 +92,11 @@ public function process(callable $handler, int $maxJobs = 0): array ]; } + /** + * Get queue statistics. + * + * @return array Array with pending, processing, failed counts and file path. + */ public function stats(): array { $data = $this->readQueueData(); diff --git a/src/Security/PolicyEngine.php b/src/Security/PolicyEngine.php index 6ddf350..c898f84 100644 --- a/src/Security/PolicyEngine.php +++ b/src/Security/PolicyEngine.php @@ -11,6 +11,14 @@ final class PolicyEngine */ private array $rules = []; + /** + * Allow an operation matching the given pattern. + * + * @param string $operation The operation to allow (e.g., 'read', 'write', '*'). + * @param string $pattern The path pattern to match (e.g., '/path/*'). + * @param callable|null $condition Optional condition callback that must return true. + * @return self This instance for method chaining. + */ public function allow(string $operation, string $pattern = '*', ?callable $condition = null): self { $this->rules[] = [ @@ -23,6 +31,14 @@ public function allow(string $operation, string $pattern = '*', ?callable $condi return $this; } + /** + * Assert that an operation is allowed on a path. + * + * @param string $operation The operation to check. + * @param string $path The path to check. + * @param array $context Additional context for condition evaluation. + * @throws PolicyViolationException If the operation is not allowed. + */ public function assertAllowed(string $operation, string $path, array $context = []): void { if (!$this->isAllowed($operation, $path, $context)) { @@ -30,6 +46,14 @@ public function assertAllowed(string $operation, string $path, array $context = } } + /** + * Deny an operation matching the given pattern. + * + * @param string $operation The operation to deny (e.g., 'read', 'write', '*'). + * @param string $pattern The path pattern to match (e.g., '/path/*'). + * @param callable|null $condition Optional condition callback that must return true. + * @return self This instance for method chaining. + */ public function deny(string $operation, string $pattern = '*', ?callable $condition = null): self { $this->rules[] = [ @@ -42,6 +66,14 @@ public function deny(string $operation, string $pattern = '*', ?callable $condit return $this; } + /** + * Check if an operation is allowed on a path. + * + * @param string $operation The operation to check. + * @param string $path The path to check. + * @param array $context Additional context for condition evaluation. + * @return bool True if allowed, false otherwise. + */ public function isAllowed(string $operation, string $path, array $context = []): bool { $decision = true; diff --git a/src/Storage/StorageFactory.php b/src/Storage/StorageFactory.php index 0c695dc..f0350f5 100644 --- a/src/Storage/StorageFactory.php +++ b/src/Storage/StorageFactory.php @@ -93,6 +93,9 @@ final class StorageFactory /** @var array): FilesystemOperator> */ private static array $drivers = []; + /** + * Clear all registered custom drivers. + */ public static function clearDrivers(): void { self::$drivers = []; @@ -134,16 +137,33 @@ public static function createFilesystem(array $config): FilesystemOperator ); } + /** + * Get the names of all registered custom drivers. + * + * @return array The driver names. + */ public static function driverNames(): array { return array_keys(self::$drivers); } + /** + * Check if a custom driver is registered. + * + * @param string $name The driver name. + * @return bool True if the driver is registered, false otherwise. + */ public static function hasDriver(string $name): bool { return isset(self::$drivers[self::canonicalDriverName($name)]); } + /** + * Check if a driver is an official Flysystem driver. + * + * @param string $driver The driver name. + * @return bool True if it's an official driver, false otherwise. + */ public static function isOfficialDriver(string $driver): bool { return isset(self::OFFICIAL_DRIVERS[self::canonicalDriverName($driver)]); @@ -171,7 +191,9 @@ public static function mountMany(array $mounts): void } /** - * @return array + * Get all official driver metadata. + * + * @return array The official drivers. */ public static function officialDrivers(): array { @@ -191,6 +213,12 @@ public static function registerDriver(string $name, callable $factory): void self::$drivers[$driver] = $factory; } + /** + * Get the suggested package for an official driver. + * + * @param string $driver The driver name. + * @return string|null The package name, or null if not an official driver. + */ public static function suggestedPackage(string $driver): ?string { $normalized = self::canonicalDriverName($driver); @@ -198,6 +226,11 @@ public static function suggestedPackage(string $driver): ?string return self::OFFICIAL_DRIVERS[$normalized]['package'] ?? null; } + /** + * Unregister a custom driver. + * + * @param string $name The driver name to unregister. + */ public static function unregisterDriver(string $name): void { unset(self::$drivers[self::canonicalDriverName($name)]); diff --git a/src/StreamHandler/DownloadProcessor.php b/src/StreamHandler/DownloadProcessor.php index 22abb86..c969a58 100644 --- a/src/StreamHandler/DownloadProcessor.php +++ b/src/StreamHandler/DownloadProcessor.php @@ -104,16 +104,31 @@ public function setAllowedRoots(array $roots): void } } + /** + * Set whether to block hidden files from being downloaded. + * + * @param bool $block If true, block hidden files. + */ public function setBlockHiddenFiles(bool $block = true): void { $this->blockHiddenFiles = $block; } + /** + * Set the chunk size for streaming downloads. + * + * @param int $chunkSize The chunk size in bytes. + */ public function setChunkSize(int $chunkSize): void { $this->chunkSize = max(1024, $chunkSize); } + /** + * Set the default download name for files without a name. + * + * @param string $name The default download name. + */ public function setDefaultDownloadName(string $name): void { $safe = $this->sanitizeFilename($name); @@ -132,16 +147,31 @@ public function setExtensionPolicy(array $allowedExtensions = [], array $blocked : $this->normalizeExtensions($blockedExtensions); } + /** + * Set whether to force attachment disposition. + * + * @param bool $enabled If true, force attachment disposition. + */ public function setForceAttachment(bool $enabled = true): void { $this->forceAttachment = $enabled; } + /** + * Set the maximum allowed download size. + * + * @param int $maxDownloadSize The maximum size in bytes, or 0 for unlimited. + */ public function setMaxDownloadSize(int $maxDownloadSize = 0): void { $this->maxDownloadSize = max(0, $maxDownloadSize); } + /** + * Enable or disable HTTP range requests. + * + * @param bool $enabled If true, enable range requests. + */ public function setRangeRequestsEnabled(bool $enabled = true): void { $this->rangeRequestsEnabled = $enabled; diff --git a/src/StreamHandler/UploadProcessor.php b/src/StreamHandler/UploadProcessor.php index d5a0a6b..281f7e9 100644 --- a/src/StreamHandler/UploadProcessor.php +++ b/src/StreamHandler/UploadProcessor.php @@ -271,6 +271,11 @@ public function setImageValidationSettings(int $maxImageWidth = 0, int $maxImage $this->maxImageHeight = max(0, $maxImageHeight); } + /** + * Set the logger for upload operations. + * + * @param LoggerInterface $logger The logger instance. + */ public function setLogger(LoggerInterface $logger): void { $this->logger = $logger; diff --git a/src/Utils/FlysystemHelper.php b/src/Utils/FlysystemHelper.php index 5d67c26..d86748e 100644 --- a/src/Utils/FlysystemHelper.php +++ b/src/Utils/FlysystemHelper.php @@ -17,6 +17,14 @@ final class FlysystemHelper /** @var array */ private static array $mounts = []; + /** + * Calculate the checksum of a file. + * + * @param string $path The file path. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @param array $config Additional configuration. + * @return string|null The checksum, or null if the file doesn't exist or algorithm is unsupported. + */ public static function checksum(string $path, string $algorithm = 'sha256', array $config = []): ?string { if (!in_array($algorithm, hash_algos(), true) || !self::fileExists($path)) { @@ -30,16 +38,29 @@ public static function checksum(string $path, string $algorithm = 'sha256', arra return $filesystem->checksum($location, $config); } + /** + * Clear the default filesystem. + */ public static function clearDefaultFilesystem(): void { self::$defaultFilesystem = null; } + /** + * Clear all mounted filesystems. + */ public static function clearMounts(): void { self::$mounts = []; } + /** + * Copy a file from source to destination. + * + * @param string $source The source file path. + * @param string $destination The destination file path. + * @param array $config Additional configuration. + */ public static function copy(string $source, string $destination, array $config = []): void { [$destinationFilesystem, $destinationLocation] = self::filesystemForFile($destination); @@ -64,6 +85,13 @@ public static function copy(string $source, string $destination, array $config = } } + /** + * Copy a directory recursively from source to destination. + * + * @param string $source The source directory path. + * @param string $destination The destination directory path. + * @param array $config Additional configuration. + */ public static function copyDirectory(string $source, string $destination, array $config = []): void { [$sourceFilesystem, $sourceLocation] = self::filesystemForDirectory($source); @@ -101,24 +129,46 @@ public static function copyDirectory(string $source, string $destination, array } } + /** + * Create a directory. + * + * @param string $path The directory path. + * @param array $config Additional configuration. + */ public static function createDirectory(string $path, array $config = []): void { [$filesystem, $location] = self::filesystemForDirectory($path); $filesystem->createDirectory($location, $config); } + /** + * Delete a file. + * + * @param string $path The file path. + */ public static function delete(string $path): void { [$filesystem, $location] = self::filesystemForFile($path); $filesystem->delete($location); } + /** + * Delete a directory. + * + * @param string $path The directory path. + */ public static function deleteDirectory(string $path): void { [$filesystem, $location] = self::filesystemForDirectory($path); $filesystem->deleteDirectory($location); } + /** + * Check if a directory exists. + * + * @param string $path The directory path. + * @return bool True if the directory exists, false otherwise. + */ public static function directoryExists(string $path): bool { try { @@ -130,6 +180,12 @@ public static function directoryExists(string $path): bool } } + /** + * Check if a file exists. + * + * @param string $path The file path. + * @return bool True if the file exists, false otherwise. + */ public static function fileExists(string $path): bool { try { @@ -141,6 +197,12 @@ public static function fileExists(string $path): bool } } + /** + * Check if a path exists (file or directory). + * + * @param string $path The path to check. + * @return bool True if the path exists, false otherwise. + */ public static function has(string $path): bool { try { @@ -152,11 +214,22 @@ public static function has(string $path): bool } } + /** + * Check if a default filesystem is set. + * + * @return bool True if a default filesystem is set, false otherwise. + */ public static function hasDefaultFilesystem(): bool { return self::$defaultFilesystem !== null; } + /** + * Get the last modified timestamp of a file. + * + * @param string $path The file path. + * @return int The last modified timestamp. + */ public static function lastModified(string $path): int { [$filesystem, $location] = self::filesystemForFile($path); @@ -181,6 +254,13 @@ public static function listContents(string $path, bool $deep = true): array return $items; } + /** + * List directory contents as a DirectoryListing. + * + * @param string $path The directory path. + * @param bool $deep Whether to list recursively. + * @return DirectoryListing The directory listing. + */ public static function listContentsListing(string $path, bool $deep = true): DirectoryListing { [$filesystem, $location] = self::filesystemForDirectory($path); @@ -188,6 +268,12 @@ public static function listContentsListing(string $path, bool $deep = true): Dir return $filesystem->listContents($location, $deep); } + /** + * Get the MIME type of a file. + * + * @param string $path The file path. + * @return string|null The MIME type, or null if the file doesn't exist. + */ public static function mimeType(string $path): ?string { if (!self::fileExists($path)) { @@ -205,6 +291,13 @@ public static function mount(string $name, FilesystemOperator $filesystem): void self::$mounts[$normalized] = $filesystem; } + /** + * Move a file from source to destination. + * + * @param string $source The source file path. + * @param string $destination The destination file path. + * @param array $config Additional configuration. + */ public static function move(string $source, string $destination, array $config = []): void { [$destinationFilesystem, $destinationLocation] = self::filesystemForFile($destination); @@ -220,6 +313,13 @@ public static function move(string $source, string $destination, array $config = self::delete($source); } + /** + * Move a directory from source to destination. + * + * @param string $source The source directory path. + * @param string $destination The destination directory path. + * @param array $config Additional configuration. + */ public static function moveDirectory(string $source, string $destination, array $config = []): void { self::copyDirectory($source, $destination, $config); @@ -253,6 +353,9 @@ public static function readStream(string $path): mixed return $filesystem->readStream($location); } + /** + * Reset the helper by clearing default filesystem and mounts. + */ public static function reset(): void { self::clearDefaultFilesystem(); @@ -279,17 +382,34 @@ public static function resolveDirectory(string $path): array return self::filesystemForDirectory($path); } + /** + * Set the default filesystem. + * + * @param FilesystemOperator $filesystem The filesystem to set as default. + */ public static function setDefaultFilesystem(FilesystemOperator $filesystem): void { self::$defaultFilesystem = $filesystem; } + /** + * Set the visibility of a file. + * + * @param string $path The file path. + * @param string $visibility The visibility to set (e.g., 'public' or 'private'). + */ public static function setVisibility(string $path, string $visibility): void { [$filesystem, $location] = self::filesystemForFile($path); $filesystem->setVisibility($location, $visibility); } + /** + * Get the size of a file. + * + * @param string $path The file path. + * @return int The file size in bytes. + */ public static function size(string $path): int { [$filesystem, $location] = self::filesystemForFile($path); @@ -310,12 +430,23 @@ public static function temporaryUrl(string $path, DateTimeInterface $expiresAt, return $callable($location, $expiresAt, $config); } + /** + * Unmount a filesystem. + * + * @param string $name The mount name. + */ public static function unmount(string $name): void { $normalized = self::normalizeMountName($name); unset(self::$mounts[$normalized]); } + /** + * Get the visibility of a file. + * + * @param string $path The file path. + * @return string|null The visibility, or null if not available. + */ public static function visibility(string $path): ?string { [$filesystem, $location] = self::filesystemForFile($path); @@ -323,12 +454,26 @@ public static function visibility(string $path): ?string return $filesystem->visibility($location); } + /** + * Write contents to a file. + * + * @param string $path The file path. + * @param string $contents The contents to write. + * @param array $config Additional configuration. + */ public static function write(string $path, string $contents, array $config = []): void { [$filesystem, $location] = self::filesystemForFile($path); $filesystem->write($location, $contents, $config); } + /** + * Write to a file from a stream. + * + * @param string $path The file path. + * @param mixed $stream The stream resource. + * @param array $config Additional configuration. + */ public static function writeStream(string $path, mixed $stream, array $config = []): void { [$filesystem, $location] = self::filesystemForFile($path); diff --git a/src/Utils/Ownership/FallbackOwnershipResolver.php b/src/Utils/Ownership/FallbackOwnershipResolver.php index c534569..6f2f815 100644 --- a/src/Utils/Ownership/FallbackOwnershipResolver.php +++ b/src/Utils/Ownership/FallbackOwnershipResolver.php @@ -4,6 +4,12 @@ final class FallbackOwnershipResolver implements OwnershipResolverInterface { + /** + * Get the username of the user who last modified the file. + * + * @param string $path The file path. + * @return string|null The username, or null if the file doesn't exist. + */ public function getLastModifiedBy(string $path): ?string { if (!file_exists($path)) { @@ -13,6 +19,12 @@ public function getLastModifiedBy(string $path): ?string $user = get_current_user(); return $user !== '' ? $user : null; } + /** + * Get ownership details for a file. + * + * @param string $path The file path. + * @return array|null Array with 'owner' and 'group' keys, or null if the file doesn't exist. + */ public function getOwnershipDetails(string $path): ?array { if (!file_exists($path)) { diff --git a/src/Utils/Ownership/OwnershipResolverFactory.php b/src/Utils/Ownership/OwnershipResolverFactory.php index 185134a..42c1da0 100644 --- a/src/Utils/Ownership/OwnershipResolverFactory.php +++ b/src/Utils/Ownership/OwnershipResolverFactory.php @@ -4,6 +4,11 @@ final class OwnershipResolverFactory { + /** + * Create an appropriate ownership resolver for the current platform. + * + * @return OwnershipResolverInterface The ownership resolver instance. + */ public static function create(): OwnershipResolverInterface { if (PHP_OS_FAMILY === 'Windows') { diff --git a/src/Utils/Ownership/PosixOwnershipResolver.php b/src/Utils/Ownership/PosixOwnershipResolver.php index 818e8b3..104ea3e 100644 --- a/src/Utils/Ownership/PosixOwnershipResolver.php +++ b/src/Utils/Ownership/PosixOwnershipResolver.php @@ -4,6 +4,12 @@ final class PosixOwnershipResolver implements OwnershipResolverInterface { + /** + * Get the username of the user who last modified the file. + * + * @param string $path The file path. + * @return string|null The username, or null if the file doesn't exist. + */ public function getLastModifiedBy(string $path): ?string { if (!file_exists($path)) { @@ -18,6 +24,12 @@ public function getLastModifiedBy(string $path): ?string $ownerInfo = posix_getpwuid($ownerId); return is_array($ownerInfo) ? ($ownerInfo['name'] ?? null) : null; } + /** + * Get ownership details for a file. + * + * @param string $path The file path. + * @return array|null Array with 'owner' and 'group' keys, or null if the file doesn't exist. + */ public function getOwnershipDetails(string $path): ?array { if (!file_exists($path)) { diff --git a/src/Utils/Ownership/WindowsOwnershipResolver.php b/src/Utils/Ownership/WindowsOwnershipResolver.php index 7705d32..17819b5 100644 --- a/src/Utils/Ownership/WindowsOwnershipResolver.php +++ b/src/Utils/Ownership/WindowsOwnershipResolver.php @@ -4,6 +4,12 @@ final class WindowsOwnershipResolver implements OwnershipResolverInterface { + /** + * Get the username of the user who last modified the file. + * + * @param string $path The file path. + * @return string|null The username, or null if the file doesn't exist. + */ public function getLastModifiedBy(string $path): ?string { if (!file_exists($path)) { @@ -18,6 +24,12 @@ public function getLastModifiedBy(string $path): ?string $currentUser = get_current_user(); return $currentUser !== '' ? $currentUser : null; } + /** + * Get ownership details for a file. + * + * @param string $path The file path. + * @return array|null Array with 'owner' and 'group' keys, or null if the file doesn't exist. + */ public function getOwnershipDetails(string $path): ?array { if (!file_exists($path)) { diff --git a/src/Utils/PathHelper.php b/src/Utils/PathHelper.php index 4687323..3c2932c 100644 --- a/src/Utils/PathHelper.php +++ b/src/Utils/PathHelper.php @@ -157,6 +157,12 @@ public static function getPathType(string $path): ?string return is_link($path) ? 'link' : null; } + /** + * Check if a path has a scheme (e.g., 's3://', 'ftp://'). + * + * @param string $path The path to check. + * @return bool True if the path has a scheme, false otherwise. + */ public static function hasScheme(string $path): bool { return preg_match('/^[a-zA-Z0-9._-]+:\/\//', $path) === 1; From 344196a35ecb01a842e6835f28427a63962390e8 Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Tue, 14 Apr 2026 21:50:28 +0600 Subject: [PATCH 3/3] docblock --- src/File.php | 62 +++++++++++++++++++++---- src/FileManager/FileCompression.php | 2 + src/Indexing/ChecksumIndexer.php | 16 +++++-- src/Queue/FileJobQueue.php | 6 ++- src/Retention/RetentionManager.php | 6 ++- src/Storage/StorageFactory.php | 22 +++++++-- src/StreamHandler/DownloadProcessor.php | 25 ++++++++-- src/StreamHandler/UploadProcessor.php | 45 +++++++++++++++++- src/Utils/FileWatcher.php | 13 +++++- src/Utils/FlysystemHelper.php | 41 +++++++++++++++- src/functions.php | 9 ++-- 11 files changed, 216 insertions(+), 31 deletions(-) diff --git a/src/File.php b/src/File.php index 2fe9d0c..a5a8411 100644 --- a/src/File.php +++ b/src/File.php @@ -56,7 +56,10 @@ public static function audit(string $logFilePath): AuditTrail } /** - * @param array $config + * Create a Flysystem filesystem from configuration. + * + * @param array $config The filesystem configuration. + * @return FilesystemOperator The created filesystem. */ public static function createFilesystem(array $config): FilesystemOperator { @@ -64,7 +67,11 @@ public static function createFilesystem(array $config): FilesystemOperator } /** - * @return array{linked: array, skipped: array} + * Deduplicate files in a directory using hard links. + * + * @param string $directory The directory to deduplicate. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return array{linked: array, skipped: array} Array with linked and skipped file paths. */ public static function deduplicate(string $directory, string $algorithm = 'sha256'): array { @@ -72,7 +79,11 @@ public static function deduplicate(string $directory, string $algorithm = 'sha25 } /** - * @return array{created: array, modified: array, deleted: array} + * Compare two snapshots and return the differences. + * + * @param array $previousSnapshot The previous snapshot data. + * @param array $currentSnapshot The current snapshot data. + * @return array{created: array, modified: array, deleted: array} The diff report. */ public static function diffSnapshots(array $previousSnapshot, array $currentSnapshot): array { @@ -90,7 +101,11 @@ public static function download(): DownloadProcessor } /** - * @return array> + * Find duplicate files in a directory. + * + * @param string $directory The directory to search for duplicates. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return array> Array mapping checksum to duplicate file paths. */ public static function duplicates(string $directory, string $algorithm = 'sha256'): array { @@ -109,7 +124,11 @@ public static function from(string $path): self } /** - * @return array> checksum => paths + * Build a checksum index for all files in a directory. + * + * @param string $directory The directory to index. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return array> Array mapping checksum to file paths. */ public static function index(string $directory, string $algorithm = 'sha256'): array { @@ -117,7 +136,11 @@ public static function index(string $directory, string $algorithm = 'sha256'): a } /** - * @param array $config + * Create and mount a filesystem under a name. + * + * @param string $name The mount name. + * @param array $config The filesystem configuration. + * @return FilesystemOperator The created filesystem. */ public static function mountStorage(string $name, array $config): FilesystemOperator { @@ -125,7 +148,9 @@ public static function mountStorage(string $name, array $config): FilesystemOper } /** - * @param array> $mounts + * Mount multiple filesystems at once. + * + * @param array> $mounts Array of mount name => config pairs. */ public static function mountStorages(array $mounts): void { @@ -154,7 +179,13 @@ public static function queue(string $queueFilePath): FileJobQueue } /** - * @return array{deleted: array, kept: array} + * Apply retention rules to a directory. + * + * @param string $directory The directory to apply retention rules to. + * @param int|null $keepLast Number of most recent files to keep (null for unlimited). + * @param int|null $maxAgeDays Maximum age of files in days (null for unlimited). + * @param string $sortBy Field to sort by ('mtime' or 'ctime'). + * @return array{deleted: array, kept: array} Array with deleted and kept file paths. */ public static function retain( string $directory, @@ -166,7 +197,11 @@ public static function retain( } /** - * @return array + * Build a snapshot map for a file or directory. + * + * @param string $path The path to snapshot. + * @param bool $recursive Whether to include subdirectories recursively. + * @return array The snapshot map. */ public static function snapshot(string $path, bool $recursive = true): array { @@ -184,7 +219,14 @@ public static function upload(): UploadProcessor } /** - * @return array + * Poll for file-system changes and invoke callback on each non-empty diff. + * + * @param string $path The path to watch. + * @param callable $onChange Callback invoked when changes detected. + * @param int $durationSeconds How long to watch in seconds. Defaults to 5. + * @param int $intervalMilliseconds Polling interval in milliseconds. Defaults to 500. + * @param bool $recursive Whether to watch subdirectories. Defaults to true. + * @return array Final snapshot. */ public static function watch( string $path, diff --git a/src/FileManager/FileCompression.php b/src/FileManager/FileCompression.php index 477fd8f..70aa13f 100644 --- a/src/FileManager/FileCompression.php +++ b/src/FileManager/FileCompression.php @@ -473,6 +473,7 @@ public function setIgnoreFileNames(array $ignoreFileNames): self * @param callable $logger The logger callable. The callable should accept * two arguments: the first is a string message, and the second is the * ZipArchive object. + * @return self This instance for method chaining. */ public function setLogger(callable $logger): self { @@ -485,6 +486,7 @@ public function setLogger(callable $logger): self * Set the password for the ZIP archive. * * @param string $password The password to encrypt the ZIP archive with. + * @return self This instance for method chaining. */ public function setPassword(string $password): self { diff --git a/src/Indexing/ChecksumIndexer.php b/src/Indexing/ChecksumIndexer.php index 7574382..54f5245 100644 --- a/src/Indexing/ChecksumIndexer.php +++ b/src/Indexing/ChecksumIndexer.php @@ -11,7 +11,11 @@ final class ChecksumIndexer { /** - * @return array> checksum => paths + * Build a checksum index for all files in a directory. + * + * @param string $directory The directory to index. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return array> Array mapping checksum to array of file paths. */ public static function buildIndex(string $directory, string $algorithm = 'sha256'): array { @@ -38,7 +42,9 @@ public static function buildIndex(string $directory, string $algorithm = 'sha256 /** * Deduplicate files by replacing duplicate entries with hard links where supported. * - * @return array{linked: array, skipped: array} + * @param string $directory The directory to deduplicate. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return array{linked: array, skipped: array} Array with linked and skipped file paths. */ public static function deduplicateWithHardLinks(string $directory, string $algorithm = 'sha256'): array { @@ -77,7 +83,11 @@ public static function deduplicateWithHardLinks(string $directory, string $algor } /** - * @return array> + * Find duplicate files in a directory. + * + * @param string $directory The directory to search for duplicates. + * @param string $algorithm The hash algorithm to use. Defaults to 'sha256'. + * @return array> Array mapping checksum to array of duplicate file paths. */ public static function findDuplicates(string $directory, string $algorithm = 'sha256'): array { diff --git a/src/Queue/FileJobQueue.php b/src/Queue/FileJobQueue.php index 248d50d..fde4036 100644 --- a/src/Queue/FileJobQueue.php +++ b/src/Queue/FileJobQueue.php @@ -45,7 +45,11 @@ public function enqueue(string $type, array $payload = [], int $priority = 0): s } /** - * @return array{processed: int, failed: int} + * Process jobs from the queue. + * + * @param callable $handler Callback to process each job. Receives job array as argument. + * @param int $maxJobs Maximum number of jobs to process (0 for unlimited). + * @return array{processed: int, failed: int} Array with processed and failed counts. */ public function process(callable $handler, int $maxJobs = 0): array { diff --git a/src/Retention/RetentionManager.php b/src/Retention/RetentionManager.php index 9df7281..523663e 100644 --- a/src/Retention/RetentionManager.php +++ b/src/Retention/RetentionManager.php @@ -13,7 +13,11 @@ final class RetentionManager /** * Apply retention rules to a directory. * - * @return array{deleted: array, kept: array} + * @param string $directory The directory to apply retention rules to. + * @param int|null $keepLast Number of most recent files to keep (null for unlimited). + * @param int|null $maxAgeDays Maximum age of files in days (null for unlimited). + * @param string $sortBy Field to sort by ('mtime' or 'ctime'). + * @return array{deleted: array, kept: array} Array with deleted and kept file paths. */ public static function apply( string $directory, diff --git a/src/Storage/StorageFactory.php b/src/Storage/StorageFactory.php index f0350f5..7ae455d 100644 --- a/src/Storage/StorageFactory.php +++ b/src/Storage/StorageFactory.php @@ -102,7 +102,11 @@ public static function clearDrivers(): void } /** - * @param array $config + * Create a filesystem from configuration. + * + * @param array $config The filesystem configuration. + * @return FilesystemOperator The created filesystem. + * @throws \InvalidArgumentException If the driver is unsupported. */ public static function createFilesystem(array $config): FilesystemOperator { @@ -170,7 +174,11 @@ public static function isOfficialDriver(string $driver): bool } /** - * @param array $config + * Create and mount a filesystem under a name. + * + * @param string $name The mount name. + * @param array $config The filesystem configuration. + * @return FilesystemOperator The created filesystem. */ public static function mount(string $name, array $config): FilesystemOperator { @@ -181,7 +189,9 @@ public static function mount(string $name, array $config): FilesystemOperator } /** - * @param array> $mounts + * Mount multiple filesystems at once. + * + * @param array> $mounts Array of mount name => config pairs. */ public static function mountMany(array $mounts): void { @@ -201,7 +211,11 @@ public static function officialDrivers(): array } /** - * @param callable(array): FilesystemOperator $factory + * Register a custom driver factory. + * + * @param string $name The driver name. + * @param callable(array): FilesystemOperator $factory Factory that receives config and returns filesystem. + * @throws \InvalidArgumentException If the driver name is empty. */ public static function registerDriver(string $name, callable $factory): void { diff --git a/src/StreamHandler/DownloadProcessor.php b/src/StreamHandler/DownloadProcessor.php index c969a58..43a97a3 100644 --- a/src/StreamHandler/DownloadProcessor.php +++ b/src/StreamHandler/DownloadProcessor.php @@ -24,6 +24,9 @@ class DownloadProcessor /** * Build secure download metadata for a file path. * + * @param string $path The file path to download. + * @param string|null $downloadName The desired download filename (null to use original). + * @param string|null $rangeHeader The HTTP Range header value (null for no range). * @return array{ * path: string, * fileName: string, @@ -36,7 +39,10 @@ class DownloadProcessor * rangeEnd: int, * contentLength: int, * headers: array - * } + * } The download metadata array. + * @throws DownloadException If the file is hidden and blocked. + * @throws FileNotFoundException If the file doesn't exist. + * @throws FileSizeExceededException If the file exceeds max download size. */ public function prepareDownload(string $path, ?string $downloadName = null, ?string $rangeHeader = null): array { @@ -91,7 +97,9 @@ public function prepareDownload(string $path, ?string $downloadName = null, ?str } /** - * @param array $roots + * Set the allowed root directories for downloads. + * + * @param array $roots Array of allowed root directory paths. */ public function setAllowedRoots(array $roots): void { @@ -136,8 +144,10 @@ public function setDefaultDownloadName(string $name): void } /** - * @param array $allowedExtensions - * @param array $blockedExtensions + * Set extension allow/block policy for downloads. + * + * @param array $allowedExtensions Array of allowed extensions (empty for all). + * @param array $blockedExtensions Array of blocked extensions (default: dangerous types). */ public function setExtensionPolicy(array $allowedExtensions = [], array $blockedExtensions = []): void { @@ -180,6 +190,10 @@ public function setRangeRequestsEnabled(bool $enabled = true): void /** * Stream a secure download to a writable resource and return the manifest. * + * @param string $path The file path to download. + * @param mixed $outputStream The output stream resource to write to. + * @param string|null $downloadName The desired download filename (null to use original). + * @param string|null $rangeHeader The HTTP Range header value (null for no range). * @return array{ * path: string, * fileName: string, @@ -193,7 +207,8 @@ public function setRangeRequestsEnabled(bool $enabled = true): void * contentLength: int, * bytesSent: int, * headers: array - * } + * } The download manifest with bytes sent. + * @throws DownloadException If the output stream is invalid or download fails. */ public function streamDownload( string $path, diff --git a/src/StreamHandler/UploadProcessor.php b/src/StreamHandler/UploadProcessor.php index 281f7e9..33ba70f 100644 --- a/src/StreamHandler/UploadProcessor.php +++ b/src/StreamHandler/UploadProcessor.php @@ -58,6 +58,10 @@ class UploadProcessor /** * Finalize a resumable upload by assembling all uploaded chunks. + * + * @param string $uploadId The unique upload identifier. + * @return string The path to the final assembled file. + * @throws UploadException If the upload directory is not set or chunks are missing. */ public function finalizeChunkUpload(string $uploadId): string { @@ -83,6 +87,8 @@ public function finalizeChunkUpload(string $uploadId): string /** * Get detailed info about the current configuration and settings. + * + * @return array Array with upload configuration details. */ public function getInfo(): array { @@ -106,6 +112,8 @@ public function getInfo(): array /** * Retrieve available validation profiles. + * + * @return array List of available validation profile names. */ public function getValidationProfiles(): array { @@ -115,7 +123,13 @@ public function getValidationProfiles(): array /** * Process an upload chunk and persist resumable state. * - * @return array{uploadId: string, receivedChunks: int, totalChunks: int, isComplete: bool} + * @param array $chunkFile The chunk file data from $_FILES. + * @param string $uploadId The unique upload identifier. + * @param int $chunkIndex The index of this chunk (0-based). + * @param int $totalChunks Total number of chunks expected. + * @param string $originalFilename The original filename. + * @return array{uploadId: string, receivedChunks: int, totalChunks: int, isComplete: bool} Chunk upload status. + * @throws UploadException If the upload directory is not set. */ public function processChunkUpload(array $chunkFile, string $uploadId, int $chunkIndex, int $totalChunks, string $originalFilename): array { @@ -157,6 +171,10 @@ public function processChunkUpload(array $chunkFile, string $uploadId, int $chun /** * Process the upload and save the file. + * + * @param array $file The file data from $_FILES. + * @return string The path to the saved file. + * @throws UploadException If validation fails or upload directory is not set. */ public function processUpload(array $file): string { @@ -228,6 +246,9 @@ public function processUpload(array $file): string /** * Configure chunk upload constraints. + * + * @param int $maxChunkCount Maximum number of chunks allowed (0 for unlimited). + * @param int $maxChunkSize Maximum size per chunk in bytes (0 for unlimited). */ public function setChunkLimits(int $maxChunkCount = 0, int $maxChunkSize = 0): void { @@ -237,6 +258,10 @@ public function setChunkLimits(int $maxChunkCount = 0, int $maxChunkSize = 0): v /** * Configure directory and path settings. + * + * @param string $uploadDir The upload directory path. + * @param bool $useDateDirectories Whether to use date-based subdirectories. + * @param string|null $tempDir The temporary directory path (null for system default). */ public function setDirectorySettings(string $uploadDir, bool $useDateDirectories = false, ?string $tempDir = null): void { @@ -264,6 +289,9 @@ public function setExtensionPolicy(array $allowedExtensions = [], array $blocked /** * Configure optional image dimension validation. + * + * @param int $maxImageWidth Maximum image width in pixels (0 for unlimited). + * @param int $maxImageHeight Maximum image height in pixels (0 for unlimited). */ public function setImageValidationSettings(int $maxImageWidth = 0, int $maxImageHeight = 0): void { @@ -285,6 +313,8 @@ public function setLogger(LoggerInterface $logger): void * Configure an optional malware scanner callback. * * Signature: fn(string $filePath, string $mimeType): bool + * + * @param callable $scanner The malware scanner callback. */ public function setMalwareScanner(callable $scanner): void { @@ -293,6 +323,9 @@ public function setMalwareScanner(callable $scanner): void /** * Configure naming strategy. + * + * @param string $namingStrategy The naming strategy ('hash' or 'timestamp'). + * @throws UploadException If an invalid strategy is specified. */ public function setNamingStrategy(string $namingStrategy): void { @@ -304,6 +337,8 @@ public function setNamingStrategy(string $namingStrategy): void /** * Require malware scanning before upload acceptance. + * + * @param bool $required If true, require malware scanning. */ public function setRequireMalwareScan(bool $required = true): void { @@ -312,6 +347,8 @@ public function setRequireMalwareScan(bool $required = true): void /** * Enable strict content checks (MIME-extension agreement + magic signature). + * + * @param bool $enabled If true, enable strict content type validation. */ public function setStrictContentTypeValidation(bool $enabled = true): void { @@ -320,6 +357,9 @@ public function setStrictContentTypeValidation(bool $enabled = true): void /** * Configure validation using a predefined profile. + * + * @param string $profile The validation profile name ('image', 'video', or 'document'). + * @throws UploadException If an invalid profile is specified. */ public function setValidationProfile(string $profile): void { @@ -337,6 +377,9 @@ public function setValidationProfile(string $profile): void /** * Configure validation settings. + * + * @param array $allowedFileTypes Array of allowed MIME types. + * @param int $maxFileSize Maximum file size in bytes. */ public function setValidationSettings(array $allowedFileTypes, int $maxFileSize): void { diff --git a/src/Utils/FileWatcher.php b/src/Utils/FileWatcher.php index fb3d7db..ae7ee53 100644 --- a/src/Utils/FileWatcher.php +++ b/src/Utils/FileWatcher.php @@ -11,7 +11,9 @@ final class FileWatcher /** * Compare snapshots and return change report. * - * @return array{created: array, modified: array, deleted: array} + * @param array $previousSnapshot The previous snapshot data. + * @param array $currentSnapshot The current snapshot data. + * @return array{created: array, modified: array, deleted: array} The diff report with created, modified, and deleted files. */ public static function diff(array $previousSnapshot, array $currentSnapshot): array { @@ -47,7 +49,9 @@ public static function diff(array $previousSnapshot, array $currentSnapshot): ar /** * Build a snapshot map for a file or directory. * - * @return array + * @param string $path The path to snapshot. + * @param bool $recursive Whether to include subdirectories recursively. + * @return array The snapshot map with file paths as keys. */ public static function snapshot(string $path, bool $recursive = true): array { @@ -100,6 +104,11 @@ public static function snapshot(string $path, bool $recursive = true): array /** * Poll for file-system changes and invoke callback on each non-empty diff. * + * @param string $path The path to watch. + * @param callable $onChange Callback invoked when changes detected. Receives diff array. + * @param int $durationSeconds How long to watch in seconds. Defaults to 5. + * @param int $intervalMilliseconds Polling interval in milliseconds. Defaults to 500. + * @param bool $recursive Whether to watch subdirectories. Defaults to true. * @return array Final snapshot. */ public static function watch( diff --git a/src/Utils/FlysystemHelper.php b/src/Utils/FlysystemHelper.php index d86748e..6f28492 100644 --- a/src/Utils/FlysystemHelper.php +++ b/src/Utils/FlysystemHelper.php @@ -238,7 +238,11 @@ public static function lastModified(string $path): int } /** - * @return array + * List directory contents as an array. + * + * @param string $path The directory path. + * @param bool $deep Whether to list recursively. Defaults to true. + * @return array The list of contents. */ public static function listContents(string $path, bool $deep = true): array { @@ -285,6 +289,12 @@ public static function mimeType(string $path): ?string return $filesystem->mimeType($location); } + /** + * Mount a filesystem under a name. + * + * @param string $name The mount name. + * @param FilesystemOperator $filesystem The filesystem to mount. + */ public static function mount(string $name, FilesystemOperator $filesystem): void { $normalized = self::normalizeMountName($name); @@ -326,6 +336,14 @@ public static function moveDirectory(string $source, string $destination, array self::deleteDirectory($source); } + /** + * Get the public URL for a file. + * + * @param string $path The file path. + * @param array $config Additional configuration for URL generation. + * @return string The public URL. + * @throws \RuntimeException If public URL generation is not supported. + */ public static function publicUrl(string $path, array $config = []): string { [$filesystem, $location] = self::filesystemForFile($path); @@ -339,6 +357,12 @@ public static function publicUrl(string $path, array $config = []): string return $callable($location, $config); } + /** + * Read the contents of a file. + * + * @param string $path The file path. + * @return string The file contents. + */ public static function read(string $path): string { [$filesystem, $location] = self::filesystemForFile($path); @@ -346,6 +370,12 @@ public static function read(string $path): string return $filesystem->read($location); } + /** + * Read a file as a stream. + * + * @param string $path The file path. + * @return mixed The file stream resource. + */ public static function readStream(string $path): mixed { [$filesystem, $location] = self::filesystemForFile($path); @@ -417,6 +447,15 @@ public static function size(string $path): int return $filesystem->fileSize($location); } + /** + * Get a temporary URL for a file. + * + * @param string $path The file path. + * @param DateTimeInterface $expiresAt The expiration time. + * @param array $config Additional configuration for URL generation. + * @return string The temporary URL. + * @throws \RuntimeException If temporary URL generation is not supported. + */ public static function temporaryUrl(string $path, DateTimeInterface $expiresAt, array $config = []): string { [$filesystem, $location] = self::filesystemForFile($path); diff --git a/src/functions.php b/src/functions.php index 502b15e..b1aaf65 100644 --- a/src/functions.php +++ b/src/functions.php @@ -179,7 +179,8 @@ function copyDirectory(string $source, string $destination): bool * - ['adapter' => $flysystemAdapter] * - ['driver' => 'custom', ...] after StorageFactory::registerDriver() * - * @param array $config + * @param array $config The filesystem configuration. + * @return FilesystemOperator The created filesystem. */ function createFilesystem(array $config): FilesystemOperator { @@ -191,7 +192,9 @@ function createFilesystem(array $config): FilesystemOperator /** * Build and mount a filesystem under a scheme name. * - * @param array $config + * @param string $name The mount name. + * @param array $config The filesystem configuration. + * @return FilesystemOperator The created filesystem. */ function mountStorage(string $name, array $config): FilesystemOperator { @@ -203,7 +206,7 @@ function mountStorage(string $name, array $config): FilesystemOperator /** * Build and mount multiple filesystems. * - * @param array> $mounts + * @param array> $mounts Array of mount name => config pairs. */ function mountStorages(array $mounts): void {