14

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

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

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

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

我的脚本将其解析为 controller =mysubfolder和 action = 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);

    }
}

谢谢你提供的所有帮助。

4

12 回答 12

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

您可以在 .htaccess 中使用或不使用 RewriteBase,只要它们使用相同的工具 - index.php。

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

于 2012-11-16T10:47:45.197 回答
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 框架手册作为参考,我也推荐使用html5 样板

基本上,您将其设置为将所有内容重定向到 url 中特定点的 index.php。

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

现在您可以从中获得正确的路径“/controller/action”$_SERVER['PATH_INFO']

除非您访问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

现在技术上你没有得到一个 URL,它只是它的路径部分,不会包含 query_string,例如在访问时

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

您只会在 $path 变量中获得 '/test',要获取查询字符串,请使用该$_SERVER['QUERY_STRING']变量

索引.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'] 与 $_GET

另请记住,该变量与and变量$_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);


}
}

我希望这有帮助

PS抱歉回复晚了:(

于 2013-06-06T19:47:17.230 回答
2

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

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

在这里,您有一本关于如何制作自己的框架的必读 指南。

于 2012-11-17T16:18:42.497 回答
0

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

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

然后,我检查 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);    
}

我希望这对您有所帮助并能给您一些启发。

于 2012-11-19T23:06:14.553 回答
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();

我没有使用模块的概念。只有控制器动作和视图被渲染。

于 2012-09-28T08:20:24.193 回答
0

如果我正确理解了您的需求,那么一种解决方案可能是继续做您正在做的事情,但还要获取主路由脚本的路径(例如使用 realpath() )。

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

只是我的 2 美分 :-)。

于 2012-09-22T22:43:03.763 回答
0

你确定你的htaccess正确吗?

我想如果你将你的框架放在 上subfolder,那么你必须将你的RewriteBaseinhtaccess文件从更改//subfolder/. 它会是这样的:

# on root
RewriteBase /

#on subfolder
RewriteBase /subfolder/

在您的情况下,这是我唯一想知道的...

于 2012-10-02T09:05:31.090 回答
0

忘记“重新发明轮子是错误的”的说法。他们不必使用我们的轮子。不久前我走在同一条路上,我非常感激我所得到的......我希望你也会

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

只需将下面的行添加到您的根 .htaccess 文件中;(如果您的子文件夹是“子文件夹”)

RewriteRule subfolder/ - [L]

这将使该文件夹与重写指令分开

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

于 2012-11-24T01:19:33.043 回答
0

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

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

部分前

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

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

于 2012-09-25T13:10:52.030 回答
0

在某些时候,您必须检查 $_SERVER ['HTTP_HOST'] 和程序员/用户定义的配置变量,它指示应用程序所在的子文件夹,并从URL 的其余部分。

您可以在 codeigniter 表单上查看此论坛帖子以获取一些提示。

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

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

于 2012-09-14T14:51:20.937 回答
0

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

于 2015-12-23T12:33:41.360 回答
-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
于 2012-08-02T23:21:24.043 回答