We are thrilled to inform you that Lancecourse is NOW INIT Academy — this aligns our name with our next goals — Read more.

It seems like you are using an ad blocker. To enhance your experience and support our website, please consider:

  1. Signing in to disable ads on our site.
  2. Sign up if you don't have an account yet.
  3. Or, disable your ad blocker by doing this:
    • Click on the ad blocker icon in your browser's toolbar.
    • Select "Pause on this site" or a similar option for INITAcademy.org.

PHP Frameworking - Routing & Autoloading & Configuration (Part 2)

By Matej Sima
Published | Updated | 36.70k

Welcome to second part of PHP Frameworking tutorial (Part 1 here), today we are going to learn how to respond and display content based on the request url, and also how to autoload our scripts (less require statements!).

Separate configuration

So far we only needed to define $viewPath, but from now on we will create more and more configurable options. We need a place where to put those options, and a place where to run initial scripts for our framework. Let's create a configuration, and a bootstrap file.

config.php:

<?php

define('BASEPATH', __DIR__);

bootstrap.php:

<?php

require('config.php');

require('core/view/viewLoader.php');
require('core/view/view.php');

$viewLoader = new ViewLoader(BASEPATH.'/views/');
$view = new View($viewLoader);

public/index.php:

<?php

require('../bootstrap.php');

$view->display('hello.php');

We have moved require calls from index file to bootstrap file, which separates the logic a bit, same as BASEPATH has been declared inside config.php.

Why should i care about autoloading?

In previous tutorial we have created just two library files we needed to include for our framework to work. Today we are going to create more and more files, and taking care of multiple require statements can get overhelming. To avoid writing tons of require statements, PHP provides us convinient way to load in files which are required to run the script properly. At begining we will use our own solution for autoloading, and later on we will implement standardized way (composer) to autoload files. Let's start by creating Autoload class:

core/autoload/autoload.php:

<?php

class Autoload{

    private $autoloadable = [];

    public function register($name, $loader = false){
        if( is_callable($loader) || $loader == false){
            $this->autoloadable[$name] = $loader;
            return;
        }
        throw new Exception('Loader must be callable '.$name);
    }

    public function load($name){
        $name = strtolower($name);
        $filepath = BASEPATH.'/core/'.$name.'/'.$name.'.php';
        if( !empty($this->autoloadable[$name]) ){
            return $this->autoloadable[$name]($name);
        }
        if( file_exists($filepath) ){
            return require($filepath);
        }
        throw new Exception($name.' is not loaded or registred for autoloading');
    }
}

Method register enables us to register a class to be loaded using a custom loader, which is a function called when class is required to be loaded. Load method is called by spl_autoload_register function, which is shown below. Everytime when we request a class which is not yet available for us, spl_autoload_register calls our load method in order to load the requested class. If we haven't registred the class which is requested to be loaded, autoloader attempts to load it by following a basic standart: core/classname/classname.php


Now with our primitive autoloading class, we can adjust our bootstrap file to autoload our files.

bootstrap.php:

<?php

require('config.php');

require('core/autoload/autoload.php');

$autoloader = new Autoload();

spl_autoload_register([$autoloader, 'load']);

$autoloader->register('viewloader', function(){
    return require(BASEPATH.'/core/view/viewLoader.php');
});

$view = new View( new ViewLoader(BASEPATH.'/views/') );

As we can see, both View & ViewLoader are being lazyloaded (loaded on demand), so we don't have to call require everytime we want to use them. They get loaded either by standart (core/...) or by registred loader ($autoloader->register...)

Now we have basic autoloader ready to be used, as well separate files for bootstraping and configuration.

Responding to different URLs

Browsing the web you may notice different paths after domain names. Let it be /contact-us, /about-us or /article/amazing-article-name. At the moment our framework shows the same reponse, without minding request URL. When you attempt to load a site, in our case when you attempt to load adress of your local server where the framework is running, browser will send a request, to the server, and request will hold an information about which URL you want to see, and we need to take advantage of that!

core/router/router.php:

<?php

class Router{

    private $routes = [];
    private $notFound;

    public function __construct(){
        $this->notFound = function($url){
            echo "404 - $url was not found!";
        };
    }

    public function add($url, $action){
        $this->routes[$url] = $action;
    }

    public function setNotFound($action){
        $this->notFound = $action;
    }

    public function dispatch(){
        foreach ($this->routes as $url => $action) {
            if( $url == $_SERVER['REQUEST_URI'] ){
                return $action();
            }
        }
        call_user_func_array($this->notFound,[$_SERVER['REQUEST_URI']]);
    }
}

Router class holds an array of routes which our site can respond to properly, and also holds a 404 callback, in case we do not have route registred for a proper response. To add a new available route, we have to use add method, passing in url, and a closure as a callback. Route callback get's executed whenever route url get's matched to current request URL. Finally we have out dispatch method, which takes current request url, and attempts to find a match in routes array, if found, calls it's callback, if any of routes does not have required URL, notFound callback is executed. 404 callback can be modified by calling setNotFound method and passing a respective callback to it.

