1.文件操作入门
1.文件读写
1.读取文件
在PHP中,file_get_contents()函数用于将文件的内容全部读取到一个字符串中,其声明方式如下。
file_get_contents( string $filename, bool $use_include_path = false, resource $context = ?, int $offset = -1, int $maxlen = ?)
在上述声明中,use_include_path为可选参数,若想在php.ini中配置的include_path路径里搜寻文件,可以将该参数设为1;
offset用于指定在文件中开始读取的位置,默认从文件头开始;$maxlen用于指定读取的最大字节数,默认为整个文件的大小。
接下来通过代码演示file_get_contents()函数的使用,具体如下。
<?php//相对路径$filename = "./123.txt";echo file_get_contents($filename); //输出当前目录下的123.txt文件内容//绝对路径$filename = "C:/Windows/System32/drivers/etc/hosts";echo file_get_contents($filename); //输出操作系统的hosts文件内容?>
除了文本文件,file_get_contents()还可以读取图片等其他类型的文件,如下所示:
<?phpheader('Content-Type:image/jpeg');echo file_get_contents('./1.jpg');?>
在上述代码中,第2行通过header()函数告知浏览器图片的类型。需要注意的是,整个脚本中不能有其他的输出内容,否则会导致图片格式被破坏。必须确保当前目录下的php.jpg是一个正确的图片文件。
值得一提的是,在默认情况下,PHP可以读取整个系统中的文件,这将不利于服务器的安全。如果需要限制PHP可以访问的路径,可以通过php.ini中的open_basedir进行配置,示例如下。
//只允许访问PHP脚本所在的目录int_set('open_basedir','C:/web/apache2.4/htdocs'); //或用相对路径“./”echo file_get_contents('./123.txt'); //可以读取echo file_get_contents('C:/web/123.txt'); //无法读取
另外,open_basedir不仅针对文件操作函数有效,对于include、require等和文件有关的操作都会产生影响。因此,大家在使用时需酌情考虑。
2.按行读取文件
file()函数可以将整个文件读入到数组中。如果该函数执行成功,则返回一个数组,数组中的每个元素都是文件中的一行,包括换行符在内。如果执行失败,则返回false。其声明方式如下。
file( string $filename, int $flags = 0, resource $context = ?)
在上述声明中,flags指定读取方式的选项。关于$flags可以指定的常量具体如下。
- FILE_USE_INCLUDE_PATH:在include_path中查找文件。
- FILE_IGNORE_NEW_LINES:指定返回值数组的每个元素值末尾不添加换行符。
- FILE_SKIP_EMPTY_LINES:跳过空行。
示例如下:
<?php//读取脚本文件自身,遍历读取后的数组foreach (file(__FILE__) as $k => $v) {echo "Line #$k:$v";}?>
运行结果:

3.写入文件
在Web开发中,当需要使用文件记录程序处理后的内容时,可以使用file_put_contents()函数来完成。其声明方式如下。
file_put_contents( string $filename, mixed $data, int $flags = 0, resource $context = ?)
上述声明中,data指定要写入的内容;
filename,或使用常量FILE_APPEND表示追加写入。函数执行成功,返回写入到文件内数据的字节数,失败返回false。
接下来通过例子来演示file_put_contents()函数的使用,具体如下。
<?php$filename = './write.txt';$content = '黑夜给我了黑色的眼睛,我却用它来寻找光明';file_put_contents($filename, $content); //替换整个文件echo file_get_contents($filename),'<br>';file_put_contents($filename, $content,FILE_APPEND); //以追加方式写入echo file_get_contents($filename);?>
输出结果:

