您可以使用 "php://input" 来接受和解析 "PUT"、"DELETE" 等请求。
<?php
// 解析 "PUT" 请求的示例
parse_str(file_get_contents('php://input'), $_PUT);
// 结果
print_r($_PUT);
?>
(非常适合 Restful API)
PHP 带有许多内置包装器,用于各种 URL 风格的协议,与文件系统函数(如 fopen()、copy()、file_exists() 和 filesize())一起使用。除了这些包装器,还可以使用 stream_wrapper_register() 函数注册自定义包装器。
注意: 用于描述包装器的 URL 语法仅支持
scheme://...
语法。不支持scheme:/
和scheme:
语法。
您可以使用 "php://input" 来接受和解析 "PUT"、"DELETE" 等请求。
<?php
// 解析 "PUT" 请求的示例
parse_str(file_get_contents('php://input'), $_PUT);
// 结果
print_r($_PUT);
?>
(非常适合 Restful API)
如何使用 php://input 获取原始 POST 数据的示例
// 读取原始数据
$roughHTTPPOST = file_get_contents("php://input");
// 解析为变量
parse_str($roughHTTPPOST);
如果您执行 readfile("php://input"),您将获得 POST 数据的长度
要创建一个原始 TCP 监听器系统,我使用以下方法
xinetd 守护程序,配置如下
service test
{
disable = no
type = UNLISTED
socket_type = stream
protocol = tcp
bind = 127.0.0.1
port = 12345
wait = no
user = apache
group = apache
instances = 10
server = /usr/local/bin/php
server_args = -n [您的 PHP 文件在这里]
only_from = 127.0.0.1 # 要爱上安全性 #
log_type = FILE /var/log/phperrors.log
log_on_success += DURATION
}
现在使用 fgets(STDIN) 读取输入。创建连接速度非常快,效果很好。写入可以使用 STDOUT 完成,或者直接使用 echo。请注意,您完全绕过了 Web 服务器,因此某些变量将不可用。
在尝试使用 PHP 和 Javascript 进行 AJAX 时,我遇到了一个问题,使用 $_REQUEST 或 $_POST 在 PHP 5 中无法读取以下 Javascript 中的 POST 参数。我终于找到了如何使用 php://input 指令读取原始数据。
Javascript 代码
=============
// 创建请求实例
xhttp = new XMLHttpRequest();
// 设置事件处理程序
xhttp.onreadystatechange = serviceReturn;
// 准备调用,http 方法=POST,true=异步调用
var Args = 'number='+NbrValue;
xhttp.open("POST", "http://<?php echo $_SERVER['SERVER_NAME'] ?>/webservices/ws_service.php", true);
// 使用参数发送调用
xhttp.send(Args);
PHP 代码
// 读取原始数据
$roughHTTPPOST = file_get_contents("php://input");
// 解析为变量
parse_str($roughHTTPPOST);
以下是一段代码,用于在不启用全局变量的情况下读取压缩的原始 POST 数据。
我需要它来读取由 ocs 代理提交的 POST 的 XML 数据。数据以 Content-Type: application/x-compressed(zlib 压缩数据)发送。
它似乎与一个旧的错误有关,该错误似乎仍然存在
https://bugs.php.net/bug.php?id=49411
重要的是将默认窗口设置为 15 而不是 -15。
代码片段
<?php
$data = '';
$fh = fopen('php://input', 'rb');
stream_filter_append($fh, 'zlib.inflate', STREAM_FILTER_READ, array('window'=>15));
while(!feof($fh)) {
$data .= fread($fh, 8192);
}
?>
流 php://temp/maxmemory:$limit 将数据存储在内存中,除非达到限制。然后它会将整个内容写入临时文件并释放内存。我找不到将至少部分数据返回到内存的方法。
即使它们的名称相同,您也可以同时打开多个 //memory 或 //temp 流;每次您 fopen() 这样的流时,都会打开一个新的流,与其他流无关。
这暗示了您在创建此类流时没有在路径中添加任何唯一标识符,但没有明确说明。
<?php
$hello = fopen('php://memory', 'r+'); // $hello、$php、$world 都是不同的流。
$php = fopen('php://memory', 'r+');
$world = fopen('php://memory', 'r+'); // 它们不是同一个流打开了三次。
fputs($hello, "Hello ");
fputs($php, "PHP ");
rewind($php);
fputs($world, "World!");
rewind($hello);
rewind($world);
echo '[', stream_get_contents($hello), '][', stream_get_contents($php), '][', stream_get_contents($world), ']';
// 如果它们是同一个流,输出将为 "[World!][World!][World!]".
?>
您可以通过组合包装器来解压缩(gzip)输入流
例如:$x = file_get_contents("compress.zlib://php://input");
我使用这种方法来解压缩推送到我的 Web 服务器的 gzip 流
要读取 XML 流,这就可以了
<?php
$arq = file_get_contents('php://input');
?>
然后你可以像这样解析 XML
<?php
$xml = xml_parser_create();
xml_parse_into_struct($xml, $arq, $vs);
xml_parser_free($xml);
$data = "";
foreach($vs as $v){
if($v['level'] == 3 && $v['type'] == 'complete')
$data .= "\n".$v['tag']." -> ".$v['value'];
}
echo $data;
?>
PS:这对于接收来自手机公司的移动源(MO)短信特别有用。
以追加模式打开 php://output 时,会出错,解决方法如下
$fp=fopen("php://output","w");
fwrite($fp,"Hello, world !<BR>\n");
fclose($fp);
写入错误流时,error_log() 函数是写入 php://stderr 的简写。这个函数还允许在通过像 apache 这样的 Web 服务器运行时写入 Web 服务器日志。
注意
file_get_contents 中使用的 file:// 协议用作“任何无法识别的协议”的默认值。因此
aldfjadlfadfladfl://whatever
将与
file://whatever
如果你想通过 php://input 过滤传入数据,可以使用这个
file_get_contents("php://filter/read=string.strip_tags/resource=php://input");
我找不到任何文档解释如何做到这一点。我遇到的所有示例都建议必须使用完整的实际 URL(这对我不起作用)。
这似乎有效。
[ 编辑注:有一种方法可以知道。所有响应头(来自最终响应服务器和中间重定向器)都可以在 $http_response_header 或 stream_get_meta_data() 中找到,如上所述。 ]
如果你打开一个 HTTP URL,并且服务器发出 Location 风格的重定向,重定向的内容将被读取,但你无法发现发生了这种情况。
因此,如果你随后解析返回的 html 并尝试合理化相对 URL,你可能会出错。
如果你正在寻找基于 Unix 的 smb 包装器,则没有内置的包装器,但我对 http://www.zevils.com/cgi-bin/viewcvs.cgi/libsmbclient-php/(末尾的 tarball 链接)很满意。
STDIN、STDOUT 和 STDERR 不仅限于 CLI 程序,而且不允许用于从 STDIN 读取的程序。如果你尝试输入一个简单的测试程序,这可能会让你困惑。
我发现使用 file_get_contents 与 php://input 非常方便且高效。以下是代码
$request = "";
$request = file_get_contents("php://input");
我不需要将 URL 文件字符串声明为“r”。它会自动处理以读取方式打开文件。
然后我可以用这个 $request 字符串作为数据来使用你的 XMLparser。
注意代码注入,伙计们 - 就像你从用户那里获取的任何其他东西一样,首先进行清理。这一点再怎么强调也不为过 - 如果我有一美元就能得到每次看到代码的地方,这些代码直接使用表单输入(我自己也是,我也很蠢),我可能会拥有 PHP。虽然在 URL 包装器中使用来自表单的数据是在自找麻烦,但你可以通过确保输入是合理的,并且不太可能为世界 LulzSec 提供造成混乱的机会来最大限度地减少麻烦。
后续
我发现如果我在 AJAX 调用中添加这行代码,这些值就会显示在 $_POST 中
xhttp.setRequestHeader('Content-Type',
'application/x-www-form-urlencoded');
处理大型文件上传的一个有用方法是执行以下操作
copy(("php://input"),$tmpfile);
因为这避免了仅仅为了缓冲文件内容而使用大量内存。
这的正确 mime 类型应该是“application/octet-stream”,但是如果你在 POST 上设置了这个或任何其他识别的 mime 类型(除了“multipart/form-data”),则会填充 $HTTP_RAW_POST_DATA,并且内存也会被消耗。
将 mime 类型设置为“multipart/form-data”会导致“PHP Warning: Missing boundary in multipart/form-data POST data in Unknown on line 0”,但是它似乎在没有问题的情况下工作。
对于 php://filter,/resource=foo 部分必须放在最后。并且 foo 完全不需要转义。
php://filter/resource=foo/read=somefilter 会尝试打开文件“foo/read=somefilter”,而 php://filter/read=somefilter/resource=foo 会打开文件“foo”,并应用 somefilter 过滤器。
将 php://temp/maxmemory 用作流会计算在脚本的内存使用量中;你没有通过使用这种类型的流来指定新的内存池。
如文档中所述,但是,当超过指定的 maxmemory 限制后,此流类型将开始写入文件。此文件缓冲区不受内存限制的观察。
如果你希望你的脚本具有合理的内存限制(例如 32MB),但仍然能够处理大量流数据(例如 256MB),这将非常方便。
这仅在使用 fputs() 等流函数时才有效;如果你使用 $buffer .= 'string' 或 $buffer = $buffer . 'string',你将调用流数据返回到 PHP,这将击中限制器。
作为一个实际的例子
<?php
// 0.5MB 内存限制
ini_set('memory_limit', '0.5M');
// 2MB 流限制
$buffer = fopen('php://temp/maxmemory:1048576', 'r+');
$x = 0;
// 尝试将 1MB 写入流
while ($x < 1*1024*1024) {
fputs($buffer, 'a');
$x++;
}
echo "这将永远不会显示";
?>
但是,将 fopen 更改为使用 php://temp/maxmemory:1(一个字节,而不是一个兆字节),它会立即开始写入无限制的文件流,避免内存限制错误。
<?php
// 在需要时启用 $HTTP_RAW_POST_DATA
ini_set('always_populate_raw_post_data',-1);
$HTTP_RAW_POST_DATA = file_get_contents('php://input');
echo $HTTP_RAW_POST_DATA;
?>
在 PHP 5.4+ 中,如果你将 enable_post_data_reading 设置为 Off,则可以通过 php://input 读取 multipart 数据。
当然,如果你将其设置为 off,则 $_POST 和 $_FILES 超级全局变量将根本不会填充。现在完全由你解析数据。
指向 php://memory 和 php://temp 的每个流指针都有其自己的内存分配,因此你可以打开多个流指针来存储你的分离值。
<?php
$fp = fopen("php://temp", "r+");
$fp2 = fopen("php://temp", "r+");
fwrite($fp, "line1\n");
fwrite($fp2, "line4\n");
fwrite($fp, "line2\n");
fwrite($fp2, "line5\n");
fwrite($fp, "line3\n");
fwrite($fp2, "line6\n");
var_dump(memory_get_usage());
rewind($fp);
while(!feof($fp)) {
var_dump(fread($fp, 1024));
}
fclose($fp);
var_dump(memory_get_usage());
rewind($fp2);
while(!feof($fp2)) {
var_dump(fread($fp2, 1024));
}
fclose($fp2);
var_dump(memory_get_usage());
?>
关闭它们的流句柄也将释放已分配的内存。
php://memory 流类型是 MEMORY,而 php://temp 流类型是 STDIO FILE*。