Shoes

How to Create a Custom Router Class in PHP

Are you looking for a way to hide your .PHP extension from the URL and secure your app. This article explores how you can create a custom router class to handle all your HTTP requests from the browser.

Working with Routers

When using a PHP framework, you map file paths in the address bar directly to corresponding controllers. For example, when using Laravel, if you send a get request to the contact page, the controller will look for a file called contact.blade.php to return a valid response.

You register routes on your application in the routes > web.php file to be loaded when a certain request is made. A good example provided by Laravel is:

Router::get('/', function(){
     return view('welcome');
}) 

This returns the HTML template on resources > views > welcome.blade.php. So to create a another page like contact or about us, you create another router and a second template on the resources > views folder.

It should look like this:

Router::get('about-us', function(){
     return view('about-us');
}) 

If you don’t create a router, it’ll return a 404 page or error in some frameworks.

Why Build a Custom PHP Router Class?

There are several reasons you might consider a custom router for your application:

Control Over Requests

Custom controllers allow you to create a single entry point into your application where you access all requests sent from the browser. Whenever a user sends as a $_get, $_post, or $_file request, you can call a certain class to handle the request.

Some developers also argue that you can secure your application better by using htaccess file. Since you’re using that to create a custom router, you might make your web application more secure.

Concise and Shorter Code

Most frameworks come with a lot of stuff you barely need for your application. While some can help manage users and databases, you may only want a small website with a few pages and blog posts.

Using controllers is a good idea. However, you might want to reduce your code, especially if you’re building large projects, accessing different databases, or dealing with different kinds of data.

Better Organization

In PHP, organization is key to managing large projects. If you don’t organize your code, it’s easy to get messy, making it hard to make changes when needed.

The best way to organize your code is to shorten it with functions and classes. In the example I give below, you’ll find that most repeating lines of codes are placed in a loop or templates.

Build Core Files and Folders

In your root folder, you’ll need four more folders:

  • Assets: For your assets files, including images, CSS, JavaScript, and SCSS
  • Database: For your database configuration files to connect, create, delete, and update the database
  • Includes: For your controllers
  • Views: For your templates

You also need htaccess, functions.php and directories.php files inside the root folder. Htaccess will handle all URLs passed through the website and redirect them to the views folder. The directories.php file will define constants and autoload classes for us.

Handle GET Requests Through the Htaccess File

To start, we need to redirect all URL requests to one PHP file, which will handle all requests. To do this, we’ll use rewrite rules to redirect all requests to the views/app.php file.

Add these lines of code in your .htaccess file on your root folder:

//Prevent direct access to sub-folders
Options -Indexes 

RewriteEngine on

//Choose files users can access directly via the browser
RewriteRule \.(js|css|swf|jpg|gif|png|eot|tff|woff|woff2|map|wav|scss)(\?|$) - [NC,QSA,L] 

//Redirect the user request
RewriteRule ^(.*)$ views/app.php?url=$1 [NC,L,QSA]

The code starts by disallowing users from viewing your files directly. Normally, when users navigate to a folder without the index file in PHP, they might see all files on your server. We obviously don’t want that.

They can only access select files via the browser. They are essential in the front-end, and the browser will download them automatically.

Lastly, the code rewrites all requests to the views > app.php file preventing direct access to the root folder. This can help secure your core files in the database, assets, and includes folder.

Create Your Router Class

There are several steps to create a working router class, but they’re simple and easy to understand. We’ve broken down the steps into functions, so it’s easier for you to see what each one does.

Step 1: Get the URL from the Browser

In your includes folder, create a new folder called URL and a file inside it named url.php. Inside the file create your class and add the following code:

final class url{
 
 private rawurl;
 private explodeurl;
 public table;
 public slug;


  private function gettheurl(){

        $rawurl         =   $_GET['url'];

        $this->rawurl   =   cleanurl($rawurl);

        return $this->rawurl;
    }

This is a private function that returns a valid URL. You can create a function cleanurl in the functions.php file to filter illegal characters from the URL. We’ll show you how to link in another post.

Step 2: Convert the URL into an Array

Idealy, your MySQL database will have tables representing each post type on your app. For example, one table can hold data for pages, posts, users, tasks, or projects, etc. We think this makes it easier to find your files on your database and to configure your URLs.

In most cases, pages URLs aren’t preceded by anything; you just type in the page slug directly into the URL. For instance, if you want to go to the contact page, you just type yourhost.com/contact, unlike for posts where you might type yourhost.com/post/category/post_slug.

In this tutorial we’ll assume that all URLs contain a reference to the post type (i.e., if you want to get the contact page, you key in yourhost.com/page/contact in your address bar.)

You can remove option if you like. With that, let’s create a new private method similar to the one shown below:


    private function explodetheurl(){

        $this->rawurl  =   $this->gettheurl();

        $this->explodeurl     =   explode('/', $this->rawurl);

        return $this->explodeurl;
    }

The function returns an array of the broken down URL. If your URL looks like this page/contact, you get an array that looks like Array ( [0] => page [1] => contact ).

Step 3: Sort Items Based on Tables and Slugs

At this point, we want to assume three things:

