PHP Routing

Ever wondered how to create PHP routing to handle different requests? Let’s create custom php function that is going to find the route and process a HTTP GET request in its own callback function. We are going to have an exercise where you can practice your skill creating routes.

Creating Routes

In the routes.php file let’s create an array of routes. A key will be a route, and a value is a callback function that will be triggered to process the route.

The routes.php file:

<?php

return [
    '/' => function () {
        echo '<h1>Home Page</h1>';
    },
    '/login' => function (array $params) {
        echo '<h1>Login Page</h1>';
    },
];

For the first route we are going to echo Home Page. And let’s also create a login route with the Login Page title.

Creating the run() function

In the functions.php file we are going to have a function that will process routes array from the routes.php file. I am going to call the function run and pass url and routes. First of all, we should parse the url argument. For this, let’s call a parse_url function and pass url. As a result, we can get path from the url that can be used to check if the path is a key of routes array.

In case of a match, we can get a callback function and call it as a result of run function.

<?php

function run(string $url, array $routes): void
{
    $uri = parse_url($url);
    $path = $uri['path'];
    if (false === array_key_exists($uri['path'], $routes)) {
        return;
    }

    $callback = $routes[$path];
    $callback();
}

Putting routes and run() function together

In the index.php file that is the main application entry point we should include functions.php file and routes array that is located in the routes.php file. Next, we can trigger the run function and pass the REQUEST_URI value from the $_SERVER array as a first argument, and routes as second.

<?php
include_once 'functions.php';

$routes = include_once 'routes.php';

run($_SERVER['REQUEST_URI'], $routes);

Passing parameters from URL query into the callback function

Sometimes we need to pass extra parameters from url. In order to do this, let’s modify the run function. We can read the query value from the uri array prepared earlier by the parse_url function and call parse_str for query. As a result we will get key-value pair array of parameters that we can pass to the callback function.

In the login callback function we should add an argument for parameters. We can refer to a name of the parameter we would like to render on the login page. I will render a name for the Welcome message. Let’s also render parameters.

function run(string $url, array $routes): void
{
    $uri = parse_url($url);
    $path = $uri['path'];
    if (false === array_key_exists($uri['path'], $routes)) {
        return;
    }

    $callback = $routes[$path];
    $params = [];
    if (!empty($uri['query'])) {
        parse_str($uri['query'], $params);
    }
    $callback($params);
}

Homework Exercise

Time for practice. In order to better memorize the routing approach and get some practical experience here is the exercise for you. Add a new logout route that will redirect a user to login page and show the optional message “You have been successfully logged out”. A small hint, you should use the header() php function and include a query parameter for the message. Do not include the message into the query.

Adding a logout route

Let’s add a new logout route after the login route in the routes.php file. We can access the route via /logout uri and the callback function should call a header function with the location to the login route. Also, we should include a query parameter that will help to identify the redirect from logout route.

<?php

return [
    '/' => function () {
        echo '<h1>Home Page</h1>';
    },
    '/login' => function (array $params) {
        echo '<h1>Login Page</h1>';
    },
    '/logout' => function () {
        header('Location: /login?success=true');
    }
];

In the login route, let’s check if there is a success key in the params array and let’s show a message and don’t forget to add a return statement to exit the function.

<?php

return [
    '/' => function () {
        echo '<h1>Home Page</h1>';
    },
    '/login' => function (array $params) {
        echo '<h1>Login Page</h1>';
        if (!empty($params['success'])) {
            echo 'You have been successfully logged out!';
            return;
        }
        echo '<p>Welcome, ' . ucfirst($params['name'] ?? 'guest') . '!</p>';
        echo '<pre>';
        print_r($params);
    },
    '/logout' => function () {
        header('Location: /login?success=true');
    }
];