引言
PHP的流抽象层(Stream Abstraction Layer)是文件系统操作的核心,它统一了文件、网络、压缩等多种数据源的读写方式。本文将深入探讨PHP文件系统和流处理的高级技术。
Stream Wrappers
PHP内置了多种流封装器,可以通过统一的接口操作不同类型的资源。
// PHP 内置流封装器一览
$wrappers = stream_get_wrappers();
printf("Available wrappers: %s\n", implode(', ', $wrappers));
// file:// — 默认本地文件系统
// http://, https:// — HTTP 请求
// ftp://, ftps:// — FTP 访问
// php:// — 各种 I/O 流
// compress.zlib:// — gzip 压缩
// compress.bzip2:// — bzip2 压缩
// zip:// — ZIP 文件
// data:// — 数据 URI
// glob:// — 文件模式匹配
// phar:// — PHP 归档
// php:// 流详解
class PhpStreamDemo
{
// 标准 I/O
public static function stdin(): string
{
return file_get_contents('php://stdin');
}
public static function stdout(string $message): void
{
file_put_contents('php://stdout', $message);
}
public static function stderr(string $message): void
{
file_put_contents('php://stderr', $message);
}
// 内存流
public static function memoryStream(): void
{
$stream = fopen('php://memory', 'r+');
fwrite($stream, 'Hello Memory Stream!');
rewind($stream);
printf("Memory: %s\n", stream_get_contents($stream));
fclose($stream);
}
// 临时文件流(自动清理)
public static function tempStream(int $maxMemory = 8192): void
{
$stream = fopen('php://temp/maxmemory:' . $maxMemory, 'r+');
fwrite($stream, str_repeat('Data ', 1000));
printf("Temp stream size: %d bytes\n", ftell($stream));
rewind($stream);
fclose($stream);
}
// 输入过滤
public static function inputFilter(): void
{
// php://input 读取 POST 原始数据
// php://filter 应用过滤器
$encoded = file_get_contents('php://filter/read=convert.base64-encode/resource=test.txt');
return $encoded;
}
// 输出缓冲
public static function outputBuffer(): void
{
// php://output 用于输出
// php://output 等价于 echo
}
// data URI
public static function dataUri(string $mime, string $data): string
{
$encoded = base64_encode($data);
return "data:$mime;base64,$encoded";
}
}
PhpStreamDemo::memoryStream();
// 处理大文件:逐行读取
class LargeFileProcessor
{
private $handle;
private int $lineCount = 0;
private int $bytePosition = 0;
public function open(string $path): void
{
$this->handle = fopen($path, 'r');
if (!$this->handle) {
throw new RuntimeException("Cannot open file: $path");
}
}
public function readLine(): ?string
{
if (feof($this->handle)) {
return null;
}
$this->lineCount++;
$line = fgets($this->handle);
if ($line !== false) {
$this->bytePosition = ftell($this->handle);
}
return $line;
}
public function readLines(int $count): array
{
$lines = [];
for ($i = 0; $i < $count; $i++) {
$line = $this->readLine();
if ($line === null) {
break;
}
$lines[] = rtrim($line, "\r\n");
}
return $lines;
}
public function seekToLine(int $lineNumber): bool
{
rewind($this->handle);
$this->lineCount = 0;
$this->bytePosition = 0;
for ($i = 0; $i < $lineNumber - 1; $i++) {
if ($this->readLine() === null) {
return false;
}
}
return true;
}
public function close(): void
{
if ($this->handle) {
fclose($this->handle);
}
}
public function getProgress(): array
{
return [
'line' => $this->lineCount,
'byte' => $this->bytePosition,
];
}
}
// 创建一个大文件测试
$testFile = tempnam(sys_get_temp_dir(), 'large_');
$handle = fopen($testFile, 'w');
for ($i = 1; $i <= 10000; $i++) {
fwrite($handle, "Line $i: " . str_repeat('x', rand(10, 100)) . "\n");
}
fclose($handle);
$processor = new LargeFileProcessor();
$processor->open($testFile);
$chunk = $processor->readLines(5);
printf("First 5 lines: %s\n", json_encode($chunk));
$processor->seekToLine(5000);
$midLine = $processor->readLine();
printf("Line 5000: %s\n", trim($midLine));
$processor->close();
unlink($testFile);
// 流上下文(Context)
流上下文允许为流操作设置选项和参数。
class StreamContextBuilder
{
private array $context = [];
public function http(array $options): self
{
$this->context['http'] = $options;
return $this;
}
public function ssl(array $options): self
{
$this->context['ssl'] = $options;
return $this;
}
public function ftp(array $options): self
{
$this->context['ftp'] = $options;
return $this;
}
public function build(): resource
{
return stream_context_create($this->context);
}
// HTTP 请求封装
public static function createHttpClient(array $headers = []): self
{
$builder = new self();
$builder->http([
'method' => 'GET',
'header' => array_map(
fn($k, $v) => "$k: $v",
array_keys($headers),
$headers
),
'timeout' => 30,
'ignore_errors' => false,
'follow_location' => true,
'max_redirects' => 5,
'protocol_version' => '1.1',
'user_agent' => 'PHP Stream Client/1.0',
]);
return $builder;
}
public static function createPostClient(array $data, array $headers = []): self
{
$builder = new self();
$postData = is_array($data) ? http_build_query($data) : $data;
$builder->http([
'method' => 'POST',
'header' => array_merge(
[
'Content-Type: application/x-www-form-urlencoded',
'Content-Length: ' . strlen($postData),
],
array_map(fn($k, $v) => "$k: $v", array_keys($headers), $headers)
),
'content' => $postData,
'timeout' => 30,
]);
return $builder;
}
public static function createSecureContext(string $cafile = null, bool $verifyPeer = true): self
{
$builder = new self();
$sslOptions = [
'verify_peer' => $verifyPeer,
'verify_peer_name' => $verifyPeer,
'allow_self_signed' => !$verifyPeer,
];
if ($cafile) {
$sslOptions['cafile'] = $cafile;
}
$builder->ssl($sslOptions);
return $builder;
}
}
// HTTP GET 请求
$context = StreamContextBuilder::createHttpClient([
'Accept' => 'application/json',
'Authorization' => 'Bearer token123',
])->build();
$response = @file_get_contents('https://httpbin.org/json', false, $context);
if ($response !== false) {
$data = json_decode($response, true);
printf("HTTP Response: %s\n", json_encode($data['slideshow']['title'] ?? 'unknown'));
}
// 获取响应头
$headers = $http_response_header ?? [];
printf("Response headers:\n");
foreach (array_slice($headers, 0, 5) as $header) {
printf(" %s\n", $header);
}
// 流过滤器
流过滤器允许在读写过程中实时转换数据。
class StreamFilterDemo
{
// 内置过滤器
public static function builtIn(): void
{
$filters = stream_get_filters();
printf("Available filters: %s\n", implode(', ', $filters));
}
// 读取时 base64 解码
public static function readBase64(string $file): string
{
$path = 'php://filter/read=convert.base64-decode/resource=' . $file;
return file_get_contents($path);
}
// 写入时 base64 编码
public static function writeBase64(string $file, string $data): void
{
$path = 'php://filter/write=convert.base64-encode/resource=' . $file;
file_put_contents($path, $data);
}
// 字符串编码转换
public static function convertEncoding(string $file, string $from, string $to): string
{
$path = sprintf(
'php://filter/read=convert.iconv.%s/%s/resource=%s',
$from,
$to,
$file
);
return file_get_contents($path);
}
// 使用 zlib 压缩流
public static function compressGzip(string $source, string $dest): void
{
$src = fopen($source, 'r');
$dst = fopen('compress.zlib://' . $dest, 'w');
stream_copy_to_stream($src, $dst);
fclose($src);
fclose($dst);
}
public static function decompressGzip(string $source): string
{
return file_get_contents('compress.zlib://' . $source);
}
}
StreamFilterDemo::builtIn();
// 自定义流过滤器
class UpperCaseFilter extends php_user_filter
{
public function filter($in, $out, &$consumed, bool $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$bucket->data = strtoupper($bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
class ChunkedFilter extends php_user_filter
{
private int $chunkSize = 8;
public function filter($in, $out, &$consumed, bool $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$data = $bucket->data;
$chunked = '';
for ($i = 0; $i < strlen($data); $i += $this->chunkSize) {
$chunked .= substr($data, $i, $this->chunkSize) . "\n";
}
$bucket->data = $chunked;
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
class HexFilter extends php_user_filter
{
public function filter($in, $out, &$consumed, bool $closing): int
{
while ($bucket = stream_bucket_make_writeable($in)) {
$bucket->data = bin2hex($bucket->data);
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
// 注册自定义过滤器
stream_filter_register('uppercase', UpperCaseFilter::class);
stream_filter_register('chunked', ChunkedFilter::class);
stream_filter_register('hex', HexFilter::class);
// 使用自定义过滤器
$tempFile = tempnam(sys_get_temp_dir(), 'filter_');
file_put_contents($tempFile, "hello world\nphp stream filters\n");
$filtered = file_get_contents('php://filter/read=uppercase/resource=' . $tempFile);
printf("Uppercase:\n%s\n", $filtered);
$filtered = file_get_contents('php://filter/read=hex/resource=' . $tempFile);
printf("Hex:\n%s\n", $filtered);
// 在打开的流上附加过滤器
$handle = fopen($tempFile, 'r');
stream_filter_append($handle, 'chunked', STREAM_FILTER_READ, ['chunkSize' => 4]);
$content = stream_get_contents($handle);
printf("Chunked:\n%s\n", $content);
fclose($handle);
unlink($tempFile);
// SplFileObject 和 SplFileInfo
SPL 提供了面向对象的文件处理接口。
class SplFileDemo
{
// 文件信息
public static function fileInfo(string $path): array
{
$info = new SplFileInfo($path);
return [
'basename' => $info->getBasename(),
'filename' => $info->getFilename(),
'extension' => $info->getExtension(),
'path' => $info->getPath(),
'real_path' => $info->getRealPath(),
'size' => $info->getSize(),
'type' => $info->getType(),
'is_file' => $info->isFile(),
'is_dir' => $info->isDir(),
'is_link' => $info->isLink(),
'is_readable' => $info->isReadable(),
'is_writable' => $info->isWritable(),
'is_executable' => $info->isExecutable(),
'permissions' => substr(sprintf('%o', $info->getPerms()), -4),
'owner' => $info->getOwner(),
'group' => $info->getGroup(),
'atime' => date('Y-m-d H:i:s', $info->getATime()),
'mtime' => date('Y-m-d H:i:s', $info->getMTime()),
'ctime' => date('Y-m-d H:i:s', $info->getCTime()),
];
}
// 逐行读取 CSV
public static function readCsv(string $path): array
{
$file = new SplFileObject($path);
$file->setFlags(SplFileObject::READ_CSV);
$file->setCsvControl(',');
$rows = [];
foreach ($file as $row) {
if ($row !== [null]) {
$rows[] = $row;
}
}
return $rows;
}
// 目录迭代器
public static function directoryTree(string $dir): array
{
$result = [];
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS)
);
foreach ($iterator as $file) {
$result[] = [
'path' => $file->getPathname(),
'size' => $file->getSize(),
'depth' => $iterator->getDepth(),
];
}
return $result;
}
// 按扩展名分组文件
public static function groupByExtension(string $dir): array
{
$groups = [];
$iterator = new DirectoryIterator($dir);
foreach ($iterator as $file) {
if ($file->isDot() || $file->isDir()) {
continue;
}
$ext = $file->getExtension();
$groups[$ext][] = $file->getFilename();
}
ksort($groups);
return $groups;
}
// 大文件尾部读取
public static function tail(string $path, int $lines = 10): array
{
$file = new SplFileObject($path);
$file->seek(PHP_INT_MAX);
$totalLines = $file->key();
$start = max(0, $totalLines - $lines);
$result = [];
for ($i = $start; $i < $totalLines; $i++) {
$file->seek($i);
$result[] = rtrim($file->current());
}
return $result;
}
}
// 创建测试文件
$testCsv = tempnam(sys_get_temp_dir(), 'csv_');
$fh = fopen($testCsv, 'w');
foreach (range(1, 100) as $i) {
fputcsv($fh, ["user$i", "user$i@example.com", rand(18, 60)]);
}
fclose($fh);
$csvData = SplFileDemo::readCsv($testCsv);
printf("CSV rows: %d\n", count($csvData));
printf("First row: %s\n", json_encode($csvData[0]));
unlink($testCsv);
// 目录操作
$dirDemo = __DIR__;
$grouped = SplFileDemo::groupByExtension($dirDemo);
printf("File groups in current dir:\n");
foreach ($grouped as $ext => $files) {
printf(" .%s: %d files\n", $ext, count($files));
}
// 文件锁
class FileLocker
{
private $handle;
private string $path;
private bool $locked = false;
public function __construct(string $path)
{
$this->path = $path;
}
public function open(string $mode = 'c+'): void
{
$this->handle = fopen($this->path, $mode);
if (!$this->handle) {
throw new RuntimeException("Cannot open: {$this->path}");
}
}
// 共享锁(读)
public function lockShared(): bool
{
$this->locked = flock($this->handle, LOCK_SH);
return $this->locked;
}
// 独占锁(写)
public function lockExclusive(): bool
{
$this->locked = flock($this->handle, LOCK_EX);
return $this->locked;
}
// 非阻塞尝试
public function tryLockExclusive(): bool
{
$this->locked = flock($this->handle, LOCK_EX | LOCK_NB);
return $this->locked;
}
public function unlock(): bool
{
if ($this->locked) {
$this->locked = !flock($this->handle, LOCK_UN);
}
return !$this->locked;
}
public function read(): string
{
rewind($this->handle);
return stream_get_contents($this->handle);
}
public function write(string $data): int
{
ftruncate($this->handle, 0);
rewind($this->handle);
return fwrite($this->handle, $data);
}
public function close(): void
{
if ($this->locked) {
$this->unlock();
}
if ($this->handle) {
fclose($this->handle);
}
}
public function __destruct()
{
$this->close();
}
}
// 并发安全的计数器
class AtomicCounter
{
private FileLocker $locker;
public function __construct(string $path)
{
$this->locker = new FileLocker($path);
$this->locker->open();
}
public function increment(): int
{
$this->locker->lockExclusive();
$current = (int)trim($this->locker->read());
$current++;
$this->locker->write((string)$current);
$this->locker->unlock();
return $current;
}
public function get(): int
{
$this->locker->lockShared();
$value = (int)trim($this->locker->read());
$this->locker->unlock();
return $value;
}
}
$counterFile = tempnam(sys_get_temp_dir(), 'counter_');
$counter = new AtomicCounter($counterFile);
// 模拟并发
for ($i = 0; $i < 10; $i++) {
$counter->increment();
}
printf("Counter: %d\n", $counter->get());
unlink($counterFile);
// 权限管理
class FilePermissionManager
{
public static function setSecurePermissions(string $path, bool $isDir = false): void
{
if ($isDir) {
chmod($path, 0755);
} else {
chmod($path, 0644);
}
}
// 检查是否安全(不被其他用户可写)
public static function isSecure(string $path): bool
{
$perms = fileperms($path);
// 检查 others 的写权限
return ($perms & 0x0002) === 0;
}
// 设置文件掩码
public static function setUmask(int $umask): int
{
return umask($umask);
}
// 更改所有者
public static function chownRecursive(string $path, string $user, string $group = null): bool
{
if (!file_exists($path)) {
return false;
}
if ($group) {
chgrp($path, $group);
}
chown($path, $user);
if (is_dir($path)) {
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
if ($group) {
chgrp($item->getPathname(), $group);
}
chown($item->getPathname(), $user);
}
}
return true;
}
}
// 临时文件管理
class TempFileManager
{
private array $files = [];
private array $dirs = [];
public function createTempFile(string $prefix = 'tmp_'): string
{
$path = tempnam(sys_get_temp_dir(), $prefix);
$this->files[] = $path;
return $path;
}
public function createTempDir(string $prefix = 'tmp_'): string
{
$path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $prefix . uniqid();
mkdir($path, 0700);
$this->dirs[] = $path;
return $path;
}
public function writeTempFile(string $prefix, string $content): string
{
$path = $this->createTempFile($prefix);
file_put_contents($path, $content);
return $path;
}
public function cleanup(): void
{
foreach ($this->files as $file) {
if (file_exists($file)) {
unlink($file);
}
}
foreach ($this->dirs as $dir) {
$this->removeDirectory($dir);
}
$this->files = [];
$this->dirs = [];
}
private function removeDirectory(string $dir): void
{
if (!is_dir($dir)) {
return;
}
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($iterator as $file) {
if ($file->isDir()) {
rmdir($file->getPathname());
} else {
unlink($file->getPathname());
}
}
rmdir($dir);
}
public function __destruct()
{
$this->cleanup();
}
}
// 跨平台路径处理
class PathHelper
{
public static function normalize(string $path): string
{
$path = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$resolved = [];
foreach ($parts as $part) {
if ($part === '.') {
continue;
}
if ($part === '..') {
array_pop($resolved);
} else {
$resolved[] = $part;
}
}
$prefix = '';
if (DIRECTORY_SEPARATOR === '\\') {
// Windows: 保留盘符和 UNC 路径
if (preg_match('/^[a-zA-Z]:$/', $resolved[0] ?? '')) {
$prefix = array_shift($resolved) . '\\';
} elseif (str_starts_with($path, '\\\\')) {
$prefix = '\\\\';
}
} else {
if (str_starts_with($path, '/')) {
$prefix = '/';
}
}
return $prefix . implode(DIRECTORY_SEPARATOR, $resolved);
}
public static function join(string ...$parts): string
{
$joined = implode(DIRECTORY_SEPARATOR, $parts);
return self::normalize($joined);
}
public static function relativeTo(string $from, string $to): string
{
$fromParts = explode(DIRECTORY_SEPARATOR, self::normalize($from));
$toParts = explode(DIRECTORY_SEPARATOR, self::normalize($to));
// 找到共同路径长度
$commonLength = 0;
$minLength = min(count($fromParts), count($toParts));
for ($i = 0; $i < $minLength; $i++) {
if (strcasecmp($fromParts[$i], $toParts[$i]) === 0) {
$commonLength++;
} else {
break;
}
}
// 上溯到共同目录
$upCount = count($fromParts) - $commonLength - 1;
$relative = array_fill(0, max(0, $upCount), '..');
// 向下到目标
for ($i = $commonLength; $i < count($toParts); $i++) {
$relative[] = $toParts[$i];
}
return implode(DIRECTORY_SEPARATOR, $relative);
}
public static function isAbsolute(string $path): bool
{
if (DIRECTORY_SEPARATOR === '\\') {
return preg_match('/^[a-zA-Z]:\\\\/', $path) === 1 || str_starts_with($path, '\\\\');
}
return str_starts_with($path, '/');
}
}
printf("Normalized: %s\n", PathHelper::normalize('/var//www/../etc/./passwd'));
printf("Joined: %s\n", PathHelper::join('/var', 'www', 'html'));
printf("Relative: %s\n", PathHelper::relativeTo('/var/www/html', '/var/www/config/app.php'));
printf("Is absolute: %s\n", PathHelper::isAbsolute('/etc/passwd') ? 'yes' : 'no');
// 高级:内存映射文件
class MemoryMappedFile
{
private $handle;
private $content;
private string $path;
public function open(string $path): void
{
$this->path = $path;
$this->handle = fopen($path, 'rb');
if (!$this->handle) {
throw new RuntimeException("Cannot open: $path");
}
}
public function map(int $length = null, int $offset = 0): string
{
if ($length === null) {
$length = filesize($this->path) - $offset;
}
// 使用 stream_get_contents 分段读取
fseek($this->handle, $offset);
$this->content = stream_get_contents($this->handle, $length);
return $this->content;
}
public function close(): void
{
$this->content = null;
if ($this->handle) {
fclose($this->handle);
}
}
public function search(string $pattern): array
{
$matches = [];
$offset = 0;
while (($pos = strpos($this->content, $pattern, $offset)) !== false) {
$matches[] = $pos;
$offset = $pos + 1;
}
return $matches;
}
public function getLine(int $lineNumber): ?string
{
$lines = explode("\n", $this->content);
return $lines[$lineNumber - 1] ?? null;
}
public function getContent(): string
{
return $this->content;
}
}
// 流操作综合示例:日志文件监控
class LogFileMonitor
{
private string $path;
private $handle;
private int $lastPosition = 0;
private array $callbacks = [];
public function __construct(string $path)
{
$this->path = $path;
}
public function onPattern(string $pattern, callable $callback): self
{
$this->callbacks[] = ['pattern' => $pattern, 'callback' => $callback];
return $this;
}
public function start(): void
{
$this->handle = fopen($this->path, 'r');
if (!$this->handle) {
throw new RuntimeException("Cannot open: {$this->path}");
}
// 跳到文件末尾
fseek($this->handle, 0, SEEK_END);
$this->lastPosition = ftell($this->handle);
}
public function tick(): array
{
$matches = [];
// 检查文件大小是否变化
clearstatcache(true, $this->path);
$currentSize = filesize($this->path);
if ($currentSize < $this->lastPosition) {
// 文件被截断,重置
fseek($this->handle, 0);
} elseif ($currentSize > $this->lastPosition) {
fseek($this->handle, $this->lastPosition);
$content = stream_get_contents($this->handle);
$lines = explode("\n", rtrim($content, "\n"));
foreach ($lines as $line) {
$matched = false;
foreach ($this->callbacks as $cb) {
if (preg_match($cb['pattern'], $line)) {
($cb['callback'])($line);
$matched = true;
}
}
if ($matched) {
$matches[] = $line;
}
}
}
$this->lastPosition = ftell($this->handle);
return $matches;
}
public function close(): void
{
if ($this->handle) {
fclose($this->handle);
}
}
}
// 创建日志文件并模拟
$logFile = tempnam(sys_get_temp_dir(), 'log_');
$logContent = '';
for ($i = 1; $i <= 100; $i++) {
$level = $i % 10 === 0 ? 'ERROR' : ($i % 5 === 0 ? 'WARNING' : 'INFO');
$logContent .= sprintf(
"[2024-01-15 10:00:%02d] %s: Event %d occurred\n",
$i,
$level,
$i
);
}
file_put_contents($logFile, $logContent);
$monitor = new LogFileMonitor($logFile);
$monitor->onPattern('/ERROR/', fn($line) => printf("ALERT: %s", $line));
$monitor->onPattern('/WARNING/', fn($line) => printf("NOTICE: %s", $line));
$monitor->start();
$monitor->tick();
$monitor->close();
unlink($logFile);
// 流操作性能对比
class StreamBenchmark
{
public static function compare(array $methods, int $iterations = 1000): array
{
$results = [];
foreach ($methods as $name => $fn) {
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
$elapsed = (microtime(true) - $start) * 1000;
$results[$name] = round($elapsed, 2);
}
return $results;
}
}
$testFile = tempnam(sys_get_temp_dir(), 'bench_');
file_put_contents($testFile, str_repeat("Hello World\n", 10000));
$benchmarks = [
'file_get_contents' => function () use ($testFile) {
return file_get_contents($testFile);
},
'fread_all' => function () use ($testFile) {
$h = fopen($testFile, 'r');
$c = fread($h, filesize($testFile));
fclose($h);
return $c;
},
'stream_get_contents' => function () use ($testFile) {
$h = fopen($testFile, 'r');
$c = stream_get_contents($h);
fclose($h);
return $c;
},
'SplFileObject' => function () use ($testFile) {
$f = new SplFileObject($testFile);
$c = $f->fread($f->getSize());
return $c;
},
];
$results = StreamBenchmark::compare($benchmarks, 100);
printf("\nStream benchmark (100 iterations):\n");
asort($results);
$fastest = reset($results);
foreach ($results as $name => $time) {
$ratio = round($time / $fastest, 1);
printf(" %-25s: %8.2f ms (%.1fx)\n", $name, $time, $ratio);
}
unlink($testFile);
PHP文件系统与流处理
张小明
前端开发工程师
涵道共轴双旋翼无人机飞控算法关键技术【附代码】
✨ 长期致力于涵道共轴双旋翼无人机、鲁棒控制、线性矩阵不等式、容错控制、动态观测研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)涵道共轴双旋翼无…
5步深度配置方案:打造高效Klipper 3D打印控制界面
5步深度配置方案:打造高效Klipper 3D打印控制界面 【免费下载链接】fluidd Fluidd, the klipper UI. 项目地址: https://gitcode.com/gh_mirrors/fl/fluidd Fluidd作为Klipper固件的现代化Web控制界面,专为追求高效、可定制化3D打印管理的用户设计…
ADG708BRUZ-REEL7选型指南:模拟多路复用器系列对比与应用选型建议
ADG708BRUZ-REEL7:低电压CMOS 8:1模拟多路复用器深度解析在多通道信号采集系统、音频/视频切换设备以及电池供电的便携仪器中,如何将多个模拟信号高效、低失真地送入单一的模数转换器(ADC)或后续处理电路,是硬件工程师…
Windows OCR文字识别革命:Text-Grab如何让屏幕文字提取效率提升300%
Windows OCR文字识别革命:Text-Grab如何让屏幕文字提取效率提升300% 【免费下载链接】Text-Grab Use OCR in Windows quickly and easily with Text Grab. With optional background process and notifications. 项目地址: https://gitcode.com/gh_mirrors/te/Tex…
使用Python SDK快速开发,让CRM网站拥有智能工单分类能力
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用Python SDK快速开发,让CRM网站拥有智能工单分类能力 为CRM系统添加工单自动分类与优先级判断能力,可以…
【MySQL】进阶02-索引
目录一,索引结构1. B-Tree(平衡多路搜索树)2.BTree3.通过BTree来索引4.通过Hash来索引二,语法1. 创建索引的基础语法2. 不同类型的索引创建语法3. 复合索引(多列索引)语法4. 索引选项:指定长度与…