1
\$\begingroup\$

In house we have been building our own MVC framework. One of the things that was important in the project was to not have to add routes manually but instead have a standard and let the index automatically handle routing based on that standard.

We want to access the URL /home/login

  • /home is the controller
  • /login is the method in the home controller

System hierarchy

  • Home
    • Controller
      • Home.php

Here is that index. Is there anything we could be doing differently or better?

We are using PHP 8.1 and NGINX with php-fpm

<?php
session_start();

// contains stuff like $engine which is etheir
// nginx or apache and the composer autoloader
require_once __DIR__.'/config.php';

if(isset($argv)){
    // this block is so we can call php index.php --uri="/controller/action"
    $options = getopt('',array('uri::'));
    
    $url = explode('/', ltrim($options['uri'],'/'));
}else{
    // Determine this engine so we know how to parse url
    if($engine == 'nginx'){
        $url = isset($_SERVER['REQUEST_URI']) ? explode('/', ltrim($_SERVER['REQUEST_URI'],'/')) : '/';
    }else if($engine == 'apache'){
        $url = isset($_SERVER['PATH_INFO']) ? explode('/', ltrim($_SERVER['PATH_INFO'],'/')) : '/';
    }
}

// Check if controller/action is provided if not load homepage
if ($url == '/' || empty($url[0])){
    require_once("{$rootPath}Home/Controller/Home.php");

    $controller = new Home();
    
    $view = $controller->index();
    
    $controller->init();
    
    if($controller->data != null){
        extract($controller->data, EXTR_PREFIX_SAME, 'wddx');       
    }

    $cssFiles = $controller->cssFiles;
    $jsFiles = $controller->jsFiles;
    $useLocalJquery = $controller->useLocalJquery;
    
    $viewPath = "{$rootPath}Home/View/{$view}.php";

    if(file_exists($viewPath)){
        require_once("{$rootPath}Template/View/template.php");
    }
}else{
    //The first element should be a controller
    $requestedController = ucfirst($url[0]); 

    // If a second part is added in the URI, 
    // it should be a method
    $requestedAction = isset($url[1]) && $url[1] != '' ? ucfirst($url[1]) : 'Index';

    // The remain parts are considered as 
    // arguments of the method
    $requestedParams = array_slice($url, 2); 

    // Check if controller exists.
    $ctrlPath = "{$rootPath}{$requestedController}/Controller/{$requestedController}.php";

    if (file_exists($ctrlPath)){
        $view = null;
        
        require_once($ctrlPath);

        $controllerObj  = new $requestedController();

        //Check that action exist or return 404
        if(!method_exists($controllerObj,$requestedAction)){
            header('HTTP/1.1 404 Not Found');
        
            require_once("{$rootPath}Template/View/404.php");
        }
        
        if($requestedAction != ''){
            $view = $controllerObj->$requestedAction($requestedParams);
        }else{
            // If no action is specified we default to loading the index
            $view = $controllerObj->index($requestedParams);
        }
        
        $controllerObj->init();
        
        if($controllerObj->data != null){
            extract($controllerObj->data, EXTR_PREFIX_SAME, 'wddx');   
        }
        
        $cssFiles = $controllerObj->cssFiles;
        $jsFiles = $controllerObj->jsFiles;
        $useLocalJquery = $controllerObj->useLocalJquery;
        
        if($view != null){
            // Store view path
            $viewPath = "{$rootPath}{$requestedController}/View/{$view}.php";

            // Check if view uses template
            if($controllerObj->useTemplate){

                $templatePath = "{$rootPath}Template/View/{$controllerObj->template}.php";

                if(file_exists($templatePath)){
                    require_once("{$rootPath}Template/View/{$controllerObj->template}.php");
                }
            }else{
                // If the view does not use the template then we just load the view
                if(file_exists($viewPath)){
                    require_once($viewPath);
                }
            }
        }
    }else{
        header('HTTP/1.1 404 Not Found');
        
        require_once("{$rootPath}Template/View/404.php");
    }
}
\$\endgroup\$
7
  • 1
    \$\begingroup\$ Why do you keep saying "we"? Sounds like you don't want ppl to think the mess is all yours. Anyway the code is so bad I would be really surprised if it ever makes it to production. Unless this is for learning purpose I recommend you try some tested working solution instead. BTW custom routes without automated magic are actually much better, IMHO... \$\endgroup\$
    – slepic
    Sep 1, 2022 at 3:40
  • 1
    \$\begingroup\$ @slepic So tell me what makes this "so bad" I only care about improving. If you have suggestions of solutions that are "superior" in your opinion then please let me know \$\endgroup\$ Sep 1, 2022 at 17:55
  • 2
    \$\begingroup\$ Sorry but one could write entire book about how to write an MVC framework. I'm suggesting to have a look on existing solutions even for learning purpose, just open their source code and see how they do it... I could also suggest looking into things like SOLID principles, specifically single responsibility and dependency inversion principles. \$\endgroup\$
    – slepic
    Sep 1, 2022 at 19:58
  • 1
    \$\begingroup\$ Please do not add redundant parentheses when using require_once. \$\endgroup\$
    – Dharman
    Mar 7, 2023 at 14:10
  • \$\begingroup\$ Do not use extract in global scope. \$\endgroup\$
    – Dharman
    Mar 7, 2023 at 14:10

1 Answer 1

2
\$\begingroup\$

Half given answer, use an existing framework - everyone regrets building their own framework (including me)

session_start() is bad - API calls wont have a session.

argv drop it, you dont want it - test properly or dont bother.

$url has a non 0 probability of being undefined.

new Home() should be treated no differently to /very/complex/path/:id

Exit early (see file_exists if statement).

Basically every mistake you could make, you made - please dont write a framework.

\$\endgroup\$
1
  • \$\begingroup\$ 1. This will never be used for an API it is purely for our website 2. drop it and replace with what exactly? \$\endgroup\$ Sep 16, 2022 at 14:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.