PHP Exec command - How to pass input to a series of questions - php

I have a program on my linux server that asks the same series of questions each time it executes and then provides several lines of output. My goal is to automate the input and output with a php script.
The program is not designed to accept input on the command line. Instead, the program asks question 1 and waits for an answer from the keyboard, then the program asks question 2 and waits for an answer from the keyboard, etc.
I know how to capture the output in an array by writing:
$out = array();
exec("my/path/program",$out);
But how do I handle the input?
Assume the program asks 3 questions and valid answers are: left 120 n
What is the easiest way using php to pass that input to the program?
Can I do it somehow on the exec line?
I’m not a php noob but simply have never needed to do this before.
Alas, my googling is going in circles.

First up, just to let you know that you're trying to reinvent the wheel. What you're really looking for is expect(1), which is a command-line utility intended to do exactly what you want without involving PHP.
However, if you really want to write your own PHP code you need to use proc_open. Here are some good code examples on reading from STDOUT and writing to STDIN of the child process using proc_open:
http://www.php.net/manual/en/function.proc-open.php#79665
How to pass variables as stdin into command line from PHP
http://camposer-techie.blogspot.com/2010/08/ejecutando-comandos-sobre-un-programa.html (this one is in Spanish, sorry, but the code is good)
Finally, there is also an Expect PECL module for PHP.
Hope this helps.

Just add the arguments to the exec line.
exec("/path/to/programname $arg1 $arg2 $arg3");
... but don't forget to apply escapeshellarg() on every argument! Otherwise, you're vulnerable to injected malicious code.

$out = array();
//add elements/parameters/input to array
string $execpath = "my/path/program ";
foreach($out as $parameter) {
$execpath += $parameter;
//$execpath += "-"+$execpath; use this if you need to add a '-' in front of your parameters.
}
exec($execpath);

Related

PHP CLI - get user input while still doing things in background

