Chapter 19. Command-Line PHP

Usually, the PHP engine is invoked by a web server in response to a request from a web client. However, the PHP engine can also be run as a command-line utility on your computer. If you’ve been running all of the code examples in the book so far, you’ve run PHP as a command-line program when you used PHPUnit and Composer.

Writing a PHP program intended for use on the command line is a little different than writing a PHP program intended for use in a website. You have access to all the same functions for string manipulation, JSON and XML handling, working with files, and so forth, but there’s no incoming form or URL data. Instead, you get information from command-line arguments. The standard print statement prints data to the console. The next section, “Writing Command-Line PHP Programs”, shows you the basics of writing a command-line PHP program.

The PHP engine also comes with a mini web server that you can invoke by running PHP on the command line. “Using PHP’s Built-in Web Server” explains how this works. The built-in web server is handy for quick testing.

One other handy use for PHP on the command line is as an interactive shell, otherwise known as a Read-Eval-Print Loop (REPL). This is a program that gives you a prompt to type in some PHP code, and then runs that PHP code and tells you the results. For exploration of how a PHP function works and quick gratification, nothing beats a REPL. “Running a PHP REPL” explains PHP’s built-in REPL, and provides information about one others.

Writing Command-Line PHP Programs

A simple PHP program that outputs data works fine from the command line. Consider Example 19-1, which uses the Yahoo! Weather API to print out the current weather conditions for a zip code.

Example 19-1. Finding the weather
// Zip code to look up weather for
$zip = "98052";

// YQL query to find the weather
// See https://developer.yahoo.com/weather/ for more info
$yql = 'select item.condition from weather.forecast where woeid in '.
       '(select woeid from geo.places(1) where text="'.$zip.'")';

// The params that the Yahoo! YQL query endpoint expects
$params = array("q" => $yql,
                "format" => "json",
                "env" => "store://datatables.org/alltableswithkeys");

// Build the YQL URL, appending the query parameters
$url = "https://query.yahooapis.com/v1/public/yql?" . http_build_query($params);
// Make the request
$response = file_get_contents($url);
// Decode the response as JSON
$json = json_decode($response);
// Select the object in the nested JSON response that contains the info
$conditions = $json->query->results->channel->item->condition;
// Print out the weather
print "At {$conditions->date} it is {$conditions->temp} degrees " .
      "and {$conditions->text} in $zip\n";

If you save Example 19-1 in a file called weather.php, you can run it with a command such as php weather.php and be told the current weather. But it’s only accurate if you want the weather for zip code 98052. Otherwise you have to edit the file. This is not very useful. It would be better if you could provide a zip code as an argument to the program when you run it. Example 19-2 is an updated version of the program that looks in the $_SERVER['argv'] array for command-line arguments. The command-line version of the PHP engine automatically populates this array with provided arguments.

Example 19-2. Accessing command-line arguments
// Zip code to look up weather for
if (isset($_SERVER['argv'][1])) {
    $zip = $_SERVER['argv'][1];
} else {
    print "Please specify a zip code.\n";
    exit();
}

// YQL query to find the weather
// See https://developer.yahoo.com/weather/ for more info
$yql = 'select item.condition from weather.forecast where woeid in ' .
       '(select woeid from geo.places(1) where text="'.$zip.'")';

// The params that the Yahoo! YQL query endpoint expects
$params = array("q" => $yql,
                "format" => "json",
                "env" => "store://datatables.org/alltableswithkeys");

// Build the YQL URL, appending the query parameters
$url = "https://query.yahooapis.com/v1/public/yql?" . http_build_query($params);
// Make the request
$response = file_get_contents($url);
// Decode the response as JSON
$json = json_decode($response);
// Select the object in the nested JSON response that contains the info
$conditions = $json->query->results->channel->item->condition;
// Print out the weather
print "At {$conditions->date} it is {$conditions->temp} degrees " .
      "and {$conditions->text} in $zip\n";

Assuming you save Example 19-2 in weather2.php, you can run php weather2 19096 to get the weather for zip code 19096.

Note that the first argument is at $_SERVER['argv'][1], even though, as mentioned in “Creating a Numeric Array”, PHP arrays start with index 0. This is because $_SERVER['argv'][0] contains the name of the program you’ve run. In the case of running php weather2.php 19096, $_SERVER['argv'][0] is weather2.php.

Using PHP’s Built-in Web Server

