Error retrieving exec() output - php

I'm using exec to grab curl output (I need to use curl as linux command).
When I start my file using php_cli I see a curl output:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 75480 100 75480 0 0 55411 0 0:00:01 0:00:01 --:--:-- 60432
It means that all the file has been downloaded correctly (~ 75 KB).
I have this code:
$page = exec('curl http://www.example.com/test.html');
I get a really strange output, I only get: </html>
(that's the end of my test.html file)
I really do not understand the reason, CURL seems to download all the file, but in $page I only get 7 characters (the lastest 7 characters).
Why?
P.S. I know I can download the source code using other php functions, but I must to use curl (as linux command).

Unless this is a really weird requirement, why not use PHP cURL library instead? You get much finer control on what happens, as well as call parameters (timeout, etc.).
If you really must use curl command line binary from PHP:
1) Use shell_exec() (this solves your problem)
2) Use 2>&1 at end of command (you might need stderr output as well as stdout)
3) Use the full path to curl utility: do not rely on PATH setting.

RTM for exec()
It returns
The last line from the result of the command.
You have to set the second parameter to exec() that will contain all the output from the command executed.
Example:
<?php
$allOutputLines = array();
$returnCode = 0;
$lastOutputLine = exec(
'curl http://www.example.com/test.html',
$allOutputLines,
$returnCode
);
echo 'The command was executed and with return code: ' . $returnCode . "\n";
echo 'The last line outputted by the command was: ' . $lastOutputLine . "\n";
echo 'The full command output was: ' . "\n";
echo implode("\n", $allOutputLines) . "\n";
?>

Related

How to get cursor position with PHP-CLI?

