How to read binary from pipe in PHP - php

I'm trying to create a PNG from an SVG using Inkscape. I'm using Linux. the command,
cat in.svg | inkscape -z /dev/stdin -w 800 -h 475 -e /dev/stderr 2> out.png
works fine, but I would rather not write the output files on the server.
My code is,
<?php
$svg_data = file_get_contents('in.svg');
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$pipes = array();
$process = proc_open("inkscape -z /dev/stdin -w 800 -h 475 -e /dev/stderr", $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $svg_data);
fclose($pipes[0]);
$fil_data = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
file_put_contents("out.png", $fil_data);
}
?>
If I change the line,
$process = proc_open("inkscape -z /dev/stdin -w 800 -h 475 -e /dev/stderr", $descriptorspec, $pipes);
to
$process = proc_open("inkscape -z /dev/stdin -w 800 -h 475 -e out.png", $descriptorspec, $pipes);
The correct "out.png" is printed.
The current code writes a file, but the file is corrupted. It seems that it is corrupted from beginning to end, but it is about the right size.
I want the data (out.png) in $fil_data, but I don't want to read it from the disk drive.
Why is out.png corrupted, and how can I make the correct conversion without resorting to writing the disk.

Inkscape was putting some messages to stderr before writing the file. The same thing happened on stdout. The problem was fixed by skipping past the messages. So the line,
$fil_data = stream_get_contents($pipes[2]);
was changed to,
$fil_data = stream_get_contents($pipes[2], -1, 150);
If there is a better way, I would like to see it.

Related

php exec grep -axv don't return anything

I'am stuck since days while requesting grep on PHP, it work in cli but don't return anything via http.
it search for files that contain non UTF-8 carachters
in CLI it retrun ퟿������ but nothing (array is null) from the web
<?php
exec("/sbin/grep -axv '.*' /srv/http/test 2>&1", $datareturn);
print_r($datareturn);
?>
disable_functions = is empty in php.ini
Also tried with proc_open :
<?php
$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 => array("file", "/tmp/error-output.txt", "a") // stderr is a file to write to
);
$process = proc_open(
"/sbin/grep -axv '.*' /srv/http/test",
$descriptorspec,
$pipes
);
if (is_resource($process)) {
// Closing $pipes[0] because we don't need it
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
//avoid a deadlock
$return_value = proc_close($process);
echo "command returned $return_value\n";
}
?>
In CLI it return :
"퟿������
command returned 0
From http, "command returned 1", error-output.txt is empty in 2 cases

Python ImportError while using youtube-dl by php

Traceback (most recent call last): File "/usr/bin/youtube-dl", line
3, in import youtube_dl File
"/usr/lib/python2.7/dist-packages/youtube_dl/init.py", line 65, in
from .utils import ( File
"/usr/lib/python2.7/dist-packages/youtube_dl/utils.py", line 18, in
import ssl File "/usr/lib/python2.7/ssl.py", line 61, in import _ssl
#if we can't import it, let the error propagate ImportError: /usr/lib/python2.7/lib-dynload/_ssl.x86_64-linux-gnu.so: symbol
GENERAL_NAME_free, version OPENSSL_1.0.0 not defined in file
libcrypto.so.1.0.0 with link time reference
The Command i used
youtube-dl --max-quality 2180 --write-thumbnail -x --audio-format mp3
-c -o "/home/bahaa/%(id)s.%(ext)s" https://www.youtube.com/watch?v=xFQFMSNZW08&list=RDV-jLo0Ovems
The php code i am using
<?php
$url = 'https://www.youtube.com/watch?v=xFQFMSNZW08&list=RDV-jLo0Ovems';
//escapeshellarg
$string = ('youtube-dl --max-quality 2180 --write-thumbnail -x --audio-format mp3 -c -o "/home/bahaa/%(id)s.%(ext)s" https://www.youtube.com/watch?v=xFQFMSNZW08&list=RDV-jLo0Ovems');
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
$process = proc_open($string, $descriptorspec, $pipes);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$ret = proc_close($process);
echo json_encode(
array(
'status' => $ret,
'errors' => str_replace("\n", "<br />", $stderr."<hr />"),
'output' => $stdout,
)
);
?>
Hi Finally I got the solution!
Firstly, would you please let me know your server configuration? I'm using XAMPP, in my case, the "Python" and its libs used by Apache are not the same as used by my ubuntu, so I build and install Python3 into /opt/lampp/bin and build openssl by myself. You can download openssl1.0.1 from its website and configure it by "./config shared" to build a .so file.
Next copy the libssl.so* and libcrypto.so* (totally 4 files) into /opt/lampp/lib (this is where LD_LIBRARY_PATH points to) and restart apache now
It works for me, Hope it will help you

Displaying Errors in online c compiler

Hi all I have a c compiler up and running shows the output but the problem is that it dosen't show errors....
shell_exec("gcc xyz.c -o ab.out ");
$output=exec("./ab.out");
echo $output;
So it is showing output but not any errors occurred while compiling.
Any help is duly appreciated.
Thanks in advance.
Any error output from the command you run will go to STDERR and none of the exec, shell_exec functions will provide you that. One way is to redirect it
exec("gcc test.c 2>&1", $out);
The most cleaner way is to use proc_open function.
$descriptorspec = array(
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w") // stderr
);
$process = proc_open('gcc test.c', $descriptorspec, $pipes);
if (is_resource($process)) {
$stderr = stream_get_contents($pipes[2]);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
fclose($pipes[2]);
$return_value = proc_close($process);
}

How can I force GPG to accept input from STDIN instead of trying to open a file?

I am trying to incorporate GPG clear-signing of text in a string in a PHP script. I can cause GPG to encrypt text in a string like this:
$encrypted = shell_exec("echo '$text' | gpg -e -a -r foo#bar.com --trust-model always");
and that works perfectly, with the encrypted text being sent to the $encrypted variable. This proves GNUPGHOME and GNUPG are set up correctly.
However, when I try to produce a clear-signed message in the same way with this:
$text = "googar";
$signature = exec("echo $passphrase | gpg -v --clearsign --no-tty --passphrase-fd 0 '$text' 2>&1 1> /dev/null", $output);
I am returned this error:
... string(51) "gpg: can't open `googar': No such file or directory"
[3]=>
string(46) "gpg: googar: clearsign failed: file open error"
}
This error is returned with or without the single quotes around the $text variable.
How can I force GPG or shell_exec to treat $text as a pipe instead of it looking for a file?
I need to echo the passphrase in this way (I know, its 'horribly insecure' because GPG has no way to pass in a passphrase as a variable on the command line.
You could use proc_open and create a separate file descriptor for your password:
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w"),
3 => array("pipe", "r"),
);
$pipes = false;
$process = proc_open("gpg -v --clearsign --no-tty --passphrase-fd 3", $descriptorspec, $pipes);
if(is_resource($process)) {
fwrite($pipes[3], $passphrase);
fclose($pipes[3]);
fwrite($pipes[0], $text);
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$retval = proc_close($process);
echo "retval = $retval\n";
echo "output= $output\n";
echo "err= $stderr\n";
}
You could use process substitution:
echo $passphrase | gpg -v --clearsign --no-tty --passphrase-fd 0 <(printf '$text') 2>&1 1> /dev/null
^^ ^
This will make gpg thinks it is reading data from a file, but the file will be a temporary named pipe which input will be printf '$text'.

