The life cycle of a request
In order to understand how Casco works, it's very useful to have a look at how a request is handled and output is returned to the browser. See also the ArchitectureOverview for an overview of the framework in general.
All requests come in at index.php, so that's where we start. Just before, superglobals like $_GET, $_POST are set by PHP, and we can already act on those raw contents if we want. But usually, the very first thing that's done is that the output buffer is started and /trunk/inc/config.inc.php is required.
The first thing bootstrap.php does, is load class.casco.php, which is the class that handles all post-bootstrap configuration and is also the major factory class where almost all instantiation happens. This is a class which is never instantiated; all functions on it are static.
After the casco class is loaded, an attempt is made to determine the docroot and httproot automatically, so the user does not have to be bothered to configure it in the common cases (important: this ensures Casco works out of the box). These are not used during the rest of the bootstrap process, so the user is free to override them if it doesn't work correctly (which should not happen anyway).
The bootstrap process continues setting up the finder_cached as the default finder (using memory-based caching initially), it sets the default timezone to UTC and requires a few essential classes directly. The classes that are loaded are the "vanilla" implementations of all core Casco strategies. This is needed because all file finding is done through the Finder, for example and you can't find a Finder without having a Finder :) For problems like this, bootstrap.php was invented.
module registration and configuration
When bootstrap.php has done its work, the configuration continues in config.inc.php, where the user can register modules using casco::register_module.
When a module is registered, Casco tries to see if there is a file called init.php in the module's root directory. If there is, it is required. The file can optionally define a module class on which the user can call functions to configure it, and it can also set some default subclasses. Basically it can do anything the user can do in config.inc.php.
At the end of config.inc.php, there's a call to casco::init() This function does the following:
- First, it instantiates vanilla implementations of the core strategies like Finder, Templator etc.
- Then, it loops through all registered module classes and allows them to perform some pre-subclassing tasks, among which are the initialization subclassed core strategies and definition of new plugin layers and components in the composer.
- Next, it instantiates subclassed implementations of the core strategies. This is deferred until this time because these subclassed strategies may be so complex that they depend on other strategies (or the old implementation of the strategy they subclass) to perform some initialization and/or loading. They can use the vanilla implementations to do this. Doing it this way solves most of our bootstrapping problems.
- Finally, it loops through all registered module classes again and allows them to perform regular initialization tasks.
Back in index.php, we now completed bootstrapping and configuration and can proceed to instantiate an area. The choice of the area to instantiated is (in the default installation) based on the value of the "area" $_GET argument. The area will handle the bulk of the request.
Inside the area, the constructor determines the name of the session cookie to use, and initializes the correct PHP session (this allows us to have several sessions per Casco application). It also initializes containers for title, HTML head and body and error messages.
In set_content, the area analyzes the requested page and looks up the corresponding page definition. It then allows the menu controller to render the menus and finally calls the controller that is responsible for rendering the main contents of the page (see below). If it returns any errors, these are finally rendered using the proper templates and inserted in the correct containers.
It depends entirely on the type of controller on what happens here, but generally speaking, a controller looks at the page definition and decides what to do. Most typical controllers request data from a model to use (or pass POST data to a model for validation and storage) and finally invoke a view to render the final HTML representing the data that was retrieved. They pass this data to the view, along with the page definition containing additional configuration options for the view.
Views are less variable in what they do. They're just a class for containing logic on what templates to render. Often, they will instantiate plugins from field definitions in the model's map to render the actual values of the model data that the controller passed in, and then they will use templates to combine these contents together into cells, rows and tables or list items and lists, for example. This is put into the containers we got from the controller and returned.
The view fills containers the controller passed to it and returns to the controller, which then fills containers that were passed to it and returns to the area, which combines all the containers together in the big page template and finally returns this as one big HTML string. This string is caught by index.php and displayed through use of PHP's echo function, the output buffer is flushed and the output is returned to the browser.