如何将变量作为标准输入从 PHP 传递到命令行

2021-12-27 00:00:00 stdin command-line stream php pdftk

我正在尝试编写一个 PHP 脚本,该脚本使用 pdftk 应用程序将 XFDF 与PDF 格式并将合并后的 PDF 输出给用户.根据 pdftk 文档,我可以通过 stdin 传递表单数据,并将 PDF 输出到 stdout 流.从命令行使用 pdftk 的正常文件而非流方式是:

I am trying to write a PHP script that uses the pdftk app to merge an XFDF with a PDF form and output the merged PDF to the user. According to the pdftk documentation, I can pass the form data in via stdin and have the PDF output to the stdout stream. The normal, file-not-stream way to use pdftk from the command line is:

pdftk blankform.pdf fill_form formdata.xfdf output filledform.pdf

要在命令行上使用流,您需要输入:

to use streams on the command line, you'd enter:

pdftk blankform.pdf fill_form - output -

我有几个问题:

1) 我已经让 pdftk 使用 xfdf 文件(而不是 stdin)通过 stdout 返回输出,如下所示:

1) I have gotten pdftk to return output via stdout using an xfdf file (instead of stdin) like so:

    exec("pdftk blankform.pdf fill_form formdata.xfdf output -", $pdf_output);
    file_put_contents("filledform.pdf",$pdf_output);

但根据 Adob​​e Reader 的说法,它创建的 pdf 已损坏,并且使用文本编辑器快速浏览该文件表明,至少,它没有将行尾设置在应有的位置.我有一个由 pdftk 创建的完全相同的 PDF,它输出到一个文件,并且 pdf 在文本编辑器中看起来不错,所以我知道它不是 pdftk 输出错误数据.

But the pdf that it creates is corrupt, according to Adobe Reader and a quick peek at the file with a text editor shows that, at the very least, it is not setting the line endings where they should be. I have an identical PDF created by pdftk where it output to a file, and the pdf looks fine in the text editor, so I know that it's not pdftk that's outputting bad data.

2) 我一生都无法弄清楚如何在 PHP 中设置 stdin 流,以便我可以使用该流作为 pdftk 的输入.从我在 PHP 文档中读到的内容来看,stdin 是只读的,那么任何东西如何进入该流?

2) I can not for the life of me figure out how to set the stdin stream in PHP so that I can use that stream as my input for pdftk. From what I'm reading on the PHP documentation, stdin is read-only, so how does anything ever get into that stream?

理想情况下,我想保持这个非常简单并避免使用 proc_open().我尝试使用该函数但不是很成功,这可能是我的错,而不是函数的错,但实际上我的目标很简单,我宁愿避免使用我不需要的强大函数.

Ideally, I would like to keep this really simple and avoid using proc_open(). I attempted to use that function and wasn't very sucessful, which is probably my fault, not the function's, but really my goals are simple enough I'd rather avoid using robust functions I don't need.

理想情况下,我的代码如下所示:

Ideally my code would look something like:

 $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 字符串放在命令行中,但是我得到了非常不可靠的结果.

Just a heads up, it is possible to put the actual xml string in the command line, but I've had very unreliable results with this.

在很多帮助下,我现在明白我真正的问题是如何通过管道将变量传递到 PHP 中的命令行执行".显然 proc_open 是最好的方法,或者至少是最直接的方法.由于我花了很长时间才弄明白这一点,而且我对谷歌的研究表明其他人可能正在苦苦挣扎,我将发布专门解决我的问题的代码:

With much help, I now understand that my real question was "how can pipe a variable to a command line execution in PHP". Apparently proc_open is the best way to go, or at least the most straightforward. Since it took me forever to figure this out and since my research on Google suggests others may be struggling, I'll post the code that specifically worked for my problem:

$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;
}

推荐答案

我不确定您要实现的目标.您可以使用 URL php://stdin 读取标准输入.但这是来自 PHP 命令行的标准输入,而不是来自 pdftk(通过 exec)的标准输入.

I'm not sure about what you're trying to achieve. You can read stdin with the URL php://stdin. But that's the stdin from the PHP command line, not the one from pdftk (through exec).

但我会给 proc_open()

<?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;
}
?>

相关文章