


[Translation][php extension development and embedding] Chapter 14 - Access to streams in php
Feb 10, 2017 am 10:18 AMAccess flow
All file I/O processing in PHP user space is through php Processed by the PHP stream wrapper introduced in 4.3. Internally, the extension code can choose to use stdio or posix file processing to communicate with the local file system or Berkeley domain socket, or it can call the same API as user space stream I/O .
Overview of streams
Generally, direct file descriptors are less expensive than calling the stream wrapper layer CPU and memory; however, this puts all the work of implementing a specific protocol on you as the extension developer. By hooking into the stream wrapper layer, your extension code can transparently use the various built-in streams Wrappers, such as HTTP, FTP, and their SSL counterparts, as well as gzip and bzip2 compression wrappers. By including specific PEAR or PECL modules, your code can also access other protocols, such as SSH2, WebDav, and even Gopher!
This chapter will introduce the basic API that works internally based on streams. Later in Chapter 16 "Interesting Streams", we will see things like applying filters, using context options and parameters, etc. Advanced concepts.
Opening a stream
Although a unified API, it actually depends on the required Types of streams, there are four different paths to open a stream. From a user space perspective, these four different categories are as follows (the function list only represents examples, not a complete list):
<?php /* fopen包裝 * 操作文件/URI方式指定遠(yuǎn)程文件類資源 */ $fp = fopen($url, $mode); $data = file_get_contents($url); file_put_contents($url, $data); $lines = file($url); /* 傳輸 * 基于套接字的順序I/O */ $fp = fsockopen($host, $port); $fp = stream_socket_client($uri); $fp = stream_socket_server($uri, $options); /* 目錄流 */ $dir = opendir($url); $files = scandir($url); $obj = dir($url); /* "特殊"的流 */ $fp = tmpfile(); $fp = popen($cmd); proc_open($cmd, $pipes); ?>
No matter what type of stream you open, they are all stored in a common structure php_stream.
fopen packaging
We first start by implementing the fopen() function. Now you should be familiar with creating extension skeletons. If not, please go back to Chapter 5 "Your First Extension" for review Take a look, here is the fopen() function we implemented:
PHP_FUNCTION(sample5_fopen) { php_stream *stream; char *path, *mode; int path_len, mode_len; int options = ENFORCE_SAFE_MODE | REPORT_ERRORS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &path, &path_len, &mode, &mode_len) == FAILURE) { return; } stream = php_stream_open_wrapper(path, mode, options, NULL); if (!stream) { RETURN_FALSE; } php_stream_to_zval(stream, return_value); }
php_stream_open_wrapper()’s purpose should be to completely bypass the bottom layer. path specifies the file name or URL to be read and written, and the read and write behavior Depends on the value of mode.
options is a collection of flag values ????in the bit field, here it is set to a set of fixed values ????as introduced below:
USE_PATHApply the include_path in the php.ini file to the relative path
. The built-in function fopen() is specified in the third This option will be set when a parameter is TRUE
.
STREAM_USE_URLSet this After option, only the remote URL will be opened. For
php://, file://, zlib://, bzip2:// these The URL wrappers do not
consider them to be remote URLs.
ENFORCE_SAFE_MODEalthough This constant is named this, but actually sets this option
is just a mandatory check to enable safe mode (
safe_mode directive in the php.ini file) . Failure to set this
option will cause the safe_mode check to be skipped (regardless of the INI setting
How to set safe_mode in)
REPORT_ERRORSWhen an error is encountered during opening of the specified resource, if set
If this option is passed, an error report will be generated.
STREAM_MUST_SEEKFor some streams, such as sockets, this is not possible seek (random
access); This type of file handle can only be used under certain circumstances
seek. If the calling scope specifies this option, and the wrapper
will refuse to open it if it detects that it cannot guarantee that it can seek.
## streams.
STREAM_WILL_CASTThe stream can be converted to stdio if the calling scope requires it Or
posix file descriptor, you should pass this option to the open_wrapper function
## , to ensure that the I/O operation fails before it occurs
STREAM_ONLY_GET_HEADERSIdentifies that only metadata needs to be requested from the stream. In fact this is used for
http wrapper, obtains the http_response_headers global variable
without actually crawling the remote file Content.
STREAM_DISABLE_OPEN_BASEDIRSimilar to the safe_mode check, if this option is not set, it will check
INI sets open_basedir. If you specify this option, you can
Bypass this default check
STREAM_OPEN_PERSISTENT Inform the stream wrapping layer that all internally allocated space is allocated persistently
, and the associated resources are registered in the persistence list.
IGNORE_PATHIf not specified, the default include path is searched. Most URLs
wrappers All browsers ignore this option.
IGNORE_URL提供這個(gè)選項(xiàng)時(shí), 流包裝層只打開本地文件. 所
有的is_url包裝器都將被忽略.
最后的NULL參數(shù)是char **類型, 它最初是用來設(shè)置匹配路徑, 如果path指向普通文件URL, 則去掉file://部分, 保留直接的文件路徑用于傳統(tǒng)的文件名操作. 這個(gè)參數(shù)僅僅是以前引擎內(nèi)部處理使用的.
此外, 還有php_stream_open_wrapper()的一個(gè)擴(kuò)展版本:
php_stream *php_stream_open_wrapper_ex(char *path, char *mode, int options, char **opened_path, php_stream_context *context);
最后一個(gè)參數(shù)context允許附加的控制, 并可以得到包裝器內(nèi)的通知. 你將在第16章看到這個(gè)參數(shù)的細(xì)節(jié).
傳輸層包裝
盡管傳輸流和fopen包裝流是相同的組件組成的, 但它的注冊(cè)策略和其他的流不同. 從某種程度上來說, 這是因?yàn)橛脩艨臻g對(duì)它們的訪問方式的不同造成的, 它們需要實(shí)現(xiàn)基于套接字的其他因子.
從擴(kuò)展開發(fā)者角度來看, 打開傳輸流的過程是相同的. 下面是對(duì)fsockopen()的實(shí)現(xiàn):
PHP_FUNCTION(sample5_fsockopen) { php_stream *stream; char *host, *transport, *errstr = NULL; int host_len, transport_len, implicit_tcp = 1, errcode = 0; long port = 0; int options = ENFORCE_SAFE_MODE; int flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &host, &host_len, &port) == FAILURE) { return; } if (port) { int implicit_tcp = 1; if (strstr(host, "://")) { /* A protocol was specified, * no need to fall back on tcp:// */ implicit_tcp = 0; } transport_len = spprintf(&transport, 0, "%s%s:%d", implicit_tcp ? "tcp://" : "", host, port); } else { /* When port isn't specified * we can safely assume that a protocol was * (e.g. unix:// or udg://) */ transport = host; transport_len = host_len; } stream = php_stream_xport_create(transport, transport_len, options, flags, NULL, NULL, NULL, &errstr, &errcode); if (transport != host) { efree(transport); } if (errstr) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %s", errcode, errstr); efree(errstr); } if (!stream) { RETURN_FALSE; } php_stream_to_zval(stream, return_value); }
這個(gè)函數(shù)的基礎(chǔ)構(gòu)造和前面的fopen示例是一樣的. 不同在于host和端口號(hào)使用不同的參數(shù)指定, 接著為了給出一個(gè)傳輸流URL就必須將它們合并到一起. 在產(chǎn)生了一個(gè)有意義的"路徑:后, 將它傳遞給php_stream_xport_create()函數(shù), 方式和fopen()使用的php_stream_open_wrapper()API一樣. php_stream_xport_create()的原型如下:
php_stream *php_stream_xport_create(char *xport, int xport_len, int options, int flags, const char *persistent_id, struct timeval *timeout, php_stream_context *context, char **errstr, int *errcode);
每個(gè)參數(shù)的含義如下:
xport基于URI的傳輸描述符. 對(duì)于基于inet的套接字流, 它可以是
tcp://127.0.0.1:80, udp://10.0.0.1:53, ssl://169.254.13.24:445等. 此
外, UNIX域傳輸協(xié)議unix:///path/to/socket,
udg:///path/to/dgramsocket等都是合法的. xport_len指定了xport的長(zhǎng)
度, 因此xport是二進(jìn)制安全的.
options這個(gè)值是由前面php_stream_open_wrapper()中介紹的選項(xiàng)通過按位
或組成的值.
flags由STREAM_XPORT_CLIENT或STREAM_XPORT_SERVER之一
與下面另外一張表中將列出的STREAM_XPORT_*常量通過按位或
組合得到的值.
persistent_idIf the requested transport stream needs to be persisted between requests, the calling scope can provide a
key Name describing the connection. Specifying this value as NULL creates a non-persistent connection; specifying a unique string value will attempt to first look up from the persistence pool. There is an existing transport stream, or if there is no
create a new persistence stream when found.
timeout How long to attempt a connection before a timeout returns and fails. Passing NULL for this value causes
to use the default specified in php.ini Timeout value. This parameter has no meaning for server-side transport streams.
errstrIf creating, connecting, binding or listening on the selected socket An error occurs, the char * reference value passed here
will be set to a string describing the cause of the error. errstr is initially
should initially point to NULL; if it is set when returning, the calling scope has
Responsibility to release the memory related to this string.
errcodeThe numerical error code corresponding to the error message returned through errstr.
The flags parameter of php_stream_xport_create() uses the STREAM_XPORT_* family of constants defined as follows:
STREAM_XPORT_CLIENTThe local end will establish a connection with the remote resource through the transport layer . This
## tag is usually used in conjunction with STREAM_XPORT_CONNECT or
#STREAM_XPORT_CONNECT_ASYNC
##Use.STREAM_XPORT_SERVER
The local end will accept the connection through the transport layer. This flag is usually
## Used with STREAM_XPORT_BIND and##STREAM_XPORT_LISTEN.
STREAM_XPORT_CONNECTUsed to illustrate that establishing a remote resource connection is a part of the creation of the transport stream
. When creating the client transport stream It is legal to omit this flag
, but doing so requires manual invocation
# #php_stream_xport_connect().
STREAM_XPORT_CONNECT_ASYNC嘗試連接到遠(yuǎn)程資源, 但不阻塞.
STREAM_XPORT_BIND將傳輸流綁定到本地資源. 用在服務(wù)端傳輸流時(shí),
這將使得accept連接的傳輸流準(zhǔn)備端口, 路徑或
特定的端點(diǎn)標(biāo)識(shí)符等信息.
STREAM_XPORT_LISTEN在已綁定的傳輸流端點(diǎn)上監(jiān)聽到來的連接. 這通
常用于基于流的傳輸協(xié)議, 比如: tcp://, ssl://,
unix://.
目錄訪問
fopen包裝器支持目錄訪問, 比如file://和ftp://, 還有第三種流打開函數(shù)也可以用于目錄訪問, 下面是對(duì)opendir()的實(shí)現(xiàn):
PHP_FUNCTION(sample5_opendir) { php_stream *stream; char *path; int path_len, options = ENFORCE_SAFE_MODE | REPORT_ERRORS; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &path, &path_len) == FAILURE) { return; } stream = php_stream_opendir(path, options, NULL); if (!stream) { RETURN_FALSE; } php_stream_to_zval(stream, return_value); }
同樣的, 也可以為某個(gè)特定目錄打開一個(gè)流, 比如本地文件系統(tǒng)的目錄名或支持目錄訪問的URL格式資源. 這里我們又看到了options參數(shù), 它和原來的含義一樣, 第三個(gè)參數(shù)NULL原型是php_stream_context類型.
在目錄流打開后, 和文件以及傳輸流一樣, 返回給用戶空間.
特殊流
還有一些特殊類型的流不能歸類到fopen/transport/directory中. 它們中每一個(gè)都有自己獨(dú)有的API:
php_stream *php_stream_fopen_tmpfile(void); php_stream *php_stream_fopen_temporary_file(const char *dir, const char *pfx, char **opened_path);
創(chuàng)建一個(gè)可seek的緩沖區(qū)流用于讀寫. 在關(guān)閉時(shí), 這個(gè)流使用的所有臨時(shí)資源, 包括所有的緩沖區(qū)(無論是在內(nèi)存還是磁盤), 都將被釋放. 使用這一組API中的后一個(gè)函數(shù), 允許臨時(shí)文件被以特定的格式命名放到指定路徑. 這些內(nèi)部API調(diào)用被用戶空間的tmpfile()函數(shù)隱藏.
php_stream *php_stream_fopen_from_fd(int fd, const char *mode, const char *persistent_id); php_stream *php_stream_fopen_from_file(FILE *file, const char *mode); php_stream *php_stream_fopen_from_pipe(FILE *file, const char *mode);
這3個(gè)API方法接受已經(jīng)打開的FILE *資源或文件描述符ID, 使用流API的某種操作包裝. fd格式的接口不會(huì)搜索匹配你前面看到過的fopen函數(shù)打開的資源, 但是它會(huì)注冊(cè)持久化的資源, 后續(xù)的fopen可以使用到這個(gè)持久化資源.
訪問流
在你打開一個(gè)流之后, 就可以在它上面執(zhí)行I/O操作了. 使用那種協(xié)議包裝API創(chuàng)建了流并不重要, 它們都使用相同的訪問API.
讀
流的讀寫可以使用下面的API函數(shù)組合完成, 它們多數(shù)都是遵循POSIX I/O中對(duì)應(yīng)的API規(guī)范的:
int php_stream_getc(php_stream *stream);
從數(shù)據(jù)流中接收一個(gè)字符. 如果流上再?zèng)]有數(shù)據(jù), 則返回EOF.
size_t php_stream_read(php_stream *stream, char *buf, size_t count);
從指定流中讀取指定字節(jié)的數(shù)據(jù). buf必須預(yù)分配至少count字節(jié)的內(nèi)存空間. 這個(gè)函數(shù)將返回從數(shù)據(jù)流實(shí)際讀到緩沖區(qū)中的數(shù)據(jù)字節(jié)數(shù).
php_stream_read()不同于其他的流讀取函數(shù). 如果使用的流不是普通文件流, 哪怕數(shù)據(jù)流中有超過請(qǐng)求字節(jié)數(shù)的數(shù)據(jù), 并且當(dāng)前也可以返回, 它也只會(huì)調(diào)用過一次底層流實(shí)現(xiàn)的read函數(shù). 這是為了兼容基于包(比如UDP)的協(xié)議的這種做法.
char *php_stream_get_line(php_stream *stream, char *buf, size_t maxlen, size_t *returned_len); char *php_stream_gets(php_stream *stream, char *buf, size_t maxlen);
這兩個(gè)函數(shù)從stream中讀取最多maxlen個(gè)字符, 直到碰到換行符或流結(jié)束. buf可以是一個(gè)指向預(yù)分配的至少maxlen字節(jié)的內(nèi)存空間的指針, 也可以是NULL, 當(dāng)它是NULL時(shí), 則會(huì)自動(dòng)的創(chuàng)建一個(gè)動(dòng)態(tài)大小的緩沖區(qū), 用從流中實(shí)際讀出的數(shù)據(jù)填充, 成功后函數(shù)返回指向緩沖區(qū)的指針, 失敗則返回NULL. 如果returned_len傳遞了非NULL值, 則在返回時(shí)它將被設(shè)置為實(shí)際從流中讀取的字節(jié)數(shù).
char *php_stream_get_record(php_stream *stream, size_t maxlen, size_t *returned_len, char *delim, size_t delim_len TSRMLS_DC);
和php_stream_get_line()類似, 這個(gè)函數(shù)將讀取最多maxlen, 或到達(dá)EOF/行結(jié)束第一次出現(xiàn)的位置. 但是它也有和php_stream_get_line()的不同指出, 這個(gè)函數(shù)允許指定任意的停止讀取標(biāo)記.
讀取目錄項(xiàng)
從php流中讀取目錄項(xiàng)和上面從普通文件中讀取普通數(shù)據(jù)相同. 這些數(shù)據(jù)放到了固定大小的dirents塊中. 內(nèi)部的php_stream_dirent結(jié)構(gòu)體如下, 它與POSIX定義的dirent結(jié)構(gòu)體一致:
typedef struct _php_stream_dirent { char d_name[MAXPATHLEN]; } php_stream_dirent;
實(shí)際上你可以直接使用php_stream_read()函數(shù)讀取數(shù)據(jù)到這個(gè)結(jié)構(gòu)體中:
{ struct dirent entry; if (php_stream_read(stream, (char*)&entry, sizeof(entry)) == sizeof(entry)) { /* 成功從目錄流中讀取到一項(xiàng) */ php_printf("File: %s\n", entry.d_name); } }
由于從目錄流中讀取是很常見的操作, php流包裝層暴露了一個(gè)API, 它將記錄大小的檢查和類型轉(zhuǎn)換處理封裝到了一次調(diào)用中:
php_stream_dirent *php_stream_readdir(php_stream *dirstream, php_stream_dirent *entry);
如果成功讀取到目錄項(xiàng), 則傳入的entry指針將被返回, 否則返回NULL標(biāo)識(shí)錯(cuò)誤. 使用這個(gè)為目錄流特殊構(gòu)建的函數(shù)而不是直接從目錄流讀取非常重要, 這樣做未來流API改變時(shí)就不至于和你的代碼沖突.
寫
和讀類似, 向流中寫數(shù)據(jù)只需要傳遞一個(gè)緩沖區(qū)和緩沖區(qū)長(zhǎng)度給流.
size_t php_stream_write(php_stream *stream, char *buf, size_t count); size_t php_stream_write_string(php_stream *stream, char *stf);
write_string的版本實(shí)際上是一個(gè)提供便利的宏, 它允許寫一個(gè)NULL終止的字符串,而不用顯式的提供長(zhǎng)度. 返回的是實(shí)際寫到流中的字節(jié)數(shù). 要特別小心的是嘗試寫大數(shù)據(jù)的時(shí)候可能導(dǎo)致流阻塞, 比如套接字流, 而如果流被標(biāo)記為非阻塞, 則實(shí)際寫入的數(shù)據(jù)量可能會(huì)小于傳遞給函數(shù)的期望大小.
int php_stream_putc(php_stream *stream, int c); int php_stream_puts(php_string *stream, char *buf);
還有一種選擇是, 使用php_stream_putc()和php_stream_puts()寫入一個(gè)字符或一個(gè)字符串到流中. 要注意, php_stream_puts()不同于php_stream_write_string(), 雖然它們的原型看起來是一樣的, 但是php_stream_puts()會(huì)在寫出buf中的數(shù)據(jù)后自動(dòng)的追加一個(gè)換行符.
size_t php_stream_printf(php_stream *stream TSRMLS_DC, const char *format, ...);
功能和格式上都類似于fprintf(), 這個(gè)API調(diào)用允許在寫的同時(shí)構(gòu)造字符串而不用去創(chuàng)建臨時(shí)緩沖區(qū)構(gòu)造數(shù)據(jù). 這里我們能夠看到的一個(gè)明顯的不同是它需要TSRMLS_CC宏來保證線程安全.
隨機(jī)訪問, 查看文件偏移量以及緩存的flush
基于文件的流, 以及另外幾種流是可以隨機(jī)訪問的. 也就是說, 在流的一個(gè)位置讀取了一些數(shù)據(jù)之后, 文件指針可以向前或向后移動(dòng), 以非線性順序讀取其他部分.
如果你的流應(yīng)用代碼預(yù)測(cè)到底層的流支持隨機(jī)訪問, 在打開的時(shí)候就應(yīng)該傳遞STREAM_MUST_SEEK選項(xiàng). 對(duì)于那些原本就可隨機(jī)訪問的流來說, 這通常不會(huì)有什么影響, 因?yàn)榱鞅旧砭褪强呻S機(jī)訪問的. 而對(duì)于那些原本不可隨機(jī)訪問的流, 比如網(wǎng)絡(luò)I/O或線性訪問文件比如FIFO管道, 這個(gè)暗示可以讓調(diào)用程序有機(jī)會(huì)在流的數(shù)據(jù)被消耗掉之前, 優(yōu)雅的失敗.
在可隨機(jī)訪問的流資源上工作時(shí), 下面的函數(shù)可用來將文件指針移動(dòng)到任意位置:
int php_stream_seek(php_stream *stream, off_t offset, int whence); int php_stream_rewind(php_stream *stream);
offset是相對(duì)于whence表示的流位置的偏移字節(jié)數(shù), whence的可選值及含義如下:
SEEK_SEToffset相對(duì)于文件開始位置. php_stream_rewind()API調(diào)用實(shí)際上是一個(gè)宏,
展開后是php_stream_seek(stream, 0, SEEK_SET), 表示移動(dòng)到文件開始
位置偏移0字節(jié)處. 當(dāng)使用SEEK_SET時(shí), 如果offset傳遞負(fù)值被認(rèn)為是錯(cuò)誤
的, 將會(huì)導(dǎo)致未定義行為. 指定的位置超過流的末尾也是未定義的, 不過結(jié)果
通常是一個(gè)錯(cuò)誤或文件被擴(kuò)大以滿足指定的偏移量.
SEEK_CURoffset相對(duì)于文件的當(dāng)前偏移量. 調(diào)用php_stream_seek(steram, offset,
SEEK_CUR)一般來說等價(jià)于php_stream_seek(stream, php_stream_tell()
+ offset, SEEK_SET);
SEEK_ENDoffset是相對(duì)于當(dāng)前的EOF位置的. 負(fù)值的offset表示在EOF之前的位置, 正
值和SEEK_SET中描述的是相同的語義, 可能在某些流實(shí)現(xiàn)上可以工作.
int php_stream_rewinddir(php_stream *dirstream);
在目錄流上隨機(jī)訪問時(shí), 只有php_stream_rewinddir()函數(shù)可用. 使用php_stream_seek()函數(shù)將導(dǎo)致未定義行為. 所有的隨機(jī)訪問一族函數(shù)返回0標(biāo)識(shí)成功或者-1標(biāo)識(shí)失敗.
off_t php_stream_tell(php_stream *stream);
如你之前所見, php_stream_tell()將返回當(dāng)前的文件偏移量.
int php_stream_flush(php_stream *stream);
調(diào)用flush()函數(shù)將強(qiáng)制將流過濾器此類內(nèi)部緩沖區(qū)中的數(shù)據(jù)輸出到最終的資源中. 在流被關(guān)閉時(shí), flush()函數(shù)將自動(dòng)調(diào)用, 并且大多數(shù)無過濾流資源雖然不進(jìn)行任何內(nèi)部緩沖, 但也需要flush. 顯式的調(diào)用這個(gè)函數(shù)很少見, 并且通常也是不需要的.
int php_stream_stat(php_stream *stream, php_stream_statbuf *ssb);
調(diào)用php_stream_stat()可以獲取到流實(shí)例的其他信息, 它的行為類似于fstat()函數(shù). 實(shí)際上, php_stream_statbuf結(jié)構(gòu)體現(xiàn)在僅包含一個(gè)元素: struct statbuf sb; 因此, php_stream_stat()調(diào)用可以如下面例子一樣, 直接用傳統(tǒng)的fstat()操作替代, 它只是將posix的stat操作翻譯成流兼容的:
int php_sample4_fd_is_fifo(int fd) { struct statbuf sb; fstat(fd, &sb); return S_ISFIFO(sb.st_mode); } int php_sample4_stream_is_fifo(php_stream *stream) { php_stream_statbuf ssb; php_stream_stat(stream, &ssb); return S_ISFIFO(ssb.sb.st_mode); }
關(guān)閉
所有流的關(guān)閉都是通過php_stream_free()函數(shù)處理的, 它的原型如下:
int php_stream_free(php_stream *stream, int options);
這個(gè)函數(shù)中的options參數(shù)允許的值是PHP_STREAM_FREE_xxx一族常量的按位或的結(jié)果, 這一族常量定義如下(下面省略PHP_STREAM_FREE_前綴):
CALL_DTOR流實(shí)現(xiàn)的析構(gòu)器應(yīng)該被調(diào)用. 這里提供了一個(gè)時(shí)機(jī)對(duì)特定的流
進(jìn)行顯式釋放.
RELEASE_STREAM釋放為php_stream結(jié)構(gòu)體分配的內(nèi)存
PRESERVE_HANDLE指示流的析構(gòu)器不要關(guān)閉它的底層描述符句柄
RSRC_DTOR流包裝層內(nèi)部管理資源列表的垃圾回收
PERSISTENT作用在持久化流上時(shí), 它的行為將是永久的而不局限于當(dāng)前請(qǐng)
求.
CLOSECALL_DTOR和RELEASE_STREAM的聯(lián)合. 這是關(guān)閉
非持久化流的一般選項(xiàng).
CLOSE_CASTEDCLOSE和PRESERVE_HANDLE的聯(lián)合.
CLOSE_PERSISTENTCLOSE和PERSISTENT的聯(lián)合. 這是永久關(guān)閉持久化流的一
般選項(xiàng).
實(shí)際上, 你并不需要直接調(diào)用php_stream_free()函數(shù). 而是在關(guān)閉流時(shí)使用下面兩個(gè)宏的某個(gè)替代:
#define php_stream_close(stream) \ php_stream_free((stream), PHP_STREAM_FREE_CLOSE) #define php_stream_pclose(stream) \ php_stream_free((stream), PHP_STREAM_FREE_CLOSE_PERSISTENT)
通過zval交換流
因?yàn)榱魍ǔS成涞絲val上, 反之亦然, 因此提供了一組宏用來簡(jiǎn)化操作, 并統(tǒng)一編碼(格式):
#define php_stream_to_zval(stream, pzval) \ ZVAL_RESOURCE((pzval), (stream)->rsrc_id);
要注意, 這里并沒有調(diào)用ZEND_REGISTER_RESOURCE(). 這是因?yàn)楫?dāng)流打開的時(shí)候, 已經(jīng)自動(dòng)的注冊(cè)為資源了, 這樣就可以利用到引擎內(nèi)建的垃圾回收和shutdown系統(tǒng)的優(yōu)點(diǎn). 使用這個(gè)宏而不是嘗試手動(dòng)的將流注冊(cè)為新的資源ID是非常重要的; 這樣做的最終結(jié)果是導(dǎo)致流被關(guān)閉兩次以及引擎崩潰.
#define php_stream_from_zval(stream, ppzval) \ ZEND_FETCH_RESOURCE2((stream), php_stream*, (ppzval), \ -1, "stream", php_file_le_stream(), php_file_le_pstream()) #define php_stream_from_zval_no_verify(stream, ppzval) \ (stream) = (php_stream*)zend_fetch_resource((ppzval) \ TSRMLS_CC, -1, "stream", NULL, 2, \ php_file_le_stream(), php_file_le_pstream())
從傳入的zval *中取回php_stream *有一個(gè)類似的宏. 可以看出, 這個(gè)宏只是對(duì)資源獲取函數(shù)(第9章"資源數(shù)據(jù)類型")的一個(gè)簡(jiǎn)單封裝. 請(qǐng)回顧ZEND_FETCH_RESOURCE2()宏, 第一個(gè)宏php_stream_from_zval()就是對(duì)它的包裝, 如果資源類型不匹配, 它將拋出一個(gè)警告并嘗試從函數(shù)實(shí)現(xiàn)中返回. 如果你只是想從傳入的zval *中獲取一個(gè)php_stream *, 而不希望有自動(dòng)的錯(cuò)誤處理, 就需要使用php_stream_from_zval_no_verify()并且需要手動(dòng)的檢查結(jié)果值.
靜態(tài)資源操作
一個(gè)基于流的原子操作并不需要實(shí)際的實(shí)例. 下面這些API僅僅使用URL執(zhí)行這樣的操作:
int php_stream_stat_path(char *path, php_stream_statbuf *ssb);
和前面的php_stream_stat()類似, 這個(gè)函數(shù)提供了一個(gè)對(duì)POSIX的stat()函數(shù)協(xié)議依賴的包裝. 要注意, 并不是所有的協(xié)議都支持URL記法, 并且即便支持也可能不能報(bào)告出statbuf結(jié)構(gòu)體中的所有成員值. 一定要檢查php_stream_stat_path()失敗時(shí)的返回值, 0標(biāo)識(shí)成功, 要知道, 不支持的元素返回時(shí)其值將是默認(rèn)的0.
int php_stream_stat_path_ex(char *path, int flags, php_stream_statbuf *ssb, php_stream_context *context);
這個(gè)php_stream_url_stat()的擴(kuò)展版本允許傳遞另外兩個(gè)參數(shù). 第一個(gè)是flags, 它的值可以是下面的PHP_STERAM_URL_STAT_*(下面省略PHP_STREAM_URL_STAT_前綴)一族常量的按位或的結(jié)果. 還有一個(gè)是context參數(shù), 它在其他的一些流函數(shù)中也有出現(xiàn), 我們將在第16章去詳細(xì)學(xué)習(xí).
LINK原始的php_stream_stat_path()對(duì)于符號(hào)鏈接或目錄將會(huì)進(jìn)行解析直到碰到
協(xié)議定義的結(jié)束資源. 傳遞PHP_STREAM_URL_STAT_LINK標(biāo)記將導(dǎo)致
php_stream_stat_path()返回請(qǐng)求資源的信息而不會(huì)進(jìn)行符號(hào)鏈接的解析.
(譯注: 我們可以這樣理解, 沒有這個(gè)標(biāo)記, 底層使用stat(), 如果有這個(gè)標(biāo)記,
底層使用lstat(), 關(guān)于stat()和lstat()的區(qū)別, 請(qǐng)查看*nix手冊(cè))
QUIET默認(rèn)情況下, 如果在執(zhí)行URL的stat操作過程中碰到錯(cuò)誤, 包括文件未找到錯(cuò)
誤, 都將通過php的錯(cuò)誤處理機(jī)制觸發(fā). 傳遞QUIET標(biāo)記可以使得
php_stream_stat_path()返回而不報(bào)告錯(cuò)誤.
int php_stream_mkdir(char *path, int mode, int options, php_stream_context *context); int php_stream_rmdir(char *path, int options, php_stream_context *context);
創(chuàng)建和刪除目錄也會(huì)如你期望的工作. 這里的options參數(shù)和前面的php_stream_open_wrapper()函數(shù)的同名參數(shù)含義一致. 對(duì)于php_stream_mkdir(), 還有一個(gè)參數(shù)mode用于指定一個(gè)八進(jìn)制的值表明讀寫執(zhí)行權(quán)限.
小結(jié)
本章中你接觸了一些基于流的I/O的內(nèi)部表象. 下一章將演示做呢樣實(shí)現(xiàn)自己的協(xié)議包裝, 甚至是定義自己的流類型.
以上就是[翻譯][php擴(kuò)展開發(fā)和嵌入式]第14章-php中流的訪問的內(nèi)容,更多相關(guān)內(nèi)容請(qǐng)關(guān)注PHP中文網(wǎng)(miracleart.cn)!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

