The PHP framework behind SitioYa

Koshkil documentation

Core

Request lifecycle

From URL to response

  1. .htaccess forwards the request to webroot/index.php.
  2. core/Koshkil.php is included: it loads the global and the domain configuration, sets error_reporting and registers the error handlers.
  3. new Application(): startup() saves the POST data (for Koshkil::old()) and initializes the cache; then the database connection is opened.
  4. Route::parseURI() detects the active plugins, applies the rewrite rules and splits the URL into plugin, controller, action and arguments.
  5. ActionDispatcher::_invoke() finds the controller file, instantiates it and runs its lifecycle and the action.
  6. Response::render() picks the template from the status code (the action's, error/error404, error/error4xx or error/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():

  1. If $openAccess is false, it checks the session with isLoggedIn(). Without a session it calls processLogin() and shows login.tpl. With a session, checkPermissions() validates $roles, $rules and $strictRules.
  2. create(), the first hook.
  3. The hooks in $lifeCycle, by default dispatch(), init() and run().
  4. The action, with the URL arguments.
  5. 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.tpl template, 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.