Creating a Yii Console Application

October 7, 2010
The Yii Book If you like my writing on the Yii framework, you'll love "The Yii Book"!

I’ve spent the past couple of weeks developing a search engine for a Yii-based site (my problems and solutions specific to the search engine will be addressed in a separate post). As you may know, a search engine is really two things:

  1. One tool for creating the site index
  2. Another tool for searching the index and displaying the results

For both, I used the Zend_Search_Lucene module within Yii (or, I think that’s the route I’ll end up using). And although I initially created a Controller method for generating the index (per the instructions I sketched out in this other post), indexing a lot of content through a Web request is less than ideal. This particular site probably only has a hundred or more database records and a couple dozen files (PDFs) to be indexed, but the process still took a minute or two. Even if the index is only created or updated once a day or once a week, that’s still a lot of Web server resources being tied up. A better solution is to use a command-line script to create the index. Since most of the indexing requires a database connection and some Model information, I didn’t want to separate the indexing script from the Yii-based site. Fortunately, Yii supports command-line scripts by creating a “console application”. As with other things in Yii that are less common, the documentation on console applications is meager, but it was enough for me to go on. Here’s what you need to know…The first thing you’ll need to do is create a new bootstrap file through which the command-line application will run. This is comparable to the index.php bootstrap that handles the Web requests. Here’s the entire code:

<?php

defined('YII_DEBUG') or define('YII_DEBUG',true);

// include Yii bootstrap file
require_once('yii/framework/yii.php');

// create application instance and run
$config=dirname(__FILE__).'/htdocs/protected/config/console.php';
Yii::createConsoleApplication($config)->run();

This is just a slight variation on what’s in the Yii documentation. Just like the Web bootstrap file, debugging is enabled, the framework is included, the configuration file is identified, and the application is started (using createConsoleApplication() instead of createWebApplication()).

Unlike the Web bootstrap file, this one does not have to be in the Web root directory. In fact, arguably, you shouldn’t put it there. I put mine in the directory above the Web root, so it can only be executed by someone on the server. Wherever you put it, you’ll need to change the path to the Yii framework and the configuration file to be correct for your situation. Speaking of the configuration file…

The default Web application created by the command-line yiic tool creates three configuration files:

  • main.php, used by the Web site
  • test.php, used for testing purposes (it also includes main.php)
  • console.php, intended for console application

The structure of console.php is the same as the other two, although it contains practically nothing when first generated. The quick and dirty solution is to use main.php for the console application, although doing so would be overkill and some configuration settings wouldn’t apply. Instead, your custom console.php configuration file could just include what’s absolutely necessary: the database connection information. You can copy the components>db section from main.php and paste it into console.php. You can find the full list of CConsoleApplication properties in the documentation, of course.

Once you’ve created the console bootstrap file and the configuration file, you’ll need to create separate files that represent the commands that will be executed (think of this like a command-line Controller). But first, let’s imagine a concrete example: Say you want to create a console application that will perform some maintenance on the database. By defining this functionality in a console application, you can then establish a cron task that executes the console application nightly or weekly. Let’s associate this process with the keyword “Maintain”.

The first thing you’ll do is create a new PHP file named MaintainCommand.php and store it in the protected/commands directory. The file needs to define a class named MaintainCommand that extends CConsoleCommand:

<?php
class MaintainCommand extends CConsoleCommand
{
    // Define attributes and methods!
}

The class must define one method, named run(), which takes an array as its lone argument:

public function run($args)
{
    // Do whatever!
}

Within the function you can do whatever needs to be done, including making use of the Yii application’s defined Models. You don’t render any views, though, as the command is being executed in the command-line environment. You can print simple messages, if you want, although if you plan on executing the script through a cron, no one would ever see those messages (but they can be useful for debugging purposes while you’re developing the code). As for the $args array…

To execute the command, which is the code within the run() function, use this syntax from the command-line interface:

php scriptname.php Command [arg1] [arg2]...

For example, if the bootstrap file is named yii_console.php and you have the above code, the command would be just:

php yii_console.php Maintain

However, you may commonly write shell scripts that do different things based upon different arguments. Maybe the maintenance would be performed on a specific table or list of tables, to be indicated when the command is run:

php yii_console.php Maintain table1 table2 table6

In this case, the $args array in the function would then have three elements with the values table1, table2, and table3. So you can use the arguments added to the command to change what the command does.

And that’s the basic premise. Pretty simple, once you know how it’s done. As I’ve already said, console applications are best for tasks that shouldn’t be executed in the Web browser or that might be executed on a schedule, in which case no one’s viewing the output. I hope this helps anyone needing this functionality and let me know if you have any questions, comments, or problems. In another post (hopefully soon), I’ll outline in detail how I used Zend_Search_Lucene to index and search a site.