


[Translation] [php extension development and embedding] Chapter 15 - Implementation of streams in php
Implementing streams
#One of the most powerful features of PHP's streams is that it can access many Data sources: ordinary files, compressed files, network transparent channels, encrypted networks, named pipes and domain sockets, which are unified APIs for user space and internally.
Under the surface of php stream
For a given stream instance, such as a file stream and a network stream, the difference is that the stream creation function you used in the previous chapter returns The ops member in the php_stream structure.
1 2 3 4 5 |
|
The php_stream_ops structure defines a collection of function pointers and a description tag.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
When the stream access function such as php_stream_read() is called, the stream packaging layer actually parses and calls the corresponding function in stream->ops, so that actually What is called is the read implementation specific to the current stream type. For example, the read function in the stream ops structure of an ordinary file is implemented as follows (the actual implementation is a bit more complicated than the example below):
1 2 3 4 5 6 7 |
|
In the ops structure used by the compress.zlib stream, read points to the following function:
1 2 3 4 5 6 7 8 |
|
The first thing to note here is that the function pointer pointed to by the ops structure is often a thin representation of the actual reading function of the data source. Proxy. In the above two examples, the standard I/O stream uses posix's read() function, while the zlib stream uses libz's gzread() function.
You may also Notice that the stream->abstract element is used here. This is a convenience pointer to the stream implementation, which can be used to obtain various related bundle information. In the above example, the pointer to the custom structure is used To store the file descriptor to be used by the underlying read function.
Another thing you may notice is that each function in the php_stream_ops structure expects an existing stream instance , but how to get the instance? How are the abstract members set up and when the stream directive uses which ops structure? The answer lies in the first open stream function you used in the previous chapter (php_stream_open_wrapper()).
When this function is called, PHP's stream wrapper layer attempts to determine what protocol is being requested based on the scheme:// part of the passed URL. This way it can be used in the registered Find the corresponding php_stream_wrapper item in the php wrapper. Each php_stream_wrapper structure can get its own ops element, which points to a php_stream_wrapper_ops structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
Here, the stream packaging layer calls wrapper->ops->stream_opener(), which will perform wrapper-specific operations to create a stream instance, assign the appropriate php_stream_ops structure, and bind related abstract data.
The dir_opener() function provides the same basic services as stream_opener(); however, it is in response to the php_stream_opendir() API call, and usually binds a different php_stream_ops structure to the returned instance. stat() and close() ) functions are repeated at this level to add protocol-specific logic to these operations of the wrapper.
Other functions allow static stream operations to be performed without Actually create the stream instance. Looking back at these stream API calls, they do not actually return php_stream objects, you will see the details of them shortly.
Although url_stat existed internally as a wrapper's ops function when the stream wrapping layer was introduced in PHP 4.3, it was not used until PHP 5.0. Additionally, the last 3 functions, rename(), stream_mkdir() and stream_rmdir() were not introduced until PHP 5.0. Before this version, they were not in the ops structure of the wrapper.
Wrapper operation
Except the url_stat() function, every operation before the const char *label element in the wrapper operation can be used on the activated stream instance. The meaning of each function is as follows:
stream_opener()instantiate a stream instance. When a user-space fopen() function is called, this function
number will be called. The php_stream instance returned by this function is the
## returned by the fopen() function. # Internal representation of file resource handle. Integrated functions such as file(), file_get_contents(),
file_put_contents(), readfile(), etc., This package is used when requesting packaging resources
installer ops.
stream_closer() This function is called when a stream instance ends its life cycle. When stream_opener()
All resources allocated should be called in this function Release.
stream_stat()Similar to the fstat() function in user space, this function should fill the ssb structure (actually
Contains only one struct statbuf sb structure member),
dir_opener() behaves the same as stream_opener(), However, it is called when calling the user space
function of the opendir() family. The underlying stream implementation used by the directory stream follows the same rules as the file stream;
#But directory streaming only needs to return records containing the file names found in the open directory, which
The size of is the size of the structure struct dirent.
Static wrapper operation
Other functions in the wrapper operation function perform atomic operations on the URI path, depending on the wrapper protocol. In the php_stream_wrapper_ops structure of php4.3, there are only url_stat() and unlink(); the other methods are to php 5.0 It is defined later. #ifdef block description should be used when encoding.
url_stat()stat() family function is used to return file metadata, such as Access authorization, size, type; and
#access, modification, creation time. Although this function appeared when PHP 4.3 introduced the flow wrapping layer
在php_stream_wrapper_ops结构体中的, 但直到php 5.0才被用户空
间的stat()函数使用.
unlink()和posix文件系统的同名函数语义相同, 它执行文件删除. 如果对于当
前的包装器删除没有意义, 比如内建的http://包装器, 这个函数应该被
定义为NULL, 以便内核去引发适当的错误消息.
rename()当用户空间的rename()函数的参数$from和$to参数指向的是相同的
底层包装器实现, php则将这个重命名请求分发到包装器的rename函
数.
mkdir() & rmdir()这两个函数直接映射到对应的用户空间函数.
实现一个包装器
为了演示包装器和流操作的内部工作原理, 我们需要重新实现php手册的stream_wrapper_register()一页示例中的var://包装器.
此刻, 首先从下面功能完整的变量流包装实现开始. 构建他, 并开始检查每一块的工作原理.
译注: 为了方便大家阅读, 对代码的注释进行了适量补充调整, 此外, 由于phpapi的调整, 原著中的代码不能直接在译者使用的php-5.4.10中运行, 进行了适当的修改. 因此下面代码结构可能和原著略有不同, 请参考阅读.(下面opendir的例子也进行了相应的修改)
config.m4
1 2 3 4 5 6 7 |
|
php_varstream.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
varstream.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
|
在构建加载扩展后, php就可以处理以var://开始的URL的请求, 它的行为和手册中用户空间实现的行为一致.
内部实现
首先你注意到的可能是这个扩展完全没有暴露用户空间函数. 它所做的只是在MINIT函数中调用了一个核心PHPAPI的钩子, 将var协议和我们定义的包装器关联起来:
1 2 3 4 5 |
|
很明显, 最重要的元素就是ops, 它提供了访问特定流包装器的创建以及检查函数. 你可以安全的忽略abstract属性, 它仅在运行时使用, 在初始化定义时, 它只是作为一个占位符. 第三个元素is_url, 它告诉php在使用这个包装器时是否考虑php.ini中的allow_url_fopen选项. 如果这个值非0, 并且将allow_url_fopen设置为false, 则这个包装器不能被脚本使用.
在本章前面你已经知道, 调用用户空间函数比如fopen将通过这个包装器的ops元素得到php_varstream_wrapper_ops, 这样去调用流的打开函数php_varstream_opener.
这个函数的第一块代码检查是否请求持久化的流:
1 |
|
对于很多包装器这样的请求是合法的. 然而目前的情况这个行为没有意义. 一方面用户空间变量的定义就是临时的, 另一方面, varstream的实例化代价很低, 这就使得持久化的优势很小.
像流包装层报告错误很简单, 只需要返回一个NULL值而不是流实例即可. 流包装层透出到用户空间的失败消息并不会说明具体的错误, 只是说明不能打开URL. 要想给开发者暴露更多的错误信息, 可以在返回之前使用php_stream_wrapper_log_error()函数.
1 2 3 4 |
|
URL解析
实例化varstream的下一步需要一个人类可读的URL, 将它分块放入到一个易管理的结构体中. 幸运的是它使用了和用户空间url_parse()函数相同的机制. 如果URL成功解析, 将会分配一个php_url结构体并设置合适的值. 如果在URL中没有某些值, 在返回的php_url中对应的将被设置为NULL. 这个结构体必须在离开php_varstream_opener函数之前被显式释放, 否则它的内存将会泄露:
1 2 3 4 5 6 7 8 9 10 11 |
|
最后, varstream包装器创建了一个数据结构, 保存了流指向的变量名, 读取时的当前位置. 这个结构体将在流的读取和写入函数中用于获取变量, 并且将在流结束使用时由php_varstream_close函数释放.
opendir()
读写变量内容的实现可以再次进行扩展. 这里可以加入一个新的特性, 允许使用目录函数读取数组中的key. 在你的php_varstream_wrapper_ops结构体之前增加下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
|
现在, 将你的php_varstream_wrapper_ops结构体中的dir_opener的NULL替换成你的php_varstream_opendir函数. 最后, 将下面新定义的类型放入到你的php_varstream.h文件的php_varstream_data定义下面:
1 2 3 4 5 |
|
在你基于fopen()实现的varstream包装器中, 你直接使用持久变量名, 每次执行读写操作时从符号表中获取变量. 而这里, opendir()的实现中获取变量时处理了变量不存在或者类型错误的异常. 你还有一个数组变量的拷贝, 这就说明原数组的改变并不会影响后续的readdir()调用的结果. 原来存储变量名的方式也可以正常工作, 这里只是给出另外一种选择作为演示示例.
由于目录访问是基于成块的目录条目, 而不是字符, 因此这里需要一套独立的流操作. 这个版本中, write没有意义, 因此保持它为NULL. read的实现使用zend_hash_get_current_key_ex()函数将数组映射到目录名. 而随机访问也只是对SEEK_SET有效, 用来响应rewinddir()跳转到数组开始位置.
实际上, 目录流并没有使用SEEK_CUR, SEEK_END, 或者除了0之外的偏移量. 在实现目录流操作时, 最好还是涉及你的函数能以某种方式处理这些情况, 以使得在流包装层变化时能够适应其目录随机访问.
操纵
5个静态包装器操作中的4个用来处理不是基于I/O的流资源操作. 你已经看到过它们并了解它们的原型; 现在我们看看varstream包装器框架中它们的实现:
unlink
在你的wrapper_ops结构体中增加下面的函数, 它可以让unlink()通过varstream包装器, 拥有和unset()一样的行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
这个函数的编码量和php_varstream_opener差不多. 唯一的不同在于这里你需要传递变量名给zend_hash_del()去删除变量.
译注: 译者的php-5.4.10环境中, 使用unlink()删除变量后, 在用户空间再次读取该变量名的值会导致core dump. 因此上面代码中译者进行了修正, 删除变量时使用了zend_delete_global_variable(), 请读者参考阅读zend_delete_global_variable()函数源代码, 考虑为什么直接用zend_hash_del()删除, 会导致core dump. 下面是译者测试用的用户空间代码:
1 2 3 4 5 6 |
|
这个函数的代码量应该和php_varstream_opener差不多. 唯一的不同是这里是传递变量名给zend_hash_del()去删除变量.
rename, mkdir, rmdir
为了一致性, 下面给出rename, mkdir, rmdir函数的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
检查
并不是所有的流操作都涉及到资源的操纵. 有时候也需要查看活动的流在某个时刻的状态, 或检查潜在可打开的资源的状态.
这一节流和包装器的ops函数都是在相同的数据结构php_stream_statbuf上工作的, 它只有一个元素: posix标准的struct statbuf. 当本节的某个函数被调用时, 将尝试填充尽可能多的statbuf元素的成员.
stat
如果设置, 当请求激活流实例的信息时, 将会调用wrapper->ops->stream_stat(). 如果没有设置, 则对应的stream->ops->stat()将会被调用. 无论哪个函数被调用, 都应该尽可能多的向返回的statbuf结构体ssb->sb中填充尽可能多流实例的有用信息. 在普通文件I/O的用法中, 它对应fstat()的标准I/O调用.
url_stat
在流实例外部调用wrapper->ops->url_stat()取到流资源的元数据. 通常来说, 符号链接和重定向都应该被解析, 直到找到一个真正的资源, 对其通过stat()系统调用这样的机制读取统计信息. url_stat的flags参数允许是下面PHP_STREAM_URL_STAT_*系列的常量值(省略PHP_STREAM_URL_STAT_前缀):
LINK不解析符号链接和重定向. 而是报告它碰到的第一个节点的信息, 无论是连
接还是真正的资源.
QUIETDoes not report errors. Note that this is the exact opposite of the REPORT_ERRORS logic found in many other stream functions.
Summary
Whether it is exposing remote network I/O or streaming resources of local data sources, it allows you to The extension hooks into the manipulation functions on Core Data, avoiding the need to reimplement the tedious work of descriptor management and I/O buffers. This makes it more useful and powerful in user space environments.
The next chapter will end the study of the flow packaging layer by learning about filters and contexts. Filters and contexts can be used to select default flow behaviors and even modify data in the process.
The above is the content of [Translation] [php extension development and embedded] Chapter 15 - Implementation of PHP mid-stream. For more related content, please pay attention to the PHP Chinese website (www. php.cn)!

