php-vfs/php-vfs
Simple virtual filesystem via a PHP stream wrapper for unit tests. Emulates files/dirs in memory so you can test code that reads/writes the filesystem without temporary folders or fixtures. Works with PHPUnit, PHPSpec, and other frameworks.
Installation:
composer require --dev php-vfs/php-vfs
Add to composer.json under require-dev:
"php-vfs/php-vfs": "^1.4"
First Use Case: Create a virtual filesystem in a test class:
use org\bovigo\vfs\vfsStream;
public function testFileOperations()
{
$root = vfsStream::setup('root');
$file = vfsStream::create(['file.txt' => 'Hello, VFS!']);
$this->assertFileExists($root->url() . '/file.txt');
}
Key Entry Points:
vfsStream::setup(): Creates a virtual root directory.vfsStream::create(): Builds a directory structure from an array.$root->url(): Gets the filesystem root URL for use with PHP filesystem functions.Isolated File Operations:
public function testFileReadWrite()
{
$root = vfsStream::setup('test');
$file = vfsStream::create(['data.txt' => 'Test content']);
$content = file_get_contents($root->url() . '/data.txt');
$this->assertEquals('Test content', $content);
}
Directory Structure Mocking:
$structure = [
'project' => [
'src' => ['index.php' => '<?php echo "Hello";'],
'tests' => ['TestCase.php' => '<?php class TestCase {}']
]
];
$root = vfsStream::create($structure);
Integration with Laravel:
Storage::fake() (Laravel 8+) for Laravel-specific filesystem testing.$root = vfsStream::setup('storage');
Storage::fake('local');
Storage::disk('local')->put('file.txt', 'Content');
$this->assertEquals('Content', file_get_contents($root->url() . '/file.txt'));
Symlinks and Permissions:
$root = vfsStream::setup('links');
vfsStream::create([
'original.txt' => 'Original',
'link.txt' => 'link'
]);
$root->addChild(vfsStream::symlink('link.txt', 'original.txt'));
$this->assertTrue(is_link($root->url() . '/link.txt'));
Testing File Uploads:
public function testUploadHandler()
{
$root = vfsStream::setup('uploads');
$file = vfsStream::create(['photo.jpg' => 'fake binary data']);
$request = new UploadedFile($root->url() . '/photo.jpg', 'photo.jpg');
$handler = new FileUploadHandler();
$this->assertTrue($handler->handle($request));
}
Mocking Storage Facade:
Use Storage::fake() for Laravel's filesystem, but fall back to VFS for legacy code:
public function testLegacyStorage()
{
$root = vfsStream::setup('legacy');
Storage::disk('local')->put('file.txt', 'Legacy content');
$this->assertEquals('Legacy content', file_get_contents($root->url() . '/file.txt'));
}
Testing filesystem Config:
Override config in tests:
$this->app->instance('path.storage', $root->url());
Path Resolution:
$root->url() to resolve paths, not relative paths.file_get_contents('data.txt'); // Fails (relative to current dir)
file_get_contents($root->url() . '/data.txt');
Glob and Directory Iteration:
glob(); use DirectoryIterator or readdir():
$iterator = new DirectoryIterator($root->url());
foreach ($iterator as $file) {
// Handle file
}
File Permissions:
chmod() if needed:
chmod($root->url() . '/file.txt', 0644);
Stream Wrapper Conflicts:
zip) if they interfere:
stream_wrapper_unregister('zip');
Laravel Caching:
$this->app->make('config')->set('filesystems.disks.local.root', $root->url());
Inspect Structure:
$this->assertTrue($root->hasChild('expected/file.txt'));
$this->assertEquals('Content', $root->getChild('file.txt')->getContent());
Log File Contents:
$content = file_get_contents($root->url() . '/file.txt');
$this->log($content); // Use Laravel's logger or var_dump
Handle Exceptions:
RuntimeException for missing files/dirs. Catch explicitly:
try {
file_get_contents($root->url() . '/missing.txt');
$this->fail('Expected exception');
} catch (RuntimeException $e) {
$this->assertStringContainsString('No such file', $e->getMessage());
}
Custom Stream Wrappers:
Extend vfsStream for domain-specific needs:
class CustomVfs extends vfsStream {
public static function createWithPermissions(array $structure, int $permissions = 0755) {
// Custom logic
}
}
Mocking File Class:
For Laravel, mock Illuminate/Filesystem/File:
$mock = Mockery::mock('Illuminate/Filesystem/File');
$mock->shouldReceive('exists')->andReturnTrue();
$mock->shouldReceive('get')->andReturn('Content');
Integration with Flysystem: Use VFS as a Flysystem adapter:
$adapter = new VfsAdapter(vfsStream::setup('flysystem'));
$filesystem = new League\Flysystem\Filesystem($adapter);
Fake Storage:
Laravel's Storage::fake() uses a different implementation. Prefer it for Laravel-specific tests:
public function testLaravelStorage()
{
Storage::fake('local');
Storage::disk('local')->put('file.txt', 'Content');
$this->assertTrue(Storage::disk('local')->exists('file.txt'));
}
Config Overrides:
Override filesystem.disks in phpunit.xml:
<env name="FILESYSTEM_DISK" value="local"/>
How can I help you explore Laravel packages today?