PHPackages                             gigablah/durian - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. gigablah/durian

ActiveLibrary[Framework](/categories/framework)

gigablah/durian
===============

A pungent PHP 5.5 microframework based on generator-style middleware.

0.1.0(12y ago)2582[2 issues](https://github.com/gigablah/durian/issues)MITPHPPHP &gt;=5.5.0

Since Feb 23Pushed 12y ago2 watchersCompare

[ Source](https://github.com/gigablah/durian)[ Packagist](https://packagist.org/packages/gigablah/durian)[ Docs](http://durianphp.com)[ RSS](/packages/gigablah-durian/feed)WikiDiscussions master Synced 2d ago

READMEChangelogDependencies (7)Versions (5)Used By (0)

Durian
======

[](#durian)

[![Build Status](https://camo.githubusercontent.com/ec7a375fd426c732d2b27be261577e0440e905e5b9c833f9fdbe5b5bf69eabc3/68747470733a2f2f7472617669732d63692e6f72672f67696761626c61682f64757269616e2e706e673f6272616e63683d6d6173746572)](https://travis-ci.org/gigablah/durian) [![Coverage Status](https://camo.githubusercontent.com/1838f1998c7164dcc2dfb86edcb11a5c0cfcfbc17d53b5fbc644548f1836c9d6/68747470733a2f2f636f766572616c6c732e696f2f7265706f732f67696761626c61682f64757269616e2f62616467652e706e67)](https://coveralls.io/r/gigablah/durian)

Durian is a PHP microframework that utilizes the newest features of PHP 5.5 together with lightweight library components to create an accessible, compact framework with performant routing and flexible generator-style middleware.

Why?
----

[](#why)

Because I can.

Installation
------------

[](#installation)

Use [Composer](http://getcomposer.org) to install the gigablah/durian library by adding it to your `composer.json`.

```
{
    "require": {
        "gigablah/durian": "~0.1"
    }
}
```

Usage
-----

[](#usage)

```
$app = new Durian\Application();
$app->route('/hello/{name}', function () {
    return 'Hello '.$this->params('name');
});
$app->run()->send();
```

Nothing special there. The `Application` container is based on [Pimple](http://pimple.sensiolabs.org) and inherits its functions for defining lazy loading services. We also make use of the Symfony2 `Request` object so you have access to request headers, parameters and cookies. But since this is a PHP 5.5 microframework, it has been tailored to take advantage of shiny new [generator functions](http://www.php.net/manual/en/language.generators.overview.php). We'll explore that starting with the core of our application: the handler stack.

Handlers
--------

[](#handlers)

When the application is run, the `Request` object is inserted into a `Context` object which is passed through a series of handler functions. They may read information from the request attributes, insert information into the context, create a `Response`, throw an exception and so on. Some of these functions may themselves comprise a series of functions, which are executed in the same manner as the main stack. If there are any generator functions encountered along the way, they are revisited in reverse order after the end of the stack is reached. This entire mechanism is encapsulated in the `Handler` class.

All handlers boil down to an array of callables or generator functions with an optional test function. They can be defined using `Application::handler`:

```
$responseTimeHandler = $app->handler(function () {
    // this executes before the rest of the stack
    $time = microtime(true);
    yield;
    // this executes after the rest of the stack
    $time = microtime(true) - $time;
    $this->response()->headers->set('X-Response-Time', $time);
}, function () use ($app) {
    // only execute for the master request in the debug environment
    return $this->master() && $app['debug'];
});
```

This returns a `Handler` object which can now be added to the front or back of the middleware stack:

```
$app->before($responseTimeHandler);
$app->after($someOtherHandler);
```

You can modify the entire stack with `Application::handlers`. The second parameter determines whether to replace the whole stack (true by default).

```
$app->handlers([
    $responseTimeHandler,
    new Durian\Middleware\RouterMiddleware($app)
], true);
```

Each handler can itself be a stack of handlers! You can insert more handlers into a particular handler like you would with the main application. You may think of them as events; each `Handler` that contains a stack (as opposed to a single function) iterates through all its registered functions (listeners) independently, eventually passing the output to the main application stack. The main stack itself is registered in the container as `$app['app.handler']`.

Normally, a stack will stop iterating once a response is found in the context. To change this behaviour, you can set the `terminate_on_response` option to false:

```
$app['app.handler']->options(['terminate_on_response' => false]);
```

### Context

[](#context)

See the lavish use of `$this` in the examples above? That's made possible by the automatic binding of each closure or generator to the `Context` object. Each time the application handles a request or subrequest, a new context is pushed onto the stack. Handlers and middlewares receive a `ContextProxy` which saves them the trouble of juggling between different context objects.

The context is a simple container for the `Request` and `Response` objects. It also holds the route parameters and the return values from each handler.

```
$app->route('/hello/{name}', function () {
    return $this->params('name');
})->get(function () {
    $name = $this->last();
    $request = $this->request();
    if (!$this->response()) {
        $this->response("Hello $name");
    }
});
```

`Context::last` holds the return (or yielded) value from the previous handler. This is one way to pass information downstream other than using the application container or request attributes.

### Middleware

[](#middleware)

Middlewares are simply handler functions defined as concrete classes by extending `Middleware`. The logic goes in `Middleware::run`. Middlewares have access to all context methods, so the syntax is essentially unchanged:

```
class ResponseTimeMiddleware extends Durian\Middleware
{
    public function run()
    {
        $time = microtime(true);
        yield;
        $time = microtime(true) - $time;
        $this->response()->headers->set('X-Response-Time', $time);
    }
}

// Middlewares accept the application container in the constructor
$app->before(new ResponseTimeMiddleware($app));
```

### Handler Injection

[](#handler-injection)

If a handler function returns another `Handler` or generator function, it will be inserted into the current position of the execution stack.

In essence, the handler stack is recursively iterated over as a multidimensional array.

Routing
-------

[](#routing)

Instead of the hierarchical routing syntax found in some other microframeworks, we use method chaining. The `yield` keyword allows us to pass execution to the next matching segment. Upon reaching the end of the chain, the execution flow is passed back to all generators in reverse order. Therefore, code before and after a `yield` statement will "wrap" subsequent route handlers:

```
$app['awesome_library'] = $app->share(function ($app) {
    return new MyAwesomeLibrary();
});

$app->route('/hello', function () use ($app) {
    $app['awesome_library']->performExpensiveOperation();
    yield 'Hello';
    $app['awesome_library']->performCleanUp();
})->route('/{name}', function () {
    return $this->last().' '.$this->params('name');
})->get(function () {
    return ['method' => 'GET', 'message' => $this->last()];
})->post(function () {
    return ['method' => 'POST', 'message' => $this->last()];
});
```

Why method chaining? The simple reason is that embedding the next route or method segment inside the route handler function forces us to execute the handler first before proceeding, thus potentially incurring expensive initialization code even if the request results in an error. Here, we stack the handler functions as each segment matches, and execute all of them in one go only if the route and method match is successful.

(At least, that was the original intention. Currently the framework utilizes [nikic/fast-route](https://github.com/nikic/FastRoute), which compiles all the routes into a single regex that maps to handler stack combinations.)

Note that `Application::route` starts a new segment and returns a new `Route` object. The method functions map to all the common HTTP request methods (get, post, put, delete, patch, options) and return the same `Route`. All the routing methods accept an arbitrary number of handler functions, so you can encapsulate surrounding operations (such as the ones in the example above) into a separate generator:

```
$expensiveOperation = function () use ($app) {
    $app['awesome_library']->performExpensiveOperation();
    yield;
    $app['awesome_library']->performCleanUp();
};

$app->route('/hello', $expensiveOperation, function () {
    return 'Hello';
})->route(...);
```

You don't necessarily have to chain route segments, the old-fashioned way of defining entire paths will still work fine:

```
// Routes will support GET by default
$app->route('/users');

// Methods can be declared without handlers
$app->route('/users/{name}')->post();

// Declare multiple methods separated by pipe characters
$app->route('/users/{name}/friends')->method('GET|POST');
```

### ResponseMiddleware

[](#responsemiddleware)

`ResponseMiddleware` takes care of converting return values to Symfony2 `Response` objects. Arrays will result in a `JsonResponse`. You may also manually craft a response:

```
$app->route('/tea', function () use ($app) {
    $this->response('I\'m a teapot', 418);
});
```

Or throw an exception:

```
$app->route('/404', function () {
    // Alternatively pass in an exception object as the first parameter
    $this->error('Not Found', 404);
});
```

Returning a HTTP status code also works:

```
$app->route('/fail', function () {
    return 500;
});
```

### Subrequests

[](#subrequests)

Subrequests are performed by calling `Application::run`:

```
$app->route('/song/{id:[0-9]+}', function () use ($app) {
    $id = $this->params('id');
    return [
        'id' => $id,
        'artist' => $app->run('GET', "/artists-by-song/$id")->getContent()
    ];
});
```

During a subrequest, `$this->master()` will return false.

Thrown exceptions from subrequests are not converted to `Response` objects; they should be handled in the master request.

Exception Handling
------------------

[](#exception-handling)

Whenever an exception is thrown, the application bubbles it up through all the generators in the stack.

This means that you can intercept any exception by wrapping a `yield` statement with a try/catch block:

```
$exceptionHandlerMiddleware = $app->handler(function () {
    try {
        yield;
    } catch (\Exception $exception) {
        if (!$this->master()) {
            throw $exception;
        }
        $this->response($exception->getMessage(), 500);
    }
});
```

For pretty exception traces, you can make use of the [filp/whoops](https://github.com/filp/whoops) library by including it in your composer.json:

```
{
    "require": {
        "gigablah/durian": "~0.1",
        "filp/whoops": "~1.0"
    }
}
```

Then, register `WhoopsMiddleware` as the first handler in your application:

```
$app->before(new Durian\Middleware\WhoopsMiddleware($app));
```

You may also register it for only a specific sub-stack, like the example below:

```
$app->handlers([
    new Durian\Middleware\ResponseMiddleware($app),
    new Durian\Handler([
        new Durian\Middleware\WhoopsMiddleware($app),
        new Durian\Middleware\RouterMiddleware($app)
    ])
]);
```

Just to drive home the point, you may also register it only for a specific route:

```
$app->route('/foo', new Durian\Middleware\WhoopsMiddleware($app), function () {
    throw new \Exception('bar');
});
```

Dependency Injection
--------------------

[](#dependency-injection)

Dependency injection? What's that? :)

Other than the fact that the application container is based on `Pimple`, a lightweight DIC (or service locator, if you're so inclined), no parameter matching is currently performed on route handlers. Eventually I'd like to have it implemented as an optional trait. Watch this space!

HttpKernelInterface
-------------------

[](#httpkernelinterface)

The `Application` container implements Symfony2's `HttpKernelInterface`, so you can compose it with other compatible applications via [Stack](http://stackphp.com).

Method List
-----------

[](#method-list)

### Application

[](#application)

```
$app->run($request); // handle a request or subrequest
$app->run($method, $path); // handle a HTTP method for a request path
$app->handler($callable, $condition); // wrap a callback as a Handler
$app->handlers(); // get the handler stack
$app->handlers(array $handlers, $replace); // replace or append to the stack
$app->before($callable, $condition); // prepend a handler to the stack
$app->after($callable, $condition); // append a handler to the stack
$app->route($path, ...$handlers); // start a new route segment
$app->handle($request, $type, $catch); // implement HttpKernelInterface::handle
```

### Handler

[](#handler)

```
$handler->run(); // iterate through the handler stack
$handler->handler($callable, $condition); // wrap a callback as a Handler
$handler->handlers(); // get the handler stack
$handler->handlers(array $handlers, $replace); // replace or append to the stack
$handler->context($context); // set the HTTP context
$handler->context(); // get the HTTP context
$handler->before($callable, $condition); // prepend a handler to the stack
$handler->after($callable, $condition); // append a handler to the stack
$handler->options(array $options); // set the handler options
$handler->options(); // get the handler options
```

### Route

[](#route)

```
$route->route($path, ...$handlers); // append a new route segment
$route->method($methods, ...$handlers); // register method handler(s)
$route->get(...$handlers); // register method handler(s) for GET
$route->post(...$handlers); // register method handler(s) for POST
$route->put(...$handlers); // register method handler(s) for PUT
$route->delete(...$handlers); // register method handler(s) for DELETE
$route->patch(...$handlers); // register method handler(s) for PATCH
$route->options(...$handlers); // register method handler(s) for OPTIONS
$route->head(...$handlers); // register method handler(s) for HEAD
$route->dump(); // recursively dump all routes to an array
```

### Context

[](#context-1)

```
$context->request(); // get the Request
$context->request($request, $type); // set the Request
$context->response(); // get the Response
$context->response($response); // set the Response
$context->response($content, $status, array $headers); // create a new Response
$context->error($exception); // throw an exception
$context->error($message, $status, array $headers, $code); // throw an exception
$context->master(); // check whether the current request is the master request
$context->params($key); // get a route parameter
$context->params($key, $default); // get a route parameter with fallback
$context->params(array $params); // insert route parameters
$context->params(); // get all route parameters
$context->append($output); // append a handler return value
$context->last(); // get the last handler return value
$context->clear(); // clear the current context
```

License
-------

[](#license)

Released under the MIT license. See the LICENSE file for details.

Credits
-------

[](#credits)

This project was inspired by the following:

- [koa](https://github.com/koajs/koa)
- [Martini](https://github.com/codegangsta/martini)
- [Bullet](https://github.com/vlucas/bulletphp)
- [Slim](https://github.com/codeguy/Slim)
- [Silex](https://github.com/silexphp/Silex)

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance18

Infrequent updates — may be unmaintained

Popularity15

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~5 days

Total

4

Last Release

4448d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/2c9db29bd91fb91e04478866b21aafca02baea586244fe8e0bc2181f4e16e925?d=identicon)[Gigablah](/maintainers/Gigablah)

---

Top Contributors

[![gigablah](https://avatars.githubusercontent.com/u/471275?v=4)](https://github.com/gigablah "gigablah (17 commits)")

---

Tags

frameworkmicromicroframework

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/gigablah-durian/health.svg)

```
[![Health](https://phpackages.com/badges/gigablah-durian/health.svg)](https://phpackages.com/packages/gigablah-durian)
```

###  Alternatives

[laravel/framework

The Laravel Framework.

34.6k509.9M17.0k](/packages/laravel-framework)[slim/slim

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and APIs

12.2k49.9M1.3k](/packages/slim-slim)[shopware/platform

The Shopware e-commerce core

3.3k1.5M3](/packages/shopware-platform)[clue/framework-x

Framework X – the simple and fast micro framework for building reactive web applications that run anywhere.

936736.7k8](/packages/clue-framework-x)[vlucas/bulletphp

A heierarchical resource-oriented micro-framework built on nested closures instead of route-based callbacks

41949.9k1](/packages/vlucas-bulletphp)[phprest/phprest

PHP Rest Framework.

3049.3k](/packages/phprest-phprest)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
