Develop web APIs in PHP
using middleware with Expressive

by Enrico Zimuel
Senior Software Engineer
Zend, a Rogue Wave Company (USA)


Code Europe, Warsaw, 24th April 2018

About me

PHP

  • PHP: Hypertext Preprocessor
  • The most popular server-side language: PHP is used by 83% of all the websites (source: w3techs.com)
  • Used by Facebook, Wikipedia, Yahoo, Etsy, Flickr, Digg, etc
  • 23 years of usage, since 1995
  • Full OOP support since PHP 5

PHP 7

Released: 3 December 2015

Previous major was PHP 5, 13 July 2004

Skipped PHP 6: Unicode failure

Last release is 7.2.4 (29 March 2018)

PHP 7 performance

PHP 7 is also faster than Python 3!

Benchmark


	$a = [];
	for ($i = 0; $i < 1000000; $i++) {
	  $a[$i] = ["hello"];
	}
	
PHP 5.6 PHP 7
Memory Usage 428 MB 33 MB
Execution time 0.49 sec 0.06 sec

From 7.0 to 7.2

PHP 7.2 is 20% faster than 7.0 and 10% faster than 7.1 (source)

Moving to PHP 7

  • Badoo saved one million dollars switching to PHP 7 (source)
  • Tumblr reduced the latency and CPU load by half moving to PHP 7 (source)
  • Dailymotion handles twice more traffic with same infrastructure switching to PHP 7 (source)

PHP 7 is not only fast!

  • Return and Scalar Type Declarations
  • Improved Exception hierarchy
  • Many fatal errors converted to Exceptions
  • Secure random number generator
  • Authenticated encryption AEAD (PHP 7.1+)
  • Nullable types (PHP 7.1+)
  • Argon2 for secure password storage
  • and more!

Web APIs in PHP

HTTP IN & OUT

Example

HTTP Request:


	GET /api/version
	

HTTP Response:


	HTTP/1.1 200 OK
	Connection: close
	Content-Length: 17
	Content-Type: application/json

	{
	  "version": "1.0"
	}
		

Middleware

A function that gets a request and generates a response


	function ($request)
	{
	    // do something with $request
	    return $response;
	}
	

Delegating middleware


	function ($request, $delegate)
	{
	    // delegating $request to another middleware
	    $response = $delegate($request);
	    return $response;
	}
	

Example: cache


	function ($request, $delegate) use ($cache)
	{
	    if ($cache->has($request)) {
	        return $cache->get($request);
	    }
	    $response = $delegate($request);
	    $cache->set($request, $response);
	    return $response;
	}
	

PHP FIG

  • PHP Framework Interop Group (PHP FIG)
  • A working group for defining common standards to interop between PHP frameworks/libraries
  • PHP Standards Recommendations (PSR)
  • More information at php-fig.org

PSR-7

Common interfaces for representing HTTP messages as described in RFC 7230 and RFC 7231, and URIs for use with HTTP messages as described in RFC 3986

Example


	use Psr\Http\Message\ResponseInterface; // PSR-7
	use Psr\Http\Message\ServerRequestInterface; // PSR-7
	use Zend\Diactoros\Response\JsonResponse;

	function (ServerRequestInterface $request): ResponseInterface
	{
	    $name = $request->getAttribute('name', 'Mr. Robot');
	    return new JsonResponse(['hello' => $name]);
	}
	

e.g. GET /api/hello[/{name}]

PSR-15

Common interfaces for HTTP server request handlers and HTTP server middleware components that use HTTP messages as described by PSR-7

PSR-15: Handler


	namespace Psr\Http\Server;

	use Psr\Http\Message\ResponseInterface;
	use Psr\Http\Message\ServerRequestInterface;

	interface RequestHandlerInterface
	{
	    public function handle(
	        ServerRequestInterface $request
	    ): ResponseInterface;
	}
	

An handler returns a response, without delegate

PSR-15: Middleware


	namespace Psr\Http\Server;

	use Psr\Http\Message\ResponseInterface;
	use Psr\Http\Message\ServerRequestInterface;

	interface MiddlewareInterface
	{
	    public function process(
	        ServerRequestInterface $request,
	        RequestHandlerInterface $handler
	    ): ResponseInterface;
	}
	

A middleware participates in processing an HTTP message, it may deletegate.

Expressive 3.0

The PHP framework for middleware applications

  • PSR-7 support (using zend-diactoros)
  • PSR-15 support
  • Piping workflow (using zend-stratigility)
  • Features: routing, dependency injection, templating, error handling
  • Last release 3.0.6, 16th April 2018

Installation

You can install Expressive using composer:

composer create-project zendframework/zend-expressive-skeleton api
	

Routes


	$app->get('/api/ping', function ($request) {
	    return JsonResponse(['ack' => time()])
	});

	// or implement a RequestHandlerInterface
	$app->get('/api/ping', App\Handler\PingHandler::class);
	

PingHandler


	namespace App\Handler;

	use Psr\Http\Message\ResponseInterface;
	use Psr\Http\Message\ServerRequestInterface;
	use Psr\Http\Server\RequestHandlerInterface;
	use Zend\Diactoros\Response\JsonResponse;

	class PingHandler implements RequestHandlerInterface
	{
	    public function handle(
	        ServerRequestInterface $request
	    ) : ResponseInterface {
	        return new JsonResponse(['ack' => time()]);
	    }
	}
	

Pipeline


	$app->pipe(ErrorHandler::class);
	$app->pipe(ServerUrlMiddleware::class);
	$app->pipe(RouteMiddleware::class);
	$app->pipe(ImplicitHeadMiddleware::class);
	$app->pipe(ImplicitOptionsMiddleware::class);
	$app->pipe(MethodNotAllowedMiddleware::class);
	$app->pipe(UrlHelperMiddleware::class);
	$app->pipe(ProblemDetailsMiddleware::class);
	$app->pipe(DispatchMiddleware::class);
	$app->pipe(NotFoundHandler::class);
	
/config/pipeline.php

Route a REST API


	$app->route('/api/users[/{id}]', [
	    Authentication\AuthenticationMiddleware::class,
	    Authorization\AuthorizationMiddleware::class,
	    Api\Action\UserAction::class
	], ['GET', 'POST', 'PATCH', 'DELETE'], 'api.users');

	// or route each HTTP method
	$app->get('/api/users[/{id}]', ..., 'api.users.get');
	$app->post('/api/users', ..., 'api.users.post');
	$app->patch('/api/users/{id}', ..., 'api.users.patch');
	$app->delete('/api/users/{id}', ..., 'api.users.delete');
	

Building a Web API

  • Managing the HTTP request and response
  • Choosing a representation format
  • Choosing an error format
  • Filtering & validating input data
  • Authenticating HTTP requests
  • Authorizing HTTP requests

Tools for Web API

REST example

github.com/ezimuel/zend-expressive-api

Run PHP as Node.js

  • Run PHP in async way!
  • Use Swoole or ReactPHP
  • Very useful for web API/microservices
  • Benchmark using Expressive + Swoole: 4x faster than nginx and Apache (source)

Thanks!

More info: http://getexpressive.org

Contact me: enrico.zimuel [at] roguewave.com

Follow me: @ezimuel



Creative Commons License
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 Unported License.
I used reveal.js to make this presentation.