Upload Processing#
Namespace: Infocyph\Pathwise\StreamHandler
Where it fits:
Use this module for HTTP uploads that need validation, deterministic naming, resumable chunk flow, and layered upload hardening.
UploadProcessor supports:
Standard upload handling with configurable destination strategy.
Validation profiles:
image,video,document.MIME and size validation with optional image dimension validation.
Extension allowlist/blocklist policy.
Naming strategies (hash/timestamp).
Chunked/resumable uploads: *
processChunkUpload()*finalizeChunkUpload()Upload ID safety validation for chunk/session identifiers.
Strict content checks: * extension <> MIME agreement * lightweight file signature verification for common formats
Malware scanner callback hook (optional or required).
Storage notes:
Uses Flysystem operations for chunk manifests and destination writes.
Supports mounted/default filesystem routing through helper resolution.
For adapter setup (S3/SFTP/FTP/custom), see
storage-adapters.
Security Hardening Controls#
UploadProcessor exposes explicit controls for upload policy:
setExtensionPolicy(array $allowedExtensions = [], array $blockedExtensions = [])to enforce extension allow/deny policies.setChunkLimits(int $maxChunkCount = 0, int $maxChunkSize = 0)to cap chunk count and per-chunk size.setRequireMalwareScan(bool $required = true)to reject uploads if scanner execution is required but unavailable.setStrictContentTypeValidation(bool $enabled = true)to enforce extension-to-MIME agreement and signature checks.
Chunk upload IDs are validated and must contain only:
letters/numbers
-and_
Identifiers with separators such as / or traversal patterns are rejected.
Examples#
Basic single upload:
use Infocyph\Pathwise\StreamHandler\UploadProcessor;
$uploader = new UploadProcessor();
$uploader->setDirectorySettings('/tmp/uploads');
$uploader->setValidationProfile('document');
$uploader->setExtensionPolicy(['pdf', 'doc', 'docx'], ['php', 'phtml', 'phar']);
$uploader->setStrictContentTypeValidation(true);
$finalPath = $uploader->processUpload($_FILES['file']);
Resumable chunk flow:
$state = $uploader->processChunkUpload(
chunkFile: $_FILES['chunk'],
uploadId: 'session-42',
chunkIndex: 0,
totalChunks: 4,
originalFilename: 'video.mp4',
);
if ($state['isComplete']) {
$finalPath = $uploader->finalizeChunkUpload('session-42');
}
Hardened chunk upload:
$uploader->setChunkLimits(maxChunkCount: 20, maxChunkSize: 2 * 1024 * 1024); // 2MB
$uploader->setRequireMalwareScan(true);
$uploader->setMalwareScanner(
fn (string $path, string $type): bool => true // return false to block
);
$uploader->processChunkUpload(
chunkFile: $_FILES['chunk'],
uploadId: 'session_42',
chunkIndex: 0,
totalChunks: 4,
originalFilename: 'video.mp4',
);
Mounted destination example:
use Infocyph\Pathwise\Storage\StorageFactory;
use Infocyph\Pathwise\StreamHandler\UploadProcessor;
StorageFactory::mount('s3', ['adapter' => $myS3Adapter]);
$uploader = new UploadProcessor();
$uploader->setDirectorySettings('s3://uploads', false, 's3://tmp');
$uploader->setValidationProfile('document');
$finalPath = $uploader->processUpload($_FILES['file']);