How to use exec function in PHP to run commands in backend? - php

I am using below curl command. exec() function is taking around 60 ms to execute which caused high response time.
curl -X POST -H 'Content-Type: application/json' --connect-timeout 1 -m 1 -d '<payload>' '<url>' > /dev/null 2>&1 &

You can use proc_open function instead of exec function. Also you can check what cause a problem in term of performance by printing in log file.
$descriptorspec = array(
array('pipe', 'r'), // stdin
array('file', 'response.log', 'a'), // stdout
array('pipe', 'w'), // stderr
);
$env = array(
'PATH' => '/usr/local/bin' //Path to node bin dir
);
$proc = proc_open('wget -qO- --post-data "{YOUR POST DATA}" "{URL}" &', $descriptorspec, $pipes, __DIR__, $env);
if (is_resource($proc))
{
proc_close($proc);
}
**Example:**
$proc = proc_open('wget -qO- --post-data "id=' . $_POST["id"] . '" "http://www.example.com" &', $descriptorspec, $pipes, __DIR__, $env);

Related

How to read binary from pipe in 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.

Mysql not connecting when callng from proc_open in php

There is a Linux shell script which includes mysql connection and queries.
filename is backup.sh.
#!/bin/bash
set -x
user="root"
pass="dbpass"
output=`/usr/local/mariadb/bin/mysql -u$user -p$pass -h127.0.0.1 -e 'select now()' `
echo "data output=$output"
It will work if i run thorugh below linux command line.
sh backup.sh
But if i call from php code it will not work. It throughs permission denied error.
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "r")
);
$process = proc_open("sh /tmp/backup.sh", $descriptorspec, $pipes, null, null); if (is_resource($process))
{
//echo ("<br>Start process:<br>");
stream_set_blocking($pipes[2], 0);
while (!feof($pipes[1])) {
$out = fgets($pipes[1], 1024);
echo ''.$out.'';
}
}
Anybody help me pls.

php proc_open file descriptor in bash command

When we use proc_open in PHP:
<?php
$descriptors = array(
array('pipe', 'r'),
array('pipe', 'w'),
array('pipe', 'w'),
);
$cmd = 'cat <(ls)';
//$cmd = 'ls';
echo $cmd . PHP_EOL;
$ph = proc_open($cmd, $descriptors, $pipes);
echo stream_get_contents($pipes[1]);
echo stream_get_contents($pipes[2]);
proc_close($ph);
When run this script, an error occurred:
$ php test.php
cat <(ls)
sh: 1: Syntax error: "(" unexpected
but run the raw cmd:
$ cat <(ls)
clusterRegions.php
composer.json
composer.lock
cvCells.php
kent
prepareForTF.php
README.txt
scoreRegions.php
test.php
tests
vendor
Seems that the <() thing cannot be recognized by proc_open.
So how to pass a bash command with a file descriptor to proc_open ?
#Etan Reisner, you are right, when I change to bash, it runs well.
$cmd = 'bash -c "cat <(ls)"';

Process do not gets a pipe from proc_open. lessc - Dynamic Stylesheet language

Here is a source to lessc software, I think it will be helpfull:
https://github.com/cloudhead/less.js/blob/master/bin/lessc
THE PROBLEM
I use lessc in shell simply:
lessc file.less
and I get a css file output.
I tried to do it by php with proc_open. But when I pipe input file to proc, lessc do not gets it. I have a error (from pipe 1):
"lessc: no input files"
which is equivalent in shell to (parameter is not passed):
lessc
lessc ''
My code:
$descriptorspec = array(
0 => array("file", 'path/to/file/foo.less', "r"),
1 => array("pipe", "w"),
2 => array("file", '/tmp/lessCompiler-errors', "a")
);
$process = proc_open('lessc', $descriptorspec, $pipes);
if (is_resource($process)) {
$contents = stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
}
btw. I'm trying avoid using the exec() function.
I will be graceful for any help.
Marcin
I ran into the same problem today, not sure if the solution was valid when you posted the question, but now you can pass '-' as input source to lessc in order to make it read from stdin. So you only have to change one line:
$process = proc_open('lessc -', $descriptorspec, $pipes);
As it took me some time to figure out all the details here is my code snippet:
/* run lessc */
$descriptors = array(
0 => array( 'pipe', 'r' ),
1 => array( 'pipe', 'w' ),
2 => array( 'pipe', 'w' ));
$process = proc_open( 'lessc --no-color -x -', $descriptors, $pipes,
'/web/less', array( 'PATH' => '/usr/local/bin/' ) );
if( !is_resource( $process ) ) {
$this->errorMessage( 'Unable to start lessc' );
exit();
}
/* write generated content */
fwrite( $pipes[0], 'some dynamically generated less code' );
fclose( $pipes[0] );
/* read compiled css */
$css = stream_get_contents( $pipes[1] );
fclose( $pipes[1] );
/* check for errors */
if( $stderr = stream_get_contents( $pipes[2] ) ) {
$this->errorMessage( "lessc error: {$stderr}" );
exit();
}
fclose( $pipes[2] );
proc_close( $process );
/* write back */
$this->write( $css );
I had to set PATH on debian wheezy, because lessc kept complaining: /usr/bin/env: node: No such file or directory, see https://github.com/joyent/node/issues/3911
--no-color supresses escape codes in the error messages
-x compresses the css

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