提问人:deceze 提问时间:6/24/2010 最后编辑:deceze 更新时间:9/14/2013 访问量:4839
为什么 php://input 可以多次阅读,尽管文档另有说明?
Why can php://input be read more than once despite the documentation saying otherwise?
问:
PHP 文档指出只能读取一次。php://input
在我的应用程序中,我需要读取它两次,一次用于身份验证目的,一次用于实际处理内容,这两个功能都由不同的独立模块处理。疯狂的是:它有效。
我可以指望它在任何地方都能工作吗,或者这是我的 PHP 版本 (5.2.10) 中的侥幸?我能找到的唯一关于这一点的文档是声明它不应该工作的文档,没有提到版本限制。
按照Dennis的预感,我做了这个测试:
$in = fopen('php://input', 'r');
echo fread($in, 1024) . "\n";
fseek($in, 0);
echo fread($in, 1024) . "\n";
fclose($in);
echo file_get_contents('php://input') . "\n";
冰壺:
$ curl http://localhost:8888/tests/test.php -d "This is a test"
This is a test
This is a test
显然,它仅限于每个打开的句柄读取一次。
再深入挖掘一下就会发现,对于 PUT 请求,确实只能读取一次。上面的示例使用了 POST 请求。php://input
答:
也许它们的意思是 fseek() 或 rewind() 不可用。 您是否在打开的 php://input 上尝试过这些功能之一?
评论
对源代码进行一些检查就会得出答案。
首先,是的,每个句柄只能读取一次,因为基础流未实现处理程序:seek
php_stream_ops php_stream_input_ops = {
php_stream_input_write,
/* ... */
"Input",
NULL, /* seek */
/* ... */
};
其次,读取处理程序具有两种不同的行为,具体取决于“POST 数据”是否已被读取并存储在 中。SG(request_info).raw_post_data
if (SG(request_info).raw_post_data) {
read_bytes = SG(request_info).raw_post_data_length - *position;
/* ...*/
if (read_bytes) {
memcpy(buf, SG(request_info).raw_post_data + *position, read_bytes);
}
} else if (sapi_module.read_post) {
read_bytes = sapi_module.read_post(buf, count TSRMLS_CC);
/* ... */
} else {
stream->eof = 1;
}
因此,我们这里有三种可能性:
- 请求正文数据已被读取并存储在 中。在这种情况下,由于存储了数据,我们可以打开并读取 的多个句柄。
SG(request_info).raw_post_data
php://input
- 请求正文数据已被读取,但其内容未存储在任何地方。 不能给我们任何东西。
php://input
- 尚未读取请求数据。这意味着我们只能打开和读取一次。
php://input
注意:以下是默认行为。不同的 SAPI 或其他扩展可能会更改此行为。
在 POST 请求的情况下,PHP 会根据内容类型定义不同的 POST 阅读器和 POST 处理程序。
案例 1.当我们有 POST 请求时,就会发生这种情况:
- 使用 content-type
application/x-www-form-encoded
。 检测具有 content-type 的 POST 请求并调用 。这将检测内容类型并定义 POST 读取器/处理程序对。POST 读取器是 ,它会立即被调用,只是将请求正文复制到 。然后调用默认的后处理器,如果设置了 ini 设置,则会填充,然后复制并清除第一个阅读器。对处理程序的调用在这里无关紧要,并且会延迟到构建超全局变量(如果激活了 JIT 并且未使用超全局变量,则可能不会发生这种情况)。sapi_activate
sapi_read_post_data
sapi_read_standard_form_data
SG(request_info).post_data
php_default_post_reader
$HTTP_RAW_POST_DATA
always_populate_post_data
SG(request_info).post_data
SG(request_info).raw_post_data
- 使用无法识别或不存在的内容类型。在本例中,没有定义的 POST 读取器和处理程序。这两种情况最终都没有读取任何数据。由于这是一个 POST 请求,并且没有读取器/处理程序对,因此将被调用。这与内容类型的读取处理程序相同,因此所有数据都会被吞噬。从现在开始,唯一的区别是始终填充(无论 的值如何),并且没有用于构建超全局变量的处理程序。
php_default_post_reader
sapi_read_standard_form_data
application/x-www-form-encoded
SG(request_info).post_data
$HTTP_RAW_POST_DATA
always_populate_post_data
案例 2.当我们有一个内容类型为“multipart/form-data”的表单请求时,就会发生这种情况。POST 读取器是 ,因此处理程序充当混合 .在该阶段不会读取任何数据。该函数最终在稍后阶段调用,该阶段又调用 POST 处理程序。 读取请求数据,填充 和 ,但在 中不保留任何内容。NULL
rfc1867_post_handler
reader/handler
sapi_activate
sapi_handle_post
rfc1867_post_handler
POST
FILES
SG(request_info).raw_post_data
案例 3.最后一种情况发生在与 POST 不同的请求(例如 PUT)中。 被直接调用。由于该请求不是 POST 请求,因此数据被 吞噬。由于没有读取任何数据,因此无需执行任何操作。php_default_post_reader
sapi_read_standard_form_data
评论
php://input
file_get_contents('php://input')