Learning from the CakePHP source code - Part II
Posted on 29/9/06 by Felix Geisendörfer
Sorry this post took me a while. The last couple days have been sort of busy and a lot of things on my lists didn't get done. Anyway, I'll continue right where I stopped, with the Dispatcher:
The heart of CakePHP - The Dispatcher
In the previous post I was showing how to use the Dispatcher::dispatch() function. Now what's more interesting, is what it actually does and in what order.
- Build the $params array. The first thing the Dispatcher::dispatch() function does is to perform an array_merge between $this->parseParams($url) and $additionalParams. Dispatcher::parseParams() is quite interesting as well. The first thing it does is to create a new Instance of the Router and call Router::parse($from_url). This returns an array with fields like 'controller', 'action', etc.. After that all variables from $_GET, $_POST and $_FILE are being merged into the $params array as well and finally returned to the Dispatcher function.
- Find the base url: The next thing Dispatcher::dispatch() does, is to find the base url, that all controller actions, routes, etc. happen within. This url is then assigned to $this->base. All the base url retrieving logic is implemented in Dispatcher::baseUrl().
- Load/Include the requested Controller: From now on I'll focus on the most important things going on because it would be insane to go into every little detail. The next block of code basically sees if $params['controller'] is emtpy, and if yes object::cakeError() is invoked to show the 'missingController' page all of us know. If the controller field is not empty, the first thing CakePHP tries to do is to load this controller (via loadController) from within /app/controllers. If that fails, CakePHP looks if there is a plugin with the name of $params['controller'] (via loadPluginController), and if not, missingController is invoked as well.
- Possible Plugin Logic: In case there was a plugin found, the next thing that happens is some plugin logic that does not get executed for normal Controller requests. It's basically some $params shifting magic (Dispatcher::_restructureParams()), and most notably the point where loadPluginModels() get's executed.
- Executing the Admin Route: If you have CAKE_ADMIN defined in your /app/config/core.php, then this is the point where the 'admin_' prefix (or whatever CAKE_ADMIN is set to) is added to the $params['action'] parameter. And here you also have a check that protects CAKE_ADMIN routes as private actions. This basically means if somebody would call /posts/admin_index instead of /admin/posts/index he wouldn't get very far.
- Render a possible missingController error: If at some point in the code above $missingController was set to true, this is the point where $this->cakeError('missingController', ...) is invoked. If not, $controller is being assigned a new instance of our $ctrlClass.
- Action exists? Action private?: The next couple of lines are dedicated to perform some checks on the $params['action'] parameter. The following things happen in the order I mention them: If $params['action'] is empty, it is set to 'index' instead. If the Controller has an action with the name $params['action'] but it starts with '_', $privateAction is set to true. If the Controller does not have an action $params['action'], or it happens to be named: 'beforeFilter', 'beforeRender' or 'afterFilter', $missingAction is set true.
- Set Controller variables: Now it's time for all of the Controller variables to be set with interesting information. This includes the 'autoRender', 'base', 'here', 'webroot', 'params', 'action', 'data', 'passedArgs', 'autoLayout', 'webservices' and 'plugin'.
- Load Components & Models: The Dispatcher now calls $controller->_initComponents() which attaches all components as references to the Controller object, but does not yet call their startup() function. After that, the same thing happens with Models when calling $controller->constructClasses().
- Render possible missingAction/privateActione errors: If there had been a missingAction or privateAction error before, this is the point where $this->cakeError() get's called to render those.
- Invoke the controller: The last call in Dispatcher::dispatch() goes to Dispatcher::_invoke(), but since it's still the same operation we are doing, I'll continue writing about things like before. The _invoke function directly executes Dispatcher::start(), which does the following things: If Controller::beforeFilter is a string/array, call the/all function(s) in this string/array (Warning: This is depracted!). Afterwards Controller::beforeFilter() get's executed without further checking. And the last thing happening in Dispatcher::start() is that all component's startup() functions get called. So back in Dispatcher::_invoke() there comes a check whether we are scaffolding or not, and if no, our Controller action get's called via call_user_func_array. Then, if $controller->autoRender is set to true, $controller->render() is called, and finally the afterFilter get's executed. Depending on whether there was some rendering going on or not either the raw output or the action's return value are getting returned to Dispatcher::dispatch() which directly returns this value back to the point where the initial call came from.
That's it, you've just witnessed the miracle of birth ... er dispatching. I've skipped some minor things here and there, but in general this together with Part I should be a good guide to the things happening before all your wonderful application code kicks in. Of course, there is a lot more like all the Model and View stuff. But talking about all of this would take for ever, bore you to death and would only be useful to very few of us. However, if you guys would be interested in it, I would post a Part III where I would just do some pointing at classes and code snippets a CakePHP user should have read to get a better understanding of the things going on in the background.
Oh, and today is probably also going to be the day I'll buy myself some yummy Cake ... : /. I'm not sure if I'll be able to take the picture and everything today, but I'll try my best. Hmm ... after that I should probably close this blog since nobody is going to hire me in the field of web development any more and I'll have to look for a new area to work in : /.
--Felix Geisendörfer aka the_undefined
You can skip to the end and add a comment.
Lucian: The associations are not my special expertise I have to admit ^^, but I'll see what I can do.
And as far as giving up goes: It's all fun, I don't think anybody will really care about it, and if so that would act as a nice filter for the kind of people I wouldn't want to work for anyway ; ).
Sorry for beeing slightly off-topic, but i have to jump on your line, fearing you never get a job again cause of putting yourself into a cake :D
Yesterday I saw a guy on TV who wears womens clothing and mentioned that in his CV when he was applying for his new job.....and got it! I don't think at all, that putting your face into a cake from time to time is as bad as wearing womens clothes ;)
Felix Geisendörfer's Blog: Learning from the CakePHP source code - Part 2...
Tom: Are you trying to encourage me to do even more stupid stuff in future? Ok, I'll see what I can do ; ).
I also anticipate a Part III. Thanks for taking the time to post.
As you wrote in Part I most of Cake's core code is "delightful and should help you to become a better programmer" and that's what makes it so easy to read. I think what you did in this post was mainly "translating sourcecode into prose"... Well, what I want to say: I just think that any programmer who is really willing to learn more about the core is also able to directly read through some lines of code and understand them without additional explanation.
All I want to suggest is just don't waste your time with discribing sections of code but tell us more about all that powerful, hidden stuff, e.g. how to use the class registry and stuff like that! I know you're - compared to most of us - damn familiar with the core so I'm really curious to learn more about the might & magic of Cake's core!
Hey, but maybe that's exactly what you wanted to do in Part III, so I'm already looking forward to it!
This is a really helpful article. Thanks!
Felix, great post, should be included in the manual to begin with ...
There is one small drawback to the Cake setup, maybe you can cover, but Ive noticed that its pretty "hackish" @ the moment to use the ORM portion of Cake by itself ... granted not what it was meant for but in legacy areas where you can do a full blown relaunch using Cake, its nice to just end up w/ the Model structures you can use and then port them over when you use the entire framework. (ie, people who cant use the controllers yet, dont have support for using the views yet, but would like to use the model setups - in CLI environment for example) ... no normal way to do this it seems?
with hackish you are referring to my "favicon.ico" bootstraping technic I guess? If so, yes it is a little hackish, but it works very nicely, and probably will continue to do so in future ; ).
Very interesting article. I hope you will write part III. :)
Great post as usual! This serie of article is being really helpful as there isn't much (if there is any) documentation about the inner-working of CakePHP.
I'm also waiting for the Part III :)
Excellent couple of articles. Please don't stop! Would be interesting to see more than just part III, maybe describing more in terms of the Model, View and Controller and how they sit in with each other (who extends what, etc)
Ok, that's asking too much :)
There really isn't enough articles like this around that actually 'disect' the source code properly - what alot of coders don't realise is that (whilst its initially harder and mindblowing), the source code of an app like this, couple with google and a little patience, is worth a THOUSAND hello world/blog tutorials.
Good job, lookin forward to more...
Great post Felix! I also vote for part III. :)
Been looking for insight like this into what's happening when since I started baking. Having this laid out in a Sequence Diagram somewhere in the manual would be an incredible learning tool.
I didn't see this in your post - where does Controller::beforeRender fit in the invocation order?
[...] So far I found only two pages with “Rails-quality” documentation: Learning from the CakePHP source code - Part I and Part II. [...]
mmh can someone show me an example program using cakePHP??
because i want to learn how to make a website using cakephp
and i can't make it because i never see the programs like before
thank you so much!
This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.
I would be interested in Part III, and especially if it will discuss a little on Model class and the way it handles the associations between models.
Don't give up, man!...have a Cake and keep going...I think you'll be hired, someday...