Class: Controller

Controller provides the request marshalling to Elefant handlers. It evaluates $_SERVER['REQUEST_URI'] against files in a apps/{appname}/handlers/ folder, defaulting to the handler specified in conf ('General', 'default_handler') if no others match.

Matching is done by reducing the URL folder-by-folder until a file matches. Here are some examples:

/ -> conf(General, default_handler)

/foo -> apps/foo/handlers/index.php,
        conf(General, default_handler)

/user/login -> apps/user/handlers/login.php,
               apps/user/handlers/index.php,
               conf(General, default_handler)

/user/info/123 -> apps/user/handlers/info/123.php,
                  apps/user/handlers/info.php,
                  apps/user/handlers/index.php,
                  conf(General, default_handler)

The controller simply returns the matching URL so you can include it via the following code:

<?php

$handler = $controller->route ($_SERVER['REQUEST_URI']);
ob_start ();
require_once ($handler);
$page->body = ob_get_clean ();

?>

Or more simply (but in practice the same):

<?php

$handler = $controller->route ($_SERVER['REQUEST_URI']);
$page->body = $controller->handle ($handler);

?>

In this way, there is less scaffolding code for individual controllers, they can simply begin executing just like an ordinary PHP script, and the output is simply echoed like an ordinary PHP script too.

The remaining elements of the URL are accessible in the array $this->params, so for /user/123 handled by handlers/user.php, you could get the value '123' via $this->params[0].

To use named parameters, you simply say:

<?php

list ($id, $title) = $this->params;

?>

You can also call one handler from within another and get its results like this:

<?php

$res = $this->run ('/user/123');

?>

Sometimes you might want to pass values to another handler for internal processing, which you can do like this:

<?php

$res = $this->run ('/user/123', array ('foo' => 'bar'));

?>

You can then access the array via:

<?php

echo $this->data['foo'];

?>

In addition to running one handler from another, you can configure hooks with one or more handlers to be run for you when you trigger the hook. This is a 3-step process:

1. Add your hook and its handler to conf/config.php:

myapp/somehandler[] = otherapp/handler

2. In myapp/somehandler, add the hook call and pass it some data:

<?php

$this->hook ('myapp/somehandler', array('id' => 123));

?>

3. In otherapp/handler, verify the request and do something interesting with the id:

<?php

if (! $this->internal) {
    die ('Cannot call me from a browser.');
}

if (! Form::verify_value ($this->data['id'], 'type', 'numeric')) {
    die ('Invalid id value');
}

// do something with $this->data['id']

?>

Properties

public static $hooks = array ()

A list of handlers defined to be called for each type of hook. Similar to the idea of webhooks, this provides a means of triggering handlers from each other without hard-coding the specific handlers in the triggering code. See conf/config.php's [Hooks] section for examples.

public static $called = array ()

Keeps track of the number of times each handler has been called in this request. You can check self::$called['handler'] for the number.

public static $loaded = array ()

Tracks which apps have been loaded.

private static $_hooks_loaded = array ()

A list of apps whose hooks have been loaded already.

public $params = array ()

Extra parameters from the end of the URL.

public $internal = true

Whether the request originated internally or externally.

public $data = array ()

Data sent from another handler to the current one.

public $cli = false

Set to true if the request came from the command line.

public $put_data

Cached PUT data from get_put_data() so it only reads it the first time.

public $app

The app that is being called. Set by route().

public $uri

The uri that was last called, as parsed by route(). This will have preceding slashes trimmed, and if it resolves to a default like app -> app/index, then it will have the /index added.

public $chunked = false

This will be set the first time chunked() is called, so the controller knows it's already started sending the response with Transfer-Encoding: chunked.

public $cache = false

Whether the current handler's output should be cached automatically when it returns. Set to true to cache indefinitely, and a number to set a timeout in seconds. The cache key will be the app and handler name, with slashes converted to underscores, e.g., myapp_handler.

Usage:

<?php

// cache indefinitely
$this->cache = true;

// cache for 5 minutes
$this->cache = 300;

?>

To clear a cached handler before its time, which you would have to do from a separate handler since the original won't be called while cached, you can use:

<?php

$cache->delete ('myapp_handler');

?>

public $status_code = false

The HTTP status code that's been set.

public $alias = false

If the page is an alias, this will contain the original value of $_SERVER['REQUEST_URI']. Otherwise, it will be false.

private $_page

Page object.

private $_i18n

I18n object.

private $_cache

Cache object.

private $_tpl

Template object.

private $_versions

Methods

public __construct ($hooks = array ())

Constructor method. Receives a list of hooks as well as a Page and I18n object.

public template ($tpl = false)

Get or set the template object.

public cache ($cache = false)

Get or set the cache object.

public page ($page = false)

Get or set the page object.

public i18n ($i18n = false)

Get or set the i18n object.

public run ($uri, $data = array (), $internal = true)

Run an internal request from one handler to another.

public error ($code = 404, $title = 'Page not found', $message = '')

