PHP 流
最后修改于 2025 年 3 月 13 日
PHP 中的流提供了一种统一的方式来处理输入和输出操作,例如从文件读取、写入网络套接字或处理来自外部源的数据。本教程涵盖了 PHP 流的基础知识,包括文件处理、网络通信和自定义流包装器。
基本文件流
PHP 提供了 fopen
、fread
和 fclose
等函数来处理文件流。这些函数允许您从文件读取和写入文件。
<?php $logFile = fopen("app_logs.txt", "a"); if ($logFile) { $timestamp = date("Y-m-d H:i:s"); fwrite($logFile, "[$timestamp] User logged in\n"); fclose($logFile); echo "Log entry added.\n"; } else { echo "Failed to open log file.\n"; }
此示例模拟将日志条目追加到应用程序中的文件中。fopen
函数以追加模式 ("a"
) 打开 app_logs.txt
,如果该文件不存在则创建它。这是跟踪用户活动或错误的系统中常见的任务。
如果流成功打开,则使用 fwrite
写入带时间戳的消息,并使用 fclose
关闭流以释放资源。输出确认该操作。流的这种实际应用演示了在真实世界的日志记录场景中基本的文件 I/O。
从文件流读取
您可以使用 fread
或 fgets
从文件流读取数据。
<?php $configFile = fopen("config.ini", "r"); if ($configFile) { $settings = []; while (($line = fgets($configFile)) !== false) { $trimmed = trim($line); if ($trimmed && strpos($trimmed, "=") !== false) { [$key, $value] = explode("=", $trimmed, 2); $settings[$key] = $value; } } fclose($configFile); print_r($settings); } else { echo "Failed to open config file.\n"; }
此示例读取配置文件 (config.ini
) 以解析键值对,这是应用程序设置中的常见任务。假设 config.ini
包含类似 host=localhost
和 port=8080
的行。该文件以读取模式 ("r"
) 打开。
fgets
函数读取每一行,代码跳过空行或没有等号的行。每行有效的行都被拆分为键和值,并存储在一个数组中。关闭流后,将打印设置。这显示了流如何实际处理结构化文件读取。
网络流
PHP 流也可用于网络通信,例如从远程 URL 读取或连接到套接字。
<?php $apiUrl = "https://api.openweathermap.org/data/2.5/weather?q=London&appid=YOUR_API_KEY"; $weatherStream = fopen($apiUrl, "r"); if ($weatherStream) { $response = stream_get_contents($weatherStream); $data = json_decode($response, true); echo "Temperature in London: " . ($data["main"]["temp"] - 273.15) . "°C\n"; fclose($weatherStream); } else { echo "Failed to fetch weather data.\n"; }
此示例从 OpenWeatherMap API 提取天气数据,这是网络流的实际应用。fopen
函数打开到 API URL 的流(将 YOUR_API_KEY
替换为有效的密钥)。这模拟了与外部服务的真实世界集成。
如果成功,stream_get_contents
读取整个 JSON 响应,并将其解码为数组。温度(以开尔文为单位)转换为摄氏度并显示。然后关闭流。这演示了流处理 HTTP 请求,这是 Web 应用程序中的常见要求。
流上下文
流上下文允许您配置流的选项,例如 HTTP 标头或 SSL 设置。
<?php $options = [ "http" => [ "method" => "POST", "header" => "Content-Type: application/json\r\n", "content" => json_encode(["user_id" => 123, "action" => "login"]) ] ]; $context = stream_context_create($options); $endpoint = "https://api.example.com/auth"; $stream = fopen($endpoint, "r", false, $context); if ($stream) { $response = stream_get_contents($stream); echo "Server response: $response\n"; fclose($stream); } else { echo "Failed to send request.\n"; }
此示例向身份验证 API 发送 POST 请求,这是用户登录系统的真实场景。流上下文使用 stream_context_create
配置 HTTP 方法、标头和 JSON 负载。这种自定义对于与现代 API 交互至关重要。
fopen
函数使用上下文打开流,stream_get_contents
检索服务器的响应(例如,令牌)。此后关闭流。这说明了上下文如何增强网络通信的流,提供了超越简单 GET 请求的灵活性。
自定义流包装器
PHP 允许您创建自定义流包装器来处理自定义协议或数据源。
<?php class MemoryLogger { private array $logs = []; private int $position = 0; public function stream_open(string $path, string $mode): bool { return true; } public function stream_write(string $data): int { $this->logs[] = $data; return strlen($data); } public function stream_read(int $count): string { if ($this->stream_eof()) { return ""; } $data = implode("", $this->logs); $ret = substr($data, $this->position, $count); $this->position += strlen($ret); return $ret; } public function stream_eof(): bool { return $this->position >= strlen(implode("", $this->logs)); } } stream_wrapper_register("memorylog", "MemoryLogger"); $logger = fopen("memorylog://debug", "w+"); fwrite($logger, "Error: Invalid input\n"); rewind($logger); echo fread($logger, 1024); fclose($logger);
此示例定义了一个 MemoryLogger
自定义流包装器,用于内存日志记录,这对于无需文件 I/O 的调试很有用。包装器将日志消息存储在数组中,模拟类似流的接口。它注册为 memorylog://
。
stream_write
方法附加数据,而 stream_read
从连接的日志中读取,跟踪位置。写入错误消息后,rewind
重置位置,而 fread
检索它。这种实用的包装器显示了流如何扩展到传统文件或网络之外。
流过滤器
流过滤器允许您在数据通过流时处理数据。PHP 提供了内置过滤器,如 string.toupper
和 string.tolower
。
<?php $file = fopen("user_data.csv", "r"); if ($file) { stream_filter_append($file, "convert.iconv.UTF-8/ISO-8859-1"); $header = fgets($file); // e.g., "name,email" while (($line = fgets($file)) !== false) { echo "Processed: $line"; } fclose($file); } else { echo "Failed to open CSV file.\n"; }
此示例使用字符编码过滤器处理 CSV 文件 (user_data.csv
),这是从不同来源导入数据时的实际需求。假设该文件采用 UTF-8 编码,我们将其转换为 ISO-8859-1 以与旧系统兼容。
将 convert.iconv.UTF-8/ISO-8859-1
过滤器附加到流,在读取时转换数据。首先读取标头,然后处理并回显每一行。这演示了过滤器如何动态处理数据,避免了手动转换步骤。
流元数据
您可以使用 stream_get_meta_data
函数检索有关流的元数据。
<?php $backupFile = fopen("backup.tar.gz", "rb"); if ($backupFile) { $metadata = stream_get_meta_data($backupFile); echo "Stream wrapper: " . $metadata["wrapper_type"] . "\n"; echo "File size: " . filesize("backup.tar.gz") . " bytes\n"; fclose($backupFile); } else { echo "Failed to open backup file.\n"; }
此示例检查压缩备份文件 (backup.tar.gz
) 的元数据,该文件以二进制读取模式 ("rb"
) 打开。这对于在存档或传输任务中验证流很有用。
stream_get_meta_data
函数返回详细信息,例如包装器类型(例如,plainfile
)和模式。在这里,我们显示包装器类型,并使用 filesize
对其进行补充以提供上下文。关闭流可确保资源清理。这显示了元数据如何帮助流管理。
写入压缩流
PHP 通过像 compress.zlib
这样的包装器支持压缩流。
<?php $archive = fopen("compress.zlib://data.gz", "wb"); if ($archive) { fwrite($archive, "Sensitive data: User IDs and emails\n"); fclose($archive); echo "Data compressed and saved.\n"; } else { echo "Failed to open compressed stream.\n"; }
此示例使用 compress.zlib
包装器写入 gzip 压缩文件,这是一种有效存储日志或数据的实用方法。文件 data.gz
以二进制写入模式 ("wb"
) 打开,在写入时压缩数据。
如果流打开,fwrite
添加一个字符串,该字符串会自动压缩,而 fclose
完成该文件。结果是一个比纯文本更小的文件,非常适合备份或传输。这利用流进行内置压缩,无需外部工具。
流式传输大文件
流通过分块读取或写入来有效处理大文件。
<?php $source = fopen("large_video.mp4", "rb"); $dest = fopen("video_copy.mp4", "wb"); if ($source && $dest) { while (!feof($source)) { $chunk = fread($source, 8192); // 8KB chunks fwrite($dest, $chunk); } fclose($source); fclose($dest); echo "Video file copied successfully.\n"; } else { echo "Failed to open files.\n"; }
此示例将大视频文件 (large_video.mp4
) 复制到一个新文件,这是媒体处理中的常见任务。源和目标都以二进制模式 ("rb"
和 "wb"
) 打开,以保留数据完整性。
while (!feof($source))
循环使用 fread
读取 8KB 的块,将每个块写入目标流。这避免了将整个文件加载到内存中,使其对千兆字节大小的文件高效。完成后,关闭两个流,并确认成功。
套接字流
流可以处理套接字通信,用于实时应用程序。
<?php $socket = stream_socket_client("tcp://:8080", $errno, $errstr, 30); if ($socket) { fwrite($socket, "GET /status HTTP/1.1\r\nHost: localhost\r\n\r\n"); $response = stream_get_contents($socket); echo "Server response: $response\n"; fclose($socket); } else { echo "Failed to connect: $errstr ($errno)\n"; }
此示例连接到本地 TCP 服务器,端口为 8080,模拟网络应用程序中的简单客户端(例如,状态检查)。stream_socket_client
函数建立套接字流,超时时间为 30 秒。
如果成功,则通过 fwrite
发送 HTTP GET 请求,stream_get_contents
读取响应(假设服务器回复状态)。此后关闭流。这演示了流在实时通信中的应用,这是聊天或监控系统的关键特性。
流的最佳实践
- 使用流上下文:使用上下文配置流,以获取高级选项,如 HTTP 标头或 SSL 设置。
- 处理错误:在执行操作之前,始终检查流是否已成功打开。
- 关闭流:使用
fclose
关闭流并释放资源。 - 使用过滤器:应用流过滤器以动态处理数据,而无需修改源。
来源
在本教程中,我们探讨了如何在 PHP 中使用流,包括文件处理、网络通信、自定义流包装器和流过滤器。流提供了一种强大而灵活的方式来处理 PHP 中的输入和输出。
作者
列出 所有 PHP 教程。