Skip to main content
Commonmark migration
Source Link
<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }    
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
  1. First, it checks the request method, if it matches, it continues to test the structure of the URLs.

    First, it checks the request method, if it matches, it continues to test the structure of the URLs.

    The structure of the route I set on the routes.php should match the structure of the route the user is accessing.

  2. If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

  3. Then it invokes the callback function passing the URL parameters with an associative array.

  4. Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

The structure of the route I set on the routes.php should match the structure of the route the user is accessing.

  1. If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

  2. Then it invokes the callback function passing the URL parameters with an associative array.

  3. Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }   
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
  1. First, it checks the request method, if it matches, it continues to test the structure of the URLs.

The structure of the route I set on the routes.php should match the structure of the route the user is accessing.

  1. If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

  2. Then it invokes the callback function passing the URL parameters with an associative array.

  3. Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }    
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
  1. First, it checks the request method, if it matches, it continues to test the structure of the URLs.

    The structure of the route I set on the routes.php should match the structure of the route the user is accessing.

  2. If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

  3. Then it invokes the callback function passing the URL parameters with an associative array.

  4. Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

added 6 characters in body
Source Link
nick
  • 345
  • 2
  • 4
  • 10
  1. I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.

    I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.

  2. I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.

    I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.

  3. If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to maintain.

    If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to maintain.

  4. It's the first time I do MVC so it's probably full of errors or unnecessary stuff.

    It's the first time I do MVC so it's probably full of errors or unnecessary stuff.

  1. I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.
  2. I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.
  3. If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to maintain.
  4. It's the first time I do MVC so it's probably full of errors or unnecessary stuff.
  1. I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.

  2. I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.

  3. If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to maintain.

  4. It's the first time I do MVC so it's probably full of errors or unnecessary stuff.

deleted 74 characters in body
Source Link
Jamal
  • 35.2k
  • 13
  • 134
  • 238

I'm trying to do a simple CMS with PHPCMS with PHP from scratch using MVC structureMVC structure.

Yesterday I posted this: PHP Login System with persistent loginthis, which is a login system using PHP and it works but it has a handful of problems regarding the OOP aspects of it.

WhichThis is a login system using PHP and it works but it has a handfulkind of problems regarding the version 2. It doesn't have the login functionality, it's just a "routing system" that will support different request methods and supports dynamic routing OOP aspects of it(URL parameters and query strings).

So now this is kind of the version 2. It doesn't have the login functionality, it's just a "routing system" that will support different request methods and supports dynamic routing (URL parameters and query strings).


.
+-- classes
|   +-- Database.php
|   +-- Route.php
+-- controllers
|   +-- AboutUs.php
|   +-- Controller.php
+-- views
|   +-- about-us.php
+-- .htaccess
+-- index.php
+-- routes.php
.
+-- classes
|   +-- Database.php
|   +-- Route.php
+-- controllers
|   +-- AboutUs.php
|   +-- Controller.php
+-- views
|   +-- about-us.php
+-- .htaccess
+-- index.php
+-- routes.php

<?php

class Database{

    public static $host = 'localhost';
    public static $dbName = 'cms';
    public static $username = 'root';
    public static $password = '';

    private static function connect(){
        $pdo = new PDO('mysql:host='.self::$host.';dbname='.self::$dbName.';charset=utf8', self::$username, self::$password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }

    public static function query($query, $params = array()){
        $statement = self::connect()->prepare($query);
        $statement->execute($params);
        if(explode(' ', $query)[0] == 'SELECT'){
            $data = $statement->fetchAll();
            return $data;
        }
    }
}

?>
<?php

class Database{

    public static $host = 'localhost';
    public static $dbName = 'cms';
    public static $username = 'root';
    public static $password = '';

    private static function connect(){
        $pdo = new PDO('mysql:host='.self::$host.';dbname='.self::$dbName.';charset=utf8', self::$username, self::$password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }

    public static function query($query, $params = array()){
        $statement = self::connect()->prepare($query);
        $statement->execute($params);
        if(explode(' ', $query)[0] == 'SELECT'){
            $data = $statement->fetchAll();
            return $data;
        }
    }
}

?>
<?php

class Route {
    public static function get($route, $function){
        //get method, don't continue if method is not the 
        $method = $_SERVER['REQUEST_METHOD'];
        if($method !== 'GET'){ return; }

        //check the structure of the url
        $struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

        //if the requested url matches the one from the route
        //get the url params and run the callback passing the params
        if($struct){
            $params = self::getParams($route, $_SERVER['REQUEST_URI']);
            $function->__invoke($params);

            //prevent checking all other routes
            die();
        }
    }

