Core
Request lifecycle
From URL to response
.htaccessforwards the request towebroot/index.php.core/Koshkil.phpis included: it loads the global and the domain configuration, setserror_reportingand registers the error handlers.new Application():startup()saves the POST data (forKoshkil::old()) and initializes the cache; then the database connection is opened.Route::parseURI()detects the active plugins, applies the rewrite rules and splits the URL into plugin, controller, action and arguments.ActionDispatcher::_invoke()finds the controller file, instantiates it and runs its lifecycle and the action.Response::render()picks the template from the status code (the action's,error/error404,error/error4xxorerror/error500) and renders it inside the layout, or sends the body as is for JSON or file responses.
// webroot/index.php
require_once('../core/Koshkil.php');
require('../vendor/autoload.php');
Koshkil::Uses('com.Application');
$app = new Application();
$app->run();
$app->done();
Inside the controller
When it is constructed, the controller creates Request, Response, Session and View, registers itself in Koshkil::$controller and initializes the language. Then the dispatcher calls startupProcess():
- If
$openAccessisfalse, it checks the session withisLoggedIn(). Without a session it callsprocessLogin()and showslogin.tpl. With a session,checkPermissions()validates$roles,$rulesand$strictRules. create(), the first hook.- The hooks in
$lifeCycle, by defaultdispatch(),init()andrun(). - The action, with the URL arguments.
shutdownProcess(), at the end.
Any hook can stop the process:
| Returns | Effect |
|---|---|
null (nothing) |
Continues with the next step. |
false |
Stops the remaining hooks and does not run the action. |
a Response |
That response is used immediately. |
The action may only return null or a Response; any other value throws an exception.
Which hook to use
create() to prepare dependencies, init() to load data shared by every action (this is what SuperController and AdminSuperController do), and the action for the specific work. If you override init(), call parent::init() first.
Which template is rendered
With status 200, the template is the view's template variable if the action set it; otherwise <controller>/<action> in lower case. ProductosController::ver() renders productos/ver.tpl, looked up in the theme's templates/<templateFolder>/ folder (front or admin).
The view does not render that template alone: it passes it as $template to the layout ($templateFile, usually main.tpl), which includes it with {html_include file="{$template}"}. See Views.
When the URL does not match
| Situation | What happens |
|---|---|
The controller does not exist and the action is index (a single segment inside a folder) |
The last segment is used as an action of the parent controller: /admin/perfil resolves to AdminController::perfil() when there is no Admin/PerfilController. |
| The controller does not exist and the URL has a single segment | System.DefaultController answers with status 404. |
The controller does not exist and the action is not index |
The request goes to System.DefaultController with that same action. |
| The controller exists but the action does not | Its index() runs if it exists (status 200); otherwise 404. |
Warning
Because of the last two rows, a non-existent URL can end up showing a controller's index() with status 200. If an action receives parameters it does not recognize, answer 404 explicitly with $this->Response->setCode(404).
Errors
ExceptionRenderer registers handlers for errors, uncaught exceptions and fatal errors:
- Warnings and notices are only logged.
- Uncaught exceptions and fatal errors answer with status 500 and the
error/error500.tpltemplate, which receives$Response(getCode(),getMessage()) and$lastError(message, file and line).
Only errors included in Error.Reporting are handled, and logs are written to tmp/logs/ when Debug.Level is greater than 0.