4.读取远程地址
PHP提供的file_get_contents()函数和file()函数除了能够读取本地文件,还可以读取远程文件。在使用前,应确保php.ini中的allow_url_fopen配置项处于开启状态,否则不允许远程请求。
<?php//请求远程地址$html = file_get_contents('http://www.linzhou.ccoo.cn');//获取响应头信息var_dump($http_response_header);//输出返回的信息echo '<br>'.htmlspecialchars($html);?>
上述代码实现了从远程地址“http://www.itheima.com”请求信息。file_get_contents()函数请求成功后,就会自动将响应消息保存到$http_response_header变量中。程序的运行结果如下图所示。
array(14) {[0]=>string(15) "HTTP/1.1 200 OK"[1]=>string(13) "Server: nginx"[2]=>string(35) "Date: Sun, 25 Apr 2021 13:40:17 GMT"[3]=>string(23) "Content-Type: text/html"[4]=>string(22) "Content-Length: 102874"[5]=>string(17) "Connection: close"[6]=>string(21) "Vary: Accept-Encoding"[7]=>string(22) "Cache-Control: private"[8]=>string(65) "Set-Cookie: ASPSESSIONIDSATAQBCS=OMNDHDECHBDGKGOGNLNODIEC; path=/"[9]=>string(21) "X-Powered-By: ASP.NET"[10]=>string(31) "X-XSS-Protection: 1; mode=block"[11]=>string(31) "X-Content-Type-Options: nosniff"[12]=>string(25) "backendIP: 192.168.3.7:80"[13]=>string(16) "backendCode: 200"}<br>
2.文件常用操作
1.文件重命名和移动路径
rename()函数用于实现文件的重命名或移动路径,其声明方式如下。
rename( string $oldname, string $newname, resource $context = ?)
上述声明中,newname表示目标路径。如果两个文件路径在同一个目录下,执行重命名操作;如果不在同一个目录下,则执行移动操作。该函数执行成功时返回true,执行失败返回false。下面通过代码进行演示,具体如下。
<?php//重命名123.txt 为 456.bakrename('./123.txt','./456.bak');//移动test.bak 到 ./123/456.txtrename('./test.bak', './123/456.txt');?>
上述代码实现了文件的重命名或移动。需要注意的是,在对文件进行操作时,若目标路径是个已经存在的文件,会自动覆盖。
另外,若rename()函数的第1个参数是目录,则可以对目录进行重命名或移动的操作。需要注意的是,当目标路径已经存在,或目标路径的上级目录不存在时,会失败并提示Warning错误。
2.文件复制
copy()函数用于实现文件复制的功能,其声明方式如下。
copy( string $source, string $dest, resource $context = ?)
上述声明中,dest表示目标路径。当文件复制成功时返回true,失败时返回false。下面通过代码进行演示,具体如下。
3.文件删除
unlink()函数的作用是删除文件,其声明方式如下。
unlink( string $filename, resource $context = ?)
在上述声明中,$filename表示文件路径,如果删除成功返回值为true,失败则返回false。下面通过代码进行演示,具体如下。
unlink('./test.txt');
上述代码执行后,当前目录下的test.txt文件将被删除。如果文件不存在,则会提示Warning错误。
4.判断文件是否存在
在操作一个文件时,如果该文件不存在,则会出现错误。为了避免这种情况出现,PHP提供了对应的函数来检查文件或目录是否存在,具体如下。
- file_exists():判断指定文件或目录是否存在。
- is_file():判断指定文件是否存在。
- is_dir():判断指定目录是否存在。
3.文件类型和属性
1.获取文件类型
使用PHP的filetype()函数可以获取文件的类型,示例代码如下。
<?phpecho filetype('./123/2.jpg'); //输出:fileecho filetype('./123'); //输出:dir?>
在Windows系统中,PHP只能获得file(文件)、dir(目录)和unknown(未知)3种文件类型,而在Linux系统中,还可以获取block(块设备)、char(字符设备)、fifo(命名管道)、link(符号链接)等文件类型。
2.获取文件属性
文件属性包括文件大小、权限、创建时间等信息。PHP内置了一系列函数用于获取这些属性,具体如下表所示。

