Search code examples
phploopsurl-routingdirectory-structure

Controller Targeting / Directory Routing


I am currently working with my Router class where I have this function that analyzes/parses the URI, and targets the controller, method, and arguments.

In this function, I have to target the actual controller which could be placed within a directory or bunch of subdirectories e.g. "other-category/some-directory/actual-controller.php".

Currently, I am manually checking it level by level where I have to put a conditional statement for each level.

In this approach, the depth of the directory that could be possibly checked for the actual controller only depends on the number of the conditional statements. So I'm finding this a little messy and definitely limited.

I'm thinking and searching for a better way to check this, something that I don't need to do those conditional statements. If you could help me end up with a better way, that would be greatly appreciated.

This is the part where I target the controller segment:

$this->target_controller = array_shift($segments);

// check if there's a first-level directory
if(is_dir(CONTROLLERPATH.$this->target_controller)){
    $this->dirs[] = $this->target_controller;
    $this->target_controller = array_shift($segments);
}

// check if there's a second-level directory
if(isset($this->dirs[0])){
    if(is_dir(CONTROLLERPATH.$this->dirs[0]."/".$this->target_controller)){
        $this->dirs[] = $this->target_controller;
        $this->target_controller = array_shift($segments);
    }
}       

// check if there's a third-level directory
if(isset($this->dirs[0]) && isset($this->dirs[1])){
    if(is_dir(CONTROLLERPATH.$this->dirs[0]."/".$this->dirs[1]."/".$this->target_controller)){
        $this->dirs[] = $this->target_controller;
        $this->target_controller = array_shift($segments);
    }
}

// check if there's a fourth-level directory
if(isset($this->dirs[0]) && isset($this->dirs[1]) && isset($this->dirs[2])){
    if(is_dir(CONTROLLERPATH.$this->dirs[0]."/".$this->dirs[1]."/".$this->dirs[2]."/".$this->target_controller)){
        $this->dirs[] = $this->target_controller;
        $this->target_controller = array_shift($segments);
    }
}

The first line of code,in the above snippet , assigns the segment to the target_controller property. Then the conditional statements below will check if the actual controller is placed within a directory or subdirectories e.g. "other-category/some-directory/actual-controller.php".


Solution

  • If your segments are safe...

    If your segments don't contain .. which may lead to getting files outside CONTROLLERPATH, you can use the code below.

    Code

      $this->dirs = array (CONTROLLERPATH);
      while (count($segments) && is_dir(implode('/', $this->dirs).'/'.($element = array_shift($segments))))
      {
          $this->dirs[] = $element;
      }
      array_shift($this->dirs);
      $this->target_controller = $element;
    

    Demo

    <?php
    
    define('CONTROLLERPATH', './'); 
    
    class A
    {
    
        public function test()
        {
            $segments = explode('/', 'a/b/c/d/controller/action/argA/argB');
            $this->dirs = array (CONTROLLERPATH);
            while (count($segments) && is_dir(implode('/', $this->dirs) . '/' . ($element = array_shift($segments))))
            {
                $this->dirs[] = $element;
            }
            array_shift($this->dirs);
            $this->target_controller = $element;
        }
    
    }
    
    
    mkdir('./a/b/c/d', 0777, true);
    $a = new A();
    $a->test();
    var_dump($a);
    

    Returns:

    class A#1 (2) {
      public $dirs =>
      array(4) {
        [0] =>
        string(1) "a"
        [1] =>
        string(1) "b"
        [2] =>
        string(1) "c"
        [3] =>
        string(1) "d"
      }
      public $target_controller =>
      string(10) "controller"
    }