For many years now, I’ve been applying a dubious definition of what it means to be an engineer. By my reckoning, an engineer is someone who uses a tool for something other than its originally intended purpose. While that isn’t always a good idea, when you get down to it all, invention and, indeed, most innovation come from using something in a way you hadn’t necessarily thought to use it before.

Imagine my surprise, then, when the idea hit me to use my old friend PHP, which has always been so reliable on Web pages, as a command-line tool. I’m hardly the first person to do this, but the idea was certainly new to me.

Of course, just the fact that you can use PHP on the command line isn’t necessarily the best reason for doing so. However, you might quickly find several pleasant surprises when you first begin to experiment with PHP in this way. To begin with, debugging existing scripts becomes far, far simpler than ever. Programs that consist primarily of output with very little logic are incredibly simple in PHP. Beyond that, you can use all your knowledge of PHP to make it accomplish tasks you would never have thought of it for before. In fact, there’s really nothing stopping you from using PHP as your Swiss Army knife for almost any given programming project.

Intrigued? Well, let’s get started and see what you can accomplish by using PHP on the command line.

Installation

Installation is straightforward to the point that it might not even be necessary to specifically install anything. Start out by trying a simple PHP script on the command line. You can use an existing PHP script, or you can try the code below. This example is in Linux®, but similar principles apply on other systems.

First, confirm the location of the PHP executable file — on most Linux systems, almost certainly /usr/bin/php. If you aren’t sure, type which php on the command line and see what the response is.

Second, type the code below, making sure to replace /usr/bin/php with the actual path to your PHP executable file.

#!/usr/bin/php -q Hello world Show more Show more icon

Save your file and make sure to mark it with executable permissions. On most systems, you would do this with chmod +x hello-world or something similar. Then, execute the PHP file (by running ./hello-world or, if necessary, php hello-world ) and see whether it runs. If it does, your installation of PHP includes command-line capabilities by default.

If the code didn’t run properly, things can get a bit hairy. There are a few different possibilities as to the cause. If you get a PHP-related error (other than something to the effect of “program not found”), then your problem is a typo in the code. If the PHP executable file isn’t found, make sure you used the proper path. If you don’t have an executable named PHP, you must get one.

Getting a PHP executable file might take different steps depending on which system you’re on, but it shouldn’t be too difficult to get the support you need. You might start by checking the documentation for your particular operating system or distribution.

Obviously, if you don’t have PHP installed on your system at this time, do that first and try the code above once more. Installing PHP is all you need for many systems. If you do need more, sometimes fixing this problem is as simple as using your favorite package-management tool (such as apt-get or yum ) to get the PHP CLI package. (The names may vary slightly.)

If you can’t find a command line in your PHP package-management tool, the worst-case scenario is that you can recompile PHP with the --enable-cli flag. In fact, doing so gives you the advantage of a slightly more optimized system and may not be a bad idea either way. One way or another, it shouldn’t be too hard to get where you need to go to get started.

By now, you should have got your HelloWorld script running, and the output is just what you probably guessed it would be. I’m not going to break down the workings of that script quite yet, but most of it will be familiar to those who work with shell scripting and PHP. Because this first script is now running with no problems (we hope!), we’ll take a little detour from true command-line interface (CLI) applications to see one of the great reasons why PHP on the command line is great for all PHP programmers: debugging.

PHP debugging

Maybe it’s just me, but I’ve often found that the debugging of CLI programs can border on the nightmarish, especially when dealing with HTML-embedded scripts, such as Microsoft® Active Server Pages (ASP) or PHP. It can often be difficult to distinguish what precisely is meant by a specific error message, user input can be difficult to reproduce, and the whole thing generally makes you want to tear out your hair.

Unfortunately, most of that isn’t particularly eased by using the command line, although a reasonably clever programmer could find ways in which the CLI could help with this (by reading input from a file or another program through a pipe, for example). However, the CLI does make one thing much, much easier: finding the error messages so you can read them in the first place.

To see the value of debugging using the command line, let’s start with the really, really bad PHP file shown below.

#!/usr/bin/php -q Don<'t>code<?php while drunk(); ?< Show more Show more icon

While some of the errors in this script are fairly obvious from glancing at the code, the code provides a superb example of less-than-useful debugging messages in the Web browser. Specifically, there aren’t any: Running this page in Apache simply results in no output at all. How can you find out what’s wrong if the bug isn’t obvious?

The traditional way of solving this problem is to look at the error logs. For example, you might run:

tail -f /var/log/httpd/error_log Show more Show more icon

before loading the page on a Linux system running Apache 2, which would result in output such as this:

[client 127.0.0.1] PHP Parse error: parse error, unexpected T_STRING, expecting '(' in /var/www/html/dont-code-drunk.php on line 2 Show more Show more icon

Unfortunately, you might agree with me in considering this solution less than ideal. For one thing, the file log locations might vary depending on your system. For another, you have to go through logs of unrelated events to find what you need, which can be a nightmare on a live server.

You’re probably thinking what I used to at this point: There must be a better way.

Locating and fixing errors using the CLI

The problem with the code can be easily isolated by running the script directly on the CLI. Run:

php dont-code-drunk.php Show more Show more icon

and you might end up with the following output:

PHP Parse error: parse error, unexpected T_STRING, expecting '(' in /var/www/html/dont-code-drunk.php on line 2 Content-type: text/html X-Powered-By: PHP/4.3.11 Show more Show more icon

Much better! The error message is isolated from other data, and you can easily recheck the page when you have made the change. In this case, it makes it clear that it is expecting a '(' somewhere on the line. The while statement seems like a reasonable place for this, so you add in a new set of parentheses to get the following source.