由于PHP中的int数据类型表示的数据范围有限,所以filesize()函数对于大于2GB的文件,并不能准确获取其大小,需斟酌使用。
除了上述方式外,还可以使用stat()函数获取文件的统计信息,示例代码如下。
<?phpprint_r(stat('./123.bak'));?>
输出结果:
Array([0] => 4294967245[1] => 0[2] => 33206[3] => 1[4] => 0[5] => 0[6] => -51[7] => 122[8] => 1619403377[9] => 1619403377[10] => 1619403377[11] => -1[12] => -1[dev] => 4294967245[ino] => 0[mode] => 33206[nlink] => 1[uid] => 0[gid] => 0[rdev] => -51[size] => 122[atime] => 1619403377[mtime] => 1619403377[ctime] => 1619403377[blksize] => -1[blocks] => -1)
具体说明如下图:

需要注意的是,上表中所列出的文件统计信息是以Linux系统为基础的,而在Windows下并没有uid、gid、blksize和blocks等属性,在Windows下它们的值分别取默认值0或-1。
4.目录操作
1.创建目录
在PHP中,mkdir()函数用于创建目录,其声明方式如下。
mkdir( string $pathname, int $mode = 0777, bool $recursive = false, resource $context = ?)
在上述声明中,mode指定目录的访问权限(用于Linux环境),默认为0777;$recursive指定是否递归创建目录,默认为false。该函数执行成功返回true,失败返回false。
mkdir('./test');mkdir('./test/test2',0777,true);
上述代码中,将mkdir()函数的第3个参数指定为true,可以自动创建给定路径中不存在的目录;若省略该参数,则会失败并提示Warning错误。另外,当要创建的最后一级目录已经存在时,也会创建失败并提示Warning错误。
2.删除目录
与mkdir()函数相对应,rmdir()函数用于删除目录,其声明方式如下。
rmdir( string $dirname, resource $context = ?)
上述声明中,$dirname指定要删除的目录名。函数执行成功时返回true,失败返回false。
rmdir('./test'); //删除空目录(删除成功)rmdir('./test1'); //删除非空目录(删除失败)rmdir('./test1/test2'); //删除空目录(删除成功)
对于非空目录,使用rmdir()进行删除时,会删除失败并提示Warning错误。因此,对于非空目录,只有先清空里面的文件,才能够删除目录。
3.遍历目录
glob()函数用于寻找与模式(pattern)匹配的文件路径,也可以用于遍历目录,其声明方式如下。
glob( string $pattern, int $flags = 0)
上述声明中,%E5%87%BD%E6%95%B0%E6%8C%87%E5%AE%9A%E7%9A%84%E6%A8%A1%E5%BC%8F%E7%9B%B8%E5%90%8C%EF%BC%9B#card=math&code=pattern%E8%A1%A8%E7%A4%BA%E5%8C%B9%E9%85%8D%E6%A8%A1%E5%BC%8F%EF%BC%8C%E5%85%B6%E5%86%99%E6%B3%95%E4%B8%8Elibc%EF%BC%88C%E8%AF%AD%E8%A8%80%E5%87%BD%E6%95%B0%E5%BA%93%EF%BC%89%E4%B8%AD%E7%9A%84glob%28%29%E5%87%BD%E6%95%B0%E6%8C%87%E5%AE%9A%E7%9A%84%E6%A8%A1%E5%BC%8F%E7%9B%B8%E5%90%8C%EF%BC%9B)flags用于指定一些选项,如GLOB_MARK表示在每个目录后面加一个斜线,GLOB_ONLYDIR表示仅返回与模式匹配的目录项。函数的返回值是查找后的文件列表数组。
输出如下:
Array([0] => ./1.jpg[1] => ./123[2] => ./123.bak[3] => ./456.bak[4] => ./Integer.php[5] => ./receive.php[6] => ./string.php[7] => ./test.php[8] => ./test2.php[9] => ./text.php[10] => ./time.php[11] => ./write.txt)Array([0] => ./write.txt)
4.查看磁盘大小和可用空间
查看磁盘大小和可用空间PHP提供了disk_total_space()函数和disk_free_space()函数,可以获取磁盘的总大小和可用空间。这两个函数的使用示例如下。
echo disk_total_space("D:");echo disk_free_space("D:");
5.解析路径
在程序中经常需要对文件路径进行操作,如解析路径中的文件名或目录等。PHP提供了basename()函数、dirname()函数和pathinfo()函数来完成对目录的解析操作,接下来分别进行讲解。
1.basename()函数
basename()函数用于返回路径中的文件名,其声明方式如下。
basename( string $path, string $suffix = ?)
在上述声明中,suffix是可选参数,如果指定了该参数,且文件名是以$suffix结尾的,则返回的结果中会被去掉这一部分字符。
$path = 'C:/web/apache2.4/htdocs/index.html';echo basename($path); //输出:index.htmlecho basename($path,'.html') //输出:index
2.dirname()函数
dirname()函数用于返回路径中的目录部分,其声明方式如下。
dirname( string $path, int $levels = 1)
在上述声明中,level是PHP 7新增的参数,表示上移目录的层数。
下面通过具体代码演示dirname()函数的使用。
$path = 'C:/web/apache2.4/htdocs/index.html';echo dirname($path); //输出:C:/web/apache2.4/htdocsecho dirname($path,2); //输出:C:/web/apache2.4echo dirname($path,3); //输出:C:/web
3.pathinfo()函数
pathinfo()函数用于以数组形式返回路径的信息,包括目录名、文件名和扩展名等,其声明方式如下。
pathinfo( string $path, int $options = PATHINFO_DIRNAME | PATHINFO_BASENAME | PATHINFO_EXTENSION | PATHINFO_FILENAME)
在上述声明中,options用于指定要返回哪些项,默认返回全部,具体包括PATHINFO_DIRNAME(目录名)、PATHINFO_BASENAME(文件名)、PATHINFO_EXTENSION(扩展名)和PATHINFO_FILENAME(不含扩展名的文件名)。
$path = 'C:/web/apache2.4/htdocs/index.html';$info pathinfo($path);echo $info['dirname']; //输出:C:/web/apache2.4/htdocsecho $info['basename']; //输出:index.htmlecho $info['extension']; //输出:htmlecho $info['filename']; //输出:index
从以上示例可以看出,pathinfo()函数的返回值是一个关联数组,通过该数组可以获取路径的信息。
2.文件操作进阶
1.文件指针
若要使用文件指针方式进行文件操作,需要先打开文件,创建文件指针,然后使用指针进行读写,最后操作完成后关闭文件。接下来将针对这些步骤所涉及的函数进行讲解。
1.打开文件
在PHP中打开文件使用的是fopen()函数,其声明方式如下。
fopen( string $filename, string $mode, bool $use_include_path = false, resource $context = ?)
在上述声明中,mode表示文件打开的模式,常用的模式如下表所示;
context用于资源流上下文操作,将在后面进行讲解。该函数执行成功后,返回资源类型的文件指针,用于其他操作。

