在自己的php框架中管理URL路由

时间:2012-08-02 23:17:38

标签: php url frameworks routes

我正在创建一个PHP框架,我有些疑惑......

框架以这种方式获取url: http:/web.com/site/index

第一个参数加载控制器(site),然后加载特定操作(index)。

如果您已经在基本URL中安装了框架,则可以正常工作,但如果您将其安装在如下的子文件夹中: http://web.com/mysubfolder/controller/action

我的脚本将其解析为控制器= mysubfolder和操作= controller

如果您有更多子文件夹,结果将会更糟。

这是我的路线代码:

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
            $url = $_SERVER['REQUEST_URI'];
            if(isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])):
                $url = str_replace("?" . $_SERVER['QUERY_STRING'], '',$url);
            endif;
        else:
            if(isset($_SERVER['PATH_INFO'])):
                $url = $_SERVER['PATH_INFO'];
            endif;
        endif;

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

        var_dump($_GET);

    }
}

感谢您提供任何帮助。

12 个答案:

答案 0 :(得分:2)

您缺少基本路径。现在,路由脚本必须在检测到检测到的模式或路径时从哪里开始。

伪代码:

 //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));

只要使用相同的线束 - index.php,就可以在有或没有RewriteBase的情况下使用.htaccess。

此外,您可以使用正则表达式函数(如preg_match和preg_match_all)改进路径匹配过程。它们允许您定义要匹配的模式,并生成匹配字符串数组 - 请参阅http://php.net/manual/en/function.preg-match.php

答案 1 :(得分:2)

是的,我想我知道如何解决这个问题。

(免责声明:我知道你知道大部分内容,但我会为其他可能不了解其中一些问题的人解释一切)

使用PATH_INFO和.htaccess

在php中有一个技巧,如果你去一条路径:

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

您将在$_SERVER['PATH_INFO']变量

中获得“/ controller / action”

现在您需要做的是获取.htaccess文件(或等效文件)并让它告诉您的php脚本当前文件夹深度。

为此,请将.htaccess文件放入“mysubfolder”

mysubfolder
    .htaccess
    index.php

.htaccess应包含:

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

(我使用yii framework manual作为参考,我还建议使用html5 boilerplate

基本上你将它设置为在URL中的特定点将所有内容重定向到index.php。

现在,如果您访问:http://web.com/mysubfolder/index.php/controller/action

现在,您可以从$_SERVER['PATH_INFO']

获取正确的路径“/ controller / action”

如果你访问http://web.com/mysubfolder/它没有任何价值,因为.htaccess文件将忽略重写,因为http://web.com/mysubfolder/路径请求实际存在的mysubfolder / index.php并被拒绝谢谢哟RewriteCond %{REQUEST_FILENAME} !-f

ifsetor功能

为此你可以使用这个名为ifsetor的超级方便的功能(我不记得我从哪里得到它)

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

它的作用是引用一个可能存在或可能不存在的变量,如果它不存在而没有抛出任何通知或错误,则提供默认值

现在您可以使用它来安全地获取PATH_INFO变量

在index.php中

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

php 5.4也有这种新的更短的三元格式,你可以使用,如果你不关心通知(我这样做)

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

处理QUERY_STRING

现在tecnically你没有得到一个URL,它只是它的path部分,不会包含query_string,例如访问时

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

你只会在$ path变量中获得'/ test',以使查询字符串使用$_SERVER['QUERY_STRING']变量

的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);

但这可能取决于您的需求

$ _ SERVER ['QUERY_STRING'] vs $ _GET

另请注意,$_SERVER['QUERY_STRING']变量与$_GET$_REQUEST变量不同,因为它会从查询字符串中保留重复参数,例如:

访问此页面

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

如果要给你一个看似

$_SERVER['QUERY_STRING']
?foo=1&foo=2&foo=3

虽然$_GET变量将是这样的数组:

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

如果您没有.htaccess

您可以尝试使用SCRIPT_NAME

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

如果你想炸掉像我这样的东西:)

您的案例

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);


}
}

我希望这会有所帮助

