Executing Python Script in PHP - php

I have written a simple chat program using twisted library in Python. Basically I have a server program(server.py) and a chat program ( client.py)
client.py is a simple python script which would connect to the server on a particular port and print the messages on the terminal.
I can run the server.py and the client.py on a local system and I can chat on different terminals.
I would like to integrate the client.py in PHP and be able to chat through the browser.
I am calling python script through exec in PHP. However it is not working.
exec("python client.py")
Any idea, if I am missing anything ?

Not sure if I can help you, but here's some things I'd consider:
Have you tried executing a different command, does that work?
Have you set the permissions of the .php file and Python.exe correctly?
Can you run your command from a terminal window (either Windows/Mac/Linux) from the folder your PHP file is in?
If you've already tried all of these things, I can't think of another solution.. Good luck!

You might find the following program helpful as a starting point. It is designed to run a Python program:
<?php
// Check that test is not FALSE; otherwise, show user an error.
function assert_($test)
{
if ($test === FALSE)
{
echo '<html><head><title>Proxy</title></head><body><h1>Fatal Error</h1></body></html>';
exit(1);
}
}
// Patch this version of PHP with curl_setopt_array as needed.
if (!function_exists('curl_setopt_array')) {
function curl_setopt_array($ch, $curl_options)
{
foreach ($curl_options as $option => $value) {
if (!curl_setopt($ch, $option, $value)) {
return FALSE;
}
}
return TRUE;
}
}
// Fetch the URL by logging into proxy with credentials.
function fetch($url, $proxy, $port, $user, $pwd)
{
$ch = curl_init($url);
assert_($ch);
$options = array(
CURLOPT_PROXY => $proxy . ':' . $port,
CURLOPT_PROXYAUTH => CURLAUTH_NTLM,
CURLOPT_PROXYUSERPWD => $user . ':' . $pwd,
CURLOPT_RETURNTRANSFER => TRUE
);
assert_(curl_setopt_array($ch, $options));
$transfer = curl_exec($ch);
curl_close($ch);
assert_($transfer);
return $transfer;
}
// Run path with stdin and return program's status code.
function order($path, $stdin, &$stdout, &$stderr)
{
$cmd = './' . basename($path);
$descriptorspec = array(
array('pipe', 'r'),
array('pipe', 'w'),
array('pipe', 'w')
);
$cwd = dirname($path);
$process = proc_open($cmd, $descriptorspec, $pipes, $cwd, $_REQUEST);
assert_($process);
for ($total = 0; $total < strlen($stdin); $total += $written)
{
$written = fwrite($pipes[0], substr($stdin, $total));
assert_($written);
}
assert_(fclose($pipes[0]));
$stdout = stream_get_contents($pipes[1]);
assert_($stdout);
assert_(fclose($pipes[1]));
$stderr = stream_get_contents($pipes[2]);
assert_($stderr);
assert_(fclose($pipes[2]));
return proc_close($process);
}
// Collect information to run over the proxy.
# $user = $_REQUEST['user'];
# $pwd = $_REQUEST['pwd'];
// Fetch the URL and process it with the backend.
$transfer = fetch('http://rssblog.whatisrss.com/feed/', 'labproxy.pcci.edu', 8080, $user, $pwd);
$status = order('/home/Chappell_Stephen/public_html/backend.py', $transfer, $stdout, $stderr);
// Check for errors and display the final output.
assert_(strlen($stderr) == 0);
echo $stdout;
?>

Related

Pipe $_POST to an external command