If you want to check out how some PHP code you’re writing behaves with real requests from a web browser, a quick way to dive in is to use the PHP engine’s built-in web server.

Tip

The built-in web server is available in PHP 5.4.0 and later.

Run php with a -S argument that provides a hostname and port number, and you’ve got a running web server providing access to the files in whatever directory you ran php in. For example, to run the web server on port 8000 of your local machine, run php -S localhost:8000. With that server running, visiting http://localhost:8000/pizza.php causes the web server to execute the code in pizza.php and send the results back to your web browser.

If the web server is not finding files that you think it should, check what directory you were in when you ran the php -S command. By default, the PHP web server serves up files in the directory that you ran php -S in (and below). To provide an alternate document root directory, add a -t argument. For example, php -S localhost:8000 -t /home/mario/web serves up the files under /home/mario/web at http://localhost:8000.

The PHP web server doesn’t do anything fancy to map URLs to files. It just looks for a filename under its base directory as specified in the URL. If you leave a filename out of the URL, it looks for index.php and index.html before returning a “file not found” error.

The built-in web server only handles one request at a time. It is best for testing functionality and experiments on a development machine. It’s much easier to get up and running than a big Apache or nginx installation that you’d need to configure, but it also is not as full-featured. When it’s time to deploy your code to a production environment, use a web server that can handle the scale and security requirements of general usage.

Running a PHP REPL

The built-in web server is a great way to quickly see your PHP code in action. Another handy tool for exploration and testing is the PHP REPL. Run php -a and you get a php > prompt at which you can type in some PHP code and immediately see the results, as shown in Example 19-3.

Example 19-3. Using the PHP REPL
% php -a
Interactive shell

php > print strlen("mushrooms");
9
php > $releases = simplexml_load_file("https://secure.php.net/releases/feed.php");
php > print $releases->entry[0]->title;
PHP 7.0.5 released!
php >

In Example 19-3, the initial % is a Unix shell prompt and php -a is what you type to run the PHP REPL. The REPL then prints Interactive shell and a php > prompt. It executes what you type when you press the Return key and prints any results. Typing print strlen("mushrooms"); (and then Return) tells the REPL to run strlen("mushrooms") and pass the results to print, so it prints 9. Don’t forget the trailing ;—PHP code typed into the REPL follows the same syntax rules as PHP code you write in a regular program.

If you just typed strlen("mushrooms"); the code would execute without any errors, but you wouldn’t see any output before the next php > prompt. The PHP REPL only displays something if the PHP code you enter creates output.

The REPL remembers variables between commands. Entering $releases = simplexml_load_file("https://secure.php.net/releases/feed.php"); uses the simplexml_load_file() function to retrieve the XML from the provided URL and store the results, as a SimpleXML object, in $releases.1 SimpleXML provides a hierarchy of objects corresponding to the structure of the returned XML, so the value of the title element under the first entry element under the top-level XML element is $releases->entry[0]->title. When the code in Example 19-3 was run, the first element in the releases feed was PHP 7.0.5.

There are other REPLs aside from the built-in one. A nifty example is PsySH. You can install it with Composer: php composer.phar global require psy/psysh.

That global before require tells Composer to install PsySH not in any package-specific directory but in a systemwide Composer directory. On OS X and Linux, this is the .composer directory under your home directory. On Windows, it’s AppData\Roaming\Composer under your home directory. For example, if you log in to your computer with username squidsy, then the Composer directory is /Users/squidsy/.composer on OS X, /home/squidsy/.composer on Linux, and C:\Users\squidsy\AppData\Roaming\Composer on Windows.

The actual psysh program is put in a vendor/bin directory under the Composer directory. So, to run it from the command line, you either need to type out the full path (e.g., /Users/squidsy/.composer/vendor/bin/psysh) or add that vendor/bin directory to your system’s $PATH, the default set of directories it looks in for program names that you type.

Once you run psysh, you get a prompt at which you can type in some PHP code. Unlike the built-in REPL, it prints the value a statement evaluates to (even if you don’t include a print command) and uses different colors of text for different kinds of variables.

Chapter Summary

This chapter covered:

  • Running from the command line a PHP program you’ve written
  • Accessing command-line arguments from a PHP program
  • Running your PHP programs via the built-in web server
  • Executing commands in the built-in PHP REPL
  • Installing PsySH and running it from the global Composer directory

1 SimpleXML is a speedy way of doing XML processing in PHP.