P.S。对不起,迟到的回复:(

答案 2 :(得分:1)

即使您正在创建自己的框架,也没有理由不重复使用健壮,经过良好测试和记录的components,就像这个Routing组件一样。

只需使用Composer,它已经成为PHP中依赖管理的标准,你会没事的。在堆栈中添加任意数量的组件。

在这里,你有 必须阅读 guide如何制作自己的框架。

答案 3 :(得分:0)

在某些时候你必须检查$ _SERVER ['HTTP_HOST']和程序员/用户定义的配置var,它指示应用程序所在的子文件夹,并删除你不感兴趣的部分从URL的其余部分开始。

您可以在codeigniter formus上查看this forum post以获取一些提示。

CodeIgniter使用另一种不同的方式在内部路由控制器/方法。 您可以通过$_SERVER['PATH_INFO']值进行路由。您可以使用以下网址:myapp.com/index.php/controller/method

为了避免在uri上显示index.php,你必须依赖Apache重写规则,但即便如此,我认为CI是一个很好的解决方案,一旦你有索引文件位置,你可以避免所有的麻烦解析URL。

答案 4 :(得分:0)

如果我正确理解你的内容,那么一个解决方案可能是继续做你正在做的事情,但也得到主路由脚本的路径(例如使用realpath())。

如果最后一个文件夹(或之前的文件夹等)与起始网址项(或其他部分)匹配,则忽略它并转到下一个文件夹。

只需2美分: - )。

答案 5 :(得分:0)

在应用程序配置脚本中放置一个变量,该变量将设置为应用程序运行的路径。

另一种方法是动态设置该路径。

在部分之前

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

使用预定义的应用程序路径从$url字符串中删除位置(子文件夹)路径。

答案 6 :(得分:0)

这是我实现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 :)'); }
 }

}

现在在我的索引文件

//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();

我没有使用modules.only控制器动作和视图的概念。

答案 7 :(得分:0)

您确定自己有htaccess吗?

我想如果您将框架放在subfolder上,则必须将RewriteBase文件中的htaccess/更改为/subfolder/。它会是这样的:

# on root
RewriteBase /

#on subfolder
RewriteBase /subfolder/

在你的情况下,我唯一能想到的就是......

答案 8 :(得分:0)

我不使用OOP,但可以向您展示我如何动态检测我是否在子目录中的一些片段。太简短了,我只会描述它的一部分而不是发布所有代码。

所以我开始使用.htaccess将每个请求发送到redirect.php,我在其中拼接$_SERVER['REQUEST_URI']变量。我使用正则表达式来决定哪些部分应该是键以及应该是什么值(我通过将以0-9开头的任何内容作为值来指定,以及以az作为键开头的任何内容)来构建数组$GET[]。 / p>

然后检查redirect.php的路径并将其与我的$GET数组进行比较,以确定实际URL的开始位置 - 或者在您的情况下,哪个键是控制器。会为你看起来像这样:

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

就是这样,我有URL的第一部分。 keyname()函数如下所示:

/*************************************
 *  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);
}

为了使链接指向正确,我使用了一个名为fixpath()的函数,如下所示:

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

这就是该功能的外观:

/*************************************
 *   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);    
}

我希望有所帮助并能给你一些启发。

答案 9 :(得分:0)

忘记“重新发明轮子是错误的”声称。他们不必使用我们的轮子。我前一段时间走在同一条路上,我非常感激我得到的......我希望你也会这样做

当涉及到您的具体问题的答案时 - 如果面临同样的问题 - 有一个非常简单的解决方案。它是根文件夹中.htaccess的新行...

只需在您的根.htaccess文件中添加以下行; (如果你的subfoler是“子文件夹”)

RewriteRule subfolder/ - [L]

这将使此文件夹与重写指令

分开

通过使用这种方式,您可以根据需要安装任意数量的框架实例。但是如果您希望这是框架驱动的,那么您必须在框架内创建/更改.htaccess。

答案 10 :(得分:0)

创建/myBaseDirectory/public目录并将文件放在那里 - 例如index.php 这是有效的,因为Apache将此目录看作基本目录。

答案 11 :(得分:-1)

基本上在你的第一个斜杠后抓取url字符串,然后将它爆炸成一个数组(我使用'/'作为分隔符)。

然后小心地将array_shift关闭元素并将它们存储为变量

item 0: controller
item 1: the action / method in that controller
item 2 thru n: the remaining array is your params