本文目录导读:

在 PHP 中模拟文件系统主要有以下几种方法,我会详细介绍每种方法及其适用场景:
使用内存文件系统(推荐)
使用 php://memory 和 php://temp
<?php
// 在内存中创建虚拟文件
$fp = fopen('php://memory', 'r+');
fwrite($fp, 'Hello World');
rewind($fp);
echo stream_get_contents($fp); // 输出: Hello World
fclose($fp);
// php://temp 会自动切换到临时文件(当数据超过2MB时)
$fp = fopen('php://temp', 'r+');
fwrite($fp, str_repeat('A', 3000000)); // 3MB 数据
rewind($fp);
echo strlen(stream_get_contents($fp)); // 3000000
fclose($fp);
使用自定义流包装器
通过实现 StreamWrapper 接口来创建虚拟文件系统:
<?php
class VirtualFileSystem implements StreamWrapper {
private $files = [];
private $currentDir = '';
public function stream_open($path, $mode, $options, &$opened_path) {
$this->currentDir = $path;
return true;
}
public function stream_read($count) {
// 实现读取逻辑
return '';
}
public function stream_write($data) {
// 实现写入逻辑
return strlen($data);
}
// 其他必须实现的方法...
}
// 注册自定义协议
stream_wrapper_register('vfs', 'VirtualFileSystem');
// 使用虚拟文件系统
$file = file_get_contents('vfs://somefile.txt');
使用第三方库
League\Flysystem - 最流行的选择
<?php
require 'vendor/autoload.php';
use League\Flysystem\Filesystem;
use League\Flysystem\InMemory\InMemoryFilesystemAdapter;
// 创建内存文件系统
$filesystem = new Filesystem(new InMemoryFilesystemAdapter());
// 使用文件系统
$filesystem->write('test.txt', 'Hello World');
$content = $filesystem->read('test.txt');
echo $content; // Hello World
$filesystem->createDirectory('subdir');
$filesystem->write('subdir/file2.txt', 'Another file');
// 检查文件是否存在
if ($filesystem->fileExists('test.txt')) {
echo '文件存在';
}
Symfony Filesystem 组件
<?php
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
$fs = new Filesystem();
try {
// 创建目录
$fs->mkdir('/tmp/virtual_dir');
// 写入文件
$fs->dumpFile('/tmp/virtual_dir/file.txt', '内容');
// 读取文件
$content = file_get_contents('/tmp/virtual_dir/file.txt');
// 清理
$fs->remove('/tmp/virtual_dir');
} catch (IOExceptionInterface $exception) {
echo "错误: " . $exception->getMessage();
}
实现简单的内存文件系统类
<?php
class MemoryFileSystem {
private $files = [];
private $directories = ['./'];
public function write($path, $content) {
$this->files[$path] = $content;
return true;
}
public function read($path) {
if (!isset($this->files[$path])) {
throw new Exception("文件不存在: $path");
}
return $this->files[$path];
}
public function exists($path) {
return isset($this->files[$path]);
}
public function delete($path) {
if (!isset($this->files[$path])) {
throw new Exception("文件不存在: $path");
}
unset($this->files[$path]);
return true;
}
public function listDirectory($dir = '.') {
$result = [];
foreach ($this->files as $path => $content) {
if (strpos($path, $dir . '/') === 0) {
$result[] = $path;
}
}
return $result;
}
public function getInfo($path) {
if (!isset($this->files[$path])) {
return false;
}
return [
'size' => strlen($this->files[$path]),
'modified' => time(),
'content' => $this->files[$path]
];
}
}
// 使用示例
$vfs = new MemoryFileSystem();
$vfs->write('/var/log/app.log', "日志内容\n第二行");
$vfs->write('/var/config.json', '{"key": "value"}');
// 读取文件
echo $vfs->read('/var/log/app.log');
// 检查文件
var_dump($vfs->exists('/var/config.json')); // true
// 列出目录
$files = $vfs->listDirectory('/var');
print_r($files);
使用 ZIP 作为虚拟文件系统
<?php
// 创建内存中的 ZIP 文件作为虚拟文件系统
$zip = new ZipArchive();
$tempFile = tempnam(sys_get_temp_dir(), 'vfs_');
$zip->open($tempFile, ZipArchive::CREATE);
// 添加文件
$zip->addFromString('config/database.php', '<?php return [];');
$zip->addFromString('public/index.php', '<?php echo "Hello";');
$zip->close();
// 使用 ZIP 流读取
$zip = new ZipArchive();
$zip->open($tempFile);
echo $zip->getFromName('config/database.php');
$zip->close();
// 清理
unlink($tempFile);
使用虚拟文件系统协议封装
<?php
class StreamVFS {
private static $registry = [];
public static function register() {
stream_wrapper_register('vfs', __CLASS__);
}
public static function setFile($path, $content) {
self::$registry[$path] = $content;
}
public static function getFile($path) {
return isset(self::$registry[$path]) ? self::$registry[$path] : null;
}
// 流包装器必需的方法
public function stream_open($path, $mode, $options, &$opened_path) {
return true;
}
public function stream_read($count) {
return '';
}
public function stream_write($data) {
return strlen($data);
}
public function stream_eof() {
return true;
}
public function stream_tell() {
return 0;
}
public function stream_seek($offset, $whence) {
return true;
}
public function stream_stat() {
return [];
}
public function stream_close() {}
}
StreamVFS::register();
StreamVFS::setFile('/test.txt', 'Hello Virtual World');
echo file_get_contents('vfs://test.txt');
选择建议
- 测试场景:使用 PHPUnit 的虚拟文件系统(vfsStream)库
- 简单演示:使用
php://memory或自定义类 - 复杂应用:使用 Flysystem 的适配器模式
- 需要真实文件操作:使用临时目录 + 真实文件系统
PHPUnit 中的虚拟文件系统
<?php
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase {
public function testFileOperations() {
// 创建虚拟文件系统
$root = vfsStream::setup('home');
// 创建虚拟文件
vfsStream::newFile('test.txt')
->at($root)
->setContent('Hello');
// 测试文件操作
$this->assertFileExists(vfsStream::url('home/test.txt'));
$this->assertEquals('Hello', file_get_contents(vfsStream::url('home/test.txt')));
}
}
这些方法各有优缺点,选择时需要考虑:
- 性能和内存使用
- 功能需求(权限、目录操作等)
- 易用性和维护成本
- 是否需要在测试环境使用