ucantblamem

Using command line tools to leverage your existing PHP application with exec()

16th Nov 2007

A short time after jumping into PHP I soon learned the value of php.net for learning about functions and how to make the language work for me in general. Unfortunately, on occasion, the manuals leave out some vital information that newbies really need to know in order to make something work. Quite often this is just general programming techniques or knowledge of computing; one such oversight caught my attention recently, so I thought I’d fill in the blanks.

First and foremost, PHP is a scripting language. In a practical sense that means that writing software is fairly quick and easy; you just write the code into a file with the extension *.php and shove it on a web-server that supports the language; Done! The cons of a scripting language is usually that to do low-level stuff, the kind of work that requires heavy lifting, isn’t so easy and, in general, scripting languages are quite a bit slower than a compiled language like C.

To illustrate this we’ll use a real world scenario that I ran into recently:

I am currently doing a lot of work on the Musicadium site, to enable independent artists to upload their music (as MP3 files, etc). The stores we supply to, iTunes, Emusic, Amazon, etc all have very specific file types that they accept, so we have to make sure we cross-code the files to the correct format. Now if I was motivated enough to go and learn the specifications for these file-types I probably could write cross-coding utilities in PHP, but as I said earlier: 1) PHP would make this process very slow and 2) There are already some great tools available for taking care of this kind of thing, so building PHP-based tools would be a waste of time.

Emusic require us to use the LAME encoder to produce a very specific type of MP3, which is available as a command-line tool on linux-based servers (which about 90% of the net runs on). This particular tool requires you to compile it yourself, which I won’t go into in this article, but once installed you can use it via the terminal like so:

lame myoriginalwavefile.wav mynewfile.mp3

There are a number of options which you can use in lame and in our scenario we need to run it like this:

lame --silent --id3v1-only -mj --preset standard -v -q 2 mywavefile.wav mynewfile.mp3

There are a number of ways to run command-line tools from PHP, but my personal preference (and what I found would work best in this scenario) is the exec() function.

As php.net points out, exec() has three parameters:

  1. The command you want to run (string)
  2. The output of the command (array)
  3. The return value (int)

This is where I need to digress for a moment, because if you don’t understand how command-line tools work, you probably won’t understand the difference between the output and return values.

All command line tools return an integer value. This will be 0 [read: zero] if it runs successfully and any other positive number if it had an error, these values usually relate to the error code, which you can trace back to a particular issue. For instance, 1 might be the error code for “No input file specified” - it depends on the application. The one thing you can definitely depend upon though, is that if the return value is more than zero, then you have an issue.

The other thing I need to explain a little is that command line tools (and desktop applications in general) don’t just have a return value. They also have access to a number of streams where they can output other types of information. For instance, in a command line tool there are STDIN, STDOUT and STDERR streams. I will allow Wikipedia the privilege of explaining these in more detail, but for the purpose of this post, understand that all the text that a command line tools spits out when it’s run isn’t a return value, but information that has been sent to one of these streams.

So, while you just see information in the terminal (whether it be from STDOUT or STDERR), you can in-fact differentiate between these.

So, back to exec(). The following is some PHP code that runs a lame process and deals with any errors accordingly:

$input_file = 'myinputfile.wav';
$output_file = 'myoutputfile.mp3';$return_var = 0;
$output = array();

exec('lame --silent --id3v1-only -mj --preset standard -v -q 2 '.$input_file.' '.$output_file.' 2>&1',
 $output, $return_var);

if ($return_var) {
	echo "We encountered an error: \\n\\n".implode("\\n", $output);
} else {
	echo $output_file." was successfully encoded from ".$input_file."\\n\\n".implode("\\n", $output);
}

To someone who deals with PHP / programming in general fairly often, you should be able to read it through and make sense of most of it.

One subtitle I really want to make is that you’ll notice I made a little addition to the end of our command in the exec() function: “2>&1″, this is a little trick that a few people have pointed out on php.net (read the comments at the bottom of the page). Basically $output will contain just the information sent over the STDOUT stream by default, so in order for us to capture the STDERR (error) output we can put that little extra in there, which essentially throws all the STDERR output into the STDOUT stream for us.

It’s also very important to know that the exec() function is not available under PHP’s “safe mode” and it is rarely available in a shared-hosting environment. To use it, you really need to get a dedicated box or something similar to Media Temple’s Grid-Service.

Scripting languages like PHP make it very easy for us simple folk to get things done. They keep us from delving into the under-belly of a computer and a number of layers away from the CPU itself. Unfortunately, in order to get things done, we need to head into the basement and fire up the ‘ol command line, but as they say “with great power, comes great responsibility” and you should understand how something works before you use it. So, I hope this little post gives you just enough information about command line tools that you are able to use them efficiently without getting frustrated when things don’t quite work the way you would expect if you were just using PHP.

Leave a Reply