covex-nn/joos-stream
JooS_Stream provides a PHP stream wrapper for a virtual filesystem protocol mapped to a base directory. Register a custom scheme, then use standard functions like file_put_contents, unlink, and file_exists on scheme:// paths; unregister when done.
Installation:
composer require covex-nn/joos-stream
Add to composer.json if not auto-loaded:
"autoload": {
"psr-4": {
"JooS\\Stream\\": "vendor/covex-nn/joos-stream/src/"
}
}
Run composer dump-autoload.
First Use Case:
Register a virtual filesystem (VFS) protocol (e.g., test-vfs) pointing to a real directory (e.g., storage/app/test-vfs):
use JooS\Stream\Wrapper_FS;
Wrapper_FS::register('test-vfs', storage_path('app/test-vfs'));
Now interact with files via the custom protocol:
file_put_contents('test-vfs://test.txt', 'Hello, VFS!');
$content = file_get_contents('test-vfs://test.txt');
Key Files to Explore:
src/Wrapper_FS.php: Core stream wrapper logic.src/Transaction.php: Transaction management for atomic operations.tests/: Example test cases for edge cases.Transactional File Operations: Use transactions to batch operations atomically:
Wrapper_FS::beginTransaction('test-vfs');
file_put_contents('test-vfs://file1.txt', 'Data 1');
file_put_contents('test-vfs://file2.txt', 'Data 2');
Wrapper_FS::commit('test-vfs'); // Applies all changes
// OR
Wrapper_FS::rollback('test-vfs'); // Discards all changes
Dynamic Protocol Registration: Register/unregister protocols dynamically (e.g., per-request or per-test):
// In a service provider or middleware
Wrapper_FS::register('user-'.$userId, $userStoragePath);
Integration with Laravel:
Storage::extend('vfs', function ($app) {
return new \JooS\Stream\Wrapper_FS('vfs', storage_path('app/vfs'));
});
Configure in config/filesystems.php:
'disks' => [
'vfs' => [
'driver' => 'vfs',
'root' => 'vfs://',
],
];
public function testFileOperations()
{
Wrapper_FS::register('test-vfs', __DIR__.'/tmp');
// Test logic here...
Wrapper_FS::unregister('test-vfs');
}
Stream Wrapper Delegation:
Delegate operations to other stream wrappers (e.g., s3, ftp) by extending Wrapper_FS:
class S3Wrapper extends Wrapper_FS {
public function stream_open($path, $mode, $options, &$opened_path) {
// Custom S3 logic here
}
}
Protocol Naming Conflicts:
Avoid registering protocols that clash with PHP’s built-in wrappers (e.g., http, ftp, zlib). Use unique prefixes like app-vfs://.
Transaction Leaks:
Uncommitted transactions consume memory and disk space. Always commit() or rollback():
try {
Wrapper_FS::beginTransaction('test-vfs');
// Operations...
Wrapper_FS::commit('test-vfs');
} catch (\Exception $e) {
Wrapper_FS::rollback('test-vfs');
throw $e;
}
Case Sensitivity: Paths in the VFS are case-sensitive on Linux but not on Windows. Normalize paths if cross-platform support is needed:
$normalizedPath = strtolower('test-vfs://File.TXT');
Permission Issues:
The underlying real directory must be writable by the PHP process. Use chmod or adjust permissions:
chmod -R 775 storage/app/test-vfs
Stream Context Limitations:
Some PHP functions (e.g., copy(), rename()) may not work as expected with custom stream wrappers. Fall back to lower-level methods:
$source = fopen('test-vfs://source.txt', 'r');
$dest = fopen('test-vfs://dest.txt', 'w');
stream_copy_to_stream($source, $dest);
fclose($source);
fclose($dest);
Enable Stream Wrapper Debugging:
Add this to php.ini or runtime:
stream_wrapper_debug = 1
stream_wrapper_log = /tmp/stream.log
Check /tmp/stream.log for wrapper activity.
Verify Registration: List registered wrappers to debug issues:
$wrappers = stream_get_wrappers();
print_r($wrappers);
Transaction Inspection: Check active transactions (if extended):
$transactions = Wrapper_FS::getActiveTransactions('test-vfs');
print_r($transactions);
Custom Stream Handlers:
Extend Wrapper_FS to add support for custom operations (e.g., encryption, compression):
class EncryptedWrapper extends Wrapper_FS {
public function stream_open($path, $mode, $options, &$opened_path) {
$decryptedPath = $this->decryptPath($path);
return parent::stream_open($decryptedPath, $mode, $options, $opened_path);
}
}
Event Hooks: Add pre/post hooks for operations (e.g., logging, analytics):
Wrapper_FS::addHook('test-vfs', 'pre_write', function ($path, $data) {
Log::debug("Writing to $path", ['data' => $data]);
});
Fallback Mechanisms: Implement fallback logic for unsupported operations:
public function stream_lock($operation) {
throw new \RuntimeException("Locking not supported for this wrapper");
}
How can I help you explore Laravel packages today?