## 前言
啥是流操作?簡單來講就是對一些文件,網絡的IO操作。PHP已經把這些IO操作,封裝成流操作。這節,我們將使用PHP擴展實現一個目錄遍歷的功能。PHP示例代碼如下:
```
<?php
function list_dir($dir) {
if (is_dir($dir) === false) {
return;
}
$dh = opendir($dir);
if ($dh == false) {
return;
}
while (($file = readdir($dh)) !== false) {
if(is_dir($dir."/".$file) && $file != "." && $file != "..") {
list_dir($dir."/".$file);
} else if ($file != "." && $file != "..") {
echo $dir."/".$file."\n";
}
}
closedir($dh);
}
list_dir("/Users/canglong/dev/test/gc");
?>
```
執行后輸出內容是:
```
/Users/canglong/dev/test/gc/blog/www.bo56.com
/Users/canglong/dev/test/gc/www.bo56.com
```
我們將在擴展中list_dir方法。
## 代碼
### 基礎代碼
這個擴展,我們將在say擴展上增加`list_dir`方法代碼。say擴展相關代碼大家請看這篇博文。PHP7擴展開發之hello word 文中已經詳細介紹了如何創建一個擴展和提供了源碼下載。
### 代碼實現
#### 第一步,先引入頭文件:
```c
#include "ext/standard/php_filestat.h"
```
#### 第二步,實現list_dir函數
`list_dir`函數的代碼如下:
```c
void list_dir(const char *dir);
PHP_FUNCTION(list_dir)
{
char *dir;
size_t dir_len;
#ifndef FAST_ZPP
/* Get function parameters and do error-checking. */
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &dir, &dir_len) == FAILURE) {
return;
}
#else
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_PATH(dir, dir_len)
ZEND_PARSE_PARAMETERS_END();
#endif
php_stat(dir, (php_stat_len) dir_len, FS_IS_DIR, return_value);
if (Z_TYPE_P(return_value) == IS_FALSE) {
RETURN_NULL();
}
list_dir(dir);
RETURN_NULL();
}
void list_dir(const char *dir)
{
php_stream *stream;
int options = REPORT_ERRORS;
php_stream_dirent entry;
int path_len;
char path[MAXPATHLEN];
zend_stat_t st;
stream = php_stream_opendir(dir, options, NULL);
if (!stream) {
return;
}
while(php_stream_readdir(stream, &entry)) {
if ((path_len = snprintf(path, sizeof(path), "%s/%s", dir, entry.d_name)) 小于 0) {
break;
}
if (zend_stat(path, &st) != -1 && S_ISDIR(st.st_mode) && strcmp(entry.d_name, ".") != 0
&& strcmp(entry.d_name, "..") != 0) {
list_dir(path);
} else if (strcmp(entry.d_name, ".") != 0 && strcmp(entry.d_name, "..") != 0) {
PUTS(path);
PUTS("\n");
}
}
php_stream_closedir(stream);
}
```
### 代碼解讀
首先說下路徑狀態的判斷。
`php_stat`函數是PHP中`is_dir`函數在實現的時候,使用的一個函數。具體代碼參見[ext/standard/filestat.c](https://github.com/php/php-src/blob/master/ext/standard/filestat.c)文件的FileFunction宏方法。在1092行附近。這個函數是判斷一個路徑的狀態。如,是否是文件夾等。一般在擴展實現的時候,不建議使用。這里只是為了演示,才使用的。
`zend_stat`宏方法。也是實現判斷一個路徑的狀態。推薦在擴展中使用。如果調用有問題,會返回-1。
PHP把一些IO操作都封裝成了流操作。這些流操作都聲明在[main/php_streams.h](https://github.com/php/php-src/blob/master/main/php_streams.h)文件中。下面我們說下,我們用到的流操作函數。
`php_stream_opendir`函數是用于打開一個目錄。
* 第一個參數:路徑
* 第二個參數:選項。控制一些函數調用行為。定義在main/php_streams.h中。多個選項可以使用異或操作。如 int options = IGNORE_PATH | REPORT_ERRORS;
`php_stream_readdir`讀取目錄流。
* 第一個參數:上面函數打開的stream流
* 第二個參數:php_stream_dirent 用于存儲當前讀取的信息。
`php_stream_closedir`關閉目錄流。參數是之前打開的流。