Trigger the default error handler. Note that you must echo the output from your handler before returning, for example:

<?php

echo $this->error ();
return;

?>

Not like this:

<?php

return $this->error ();

?>

public hook ($type, $data = array ())

Run any handlers for the specified hook type. Output of the handlers is concatenated and returned. Hooks are specified in the [Hooks] section of each app's conf/config.php file, like follows:

[Hooks]

myapp/newpost[] = search/add
myapp/newpost[] = twitter/posttweet

Hooks may also be listed in the global conf/config.php in the same format.

public params ()

Takes a list of names as arguments and returns an associative array of the handler parameters using the names as keys. Note that it will return false if the number of names is different than the number of parameters. Handy for using named parameters via:

<?php

extract ($this->params ('id', 'title'));

?>

Note that you can also achieve the same thing via:

<?php

list ($id, $title) = $this->params;

?>

public data ($key, $default, $validations = array ())

Returns the specified $data element, or a default value. You can also specify a list of validations. If the validations fail, the default is returned (uses the Validator class).

Full usage:

$width = $this->data ('width', 500, ['type' => 'numeric']);

public handle ($handler, $internal = true, $data = array ())

Execute the request handler. $internal determines whether the request originated internally from another handler or template, or externally from a browser request.

public route ($uri)

Route a request URI to a file.

public override ($handler)

Looks for an override of the current handler in the app configuration in a [Custom Handlers] section. Overrides are handlers that should be called transparently in place of the current handler, overriding its behaviour without modifying the original handler.

An override setting's key should be the app/handler name, and the value can be either the same app/handler name (meaning no override), another app/handler name (meaning override with that handler), or Off (meaning disable the handler). A handler that has been disabled will return a 404 error.

If the response is false, there was no override or disabling, and the handler should continue running, otherwise the response will contain the output of the override handler which should be echoed and the original handler should return and stop further execution.

public clean ($url)

Is this URL clean of any directory manipulation attempts?

public add_param ($param)

Adds to the start, since route() parse them off the end of the URI.

public add_notification ($msg, $cookie_name = 'elefant_notification')

Add a notification to the elefant_notification cookie, which is monitored by admin/head and will be displayed using jGrowl. Handy for setting confirmation messages and other notices for the current user to display on a subsequent screen.

public absolutize ($url, $base = false)

Turn a relative URL into an absolute URL. If the $base is false, it will use the HTTP_HOST to construct a base for you.

public redirect ($url, $exit = true)

Redirect the current request and exit.

public permanent_redirect ($url, $exit = true)

Permanently redirect to a new address and exit.

public quit ()

Wrapper around exit to work with subfolder installations. For more info: https://gist.github.com/jbroadway/1558300

public get_put_data ()

Get the stdin stream for PUT requests.

public get_raw_post_data ()

Get the raw POST data.

public request_method ()

Get the request method. If X-HTTP-Method-Override header is set, it will return that instead of the actual request method.

public status_code ($code, $text = '')

Get or set the HTTP response status code.

public restful ($obj)

Use an object's methods to handle RESTful requests for the current handler.

public restful_error ($message, $code)

Returns a RESTful error response.

public flush ($out = false)

Changes the response to use Transfer-Encoding: chunked and sends the current buffer to the client. Call this each time you want the script to send the next chunk of data to the client.

Note that this will cause render() to call flush(null) at the end, which will not return your output to be included in a page layout. It will also flush and exit prior to setting a controller-level cache of your output.

public header ($header, $replace = true, $http_response_code)

Wraps header() with a check for headers_sent() to avoid "headers already sent" errors.

public remote_addr ()

Get the remote IP address from REMOTE_ADDR, or if it's set, from HTTP_X_FORWARDED_FOR which may be set by a load balancer.

public is_https ()

Returns whether the current request is made over HTTPS or not.

public force_https ()

Forces the current request to be over HTTPS instead of HTTP via redirect if necessary.

public force_http ()

Forces the current request to be over HTTP instead of HTTPS via redirect if necessary.

public require_login ($redirect = '/user/login')

Require the user to be logged in to proceed with the request. If not, it will redirect to the appropriate login handler.

public require_admin ($redirect = '/admin')

Require the user to be an administrator to proceed with the request. If not, it will redirect to the appropriate admin login handler.

public require_acl ($resource)

Require the user to have access to one or more resources. Accepts any number of parameters, which should be resource names. If any resource fails, it will redirect to either the member login screen at /user/login, or the /admin login screen if the admin resource is included in the list.

Usage:

$this->require_acl ('admin', 'admin/edit');

public require_auth ($verifier, $method = false)

Require authentication via custom callbacks that are passed to simple_auth(). If the second callback is missing, the first will be assumed to be an array containing the two callbacks.

See apps/user/lib/Auth for built-in auth handlers.

public installed ($app, $version)

Check if an app and version have been installed. Returns true if installed, false if not, and current installed version if an upgrade should be performed.

public mark_installed ($app, $version)

Mark an app and version as installed.