Introducing OpenSwoole Psr

Latest version: pecl install openswoole-22.1.2 | composer require openswoole/core:22.1.5

Overview

Psr (PHP Standards Recommendations) is the community effort of moving PHP forward through collaboration and standards. To better support the community standards, OpenSwoole Psr is a part of OpenSwoole Core since v22 (version 2022).

You have to install OpenSwoole Core library with composer require openswoole/core to use this feature.

Concepts

  • Request and Response (PSR-7: HTTP message interfaces)
  • Middleware and Stack Handler (PSR-15: HTTP Server Request Handlers)

Components

PSR-7 Request and Response

PSR-7 defines interfaces for Request and Response.

OpenSwoole provides PSR-7 implementation and you can use any PSR-7 implementation.

The PSR-7 interface provides these methods to transform Request and Response objects:

Request:

Response:

PSR-15 Middleware

Middlewares implement the PSR-15 Middleware Interface Psr\Http\Server\MiddlewareInterface:

Psr\Http\Message\ServerRequestInterface - PSR-7 request object Psr\Http\Server\RequestHandlerInterface - PSR-15 request handler

You can run codes before and after your main application and modify Request and Response with Middleware.

Example Middleware:

<?php

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

class MiddlewareA implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $requestBody = $request->getBody();
        var_dump('BEFORE');
        $response = $handler->handle($request);
        var_dump('AFTER');
        return $response;
    }
}

StackHandler

You can use StackHandler to add multiple Middlewares into your application.

OpenSwoole provides the built-in server request handler \OpenSwoole\Core\Psr\Middleware\StackHandler.

PSR RequestHandler and $server->setHandler()

You can use $server->setHandler() API to integrate with any Psr request handler implements \Psr\Http\Server\RequestHandlerInterface.

<?php

use OpenSwoole\HTTP\Server;

$server = new Server('127.0.0.1', 9501, Server::SIMPLE_MODE);
$server->setHandler(\Psr\Http\Server\RequestHandlerInterface $handler);
$server->start();

Alternatively, you can use $server->handle() API to handle Psr requests implementing Psr\Http\Message\RequestInterface:

<?php

use OpenSwoole\Core\Psr\Response;

$server->handle(function ($request) {
    // ...
    return (new Response('Hello OpenSwoole Psr'))->withHeader('x-version', '2022');;
});

// or pass in your handler

$server->handle(function ($request) use ($handler) {
    return $handler->handle($request);
});

Example

Use FastRoute package to implement a HTTP Psr server.

<?php

declare(strict_types=1);

use FastRoute\RouteCollector;
use OpenSwoole\Core\Psr\Middleware\StackHandler;
use OpenSwoole\Core\Psr\Response;
use OpenSwoole\HTTP\Server;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;

$server = new Server('127.0.0.1', 9501, Server::SIMPLE_MODE);

$server->on('start', function (Server $server) {
    echo "OpenSwoole http server is started at http://127.0.0.1:9501\n";
});

class MiddlewareA implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $requestBody = $request->getBody();
        var_dump('A1');
        $response = $handler->handle($request);
        var_dump('A2');
        return $response;
    }
}

class MiddlewareB implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $requestBody = $request->getBody();
        var_dump('B1');
        $response = $handler->handle($request);
        var_dump('B2');
        return $response;
    }
}

$dispatcher = \FastRoute\simpleDispatcher(function (RouteCollector $r) {
    $r->addRoute('GET', '/hello/{name}', function ($request) {
        $name = $request->getAttribute('name');
        return new Response(sprintf('Hello %s', $name));
    });
});

class RouteMiddleware implements MiddlewareInterface
{
    private $dispatcher;

    public function __construct($dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $routeInfo = $this->dispatcher->dispatch($request->getMethod(), $request->getUri()->getPath());

        switch ($routeInfo[0]) {
            case \FastRoute\Dispatcher::NOT_FOUND:
                return new Response('Not found', 404, '', ['Content-Type' => 'text/plain']);
            case \FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
                return new Response('Method not allowed', 405, '', ['Content-Type' => 'text/plain']);
            case \FastRoute\Dispatcher::FOUND:
                foreach ($routeInfo[2] as $key => $value) {
                    $request = $request->withAttribute($key, $value);
                }
                return $routeInfo[1]($request);
        }
    }
}

$stack = (new StackHandler())
    ->add(new RouteMiddleware($dispatcher))
    // ->add(new MiddlewareA())
    // ->add(new MiddlewareB())
;

$server->setHandler($stack);

$server->start();
Last updated on January 19, 2023