I have a program that reads a JSON request from stdin, which I want to call in PHP.
Here's what I have right now
<?php
echo exec(
'echo \''.json_encode($_POST,JSON_NUMERIC_CHECK).'\' | '.
'program'
);
?>
This works, but is there a more direct way to put a string in PHP in stdin?
Something along the lines of pipe(json_encode($_POST,JSON_NUMERIC_CHECK),'program') maybe?
What I have may be fine in this particular case, but what if instead of JSON, I'll need to pipe binary data? What if it's too long to fit into a shell argument, or contains single quotes?
Edit:
Following miken32's suggestion, I used proc_open() like this:
$proc = proc_open(
'LD_LIBRARY_PATH=/foo/bar/lib program args',
array(0 => array('pipe','r'), 1 => array('pipe','w')),
$pipes,
NULL
//, array('LD_LIBRARY_PATH','/foo/bar/lib')
);
if (is_resource($proc)) {
fwrite($pipes[0],json_encode($_POST,JSON_NUMERIC_CHECK));
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($proc);
}
?>
But I had to add LD_LIBRARY_PATH=/foo/bar/lib to the command argument. The $env argument seems to have no effect. Does anyone know why?
You can use popen() to create a pipeline to a process:
<?php
$data = json_encode($_POST, JSON_NUMERIC_CHECK);
$p = popen("program", "w");
fwrite($p, $data . PHP_EOL);
$exit_code = pclose($p);
If you need to get data back from the program, things get more involved and you need to use proc_open() instead.
<?php
$data = json_encode($_POST, JSON_NUMERIC_CHECK);
$fds = [
0=>["pipe", "r"], // STDIN
1=>["pipe", "w"], // STDOUT
2=>["pipe", "w"], // STDERR
];
$dir = "/path/to/working/directory";
$env = [
"PATH" => "/usr/local/foo/bin:/usr/local/bin:/usr/bin",
"LD_LIBRARY_PATH" => "/usr/local/foo/lib/",
];
$p = proc_open("program", $fds, $pipes, $dir, $env);
fwrite($pipes[0], $data . PHP_EOL);
fclose($pipes[0]);
$return = stream_get_contents($pipes[1]);
$err = stream_get_contents($pipes[2]);
fclose($pipes[1]);
fclose($pipes[2]);
$exit_code = proc_close($p);
if ($exit_code == 0) {
// successful return
echo $return;
} else {
// error
echo "ERR: $err";
}

Run Function if Different Function Fails to Complete Within Time Frame

I already have a function on a website for a client that grabs data from a live server to save it locally, but something I didn't expect was that sometimes these local servers aren't in an area with good service, so every now and then the script dies after a certain amount of time because it failed to connect.
I already have a system implemented to disable external calls for these types of situations, but the clients can't get to the option to set this "Offline Mode" to begin with because the service is bad and the server is trying to reach the live server.
So what I need to do, is wrap my SyncTable function with a function like set_time_limit(8) that calls a different function that sets the "Offline Mode" automatically if the SyncTable Function fails to complete in the 8 seconds.
Is something like this possible? If so, I would love to know how so I can save these clients some time in areas with rough service.
You can accomplish this using proc_open, proc_get_status, and proc_terminate to start the SyncTable operation as a process, monitor it, and terminate it if necessary. Note: You may need to create a simple wrapper script so you can start the SyncTable function as a standalone process.
Here's a function I use to do this and enforce a timeout:
/// Executes a command and returns the output
/// If the timeout value (in seconds) is reached, it terminates the process
/// and returns FALSE
function exec_timeout($cmd, $timeout=30)
{
$descriptors = array(
0 => array('pipe', 'r'), // stdin
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
);
$pipes = Array();
$process = proc_open($cmd, $descriptors, $pipes);
$result = '';
$end_time = time() + $timeout;
if (is_resource($process))
{
// set the streams to non-blocking
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
$timeleft = $end_time - time();
while ($timeleft > 0)
{
$status = proc_get_status($process);
$result .= stream_get_contents($pipes[1]);
// leave the loop if the process has already finished
if (!$status['running'])
break;
$timeleft = $end_time - time();
}
if ($timeleft <= 0)
{
proc_terminate($process);
$result = FALSE;
}
}
// check for errors
$errors = stream_get_contents($pipes[2]);
if (!empty($errors))
fwrite(STDERR, "$errors\n");
// close streams
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $result;
}

SSH2 change a user password

I've been playing around with SSH and now I need to change a user's password via the PHP's ssh2,
Here's my code:
$stream = ssh2_exec($ssh, 'passwd test1234');
stream_set_blocking($stream, true);
$data = '';
while($buffer = fread($stream, 4096)) {
$data .= $buffer;
}
fclose($stream);
echo $data."<hr/>";
$stream = ssh2_exec($ssh, 'saulius123');
stream_set_blocking($stream, true);
$data = '';
while($buffer = fread($stream, 4096)) {
$data .= $buffer;
}
echo $data."<hr/>";
$stream = ssh2_exec($ssh, 'saulius123');
stream_set_blocking($stream, true);
$data = '';
while($buffer = fread($stream, 4096)) {
$data .= $buffer;
}
echo $data."<hr/>";
However this just make's my PHP script hang, any ideas?
ssh2_exec invokes the command; to send input, you'll need to write to the stream.
That is, $stream gives you access to standard input and standard output. So you'll need to write the password you wish to set using fwrite on $stream before trying to read back the output.
Since you've put the stream in blocking mode, passwd is awaiting your input (the password) at the same time your script is waiting for passwd. As a result, the script hangs.
Personally, I'd use phpseclib, a pure PHP SSH implementation. Example:
<?php
include('Net/SSH2.php');
$key = new Crypt_RSA();
//$key->setPassword('whatever');
$key->loadKey(file_get_contents('privatekey'));
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', $key)) {
exit('Login Failed');
}
echo $ssh->read('username#username:~$');
$ssh->write("ls -la\n");
echo $ssh->read('username#username:~$');
?>
The biggest advantage of it over libssh2 is portability. We use Amazon Web Services were I work and sometimes we move over to new prod servers or dev servers and the most difficult part in setting them up is installing all the PECL extensions and what not.
phpseclib, in contrast, doesn't have any requirements.

