Download the PHP package gigablah/durian without Composer
On this page you can find all versions of the php package gigablah/durian. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download gigablah/durian
More information about gigablah/durian
Files in gigablah/durian
Package durian
Short Description A pungent PHP 5.5 microframework based on generator-style middleware.
License MIT
Homepage http://durianphp.com
Informations about the package durian
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?
Because I can.
Installation
Use Composer to install the gigablah/durian library by adding it to your composer.json
.
Usage
Nothing special there. The Application
container is based on Pimple 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. We'll explore that starting with the core of our application: the handler stack.
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
:
This returns a Handler
object which can now be added to the front or back of the middleware stack:
You can modify the entire stack with Application::handlers
. The second parameter determines whether to replace the whole stack (true by default).
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:
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.
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
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:
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
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:
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, 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:
You don't necessarily have to chain route segments, the old-fashioned way of defining entire paths will still work fine:
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:
Or throw an exception:
Returning a HTTP status code also works:
Subrequests
Subrequests are performed by calling Application::run
:
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
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:
For pretty exception traces, you can make use of the filp/whoops library by including it in your composer.json:
Then, register WhoopsMiddleware
as the first handler in your application:
You may also register it for only a specific sub-stack, like the example below:
Just to drive home the point, you may also register it only for a specific route:
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
The Application
container implements Symfony2's HttpKernelInterface
, so you can compose it with other compatible applications via Stack.
Method List
Application
Handler
Route
Context
License
Released under the MIT license. See the LICENSE file for details.
Credits
This project was inspired by the following:
All versions of durian with dependencies
pimple/pimple Version ~1.0
symfony/http-foundation Version >=2.3,<2.5-dev
symfony/http-kernel Version >=2.3,<2.5-dev
nikic/fast-route Version dev-master