对于除“r”和“r+”模式外的其他操作,如果文件不存在,会尝试自动创建。
2.关闭文件
在PHP中关闭文件使用的是fclose()函数,其声明方式如下。
fclose( resource $handle)
在上述代码中,fclose()函数只有1个参数$handle,表示fopen()函数成功打开文件时返回的文件指针。如果文件关闭成功返回true,失败返回false。
3.读取文件
在使用fopen()函数打开文件后,通过fread()函数、fgetc()函数、fgets()函数可以进行不同形式的文件读取操作,下面分别进行讲解。
1.fread()函数
fread()函数用于读取指定长度的字符串,其声明方式如下。
fread( resource $handle, int $length)
上述声明中的length用于指定读取的字节数。该函数在读取到$length指定的字节数,或读取到文件末尾时就会停止读取,返回读取到的内容。当读取失败时返回false。
示例如下:
<?php//准备测试文件$filename = "./123.txt";file_put_contents($filename, '黑夜给了我黑色的眼镜,我却用他来寻找光明。');//以只读方式打开文件$handle = fopen($filename,'r');//从文件中读取前9个字节(1个UTF-8中文占3个字节)echo fread($handle,9),'<br>';//从上一次读取的位置继续读取,直到末尾echo fread($handle, filesize($filename));//关闭文件fclose($handle);?>
程序的运行结果如下图所示。