Hot AI Tools

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

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

Undress AI Tool
Undress images for free

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

PHP 8.4 brings several new features, security improvements, and performance improvements with healthy amounts of feature deprecations and removals. This guide explains how to install PHP 8.4 or upgrade to PHP 8.4 on Ubuntu, Debian, or their derivati

Visual Studio Code, also known as VS Code, is a free source code editor — or integrated development environment (IDE) — available for all major operating systems. With a large collection of extensions for many programming languages, VS Code can be c

JWT is an open standard based on JSON, used to securely transmit information between parties, mainly for identity authentication and information exchange. 1. JWT consists of three parts: Header, Payload and Signature. 2. The working principle of JWT includes three steps: generating JWT, verifying JWT and parsing Payload. 3. When using JWT for authentication in PHP, JWT can be generated and verified, and user role and permission information can be included in advanced usage. 4. Common errors include signature verification failure, token expiration, and payload oversized. Debugging skills include using debugging tools and logging. 5. Performance optimization and best practices include using appropriate signature algorithms, setting validity periods reasonably,

A string is a sequence of characters, including letters, numbers, and symbols. This tutorial will learn how to calculate the number of vowels in a given string in PHP using different methods. The vowels in English are a, e, i, o, u, and they can be uppercase or lowercase. What is a vowel? Vowels are alphabetic characters that represent a specific pronunciation. There are five vowels in English, including uppercase and lowercase: a, e, i, o, u Example 1 Input: String = "Tutorialspoint" Output: 6 explain The vowels in the string "Tutorialspoint" are u, o, i, a, o, i. There are 6 yuan in total

This tutorial demonstrates how to efficiently process XML documents using PHP. XML (eXtensible Markup Language) is a versatile text-based markup language designed for both human readability and machine parsing. It's commonly used for data storage an

Static binding (static::) implements late static binding (LSB) in PHP, allowing calling classes to be referenced in static contexts rather than defining classes. 1) The parsing process is performed at runtime, 2) Look up the call class in the inheritance relationship, 3) It may bring performance overhead.

What are the magic methods of PHP? PHP's magic methods include: 1.\_\_construct, used to initialize objects; 2.\_\_destruct, used to clean up resources; 3.\_\_call, handle non-existent method calls; 4.\_\_get, implement dynamic attribute access; 5.\_\_set, implement dynamic attribute settings. These methods are automatically called in certain situations, improving code flexibility and efficiency.

PHP and Python each have their own advantages, and choose according to project requirements. 1.PHP is suitable for web development, especially for rapid development and maintenance of websites. 2. Python is suitable for data science, machine learning and artificial intelligence, with concise syntax and suitable for beginners.