I'm working on a game, written in PHP and that runs in a console. Think back to old MUDs and other text-based games, even some ASCII art!
Anyway, what I'm trying to do is have things happening while also accepting user input.
For instance, let's say it's a two player game and Player 1 is waiting for Player 2 to make a move. This is easily done by just listening for a message.
But what if Player 1 wants to change some options? What if they want to view details on aspects of the game state? What about conceding the game? There are many things a Player may want to do while waiting for their opponent to make a move.
Unfortunately the best I have right now is the fact that Ctrl+C completely kills the program. The other player is then left hanging, until the connection is dropped. Oh, and the game is completely lost.
I get user input with fgets(STDIN). But this blocks execution until input has been received (which is usually a good thing).
Is it even possible for a console program like this to handle input and output simultaneously? Or should I just look at some other interface?
In short PHP is not built for this, but you might get some help from one of these extensions. I'm not sure how thorough they are, but you really probably want to use a text UI library. (And really you probably do not want to use PHP for this.)
All that said, you need to get non blocking input from STDIN character by character. Unfortunately most terminals are buffered from PHP's point of view, so you won't get anything until enter is pressed.
If you run stty -icanon (or your OS's equivalent) on your terminal to disable buffering, then the following short program basically works:
<?php
stream_set_blocking(STDIN, false);
$line = '';
$time = microtime(true);
$prompt = '> ';
echo $prompt;
while (true)
{
if (microtime(true) - $time > 5)
{
echo "\nTick...\n$prompt$line";
$time = microtime(true);
}
$c = fgetc(STDIN);
if ($c !== false)
{
if ($c != "\n")
$line .= $c;
else
{
if ($line == 'exit' || $line == 'quit')
break;
else if ($line == 'help')
echo "Type exit\n";
else
echo "Unrecognized command.\n";
echo $prompt;
$line = '';
}
}
}
(It relies on local echo being enabled to print the characters as they are typed.)
As you see, we are just looping around forever. If a character exists, add it to the $line. If enter is pressed, process $line. Meanwhile, we are ticking every five seconds just to show that we could be doing something else while we wait for input. (This will consume maximum CPU; you'd have to issue a sleep() to get around that.)
This isn't meant to be a practical example, per se, but perhaps will get you thinking in the proper direction.
It is possible to build a game like you describe using ncurses (non-blocking mode) and libevent. That way, you get close to no CPU consumption. Handling individual keys is sometimes awkward (implement Backspace yourself, it's not fun at all - and did you know various OSes send different keycodes on Backspace press?), and gets really tricky if you want to support UTF-8 properly. Still, completely viable.
In particular, it is beneficial to make extensive use of libevent, by reading both the network and keyboard (stdin) input with it. This function enables you to listen for individual keys:
http://www.php.net/manual/en/function.ncurses-cbreak.php
which you can later read using libevent API. The key to keep in mind is that you will sometimes end up reading more than 1 key at a time, and it has to be handled (so loop over everything that you have read). Otherwise, the user will be annoyed to see that not all key presses are "reaching" the application and some are lost.
Sorry Matthew, I'm going to have to un-accept your answer, because I have found it myself:
Use the following code to receive user input while still doing something else:
while(/* some condition that the code running is waiting on */) {
// perform one step or iteration of that code
exec("choice /N /C ___ /D _ /T _",$out,$ret);
// /C is a list of letters that do something
// /D is the default action that will be used as a no-op
// /T is the amount of time to wait, probably best set to one second
switch($ret) {
// handle cases - the "default" case should be "continue 2"
}
}
This can then be used to interrupt the loop and enter an options menu, or trigger some other event, or could even be used to type out a command if used right.

python 2.6.5, and getting the result of a call to a php app

i've got this snazzy python code:
import subprocess
value = subprocess.Popen(["php","./php/php_runner.php"],stdout=subprocess.PIPE);
the problem is, i have no idea how to check if the php_runner, well, ran. Currently, it has the following salient sections:
if (count($argv) != 4){
die("four arguments are needed\n");
}
and
$returnValue = call_to_another_php_class();
return $returnValue;
So what i want is this:
How do i get the return value, whatever it may be, using python?
You probably are going to tell me to use "PIPE" in the answer, but the (to me, incomprehensible) python docs (http://docs.python.org/library/subprocess.html) state:
Do not use stdout=PIPE or stderr=PIPE with this function. As the pipes are not being read in >the current process, the child process may block if it generates enough output to a pipe to fill up >the OS pipe buffer.
So what do i use then, because while I don't really know what they're barking on about, i sit up and take note about notes in grey boxes. Pity they didn't spell out what i'm meant to do - but, well, what am i meant to do?
the "returnValue" that my php code returns, is that what python is going to pickup as the return value from the function? If not, how do i return that value?
cheers!
UPDATE
Thanks to the given answer, here's the changes i made:
edited /etc/php5/cli/conf.d/mcrypt.ini (actually, this is just a change for ubuntu 10.04, and I changed the first line to begin with a ; instead of a #. That stopped an annoying "we don't like #" error that kept popping up)
in my php, I changed the code to read:
if (count($argv) != 4){
fwrite(STDERR, "four arguments are needed\n");
exit(1); // A response code other than 0 is a failure
}
this puts my error value as an error. the die() command wasn't doing that for me.
changed the python to read:
value = subprocess.Popen(["php","./php/php_runner.php"],stdout=subprocess.PIPE,
stderr=subprocess.PIPE);
print value.communicate();
Yeah, realistically, i'd do an if on value.communicate()[1], becase that is where the errors are.
$returnValue = call_to_another_php_class();
if ($returnValue == 1){ //hah, php has a good return value as 1.
//no problem
} else {
fwrite(STDERR,get_error_from_php_class());
exit(1);
}
booyah!
Since you're using the Popen constructor rather than the call functions, those notes about PIPE don't apply to you.
Use .communicate() as documented to wait for the program to finish and get the output.

Interactive shell using PHP

Is it possible to create an interactive shell, using PHP alone?
I mean something like you have with databases, Python, etc. If it is, how?
Yes, it's possible. In order to be interactive, the program must be able to wait for and read in user input from stdin. In PHP, you can read from stdin by opening a file descriptor to 'php://stdin'. Taken from an answer to different question, here's an example of an interactive user prompt in PHP (when run from the command line, of course):
echo "Continue? (Y/N) - ";
$stdin = fopen('php://stdin', 'r');
$response = fgetc($stdin);
if ($response != 'Y') {
echo "Aborted.\n";
exit;
}
Of course, to get a full line of input rather than a single character, you'd need fgets() instead of fgetc(). Depending what your program/shell will do, the whole program might be structured as one big continuous loop. Hopefully that gives you an idea how to get started. If you wanted to get really fancy (CLI pseudo-GUI), you could use ncurses.
Since this question has been asked and answered, a better solution has been added to PHP. In all recent PHP versions, at least PHP 5.4, you can easily get user input as so:
$input = fgets(STDIN);
The way I understand your question you just want the PHP interpreter to run on the command line so you that it will evaluate any PHP code that you type. I use that feature of Python all the time to test code snippets. In which case I believe the answer you are looking for is to execute (from the command line):
php -a
Assuming PHP is in your path this will drop you in to the PHP interpreter, like it does on mine:
php -a
Interactive shell
php >
From there you can start to evaluate arbitrary PHP expressions and see the results:
php > $a = 'abcdef';
php > echo strlen($a);
6
Here's an expanded take on this. I've added an isCLI() check in case you're run your script both in CLI and on a web server. Otherwise the server could loop using my function. This solution will prompt the user, check the input, and re-prompt the user for fixed input if necessary.
I rtrim() the input, because if the user uses return to submit their entry, that may be appended to the entry. Validation is not necessary; just don't pass a function in that case.
function isCLI() {
return (php_sapi_name() === 'cli' OR defined('STDIN'));
}
function userPrompt($message, $validator=null) {
if (!isCLI())
return null;
print($message);
$handle = fopen ('php://stdin','r');
$line = rtrim(fgets($handle), "\r\n");
if (is_callable($validator) && !call_user_func($validator, $line)) {
print("Invalid Entry.\r\n");
return userPrompt($message, $validator);
} else {
print("Continuing...\r\n");
return $line;
}
}
// Example =====================
function validateSetLangCode($str) {
return preg_match("/^[A-Z0-9]{3}-[A-Z]{2}$/", $str);
}
$code = userPrompt("Please enter the set / language codes. Use the format 'SET-EN', where SET is the three-letter set code and EN is the two-letter lang code. \r\n", 'validateSetLangCode') ?: 'SET-EN';
var_dump($code);
Since PHP has a built-in Unix-only function readline() to do exactly that, note:
We can use and hold the result of readline in a variable.
#!/usr/bin/php
<?php
$user = readline("List dir [l] | Say hello [h] | exit [q]: ");
if ($user === "l"){ system("ls"); }
if ($user === "h"){ echo "Hello!"; }
if ($user === "q"){ exit; }
echo "\nThanks!";
Example output:
l ls result
h «hello»
q exit
Ctrl + C exit.
Ctrl + D with empty input, continue to the next sequence. «Thanks». $user is defined and empty, no error.
Ctrl + D with some input: No action. Still waiting for input.
Ctrl + M Continue and take the current input in $user.
Ctrl + J Continue and take the current input in $user, same behavior as Ctrl + M.
Return continue to the next sequence «Thanks». $user can stay empty, no error.
Ctrl + Z may be used to cancel a loop and move to the top one. $user will be unset if the var is not defined in this scope.
Depending input, we can define empty values using!empty or do more surgical testings (the readline response can be many chars).
$user can be tested with !isset if not yet asked.
There is also the built-in readline_add_history() to store the user input into an object, where values can be retrieved directly by their name (nice for code clarity):
readline_add_history($user);
print_r(readline_list_history());
print_r(readline_user());
It is very useful to build real complex stuffs!
See how to catch and send POSIX signals.
PHP function readline()
If you want the interactive shell to process PHP commands, one example is phpsh which was apparently created at Facebook, but it is written in Python.
I know the questioner didn't want the second option, but for those that wanted the second option as I did, in addition to phpsh, PHP also has its own shell:
Just run php -a.
Check out Sheldon.
It's pretty easy to get started. It includes Symfony 2 and Zend Framework libraries that do a lot of the basic console I/O work and gives you a higher-level abstraction built around command objects (with regex routes) and Contexts (which hold immutable state).
One of the things I love is that "out of the box", your application can run as either an interactive shell, or as a standard script that you can run from the command line, specify a command, pass any arguments, and when the command is finished the application exits.

PHP + Python - use PHP to pass string to Python program, and parse output

I have a great Python program on my webserver, which I want to use from inside my PHP web app.
Here's an example of the python command, and output as you would see it in terminal:
>>> print MBSP.parse('I ate pizza with a fork.')
I/PRP/I-NP/O/NP-SBJ-1/O/i
ate/VBD/I-VP/O/VP-1/A1/eat
pizza/NN/I-NP/O/NP-OBJ-1/O/pizza
with/IN/I-PP/B-PNP/O/P1/with
a/DT/I-NP/I-PNP/O/P1/a
fork/NN/I-NP/I-PNP/O/P1/fork ././O/O/O/O/.
You might recognize this as a typical POS tagger.
In any case, I'm confused about how to use a PHP-based web app to send this program a string like "I ate pizza with a fork", and somehow get the response back in a way that can be further parsed in PHP.
The idea is to use PHP to pass this text to the Python program, and then grab the response to be parsed by PHP by selecting certain types of words.
It seems like in PHP the usual suspects are popen() and proc_open(), but popen() is only for sending, or receiving information - not both? Is popen() able to give me access to this output (above) that I'm getting from the Python program? Or is there a better method? What about curl?
Here are all my options in terms of functions in PHP:
http://us.php.net/manual/en/function.proc-open.php
I'm lost on this, so thanks for your wise words of wisdom!
I use exec() for this purpose.
exec($command, $output);
print_r($output);
If you want to get a little heavier / fancier... give your python script an http (or xmlrpc) front end, and call that with a GET/POST. Might not be worth all that machinery though!
You could use popen(), and pass the input to your Python script as a command line argument, then read the output from the file descriptor popen gives you, or proc_open() if you want to interact bi-directionally with the Python script.
Example 1 in the proc_open manual: http://us.php.net/manual/en/function.proc-open.php gives an example of this.
If your Python needs it as stdin, you could try popening a command line:
echo "I ate pizza!"|my_python_progam.py
and just read the output. As usual, do proper input validation before sending it to the command-line.
Something like this would work
$command = '/usr/bin/python2.7 /home/a4337/Desktop/script.py'
$pid = popen('$command',r)
........
........
.........
pclose($pid)

Options for PHP CLI on windows

I'm working with the PHP CLI on windows at the moment to write some small desktop command-line apps.
I wanted to know if and how it may be possible to:
Clear the screen (cls would be the normal command but exec() won't work with it)
Change the color, change the color of parts of the output (seen this in programs before)
Make the command line horizontally bigger - things quickly get unreadable
Is any of the above possible from inside a PHP script?
On Windows, in the standard CLI prompt, you cannot output in colour (as in the answer by Spudley).
You can change the size of the window as a user by right-clicking the command window's title bar and selecting Properties, then ammending values in the Layout tab. I do not think it is possible to ammend the width of the CLI within PHP.
You can check the width of the CLI window on Windows using the function I wrote here
See the PHP manual page for working with the commandline
To directly answer each of your bullet points:
There is a comment on that page which gives a function that can clear the screen. I'll quote it here for you:
<?php
function clearscreen($out = TRUE) {
$clearscreen = chr(27)."[H".chr(27)."[2J";
if ($out) print $clearscreen;
else return $clearscreen;
}
?>
There's also another comment which explains how to change the colours. Again, I'll quote it:
<?php
echo "\033[31m".$myvar; // red foreground
echo "\033[41m".$myvar; // red background
?>
and to reset:
<?php
echo "\033[0m";
?>
You should read through the rest of that page for a whole load more suggestions on how to manipulate the CLI.
The only part of your question that leave unanswered is the third bullet point. Sadly, I don't believe you'll be able to do this, and I don't think it's possible to horizontally resize the Windows command line window.
Hope that helps.
I created a small backup script with PHP, and from what I can remember, you can print backspace characters to remove content. Not really ideal though.
Just google'd it: http://www.php.net/manual/en/features.commandline.php#77142
As far as the third question goes, I suggest you witch the default command line to Console 2. It is a great replacement that not only lets you use any width you like (as long as it fits your screen), but also supports command history, tabs, and some UI sugar.
The provided code will not work under Windows because PHP under windows does something to the command window. I am not sure what PHP does but I wrote a simple Freebasic program with only two lines:
cls
end
I then compiled it and ran it under a regular command line window. It cleared the screen without any kind of a problem. I then did the following in PHP:
<?php
echo "This is a test\n";
system( "cls.exe" );
exec( "cls.exe" );
passthru( "cls.exe" );
?>
When I ran the program it did nothing more than just the "This is a test" line. Thus, there is some kind of suppression going on with PHP that looks for and stops any kind of escape sequence from occurring. Until this is fixed in PHP - you will never be able to do a cls, nor use curses, ncurses, or any other library. What has to be done is to integrate something like FreeBasic's windowing methods as some kind of a class (or maybe just a C set of routines) that will open a new window via THAT language's methodologies and use them to do the text window. Then all of the escape sequences will work. Until then - they won't.
What I find weird about this is that PHP was originally written in Perl and Perl will do ncurses on Windows without any problems. Perl will also allow all escape sequences to work. So there is just something being done on the Windows compile that is causing this problem.

Categories