Holkerveen Logo

Build Your Own PHP Framework

In this post we will actually start implementing out own framework. I assume you have installed php locally. We will now create our front controller and add routing setup.


Creating a front controller

The front controller will be the main entrypoint to our application. It is the first PHP file that is called and is tasked with bootstrapping the application and then running it.

Choose a project directory and open it in your favourite IDE. The project should be completely empty at this point. Let's create our first file and test it.

<?php
echo "Hello world"

Test your file by starting the php built-in server:

php -S localhost:8000 public\index.php

Using composer

Composer is the pretty much universal package manager for php. Let's make our project a composer project. Install composer using the instructions on their website. Then, create a composer.json file in your project root:

{
"name": "your-name/your-framework",
"autoload": {
"psr-4": {
"Framework\\": "src/"
}
},
"require": {}
}

Next, run composer install to generate the autoloader. We will load that in our public/index.php file:

<?php // public/index.php
require(__DIR__ . "../vendor/autoload.php");
echo "Hello World!";

With this setup in place we can now install and use composer packages where we want. We can also use the autoloader to load our files, as long as these files reside in the src/ dir and in the Framework namespace. Check the composer.json for the exact path/namespace mapping.

Now, we cannot use the PSR-4 importer provided by composer in index.php because composer is not yet loaded. That is why we will now create an Application class, load that from index.php and run our application from there.

The new entry point looks like:

<?php // public/index.php
require(__DIR__ . "/../vendor/autoload.php");
require(__DIR__ . "/../src/Application.php");
$application = new \Framework\Application();
$response = $application->run();
echo $response;

Then, let's implement the Framework\Application class in our second PHP file:

<?php // src/Application.php
namespace Framework;
class Application {
public function run(): string {
return "Hello World!";
}
}

What we do now is setup the application in our entry point index.php and let Application.php serve as our front controller.

At this point you will probably want to test your application again. Start the server again as you did before and open your browser. You should still see the 'Hello World' thing.

One more thing: I have been working on the framework as discussed here in parallel. At this point I created a git repository so you can check out the state of the files you should have. If something is not working for you, you might be able to find any errors by comparing with the repository. I will share links to the repository at certain commits so you can easily check it out. Github repository at this point

Creating a basic router

The router's job is to execute the right code based on which URL is requested by the user. We have the REQUEST_URI server variable available which holds the path and query string of the url.

Let's revisit the request flow diagram from the previous episode:

Mermaid render error: Command failed: npx mmdc --input - --output - -e svg --backgroundColor none

As you can see, the front controller (our Application class) will call the router and get a callable back (the controller). That callable will get called and its response given back to the Front Controller. The Front controller can then output the response for the user.

So, let's modify Application.php and create an implementation for the run function that gets a callable from the router, calls the callabe and return the result:

<?php // src/Application.php
namespace Framework;
class Application {
public function run(): string {
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$router = new Router();
$callable = $router->getController($path);
return $callable();
}
}

Now, we need to implement the Router class. As you can see the class should have a getController function that gets the path and returns a callable. Here is what the router looks like:

<?php // src/Router.php
namespace Framework;
class Router
{
public function getController(string $path): callable
{
$home = fn() => "Hello World!";
$test = fn() => "Test route";
return match ($path) {
'/' => $home,
'/test' => $test,
};
}
}

For now, we have defined two test routes in the router itself. We will be replacing them with proper routes in the next episode.

If you now restart your server:

php -S localhost:8000 public\index.php

and open http://localhost:8000, you should see the "Hello World!" message again. If you now navigate to http://localhost:8000/test, you should see the second message 'Test route'!

Check the full state of the current project at GitHub

Next

Now that we have a basic router working, let's start with the MVC part of our framework! Next up - creating our controllers!

Navigation

Recent posts