    public static function urlToArray($url1, $url2){
        //convert route and requested url to an array
        //remove empty values caused by slashes
        //and refresh the indexes of the array
        $a = array_values(array_filter(explode('/', $url1), function($val){ return $val !== ''; }));
        $b = array_values(array_filter(explode('/', $url2), function($val){ return $val !== ''; }));

        //debug mode for development
        if(true) array_shift($b);
        return array($a, $b);
    }

    public static function checkStructure($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        //if the sizes of the arrays don't match, their structures don't match either
        if(sizeof($a) !== sizeof($b)){
            return false;
        }

        //for each value from the route
        foreach ($a as $key => $value){

            //if the static values from the url don't match
            // or the dynamic values start with a '?' character
            //their structures don't match
            if($value[0] !== ':' && $value !== $b[$key] || $value[0] === ':' && $b[$key][0] === '?'){
                return false;
            }
        }

        //else, their structures match
        return true;
    }

    public static function getParams($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        $params = array('params' => array(), 'query' => array());

        //foreach value from the route
        foreach($a as $key => $value){
            //if it's a dynamic value
            if($value[0] == ':'){
                //get the value from the requested url and throw away the query string (if any)
                $param = explode('?', $b[$key])[0];
                $params['params'][substr($value, 1)] = $param;
            }
        }

        //get the last item from the request url and parse the query string from it (if any)
        $queryString = explode('?', end($b))[1];
        parse_str($queryString, $params['query']);

        return $params;
    }
}

?>
<?php

class Route {
    public static function get($route, $function){
        //get method, don't continue if method is not the 
        $method = $_SERVER['REQUEST_METHOD'];
        if($method !== 'GET'){ return; }

        //check the structure of the url
        $struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

        //if the requested url matches the one from the route
        //get the url params and run the callback passing the params
        if($struct){
            $params = self::getParams($route, $_SERVER['REQUEST_URI']);
            $function->__invoke($params);

            //prevent checking all other routes
            die();
        }
    }

    public static function urlToArray($url1, $url2){
        //convert route and requested url to an array
        //remove empty values caused by slashes
        //and refresh the indexes of the array
        $a = array_values(array_filter(explode('/', $url1), function($val){ return $val !== ''; }));
        $b = array_values(array_filter(explode('/', $url2), function($val){ return $val !== ''; }));

        //debug mode for development
        if(true) array_shift($b);
        return array($a, $b);
    }

    public static function checkStructure($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        //if the sizes of the arrays don't match, their structures don't match either
        if(sizeof($a) !== sizeof($b)){
            return false;
        }

        //for each value from the route
        foreach ($a as $key => $value){

            //if the static values from the url don't match
            // or the dynamic values start with a '?' character
            //their structures don't match
            if($value[0] !== ':' && $value !== $b[$key] || $value[0] === ':' && $b[$key][0] === '?'){
                return false;
            }
        }

        //else, their structures match
        return true;
    }

    public static function getParams($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        $params = array('params' => array(), 'query' => array());

        //foreach value from the route
        foreach($a as $key => $value){
            //if it's a dynamic value
            if($value[0] == ':'){
                //get the value from the requested url and throw away the query string (if any)
                $param = explode('?', $b[$key])[0];
                $params['params'][substr($value, 1)] = $param;
            }
        }

        //get the last item from the request url and parse the query string from it (if any)
        $queryString = explode('?', end($b))[1];
        parse_str($queryString, $params['query']);

        return $params;
    }
}

?>
<?php

class AboutUs extends Controller{
    
}

?>
<?php

class AboutUs extends Controller{
    
}

?>
<?php

class Controller extends Database{

    public static function CreateView($viewName, $urlParams = null){
        require_once('./views/'.$viewName.'.php');
    }
}

?>
<?php

class Controller extends Database{

