Manage URL routes in own php framework

前端 未结 12 2122
盖世英雄少女心
盖世英雄少女心 2021-02-02 16:34

I\'m creating a PHP Framework and I have some doubts...

The framework takes the url in this way: http:/web.com/site/index

It takes the first paramet

相关标签:
12条回答
  • 2021-02-02 17:10

    If I am understanding what you are after correctly, then one solution may be to carry on doing what you are doing, but also get the path of the main routing script (using realpath() for example).

    If the last folder (or folder before that etc) matches the beginning URL item (or another section), you ignore it and go for the next one.

    Just my 2 cents :-).

    0 讨论(0)
  • 2021-02-02 17:11

    Within the application configuration script place a variable which will be set to the path the application runs at.

    An alternative is to dynamically set that path.

    Before the part

    $url = explode('/',preg_replace('/^(\/)/','',$url));
    

    strip the location (sub-folder) path out of the $url string using the predefined application path.

    0 讨论(0)
  • 2021-02-02 17:11

    I don't use OOP, but could show you some snippets of how I do things to dynamically detect if I'm in a subdirectory. Too keep it short and to the point I'll only describe parts of it instead of posting all the code.

    So I start out with a .htaccess that send every request to redirect.php in which I splice up the $_SERVER['REQUEST_URI'] variable. I use regex to decide what parts should be keys and what should be values (I do this by assigning anything beginning with 0-9 as a value, and anything beginning with a-z as key) and build the array $GET[].

    I then check the path to redirect.php and compare that to my $GET array, to decide where the actual URL begins - or in your case, which key is the controller. Would look something like this for you:

    $controller = keyname($GET, count(explode('/', dirname($_SERVER['SCRIPT_NAME']))));
    

    And that's it, I have the first part of the URL. The keyname() function looks like this:

    /*************************************
     *  get key name from array position
     *************************************/
    function keyname ($arr, $pos)
    {
        $pos--;
        if ( ($pos < 0) || ( $pos >= count($arr) ) )
              return "";  // set this any way you like
    
        reset($arr);
        for($i = 0;$i < $pos; $i++) next($arr);
    
        return key($arr);
    }
    

    To get the links pointing right I use a function called fixpath() like this:

    print '<a href="'.fixpath('/admin/logout').'">link</a>';
    

    And this is how that function looks:

    /*************************************
     *   relative to absolute path
     *************************************/
    function fixpath ($path)
    {
        $root = dirname($_SERVER['SCRIPT_NAME']);
        if ($root == "\\" || $root == ".") {
            $root = "";
        }
        $newpath = explode('/', $path);
        $newpath[0] .= $root;
        return implode('/', $newpath);    
    }
    

    I hope that helps and can give you some inspiration.

    0 讨论(0)
  • 2021-02-02 17:13

    You are missing a base path. The routing script must now where to start when detecting a pattern or route detected.

    Pseudo code:

     //set the base URI
    $base_uri = '/base';
    //remove the base URI from the original uri. You can also REGEX or string match against it if you want
    $route_uri = str_replace($base_uri,'',$uri);
    //perform route matching $route_uri, in your code a simple explode
    
    $url = explode('/',preg_replace('/^(\/)/','',$route_uri));
    

    You can use this with or without RewriteBase for your .htaccess so long as they use the same harness - index.php.

    Additionally, you can improve your route match procedure using Regular Expressions function like preg_match and preg_match_all. They let you define a pattern to match against and results to an array of matching strings - see http://php.net/manual/en/function.preg-match.php.

    0 讨论(0)
  • 2021-02-02 17:19

    Yes, I think I know how to fix that.

    (Disclaimer: I know that you know most of this but I am going to explain everything for others who may not know some of the gotchas)

    Using PATH_INFO and .htaccess

    There is a trick in php where if you go to a path like:

    http://web.com/mysubfolder/index.php/controller/action

    you will get "/controller/action" in the $_SERVER['PATH_INFO'] variable

    Now what you need to do is take a .htaccess file (or equivalent) and make it tell your php script the current folder depth.

    To do this, put the .htaccess file into the "mysubfolder"

    mysubfolder
        .htaccess
        index.php
    

    .htaccess should contain:

    RewriteEngine on
    
    # if a directory or a file exists, use it directly
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    
    # otherwise forward it to index.php
    RewriteRule (.*) index.php/$1
    

    (I used the yii framework manual as reference, I also recommend using the html5 boilerplate)

    Basically you set it up to redirect everything to index.php at a specific point in the url.

    Now if you visit: http://web.com/mysubfolder/index.php/controller/action

    Now you can get the right path "/controller/action" from $_SERVER['PATH_INFO']

    Except it's not going to have any value if you visit http://web.com/mysubfolder/ because the .htaccess file will ignore the rewrite because the http://web.com/mysubfolder/ path requests the mysubfolder/index.php which actually exists and gets denied thank yo RewriteCond %{REQUEST_FILENAME} !-f.

    ifsetor function

    For this you can use this super handy function called ifsetor (I don't remember where I got it)

    function ifsetor(&$variable, $default = null) {
        return isset($variable)? $variable: $default;
    }
    

    What it does is take a reference to a variable that might or might not exist and provide a default if it does not exist without throwing any notices or errors

    Now you can use it to take the PATH_INFO variable safely like so

    In your index.php

    function ifsetor(&$variable, $default = null) {
        return isset($variable)? $variable: $default;
    }
    $path = ifsetor($_SERVER['PATH_INFO'],'/');
    var_dump($path);
    

    php 5.4 also has this new shorter ternary format which you can use if you don't care about notices (I do)

    $path = $_SERVER['PATH_INFO']?:'/';
    

    Handling the QUERY_STRING

    Now tecnically you are not getting a URL, it is merely its path part and will not contain the query_string, for example when visiting

    http://web.com/mysubfolder/index.php/test?param=val

    you will only get '/test' in the $path variable, to get the query string use the $_SERVER['QUERY_STRING'] variable

    index.php

    function ifsetor(&$variable, $default = null) {
        return isset($variable)? $variable: $default;
    }
    $path = ifsetor($_SERVER['PATH_INFO'],'/');
    $fullpath = ($_SERVER['QUERY_STRING'])? $path.'?'.$_SERVER['QUERY_STRING']:$path;
    var_dump($fullpath);
    

    But that might depend on your needs

    $_SERVER['QUERY_STRING'] vs $_GET

    Also keep in mind that the $_SERVER['QUERY_STRING'] variable is different from the $_GET and $_REQUEST variables because it keeps duplicate parameters from the query string, for example:

    Visiting this page

    http://web.com/mysubfolder/controller/action?foo=1&foo=2&foo=3

    if going to give you a $_SERVER['QUERY_STRING'] that looks like

    ?foo=1&foo=2&foo=3
    

    While the $_GET variable is going to be an array like this:

    array(
        'foo'=>'3'
    );
    

    If you don't have .htaccess

    You can try using the SCRIPT_NAME to your advantage

    list($url) = explode('?',$_SERVER['REQUEST_URI']);
    list($basepath) = explode('index.php',$_SERVER['SCRIPT_NAME']);
    $url = substr($url,strlen($basepath));
    

    If you like to blow up stuff like me :)

    Your case

    Class Route
    {
    private $_htaccess = TRUE;
    private $_suffix = ".jsp";
    
    public function params()
    {
        $url='';
    
        //nombre del directorio actual del script ejecutandose.
        //basename(dirname($_SERVER['SCRIPT_FILENAME']));
    
        if($this->_htaccess !== FALSE):
            //no está funcionando bien si está en un subdirectorio web, por ej stynat.dyndns.org/subdir/
            // muestra el "subdir" como primer parámetro
            list($url) = explode('?',$_SERVER['REQUEST_URI']);
            $basepath = dirname($_SERVER['SCRIPT_NAME']);
            $basepath = ($basepath==='/')? $basepath: $basepath.'/';
            $url = substr($url,strlen($basepath));
        else:
            if(isset($_SERVER['PATH_INFO'])):
                $url = $_SERVER['PATH_INFO'];
                $url = preg_replace('|^/|','',$url);
            endif;
        endif;
    
        $url = explode('/',$url);
        var_dump($url);
    
        var_dump($_GET);
    
    
    }
    }
    

    I hope this helps

    P.S. Sorry for the late reply :(

    0 讨论(0)
  • 2021-02-02 17:24

    This is how i implemented loader.php

       <?php
    /*@author arun ak
    auto load controller class and function from url*/
    class loader
    {
        private $request;
        private $className;
        private $funcName;
    
        function __construct($folder    = array())
        {   
            $parse_res  = parse_url($this->createUrl());
            if(!empty($folder) && trim($folder['path'],DIRECTORY_SEPARATOR)!='')
             {
                $temp_path  = explode(DIRECTORY_SEPARATOR,trim($parse_res['path'],DIRECTORY_SEPARATOR));
                $folder_path    = explode(DIRECTORY_SEPARATOR,trim($folder['path'],DIRECTORY_SEPARATOR));
                $temp_path      = array_diff($temp_path,$folder_path);
    
                    if(empty($temp_path))
                    {
                        $temp_path =    '';
                    }
             }else
             {
                if(trim($parse_res['path'],DIRECTORY_SEPARATOR)!='')
                {
                 $temp_path = explode(DIRECTORY_SEPARATOR,trim($parse_res['path'],DIRECTORY_SEPARATOR));
                }
                     else
                 $temp_path ='';
             }
            if(is_array($temp_path))
            {   
                if(count($temp_path) ==1)
                {
                    array_push($temp_path,'index');
                }
                foreach($temp_path as $pathname)
                {   
                    $this->request .= $pathname.':';
                }
            }
            else $this->request = 'index'.':'.'index';
    
        }
    
     private function createUrl()
     {  
        $pageURL  = (@$_SERVER["HTTPS"] == "on") ? "https://" : "http://";
        $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
        return $pageURL;
     }
     public function autolLoad()
     {
        if($this->request) 
        {   
            $parsedPath = explode(':',rtrim($this->request,':'));
            if(is_file(APPLICATION_PATH.DIRECTORY_SEPARATOR.'controllers'.DIRECTORY_SEPARATOR.$parsedPath[0].'_controller'.'.php'))
            {
                include_once(APPLICATION_PATH.DIRECTORY_SEPARATOR.'controllers'.DIRECTORY_SEPARATOR.$parsedPath[0].'_controller'.'.php');
                if(class_exists($parsedPath[0].'_controller'))
                {
                    $class  = $parsedPath[0].'_controller';
                    $obj    = new $class();
                    //$config       = new config('localhost','Winkstore','nCdyQyEdqDbBFpay','mawinkcms');
                    //$connect  =  connectdb::getinstance();
                    //$connect->setConfig($config);
                    //$connection_obj = $connect->connect();
                    //$db               = $connect->getconnection();//mysql link object
                    //$obj->setDb($db);
                    $method = $parsedPath[1];
                    if(method_exists ($obj ,$parsedPath[1] ))
                    {
                        $obj->$method();
                    }else die('class method '.$method.' not defined');
    
                }else die('class '.$parsedPath[0]. ' has not been defined' );
    
            } else die('controller not found plz define one b4 u proceed'.APPLICATION_PATH.DIRECTORY_SEPARATOR.'controllers'.DIRECTORY_SEPARATOR.$parsedPath[0].'.php');
    
         }else{ die('oops request not set,i know tis is not the correct way to error :)'); }
     }
    
    }
    

    Now in my index file

    //include_once('config.php');
    include_once('connectdb.php');
    require_once('../../../includes/db_connect.php');
    include_once('view.php');
    include_once('abstractController.php');
    include_once('controller.php');
    include_once('loader.php');
    $loader = new loader(array('path'=>DIRECTORY_SEPARATOR.'magsonwink'.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.'admin'.DIRECTORY_SEPARATOR.'atom'.DIRECTORY_SEPARATOR));
    $loader->autolLoad();
    

    I haven't used the concept of modules.only controller action and view are rendered.

    0 讨论(0)
提交回复
热议问题