PHP Process Execution Timeout

I have the following code:
/**
* Executes a program and waits for it to finish, taking pipes into account.
* #param string $cmd Command line to execute, including any arguments.
* #param string $input Data for standard input.
* #param integer $timeout How much to wait from program in msecs (-1 to wait indefinitely).
* #return array Array of "stdout", "stderr" and "return".
*/
function execute($cmd,$stdin=null,$timeout=-1){
$proc=proc_open(
$cmd,
array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
$pipes=null
);
fwrite($pipes[0],$stdin); fclose($pipes[0]);
$stdout=stream_get_contents($pipes[1]); fclose($pipes[1]);
$stderr=stream_get_contents($pipes[2]); fclose($pipes[2]);
$return=proc_close($proc);
return array(
'stdout' => $stdout,
'stderr' => $stderr,
'return' => $return
);
}
It has two "problems".
The code is synchronous; it freezes until the target process closes.
So far, I've not been able to it from "freezing" without issuing a different kind of command (such as $cmd > /dev/null & on linux and start /B $cmd on windows)
I don't mind the "freeze", at all. I just need to implement that timeout.
Note: It is important that the solution is cross-platform compatible. It is also important that the $cmd doesn't have to change - I'm running some complex commands and I'm afraid there may be some issues, however, this depends on the type of fix - I'm happy to hear these out, just that I'd prefer a different alternative.
I've found some resources that may help:
Run perl file from PHP script but not wait for output on Windows Server
PHP set timeout for script with system call, set_time_limit not working
http://www.shapeshifter.se/2008/08/04/asynchronous-background-execution-with-php/
There is a few mistakes on the code.
That is actually working:
function execute($cmd, $stdin = null, $timeout = -1)
{
$proc=proc_open(
$cmd,
array(array('pipe','r'), array('pipe','w'), array('pipe','w')),
$pipes
);
var_dump($pipes);
if (isset($stdin))
{
fwrite($pipes[0],$stdin);
}
fclose($pipes[0]);
stream_set_timeout($pipes[1], 0);
stream_set_timeout($pipes[2], 0);
$stdout = '';
$start = microtime();
while ($data = fread($pipes[1], 4096))
{
$meta = stream_get_meta_data($pipes[1]);
if (microtime()-$start>$timeout) break;
if ($meta['timed_out']) continue;
$stdout .= $data;
}
$stdout .= stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
$return = proc_close($proc);
return array(
'stdout' => $stdout,
'stderr' => $stderr,
'return' => $return
);
}
Rather than stream_get_contents, you could look at using fread to gain more finely grained control over what your code is doing. That combined with stream_set_timeout may give you what you're looking for.
I tossed something together as a demonstration of what I was thinking might work - this code is completely untested and comes with no guarantees, but might send you in the right direction. ;)
function execute($cmd,$stdin=null,$timeout=-1){
$proc=proc_open(
$cmd,
array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
$pipes=null
);
fwrite($pipes[0],$stdin); fclose($pipes[0]);
stream_set_timeout($pipes[1], 0);
stream_set_timeout($pipes[2], 0);
$stdout = '';
$start = microtime();
while ($data = fread($pipes[1], 4096))
{
$meta = stream_get_meta_data($pipes[1]);
if (microtime()-$start>$timeout) break;
if ($meta['timed_out']) continue;
$stdout .= $data;
}
$return = proc_close($proc);
$stdout .= stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
return array(
'stdout' => $stdout,
'stderr' => $stderr,
'return' => $return
);
}
This seems to be working for me:
public function toPDF() {
$doc = $this->getDocument();
$descriptor = [
['pipe','r'],
['pipe','w'],
['file','/dev/null','w'], // STDERR
];
$proc = proc_open('/usr/local/project/scripts/dompdf_cli.php',$descriptor,$pipes,sys_get_temp_dir());
fwrite($pipes[0],"$doc[paper]\n$doc[html]");
fclose($pipes[0]);
$timeout = 30;
stream_set_blocking($pipes[1], false);
$pdf = '';
$now = microtime(true);
try {
do {
$elapsed = microtime(true) - $now;
if($elapsed > $timeout) {
throw new \Exception("PDF generation timed out after $timeout seconds");
}
$data = fread($pipes[1], 4096);
if($data === false) {
throw new \Exception("Read failed");
}
if(strlen($data) === 0) {
usleep(50);
continue;
}
$pdf .= $data;
} while(!feof($pipes[1]));
fclose($pipes[1]);
$ret = proc_close($proc);
} catch(\Exception $ex) {
fclose($pipes[1]);
proc_terminate($proc); // proc_close tends to hang if the process is timing out
throw $ex;
}
if($ret !== 0) {
throw new \Exception("dompdf_cli returned non-zero exit status: $ret");
}
// dump('returning pdf');
return $pdf;
}
I'm not sure what the purpose of stream_set_timeout is -- that just sets the per-read timeout, but if you want to limit the overall time, you just have to set the stream to non-blocking mode and then time how long it takes.