With a PHP script which runs in CLI mode, I want to get the cursor position in a portable way.
With the code :
// Query Cursor Position
echo "\033[6n";
In the terminal, this code reports the cursor position, as
wb ?> ./script.php
^[[5;1R
wb ?> ;1R
But, I can't retrieve the two values (row: 5, column: 1) in the code.
After some tests with output buffering :
ob_start();
echo "\033[6n";
$s = ob_get_contents();
file_put_contents('cpos.txt',$s);
I've "\033[6n" in the cpos.txt file, not the device answer.
And reading STDIN :
$timeout = 2;
$sent = false;
$t = microtime(true);
$buf = '';
stream_set_blocking(STDIN,false);
while(true){
$buf .= fread(STDIN,8);
if(!$sent){
echo "\033[6n";
$sent = true;
}
if($t+$timeout<microtime(true))
break;
}
var_dump($buf);
The buffer is empty but the terminal show the device answer :
wb ?> ./script.php
^[[5;1R
string(0) ""
wb ?>
Is there a way, without curses, to get the cursor position ?
The code you have so far almost works, and you'll find that hitting enter and waiting for your timeout to complete does produce a string containing the answer, but with a \n character on the end. (Note the string length of 7 instead of 0.)
$ php foo.php
^[[2;1R
string(7) "
"
The issue here is that stream_set_blocking does not prevent the terminal from buffering input line-by-line, so the terminal doesn't send anything to stdin of your program until the enter key is pressed.
To make the terminal send characters immediately to your program without line-buffering, you need to set the terminal to "non-canonical" mode. This disables any line-editing features, such as the ability to press backspace to erase characters, and instead sends characters to the input buffer immediately. The easiest way to do this in PHP is to call the Unix utility stty.
<?php
system('stty -icanon');
echo "\033[6n";
$buf = fread(STDIN, 16);
var_dump($buf);
This code successfully captures the response from the terminal into $buf.
$ php foo.php
^[[2;1Rstring(6) ""
However, this code has a couple of issues. First of all, it doesn't re-enable canonical mode in the terminal after it's finished. This could cause issues when trying to input from stdin later in your program, or in your shell after your program exits. Secondly, the response code from the terminal ^[[2;1R is still echoed to the terminal, which makes your program's output look messy when all you want to do is read this into a variable.
To solve the input echoing issue, we can add -echo to the stty arguments to disable input echoing in the terminal. To reset the terminal to its state before we changed it, we can call stty -g to output a list of current terminal settings which can be passed to stty later to reset the terminal.
<?php
// Save terminal settings.
$ttyprops = trim(`stty -g`);
// Disable canonical input and disable echo.
system('stty -icanon -echo');
echo "\033[6n";
$buf = fread(STDIN, 16);
// Restore terminal settings.
system("stty '$ttyprops'");
var_dump($buf);
Now when running the program, we don't see any junk displayed in the terminal:
$ php foo.php
string(6) ""
One last potential improvement we can make to this is to allow the program to be run when stdout is redirected to another process / file. This may or may not be necessary for your application, but currently, running php foo.php > /tmp/outfile will not work, as echo "\033[6n"; will write straight to the output file rather than to the terminal, leaving your program waiting for characters to be sent to stdin as the terminal was never sent any escape sequence so will not respond to it. A workaround for this is to write to /dev/tty instead of stdout as follows:
$term = fopen('/dev/tty', 'w');
fwrite($term, "\033[6n");
fclose($term); // Flush and close the file.
Putting this all together, and using bin2hex() rather than var_dump() to get a listing of characters in $buf, we get the following:
<?php
$ttyprops = trim(`stty -g`);
system('stty -icanon -echo');
$term = fopen('/dev/tty', 'w');
fwrite($term, "\033[6n");
fclose($term);
$buf = fread(STDIN, 16);
system("stty '$ttyprops'");
echo bin2hex($buf) . "\n";
We can see that the program works correctly as follows:
$ php foo.php > /tmp/outfile
$ cat /tmp/outfile
1b5b323b3152
$ xxd -p -r /tmp/outfile | xxd
00000000: 1b5b 323b 3152 .[2;1R
This shows that $buf contained ^[[2;1R, indicating the cursor was at row 2 and column 1 when its position was queried.
So now all that's left to do is to parse this string in PHP and extract the row and column separated by the semicolon. This can be done with a regex.
<?php
// Example response string.
$buf = "\033[123;456R";
$matches = [];
preg_match('/^\033\[(\d+);(\d+)R$/', $buf, $matches);
$row = intval($matches[1]);
$col = intval($matches[2]);
echo "Row: $row, Col: $col\n";
This gives the following output:
Row: 123, Col: 456
It's worth noting that all this code is only portable to Unix-like operating systems and ANSI/VT100-compatible terminals. This code may not work on Windows unless you run the program under Cygwin / MSYS2. I'd also recommend that you add some error handling to this code in case you don't get the response from the terminal that you expect for whatever reason.
(this is really a comment, but it's a bit long)
Using hard coded terminal sequences is a very long way from "portable". While most terminal emulators available currently will support ANSI, vt100 or xterm codes which have a common base there is a very well defined API for accessing interactive terminals known as "curses". A PHP extension is available in pecl. This is just a stub interface to the curses system - present on any Unix/Linux system. While it is possible to set this up on mswindows, using cygwin or pdcurses, it's not an easy fit. You omitted to mention what OS you are working on. (The mswindows console uses ANSI sequences)
There is a toolkit (hoa) based on termcap (predecessor to curses) which might be useful.
To "retrieve" the data you just need to read from stdin (although it would be advisable to uses non-blocking up for this).

PHP exec() function only runs extremely short Python scripts

I'm having some trouble using the PHP exec() function. Whenever the script I'm attempting to run is short, exec() works just fine. But if the script takes any more than a second or so, it fails. Note, I've attempted run the long script manually on the command line, and it works just fine. It seems as though the PHP interpreter is killing my external script if it takes any longer than a second or so to run. Any thoughts or suggestions? Here is my code:
<?php
$fileName = "foobar.docx";
$argVar = $fileName;
exec("python3 /var/www/html/jan8/alexandrina.py /var/www/html/jan8/$argVar");
echo "$output";
?>
And here is my script:
#!/usr/bin/env python3
import docx
import sys
docxFile = "".join(sys.argv[1:])
# The Three Lines Below Create a New Variable "htmlFile"
# "htmlFile" is the same as "docxFile", except ".docx" is cut off
# and replaced with ".html"
myNumber = len(docxFile) - 5
htmlFile = docxFile[0:myNumber]
htmlFile = htmlFile + '.html'
def generateHTML(filename):
doc = docx.Document(filename)
fullText = []
for para in doc.paragraphs:
fullText.append('<p>')
fullText.append(para.text)
fullText.append('</p>')
fullText.append('\n')
return '\n'.join(fullText)
file = open(htmlFile, "w")
file.write(generateHTML(docxFile))
file.close()
print("end python script")
Additional notes: I've increased the max execution time limits in php.ini, but I don't think that should matter, as the "long script" should only take a few seconds to run. Also, when I refer to the "short script", and the "long script", I'm actually referring to the same script. The difference between the "long script" and the "short script" is just the time to execute as it may vary depending on the size of the file I'm asking the script to process. Anyway... any suggestions would really be appreciated!
Ordinarily, php exec function should block until the command you run has completed. I.e., the PHP script will halt, waiting for the command to finish until continuing with the rest of your script. I was half thinking that your server was experiencing a max_execution_time timeout, but you've clearly stated that even just a couple of seconds is too long and even these fairly short scripts are having trouble.
A couple of solutions occur to me. The simplest one is to alter the python command so that a) any output is routed to a file or output stream and b) the process is run in the background. According to the docs on exec:
If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
I also would like you to make use of the two additional optional parameters for the exec function.
$fileName = "foobar.docx";
$argVar = $fileName;
$cmd = "python3 /var/www/html/jan8/alexandrina.py /var/www/html/jan8/$argVar";
// modify your command to toss output, background the process, and output the process id
$cmd_modified = $cmd . " >/dev/null & echo \$!";
$cmd_output = NULL; // this will be an array of output
$cmd_return_value = NULL; // this will be the return value of the script
exec($cmd_modified, $cmd_output, $cmd_return_value);
echo "exec has completed</br>";
echo "output:<br>" . print_r($cmd_output, TRUE) . "<br>";
echo "return value: " . print_r($cmd_return_value, TRUE);
This may help or may not. If it does not, we still might be able to solve the problem using posix commands.
EDIT: according to crispytx, the long scripts are resulting in a $cmd_return_val of 1 which means an error is happening. Try changing this one line:
$cmd_modified = $cmd . " >/dev/null & echo \$!";
to this
$cmd_modified = $cmd . " & echo \$!";
And let us know what the output of $cmd_output is -- it should at the very least have the process id of the newly spawned process.
Thanks for all the help S. Imp. I had a little trouble debugging using your suggestions because I happened to be using AJAX to call the script. However, I wrote simpler script using your suggestions to try and debug the problem and this is what I found:
Array ( [0] => Traceback (most recent call last): [1] => File "/var/www/html/jan8/alexandrina.py", line 28, in [2] => file.write(generateHTML(docxFile)) [3] => UnicodeEncodeError: 'ascii' codec can't encode character '\u2026' in position 25: ordinal not in range(128) )
So it looks like the problem has to do with ascii encoding! Even though the larger file was just a docx file with the same text as the shorter docx file repeated over and over again for 300 pages. It seems that if a docx file exceeds 1 pages, ascii characters are inserted that aren't present in single page docx files. I have no idea if this post will ever end up helping anyone, but who knows!
[SOLVED]

Executing python commands from php script

I have installed SymPi in the server and from the command line, I am able to execute the following.
python ./sympy-0.7.5/bin/isympy
(this will open a console where I can type mathematical expressions. then the following expression)
1 + 2
(will give 3 as output)
My aim is to do the same from php using shell_exec. I have written a php file as given below, but is not working.
$command = escapeshellcmd('python ./sympy-0.7.5/bin/isympy');
shell_exec($command);
$output = shell_exec('1 + 2');
Can anybody help me to figure out why this is not working?
Please note that the following script works fine which just execute a python script and retrieve the output.
$command = escapeshellcmd('python C:\PythonPrograms\test3.py');
$output = shell_exec($command);
echo $output;
My guess is that the working directory (cwd) of shell_exec is different from the one you're in when you execute it manually.
Your working example specifies a hard path that will work from anywhere. Whereas your not-working example specifies a relative path (./ is the cwd).
Convert your call to isympy to give its full path on disk. Or figure out how to set the cwd of shell_exec.
(If this doesn't solve it, say more than "is not working." What happens? An error? What is the full text of the error?)
Each time you run shell_exec, it opens a completely new instance of the shell.
Edit:
You can pass a command for python to execute like this:
$expression = '1 + 2';
$cmd = 'python -c \'print "%f" % (' . $expression . ')\'';
$output = shell_exec($cmd);
This, admittedly is not using sympy, but for simple mathmatical expressions you may not need to. If you do, you would just need to import the library in the same command, like this: python -c 'import sympy; print "%f" % sympy.sqrt(3)'
I could manage the desired result in a different way.
Created a python script which accepts the expression as the command line argument , execute and display the output.
Call this script from php by passing the expression as the command line argument.

Why stream_select on STDIN becomes blocking when cmd.exe loses focus?

Goal: run a PHP file in cmd, script loops x times and on every iteration checks to see if user has entered any input (stream_select() with STDIN) and if so - pauses the loop until the user hits enter, then prints out the input and continues with iteration.
Problem: Script runs perfectly as long as cmd.exe window is in focus - when I click on another window the script pauses at stream_select and doesn't continue until I but the cmd window back in focus and send it some input (a simple enter key press would do the trick). No errors.
Question: why does losing focus on cmd affect stream_select and block the loop? ...and is there a workaround? (e.g. is it possible to check if the current cmd window is in focus?)
Code example, used cmd php script.php in working directory.
<?php
$loopCount = 20;
while ($loopCount) {
$start = microtime(true);
echo 'check on "' . $loopCount . '"' . PHP_EOL;
$stream = fopen('php://stdin', 'r');
$stream_array = array($stream);
$write = array();
$except = array();
if (stream_select($stream_array, $write, $except, 1, 0)) {
$input = trim(fgets($stream));
if ($input) {
echo 'input was "' . $input . '"' . PHP_EOL;
}
}
fclose($stream);
echo $loopCount . ' in ' . (microtime(true) - $start) . PHP_EOL;
$loopCount--;
}
Things I have tried with no luck:
moving fopen and fclose outside the loop
ignore_user_abort(1);
stream_set_blocking($stream, 0);
null, 0 and higher values for both tv_sec and tv_usec params of stream_select()
checking for connection_aborted() and connection_status()
Environment: Windows 7, XAMPP for windows, PHP 5.4.19 (cli), Zend Engine v2.4.0
I think the problem is because stream_select() will also return 1 for STDIN when the stream receives EOL, which is what I think happens when cmd loses focus.
From http://www.php.net/manual/en/function.stream-select.php
in particular, a stream resource is also ready on end-of-file, in which case an fread() will return a zero length string
When focus is not lost and no input is given, stream_select returns 0 and the $read array passed in is emptied by reference. When focus is lost OR when input is given, the $read array remains intact and you are expected to read from each index.
But that leaves us window's users in a bad situation because we don't have a way to non-blockingly read that zero length string with stream_get_(contents|line) or fread or fgets. And since we cannot differentiate between losing focus and supplying actual data I believe we have to wait for one of these bugs to get fixed:
https://bugs.php.net/bug.php?id=36030
https://bugs.php.net/bug.php?id=34972
I'm currently exploring an option to prevent the window from losing focus via the win32 API, but that looks impractical for even my needs.
Here is the workaround I'm using for now...
https://gist.github.com/anonymous/80080060869f875e7214

How do I pipe rake output using php?

I'm building a RAKEFILE and I want to display the output on a php generated page as it gets executed.
I tried using system() since the PHP docs mention this:
The system() call also tries to automatically flush the web server's output buffer after each line of output if PHP is running as a server module.
This seems to work with multiple shell comands but when I execute rake I only get the first line:
(in /Users/path/to/proj)
Any ideas?
Cheers!
Try use exec() function
exec($command, $output);
$output is an array
//retrieved data
for($out = '',$x = 0,$len = count($output); $x < $len; $x++) {
$out .= $output[$x] . "\r\n";
}
or simple:
$out = join("\r\n", $output);
The system() call also tries to automatically flush the web server's output buffer after > each line of output if PHP is running as a server module.
This means you would only get the last line of output from the return value. The example in the system() manual page shows that and it suggests to use passthru() to get raw output. I usually use exec() though.
Turs out both functions system() & exec() actually work. The generated rake output when using --verbose isn't taken into consideration though. That's why I was confused. If anyone has more extensive knowledge on the distinction, do share :)

Categories