PHP Tutorial: How to make your own PHP framework [part 2 of 2]

13th August 2022, 11:05

This is the second part in our “How to make your own PHP framework”-tutorial. Check out the first part of the tutorial in PHP Tutorial: How to make your own PHP framework [part 1 of 2].

If you’re using Wamp or LAMP or any other application that uses Apache2, you should add an .htaccess file, next to the index.php, with the following contents:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php [NC,L,QSA]

The above file will ensure that any url requested, will always be processed by our index.php, unless the requested url is an existing file.

If you’re using Nginx you can add the following code:

location / {
    try_files $uri $uri/ /index.php;
}

In part 1 of the tutorial we succesfully displayed the following message:

Hoorah! We finished the first part of the tutorial!

Routing

The first thing we’ll do is add the functionality of registering new routes to our framework.

Replace the contents of the Application addRoutes method with the following code:

    public function addRoutes(array $routes)
    {
        $this->routes = array_merge($this->routes, $routes);
    }

The above code stores the routes we add, directly into the Application itself in a variable named routes. By using an array_merge we can call $app->addRoutes multiple times to keep adding new routes.

Now we’re going to move on to the part of checking the url to see if it matches any of our registered routes. Firstly we’re going to update our run method to the following:

class Application
{

    ...

    public function run()
    {
        try {
            foreach ($this->routes as $url => $closure) {
                if ($url == $this->request->getPath()) {
                    echo $closure->call($this, ...[$this->request]);
                    exit;
                }
            }

            echo $this->render("templates/404.php", [
                "request" => $this->request
            ]);

        } catch (\Exception $exception) {
            echo $this->render("templates/error.php", [
                "exception" => $exception
            ]);
        }
    }

That’s a whole bunch of code, but what does it do?

  • When we call the method $app->run() we’re going to iterate over each of our registered routes
    Every route is stored by it’s url as a key and our closure as the callback
  • For each of our routes, we check if the url matches the current request url
  • If a route exists with exactly the same url as the one we want to navigate to, we execute the closure
  • We use the echo method to display our rendered template
  • And finally after executing our closure, we stop PHP from preventing any further code by calling exit
  • If no route was found for the requested url, we’ll render a 404 Error page

If for whatever reason an Exception occurs while executing the closure or calling the render method, we’ll show the error that was triggered.

When running the application, you’ll probably get an exception saying Application->render does not exist and that’s correct. So let’s move on to creating our render method.

Rendering

Under the run method in our Application class, add a new method called render. This method will accept two arguments: template and data.

    public function run()
    {
        ...
    }

    public function render(string $template, array $data = [])
    {

    }

In this render function we want to do two things

  • Check if a template exists and render it
  • Trigger an error when the template doesn’t exist

To achieve the above we’ll want to add the following code to our Application render method:

    public function render(string $template, array $data = [])
    {
        if (file_exists($template) == false) {
            throw new \Exception("The template file \"{$template}\" could not be found");
        }

        ob_start();
        extract($data);
        include $template;
        $contents = ob_get_contents();
        ob_end_clean();

        return $contents;
    }

The above code looks kinda wacky, and that’s for a reason.

  • We check if the template we want to render exists by calling the file_exists function. If the template file does not exist, we throw an Exception
  • If the template does exist, we call ob_start() to turn on output buffering. This will prevent PHP from immediately showing our template
  • To make our data available for our template, we use the extract function. This will make each variable in our data available as a separate variable.
  • Next we use include to render our template
  • We call ob_get_contents() to get the output from our include method and store it in a variable called contents
  • Finally we call ob_end_clean() to let PHP resume it’s not output behaviour.

Now that we have rendered our template and have the contents store in our contents variable, we return the contents variable.

The templates

Now we can wrap up things by creating our actual template parts. In the code we just wrote, we can see we need at least two templates, templates/error.php and templates/404.php

You can create these two new files, by creating a folder named templates and creating the files error.php and 404.php.

In the file error.php we want to add something like this:

<html>
    <head>
        <title>My PHP framework! | An error occurred</title>
    </head>
    <body>
        <h1>An error occurred</h1>
        <?= $exception->getMessage() ?>
    </body>
</html>

In the file 404.php we want to add the following:

<html>
    <head>
        <title>My PHP framework! | Page not found</title>
    </head>
    <body>
        <h1>Page not found</h1>
        <p>The requested page "<?= $request->getPath() ?>" does not exist.</p>
    </body>
</html>

In the first part of this tutorial we also added a route for /my-page. Now is the time to create the template for it.

Create a new file in the templates folder called my-page.php and add:

<html>
    <head>
        <title>My PHP framework!</title>
    </head>
    <body>
        <h1><?= $title ?></h1>
        <?= $content ?>
    </body>
</html>

Finishing up

Now before we test our framework, let’s add another route for the url /my-page-with-an-error:

$app = new Application();
$app->addRoutes([
    "/my-page" => function (Request $request) {
        return $this->render("templates/my-page.php", [
            "title" => "I made this myself!",
            "content" => "Hello from my page!"
        ]);
    },
    "/my-page-with-an-error" => function (Request $request) {
        return $this->render("templates/this-template-does-not-exist.php");
    }
]);
$app->run();

Now if all goes well we should be able to see the following if we navigate to /my-page:

Very nice. Navigate to the url /my-page-with-an-error and you should be see:

Cool and good. Now finally we want to check if the 404 page is working correctly so go to any random url, for example /404 or /asdasipojfeoifjes or /i-should-see-a-404 and you should see:

What we learned

These are the basics of any modern framework. Most frameworks are obviously a lot more complex and can have dynamic routes, middleware, database-driven templates, caching and a lot more but in essence; what we made is the basis for any framework.

Thank you for following this tutorial, we hope you liked it and learned a little more about frameworks.


Comments

  1. Hairstyles says:

    Its such as you learn my mind! You appear to grasp a lot about this, such as you wrote the guide in it or something. I feel that you simply could do with some percent to force the message house a bit, but other than that, this is excellent blog. A great read. I’ll certainly be back.

  2. Gal Jerman says:

    Top site ,.. amazaing post ! Just keep the work on !

  3. Gal Straman says:

    Hm,.. amazing post ,.. just keep the good work on!

  4. Gal Straman says:

    Top ,.. I will save your website !

Leave a Reply

Your email address will not be published. Required fields are marked *


Recent posts

PHP Tutorial: How to make your own PHP framework [part 1 of 2]

We’ve all been there; “how does a framework work?”, “what is the purpose of a framework?” or maybe even “how […]

Read more
If-statements

This is a post about if statements and how to use them accordingly (depending on your language of course).

Read more