NOTE: If you're looking for Dispatch 1.x, switch to the 1.x branch.
Dispatch is another PHP micro-framework. It's very small and very straightforward to use. No classes, no namespaces.
Dispatch requires at least PHP 5.4 to work.
However, no strict version check is being done by Dispatch, and no functions specific to 5.4 are being used. The reason for the 5.4 requirement is because 5.3's end of life has already been announced, and 5.5 has already been released.
The other reason is that the adhoc tests for Dispatch make use of 5.4's built-in web server.
Get the code on GitHub: http://github.com/noodlehaus/dispatch.
To install using composer
, have the following lines in your composer.json
file.
{
"require": {
"php": ">= 5.4.0",
...
"dispatch/dispatch": "2.*",
}
}
Then do a composer install
or composer update
to install the package.
If you don't use composer
, just download and include dispatch.php
directly in
your application.
Note that Dispatch functions are all loaded into the global namespace.
If you have access to mod_rewrite
, make sure to redirect all your PHP requests to
your app.
<IfModule mod_rewrite.c>
RewriteEngine on
# in this case, our app bootstrap file is index.php
RewriteRule !\.(js|html|ico|gif|jpg|png|css)$ index.php
</IfModule>
Some settings are needed by Dispatch, and they can be set via config()
.
<?php
// REQUIRED, base path for your views
config('dispatch.views', '../views');
// OPTIONAL, layout file to use (defaults to 'layout')
config('dispatch.layout', 'layout');
// OPTIONAL, cookie for flash messages (defaults to '_F')
config('dispatch.flash_cookie', '_F');
// OPTIONAL, specify your app's full URL
config('dispatch.url', 'http://somedomain.com/someapp/path');
// OPTIONAL, routing file to be taken off of the request URI
config('dispatch.router', 'index.php');
// you can also just pass a hash of settings in one call
config([
'dispatch.views' => '../views',
'dispatch.layout' => 'layout',
'dispatch.flash_cookie' => '_F',
'dispatch.url' => 'http://somedomain.com/somapp/path',
'dispatch.router' => 'index.php'
]);
?>
Application routes are created via calls to on($method, $path, $callback)
. Supported methods are
GET
, POST
, PUT
, DELETE
, HEAD
and PATCH
. The $method
parameter can be a single method, an
array of methods, or *
(for all methods).
<?php
// get route for index
on('GET', '/index', function () {
echo "hello, world!\n";
});
// support the route for multiple methods
on(['GET', 'POST'], '/greet', function () {
echo "hello, world!\n";
});
// handle all supported methods for a route (get, post, put, delete, head, patch)
on('*', '/multi', function () {
echo "it works!\n";
});
?>
When working on APIs, you tend to create routes that resemble resources. You can do this by
including the resource name in your route, or by scoping your route creation with a
resource($path, $routine)
call, where $path
contains the name of the resource, and
$routine
is a callable that contains routing calls.
<?php
// let's create a users resource
resource('users', function () {
on('GET', '/index', function () {
// show list of users
});
on('GET', '/:username/show', function () {
// show user details
});
});
// this is a route created outside of users
on('GET', '/about', function () {
// about page
});
?>
From the code sample, routes /users/index
and /users/:username/show
will be made. Then
outside of the users
resource, a /about
route is also made.
If your app resides in a subfolder, include this path in your dispatch.url
setting, so Dispatch
knows which parts of the REQUEST_URI
need to be removed. This URL or your app path can then be
accessed via site($path_only = false)
.
<?php
// our app lives in /mysite
config('dispatch.url', 'http://somehost.com/mysite');
on('GET', '/users', function () {
echo "listing users...";
});
// requested URI = http://somehost.com/mysite/users
// response = "listing users..."
// get your full URL
$url = site();
// get just /mysite
$path = site(true);
?>
If you don't have access to URL rewrites, and are using a file router (ie. /index.php/controller/action),
you need to specify this via dispatch.router
. The string you set this to gets stripped off of the
REQUEST_URI
before Dispatch routes the request.
<?php
// strip index.php from all route requests
config('dispatch.router', 'index.php');
on('GET', '/users', function () {
echo "listing users...";
});
// requested URI = /index.php/users
// response = "listing users..."
?>
Redirects are done via redirect($path, $code = 302, $condition = true)
. The third
parameter, $condition
, is useful if you want your redirects to happen depending on
the result of an expression.
<?php
// basic redirect
redirect('/index');
// with a custom code
redirect('/new-url', 301);
// redirect if authenticated() is false
redirect('/denied', 302, !authenticated());
?>
Method overrides set either via the _method
form field or the
'X-Http-Method-Override' header are supported. If either of these two are
present in the request, their values are respected. Below is the particular
code that describes how this is handled.
<?php
// check for method override
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']))
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
else if (params('_method'))
$method = params('_method');
else
$method = $_SERVER['REQUEST_METHOD'];
?>
In cases where you're handling PUT requests or JSON posts and you need access
to the raw http request body contents, you can use request_body()
.
For content of type application/json
and application/x-www-form-urlencoded
,
the content are automatically parsed and returned as arrays.
<?php
on('PUT', '/users/:id', function ($id) {
$data = request_body();
});
?>
Route filters let you map functions against symbols in your routes. These functions get executed when those symbols are found in the request's matching route.
<?php
// preload blog entry whenever a matching route has :blog_id in it
filter('blog_id', function ($blog_id) {
$blog = Blog::findOne($blog_id);
// scope() lets you store stuff for later use (NOT a cache)
scope('blog', $blog);
});
// here, we have :blog_id in the route, so our preloader gets run
on('GET', '/blogs/:blog_id', function ($blog_id) {
// pick up what we got from the stash
$blog = scope('blog');
render('blogs/show', array('blog' => $blog);
});
?>
To setup routines to be run before and after a request, use before($callable)
and after($callable)
respectively. The callback routines will receive two arguments - the REQUEST_METHOD
, and the REQUEST_URI
.
<?php
// setup a function to be called before each request
before(function ($method, $path) {
// setup stuff
});
// setup a function to be called after each request
after(function ($method, $path) {
// clean up stuff
});
?>
You can create custom HTTP error handlers and trigger them as well via calls to
error($code, $callback_or_string = null)
.
<?php
// create a 404 handler
error(404, function () {
echo "Oops!\n";
});
// trigger the error
error(404);
// trigger another error, with a custom message
error(500, "Something broke!");
?>
For Dispatch to work with layouts, views and partials, you need three settings:
dispatch.views
- where to find all the viewsdispatch.layout
- the layout file to use (without .html.php) from the views path- your layout, views and partials should end with
.html.php
The layout file you specify needs to contain a call to content()
. This will plug in
the contents of your view into your layout file.
<!DOCTYPE html>
<html>
<head><title>My Layout File</title></head>
<body>
<!-- this call will plug in the contents of your view -->
<?= content() ?>
</body>
</html>
With these set, you can then call render()
in the following ways:
<?php
// render a view with locals using the configured layout file
render('index', ['name' => 'joe']);
// render a view using a different layout file (mobile-layout.html.php)
render('index', ['name' => 'bob'], 'mobile-layout');
// render a view without using a layout file
render('index', ['name' => 'bob'], false);
?>
For partials, the files are expected to begin with the _
character, and can be
loaded via partial($path, $locals = [])
.
<?php
// underscore on the filename is added automatically by partial()
$html = partial('users/profile_page', array('data' => $data));
?>
JSON and JSONP responses are done via json_out($obj, $func = null)
.
<?php
// object to dump
$obj = ['name' => 'noodlehaus', 'age' => 34];
// non-cacheable json response
json_out($obj);
...
// jsonp callback name
$fxn = 'parseResponse';
// non-cacheable jsonp response
json_out($obj, $fxn);
?>
If you want to output non-cacheable content, you can do this by calling nocache()
before
outputting any content.
<?php
// output nocache headers
nocache();
echo "comes fresh, everytime!";
?>
Get and set cookie values via cookie($name, $value = null, $expire = 0, $path = '/')
.
<?php
// set a cookie
cookie('user_id', 'user-12345');
// get a cookie
$user_id = cookie('user_id');
?>
For getting and setting session values, use session($name, $value = null)
. Calls to session()
will
fail and raise an error if you have sessions disabled in your php.ini
.
If sessions are enabled, session_start()
is called automatically for you.
<?php
// set a session value
session('authenticated', false);
// get a session value
$authenticated = session('authenticated');
// remove a session variable
session('authenticated', null);
?>
Cross-request messages, or flash messages, can be done via flash($name, $message = null, $now = false)
.
<?php
// set an error message to show after a redirect
flash('error', 'You did something wrong!');
redirect('/some-page');
// .. then on your other page ..
$message = flash('error');
?>
To fetch a value from a request without regard to wether it comes from $_GET
,
$_POST
, or the route symbols, use params($name)
. This is just like Rails' params
hash.
<?php
// get 'name' from $_GET, $_POST or the route symbols
$name = params('name');
// get 'name', set a default value if not found
$name = params('name', 'stranger');
?>
During file uploads, to get consolidated info on the file, call upload($name)
, where $name
is the name of the file input field. If the file input field is an array, info is grouped
conveniently by file and returned as an array.
<?php
// get info on an uploaded file
$file = upload('photo');
?>
You can make use of ini files for configuration by calling config('source', 'myconfig.ini')
.
<?php
// load the contents of my-settings.ini into config()
config('source', 'my-settings.ini');
// load another ini file, merge it with the previous one
config('source', 'my-other-settings.ini');
// get a config value from the loaded configs
$secret = config('some.setting');
?>
Some utility functions are also provided - for getting the client's IP, for making a string HTML-safe, for making a string URL-safe, and for setting/fetching values cross-scope.
<?php
// get the client's ip
$ip = ip();
// store a value that can be fetched later
scope('user', $user);
// fetch a stored value
scope('user');
// client's IP
$ip = client_ip();
// escape a string's entities
h('Marley & Me');
// make a string url-safe
u('http://noodlehaus.github.com/dispatch');
?>
Below's the list of functions provided by Dispatch.
<?php
// routing functions
function on($method, $path, $callback)
function resource($name, $cb)
function error($code, $callback = null)
function before($callback)
function after($callback)
function filter($symbol, $callback)
function redirect($path, $code = 302, $condition = true)
// views, templates and responses
function render($view, $locals = null, $layout = null)
function partial($view, $locals = null)
function json_out($obj, $func = null)
function nocache()
// request data helpers
function params($name = null, $default = null)
function cookie($name, $value = null, $expire = 0, $path = '/')
function scope($name, $value = null)
function upload($name)
function request_body()
// configurations and settings
function config($key, $value = null)
function site($path_only = false)
// misc helpers
function flash($key, $msg = null, $now = false)
function u($str)
function h($str, $flags = ENT_QUOTES, $enc = 'UTF-8')
function ip()
// entry point
function dispatch($method = null, $path = null)
?>
Dispatch is written by Jesus A. Domingo.
The following projects served as both references and inspirations for Dispatch:
Thanks to the following contributors for helping improve this tool :)
- Kafene kafene
- Martin Angelov martingalv
- Lars larsbo