This article is also available as a TechRepublic download, which includes code Listings A-H in a more manageable text file format.

No one, not even its harshest critics, will dispute that PHP is versatile: at last count, the language had over 1500 built-in functions and more than 150 add-on extensions. However, the creators of PHP, smart people that they are, knew that this wasn't going to be enough to satisfy everyone. So they added an extra degree of flexibility -- allowing developers to launch their own external programs from within PHP and inject the resulting output back into a PHP application -- all without breaking a sweat!

This document introduces the PHP functions that allow you to launch external programs, shows you how to capture the output or use the result code in your PHP script, and alerts you to potential vulnerabilities in the process.

The backtick operator

If you've used the Bash shell in *NIX, you'll already be familiar with the backtick operator (`), which can be used to run an external program from within a Bash script. PHP uses exactly the same technique: simply enclose the command line to your external program within backticks inside a PHP script, and PHP will launch the external program when it reaches that line of code. Listing A shows you how.

Listing A


<?php
`ls -l`;
?>

Of course, this is fairly useless by itself; in most cases, you will want to import the output of the external program into your PHP script for further processing. Fortunately, that's simple -- all you do is treat the backtick-enclosed code as a regular PHP variable and display it using echo() or print(). (Figure B)

Listing B


<?php
echo "Current date and time is: " . `date`;
?>

In this case, the output of the system's date command is captured by PHP and interpolated into a string using echo(). The output would look like this:

Current date and time is: Wed Jun 21 04:27:01 CDT 2006

If you'd prefer, you can also capture and store the output of an external command, simply by assigning the backtick-enclosed command to a variable. (Figure C)

Listing C


<?php
$date = `date`;
echo "Current date and time is: $date";
?>

And here's the output:

Current date and time is: Wed Jun 21 04:27:01 CDT 2006

The exec() and passthru() functions

The backtick operator is disabled when PHP's safe mode is enabled, which poses a problem if you're running your application on a shared host which offers restricted control over PHP configuration. In such cases, you have no choice but to reach for two built-in PHP functions, exec() and passthru(), which provide similar functionality.

The exec() function accepts a single mandatory argument containing the command to be executed, runs it, and returns the last line of output. (Listing D)

Listing D


<?php
echo exec('ls -l');
?>

And the output is:

drwxr-xr-x 5 user cust 512 Jan 27 2005 vhost

In most cases, merely retrieving the last line of the command will be insufficient, which is why exec() comes with two optional parameters. The first of these is an array, which gets filled with every line of output resulting from the command; the second is a variable which holds the status code returned by the command.

Listing E shows you an example.

Listing E


<?php
$data = array();                // define array

exec('ls -l', $data, $ret);     // execute command, output is array

echo "<pre>";
if ($ret == 0) {                // check status code. if successful
    foreach ($data as $line) {  // process array line by line
        echo "$line \n";
    }
} else {
    echo "Error in command";    // if unsuccessful display error
}
echo "</pre>";
?>

This seems complex, but is actually quite simple. First, an empty array $data is defined, and the exec() command is called to obtain a directory listing. The return code of the command, indicating whether or not it was successful, is then stored in $ret, and the output of the listing (if any) is stored in the previously-defined array $data. Next, the return code is checked and, if zero (indicating success), a foreach() loop is used to process the array and display the directory listing. If the return code is non-zero, one may assume that an error occurred; the array processing is skipped and an error message displayed instead.

Listing F shows you what the output might look like.

Listing F


total 90
-rw-r--r--   1 user  cust         611 Jul 17  2004 CHANGE
drwxr-xr-x   2 user  cust         512 Jul 27  2004 cgi
drwxr-xr-x   5 user  cust         512 Jan 27  2005 vhost

The passthru() function is similar to exec(), except that it directly returns the raw output to the browser when invoked. Typically, this is only useful when the output of your external command is raw binary data which needs to be piped directly to a decoder -- for example, an audio stream or image. Listing G is an example illustrating how passthru() works.

Listing G


<?php
header("Content-Type: audio/wav");    // send MIME headers
passthru("cat hello.wav");            // send binary data
?>

Potential vulnerabilities

Executing external commands through a PHP script is always a risky proposition. If your application allows user input, there is always the danger that a user will trick the system into executing harmful commands. To avoid this, there are two options available:

1. Escaping Command Strings

PHP provides the escapeshellcmd() and escapeshellarg() functions, which "sanitize" command strings and arguments by escaping them with quotes and backslashes. It's a good idea to run these commands on any user input before passing them to exec(), passthru() or the backtick operator. (Listing H)

Listing H


<?php
$cmd = "ls / -als & rm -rf /";
exec(escapeshellcmd($cmd));
?>

2. Using Safe Mode

Another option is to enable PHP's "safe mode", to restrict what users can actually do with exec() and passthru(). The backtick operator is disabled in safe mode, while exec() and passthru() can only be used to execute commands located in a pre-defined "safe" directory. This provides a clear boundary around these commands and reduces the total risk associated with using them in a script.

Hopefully, you should now have a good idea of how you can launch external programs from within PHP, as well as some awareness of how to protect your application from hackers when using these capabilities. Go on and try them out for yourself...and happy coding!