Setting up Cron with Zend Framework 1.11

If you’re here you like me are struggling to figure out how we can setup cron jobs whilst using the good parts of the zend framework but whilst also removing the view rendering. As we do not want any layout stuff to be processed.

To achieve this we need the following:

1. A new Entry point
2. Setup default module
3. A base Controller for all scripts
4. A script Module with controllers

My setup is pretty standard, i have modules within my applications and all of my library files are within Library/App

A new Entry Point

Zend implements a single point of entry for all web applications, index.php. This sets up the application calling the bootstrap. Our script entry point needs to work the same as index except we need to only call the bootstrap functions that we need.

A good way of achieving a common entry point is to create a separate file within public/ called common_includes.php. This will probably look something like:

date_default_timezone_set("LOCALE");

// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

define("SITE_ROOT", realpath(dirname(__FILE__)) . "/../");
// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
// Make sure we have Models too
set_include_path(implode(PATH_SEPARATOR, array(
	realpath(APPLICATION_PATH),
    realpath(APPLICATION_PATH . '/../library'),
    realpath(APPLICATION_PATH . '/../library/Frameworks'),
    realpath(APPLICATION_PATH . '/models'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

then within your index.php do the following

bootstrap()
   	       ->run();

Your script.php ( entry for scripts ) will be slightly different, it might look something like:

bootstrap(array("Constants",........))
   	       ->run();

where each entry of the array corresponds to an _init function

Setup a default module

We need to force all controllers and actions to look within a particular module. I do this within a bootstrap function called _initCli() which looks like:

function _initCli() {
	$front 	= Zend_Controller_Front::getInstance();
	if (PHP_SAPI == 'cli') {
	    $front->registerPlugin(
	        new Zend_Controller_Plugin_ErrorHandler(
	            array(
		      'module'     => 'script',
		      'controller' => 'error',
		      'action'     => 'index'
		    )
		)
	    );
	    $front->setDefaultModule("script");
	} else {
	    $front->setDefaultModule("default");
	}
}

This will be called by both the index and script.php but when it’s a script it sets the module to be script and sets up the error controller. ( I’m sure there is a better way of doing this – separate bootstrap files? )

Setup new Router

Our solution for doing scripts is to allow us to do something like this:

script.php controller=mail action=send

this needs to be mapped to call the mail controller and send action. Within that action we do what we need to do – in this example send any new mail.

Here is the router i use and i’ll try and explain how it works

class App_Router_Cli extends Zend_Controller_Router_Abstract {
      public function route (Zend_Controller_Request_Abstract $dispatcher) {

		$getopt 	= new Zend_Console_Getopt (array ());
		$arguments 	= $getopt->getRemainingArgs();

		$controller = "";
		$action 	= "";
		$params		= array();

        if ($arguments) {

        	foreach($arguments as $index => $command) {

        		$details = explode("=", $command);

        		if($details[0] == "controller") {
        			$controller = $details[1];
        		} else if($details[0] == "action") {
        			$action = $details[1];
        		} else {
        			$params[$details[0]] = $details[1];
        		}
        	}

        	if($action == "" || $controller == "") {
        		die("
        			Missing Controller and Action Arguments
        			==
        			You should have:
        					php script.php controller=[controllername] action=[action]
        		");
        	}

			$dispatcher->setControllerName($controller);
			$dispatcher->setActionName($action);
			$dispatcher->setParams($params);

			return $dispatcher;
		}
		echo "Invalid command.\n", exit;
        echo "No command given.\n", exit;
    }

    public function assemble ($userParams, $name = null, $reset = false, $encode = true) {
        throw new Exception("Assemble isnt implemented ", print_r($userParams, true));
    }
}

Note: I found this on someone elses blog and have tailored it to my needs. As you can see it will look for controller and action ARGV and sets the dispatcher accordingly. It will then pass everything else to the params so within the controller we’re able to access them as we need.

This router is called from within my bootstrap:

	protected function _initRouter () {
	    if (PHP_SAPI == 'cli') {
	        $this->bootstrap ('frontcontroller');
	        $front = $this->getResource('frontcontroller');
	        $front->setRouter (new App_Router_Cli());
	        $front->setRequest (new Zend_Controller_Request_Simple ());
	    }
	}

notice i only run this if we’re running as a script

So at this stage when we run:


php -f script.php controller=mail action=send

zend should try to load MailController->sendAction() within the module.

Base Controller

We need to update the MailController to extend a basecontroller that turns of view rendering.

class App_Controller_Script_Abstract extends Zend_Controller_Action {
	function init() {
		parent::init();
		$this->_helper->viewRenderer->setNoRender(TRUE);
	}
}

This will turn off the dispatcher meaning we do not need any view scripts.

Hey Presto

So after all of those changes you should now be able to run your controllers and actions from within your command line or session.

This entry was posted on Wednesday, October 19th, 2011 at 12:08 and is filed under linux, mysql, zend. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

Leave a Reply

You must be logged in to post a comment.