Simple MVC Framework With PHP (Routing)

Chibuzo Miracle
4 min readAug 19, 2022

--

Part 1:Simple MVC Framework With PHP

Part 2: Simple MVC Framework With PHP (bootstrapping)

NB: To get better result on local development environments, I will advice you create a virtual host.

With this blog post, we will try to achieve something close to some popular PHP framework’s routing and further framework bootstrapping.

The aim of this post is to achieve the routing system defined below

<?php

use Core\Framework;

$app = new Framework();

$app::get('/', 'Controller', 'Method');


$app->run();

Step 1: update composer.json file

{
"name": "emiracle/simple-mvc-framework-with-php",
"description": "Simple MVC framework with PHP",
"autoload": {
"psr-4": {
"App\\": "app/",
"Core\\": "bootstrap/"
}
}
}

We added the core psr-4 route to load Core namespace files from the bootstrap folder. after which you run “composer dump-autoload” again

Step 2: create a Router.php file inside the bootstrap folder

<?php
namespace Core;

trait Router
{
private static $map;
public static function get($url, $class, $method)
{
self::cleanUrl($url);
self::cleanClass($class);
self::cleanMethod($method);

self::$map['get'][$url] = [
'class'=>$class,
'method'=>$method
];
}

public static function post($url, $class, $method)
{
self::cleanUrl($url);
self::cleanClass($class);
self::cleanMethod($method);

self::$map['post'][$url] = [
'class'=>$class,
'method'=>$method
];
}

private static function cleanUrl($url)
{

}

private static function cleanClass($class)
{

}

private static function cleanMethod($method)
{

}

public static function getMap(){
return self::$map;
}
}

This class will be the class we are going to map our routes:

The first static property map will hold an array of all registered routes.

Static methods get and post will take in the request URI, class and method and match it to their respective request method.

Static function getMap will serve as a getter to fetch the values of the private static $map method.

NB: Take note of the namespaces

Step 3: Create another trait PHP file in the bootstrap folder and name it “UrlEngine.php”, paste the below code inside it:

<?php
namespace Core;

trait UrlEngine
{
public function method()
{
return strtolower($_SERVER['REQUEST_METHOD']);
}

public function path()
{
return $_SERVER['REQUEST_URI'];
}
}

This file has two responsibilities for now.

  1. get us a lower case value of the request method
  2. get us the current path

Step 4: Create a file in the boostrap folder (This can be anything unique but I decided to go with ‘Framework.php’, you can use any unique name you wish to use but please keep it consistent across your files). Paste the below code inside.

<?php
namespace Core;

class Framework
{
use Router, UrlEngine;

/**
*
@throws \Exception
*/
public function run()
{
//run the match function to get the class and method
$callable = $this->match($this->method(), $this->path());
if (!$callable){
throw new \Exception('Oops! you are lost', 404);
}
//call the class, pass the method
//add the default namespace to the controller
$class = "App\\Controllers\\".$callable['class'];
if (!class_exists($class)){
throw new \Exception('Class does not exist', 500);
}

$method = $callable['method'];

if (!is_callable($class, $method)){
throw new \Exception("$method is not a valid method in class $callable[class]", 500);
}
$class = new $class();

//run the method
$class->$method();
return;
}

private function match($method, $url)
{
foreach (self::$map[$method] as $uri=>$call){
//does the $url have a trailing slash? if yes, remove it
//make sure the only string present is not the slash
if (substr($url, -1) === '/' && $uri != '/'){
//remove the slash
$url = substr($url, 0, -1);
}
//if our $uri does not contain a pre-slash, we add it
if ($url == $uri){
return $call;
}
}
return false;
}
}

NB: make sure you class name matches your file name as seen above.

The class below has two methods that performs two critical operations.

  1. “match”: this method checks the current request URI and matches with the $map array we have to return an array of the class and method or false if no match is found.
  2. “run”: this gets the class and method from the match method, adds our default namespace to the class, checks if the class and method exists, then calls the method on the class.

Now let’s create our first controller inside the app/Controllers folder as seen below:

<?php
namespace App\Controllers;

class HomeController
{
public function test()
{
echo "bootstrap working";
}
}

NB: take not of the namespace.

We created a method test, this method will echo “bootstrap working” if everything go as planned.

Step 5: create a web.php file in our routes folder and past in the below code

<?php

use Core\Framework;

$app = new Framework();

$app::get('/', 'HomeController', 'test');
$app::post('/', 'HomeController', 'test');$app->run();

Step 6: we include our web.php file into our index.php file in public folder. It should look something like below.

<?php 
require_once '../config/app.php';

require_once '../vendor/autoload.php';

require_once '../routes/web.php';

Step 7: Run your application, if everything goes well you should see something like below:

Part 4: Simple MVC Framework With PHP (View)

--

--

Chibuzo Miracle

B Eng A&B Engineering || Software Developer || Technical Writer