A super micro PHP framework based on Pimple and PSR standards
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples
src
tests/unit
.gitignore
.scrutinizer.yml
CHANGELOG.md
CONTRIBUTING.md
LICENSE
README.md
architecture.png
composer.json
composer.lock
logo.png
phpcs.xml.dist
phpunit.xml.dist

README.md

µSilex logo

µSilex

Latest Version on Packagist Software License Scrutinizer Code Quality Code Coverage Build Status

µSilex (aka micro silex) is a micro framework inspired by Pimple and PSR standards. All with less than 100 lines of code!

This project is a try to build a standard middleware framework for developing micro-services and APIs endpoints that require maximum performances with a minimum of memory footprint.

Why Pimple? Because it is lazy, consistent, fast, elegant and small (about 80 lines of code). What else?

Why PSR standards? Because it is a successful community project with a lot of good implementations (psr15-middlewares, zend stratigility, Guzzle, etc. etc.).

Why µSilex? Silex was a great framework now abandoned in favour of Symfony + Flex. This is good when you need more power and flexibility. But you have to pay a price in terms of complexity and memory footprint. µSilex it is a new project that covers a small subset of the original Silex project: a µSilex Application is just a Pimple Container implementing the PSR-15 specifications. That's it.

As a matter of fact, in the JAMStack, Docker and XaaS era, you can let lot of conventional framework features to other components in the system application architecture (i.e. caching, authentication, security, monitoring, rendering, etc. etc).

Is µSilex a replacement of Silex? No, but it could be used to build your own "Silex like" framework .

There are alternatives to µSilex? Yes of course. For example the Zend Expressive component of the Zend Framework shares similar principles. But it is not "container focused" and it is bound to zend libraries. Beside routing, Zend Expressive implements "piping" as mechanism for adding middlewares to your application.

µSilex is based on few principles:

  • keep it simple: so you can understand all your code;
  • keep it small: so you can control your project;
  • keep it fast: well, keep it faster...;
  • use PSR standards: do not reinvent the wheel;
  • adopt the middlewares architecture;
  • "one-for-all" does not exist!. And µSilex is not an exception. Select the right framework for your problem.

Have a nice day!

Install

compose require linkeddatacenter/usilex

Overview

Basically a µSilex provides the class Application that is a Pimple container that implements both the PSR-15 middleware interface and PSR-11 Container interface.

Middleware is now a very popular topic in the developer community, The idea behind it is “wrapping” your application logic with additional request processing logic, and then chaining as much of those wrappers as you like. So when your server receives a request, it would be first processed by your middlewares, and then after you generate a response it will also be processed by the same set (image from Zend Expressive).

architecture

Note that in this model, the traditional routing by controller is just an optional step in middleware pipeline.

A middleware is a piece of software that implements the PSR-15 middleware interface:

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Server\MiddlewareInterface;

class MyMiddleware implements MiddlewareInterface 
{
    public function process(
    	ServerRequestInterface $request, 
    	RequestHandlerInterface $handler
    	): ResponseInterface
    {
        //here your code that returns a response or passes the control to the handler
    }
}  

µSilex is not bound to any specific specific implementations (apart from Pimple) nor to any middleware implementation.

Instead µSilex realizes a framework to use existing standard implementations. µSilex adopts PSR-7 specifications for http messages, PSR-15 for managing http handles and middleware and PSR-11 for containers.

Usage

To bind µSilex with specific interface specifications, you need to configure some entries in the container:

  • uSilex.request: a service that instantiate an implementation of PSR-7 server request object
  • uSilex.responseEmitter: an optional callable that echoes the http. If not provided, no output is generated.
  • uSilex.exceptionHandler a callable that generates an http response from a PHP Exception. If not provided just an http 500 header with a text body is output
  • uSilex.httpHandler: a service that instantiate an implementation of PSR-15 http handler

µSilex Application exposes the run method that realize typical server process workflow:

  • creates a request using uSilex.request service
  • calls the uSilex.httpHandler
  • emits the http response calling uSilex.responseEmitter

If some php exceptions are thrown in the process, they are translated in Response by uSilex.exceptionHandler and then emitted by uSilex.responseEmitter.