#!/usr/bin/php -q Don<'t>code<?php while (drunk()); ?< Show more Show more icon

Now, rerunning the code results in the following output:

Listing 1. Rerun results

Content-type: text/html X-Powered-By: PHP/4.3.11 <'t>codePHP Fatal error: Call to undefined function: drunk() in /var/www/html/dont-code-drunk.php on line 2 Show more Show more icon

The script is still full of problems, but you’ve made some progress. You’ve picked out the error message that you needed with comparative ease, allowing you to quickly fix the problem in the program and move on to the next problem.

Anyone who uses PHP at all can make good use of CLI PHP for debugging in this way. But why not stretch yourself a bit further and start making true shell scripts in PHP? Forge ahead, and you’ll see some of the possibilities of using PHP for simple — or not-so-simple — shell scripting.

PHP I/O channels

Make the initial goal for your first PHP script a simple one: create a script that reads in a file and shuffles the lines in that file. This functionality can be handy if you’re shuffling an m3u file or something similar. Doing so means you must be able to read from files or standard input and write back to the terminal.

This raises our first general challenge in PHP. PHP wasn’t originally designed to be used with direct keyboard input or text output to the user. Understanding this design is critical because if you want to get anything done on the command line, you must be able to communicate back and forth to the user. In traditional programming languages such as C , you would use STDIN , STDOUT , and STDERR to accomplish this. You can use the same channels in PHP for input, standard output, and output to the error channel, respectively.

STDOUT: echo, print, STDOUT, and php://stdout

Even though PHP was designed for output to a browser, rather than a CLI, creating output from PHP is so straightforward that it takes little time to consider. Remember that anything given outside a PHP tag will be output directly to the CLI, which is why the HelloWorld program above was so simple. This is also why Don<'t>code outputted above before the error message came out. It doesn’t matter whether you wrap the words in HTML tags; it will be displayed either way. In fact, you generally want to avoid HTML tags because they print directly to the user.

You can use basic functions for output, as well. For example, the echo and the print command print to standard output.

#!/usr/bin/php -q Output #1. <?php echo "Output #2."; print "Output #3."?> Show more Show more icon

This results in:

Output #1. Output #2.Output #3. Show more Show more icon

Note that the newline outside the PHP tags is output, but there is no implied newline in the echo command or the print command. In fact, the command prompt reappears on the same line as Output #2.Output #3. . Any other printing functions PHP has will work equally well for this, as will any functions that write to files.

#!/usr/bin/php -q <?php $STDOUT = fopen("php://stdout", "w"); fwrite($STDOUT, "Output #1."); fclose($STDOUT); ?> Show more Show more icon

The above code explicitly opens php://stdout as an output channel, and php://output generally acts the same way as php://stdout . Recent versions of PHP can use STDOUT as a constant instead of defining the variable $STDOUT used above.

STDERR: STDERR and php://stderr

STDERR closely parallels STDOUT . All the techniques you will use to write to this channel mirror those of STDOUT , the only difference being that you open php://stderr instead of php://stdout or php://error .

STDIN: STDIN and php://stdin

STDIN is the most interesting change from Web programming because it opens you up to true user input without using forms or other browser-based methods. Try the following command:

#!/usr/bin/php -q <?php $file = file_get_contents("php://stdin", "r"); echo $file; ?> Show more Show more icon

This code should act much like cat , echoing back all input given to it. However, it will not accept arguments at this time.

Your first PHP shell script

OK — here’s where things get interesting. Taking the simple knowledge you’ve gained thus far, you can make a simple yet useful shell script. Type the code below into your text editor.

Listing 2. randomize-lines

#!/usr/bin/php -q <?php $lines = split("

", file_get_contents("php://stdin", "r")); shuffle($lines); foreach ($lines as $line) { if ($line !== "") { echo "$line

"; } } ?> Show more Show more icon

Now, just a few quick checks to make this script work:

Make sure the hashbang (the first line, which starts with #! ) is set to the location of the PHP executable file as described earlier Save the file Use chmod to add executable permissions Run the program

Note that randomize-lines does just what you would expect: It shuffles the lines of input you type in and spits them back out in a different order. This functionality can fill a valuable gap in your shell script library.

As one example of an application for this script, you can use it to generate a random playlist for a music or video player on the fly. For example, to shuffle your XMMS playlist, try:

./randomize-lines < .xmms/xmms.m3u > temp mv temp .xmms/xmms.m3u Show more Show more icon

Now, take it up one more notch.

Command-line arguments

Real command-line programs use arguments. Again, just like C and other similar languages, you can use argv and argc for this purpose. In particular, argv is an array of arguments to the program, the first argument being the program itself. Using this, it wouldn’t be difficult to build a program that read from files or user input depending on the arguments given. For example, see the code below.

Listing 3. randomize-lines-w-args

#!/usr/bin/php -q <?php array_shift($argv); if (count($argv) == 0) { $argv[0] = "php://stdin"; } foreach ($argv as $file) { $lines = split("

", file_get_contents($file, "r")); shuffle($lines); foreach ($lines as $line) { if ($line !== "") { echo "$line

"; } } } ?> Show more Show more icon

And there you have it: a fully functioning CLI PHP program that accepts either user input or a list of files and randomizes the relative contents of each file.

Conclusion

Pick the right tool for the job, but remember that the best tool is often not the one you would expect. Give PHP a chance on the command-line interface, and you may find that it has become your new favorite shell-scripting tool. Worst-case scenario: It can save you some Web server migraines.