  1. If returned array has no items, the user has requested for the homepage
  2. If there’s only one item in the array, the user wants a list of all items in a table (i.e., the posts table)
  3. If there’s a second item on the array, the user wants a specific item from a table

With that, we can create methods to sort the array well to send to a class that’ll query the data from the database. We need to know which part is the table and which is the slug.

We created a method that looks like this:

private function listquery(){

        $this->explodeurl   =   $this->explodetheurl();

        $this->table        =   $this->explodeurl[0];

        if($this->table === '') $this->table = 'homepage'; 

        return $this->table;
    }

 private function singlequery(){

        $this->explodeurl   =   $this->explodetheurl();

        $this->table        =   $this->explodeurl[0];

        $this->slug         =   $this->explodeurl[1];

        return $this->table;

  }

Both functions return the table name on MySQL, but we can still access the slug property throughout the class; we don’t have to return each property. This is just to make it easier to assign the methods to one variable in the future.

To get the table and slug from another class, you might call this method since both properties are public:

public function geturlpath(){
        //We're fetching the data from the exploded url
        $this->explodeurl   =   $this->explodetheurl();

        $counturl     =   count($this->explodeurl);

        if($counturl === 1){
            //we're only getting the table because there's only one url
            return $this->table =   $this->listquery();           
        }

        if($counturl === 2){

            //we're getting the table and the action or the slug to show single item or params to search query
            return $this->table    =   $this->singlequery();
        }

}

You can now create another method that fetches the classes with the information regarding HTML templates and data from the database.

For example, we did something like this:

public function gotourl(){
        //Get the table name and post slug
        $table  =   $this->geturlpath();
        
        //we can check if the user is logged in before querying the database
        if(!isset($_SESSION['user_id'])){
            $rq     =   new login;
            //we'll need the information later to redirect the user to their page/post
            return $rq->login($table, $this->slug); 
        }

        //we check if the user is logged in 
        if(isset($_SESSION['user_id'])){ 
            //Check if a file with the same name exists in the includes directory
            if(file_exists('includes/logged_in/'.$table.'.php')){
                $rq     =   new $table;
                return $rq->$table($table, $this->slug); 
            }       
            return 'Class Doesn't Exist';
        }
}

As you can see, we first check to see whether the user is logged in. We can use a different class to redirect them to the login, registration, email verification, and password change pages.

If the user is logged in, we call a class with the same name as the table to shorten our code. To do that, you’ll need the PHP autoloader function.

Create a PHP AutoLoader Function

You don’t have to include a list of all your classes on the app.php file. The spl_autoload_register() function can register any number of classes on your app. You just need to tell it where to look.

In your directories.php file, add the following code:

//Define the app root path so we don't have to keep checking this code
define('root_path', $_SERVER["DOCUMENT_ROOT"].'/');

//define the folders with your core files
$def        =   array(
    'assets'     =>  root_path."assets/",
    'views'      =>  root_path."views/",
    'ul'         =>  root_path."includes/url", 
    'db'         =>  root_path."database/", 
    'logged_out' =>  root_path."includes/logged-out/", 
    'logged_in'  =>  root_path."includes/logged-in/",
);

foreach ($def as $key => $value) {
    define($key, $value);
}

//whenever you invoke a class, PHP will check these before it returns an error
spl_autoload_register(
    function($class){
        global $def;
        foreach ($def as $key => $value) {
            if(file_exists(constant($key)."$class.php")){
                return include constant($key)."$class.php";
            }
        }
    }
);

In the above code, we’ve declared constants so you can use them to fetch files in the views and assets folders, and included the classes in the PHP autoloader function.

That means you only need to include the directories.php file on your app.php file, and your app should start running.

Invoking the Class From Your App.php File

Since all URLs rewrite to views > app.php, you must call the class and include the directories file. This is easy. Just add this to your app.php file:

<?php

include_once '../directories.php';
include_once '../functions.php';

$url     =   new geturl;

$url->gotourl();

That is the only code you need on your app.php file, which will now act as your index.php file. You can create classes in your app’s includes and database folders, but make sure to include the directories in the spl_autoload_register() function.

Shoes

How React Render and Commit Works

React is a JavaScript library that creates user interfaces by separating the design into individu...

Shoes

PHP Was Dead 10 Years Ago, Right?

Ten years ago, tech pundits predicted the demise of PHP in web development. They claimed that the...

Shoes

The importance of SEO in Digital M...

Digital marketing is a powerful tool modern businesses use to connect with their target audience,...

Shoes

Accelerate Your Business Growth th...

Digital transformation is no longer optional for companies looking to accelerate their growth and...

Shoes

How to Create a Custom Router Clas...

Are you looking for a way to hide your .PHP extension from the URL and secure your app. This arti...

Shoes

Cloud Computing Infrastructure: Ev...

Cloud computing is renting a broad and complex infrastructure setup to provide cloud services and...

Shoes

Node Js vs JavaScript 

We think of JavaScript when designing a dynamic and interactive website and Node Js for backend d...

Shoes

What is the best OS for Web Server...

The foundational software on your web server is an Operating System (OS.) Linux is the most ...

Shoes

Learn Why the 68-Year-Old Platelet...

You might know about platelets and how they merge with damaged blood vessels to stop bleeding and...

Shoes

8 Reasons Why Virtual Reality Simu...

The only limit to how healthcare professionals can use VR is their creativity. Here, we believe t...

Shoes

Text Message Scams Targeting Denti...

Fake text messages from scammers pretending to be local banks and institutions account for the la...

Shoes

Difference Between Cloud Storage a...

Cloud technology service providers rarely spell out the terminology they use. You might find comp...