Interact with running python script with PHP - php

I have python script which may can ask for input while running (I must run it from PHP and can't modify python script). My question is, can I interact with that script with PHP. For example, run "python script.py" and then if last message is "String:", than insert input.
>> python script.py
>> Log 1 ...
>> Log 2 ...
>> Enter value A:
>> 2
>> Enter value B:
>> 4
i want to do this.
If(cmdlogtext == 'Enter value A:')
* Enter number 2

To interact with running script you need to use proc_open.
proc_open Official docs
Below example is a simple usage.
$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", "error-output.txt", "a") // stderr is a file to write to
);
$process = proc_open('python script.py', $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
// Any error output will be appended to error-output.txt
fwrite($pipes[0], 'Input 1' . PHP_EOL);
fwrite($pipes[0], 'Input 2' . PHP_EOL);
echo stream_get_contents($pipes[1]);
fclose($pipes[0]);
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);
}
Keep in mind:
To send inputs to running script, you must use fwrite on $pipes[0].
All parameters needs to be ended by PHP_EOL

You can use the exec function and then inspect the output.
exec("python script.py", $output, $returnValue);
Edit: Re-read your question and I'm not sure about interactively providing input but you may be able to modify the exec() function to pass arguments to the Python script assuming the script can accept them.

Related

content instead of path in shell

Pngquant has the following example for php
// '-' makes it use stdout, required to save to $compressed_png_content variable
// '<' makes it read from the given file path
// escapeshellarg() makes this safe to use with any path
$compressed_png_content = shell_exec("pngquant --quality=$min_quality-$max_quality - < ".escapeshellarg( $path_to_png_file));
I want to replace $path_of_file with the actual content.
This will avoid wasting I/O when converting a file from one format to png and then optimize it
What will be the new shell_exec() command in that situation
I am no PHP expert, but I believe you are looking for a 2-way pipe (write and read) to another process, so that you can write data to its stdin and read data from its stdout. So, I think that means you need proc_open() which is described here.
It will look something like this (untested):
$cmd = 'pngquant --quality ... -';
$spec = array(array("pipe", "r"), array("pipe", "w"), array("pipe", "w"));
$process = proc_open($cmd, $spec, $pipes);
if (is_resource($process))
{
// write your data to $pipes[0] so that "pngquant" gets it
fclose($pipes[0]);
$result=stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
}

Running Stockfish chess engine under php script

I'm thinking about making a php script which opens stockfish chess engine CLI, send fews commands and get back the output.
I think I can achieve this by using proc_open with pipes array but I can't figure out how to wait the whole output... If there's another better solution it's appreciated!
Here's my code:
// ok, define the pipes
$descr = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$pipes = array();
// open the process with those pipes
$process = proc_open("./stockfish", $descr, $pipes);
// check if it's running
if (is_resource($process)) {
// send first universal chess interface command
fwrite($pipes[0], "uci");
// send analysis (5 seconds) command
fwrite($pipes[0], "go movetime 5000");
// close read pipe or STDOUTPUT can't be read
fclose($pipes[0]);
// read and print all output comes from the pipe
while (!feof($pipes[1])) {
echo fgets($pipes[1]);
}
// close the last opened pipe
fclose($pipes[1]);
// at the end, close the process
proc_close($process);
}
The process seems to start, but the second STDINPUT I send to process isn't able to wait until it finishes because the second command produces analysis lines for about 5 seconds and the result it prints it's immediate.
How can I get on it?
CLI link, CLI documentation link
Please, ask me for more information about this engine if you need.
Thank you!
fwrite($pipes[0], "uci/n");
fwrite($pipes[0], "go movetime 5000/n");
Without /n Stockfish see this as one command (ucigo movetime 5000) and don't recognise it.
Actually, your code works. $pipes[1] contained all the output from stockfish...
You might need to change the line
position startpos moves 5000
to a different number, as the 5000 means 5000 ms = 5 seconds, ie. the time when the engine stops. Try 10000 and the engine stops after 10 seconds etc.
You need to remove: fclose($pipes[0]); and make check for bestmove in while cycle, if bestmove is found - break the cycle, and after that only put fclose($pipes[0]);. That worked for me. And add \n separator at the end of commands.
Thanks for the code!

Multiple writes to a process opened with proc_open

So I open a process with $process = proc_open("my_process", $descriptors, $pipes);
Then I write to the stdin of the process using fwrite($pipes[0], "some_command");
Then I have to close the pipe using fclose($pipes[0]); before i can read from the pipes stdout using $output = stream_get_contents($pipes[1]);. If I don't close the pipe my php script hangs on this call.
But once I have received the output from stdout what if I want to send another command to the process...the stdin pipe is closed so I have no way to send it. So is it possible to somehow send another command to the process?
It sounds like the other process is blocking waiting for EOL or EOF on STDIN. What are you trying to execute?
Regardless, there's a pretty good chance this will sort it out: Just append \n to the command you are sending to the other process.
E.g.
$process = proc_open("my_process", $descriptors, $pipes);
$command = "some_command";
fwrite($pipes[0], $command."\n");
// Fetch the contents of STDOUT
Now, one issue that you may also be running into is to do with the fact that you are using stream_get_get_contents() - which will wait for EOF before it returns. You may have to be a bit more intelligent about how your retrieve the data from $pipes[1], using fgets() and looking for a specific number of lines or a string to indicate the end of the output.
If you tell us what you are executing, I may be able to give you a more specific answer.

Running at from PHP gives no output

