PHP课程 - 第2课 - 文件操作:打开、写入与读取。
在上一课中,我们已经为我们的留言簿、博客或聊天室搭建了框架。现在只需继续添加功能。在本课中,我们将学习如何将我们的内容写入文件,并从文件中读取内容。
PHP 提供了大量用于文件操作的函数,我们只会学习其中一部分。因为在实际项目中,你的网站大多数情况下会将数据保存到数据库中(我非常确定),而不是写入文件。不过,了解文件操作的原理有助于理解 PHP 的基本机制。之后我们会学习如何操作数据库。
如果你还没有上节课创建的那些文件,请先返回上一课并下载它们。
现在让我们进入添加记录的表单:
/index.php?admin=1
我们将通过这个表单来添加内容。要处理提交的数据,我们需要在 index.php 页面上检查是否存在 $_POST 变量。检查方法非常简单:
if($_POST){ }
是不是很简单?现在我们要在大括号内编写对 $_POST 数组的处理逻辑。但我们不会直接在这里写所有逻辑,而是将其放入我们的类中,并通过对象的方法来调用。在 index.php 文件中这样写:
if($_POST){ $obj->write($_POST); }
而在 simpleCMS.php 文件中,我们将实现 write() 方法:
public function write($p) { // 写入消息的方法 }
为什么这样做?因为这样可以简化 index.php 文件的代码。index.php 只负责控制流程,而具体的数据处理和输出应该放在类文件中。这样的结构清晰明了,不仅方便他人阅读代码,也方便自己维护。
我们先从最简单的操作开始——打印 $_POST 数组内容:
public function write($p) { // 写入消息的方法 print_r($p); }
正如你所见,我们使用了本地变量 $p,虽然传入的是全局变量 $_POST。我们不直接操作全局变量是为了让方法更通用,比如将来我们可能希望从 XML 文件中导入数据,而不仅仅是表单提交。
现在填写表单并提交后,你将看到如下数组:
Array ( [title] => Иван [bodytext] => Привет, Мир! )
如果你的表单内容不同,数组值也会不同。我们打印数组是为了知道有哪些键名可以使用,这里是 $p['title'] 和 $p['bodytext']。了解这些之后,我们就可以将数据写入文件了。
PHP 写入文件
在写入文件之前,必须先“打开文件以进行写入”。打开文件意味着将文件与一个 PHP 变量关联。文件在 PHP 中被表示为一种 resource(资源)类型。我们来看一个例子:
public function write($p) { // 写入消息的方法 $message = fopen("messages/file.txt","w"); print_r($message); }
我在 messages 文件夹中创建了一个 file.txt 文件。提交表单后,你会看到如下输出:
Resource id #4
Resource 是变量 $message 的类型,而数字 4 表示该资源在内存中的 ID。对 $message 的任何操作都会影响文件本身。我们将按行写入文件,以便以后可以按行读取:
public function write($p) { // 写入消息的方法 $message = fopen("messages/file.txt","w"); fputs ($message, $p['title']. "\r\n"); fputs ($message, $p['bodytext']); }
双引号中的 \r\n 表示换行符(在单引号中可能不起作用)。写入后的文件内容如下:
Иван
Привет, Мир!
完成文件操作后,记得关闭文件以释放内存:
public function write($p) { // 写入消息的方法 $message = fopen("messages/file.txt","w"); // 打开文件 fputs ($message, $p['title']. "\r\n"); fputs ($message, $p['bodytext']); fclose ($message); // 关闭文件 }
你可能注意到,每次重新写入文件时内容都会被覆盖。这取决于 fopen() 函数的第二个参数。我们可以控制文件打开的方式:
$message = fopen("messages/file.txt","r"); // 仅以读取模式打开文件
- r – 只读模式。
- r+ – 读写模式。
- w – 创建一个新空文件(若文件已存在则会被覆盖)。
- w+ – 读写模式,但若文件存在则清空其内容。
- a – 以追加方式写入文件,指针移动到文件末尾。
- a+ – 读写并追加模式,指针在文件末尾,原内容保留。
如上所述,当以参数 w 打开文件时,会创建一个新文件。我们可以使用 time() 函数生成基于时间的唯一文件名。
time() 函数返回自 1970 年 1 月 1 日起的秒数。
代码如下:
public function write($p) { // 写入消息的方法 $message = fopen('messages/'.time(),"w"); // 打开文件 fputs ($message, $p['title']. "\r\n"); fputs ($message, $p['bodytext']. "\r\n"); fputs ($message, time()); fclose ($message); // 关闭文件 }
这样每一秒都会创建一个新文件,文件名为时间戳。添加新记录后,新的文件将出现在 messages 文件夹中。接下来我们来读取文件内容。
PHP 读取文件
要读取文件,需以只读模式打开文件(参数为 r)。所有文件都在 messages 文件夹中,我们将使用 while 循环读取全部文件。
通过 opendir() 函数打开目录:
if (is_dir('messages')) { // 检查目录是否存在 if ($dh = opendir('messages')) { // 打开目录读取 while (($file = readdir($dh)) !== false) { // 当文件存在时循环读取 // 执行操作 } closedir($dh); // 关闭目录 } }
函数 readdir() 返回文件名,存储在变量 $file 中。循环结束后关闭目录释放内存。
现在我们可以读取文件了,改进 display_public() 方法如下:
public function display_public() { // 输出消息的方法 $content = ''; if (is_dir('messages')) { // 检查目录是否存在 if ($dh = opendir('messages')) { // 打开目录读取 while (($file = readdir($dh)) !== false) { // 遍历文件 if(substr($file, -4) == '.txt'){ // 只读取 .txt 文件 $filename = 'messages/' . $file; // 文件完整路径 $message = fopen($filename, 'r'); // 打开文件 $title = fgets($message); // 读取第一行 $body = fgets($message); // 读取第二行 $time = fgets($message); // 读取第三行 print '<div class="post">'; print '<span class="time">' . date('d-m-Y', $time) . '</span><h2>' . $title . '</h2>'; print '<p>' . $body . '</p>'; print '</div>'; fclose($message); // 关闭文件 } } closedir($dh); // 关闭目录 } } $content .= '<p><a href="/index.php?admin=1">添加新消息</a></p>'; return $content; }
我在每一行都添加了注释,若有不明白的地方请在评论中提问,我会详细解释。
在实际开发中,你可能不会用逐行读取文件的方式存储内容,但理解这些基本的文件操作函数对掌握 PHP 的底层机制非常有帮助。
下面是当前网站的源代码示例。