可以看出,当使用fread()函数读取文件时,会影响文件指针指向的文件位置。通过ftell()函数可以返回当前文件指针的位置,通过rewind()函数可以倒回文件指针的位置。示例代码如下。
<?php$filename = "./123.txt";file_put_contents($filename, '黑夜给了我黑色的眼镜,我却用他来寻找光明。');$handle = fopen($filename,'r'); //打开文件fread($handle,9); //读取9个字节echo ftell($handle); //输出结果:9rewind($handle); //倒回文件指针echo ftell($handle); //输出结果:0fclose($handle);?>
2.fgetc()函数
fgetc()函数用于在打开的文件中读取一个字符,其声明方式如下。
fgetc( resource $handle)
在上述声明中,$handle表示文件指针,该函数每次只能读取一个字节。如果遇到EOF(End OfFile,文件结束符标志)时,返回false。下面通过具体代码演示如何使用fgetc()函数。
<?php$filename = "./123.txt";file_put_contents($filename, 'niubishanshan');$handle = fopen($filename,'r'); //打开文件echo fgetc($handle); //输出:necho fgetc($handle); //输出:i?>
3.fgets()函数
fgets()函数用于读取文件中的一行,其声明方式如下。
fgets( resource $handle, int $length = ?)
在上述声明中,length-1字节的字符串。在碰到换行符、EOF或已经读取了
length,则默认值为1024字节。fgets()函数的使用示例如下。
<?php$filename = "./123.txt";file_put_contents($filename, "123456\n789");$handle = fopen($filename,'r'); //打开文件echo fgets($handle,4); //输出:123echo str_replace("\n", '*', fgets($handle)); //输出:456*?>
从上述示例可以看出,fgets()函数的输出结果中包含了换行符。
4.写入文件
fwrite()函数用于写入文件,其声明方式如下。
fwrite( resource $handle, string $string, int $length = ?)
在上述声明中,string表示要写入的字符串;$length表示指定写入的字节数,如果省略,表示写入整个字符串。下面通过具体代码演示fwrite()函数的使用。
<?php$filename = './123.txt';$handle = fopen($filename,'w'); //以写入方式打开文件fwrite($handle, 'test'); //向文件中写入内容echo file_get_contents($filename); //输出:testfwrite($handle, 123456); //继续向文件中写入内容echo file_get_contents($filename); //输出:test123456?>
从上述代码可以看出,fwrite()函数会从文件指针的位置开始写入内容。需要注意的是,若文件指针的位置原来已经有了内容,会被自动覆盖。
5.文件加锁机制
Web应用程序上线之后面临的一个普遍问题就是并发访问,这对于文件操作尤为明显。如果有多个浏览器在同一时刻访问服务器上的某一个文件,这意味着不同的访问进程会在同一时刻读/写同一个文件,很有可能造成数据的紊乱或者文件的损坏。为了避免这个问题,PHP中提供了文件加锁机制,这种机制是通过flock()函数来实现的,其声明方式如下。
flock( resource $handle, int $operation, int &$wouldblock = ?)
上述声明中的operation指定了使用哪种锁类型,$wouldblock为可选参数,设置为1或true时,表示当进行锁定时阻挡其他进程。
flock()函数的$operation参数的取值常量有多个,具体如下。
- LOCK_SH:取得共享锁定(读文件时使用)。
- LOCK_EX:取得独占锁定(写文件时使用)。
- LOCK_UN:释放锁定(无论共享或独占,都用它释放)
- LOCK_NB:如果不希望flock()在锁定时堵塞,则给$operation加上LOCK_NB。
当一个用户进程在访问文件时加上锁,其他用户进程要想对该文件进行访问,就必须等到锁定被释放,这样就可以避免在并发访问同一个文件时破坏数据。
示例如下:
<?php$handle = fopen('./lock.txt','w');if (flock($handle, LOCK_EX)){fwrite($handle, 'test'); //取得独占锁定flock($handle, LOCK_UN); //释放锁定} else {echo '文件不能被锁定';}?>
在上述代码中,为了确保数据的安全,在写入数据之前,取得独占锁,其他进程在访问该文件时,就必须处于等待状态。当数据写入完成之后,释放锁定,这时就可以让其他进程来访问并操作该文件。
2.目录句柄
在PHP中,对目录的操作可以通过目录句柄来完成。PHP提供了opendir()、closedir()、readdir()和rewinddir()等函数用于实现目录操作,如目录的遍历。接下来针对这4个函数进行讲解。
1.opendir()函数
opendir()函数用于打开一个目录句柄,其声明方式如下。
opendir( string $path, resource $context = ?)
在上述声明中,$path指定要打开的目录路径。该函数如果执行成功,返回资源类型的目录句柄,如果失败,返回false。
2.closedir()函数
closedir()函数用于关闭目录句柄,其声明方式如下。函数执行后没有返回值。
closedir( resource $dir_handle = ?)
3.readdir()函数
readdir()函数用于从目录句柄中读取条目,其声明方式如下。
readdir( resource $dir_handle = ?)
函数执行成功返回目录中下一个文件的文件名,失败时返回false。
4.rewinddir()函数
rewinddir()函数用于倒回目录句柄,其声明方式如下。
rewinddir( resource $dir_handle)
函数执行后将$dir_handle重置到目录的开头,没有返回值。
接下来通过下面例子来演示如何通过上述函数实现目录的遍历,具体如下。
<?php$handle = opendir('C:/Environment/phpstudy/WWW/study/php');while (false !== ($file = readdir($handle))) {echo "$file<br>";}closedir($handle);?>
上述代码第3行通过while循环调用readdir()函数来获取目录中的条目。运行结果如下图所示。需要注意的是,在遍历任何一个目录的时候,都会包括“。”和“..”两个特殊的目录,前者表示当前目录,后者表示上一级目录。

3.资源流
在前面讲过的如file_get_contents()、fopen()等函数中,有一个可选参数$content,表示资源流下上文。所谓流(Stream)是指数据源在程序之间经历的路径,当传输方以二进制流的方式传送某个资源(如文件内容)给接收方时,就形成了一条资源流。
PHP提供了流相关的函数,用于通过一套统一的操作,来处理文件、网络连接、压缩传输等多种类型的数据源。为了方便开发时使用,PHP封装了“file://”“http://”“ftp://”和“zlib://”等常用协议。下面以HTTP协议POST方式为例,演示资源流的使用。
<?php//准备以POST方式发送的数据$data = http_build_query(['name' => 'test','age' => '18']);//定义资源流的选项$options = ['http' => ['method' => 'POST','header' => "Content-Type: applircation/x-www-form-urlenclded\r\n".'Content-Length:'.strlen($data)."\r\n",'content' => $data]];//创建资源流上下文$context = stream_context_create($options);//发送请求并获取响应结果echo file_get_contents('http://localhost/study/php/test.php',false,$context);?>
在上述代码中,第14行通过stream_context_create()函数创建资源类型的资源流上下文,保存到变量$context中,该变量用于在使用file_get_contents()等函数时传入。第5~12行代码用于定义资源流的选项,该数组的结构是按照PHP手册中的说明进行定义的。
为了测试执行结果,在test.php中将接收到的$_POST数组打印出来,代码如下。
<?phpprint_r($_POST);?>
