Skip to content

Router

Defining routes

Routes are defined in controller classes, which are then connected to the router. You will add different routes as class names and then you will run the router:

  • Router::addClass(App\HomeController::class)
  • Router::run()

The first method sets up some application endpoints and the latter method runs the application. An Exception is thrown if no valid endpoint is found.

The router instance is looking for the tags route and verbs using the attribute Route. A simple controller example could look like this:

src/SimpleHomeController.php ->

<?php

namespace App;

use Pebble\Attributes\Route;
use Pebble\Router\Request;

class SimpleHomeController {

    #[Route(path: '/', verbs: ['GET', 'POST'])]
    public function index() {
        echo "Hello world!";
    }

    #[Route(path: '/user/:username')]
    public function userGreeting(Request $request) {
        $username = $request->param('username');
        echo "Hello world $username!";
    } 
}

The route will accept the verbs GET and POST and the path / will dispatch the method index.

The second route using the path /user/:username will dispatch the method userGreeting. This method transforms the second URL segment into a string parameter, which the controller method may use. This route only accepts GET requests (which is used if no verbs is used).

The path can also be made a bit more complex, like e.g. /user/:username/actions/:action. If this route is matched, then the Request will contain both username and action keys and values.

Let's connect the above SimpleHomeController class to a router instance in an index.php file:

examples/router_simple/index.php ->

<?php

require_once "../../vendor/autoload.php";

use Pebble\Router;

// Init
$router = new Router();

// Add the controller class name to the router
$router->addClass(App\SimpleHomeController::class);

// Run the application
$router->run();

Run the above example:

php -S localhost:8000 -t examples/router_simple

If you visit http://localhost:8000, you should receive a response from the server saying hello world!

If you visit http://localhost:8000/user/helen, you should receive a response saying Hello world helen!

Error handling

If you visit a route that is not defined, you may get a 500 error without any useful message, but this depends on your server configuration.

We will make a setup in order to catch all errors. This will also deliver a better user experience:

examples/router_error/index.php ->

<?php

require_once "../../vendor/autoload.php";

use Pebble\Router;
use Pebble\Exception\NotFoundException;
use Pebble\ExceptionTrace;

try {

    $router = new Router();
    $router->addClass(App\SimpleHomeController::class);
    $router->run();
} catch (NotFoundException $e) {

    // You may show a propper '404 Not Found' page here
    echo $e->getMessage();
    echo "<pre>" . ExceptionTrace::get($e) . "</pre>";
} catch (Throwable $e) {

    // You may show a '500 Internal Server Error' page here
    // This is an application error
    echo $e->getMessage();
    echo "<pre>" . ExceptionTrace::get($e) . "</pre>";
}

You may run this example:

php -S localhost:8000 -t examples/router_error

If you visit http://localhost:8000/does/not/exists, you will get a message saying The page does not exist

You will also get a better trace of the error.

Middleware

You may add middleware to you application. Middleware are just callables which will be called before hitting the controller method. You may specify multiple middleware callables.

Middleware are called in the order that they are added to your Router instance. The middleware callables will receive the same $request parameter as your controller.

Here is a controller where we expect a message to be set on the $request object:

src/HomeController.php ->

<?php

namespace App;

use Pebble\Attributes\Route;
use Pebble\Router\Request;

class HomeController {

    #[Route(path: '/user/:username')]
    public function userGreeting(Request $request) {
        $username = $request->param('username');
        $message = $request->message;
        echo "Hello world $username!<br />";
        echo $message . "<br />";

        // Note: You can always get the current route from the request object if you need to. 
        echo "Current route is: " . $request->getCurrentRoute();
    }   
}

Create an application like this:

examples/router_middleware/index.php ->

<?php

require_once "../../vendor/autoload.php";

use Pebble\Router;
use Pebble\Exception\NotFoundException;
use Pebble\ExceptionTrace;
use Pebble\Router\Request;

try {

    $router = new Router();
    $router->addClass(App\HomeController::class);

    # middleware just add a property to the request object
    function middle_ware_1 (Request $request) {
        $request->message = 'From middle_ware_1';
    }

    function middle_ware_2 (Request $request) {
        $request->message = 'From middle_ware_2';
    }

    // Connect the middleware
    $router->use('middle_ware_1');
    $router->use('middle_ware_2');

    $router->run();
} catch (NotFoundException $e) {

    // You may show a propper '404 Not Found' page here
    echo $e->getMessage();
    echo "<pre>" . ExceptionTrace::get($e) . "</pre>";
} catch (Throwable $e) {

    // You may show a '500 Internal Server Error' page here
    // This is an application error
    echo $e->getMessage();
    echo "<pre>" . ExceptionTrace::get($e) . "</pre>";
}

Run the example:

php -S localhost:8000 -t examples/router_middleware

If you visit http://localhost:8000/user/helen, you should get the following response:

Hello world helen!
From middle_ware_2
Current route is: /user/:username

Edit this page on GitHub