Writing a custom CakePHP console script
As you may have noticed, last weekend all command line related files in CakePHP 1.2 have been moved from /cake/scripts to /cake/console. Coupled with this move was a change in the way you use the command line scripts. Instead of
php bake.php -app /path_to_app_dir
you now have to use
./cake bake -app /path_to_app_dir
to run the bake script. The ACL script can be used in a similar way.
This switch to the new console is probably the end of bake2, as it doesn’t (yet) work with the console, and the console provides a similar functionality as bake2, i.e. the console allows you to execute your own command line scripts. In the meantime the bake2 script has been removed from the repository. (Update 2007-05-15: The bake2 script has been removed from the repository)
Writing such a command line script (or shell scripts as they are called in CakePHP) is easy, so let us write a simple script which creates a file specified by the user. The custom scripts are placed in /app/vendors/shells, if they are specific to an application, or in /vendors/shells, if they are generic.
In our case it doesn’t matter where we place the file as it is only an example, so we put it to /vendors/shells. A shell class has to extend the class “Shell” and the class name has to end with “Shell”. All public functions of a shell class are treated as “commands”, i.e. they can be called directly via command line. What the “index” function is for controllers, is the “main” function for shell scripts. It is automatically called if you don’t specify a different command.
In our case we also have to override the “initialize” function, as we don’t have a database.php file in app/config (else we would get an error). The code for our shell script is straight-forward and should be self-explanatory:
// vendors/shells/demo.php
class DemoShell extends Shell {
function initialize() {
// empty
}
function main() {
$this->out('Demo Script');
$this->hr();
if (count($this->args) === 0) {
$filename = $this->in('Please enter the filename:');
} else {
$filename = $this->args[0];
}
$this->createFile(TMP.$filename, ‘Test content’);
}
function help() {
$this->out(’Here comes the help message’);
}
}
This script can now be executed in one of the following ways:
./cake demo // asks the user for a file name ./cake demo test.txt // uses the specified file name ./cake demo help // shows the help message
Happy baking :)
Update (2007-05-13): Adapted to the latest changes in the code (CakeScript has been renamed to Shell)




I smell a new package installer … CakeBox perhaps? :)
Hi Daniel, first, thanks a lot for your efforts!
The idea with the demo script is nice. I would like to write such a script and use it from crontab, doing some periodic update/insert with my models. Now u mentioned the initalize function. Would it, if not overridden, connect to my database? And what approach should be used to work with Models (find,save,delete,…)?
Good night! ;)
mazoo
Hi Daniel,
that looks very promising! And I was wondering the same thing as mazoo. Maybe you could follow up with how one could work with db models? Please? :)
Thanks for your blog!
Small correction to the way scripts are called. While it is valid and possible to call scripts like
./cake bake -app /path_to_app_dir
as you have shown above, this defeats half the purpose of having a shell script wrapper. Instead, you can do the following:
cd /path_to_app_dir
./cake bake
That way, you can execute multiple commands without having to continually re-type your app path.
Nate,
I’m confused. The cake script lives at cake/scripts/cake. How would one call it from app/ as ./cake?
You add cake/console to your system path. (It was moved from cake/scripts to cake/console.)
@all: Thanks for your comments!
@mazoo, Mike: At the moment it seems that the functionality to access the db is broken due to a bug, see https://trac.cakephp.org/ticket/2544. When this bug is fixed, I will write a follow-up.
@nate: Thanks for the correction.
How does it differentiate between function name and argument for the default function?
in your example:
[quote]
./cake demo test.txt // uses the specified file name
./cake demo help // shows the help message
[/quote]
what if “test.txt” was meant to be an action that needs to be called, or “help” the argument for the main function?
Does cake search for all available functions first, and when it doesn’t find any match, it uses it as argument for the main function?
Cool, any guess on the possibility to have an interactive shell like RoR has?
This days i’ve been looking for one, since the one that comes with php5 really sucks. But this one works great: http://jan.kneschke.de/projects/php-shell
maybe they could implement something like it. :)
@Dieter: Yes, first it looks whether there is an action with the specified name. If there is no such action, the main action is used. To use a parameter in the main action with the same name as an action, you have to specify the action name explicitly:
@Carlos: I don’t know what features are planned for the console. If you think something could be useful in CakePHP, don’t hesitate to open an enhancement ticket on https://trac.cakephp.org
[...] scripts are now called “Shells” instead of “CakeScripts”, so I adapted the previous article to those [...]
Hello Everybody,
Could anybody please tell me if there is way where a console script could be used to access models. I am thinking of an approach where I could use something like “loadModel” in the console script file.
Thanks in advance for the help! :)
@mjk: The answer is already in the question ;-)
Use something like:
loadModel('MyModel'); $myModel = new MyModel(); // do something with the modelHTH
Thanks for the reply! But I think something is wrong with how I used the app_model as it creates an error when I load a model from the cake script:
Fatal error: Class ‘CakeSession’ not found in c:\wamp\www\cake\src\apps\mjk\app_model.php on line 10
that line corresponds to a line within the constructor in the app_model.php
function __construct($id=false, $table=null, $ds=null)
{
// If the database was not specifically set, then use the value from the logged in user
if ($this->useDbConfig == ‘default’)
{
$session = new CakeSession();
$sessionVars = $session->__returnSessionVars();
if (isset($sessionVars['Auth']['User']['databasename']))
{
$this->useDbConfig = $sessionVars['Auth']['User']['databasename'];
}
else
{
// User may ask for models they do not have access to, just ignore request
return;
}
}
parent::__construct($id, $table, $ds);
}
… and line 10 is $session = new CakeSession();
Do i need to call the Cake Session in the cake script? If so can anybody please help me how to do it?
I have just been working with cakephp for 4 months now and I’m not yet attuned to working on the “deeper” parts of the framework. Thanks in advance. and more power to cakephp and the people who use it! :)
@mjk: You can probably use the uses() function to load the CakeSession class:
uses('session');But I am not sure whether there is a session available on the command line, probably not.
Thanks a lot for the tip! I have dropped this part of my project for a while to move on to other things.
thanks a lot for the tip. I have dropped this part of my project for the meantime. I’ll try to do as suggested when I come around it again. Thanks again.
@mjk: Good luck :)
[...] cakebaker » Writing a custom CakePHP console script - [...]
[...] http://cakebaker.42dh.com/2007/05/07/writing-a-custom-cakephp-console-script/ [...]