如何将变量作为标准输入从 PHP 传递到命令行
Posted
技术标签:
【中文标题】如何将变量作为标准输入从 PHP 传递到命令行【英文标题】:How to pass variables as stdin into command line from PHP 【发布时间】:2011-01-24 08:08:08 【问题描述】:我正在尝试编写一个 php 脚本,该脚本使用 pdftk 应用程序将 XFDF 与 PDF 表单合并并将合并后的 PDF 输出给用户。根据 pdftk 文档,我可以通过stdin
传递表单数据并将PDF 输出到stdout
流。从命令行使用 pdftk 的正常文件非流方式是:
pdftk blankform.pdf fill_form formdata.xfdf output filledform.pdf
要在命令行上使用流,您需要输入:
pdftk blankform.pdf fill_form - output -
我有几个问题:
1) 我已经让 pdftk 使用 xfdf 文件(而不是 stdin
)通过 stdout
返回输出,如下所示:
exec("pdftk blankform.pdf fill_form formdata.xfdf output -", $pdf_output);
file_put_contents("filledform.pdf",$pdf_output);
但根据 Adobe Reader 的说法,它创建的 pdf 文件已损坏,使用文本编辑器快速查看文件表明,至少,它没有将行尾设置在应有的位置。我有一个由 pdftk 创建的相同 PDF,它输出到一个文件,并且 pdf 在文本编辑器中看起来很好,所以我知道输出错误数据的不是 pdftk。
2) 我终生无法弄清楚如何在 PHP 中设置stdin
流,以便我可以使用该流作为 pdftk 的输入。根据我在 PHP 文档中阅读的内容,stdin
是只读的,那么任何东西是如何进入该流的呢?
理想情况下,我希望保持简单,避免使用proc_open()
。我尝试使用该功能但不是很成功,这可能是我的错,而不是功能的错,但实际上我的目标很简单,我宁愿避免使用我不需要的强大功能。
理想情况下,我的代码如下所示:
$form_data_raw = $_POST;
$form_data_xfdf = raw2xfdf($form_data_raw); //some function that turns html-form data to XFDF
$blank_pdf_form = "blankform.pdf";
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="output.pdf"');
passthru("pdftk $blank_pdf_form fill_form $form_data_xfdf output -);
请注意,可以将实际的 xml 字符串放在命令行中,但我的结果非常不可靠。
编辑
在很多帮助下,我现在明白我真正的问题是“如何通过管道将变量传递给 PHP 中的命令行执行”。显然 proc_open 是最好的方法,或者至少是最直接的方法。由于我花了很长时间才弄清楚这一点,而且我对 Google 的研究表明其他人可能正在苦苦挣扎,所以我将发布专门解决我的问题的代码:
$blank_pdf_form = "blankform.pdf";
$cmd = "pdftk $blank_pdf_form fill_form - output -";
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w")
);
$process = proc_open($cmd, $descriptorspec, $pipes);
if (is_resource($process))
//row2xfdf is made-up function that turns HTML-form data to XFDF
fwrite($pipes[0], raw2xfdf($_POST));
fclose($pipes[0]);
$pdf_content = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$return_value = proc_close($process);
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="output.pdf"');
echo $pdf_content;
【问题讨论】:
我正试图让它在 ubuntu 上运行,但 pdftk 现在作为 snap 提供,并且几乎可以访问我的文件系统上的任何内容,甚至更少从 php 运行。是否有任何解决方法可以使其与 pdftk 一起使用? 【参考方案1】:1) 为什么要输出到标准输出,然后将这些内容放入文件中?为什么不直接将 pdftk 转储到文件中,即
exec("pdftk blankform.pdf fill_form formdata.xfdf output filledform.pdf");
2) 使用proc_open()
。随时发布您对该功能有任何问题。
【讨论】:
我正在写入文件以进行测试。您会注意到,在我最后的“我希望它看起来像什么”位中,我在设置标题后输出到浏览器。最终目标是不需要存储在服务器上的任何文件,除了空白的 pdf 表单。 啊,对不起。如您所知,我没有仔细阅读您的问题。但是,如果你按照我上面写的那样做,你会得到格式正确的 PDF 吗? 其实,没有。我本可以发誓我昨天测试时尝试了与您的示例几乎相同的东西,但是今天,我得到了缺少行结尾的损坏的 pdf。 不,等等。似乎愚蠢的程序 * 咳嗽梦韦弗咳嗽 * 没有更新我对服务器的更改。通过 exec 使用没有流的所有文件位置确实有效。我偷偷怀疑我在损坏的 PDF 输出中遇到的问题是由于 exec 将输出作为数组返回。我会告诉你的。 啊,成功了。看起来这是由于变量类型。当我将其更改为file_put_contents("filledform",implode("\n",$pdf_output));
时,效果很好。当然,当我尝试使用 \r
或 \r\n
时,它会很合适,所以任何想要为他们的代码执行此操作的人都要注意。【参考方案2】:
我不确定您要达到的目标。您可以使用 URL php://stdin 读取标准输入。但这是来自 PHP 命令行的标准输入,而不是来自 pdftk(通过 exec)的标准输入。
但我会给proc_open()
+1
<?php
$cmd = sprintf('pdftk %s fill_form %s output -','blank_form.pdf', raw2xfdf($_POST));
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => null,
);
$process = proc_open($cmd, $descriptorspec, $pipes);
if (is_resource($process))
// $pipes now looks like this:
// 0 => writeable handle connected to child stdin
// 1 => readable handle connected to child stdout
fwrite($pipes[0], stream_get_contents(STDIN)); // file_get_contents('php://stdin')
fclose($pipes[0]);
$pdf_content = stream_get_contents($pipes[1]);
fclose($pipes[1]);
// It is important that you close any pipes before calling
// proc_close in order to avoid a deadlock
$return_value = proc_close($process);
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="output.pdf"');
echo $pdf_content;
?>
【讨论】:
我想我不明白的是,当我告诉它使用标准输入时 pdftk 正在使用什么以及如何在调用 pdftk 之前写入该流。 如果你想修改 pdftk 的标准输入,你需要使用proc_open()
。你不能写信给stdin
,所以它只有在从命令行运行 PHP 时才有用。
没错。您不能使用 exec 或 similair 函数将某些内容传递给标准输入。我只是在编辑我的答案,向您展示proc_open()
的样子。
是的,在玩了更多之后,我可以说我对流包装器是什么以及如何使用它们有错误的想法。我想我可以设置 php://stdin 然后通过 exec 在命令行中使用它作为标准输入。
我认为你的回答有点基于我的误解,所以它比我最终做的更复杂,但如果没有这个,我肯定不会到达那里。我将稍作修改的版本发布到答案的末尾。以上是关于如何将变量作为标准输入从 PHP 传递到命令行的主要内容,如果未能解决你的问题,请参考以下文章
通过回声管道将python变量(字符串)传递给bash命令[重复]