Cómo pasar variables como stdin a la línea de comandos desde PHP


Estoy intentando escribir un script PHP que use la aplicación pdftk para fusionar un XFDF con un formulario PDF y generar el PDF fusionado al usuario. De acuerdo con la documentación de pdftk, puedo pasar los datos del formulario a través de stdin y tener la salida PDF a la secuencia stdout. La forma normal de usar pdftk desde la línea de comandos es:

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

Para usar secuencias en la línea de comandos, debe ingresar:

pdftk blankform.pdf fill_form - output -

Tengo un par de problemas:

1) He conseguido pdftk para devolver la salida a través de stdout usando un archivo xfdf (en lugar de stdin) de la siguiente manera:

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

Pero el pdf que crea está dañado, según Adobe Reader y un vistazo rápido al archivo con un editor de texto muestra que, al menos, no está configurando los finales de línea donde deberían estar. Tengo un PDF idéntico creado por pdftk donde la salida a un archivo, y el pdf se ve bien en el editor de texto, así que sé que no es pdftk que está produciendo datos malos.

2) No puedo para el life of me averigua cómo configurar el flujo stdin en PHP para que pueda usar ese flujo como entrada para pdftk. Por lo que estoy leyendo en la documentación de PHP, stdin es de solo lectura, así que ¿cómo llega algo a esa secuencia?

Idealmente, me gustaría mantener esto realmente simple y evitar usar proc_open(). Intenté usar esa función y no fue muy exitoso, lo que probablemente es mi culpa, no la función, pero en realidad mis objetivos son lo suficientemente simples Prefiero evitar el uso de funciones robustas No necesito.

Idealmente mi código se vería algo así como:

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

Solo un heads up, es posible poner la cadena xml real en la línea de comandos, pero he tenido muy resultados poco fiables con esto.

Editar

Con mucha ayuda, ahora entiendo que mi verdadera pregunta era "cómo canalizar una variable a una ejecución de línea de comandos en PHP". Aparentemente proc_open es el mejor camino a seguir, o al menos el más sencillo. Desde que me llevó una eternidad resolver esto y ya que mi investigación en Google sugiere que otros pueden estar luchando, voy a publicar el código que funcionó específicamente para mi problema:

$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;
}
Author: Anthony, 2010-03-06

2 answers

No estoy seguro de lo que estás tratando de lograr. Puedes leer stdin con la URL php: / / stdin. Pero esa es la stdin de la línea de comandos PHP, no la de pdftk (a través de exec).

Pero voy a dar un + 1 para 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;
}
?>
 22
Author: Savageman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-06 00:35:31

1) ¿Por qué estás enviando a standard out y luego poniendo esas cosas en un archivo? ¿Por qué no simplemente tener pdftk volcado en el archivo, es decir,

exec("pdftk blankform.pdf fill_form formdata.xfdf output filledform.pdf");

2) Utilizar proc_open(). No dude en publicar cualquier problema que tenga con la función.

 0
Author: Michael Mior,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-05 23:49:45