PHP StdErr after Exec()

In PHP I am executing a command with exec(), and it returns if successful an URL;
$url = exec('report');
However, I want to check stderr, if something went wrong. How would I read the stream?
I want to use php://stderr, but I am not sure how to use it.
If you want to execute a command, and get both stderr and stdout, not "merged", a solution would probably to use proc_open, which provides a great level of control over the command that's being executed -- including a way to pipe stdin/stdout/stderr.
And here is an example : let's consider we have this shell-script, in test.sh, which writes to both stderr and stdout :
#!/bin/bash
echo 'this is on stdout';
echo 'this is on stdout too';
echo 'this is on stderr' >&2;
echo 'this is on stderr too' >&2;
Now, let's code some PHP, in temp.php -- first, we initialize the i/o descriptors :
$descriptorspec = array(
0 => array("pipe", "r"), // stdin
1 => array("pipe", "w"), // stdout
2 => array("pipe", "w"), // stderr
);
And, then, execute the test.sh command, using those descriptors, in the current directory, and saying the i/o should be from/to $pipes :
$process = proc_open('./test.sh', $descriptorspec, $pipes, dirname(__FILE__), null);
We can now read from the two output pipes :
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
And, if we output the content of those two variables :
echo "stdout : \n";
var_dump($stdout);
echo "stderr :\n";
var_dump($stderr);
We get the following output when executing the temp.php script :
$ php ./temp.php
stdout :
string(40) "this is on stdout
this is on stdout too
"
stderr :
string(40) "this is on stderr
this is on stderr too
"
A little function that might be helpful:
function my_shell_exec($cmd, &$stdout=null, &$stderr=null) {
$proc = proc_open($cmd,[
1 => ['pipe','w'],
2 => ['pipe','w'],
],$pipes);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
return proc_close($proc);
}
The exit code is returned and STDOUT and STDERR are reference params if you need them.
The short way to do such a things with exec is to return the exit code ( status of the command )
Note that I am trying to list a non-exists directory /non-dir/
exec('ls /non-dir/', $out, $retval);
var_dump($retval);
Output
ls: cannot access '/non-dir/': No such file or directory
int(2)
Normally in unix-based system most of successful statuses codes is ( 0 ) so you can check your $retval to know the status of the command.
to dismiss the error from listing an invalid path ls: cannot access '/non-dir/': No such file or directory you can redirect your stderr to null
exec('ls /non-dir/ 2>/dev/null', $out, $retval);
var_dump($retval);
this will output :
int(2)
also if you need the error string to use it in any scenario you may redirect your stderr to the stdout.
exec('ls /non-dir/ 2>&1', $out, $retval);
print_r($out);
var_dump($retval);
this will output the following:
Array
(
[0] => ls: cannot access '/non-dir/': No such file or directory
)
int(2)
Another way to get unmerged stdout/stderr.
$pp_name = "/tmp/pp_test";
#unlink($pp_name);
posix_mkfifo($pp_name, 0777);
$pp = fopen($pp_name, "r+");
stream_set_blocking($pp, FALSE);
exec("wget -O - http://www.youtube.com 2>$pp_name", $r_stdout);
$r_stderr = stream_get_contents($pp);
var_dump($r_stderr);
fclose($pp);
unlink($pp_name);
If you want to ignore stdout and get only stderr, you can try this:
exec("wget -O - http://www.youtube.com 2>&1 >/dev/null", $r_stderr);
exec("{$command} 2>&1"
,$output
,$exitCode
);
2>&1 redirects stderr to stdout for consistent success / fail behaviour.
$exitCode determines $command completion status.
$output contains all output associated with $exitCode.
Slightly ugly but good enough. Put the stderr into a temp file and read it back.
$tmp = tempnam("/tmp", "ERR_");
exec('report 2> ' . escapeshellarg($tmp), $stdout, $retcode);
$stderr = file_get_contents($tmp);
unlink($tmp);
if ($retcode == 0)
{
// good
$url = $stdout[0];
} else {
// bad
error_log("FAIL: $stderr");
}

Categories