How to make fread blocking and read until the end?

I am executing a TCL script from PHP using proc_open.
I first open the TCL shell
2) Send a command using fwrite
3) What I need is fread to wait/block until the
command sent by fwrite is complete
and get all the contents .The command may take some time to complete.
(I am able to read just 2 lines and then it is going off to the next loop)
Can someone guide me.
The present code is
<?php
$app = 'tclsh84';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/wamp/www/tcl/bin/g.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process))
{
for($i=0;$i<4;$i++)
{
fwrite($pipes[0], 'source c:/wamp/www/tcl/bin/test.tcl'."\n");
$content= fread($pipes[1],8192)
print "$content";
}
fclose($pipes[0]);
fclose($pipes[1]);
proc_close($process);
}
?>
I'm thinking about a combination of
stream_select and/or feof()
fread() and concatenation of the partial results ($result .= fread())
and maybe proc_get_status() to determine the end of the process
You want to wait until the tcl application doesn't write something to its stdout for a certain amount of time (presuming that this means the end of the last command) and then send the next command/line to its stdin?
edit:
Seems like you can send all commands to the tcl shell at once and they are processed one by one, i.e. the shell reads the next input line/command when it's done with the previous one. I've tested this with the script.
incr a 1
after 1000
puts [concat [clock seconds] $a]
and
<?php
$app = 'c:/programme/tcl/bin/tclsh85.exe';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/god.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "set a 1\n");
for($i=0;$i<4;$i++) {
fwrite($pipes[0], "source c:/helloworld.tcl\n");
}
// when all scripts are done the shell shall exit
fwrite($pipes[0], "exit\n");
fclose($pipes[0]);
do {
$read=array($pipes[1]); $write=array(); $except=array($pipes[1]);
// wait up to 1 second for new output of the tcl process
$ready = stream_select($read, $write, $except, 1, 0);
if ( $ready && $read /* is not empty */) {
// get the partial output
$r = fread($pipes[1], 2048);
echo $r;
}
// is the process still running?
$status = proc_get_status($process);
} while($status['running']);
fclose($pipes[1]);
proc_close($process);
}
?>
You probably want to add some more error handling. E.g. if stream_select() returns x times with an timeout something might have gone wrong.
edit2:
Let the shell print something you can scan for after each script.
<?php
// something that's not in the "normal" output of the scripts
$id = 'done'. time();
$app = 'c:/programme/tcl/bin/tclsh85.exe';
$descriptorspec = array(
0 => array("pipe","r"),
1 => array("pipe","w"),
2 => array("file","C:/god.txt","w")
) ;
$process = proc_open($app, $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], "set a 1\n");
for($i=0;$i<4;$i++) {
$output = '';
$continue = true;
$cTimeout = 0;
echo 'loop ', $i, "\n";
fwrite($pipes[0], "source c:/helloworld.tcl\n");
fwrite($pipes[0], "puts $id\n");
echo "waiting for idle\n";
do {
$read=array($pipes[1]);
$write=array();
$except=array($pipes[1]);
$ready = stream_select($read, $write, $except, 1, 0);
if ( $ready && $read ) {
$output .= fread($pipes[1], 2048);
// if the delimiter id shows up in $output
if ( false!==strpos($output, $id) ) {
// the script is done
$continue = false;
}
}
} while($continue);
echo 'loop ', $i, " finished\n";
}
proc_close($process);
}
?>
Try:
$content = '';
while(!feof($pipes[1]))
{
$content .= fread($pipes[1],8192);
}
Does that wait?

Categories