Let's add router to bootstrap file.

bootstrap.php:

<?php

require('config.php');

require('core/autoload/autoload.php');

$autoloader = new Autoload();

spl_autoload_register([$autoloader, 'load']);

$autoloader->register('viewloader', function(){
    return require(BASEPATH.'/core/view/viewLoader.php');
});

$view = new View( new ViewLoader(BASEPATH.'/views/') );
$router = new Router();

Now we can apply our router in index file.

public/index.php:

<?php

require('../bootstrap.php');

$router->add('/',function() use ($view){
    $view->display('hello.php');
});

$router->add('/about-us',function() use ($view){
    $view->display('about.php');
});

$router->dispatch();

As you may see, we are registering two routes, / & /about-us, both of them have their own callbacks, which are executed once their URLs are requested by client. Final step is to register routes in a separate file.

routes.php:

<?php

$router->add('/',function() use ($view){
    $view->display('hello.php');
});

$router->add('/about-us',function() use ($view){
    $view->display('about.php');
});

public/index.php:

<?php

require('../bootstrap.php');
require('../routes.php');

$router->dispatch();

Thanks for reading

In next tutorial we will revise what we have learned so far, and upgrade our framework to match latest autoloading standarts, also we will add advanced router functionality, such as URL variables or implement more sophisticated & advanced object-oriented approach to code we've written.

I hope you have enjoyed PHP Frameworking tutorial so far, if you have any question or concerns, leave me a comment down below, or contact me via Facebook or Twitter.

Last updated 2024-01-11 UTC

8 Comments

Sign in to join the discussion

zooboole
zooboole
10 years ago Reply
If you have a php version lower than PHP 5.4 use this:|

private $autoloadable = array();
Auto
Auto
10 years ago Reply
First off, thanks for making this tut. I got an issue though. I am getting a fatal error having to do with the BASEPATH constant:

Notice: Use of undefined constant BASEPATH - assumed \'BASEPATH\' in C:\\xampp\\htdocs\\mvc-PHP2\\bootstrap.php on line 8

Fatal error: Uncaught exception \'Exception\' with message \'View does not exist: BASEPATH/views/hello.php\'

Followed everything exactly, just can\'t get past. Maybe I\'m missing something simple, please enlighten me.
Auto
Auto
10 years ago Reply
I just could not let it go. I kept at it. Something so simple as a constant that just doesn\'t seem to work was racking my mind, I could understand if it was some very complex operations but... Anyhow I stepped back, relaxed and cleared my head by just watching a little tv and throwing in a movie which I skipped through. In any event, that kinda allowed me to look at what was going on from a high level, i.e What is this constant BASEPATH doing anyway ? What\'s it purpose to begin with ? Its just a pointer to the root directory at the end of the day. Why does it need to be a in its own separate file \"config.php\" when \"bootstrap.php\" is at the same directory level ? It doesn\'t. So I did away with requiring \"config.php\" altogether. No need. Just moved defining BASEPATH to \"boostrap.php\"


/*boostrap.php */
define(\'BASEPATH\', dirname(realpath(__FILE__)));
// Samething
define(\'BASEPATH\', __DIR__);

I don\'t know why this caused an error in the first place.
zooboole
zooboole
10 years ago Reply
**Auto default**, The `config.php` becomes very useful when your project becomes bigger. This file could contain other settings your app could need. Now for you bringing it into bootstrap does not cause any issue as far as I am concerned.

But one thing you should also consider is your PHP Version. Can you please tell us the version of PHP you are using? Thanks.
zooboole
zooboole
10 years ago Reply
You can also check out this comment : [http://phpocean.com/tutorials/back-end/php-frameworking-introduction-part-1/9#4](http://phpocean.com/tutorials/back-end/php-frameworking-introduction-part-1/9#4)
Auto
Auto
10 years ago Reply
**Zooboole**, I\'m running PHP 5.6.3 and I did play around with the directory path as per your suggestion but some reason BASEPATH is just not making it to /public/index.php

And I agree with about \"config.php\" and it role down the road but for now I\'m going to go with having BASEPATH in \"bootstrap.php\" so I can move forward with this tutorial. Afterwards I\'ll come back to it.
zooboole
zooboole
10 years ago Reply
Great, I will also give a closer look at it. If anything I will surely let you know.
Auto
Auto
10 years ago Reply
I added the router class, instantiated it in \"bootstrap.php\" and added the routes in separate \"routes.php\" file which is required in \"index.php\" but I\'m still getting a 404 not found! page

404 - /mvc-PHP2/public/about-us was not found!;

after this code is in place:

$router->add(\'/about-us\', function() use ($view) {
$view->display(\'about.php\');

And I did create a test page \"about.php\" in the views folder.