Connect pipes of processes in php - php

I would like the output of one process created with proc_open to be piped to another one created with proc_open (in php). For example. In bash I can do:
[herbert#thdev1 ~]$ cat foo
2
3
1
[herbert#thdev1 ~]$ cat foo | sort
1
2
3
[herbert#thdev1 ~]$
I would like to simulate this in php using proc_open (instead of shell_exec) in order to have control over return-codes, pipes, etc. So I want something like this:
$catPipes=array();
$sortPipes=array();
$cwd = '/tmp';
$env = array();
$catProcess = proc_open("cat foo", array(
0 => array("pipe", "r"),
1 => array("pipe", "w")
), $catPipes, $cwd, $env);
$sortProcess = proc_open("sort", array(
0 => array("pipe", "r", $catPipes[1]),
1 => array("pipe", "w"),
), $sortPipes, $cwd, $env);
echo stream_get_contents($sortPipes[1]);
fclose($sortPipes[1]);
//proc_close(this) ... proc_close(that) ... etc
Would someone know how I can simulate the "|" of bash in php, i.e. connect the second descriptor of the cat-process to the first descriptor of the sort-process? Any help would be appreciated! But please do not redirect me to shell_exec, as I want to be able to check exit-codes and log errors :).
EDIT:
My needs-to-work-business-solution btw is:
while(!feof($searchPipes[1])) fwrite($lookupPipes[0], stream_get_line($searchPipes[1], 40000));
Which is basically what the OS would do, but I do not want to my own pipe-management, as I have a kernel/posix for that, and let's be honest, it's not 1976 :)

Yes, you can -- but i think you have to define this the way round. That you can use the STDIN of "sort" as STDOUT pipe for "cat". Have a look at the following, which works for me:
<?php
$txt = "a\nc\ne\nb\nd\n";
$fh = fopen('data://text/plain;base64,' . base64_encode($txt), 'r');
$sort_pipes = array();
$sort_proc = proc_open(
'sort',
array(
array('pipe', 'r'),
STDOUT
),
$sort_pipes
);
$cat_pipes = array();
$cat_proc = proc_open(
'cat',
array(
$fh,
$sort_pipes[0]
),
$cat_pipes
);
In the first two rows i defined a data stream from a text string that i do not have to rely on a file somewhere in the filesystem. Note, that i have a list of unsorted characters stored in the data stream (a, c, e, b, d). Running the script above should return a sorted list to STDOUT.
Note, that you can specify resources as descriptors, too. In this case you must omit the array notation, so:
STDOUT
instead of
array(STDOUT)
etc.
Btw.: you can even write directly to a file specified by a file name. You can find further information on the descriptor specification in the manual entry for proc_open at http://en.php.net/manual/de/function.proc-open.php
EDIT
The other way works too, of course: you can also write "cat" to an STDOUT pipe array('pipe', 'w') and use $cat_pipes[1] as STDIN for "sort". :)

Related

Add proc_open $pipe inputs on the go dynamically

I am trying to create a C program compiler + executer using PHP.
My PHP code is
<?php
$cwd='/path/to/pwd';
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("file", "/path/to/log/file", "a") );
$process = proc_open("./a.out", $descriptorspec, $pipes, $cwd);
fwrite($pipes[0], 123);
fclose($pipes[0]);
echo stream_get_contents($pipes[1]);
fclose($pipes[1]);
My C file is
#include <stdio.h>
int main() {
int a;
printf("Enter a number");
scanf("%d", &a);
printf("The value is: %d", a);
return 0;
}
Now when I run this, I am getting the output
Enter a numberThe value is: 123
Is there anyway to do it like a terminal execution? So it should wait for the input after showing Enter a number. Then once I enter the number it will execute further and show the output.
It will be like a web site with a terminal.
Will it be possible with proc_open and PHP? Is it possible to achieve by using Web socket in some way?
Please help. Thanks in advance.

Php proc_open - save process handle to a file and retrieve it