I've been wrestling with exec(), trying to capture the output from it when I add a task using the at Unix system command. My problem is that it is giving no output when run from my script, however running it from the terminal and PHP in interactive mode prints out a couple of lines.
The command I want to execute is this:
echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
var_dump() gives string(0) "", and print_r() spits out Array (). I've tried using shell_exec(), which outputs NULL, however the following outputs hi when run in a web page context:
echo exec("echo 'hi'");
This also outputs stuff:
echo exec("atq");
However, as soon as I use at, nothing is output. How can I get the output of:
exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
Because at present it outputs nothing when run as "normal" by PHP through Apache, however running the command in the terminal as well as in PHP's interactive console gives me the expected result of something like:
php > echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", $result);
warning: commands will be executed using /bin/sh
job 1219 at Sun Jun 10 12:43:00 2012
safe_mode is off, and I cannot work out why I don't get any output from at with a piped-in echo statement, when executing atq or any other commend with exec() gives me output. I've searched and read this question, all to no avail.
How can I get exec() to return the output from at to either a string, or an array if using a second argument with exec()?
Working, one line solution
I didn't realise it could be this simple. All that is required is to reroute stderr to stdout by putting 2>&1 at the end of the command to execute. Now any output from at is printed to stdout, therefore captured by exec():
echo exec("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes 2>&1", $result);
My old solution:
I was trying to keep to a one/two line solution, however the only thing that worked in the end was using proc_open() because at logs to stderr, which exec() doesn't read! I'd like to thank #Tourniquet for pointing this out, however he has deleted his answer. To quote:
As far as i can see, at outputs to stderr, which isn't captured by
exec. I'm not really confident in it, but consider using
http://php.net/manual/en/function.proc-open.php, which allows you to
direct stderr to its own pipe.
This is actually the correct way of doing things. My solution (because I only want stderr) was to do this:
// Open process to run `at` command
$process = proc_open("echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes", array(2 => array("pipe", "w")), $pipes);
// Get stuff from stderr, because `at` prints out there for some odd reason
if(is_resource($process)) {
$output = stream_get_contents($pipes[2], 100);
fclose($pipes[2]);
$return_value = proc_close($process);
}
$output now contains whatever at printed to stderr (which should really go to stdout because it's not an error), and $return_value contains 0 on success.
Here a more complex solution with proc_open. I'm writing this answer because, in my case, the '2>&1' workaround doesn't work.
function runCommand($bin, $command = '', $force = true)
{
$stream = null;
$bin .= $force ? ' 2>&1' : '';
$descriptorSpec = array
(
0 => array('pipe', 'r'),
1 => array('pipe', 'w')
);
$process = proc_open($bin, $descriptorSpec, $pipes);
if (is_resource($process))
{
fwrite($pipes[0], $command);
fclose($pipes[0]);
$stream = stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
}
return $stream;
}
Usage examples:
// getting the mime type of a supplied file
echo runCommand('file -bi ' . escapeshellarg($file));
Another example using the command parameter:
// executing php code on the fly
echo runCommand('/usr/bin/php', '<?php echo "hello world!"; ?>');
Another example using the force parameter (this can be useful for commands that will change the output during the execution process):
// converting an mp3 to a wav file
echo runCommand('lame --decode ' . escapeshellarg($source) . ' ' . escapeshellarg($dest), '', true);
I hope this helps :-)
Try to create a minimum (non-)working example. Break everything down, and test only one thing at a time.
Here is one error in your bash:
hpek#melda:~/temp$ echo 'php -f /path/to/file.php foo=1' | at now + 1 minutes
at: pluralization is wrong
job 22 at Sun Jun 10 14:48:00 2012
hpek#melda:~/temp$
Write minute instead og minutes.
My output from at is send to me by mail!!

How to pass the content of a variable trough an external command in php?

I have a variable that contains a long string. (specifically it contains a few kilobytes of javascript-code)
I want to pass this string trough an external command, in this case a javascript-compressor, and capture the output of the external command (the compressed javascript) in php, assigning it to a variable.
I'm aware that there's classes for compressing javascript in php, but this is merely one example of a general problem.
originally we used:
$newvar = passthru("echo $oldvar | compressor");
This works for small strings, but is insecure. (if oldvar contains characters with special meaning to the shell, then anything could happen)
Escaping with escapeshellarg fixes that, but the solution breaks for longer strings, because of OS-limitations on maximum allowable argument-length.
I tried using popen("command" "w") and writing to the command - this works, but the output from the command silently disappears into the void.
Conceptually, I just want to do the equivalent of:
$newvar = external_command($oldvar);
Using the proc_open-function you can get handles to both stdout and stdin of the process and thus write your data to it and read the result.
Using rumpels suggestion, I was able to device the following solution which seems to work well. Posting it here for the benefit of anyone else interested in the question.
public static function extFilter($command, $content){
$fds = 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("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($command, $fds, $pipes, NULL, NULL);
if (is_resource($process)) {
fwrite($pipes[0], $content);
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$return_value = proc_close($process);
// Do whatever you want to do with $stderr and the commands exit-code.
} else {
// Do whatever you want to do if the command fails to start
}
return $stdout;
}
There may be deadlock-issues: if the data you send is larger than the combined sizes of the pipes, then the external command will block, waiting for someone to read from it's stdout, while php is blocked, waiting for stdin to be read from to make room for more input.
Possibly PHP takes care of this issue somehow, but it's worth testing out if you plan to send (or receive) more data than fits in the pipes.

Categories