Calling controller actions from cron and the command line
This is a very simple tutorial that shows how you can modify the dispatcher to call controller actions from the command line and cron.
On the Google Group there has been quite a few threads about the best way to run cron jobs when using CakePHP. This is something I had do for my one of projects and I figured out a very simple way to do it by modifying the dispatcher so controller actions - such as an action to send out a batch of emails - can be invoked from the command line.
It's very simple. What you do is make a copy of index.php in
/app/webroot/, call it cron_dispatcher.php and place it in /app.
Scroll down in the code till you come to the line:-
Download code
Now replaces everything below it with the following code:-
Download code
What this means is you can call controller actions from the command
line, so for example to call the send action in your mailouts
controller you just create a cron job like this:
php cron_dispatcher.php /mailouts/send
As you can see it's a very simple approach that doesn't break the MVC framework.
Since you are invoking your cron jobs as controller actions it is possible for users to invoke the action from the site proper. If there are certain actions that you don't want users to invoke add a line to the action to check that the constant CRON_DISPATCHER is defined. For example
Download code
It's very simple. What you do is make a copy of index.php in
/app/webroot/, call it cron_dispatcher.php and place it in /app.
Scroll down in the code till you come to the line:-
Download code
require CORE_PATH.'cake'.DS.'bootstrap.php';
Now replaces everything below it with the following code:-
Download code
// Dispatch the controller action given to it
// eg php cron_dispatcher.php /controller/action
define('CRON_DISPATCHER',true);
if($argc == 2) {
$Dispatcher= new Dispatcher();
$Dispatcher->dispatch($argv[1]);
}
What this means is you can call controller actions from the command
line, so for example to call the send action in your mailouts
controller you just create a cron job like this:
php cron_dispatcher.php /mailouts/send
As you can see it's a very simple approach that doesn't break the MVC framework.
Since you are invoking your cron jobs as controller actions it is possible for users to invoke the action from the site proper. If there are certain actions that you don't want users to invoke add a line to the action to check that the constant CRON_DISPATCHER is defined. For example
Download code
function send()
{
// Check the action is being invoked by the cron dispatcher
if (!defined('CRON_DISPATCHER')) { $this->redirect('/'); exit(); }
$this->layout = null; // turn off the layout
// do something here
}
Comments
Comment
1 Protecting this from the public
1) put the cron_dispatcher.php in a directory like /usr/local/bin, just make sure that the ROOT and APP_DIR constants are defined correctly.
2) pass an optional parameter as a password, like this:
php cron_dispatcher.php mysecretpassword /mailouts/send
then the code would become this:
[code php] define('MY_SECRET_PASSWORD','mysecretpassword');
if(($argc == 3) && $argv[1] == MY_SECRET_PASSWORD) {
$Dispatcher= new Dispatcher();
$Dispatcher->dispatch($argv[2]);
PS: i wouldn't replace *everything* below require CORE_PATH.'cake'.DS.'bootstrap.php';
this part still remains useful:
[code php] if (DEBUG) {
echo "";
}
Bug
2 Didnt seem to work
require CORE_PATH . 'cake' . DS . 'bootstrap.php';
define ('APP', ROOT.DS.APP_DIR.DS.'app'.DS);
define('CRON_PASS','bjsf');
/**
* Call dispatcher
**/
if(($argc == 3) && $argv[1] == CRON_PASS) {
$Dispatcher= new Dispatcher();
$Dispatcher->dispatch($argv[2]);
}
if (Configure::read() > 0) {
echo "";
}
usage is the same:
php cron_dispatcher.php bjsf /workflows/check_timed_events
Comment
3 Tutorial updated
Thanks for the feedback. In terms of the password parameter, it would add extra security but it wouldn't be necessary if the cron dispatcher is located outside the document root and hence not accessible by the public.
Comment
4 An alternative way of protecting your cron dispatch
if (isset($_GET['url']) && $_GET['url'] === 'favicon.ico') {
}
elseif(php_sapi_name() === "cli")
{
// this is a command line call - dispatch it with the argument
define('CRON_DISPATCHER',true);
$Dispatcher=new Dispatcher();
$Dispatcher->dispatch($argv[2]);
}
else {
$Dispatcher=new Dispatcher();
$Dispatcher->dispatch($url);
}
Question
5 alternative way is giving error
I tried using your alternative way. I just added the else if condition in my webroot/index.php and using command line , I tried calling "php index.php controller/actions/parameter" -- also index.php recognizes it as a command line call, I am getting an error that FULL_BASE_URL is not defined. Below is the error message I am getting
Notice (8): Use of undefined constant FULL_BASE_URL - assumed 'FULL_BASE_URL' [CORE\cake\libs\router.php, line 569]
Context | Code$url = "/users/logout/?from=/"
$full = true
$_this = Router object
$params = array("plugin" => null, "controller" => "articles", "action" => "indexer", "pass" => array, "form" => array, "url" => array, "bare" => 0, "webservices" => null)
$defaults = array("plugin" => null, "controller" => null, "action" => "index")
$path = array("plugin" => null, "controller" => null, "action" => null, "base" => false, "here" => "//articles/indexer/1", "webroot" => "/", "passedArgs" => array, "argSeparator" => ":", "namedArgs" => array, "webservices" => null)
$base = false
$frag = null
$q = null
$mapped = null
$output = "/users/logout/?from=/"
$extension = null
$output = str_replace('//', '/', $output);
}
if ($full) {
$output = FULL_BASE_URL . $output;
}
Router::url() - CORE\cake\libs\router.php, line 569
Controller::redirect() - CORE\cake\libs\controller\controller.php, line 499
AppController::redirect() - CORE\app\app_controller.php, line 75
othAuthComponent::redirect() - CORE\app\controllers\components\oth_auth.php, line 804
othAuthComponent::check() - CORE\app\controllers\components\oth_auth.php, line 923
AppController::beforeFilter() - CORE\app\app_controller.php, line 62
Dispatcher::start() - CORE\cake\dispatcher.php, line 380
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 334
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 323
[main] - CORE\app\webroot\index.php, line 88
Comment
6 Problem Solved for alternative way is giving error
Cheers :)
Ritesh
Comment
7 Link to a working cron dispatcher file
http://www.mail-archive.com/cake-php@googlegroups.com/msg29333.html
Comment
8 great
Thanks Matt!
Comment
9 cant update
Can't update my comment at the moment for some reason, but replace after include(...bootstrap.php) in 1.2.