TostaycurrentwithPHPdevelopmentsandbestpractices,followkeynewssourceslikePHP.netandPHPWeekly,engagewithcommunitiesonforumsandconferences,keeptoolingupdatedandgraduallyadoptnewfeatures,andreadorcontributetoopensourceprojects.First,followreliablesource

PHPbecamepopularforwebdevelopmentduetoitseaseoflearning,seamlessintegrationwithHTML,widespreadhostingsupport,andalargeecosystemincludingframeworkslikeLaravelandCMSplatformslikeWordPress.Itexcelsinhandlingformsubmissions,managingusersessions,interacti

TosettherighttimezoneinPHP,usedate_default_timezone_set()functionatthestartofyourscriptwithavalididentifiersuchas'America/New_York'.1.Usedate_default_timezone_set()beforeanydate/timefunctions.2.Alternatively,configurethephp.inifilebysettingdate.timez

TovalidateuserinputinPHP,usebuilt-invalidationfunctionslikefilter_var()andfilter_input(),applyregularexpressionsforcustomformatssuchasusernamesorphonenumbers,checkdatatypesfornumericvalueslikeageorprice,setlengthlimitsandtrimwhitespacetopreventlayout

ThePhpfunctionSerialize () andunserialize () AreusedtoconvertcomplexdaTastructdestoresintostoraSandaBackagain.1.Serialize () c OnvertsdatalikecarraysorobjectsraystringcontainingTypeandstructureinformation.2.unserialize () Reconstruct theoriginalatataprom

