Kontynuujemy pracę nad routerem. Poznajemy pojęcie middleware i staramy się je w prosty sposób zaimplementować. Do dzieła.

Na początku przypomnienie, co już mamy:

<?php 

class TransactionController {
    public function makeTransaction($params){
        echo "Making transaction {$params['transaction']} <br>";
        echo "Recepit: {$params['receipt']} <br>";
        echo "DONE";
    }
}

class Router {

    private array $routes = [];

    public function normalizePath($path){

        $path = trim($path, '/');
        $path = "/{$path}/";
        $path = preg_replace('#[/]{2,}#', '/', $path);

        return $path;
    }

    public function add(string $method, string $path, array $controller)
  {
    $path = $this->normalizePath($path);

    $regexPath = preg_replace('#{[^/]+}#', '([^/]+)', $path);

    $this->routes[] = [
      'path' => $path,
      'method' => strtoupper($method),
      'controller' => $controller,
      'middlewares' => [],
      'regexPath' => $regexPath
    ];
  }

  public function dispatch(string $path, string $method, $container = null)
  {
    $path = $this->normalizePath($path);
    $method = strtoupper($_POST['_METHOD'] ?? $method);
    
    foreach ($this->routes as $route) {
        if (
          !preg_match("#^{$route['regexPath']}$#", $path, $paramValues) ||
          $route['method'] !== $method
        ) {
          continue;
        }
  
        array_shift($paramValues);
  
        preg_match_all('#{([^/]+)}#', $route['path'], $paramKeys);
  
        $paramKeys = $paramKeys[1];
  
        $params = array_combine($paramKeys, $paramValues);
  
        [$class, $function] = $route['controller'];
  
        $controllerInstance = $container ?
          $container->resolve($class) :
          new $class;
  
        $action = fn () => $controllerInstance->{$function}($params);
        $action();

      return;
    }
  }

}

$router = new Router;
$router->add('GET', '/transaction/{transaction}/receipt/{receipt}/', [TransactionController::class, 'makeTransaction']);
$router->dispatch('/transaction/123/receipt/helloworld/', 'GET');

// Making transaction 123
// Recepit: helloworld
// DONE

Middleware to takie coś, co wykonuje swoją robotę i przechodzi do następnego kroku. Ostatnim krokiem jest dispatch na wybrany route.

Dokonamy sobie zatem drobnej zmiany i napiszemy interfejs dla Middleware:

$router = new Router;
$router->add('GET', '/transaction/{transaction}/receipt/{receipt}/', [TransactionController::class, 'makeTransaction']);
$next = fn() => $router->dispatch('/transaction/123/receipt/helloworld/', 'GET');

interface MiddlewareInterface
{
  public function process(callable $next);
}

Teraz piszemy głupie middleware:

class StupidMiddleware implements MiddlewareInterface
{
  public function process(callable $next)
  {
    echo "This is stupid middleware speaking <br>";
    $next();
  }
}

Ok, testujemy:

class TransactionController {
    //(...)
}

class Router {
    //(...)
}

$router = new Router;
$router->add('GET', '/transaction/{transaction}/receipt/{receipt}/', [TransactionController::class, 'makeTransaction']);
$next = fn() => $router->dispatch('/transaction/123/receipt/helloworld/', 'GET');

interface MiddlewareInterface
{
  public function process(callable $next);
}

class StupidMiddleware implements MiddlewareInterface
{
  public function process(callable $next)
  {
    echo "This is stupid middleware speaking <br>";
    $next();
  }
}

$stupidMiddleware = new StupidMiddleware;
$stupidMiddleware->process($next);


// This is stupid middleware speaking
// Making transaction 123
// Recepit: helloworld
// DONE

Wykonało się i przeszło do następnej akcji. W następnej lekcji implementujemy łańcuch middleware w routerze.