chubbyphp/chubbyphp-framework

A minimal Framework using PSR 3, PSR 7, PSR 11, PSR 15 and PSR 17.


README

Build Status Coverage Status Total Downloads Monthly Downloads Latest Stable Version Latest Unstable Version

Description

A minimal middleware based micro framework using PHP Framework Interop Group - PSR, with the goal is to achive the best combination of flexibility and simplicity by using standards.

Application workflow

Requirements

Suggest

Router

Any Router which implements Chubbyphp\Framework\Router\RouterInterface can be used.

PSR 7 / PSR 17

Installation

Through Composer as chubbyphp/chubbyphp-framework.

Usage

Aura.Router

composer require chubbyphp/chubbyphp-framework "^2.3" \
    aura/router "^3.1" zendframework/zend-diactoros "^2.0"
<?php

declare(strict_types=1);

namespace App;

use Chubbyphp\Framework\Application;
use Chubbyphp\Framework\ErrorHandler;
use Chubbyphp\Framework\Middleware\ExceptionMiddleware;
use Chubbyphp\Framework\Middleware\RouterMiddleware;
use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler;
use Chubbyphp\Framework\Router\AuraRouter;
use Chubbyphp\Framework\Router\Route;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\ResponseFactory;
use Zend\Diactoros\ServerRequestFactory;

$loader = require __DIR__.'/vendor/autoload.php';

set_error_handler([ErrorHandler::class, 'handle']);

$responseFactory = new ResponseFactory();

$app = new Application([
    new ExceptionMiddleware($responseFactory, true),
    new RouterMiddleware(new AuraRouter([
        Route::get('/hello/{name}', 'hello', new CallbackRequestHandler(
            function (ServerRequestInterface $request) use ($responseFactory) {
                $name = $request->getAttribute('name');
                $response = $responseFactory->createResponse();
                $response->getBody()->write(sprintf('Hello, %s', $name));

                return $response;
            }
        ))->pathOptions(['tokens' => ['name' => '[a-z]+']])
    ]), $responseFactory),
]);

$app->send($app->handle(ServerRequestFactory::fromGlobals()));

FastRoute

composer require chubbyphp/chubbyphp-framework "^2.3" \
    nikic/fast-route "^1.3" zendframework/zend-diactoros "^2.0"
<?php

declare(strict_types=1);

namespace App;

use Chubbyphp\Framework\Application;
use Chubbyphp\Framework\ErrorHandler;
use Chubbyphp\Framework\Middleware\ExceptionMiddleware;
use Chubbyphp\Framework\Middleware\RouterMiddleware;
use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler;
use Chubbyphp\Framework\Router\FastRouteRouter;
use Chubbyphp\Framework\Router\Route;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\ResponseFactory;
use Zend\Diactoros\ServerRequestFactory;

$loader = require __DIR__.'/vendor/autoload.php';

set_error_handler([ErrorHandler::class, 'handle']);

$responseFactory = new ResponseFactory();

$app = new Application([
    new ExceptionMiddleware($responseFactory, true),
    new RouterMiddleware(new FastRouteRouter([
        Route::get('/hello/{name:[a-z]+}', 'hello', new CallbackRequestHandler(
            function (ServerRequestInterface $request) use ($responseFactory) {
                $name = $request->getAttribute('name');
                $response = $responseFactory->createResponse();
                $response->getBody()->write(sprintf('Hello, %s', $name));

                return $response;
            }
        ))
    ]), $responseFactory),
]);

$app->send($app->handle(ServerRequestFactory::fromGlobals()));

Advanved example with Middleware before and after routing

This is an example of middleware(s) before and after the routing was done.

If you need to be able to continue without finding a route, I recommend writing a RouterMiddleware that will pass either the route or the RouteException and at the end another middleware that will convert the RouteException to a http response.

<?php

declare(strict_types=1);

namespace App;

use Chubbyphp\Framework\Application;
use Chubbyphp\Framework\ErrorHandler;
use Chubbyphp\Framework\Middleware\CallbackMiddleware;
use Chubbyphp\Framework\Middleware\ExceptionMiddleware;
use Chubbyphp\Framework\Middleware\RouterMiddleware;
use Chubbyphp\Framework\RequestHandler\CallbackRequestHandler;
use Chubbyphp\Framework\Router\FastRouteRouter;
use Chubbyphp\Framework\Router\Route;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\ResponseFactory;
use Zend\Diactoros\ServerRequestFactory;

$loader = require __DIR__.'/vendor/autoload.php';

set_error_handler([ErrorHandler::class, 'handle']);

$responseFactory = new ResponseFactory();

$app = new Application([
    new ExceptionMiddleware($responseFactory, true),
    new CallbackMiddleware(function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
        return $handler->handle($request);
    }),
    new RouterMiddleware(
        new FastRouteRouter([
            Route::get('/hello/{name:[a-z]+}', 'hello', new CallbackRequestHandler(
                function (ServerRequestInterface $request) use ($responseFactory) {
                    $name = $request->getAttribute('name');
                    $response = $responseFactory->createResponse();
                    $response->getBody()->write(sprintf('Hello, %s', $name));

                    return $response;
                }
            ))
        ]),
        $responseFactory
    ),
    new CallbackMiddleware(function (ServerRequestInterface $request, RequestHandlerInterface $handler) {
        /** @var Route $route */
        $route = $request->getAttribute('route');

        if ('hello' === $route->getName()) {
            $request = $request->withAttribute('name', 'world');
        }

        return $handler->handle($request);
    }),
]);

$app->send($app->handle(ServerRequestFactory::fromGlobals()));

Middleware

RequestHandler

Router

Webserver

Skeleton

ReactPHP

<?php

declare(strict_types=1);

namespace App;

use Chubbyphp\Framework\Application;
use React\EventLoop\Factory;
use React\Http\Server;
use React\Socket\Server as Socket;

/** @var Application $app*/
$app = ...;

$loop = Factory::create();

$socket = new Socket(8080, $loop);

$server = new Server($app);
$server->listen($socket);

$loop->run();

Swoole

Migration

Replace the code from the first block with the code of the second ones.

use Chubbyphp\Framework\Application;
use Chubbyphp\Framework\ExceptionHandler;
use Chubbyphp\Framework\Middleware\MiddlewareDispatcher;
use Chubbyphp\Framework\Router\FastRouteRouter;

$app = new Application(
    new FastRouteRouter([$route]),
    new MiddlewareDispatcher(),
    new ExceptionHandler($responseFactory, true)
);
use Chubbyphp\Framework\Application;
use Chubbyphp\Framework\Middleware\ExceptionMiddleware;
use Chubbyphp\Framework\Middleware\RouterMiddleware;
use Chubbyphp\Framework\Router\FastRouteRouter;

$app = new Application([
    new ExceptionMiddleware($responseFactory, true),
    new RouterMiddleware(new FastRouteRouter([$route]), $responseFactory),
]);

Copyright

Dominik Zogg 2019