Question: Is it possible to use php://memory on a exec or passthru command?
I can use php variables in the exec or passthru with no problem, but I am having trouble with php://memory
background:
I am trying to eliminate all of my temporary pdf file writing with PDFTK.
1)I am writing an temporary fdf file
2) form-fill a temporary pdf file using #1
3) repeat #1 and #2 for all the pdfs
4) merge all pdf's together.
This currently works - but it creates a lot of files, and is the bottleneck.
I would like to speed things up with pdftk by making use of the virtual file php://memory
First, I am trying to just virtualize the fdf file used in #1.
Answering this alone is enough for a 'correct answer'. :)
The code is as follows:
$fdf = 'fdf file contents here';
$tempFdfVirtual= fopen("php://memory", 'r+');
if( $tempFdfVirtual ) {
fwrite( $tempFdfVirtual, $fdf);
} else {
echo "Failure to open temporary fdf file";
exit;
}
rewind( $tempFdfVirtual);
$url = "unfilled.pdf";
$temppdf_fn = "output.pdf";
$command = "pdftk $url fill_form $tempFdfVirtual output $temppdf_fn flatten";
$error="";
exec( $command, $error );
if ($error!="") {
$_SESSION['err'] = $error;
} else {
$_SESSION['err'] = 0;
}
I am getting an errorcode #1. If I do a stream_get_contents($tempFdfVirtual), it shows the contents.
Thanks for looking!
php://memory
and php://temp
(and in fact any file descriptor) are only available to the currently-running php process. Besides, $tempFdfVirtual
is a resource handle so it makes no sense to put it in a string.
You should pass the data from your resource handle to the process through its standard-in. You can do this with proc-open
, which gives you more control over input and output to the child process than exec
.
Note that for some reason, you can't pass a 'php://memory' file descriptor to a process. PHP will complain:
Warning: proc_open(): cannot represent a stream of type MEMORY as a File Descriptor
Use php://temp
instead, which is supposed to be exactly the same except it will use a temporary file once the stream gets big enough.
This is a tested example that illustrates the general pattern of code that uses proc_open()
. This should be wrapped up in a function or other abstraction:
$testinput = "THIS IS A TEST STRING\n";
$fp = fopen('php://temp', 'r+');
fwrite($fp, $testinput);
rewind($fp);
$cmd = 'cat';
$dspec = array(
0 => $fp,
1 => array('pipe', 'w'),
);
$pp = proc_open($cmd, $dspec, $pipes);
// busywait until process is finished running.
do {
usleep(10000);
$stat = proc_get_status($pp);
} while($stat and $stat['running']);
if ($stat['exitcode']===0) {
// index in $pipes will match index in $dspec
// note only descriptors created by proc_open will be in $pipes
// i.e. $dspec indexes with an array value.
$output = stream_get_contents($pipes[1]);
if ($output == $testinput) {
echo "TEST PASSED!!";
} else {
echo "TEST FAILED!! Output does not match input.";
}
} else {
echo "TEST FAILED!! Process has non-zero exit status.";
}
// cleanup
// close pipes first, THEN close process handle.
foreach ($pipes as $pipe) {
fclose($pipe);
}
// Only file descriptors created by proc_open() will be in $pipes.
// We still need to close file descriptors we created ourselves and
// passed to it.
// We can do this before or after proc_close().
fclose($fp);
proc_close($pp);
Untested Example specific to your use of PDFTK:
// Command takes input from STDIN
$command = "pdftk unfilled.pdf fill_form - output tempfile.pdf flatten";
$descriptorspec = array(
0 => $tempFdfVirtual, // feed stdin of process from this file descriptor
// 1 => array('pipe', 'w'), // Note you can also grab stdout from a pipe, no need for temp file
);
$prochandle = proc_open($command, $descriptorspec, $pipes);
// busy-wait until it finishes running
do {
usleep(10000);
$stat = proc_get_status($prochandle);
} while ($stat and $stat['running']);
if ($stat['exitcode']===0) {
// ran successfully
// output is in that filename
// or in the file handle in $pipes if you told the command to write to stdout.
}
// cleanup
foreach ($pipes as $pipe) {
fclose($pipe);
}
proc_close($prochandle);