 | Level: Intermediate Duane O'Brien (d@duaneobrien.com), PHP developer, Freelance
19 Feb 2008 A short few years ago, a common criticism about PHP was that it
did not support MVC-style architectures. Today, developers can chose from many
PHP frameworks. This "PHP
frameworks" series takes a look at three widely used PHP frameworks — Zend,
symfony, and CakePHP — examining their similarities and differences while
building and extending a sample application in each of the three. In this article, you will integrate external tasks,
creating a simple task that can be called using the scheduler cron.
About this series
This series is designed for PHP developers who want to start using a framework, but
have not examined the available frameworks in detail. This series examines why the
three frameworks under examination were chosen, how to install each,
and you'll get a good handle on the test application that you're going to extend in
each framework. It might sound like a lot, but don't worry — we break it down
into manageable chunks.
Part 1 of this series lays out the scope for the series, introduces the frameworks
being examined, covers their installation, and scopes out the first test application you will build. (Phew!)
Part 2 walks you through building the sample
application in each of the three frameworks, highlighting their similarities and differences.
Part 3 starts with extending the test application, then deals with exceptions to the
rule. All frameworks work well when doing the tasks for which they were designed.
Needing to do something the framework wasn't built to do happens on every project. Part
3 looks at such instances.
Part 4 focuses primarily on Ajax support. The use of Ajax, using native code and
third-party libraries, is examined — specifically, how each framework
behaves and accepts specific popular libraries.
Part 5 deals with working outside the frameworks. A single task is identified (nightly
update script), and the process for accomplishing this task is examined in each
framework.
About this article
Whenever an application interfaces with a database, it's not uncommon to find yourself
needing to set up some automated tasks. Typically, these tasks are basic database
management-oriented, such as table pruning, expiring accounts, etc. Historically
speaking, it's common for these tasks to be written in Perl. While Perl is an
excellent language, unless it is bringing something to the table that you don't already
get from PHP, it makes more sense to write your automated tasks and your application
with one language. It's easier to support, you can share code, and you don't get your
syntax confused when switching back and forth.
In that spirit, since you've written Blahg in a framework, it would be awfully nice if
you could continue to use the framework to execute common automated tasks. You can use
the common structure that is already established, you can use methods you have already
created, and you maintain that database independence you get from using a model instead
of writing database interface code for a specific database. If you are going to use a
framework, it makes sense to use it across the board.
In this article, you will look at writing an automated task for Blahg that prunes any
posts older than 30 days. You will be able to call this task from the command line,
enabling you to automate the task using a scheduler like cron. You'll build the
application in each framework, getting a sense for how Zend, symfony, and CakePHP handle automated tasks differently.
You should have already gone through Parts 1-4, which cover installation,
prerequisites, the initial application build and extension, and Ajax for each
framework. If you haven't, you should do so now.
Note: Each article includes a ZIP file containing all the code built and
discussed for the article. This file will be referred to throughout the article series as the code archive.
Let's get this out of the way now
Given the structure of the frameworks, there is a short path to creating an automated
task that you can call from cron: Make the task an action in an existing (or new)
controller and call the action using wget. There's absolutely no reason this will not
work, and if you are looking for a down-and-dirty and, above all, insecure way of
executing tasks, using wget would probably be fine.
But the word "insecure" in that sentence should raise some flags. By writing your
automated tasks this way, you make them available to the world. Even if you don't link
to the action in any way, use a controller named Mxyzptlk, or require a 40-character
unique string that must be passed to the action before execution, the action is still
available for the world to execute. Security through obscurity is no security at all.
Since many automated tasks can tend to be database-intensive, you do not want these actions executed at random.
So, while there is a short path to automating your tasks you are ideally looking for
a more secure approach. Each example below will show you one way to do things for each
framework. There is always more than one way to do things. Once you get through the
article, you may want to experiment on your own or do some additional research. See how other people have done it.
Also, do yourself a favor: back up your tables before you proceed. You will be
deleting some records, and it will be helpful to be able to restore the tables to a default state for later testing.
Enough prologue. Let's dive in.
External tasks in the Zend Framework
Due to the pick-and-choose nature of the Zend Framework, creating an automated task
using the framework allows for some flexibility in deciding what you use and don't use.
Because you are not going to be accessing the Web application through the existing
front controller, which services Web requests, you should create an additional
controller to control script execution. This controller will look similar to the front
controller, in that you will be registering the autoloader and defining the base
adapter for the model. But an important difference may be in your PHP installation or configuration.
Compared to invoking PHP from the Web server, command-line PHP typically is executed
using a different Server Application Programming Interface (SAPI). Depending on your
installation and configuration, this may mean that the include path is different. For
this reason, you should set the include_path ini setting to include the /column/include
directory where you installed the Zend Framework files.
Note: Remember that the way you define the include_path
will be different depending on your OS. The example below is for Linux®.
Create a new directory called /column/protected/zend/scripts. This directory will be
used to hold your scripts controller and any additional script-related files you
create. Since you are only creating one task at this time, start by creating the file
prune.php in the new scripts directory. The top half of this file will contain the
primary code for your scripts controller.
Listing 1. Code for your scripts controller
<?php
ini_set('include_path', ini_get('include_path') . ':/column/include');
require_once("Zend/Loader.php");
Zend_Loader::registerAutoload();
$params = array(
'host' => 'localhost',
'username' => 'frameworks',
'password' => 'fwpw',
'dbname' => 'zend'
);
$db = Zend_Db::factory('PDO_MYSQL', $params);
Zend_Db_Table::setDefaultAdapter($db);
|
The rest of this file will contain the simple task you want to execute. Include
and instantiate the posts model, create a where clause and
run a standard delete query.
Listing 2. Task to execute
require_once("/column/protected/zend/models/Posts.php");
$posts = new Posts();
$where = $posts->getAdapter()->quoteInto('modified < ?',
date('Y-m-d H:i:s', strtotime('-30 days')));
$posts->delete($where);
?>
|
Once this is complete, you can execute this script at the command line: php /column/protected/zend/scripts/prune.php.
If you can call it from the command line, you can schedule it, batch it, or cron it. A
crontab entry to execute this script at midnight could look like this: 00 00 * * * php /column/protected/zend/scripts/prune.php.
Once you've built your scripts controller, you have a whole host of options for
executing scripts using the Zend Framework. You can use the same models the rest of
your application uses, create complex script objects where required, and more.
Now you have a good handle on how to do a cron job for Zend. Next, let's look at doing
the same thing in symfony.
External tasks in symfony
When you wanted to execute a command-line script in the Zend Framework, you created a
scripts controller that would be used instead of your front controller. You will do much the same thing with symfony.
Create a directory to hold your symfony Blahg scripts —
/column/protected/sf_column/apps/blahg/scripts/ — and create a file called
prune.php to be your new scripts controller. The first half of this file will look much
like the symfony front controller you created. You will define the required constants
and require the config file. The difference in this case is in how you get the symfony
instance. In the front controller, you told symfony to look for the controller and
dispatch the request. For the script controller, this is not the case.
Listing 3. Defining the required constants and requiring the config file
<?php
define('SF_ROOT_DIR', '/column/protected/sf_column');
define('SF_APP', 'blahg');
define('SF_ENVIRONMENT', 'prod');
define('SF_DEBUG', false);
require_once(SF_ROOT_DIR.DIRECTORY_SEPARATOR.'apps'.DIRECTORY_SEPARATOR
.SF_APP.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'config.php');
sfContext::getInstance();
|
Next, you'll create a basic Criteria to pass to the post
model and run the delete.
Listing 4. Creating the basic Criteria
$c = new Criteria();
$c->add(PostPeer::MODIFIED, date("Y-m-d H:i:s",
strtotime("-45 days")), Criteria::LESS_THAN);
$posts = PostPeer::doDelete($c);
|
Save the file. That's all there is to it. Now you can execute the script at the
command line, much like you did in the Zend Framework: php /column/protected/sf_column/apps/blahg/scripts/prune.php.
Calling this task from a scheduler would look just like it did for the Zend Framework
(specifying the symfony script rather than the Zend script, of course). For cron, it
would look like this: 00 00 * * * php /column/protected/sf_column/apps/blahg/scripts/prune.php.
Finally, let's look at how you would do the same task in CakePHP.
External tasks in CakePHP
The Cake console is a new feature in CakePHP V1.2 that provides a command-line
interface to the Cake framework. To create your own command-line tasks, you create
what is known as a shell. A shell looks much like the controllers you already created.
Start by creating the file prune.php in the
/column/protected/cakephp/app/vendors/shells directory. This is your new shell called
prune and will be used to delete any posts older than 30 days. Define a new class,
PruneShells, that extends the shell class. Since you are
going to be deleting posts, the shell will need to use the post model, which you can
specify using the $uses variable.
By default, when you tell Cake to execute a shell, and you pass no specific action,
Cake looks for a method called main and, if found, executes
the method. At this point, the empty shell would look like Listing 5.
Listing 5. Defining a new PruneShells class
<?php
class PruneShell extends Shell {
var $uses = array('Post');
function main() {
}
}
?>
|
Now all you need to do is add code to the main method to delete any posts older than 30 days.
Listing 6. Deleting posts older than 30 days
$conditions = array (
"Post.modified" => "< " . date("Y-m-d H:i:s", strtotime("-30 days"))
);
$this->Post->deleteAll($conditions);
|
To execute this script from the command line, you tell Cake that you want to run
the prune shell. Since all the code is in the main method,
it will execute by default. You should also tell Cake what the app directory is. This
isn't necessary if you are running the command from the app directory, but a cron job
won't be executing from the right directory unless you tell it to:
/column/protected/cakephp/cake/console/cake prune -app /column/protected/cakephp/app/.
Scheduling a cron job to run at midnight that executes this shell would look like this:
00 00 * * * /column/protected/cakephp/cake/console/cake prune -app /column/protected/cakephp/app/.
Note: If you add the /column/protected/cakephp/cake/console directory to your
PATH, you don't have to specify the full path, which makes
working with the console much easier. Specifying the full path in your batch or cron
job will help ensure proper execution regardless of the user that executes the job.
Now that you can call it from the command line, you know you can batch it, cron it, or schedule it.
Summary
Writing automated tasks for your application has obvious benefits. You should be able
to write these automated tasks in the framework of your choosing. By writing your
tasks using your framework, you apply a consistent approach throughout your code and
take advantage of the existing structure you already have in place.
As you may have noticed while testing out the prune scripts, comments and ranks
associated with the posts may not have been orphaned when the pruned posts were
deleted. Taking what you have already learned about the frameworks, modify each prune
script so that the related comments and ranks are deleted during pruning. Try to use
each frameworks particular way of interacting with related tables.
Download | Description | Name | Size | Download method |
|---|
| Part 5 sample code | os-php-fwk5-column_part5.zip | 32KB | HTTP |
|---|
Resources Learn
-
Read William Graham's blog for an example of using Ajax and Zend:
Ajax
101: A Simple Example of Using Ajax with the Zend Framework.
-
For a tutorial that will show you step by step how to create an Ajax-powered symfony
application in minutes, see "Easy Ajax in symfony."
-
See the Zend Framework manual.
-
Read the DevNetwork Forums discussion on Zend and Ajax.
-
For a good tutorial series on using the Zend Framework, read "Understanding
the Zend Framework, Part 1."
-
Another good Zend tutorial is "Getting
Started with the Zend Framework."
-
Get the symfony documentation.
-
Check out the "My first symfony project"
tutorial.
-
Get the CakePHP Manual. (Note: The manual is
written to CakePHP V1.1. There may be some variance as you are using CakePHP V1.2).
-
Read the five-part series "Cook
up Web sites fast with CakePHP."
-
Read the Wikipedia entry for an overview of software
frameworks.
-
Get an overview of Model-View-Controller architecture from Wikipedia.
-
Check out the PHP Manual.
-
PHP.net is the central resource for PHP developers.
-
Check out the "Recommended PHP reading list."
-
Browse all the PHP content on developerWorks.
-
Expand your PHP skills by checking out IBM developerWorks' PHP project resources.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Using a database with PHP? Check out the Zend Core for
IBM, a seamless, out-of-the-box, easy-to-install PHP development and production environment that supports IBM DB2 V9.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
-
Innovate your next open source development project with IBM trial software, available for download or on DVD.
-
Download IBM product evaluation versions, and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
About the author  | |  | Duane O'Brien has been a technological Swiss army knife since the Oregon Trail was text only. His favorite color is sushi. He has never been to the moon. |
Rate this page
|  |