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 教程。