I use the following code to open a process with proc_open, and to save the handle and the pipes to a file:
$command = "COMMAND_TO_EXECUTE";
$descriptors = 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
);
$pipes = array();
$processHandle = proc_open($command, $descriptors, $pipes);
if (is_resource($processHandle)) {
$processToSave = array(
"process" => $processHandle,
"pipes" => $pipes
);
file_put_contents("myfile.bin", serialize($processToSave) );
}
And in a second moment i need to retrieve this file handle from the file, i've used this code:
$processArray = unserialize(file_get_contents("myfile.bin"));
$processHandle = $processArray["process"];
$pipes = $processArray["pipes"];
But when I print a var_dump of $processHandle and $pipes after retrieving from file, I'll get integers instead of resource or process, but why??
var_dump($processHandle) -> int(0)
var_dump($pipes) - > array(2) { int(0), int(0) }
And at this point of course, if I try to close the pipes, i will get an error, resource expected, integer given.
How can I make this working? (NOTE: This is the solution I'm looking for)
But alternatively, I can get also the pid of process and then use this pid to stop or kill or do anything else with the process, but what about the pipes?
How can I read/write or save error from/to the process?
Thank you
Found the solution to myself, it's not to possible to serialize resource and when the script has done, those resource handler were free.
Solution was to create a daemon listening on a port, wich on request launch and stop process. Because the process is always running, it can maintain a list of handler process and stop when requested.

maxima- How can I execute some maxima script from php?

I'm new to CAS and maxima. I'd like to know whether it's feasible to do the following:
1) I have a list of parameters e.g. a, b, c
2) In PHP, I have some maxima script stored as a string, involving a, b, c, e.g.:
do {
a=random(20);
b=random(20);
c=random(20);
}while (!(a > b) || !(b > c))
Such that a, b, c are randomized to desired values and satisfy requirements.
3) Retrieve the values of a, b, c in PHP.
The purpose is to create randomized questions with reasonable parameters for students. So how can I execute the maxima script and retrieve the values of parameters? Is it suitable for my purpose?
You can pass the command string in a file to Maxima, that is supported by command-line Maxima.
if your OS is Linux/Unix/MacOS:
In PHP:
exec('maxima -q -b file');
or
system('maxima -q -b file');
if your OS is Win:
$maximaDir = 'D:/Program Files/Maxima-5.30.0'; // If your maxima is installed in elsewhere, please modified this location
exec($maximaDir.'/bin/maxima.bat -q -b "result.txt"');
In Maxima, you can use stringout(); to get the result in a file, then in PHP read the file as a string, you can do any other manipulations to the string as you want.
I dont really know how your code works but if you save the maxima as a php extension it could work. Place this line of code at the start of the php file
<?php
require_once("extension/Maxima.php");
?>
For the echo example
echo $A ;
<?php
require_once($_SERVER['PM_BASE_CONFIG_PATH']);
class maxima_core {
private $executable_command;
protected $dbg_bool;
protected $dbg_info;
public function __construct($dbg=FALSE){
$this->executable_command=constant('PM_MAXIMA_EXEC_CMD');
$this->dbg_bool=$dbg;
$this->dbg_info="";
}
protected function exec($query){// to include package that is loaded by init_command
$descriptor = 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-log.txt", "a")//constant('PM_SERVER_LOG_DIR')."/maxima/error.log", "a") // stderr is a file to write to
);
$cwd=constant('PM_ACTIVITY_PLUGIN_URL')."/engine_solver";
$MAXIMA_DIR = constant('PM_ACTIVITY_PLUGIN_DIR');
$env=array();
$init_command="display2d:false$" . "PM_ACTIVITY_PLUGIN_URL: \"" . $MAXIMA_DIR . "\"$";
//'load("/home/gabriel/github/moodledata/stack/maximalocal.mac");';
$exec_cmd=$this->executable_command." --quiet";
// --userdir='".constant('PM_ACTIVITY_PLUGIN_DIR')."/engine_solver/maxima_userdir'";
// change
$result=NULL;
$process=proc_open($exec_cmd,$descriptor,$pipes,$cwd,$env);
if(is_resource($process)){
if (!fwrite($pipes[0], $init_command)) {
echo "<br />Could not write to the CAS process!<br />\n";
} else {
fwrite($pipes[0], $query);
fwrite($pipes[0], "quit();");
fclose($pipes[0]);
$result=stream_get_contents($pipes[1]);
fclose($pipes[1]);
proc_close($process);
}
}
return $result;
}
public function dbg_info(){
return $this->dbg_info;
}
}
?>
If the goal is to use Maxima to create homework problems which are presented by a web server, I think that there have been projects to do just that. I think one of them is named LON-CAPA -- a web search should find it. There might be some other projects mentioned on the related projects page at the Maxima web site. [1]
[1] http://maxima.sf.net/relatedprojects.html

How to background a process via proc_open and have access to STDIN?

I'm happily using proc_open to pipe data into another PHP process.
something like this
$spec = array (
0 => array('pipe', 'r'),
// I don't need output pipes
);
$cmd = 'php -f another.php >out.log 2>err.log';
$process = proc_open( $cmd, $spec, $pipes );
fwrite( $pipes[0], 'hello world');
fclose( $pipes[0] );
proc_close($process);
In the other PHP file I echo STDIN with:
echo file_get_contents('php://stdin');
This works fine, but not when I background it. Simply by appending $cmd with & I get nothing from STDIN. I must be missing something fundamental.
It also fails with fgets(STDIN)
Any ideas please?
You can't write to STDIN of a background process (at least, not in the normal way).
This question on Server Fault may give you some idea of how to work around this problem.
Unrelated: you say do don't need outputs in the spec, yet you specify them im your $cmd; you can write $spec like this:
$spec = array (
0 => array('pipe', 'r'),
1 => array('file', 'out.log', 'w'), // or 'a' to append
2 => array('file', 'err.log', 'w'),
);

How can I invoke the MySQL interactive client from PHP?

I'm trying to get
`mysql -uroot`;
to enter the MySQL interactive client just as executing
$ mysql -uroot
from the shell does.
It's okay if the PHP script exists after (or before), but I need it to invoke the MySQL client.
I've tried using proc_open() and of course system(), exec() and passthru(). Wondering if anyone has any tips.
New solution:
<?php
$descriptorspec = array(
0 => STDIN,
1 => STDOUT,
2 => STDERR
);
$process = proc_open('mysql -uroot', $descriptorspec, $pipes);
Old one:
Save for tab completion (you could probably get it in there if you read out bytes with fread instead of using fgets), this gets you on your way, lots left to tweak:
<?php
$descriptorspec = array(
0 => array("pty"),
1 => array("pty"),
2 => array("pty")
);
$process = proc_open('mysql -uroot', $descriptorspec, $pipes);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking(STDIN,0);
do {
echo stream_get_contents($pipes[1]);
echo stream_get_contents($pipes[2]);
while($in = fgets(STDIN)) fwrite($pipes[0],$in);
} while (1);
I guess it does work, but it's waiting for some input. Try sending some sql commands to it's stdin. Of course, since the backtick operator doesn't support IO remapping, you'll need more complex process handling.

Categories