Execute User Input at breaks from PHP to Shell - php

I have a php script that is called and it executes a shell command through shell_exec(). This shell command requires multiple user inputs at different stages. I am stuck with getting the interactivity piece working at each input stage.
This is just an example of how I imagine it working...
<?php
$return = shell_exec('runcmd');
//Let runcmd run until first break
echo $return;
$userinput1 = 'foo';
//Let runcmd run until next break
echo $return;
$userinput2 = 'bar';
//Let runcmd run until nth break
echo $return;
$userinputNth = 'nth';

To feed input to a shell command, use popen.
$handle = popen('runcmd', 'w');
fputs($handle, 'foo
bar
nth
');
pclose($handle);
Since output is not captured, we needn't echo it.

Related

dynamically get results from exec

I have a php script that calls a go script. It gets results every 1-2 seconds, and print's them. Using php's exec and output, I only get the results when the program finishes. Is there a way I can check the output to see when it changes and output that while it's still running?
Something like this, but pausing the execution?:
$return_status = 0;
$output = [];
$old_output = ["SOMETHING ELSE"];
while ($return_status == 0) {
exec($my_program,$output,$return_status); #somehow pause this?
if $output != $old_output {
echo($output);
$old_output = $output;
}
}
Yes. Use the popen() function to get a file handle for the command's output, then read from it a line at a time.

bash echoing value to process, php loop (the process) not reading stdin

Background:
I'm in a position where I'm placing data into the command line and I need a php loop (what will become a server of sorts) to read STDIN and just echo what it reads to the shell its running in.
The following terrible code works when the process is running in the same shell as the content echoed:
<?php
echo getmypid();
$string = "/proc/" . getmypid() . "/fd/0";
while (true) {
fwrite(STDOUT, fgets(fopen($string, 'r'), 4096) . " worked\n");
}
?>
I've tried many variants:
<?php
echo getmypid();
$string = "/proc/" . getmypid() . "/fd/0";
while (true) {
$fo = fread(STDIN, 1024);
fwrite(STDOUT, $fo);
}
?>
The problem is that whenever I write to this loop from a separate terminal, the output appears in the other terminal but is not processed by the loop.
When I enter text in the same terminal, the text is echoed right back.
I need a way to get command line data into this loop from any source.

How can I pass a text file created by php to my python script for processing?

I have a php code that is writing the user input on the webpage into a text file. I wish to pass the text file into my python script that looks like follows:
PHP Script (getfile.php)
<?php
function writetofile($file, $content, $writeType){
$fo = fopen($file, $writeType);
if($fo){
fwrite($fo,$content);
fclose($fo);
}
}
?>
Python Script (predict.py)
clf=joblib.load('model.pkl')
def run(command):
output = subprocess.check_output(command, shell=True)
return output
row = run('cat '+'/Users/minks/Documents/X-test.txt'+" | wc -l").split()[0]
print("Test row size:")
print(row)
matrix_tmp_test = np.zeros((int(row),col), dtype=np.int64)
print("Test matrix size:")
print(matrix_tmp_test.size)
What I am asking is, after writing to a file : $file in php, how can I then pass this file to replace:
row = run('cat '+'/Users/minks/Documents/X-test.txt'+" | wc -l").split()[0]
where the path gets replace by $file and the processing continues? Also, is it possible to pass $file directly to the python code via command line? I am little confused on how this entire passing and processing can be carried out.
Dow you want something like this?
PHP:
$path = "my.txt";
system("python predict.py $path");
Python:
row = run("cat %s | wc -l" % sys.argv[1]).split()[0]

How can I use PHP call python script return result in realtime?

I used PHP to call python script successfully and got the result . But I have to wait for the end of script running without anything output. It looks not friendly to my customer.
How can I return the script results to the PHP web in realtime ?
For instance ,for code below , I want to the PHP web will show output message in realtime instead of show them together at the end . How can I change my code?
Thank you .
PHP Code:
<?php
$k = $_REQUEST['k'];
if (!empty($k))
{
$k = trim($k);
$a = array();
exec('python ./some.py '.$k, $a);
echo $a[0];
}
?>
Python Code:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import sys
def do_some(a):
print 'test1'
time.sleep(30)
print 'test2'
if __name__ == '__main__':
print 'Now the python scritp running'
time.sleep(20)
a = sys.argv[1]
if a:
print 'Now print something'
T = do_some(a)
By specification, exec stop the calling program until the end of the callee. After that, you get back the output in a variable.
If you want to send data as soon as they are produced, you should use popen. It will fork a new process, but will not block the caller. So you can perform other tasks, like looping to read the sub-process output line by line to send it to your client. Something like that:
$handle = popen("python ./some.py ", 'r');
while(!feof($handle)) {
$buffer = fgets($handle);
echo "$buffer<br/>\n";
ob_flush();
}
pclose($handle)

Running a script from another script with session data

I am trying to have a script start another script and put its data into a session variable for the other script to use. The problem is that when the second script, data.php, runs it doesn't seem to be able to access the session variables. They are blank and nothing gets written to data.txt. If I run data.php by itself it writes the last value that $_SESSION["data"] was set to properly, but not when it's run with exec. I am not sure what the problem is. Any ideas?
input.php:
session_start();
$_SESSION["data"] = "Data!";
exec("/usr/bin/php /path/to/data.php > /dev/null 2>&1 &");
data.php:
session_start();
$fp = fopen('data.txt', 'w');
fwrite($fp, $_SESSION["data"]);
fclose($fp);
Edit: I am trying to start data.php from inside input.php and have the variables from input.php accessible in data.php.
You can pass data to PHP scripts running with the CLI as command line arguments. This data will be available to the child script in the $argv array.
input.php:
$arg = "Data!";
exec("/usr/bin/php /path/to/data.php ".escapeshellarg($arg)." > /dev/null 2>&1 &");
data.php
$fp = fopen('data.txt', 'w');
fwrite($fp, $argv[1]);
fclose($fp);
A couple of notes:
It is important to pass each argument through escapeshellarg() to ensure that users are not able inject commands into your shell. This will also stop special shell characters in arguments from breaking your scripts.
$argv is a global variable, not a superglobal like $_GET and $_POST. It is only available in the global scope. If you need to access it in a function scope, you can use $GLOBALS['argv']. This is about the only situation in which I consider the use of $GLOBALS acceptable, although it is still better to handle the arguments in the global scope on startup, and pass them through the scopes as arguments.
$argv is a 0-indexed array, but the first "argument" is in $argv[1]. $argv[0] always contains the path to the currently executing script, because $argv actually represents the arguments passed to the PHP binary, of which the path to your script is the first.
Values from command line arguments always have a string type. PHP is very promiscuous with its typing so with scalar values this doesn't matter, but you (fairly obviously) can't pass vector types (objects, arrays, resources) through the command line. It is possible to pass objects and arrays by encoding them with e.g. serialize() or json_encode(). There is no way to pass resources through the command line.
EDIT When passing vector types I prefer to use serialize() because it carries with it information about the classes that objects belong to.
Here is an example:
input.php:
$arg = array(
'I\'m',
'a',
'vector',
'type'
);
exec("/usr/bin/php /path/to/data.php ".escapeshellarg(serialize($arg))." > /dev/null 2>&1 &");
data.php
$arg = unserialize($argv[1]);
$fp = fopen('data.txt', 'w');
foreach ($arg as $val) {
fwrite($fp, "$val\n");
}
fclose($fp);
Here is a couple of functions from my clip collection I use to simplify this process:
// In the parent script call this to start the child
// This function returns the PID of the forked process as an integer
function exec_php_async ($scriptPath, $args = array()) {
$cmd = "php ".escapeshellarg($scriptPath);
foreach ($args as $arg) {
$cmd .= ' '.escapeshellarg(serialize($arg));
}
$cmd .= ' > /dev/null 2>&1 & echo $$';
return (int) trim(exec($cmd));
}
// At the top of the child script call this function to parse the arguments
// Returns an array of parsed arguments converted to their correct types
function parse_serialized_argv ($argv) {
$temp = array($argv[0]);
for ($i = 1; isset($argv[$i]); $i++) {
$temp[$i] = unserialize($argv[$i]);
}
return $temp;
}
If you need to pass a large amount of data (larger than the output of getconf ARG_MAX bytes) you should dump the serialized data to a file and pass the path to the file as a command line argument.
You could try to urlencode the $_SESSION ["data"] and pass it as an argument to the CLI script:
Script 1
$URLENCODED = urlencode($_SESSION["data"]);
exec("/usr/bin/php /path/to/data.php " . $URLENCODED . " > /dev/null 2>&1 &")
Script 2
$args = urldecode($argv[1]); // thanks for the reminder daverandom ..forgot to do this :)
fwrite($fp, $args);

Categories