Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 78 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ Pathwise is a robust PHP library designed as streamlined file and directory mana
- [PathHelper](#pathhelper)
- [PermissionsHelper](#permissionshelper)
- [MetadataHelper](#metadatahelper)
9. [Handy Functions](#handy-functions)
9. [Storage Adapter Setup](#storage-adapter-setup)
10. [Handy Functions](#handy-functions)
- [File and Directory Utilities](#file-and-directory-utilities)
10. [Support](#support)
11. [License](#license)
11. [Support](#support)
12. [License](#license)

## **Prerequisites**
- Language: PHP 8.4/+
Expand All @@ -57,12 +58,85 @@ 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.
- 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 pipelines: chunked/resumable uploads, validation profiles (image/video/document), extension allow/deny controls, strict MIME/signature checks, upload-id safety validation, malware-scan hook.
- 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.
- Compression workflows: include/exclude glob patterns, ignore files, progress callbacks, hooks, optional native acceleration.
- Operational tooling: `AuditTrail`, `FileJobQueue`, `FileWatcher`, `RetentionManager` and policy engine support.

## **Storage Adapter Setup**

Pathwise supports any Flysystem adapter. You can mount storages through `StorageFactory` and use them with all modules (`UploadProcessor`, `DownloadProcessor`, `FileOperations`, etc.).

`StorageFactory` supports:
- `['driver' => 'local', 'root' => '/path']`
- `['driver' => 'aws-s3', 'adapter' => $adapter]`
- `['driver' => 'aws-s3', 'constructor' => [...]]`
- `['filesystem' => $filesystemOperator]`
- custom drivers via `StorageFactory::registerDriver()`

Official adapter driver keys covered:
- `local`, `ftp`, `inmemory` (`in-memory`)
- `read-only`, `path-prefixing`
- `aws-s3` (`s3`), `async-aws-s3`
- `azure-blob-storage`, `google-cloud-storage`, `mongodb-gridfs`
- `sftp-v2`, `sftp-v3`, `webdav`, `ziparchive`

### **Local Driver**

```php
use Infocyph\Pathwise\Storage\StorageFactory;
use Infocyph\Pathwise\Utils\FlysystemHelper;

StorageFactory::mount('assets', [
'driver' => 'local',
'root' => '/srv/storage/assets',
]);

FlysystemHelper::write('assets://reports/a.txt', 'hello');
```

### **Any Adapter (Example: S3)**

```php
use Aws\S3\S3Client;
use Infocyph\Pathwise\Storage\StorageFactory;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;

$client = new S3Client([
'version' => 'latest',
'region' => 'us-east-1',
'credentials' => [
'key' => getenv('AWS_ACCESS_KEY_ID'),
'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
],
]);

$adapter = new AwsS3V3Adapter($client, 'my-bucket', 'app-prefix');

StorageFactory::mount('s3', ['adapter' => $adapter]);
// Use s3://... paths in processors and managers.
```

### **Custom Driver Registration**

```php
use Infocyph\Pathwise\Storage\StorageFactory;
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;

StorageFactory::registerDriver('tenant-local', function (array $config): Filesystem {
$tenant = (string) ($config['tenant'] ?? 'default');
return new Filesystem(new LocalFilesystemAdapter('/srv/tenants/' . $tenant));
});

StorageFactory::mount('tenant', [
'driver' => 'tenant-local',
'tenant' => 'acme',
]);
```

## **FileManager**

The `FileManager` module provides classes for handling files, including reading, writing, compressing and general file operations.
Expand Down
15 changes: 14 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,20 @@
"ext-pcntl": "required if you want to use long-running watch loops.",
"ext-posix": "required if you want to use permissions.",
"ext-xmlreader": "required if you want to use XML parsing.",
"ext-simplexml": "required if you want to use XML parsing."
"ext-simplexml": "required if you want to use XML parsing.",
"league/flysystem-aws-s3-v3": "required for AWS S3 adapter support.",
"league/flysystem-async-aws-s3": "required for AsyncAWS S3 adapter support.",
"league/flysystem-azure-blob-storage": "required for Azure Blob Storage adapter support.",
"league/flysystem-google-cloud-storage": "required for Google Cloud Storage adapter support.",
"league/flysystem-gridfs": "required for MongoDB GridFS adapter support.",
"league/flysystem-sftp-v3": "required for SFTP (v3) adapter support.",
"league/flysystem-sftp-v2": "required for SFTP (v2) adapter support.",
"league/flysystem-ftp": "required for FTP adapter support.",
"league/flysystem-webdav": "required for WebDAV adapter support.",
"league/flysystem-ziparchive": "required for ZipArchive adapter support.",
"league/flysystem-memory": "required for in-memory adapter support.",
"league/flysystem-read-only": "required for read-only adapter wrapper support.",
"league/flysystem-path-prefixing": "required for path-prefixing adapter wrapper support."
},
"require-dev": {
"captainhook/captainhook": "^5.29.2",
Expand Down
19 changes: 19 additions & 0 deletions docs/capabilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,22 @@ What you get:
* Strict content checks (MIME-extension agreement and file signature checks).
* Optional or required malware scan callback.

Downloads (``Infocyph\Pathwise\StreamHandler``)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Class:

* ``DownloadProcessor``

What you get:

* Secure download metadata generation for HTTP adapters.
* Extension allowlist/blocklist controls.
* Allowed-root restriction to prevent path breakout.
* Hidden-file blocking and max-size limits.
* Optional range request handling and partial-download metadata.
* Stream copy into caller-provided output resources.

Security and Operations
^^^^^^^^^^^^^^^^^^^^^^^

Expand All @@ -84,6 +100,9 @@ Pathwise accepts:
Mounting is done through ``FlysystemHelper::mount()``. Once mounted, most
high-level modules can use the scheme path directly.

For config-driven adapter bootstrap, use
``Infocyph\Pathwise\Storage\StorageFactory`` (see ``storage-adapters``).

Runtime and Extensions
----------------------

Expand Down
85 changes: 85 additions & 0 deletions docs/download-processing.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
Download Processing
===================

Namespace: ``Infocyph\Pathwise\StreamHandler``

Where it fits:

* Use this module when you need secure download metadata and controlled stream
delivery for local or mounted filesystems.

``DownloadProcessor`` supports:

* Download metadata generation with headers suitable for HTTP adapters.
* Safe download filename handling for ``Content-Disposition``.
* Extension allowlist/blocklist controls.
* Allowed-root restrictions to prevent serving files outside trusted paths.
* Hidden-file blocking.
* Optional max download size enforcement.
* Optional range requests with byte-range parsing and partial metadata.
* Stream copy to caller-provided output resource.
* Mounted/default filesystem paths (e.g. ``s3://...``) via Flysystem routing.

Security controls
-----------------

``DownloadProcessor`` exposes explicit hardening options:

* ``setAllowedRoots(array $roots)``
* ``setExtensionPolicy(array $allowedExtensions = [], array $blockedExtensions = [])``
* ``setBlockHiddenFiles(bool $block = true)``
* ``setMaxDownloadSize(int $maxDownloadSize = 0)``
* ``setRangeRequestsEnabled(bool $enabled = true)``
* ``setForceAttachment(bool $enabled = true)``
* ``setDefaultDownloadName(string $name)``
* ``setChunkSize(int $chunkSize)``

Examples
--------

Prepare secure metadata:

.. code-block:: php

use Infocyph\Pathwise\StreamHandler\DownloadProcessor;

$downloads = new DownloadProcessor();
$downloads->setAllowedRoots(['/srv/app/downloads']);
$downloads->setExtensionPolicy(['pdf', 'zip'], ['php', 'phar', 'exe']);

$manifest = $downloads->prepareDownload(
path: '/srv/app/downloads/report.pdf',
downloadName: 'monthly-report.pdf',
rangeHeader: null,
);

// Use $manifest['status'] and $manifest['headers'] in your framework response.

Stream output with range support:

.. code-block:: php

$output = fopen('php://output', 'wb');

$manifest = $downloads->streamDownload(
path: '/srv/app/downloads/video.mp4',
outputStream: $output,
downloadName: 'video.mp4',
rangeHeader: $_SERVER['HTTP_RANGE'] ?? null,
);

// $manifest includes status, headers, rangeStart/rangeEnd and bytesSent.

Mounted storage example:

.. code-block:: php

use Infocyph\Pathwise\Storage\StorageFactory;
use Infocyph\Pathwise\StreamHandler\DownloadProcessor;

StorageFactory::mount('s3', ['adapter' => $myS3Adapter]);

$downloads = new DownloadProcessor();
$downloads->setAllowedRoots(['s3://downloads']);

$manifest = $downloads->prepareDownload('s3://downloads/report.pdf');
12 changes: 12 additions & 0 deletions docs/helper-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ Available helpers (brief):
* ``createDirectory(string $directoryPath, int $permissions = 0755): bool``
* ``listFiles(string $directoryPath): array``
* ``copyDirectory(string $source, string $destination): bool``
* ``createFilesystem(array $config): FilesystemOperator``
* ``mountStorage(string $name, array $config): FilesystemOperator``
* ``mountStorages(array $mounts): void``

Notes:

Expand All @@ -28,3 +31,12 @@ Example

$size = getDirectorySize('/tmp/demo');
$files = listFiles('/tmp/demo');

Storage setup helper example:

.. code-block:: php

mountStorage('assets', [
'driver' => 'local',
'root' => '/srv/storage/assets',
]);
2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,13 @@ queue/audit tooling, and operational helpers.
overview
installation
capabilities
storage-adapters
quickstart
recipes
file-manager
directory-manager
upload-processing
download-processing
security
queue
observability
Expand Down
18 changes: 18 additions & 0 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,24 @@ Optional extensions:
* ``ext-posix`` for richer Unix ownership details.
* ``ext-xmlreader`` and ``ext-simplexml`` for XML helpers.

Optional adapter packages (choose per driver):

* AWS S3: ``league/flysystem-aws-s3-v3`` + ``aws/aws-sdk-php``
* Async AWS S3: ``league/flysystem-async-aws-s3`` + ``async-aws/s3``
* Azure Blob Storage: ``league/flysystem-azure-blob-storage``
* Google Cloud Storage: ``league/flysystem-google-cloud-storage``
* MongoDB GridFS: ``league/flysystem-gridfs``
* SFTP: ``league/flysystem-sftp-v3``
* SFTP (V2): ``league/flysystem-sftp-v2``
* FTP: ``league/flysystem-ftp``
* WebDAV: ``league/flysystem-webdav``
* ZIP archive: ``league/flysystem-ziparchive``
* In-memory: ``league/flysystem-memory``
* Read-only wrapper: ``league/flysystem-read-only``
* Path prefixing wrapper: ``league/flysystem-path-prefixing``

See ``storage-adapters`` for setup patterns.

Where to Use First
------------------

Expand Down
1 change: 1 addition & 0 deletions docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Main namespaces:
* ``Infocyph\Pathwise\FileManager``
* ``Infocyph\Pathwise\DirectoryManager``
* ``Infocyph\Pathwise\StreamHandler``
* ``Infocyph\Pathwise\Storage``
* ``Infocyph\Pathwise\Security``
* ``Infocyph\Pathwise\Queue``
* ``Infocyph\Pathwise\Observability``
Expand Down
10 changes: 5 additions & 5 deletions docs/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ This quickstart shows the fastest way to understand what Pathwise can do.

.. code-block:: php

use Infocyph\Pathwise\Storage\StorageFactory;
use Infocyph\Pathwise\Utils\FlysystemHelper;
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;

FlysystemHelper::mount('assets', new Filesystem(
new LocalFilesystemAdapter('/srv/storage/assets')
));
StorageFactory::mount('assets', [
'driver' => 'local',
'root' => '/srv/storage/assets',
]);

FlysystemHelper::write('assets://reports/a.txt', "hello\n");
$text = FlysystemHelper::read('assets://reports/a.txt');
Expand Down
10 changes: 5 additions & 5 deletions docs/recipes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ Goal:
.. code-block:: php

use Infocyph\Pathwise\FileManager\FileCompression;
use Infocyph\Pathwise\Storage\StorageFactory;
use Infocyph\Pathwise\Utils\FlysystemHelper;
use League\Flysystem\Filesystem;
use League\Flysystem\Local\LocalFilesystemAdapter;

FlysystemHelper::mount('mnt', new Filesystem(
new LocalFilesystemAdapter('/srv/storage')
));
StorageFactory::mount('mnt', [
'driver' => 'local',
'root' => '/srv/storage',
]);

FlysystemHelper::write('mnt://source/a.txt', 'A');
FlysystemHelper::write('mnt://source/b.txt', 'B');
Expand Down
Loading
Loading