    public static function CreateView($viewName, $urlParams = null){
        require_once('./views/'.$viewName.'.php');
    }
}

?>
<h1>About Us</h1>
<h1>About Us</h1>
RewriteEngine On

RewriteRule ^([^/]+)/? index.php?url=$1 [L,QSA]
RewriteEngine On

RewriteRule ^([^/]+)/? index.php?url=$1 [L,QSA]
<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }   
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }   
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
<?php

Route::get('/about-us', function(){
    AboutUs::CreateView('about-us');
});

?>
<?php

Route::get('/about-us', function(){
    AboutUs::CreateView('about-us');
});

?>

1) First, it checks the request method, if it matches, it continues to test the structure of the URLs.

  1. First, it checks the request method, if it matches, it continues to test the structure of the URLs.

2) If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

3) Then it invokes the callback function passing the URL parameters with an associative array.

4) Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

  1. If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

  2. Then it invokes the callback function passing the URL parameters with an associative array.

  3. Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

BTW, I have only the GET method right now but then when I add more, I'll probably put this into a function and use that in every request method:

//check the structure of the url
$struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

//if the requested url matches the one from the route
//get the url params and run the callback passing the params
if($struct){
    $params = self::getParams($route, $_SERVER['REQUEST_URI']);
    $function->__invoke($params);

    //prevent checking all other routes
    die();
}
//check the structure of the url
$struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

//if the requested url matches the one from the route
//get the url params and run the callback passing the params
if($struct){
    $params = self::getParams($route, $_SERVER['REQUEST_URI']);
    $function->__invoke($params);

    //prevent checking all other routes
    die();
}

into a function and use that in every request method.


/users/:username
/users/:username/photos
/users/:username/photos/:photoId
/users/:username/photos/:photoId?showComments=false
/users/:username
/users/:username/photos
/users/:username/photos/:photoId
/users/:username/photos/:photoId?showComments=false
/users/jake1990
/users/jake1990/photos
/users/jake1990/photos/931280134
/users/jake1990/photos/931280134?showComments=false
/users/jake1990
/users/jake1990/photos
/users/jake1990/photos/931280134
/users/jake1990/photos/931280134?showComments=false
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: [showComments => false]];
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: [showComments => false]];

1) I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.
2) I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.
3) If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to mantain.
4) It's the first time I do MVC so it's probably full of errors or unnecessary stuff.


Thanks in advance, if there's any question about the code just ask!

  1. I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.
  2. I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.
  3. If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to maintain.
  4. It's the first time I do MVC so it's probably full of errors or unnecessary stuff.

I'm trying to do a simple CMS with PHP from scratch using MVC structure.

Yesterday I posted this: PHP Login System with persistent login

Which is a login system using PHP and it works but it has a handful of problems regarding the OOP aspects of it.

So now this is kind of the version 2. It doesn't have the login functionality, it's just a "routing system" that will support different request methods and supports dynamic routing (URL parameters and query strings).


.
+-- classes
|   +-- Database.php
|   +-- Route.php
+-- controllers
|   +-- AboutUs.php
|   +-- Controller.php
+-- views
|   +-- about-us.php
+-- .htaccess
+-- index.php
+-- routes.php

<?php

class Database{

    public static $host = 'localhost';
    public static $dbName = 'cms';
    public static $username = 'root';
    public static $password = '';

    private static function connect(){
        $pdo = new PDO('mysql:host='.self::$host.';dbname='.self::$dbName.';charset=utf8', self::$username, self::$password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }

    public static function query($query, $params = array()){
        $statement = self::connect()->prepare($query);
        $statement->execute($params);
        if(explode(' ', $query)[0] == 'SELECT'){
            $data = $statement->fetchAll();
            return $data;
        }
    }
}

?>
<?php

class Route {
    public static function get($route, $function){
        //get method, don't continue if method is not the 
        $method = $_SERVER['REQUEST_METHOD'];
        if($method !== 'GET'){ return; }

        //check the structure of the url
        $struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

        //if the requested url matches the one from the route
        //get the url params and run the callback passing the params
        if($struct){
            $params = self::getParams($route, $_SERVER['REQUEST_URI']);
            $function->__invoke($params);

            //prevent checking all other routes
            die();
        }
    }

    public static function urlToArray($url1, $url2){
        //convert route and requested url to an array
        //remove empty values caused by slashes
        //and refresh the indexes of the array
        $a = array_values(array_filter(explode('/', $url1), function($val){ return $val !== ''; }));
        $b = array_values(array_filter(explode('/', $url2), function($val){ return $val !== ''; }));

        //debug mode for development
        if(true) array_shift($b);
        return array($a, $b);
    }

    public static function checkStructure($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        //if the sizes of the arrays don't match, their structures don't match either
        if(sizeof($a) !== sizeof($b)){
            return false;
        }

        //for each value from the route
        foreach ($a as $key => $value){

            //if the static values from the url don't match
            // or the dynamic values start with a '?' character
            //their structures don't match
            if($value[0] !== ':' && $value !== $b[$key] || $value[0] === ':' && $b[$key][0] === '?'){
                return false;
            }
        }

        //else, their structures match
        return true;
    }

    public static function getParams($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        $params = array('params' => array(), 'query' => array());

        //foreach value from the route
        foreach($a as $key => $value){
            //if it's a dynamic value
            if($value[0] == ':'){
                //get the value from the requested url and throw away the query string (if any)
                $param = explode('?', $b[$key])[0];
                $params['params'][substr($value, 1)] = $param;
            }
        }

        //get the last item from the request url and parse the query string from it (if any)
        $queryString = explode('?', end($b))[1];
        parse_str($queryString, $params['query']);

        return $params;
    }
}

?>
<?php

class AboutUs extends Controller{
    
}

?>
<?php

class Controller extends Database{

    public static function CreateView($viewName, $urlParams = null){
        require_once('./views/'.$viewName.'.php');
    }
}

?>
<h1>About Us</h1>
RewriteEngine On

RewriteRule ^([^/]+)/? index.php?url=$1 [L,QSA]
<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }   
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
<?php

Route::get('/about-us', function(){
    AboutUs::CreateView('about-us');
});

?>

1) First, it checks the request method, if it matches, it continues to test the structure of the URLs.

2) If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

3) Then it invokes the callback function passing the URL parameters with an associative array.

4) Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

BTW, I have only the GET method right now but then when I add more, I'll probably put this:

//check the structure of the url
$struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

//if the requested url matches the one from the route
//get the url params and run the callback passing the params
if($struct){
    $params = self::getParams($route, $_SERVER['REQUEST_URI']);
    $function->__invoke($params);

    //prevent checking all other routes
    die();
}

into a function and use that in every request method.


/users/:username
/users/:username/photos
/users/:username/photos/:photoId
/users/:username/photos/:photoId?showComments=false
/users/jake1990
/users/jake1990/photos
/users/jake1990/photos/931280134
/users/jake1990/photos/931280134?showComments=false
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: [showComments => false]];

1) I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.
2) I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.
3) If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to mantain.
4) It's the first time I do MVC so it's probably full of errors or unnecessary stuff.


Thanks in advance, if there's any question about the code just ask!

I'm trying to do a simple CMS with PHP from scratch using MVC structure.

Yesterday I posted this, which is a login system using PHP and it works but it has a handful of problems regarding the OOP aspects of it.

This is kind of the version 2. It doesn't have the login functionality, it's just a "routing system" that will support different request methods and supports dynamic routing (URL parameters and query strings).

.
+-- classes
|   +-- Database.php
|   +-- Route.php
+-- controllers
|   +-- AboutUs.php
|   +-- Controller.php
+-- views
|   +-- about-us.php
+-- .htaccess
+-- index.php
+-- routes.php
<?php

class Database{

    public static $host = 'localhost';
    public static $dbName = 'cms';
    public static $username = 'root';
    public static $password = '';

    private static function connect(){
        $pdo = new PDO('mysql:host='.self::$host.';dbname='.self::$dbName.';charset=utf8', self::$username, self::$password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        return $pdo;
    }

    public static function query($query, $params = array()){
        $statement = self::connect()->prepare($query);
        $statement->execute($params);
        if(explode(' ', $query)[0] == 'SELECT'){
            $data = $statement->fetchAll();
            return $data;
        }
    }
}

?>
<?php

class Route {
    public static function get($route, $function){
        //get method, don't continue if method is not the 
        $method = $_SERVER['REQUEST_METHOD'];
        if($method !== 'GET'){ return; }

        //check the structure of the url
        $struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

        //if the requested url matches the one from the route
        //get the url params and run the callback passing the params
        if($struct){
            $params = self::getParams($route, $_SERVER['REQUEST_URI']);
            $function->__invoke($params);

            //prevent checking all other routes
            die();
        }
    }

    public static function urlToArray($url1, $url2){
        //convert route and requested url to an array
        //remove empty values caused by slashes
        //and refresh the indexes of the array
        $a = array_values(array_filter(explode('/', $url1), function($val){ return $val !== ''; }));
        $b = array_values(array_filter(explode('/', $url2), function($val){ return $val !== ''; }));

        //debug mode for development
        if(true) array_shift($b);
        return array($a, $b);
    }

    public static function checkStructure($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        //if the sizes of the arrays don't match, their structures don't match either
        if(sizeof($a) !== sizeof($b)){
            return false;
        }

        //for each value from the route
        foreach ($a as $key => $value){

            //if the static values from the url don't match
            // or the dynamic values start with a '?' character
            //their structures don't match
            if($value[0] !== ':' && $value !== $b[$key] || $value[0] === ':' && $b[$key][0] === '?'){
                return false;
            }
        }

        //else, their structures match
        return true;
    }

    public static function getParams($url1, $url2){
        list($a, $b) = self::urlToArray($url1, $url2);

        $params = array('params' => array(), 'query' => array());

        //foreach value from the route
        foreach($a as $key => $value){
            //if it's a dynamic value
            if($value[0] == ':'){
                //get the value from the requested url and throw away the query string (if any)
                $param = explode('?', $b[$key])[0];
                $params['params'][substr($value, 1)] = $param;
            }
        }

        //get the last item from the request url and parse the query string from it (if any)
        $queryString = explode('?', end($b))[1];
        parse_str($queryString, $params['query']);

        return $params;
    }
}

?>
<?php

class AboutUs extends Controller{
    
}

?>
<?php

class Controller extends Database{

    public static function CreateView($viewName, $urlParams = null){
        require_once('./views/'.$viewName.'.php');
    }
}

?>
<h1>About Us</h1>
RewriteEngine On

RewriteRule ^([^/]+)/? index.php?url=$1 [L,QSA]
<?php

    function loadClasses($class_name){
        $classesPath = './classes/'.$class_name.'.php';
        $controllersPath = './controllers/'.$class_name.'.php';

        if(file_exists($classesPath)){
            require_once($classesPath);
        }else if(file_exists($controllersPath)){
            require_once($controllersPath);
        }   
    }

    spl_autoload_register(loadClasses);

    require_once('routes.php');
?>
<?php

Route::get('/about-us', function(){
    AboutUs::CreateView('about-us');
});

?>
  1. First, it checks the request method, if it matches, it continues to test the structure of the URLs.
  1. If this is the case, it parses the requested URL and looks for URL parameters and query strings, using the route URL as a base.

  2. Then it invokes the callback function passing the URL parameters with an associative array.

  3. Then I use a die() statement to stop executing the code. I made this because when I have multiple routes, it will check for the structure of everyone even if one just matched.

BTW, I have only the GET method right now but then when I add more, I'll probably put this into a function and use that in every request method:

//check the structure of the url
$struct = self::checkStructure($route, $_SERVER['REQUEST_URI']);

//if the requested url matches the one from the route
//get the url params and run the callback passing the params
if($struct){
    $params = self::getParams($route, $_SERVER['REQUEST_URI']);
    $function->__invoke($params);

    //prevent checking all other routes
    die();
}
/users/:username
/users/:username/photos
/users/:username/photos/:photoId
/users/:username/photos/:photoId?showComments=false
/users/jake1990
/users/jake1990/photos
/users/jake1990/photos/931280134
/users/jake1990/photos/931280134?showComments=false
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: []];
Array -> [params => [username => jake1990, photoId => 931280134], query: [showComments => false]];
  1. I just coded the GET method in the Route class but implementing the other ones (POST, PUT, DELETE) should be easy.
  2. I don't know if the code from the routing covers all the cases but I'm trying to achieve that, so if there is a case where it doesn't work and it should, please point it out.
  3. If there's any bad practice or antipattern in my code, please tell because I'm striving to follow the best practices to make the code flexible and easy to maintain.
  4. It's the first time I do MVC so it's probably full of errors or unnecessary stuff.
Source Link
nick
  • 345
  • 2
  • 4
  • 10
Loading