The signature for uSilex.responseEmitter is function ($response) { echo ....} . The signature for uSilex.exceptionHandler is function ($exception, $request) {}.

There are tons of libraries that implement great reusable middlewares and http handlers that are fully compatible with µSilex. For example see MW library). µSilex is also compatible with lot of Silex Service Providers and with some Silex Application traits.

You can create your custom framework just selecting the the components that fit your needs. This fragment uses the Relay library for PSR-15 http handler and Diactoros for PSR-7 http messages.

require_once __DIR__.'/../vendor/autoload.php';
include "MyMiddleware.php"; // here your MyMiddleware class definition
$app = new \uSilex\Application;
$app['uSilex.request'] = \Zend\Diactoros\ServerRequestFactory::fromGlobals();
$app['uSilex.responseEmitter'] = $app->protect(function($response) {echo $response->getBody(); });
$app['uSilex.httpHandler'] = function($app) { 
    return new \Relay\Relay([new MyMiddleware($app)]); 
};
$app->run();

the µSilex service providers

out-of-the-box µSilex give to you a set of Service Providers that you can use as example to implement yours.

Provider\Psr7\DiactorosServiceProvider

Bound a µSilex application to the Zend Diactoros implementation for Psr7 specifications.

Provider\Psr7\GuzzleServiceProvider

Bound a µSilex application to the Guzzle implementation for Psr7 specifications.

Provider\Psr15\RelayServiceProvider

Bound a µSilex application to Relay, a fast, no frill implementation of the PSR-15 specifications.

Provider\Psr15\ZendPipeServiceProvider

Bound a µSilex application to MiddlewarePipe part of the zend-stratigility library Psr15 implementation.

Configuring new service providers

µSilex Services provider are normal Pimple service providers that, optionally, define the method boot. This method will be called only once by the application method boot. Use this feature only if strictly necessary. The boot method is called automatically by the Application run method.

A best practice to write a PSR-15 service provider is to allow users to declare middlewares as Pimple services and to allow users to define the middleware queue (i.e. pipeline) in an array with the name handler.queue. The handler.queue element can also be a service that resolves in an implementation of the iterable interface. For instance:

...
$app= new Application;
$app->register( new MyPsr7ServiceProvider() };
$app['my.router'] = function($app) { return new \My\RouterMiddleWare($app) };
$app['my.notfound'] = function($app) { return new \My\NotFoundMiddleWare($app) };
$app['handler.queue'] = [
	'my.router'
	'my.notfound'
];
$app['uSilex.httpHandler'] = function($app) {
   return new MyHttpHandler($app['handler.queue']);
};
$app->boot()->run();

Other tools

µSilex also provides two ready to use anti-pattern traits: \uSilex\Psr11Trait that implements a PSR-11 interface and \uSilex\ContainerAwareTrait that attach a PSR-11 container (e.g a µSilex Application) to any object.

A complete example

<?php
require_once __DIR__.'/../vendor/autoload.php';
use uSilex\Application;
use uSilex\ContainerAwareTrait;
use uSilex\Provider\Psr15\RelayServiceProvider as Psr15Provider;
use uSilex\Provider\Psr7\DiactorosServiceProvider as Psr7Provider ;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Server\MiddlewareInterface;
use Zend\Diactoros\Response\TextResponse;

class MyMiddleware implements MiddlewareInterface {
    use ContainerAwareTrait;
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
        return new \Zend\Diactoros\Response\TextResponse( $this->containerGet('message', 'Hi'));
    }
}  

$app = new Application;
$app->register(new Psr15Provider());
$app->register(new Psr7Provider());
$app['myMiddleware'] = function($app) { return new MyMiddleware($app); };
$app['message'] = 'hello world!';
$app['handler.queue'] = ['myMiddleware'];
$app->run();

See more examples in the html directory.

Developers quick start

Install docker and run

docker run --rm -ti -p 8000:8000 -v $PWD/.:/app composer bash
composer cs-fix
composer test
composer coverage
composer examples
# Until ctr-c is pressed, point your browser to http://localhost:8000/examples/
exit

Please see CONTRIBUTING for contributing details.

Credits

µSilex is inspired to the following projects: