justim/vice

Vice is a small web framework for easy dispatching actions for a given URL

dev-master 2015-03-11 17:13 UTC

This package is auto-updated.

Last update: 2024-04-05 03:24:12 UTC


README

Vice is a small web framework for easy dispatching actions for a given URL.

It uses some nifty tricks to make sure you don't have to;

  • Easy restriction of HTTP-methods
  • Simple AJAX response
  • Arguments filled based on their name, for easy access
  • Named parameters in the URI
  • Powerfull filters

Requirements

  • PHP >= 5.4

Installation

  • For Vice to work you only need the Vice.php file, download it and hack away
  • Also available at Packagist (Composer)

Example application

You can check the example-directory in this repository to see it all in action. You can run the example with PHPs builtin server:

php -S localhost:9000 -t example/

Now open http://localhost:9000 in your browser to see the example.

Examples

Basic example

$app = new Vice;
$app->route('/', function()
{
    echo 'Hello world!';
});
$app->run();

JSON Helper

$app = new Vice;

// route that only responds to ajax-post requests
$app->post('/users', 'is:ajax', function($json)
{
    // builtin helper, sets a header, encodes it, and dies
    $json([
        [
            'name' => 'Pizza kat',
        ]
    ]);
});

Global store

$app = new Vice('/', [
    'db' => new Database, // some database connection
]);

$app->get('/', function($db)
{
    // $db is the one that you put in the store
});

Advanced filters

$app = new Vice;
$app->registerFilter('is:logged', function()
{
    if (/* some login check */)
    {
        return [
            'name' => 'Flavored mushrooms',
        ];
    }
    else
    {
        return false;
    }
});

$app->get('/admin', 'is:logged', function($isLogged)
{
    // $isLogged would contain the result of the filter,
	// if the filter fails, then this function is never called
	echo 'Hello ' . $isLogged['name'] . '!';
});

Note: filters are run everytime they are called.

Subapps

$app = new Vice('/', [ 'db' => new Database ]);
$users = new Vice;

$users->get('<id>', function($json, $db, $id, $param)
{
    // $param('id') === $id
    $json($db->users->get($id));
});

// users subapp is available at /users and only for ajax-get-requests
// this would make the route /users/1 available
$app->get('users', 'is:ajax', $users);

Subapps share the filters and store from their parent, but not the other way around.

Multiple filters chained

$app = new Vice('/', [ 'currentUser' => 1, 'db' => new Database ]);

$app->registerFilter('is:logged', function($db, $currentUser)
{
    // check for existince of current user and return it
    return $db->users->get($currentUser) ?: false;
});

$app->registerFilter('is:admin', function($isLogged)
{
    return $isLogged['admin'] === true;
});

$app->route('admin', 'is:logged is:admin', function($isLogged)
{
    echo 'Hello ' . $isLogged['name'] . ', you are an admin!';
});

// a different way to handle this is by making a filter dependable
// on another filter. this way you don't have to specify 'is:logged'
// in your route definition
$app->registerFilter('is:admin', 'is:logged', function($isLogged)
{
	return $isLogged['admin'] === true;
});

$app->route('admin', 'is:admin', function($isLogged)
{
	// $isLogged is still available, even though it was not specified for this particular route
    echo 'Hello ' . $isLogged['name'] . ', you are an admin!';
});

Builtin filters

  • GET-request
  • POST-request
  • PUT-request
  • DELETE-request
  • AJAX-request

(by $app->get(..) or as a filter $app->route('/', 'is:get') (same for all others)

Builtin named arguments

  • $post -> function($key, $default = null)-wrapper for $_POST
  • $get -> function($key, $default = null)-wrapper for $_GET
  • $param -> function($key, $default = null)-wrapper for the parameters in the URI
  • $server -> function($key, $default = null)-wrapper for $_SERVER
  • $store -> function($key, $default = null)-wrapper for the global store
  • $filter -> function($key, $default = null)-wrapper for passed filter results
  • $ajax -> boolean that tells you if the request is an AJAX one
  • $json -> function($data) JSON helper
  • Keys from the params, store, filterResults (in that order)

Some technical insights

  • Every route is compiled to a regex, which is then matched against the current URI. When a match is found all the defined filters for that route are tested. If it is still a match, then we run the corresponding action. When the action is an app itself, the whole process is ran again, but without the prefix we already matched. After some looping something probably happened and we're done.
  • Reflection is used to determine the value of the arguments
  • It's a single class, everything else is a function

TODO

  • Some proper error handling