This package is abandoned and no longer maintained. The author suggests using the espresso/application package instead.

PHP with a cup of coffee

3.1.0 2019-08-21 04:40 UTC

This package is auto-updated.

Last update: 2019-12-17 12:41:32 UTC


README

PHP with a cup of coffee

Travis (.org) Codacy branch coverage Codacy grade Packagist Version PHP from Packagist GitHub code size in bytes GitHub last commit GitHub issues

Introduction

Espresso is an HTTP micro framework for your web application built with PHP. It is heavily inspired in Node's Express.

We aim to make PHP development as simple as writing a Node Js app, while using PHP awesome object oriented features, the best patterns and the FIG standards for maximum interoperability.

Installation

To install Espresso, simply run:

composer require espresso/app

Or, if you prefer an skeleton project with some directory structure you can use:

composer create-project espresso/skeleton <folder>

Please refer to the skeleton documentation to know how to use it.

NOTE: Espresso is an HTTP framework that comes with some tooling out of the box to make your life easier. If you don't need all that tooling, you might be fine just by using espresso/http-module, which is the base class for Espresso.

Quick Start

Bootstrapping Espresso is simple. To create a hello world application, simply create your front controller (index.php) in a public directory, and then write:

<?php

use Espresso\App\Espresso;
use Zend\Diactoros\Response\HtmlResponse;

// We create the application
$app = Espresso::createApp();

// We define a single get route with a callable handler
$app->get('/', static function () {
   return new HtmlResponse('Hello World'); 
});

// We run the app, processing the request and emitting a response.
$app->run();

Architecture Overview

Espresso's key word is middleware. If you don't understand what middleware is and how it works, you'll have a hard time using Espresso. I recommend you to read the following resources to learn more about the pattern:

  1. Slim framework docs on what is middleware
  2. Matthew Weier O'Phinney on the state of the PHP ecosystem before middleware
  3. Phil Sturgeon on why middleware is important
  4. Anthony Ferrara on what is the best way of implementing middleware

In Espresso everything is a middleware (Routes, Paths, Handlers, Execution Pipelines, you name it). Even the main Espresso instance is a middleware itself. A well structured Espresso app is no more than a tree of middleware. The idea behind Espresso is that you don't have to compose this tree manually, but instead use the simple Api provided by Espresso, which is very similar to the one of Express JS.

Using Espresso

There are two main actions you can do in Espresso:

  1. Append middleware
  2. Register a route

NOTE: Really, there's one action Espresso does, and that is to append middleware to the middleware queue. Routes are just middleware too, as well as paths.

Appending middleware

To append middleware, you must call the use method on the Espresso instance.

For example, to append a middleware that adds the header X-Powered-By to the response.

<?php

use Psr\Http\Message\ServerRequestInterface as Req;
use Psr\Http\Message\ResponseInterface as Res;
use Psr\Http\Server\RequestHandlerInterface as Next;

$app->use(static function (Req $req, Next $next): Res {
    return $next->handle($req)
        ->withAddedHeader('X-Powered-By', 'Espresso');
});

Registering routes

To register routes, you have the get, post, put, patch, delete and route methods. All of them take a route path and one or more handlers.

<?php

use Psr\Http\Message\ServerRequestInterface as Req;
use Psr\Http\Message\ResponseInterface as Res;
use Zend\Diactoros\Response\JsonResponse;

$app->get('/users/:id', static function (Req $req): Res {
    // We get the user id from the request
    $userId = $req->getAttribute('id');
    return new JsonResponse(['userId' => $userId]);
});

Supported types for function calls

As you have seen in the examples, we have been passing closures to these methods. But that is not very convenient for larger applications. Ideally, we need to encapsulate all that logic in their own classes.

So in Espresso, you can pass psr-15 middleware instances instead of closures.

Let's implement the same previous example but with a class.

<?php

use Psr\Http\Message\ServerRequestInterface as Req;
use Psr\Http\Message\ResponseInterface as Res;
use Zend\Diactoros\Response\JsonResponse;
use Psr\Http\Server\RequestHandlerInterface as Next;
use Psr\Http\Server\MiddlewareInterface;

class UserIdHandler implements MiddlewareInterface
{
    public function process(Req $req, Next $next) : Res
    {
        $userId = $req->getAttribute('id');
        return new JsonResponse(['userId' => $userId]);   
    }
}

$app->get('/users/:id', new UserIdHandler());

The Middleware Resolver

Espresso allows you to register both objects and closures, and much more types of arguments thanks to it's MiddlewareResolver.

The Middleware Resolver is an interface whose sole purpose is to take any type of argument and return an object implementing MiddlewareInterface.

Espresso uses the default espresso/http-module implementation which can resolve the following:

  • arrays: will recursively resolve each element and put it into a EspressoPipeline, which is a middleware that executes a chain of middleware.
  • middleware instances: It just returns it as it is.
  • request handler instances: Decorates them in a Request Handler Middleware.
  • closures: Decorates them in a Callable Middleware, and optionally binds an object to $this.
  • strings: If you provide a DI Container and a service with the string passed is available, then creates a Lazy Middleware wrapping that service.
  • strings containing @: Will split the parts, and find a service and then call a method on it. This is so you can use the Controller pattern.

Using a DI Container

Espresso can use a DI Container to resolve service names. Just pass it to the factory method and it will resolve every service that encounters.

Espresso comes with a ready-to-use Service Provider for league/container, which is the preferred choice for the skeleton.