You can embed PHP code into HTML files, but make sure that the file has an extension of .php so that the server can parse it correctly. Use standard tags to wrap PHP code, insert dynamic content anywhere in HTML. In addition, you can switch PHP and HTML multiple times in the same file to realize dynamic functions such as conditional rendering. Be sure to pay attention to the server configuration and syntax correctness to avoid problems caused by short labels, quotation mark errors or omitted end labels.

The key to writing clean and easy-to-maintain PHP code lies in clear naming, following standards, reasonable structure, making good use of comments and testability. 1. Use clear variables, functions and class names, such as $userData and calculateTotalPrice(); 2. Follow the PSR-12 standard unified code style; 3. Split the code structure according to responsibilities, and organize it using MVC or Laravel-style catalogs; 4. Avoid noodles-style code and split the logic into small functions with a single responsibility; 5. Add comments at key points and write interface documents to clarify parameters, return values ??and exceptions; 6. Improve testability, adopt dependency injection, reduce global state and static methods. These practices improve code quality, collaboration efficiency and post-maintenance ease.

Yes,youcanrunSQLqueriesusingPHP,andtheprocessinvolveschoosingadatabaseextension,connectingtothedatabase,executingqueriessafely,andclosingconnectionswhendone.Todothis,firstchoosebetweenMySQLiorPDO,withPDObeingmoreflexibleduetosupportingmultipledatabas
