How to pipe STDIN to a PHP-spawned proc? - php

To my PHPeoples,
This is sort of a weird PHPuzzle, but I'm wondering if it's PHPossible.
The end goal is the equivalent functionality of
mysql mydb < file.sql
But with an API like this
./restore < file.sql
Where restore is a PHP script like this
#!/usr/bin/env php
$cmd = "msyql mydb";
passthru($cmd, $status);
However, I want to pass STDIN to the passthru command.
The clear benefit here is that I can put restore somewhere in a pipeline and everything works peachy. Here's an example
# would be pretty awesome!
ssh $remote "msyqldump $config mydb | gzip" | gzip -dc | ./restore
Anyway, I doubt it's possible using passthru, but perhaps with proc_open in some way?
As a last case resort, in the event of an unsolvable PHPredicament, I would do something like this
./restore file.sql
With script like this
#!/usr/bin/env php
$cmd = sprintf("mysql mydb < %s", $argv[1]);
passthru($cmd, $status);

PHPwnd
./restore
#!/usr/bin/env php
<?php
// set descriptors
$desc = array(
0 => array("file", "php://stdin", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
// open child proc
$proc = proc_open("mysql -uUSER -pPASS mydb", $desc, $pipes);
if (is_resource($proc)) {
// display child proc stdout
echo stream_get_contents($pipes[1]);
// close file streams
array_map('fclose', $pipes);
// get return value
$ret = proc_close($proc);
// display return value
printf("command returned %s\n", $ret);
}
Example use
cat file.sql.gz | gzip -dc | ./restore

What! PHProfound!
Turns out passthru already does this! PHenomenalP!
Check it out, PHPals:
gzip.php
#!/usr/bin/env php
<?php
passthru("gzip", $status);
if ($status !== 0) {
error_log("gzip exited with status %d", $status);
}
How PHProgressivist!
echo "hello" | php gzip.php | gzip -dc
Output
hello

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.

Connect pipes of processes in 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". :)

How to run 'at' command via php system()

For some strange reason this
echo system("echo 'echo hello > /dev/pts/2' | /usr/bin/at 19:36");
Refuses to work from my php script, however the command works fine when I just enter it through command line.
I know php has permission to execute some commands. I can run 'ls' from the php script but not the 'at' command. I've tried playing around with file permissions, but so far to no avail :(
edit
Permissions for /usr/bin/at are:
-rwxr-sr-x 1 daemon daemon 42752 Jan 15 2011 at
I think it's a permissions problem, if I execute the php file from my ssh terminal it works fine, but not from the web.
What you are executing is
echo 'hello' > /dev/pts/2 | /usr/bin/at 19:36
meaning
echo 'hello' > /dev/pts/2
and pipe stdout to /usr/bin/at 19:36 but since you already redirected the echo to /dev/pts/2, this will be empty. What you probably meant to do is:
echo system("echo 'echo hello > /dev/pts/2' | /usr/bin/at 19:36");
You might also want to use shell_exec to pass the command through a shell or alternatively proc_open which gives you better control over stdin/out/err of the command you are executing. Your example would correspond to (adapted example from php.net docs):
<?php
$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("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open('/usr/bin/at', $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], 'echo "hello" > /dev/pts/2');
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);
echo "command returned $return_value. stdout: $stdout, stderr: $stderr\n";
} else {
echo "Process failed";
}
?>
In your php.ini file check for disable_functions sometimes functions like system are disabled for security reasons.

php exec() and tesseract goes ''Cannot open input file'

I use Ghostscript to strip images from PDF files into jpg and run Tesseract to save txt content like this:
Ghostscript located in c:\engine\gs\
Tesseract located in c:\engine\tesseract\
web located pdf/jpg/txt dir = file/tmp/
Code:
$pathgs = "c:\\engine\\gs\\";
$pathtess = "c:\\engine\\tesseract\\";
$pathfile = "file/tmp/"
// Strip images
putenv("PATH=".$pathgs);
$exec = "gs -dNOPAUSE -sDEVICE=jpeg -r300 -sOutputFile=".$pathfile."strip%d.jpg ".$pathfile."upload.pdf -q -c quit";
shell_exec($exec);
// OCR
putenv("PATH=".$pathtess);
$exec = "tesseract.exe '".$pathfile."strip1.jpg' '".$pathfile."ocr' -l eng";
exec($exec, $msg);
print_r($msg);
echo file_get_contents($pathfile."ocr.txt");
Stripping the image (its just 1 page) works fine, but Tesseract echoes:
Array
(
[0] => Tesseract Open Source OCR Engine v3.01 with Leptonica
[1] => Cannot open input file: 'file/tmp/strip1.jpg'
)
and no ocr.txt file is generated, thus leading into a 'failed to open stream' error in PHP.
Copying strip1.jpg into c:/engine/tesseract/ folder and running Tesseract from command (tesseract strip1.jpg ocr.txt -l eng) runs without any issue.
Replacing the putenv() quote by exec(c:/engine/tesseract/tesseract ... ) returns the a.m. error
I kept strip1.jpg in the Tesseract folder and ran exec(tesseract 'c:/engine/tesseract/strip1.jpg' ... ) returns the a.m. error
Leaving away the apostrophs around path/strip1.jpg returns an empty array as message and does not create the ocr.txt file.
writing the command directly into the exec() quote instead of using $exec doesn't make the change.
What am I doing wrong?
Halfer, you made my day:-)
Not exactly the way as described in your post but like this:
$path = str_replace("index.php", "../".$pathfile, $_SERVER['SCRIPT_FILENAME']);
$descriptors = array(
0 => array("pipe", "r"),
1 => array("pipe", "w"),
2 => array("pipe", "w")
);
$cwd = $pathtess;
$command = "tesseract ".$path."strip1.jpg" ".$path."ocr -l eng";
$process = proc_open($command, $descriptors, $pipes, $cwd);
if(is_resource($process)) {
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
}
echo file_get_contents($path."ocr.txt");
Perhaps the missing environment variables in PHP is the problem here. Have a look at my question here to see if setting HOME or PATH sorts this out?

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