summaryrefslogtreecommitdiff
path: root/src/Router/Router.php
blob: 1739e7c9bcd1767229b9a5bd5f9c31bc45955163 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
<?php

namespace App\Router;

use App\Errors\ErrorCode;
use App\Errors\ErrorResponse;
use App\Errors\Exception;
use App\Singleton;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;

class Router
{
  use Singleton;

  private RouteCollection $routes;
  private RouteConfigurator $configurator;

  public function __construct()
  {
    $this->routes = new RouteCollection();
    $this->configurator = new RouteConfigurator($this->routes, $this->routes);

    $this->addRoutes();
  }

  /**
   * match the current url against the routes.
   * also add preflight CORS headers on OPTIONS requests.
   */
  public function run(): Response
  {
    $request = Request::createFromGlobals();

    if ($request->isMethod("OPTIONS")) {
      $response = new Response();
      $response->headers->add([
        "Access-Control-Allow-Origin" => "*",
        "Access-Control-Allow-Methods" => "GET, POST, PUT, DELETE, OPTIONS, PATCH, HEAD",
        "Access-Control-Allow-Headers" => "X-Requested-With, Content-Type, Authorization",
      ]);

      return $response;
    }

    $context = new RequestContext();
    $context->fromRequest($request);

    try {
      $matcher = new UrlMatcher($this->routes, $context);
      $match = $matcher->matchRequest($request);

      $class = $match["_controller"][0];
      $method = $match["_controller"][1];

      return (new $class)->$method();
    } catch (Exception $exception) {
      return ErrorResponse::fromException($exception);
    } catch (ResourceNotFoundException $exception) {
      return new ErrorResponse(ErrorCode::NOT_FOUND, "404", Response::HTTP_NOT_FOUND);
    } catch (MethodNotAllowedException $exception) {
      return new ErrorResponse(ErrorCode::FORBIDDEN, "403", Response::HTTP_FORBIDDEN);
    } catch (\Exception $exception) {
      return new ErrorResponse(
        ErrorCode::UNKNOWN,
        $exception->getMessage() ?: "Unknown error occured",
        Response::HTTP_INTERNAL_SERVER_ERROR
      );
    }
  }

  /**
   * add routes from the routes file
   */
  private function addRoutes(): void
  {
    $routesClientServer = include_once(__DIR__ . "/routes_client_server.php");
    $routesClientServer($this->configurator);

    $routesServerServer = include_once(__DIR__ . "/routes_server_server.php");
    $routesServerServer($this->configurator);
  }
}