Управление URL-адресами в собственной php-инфраструктуре

Я создаю PHP Framework, и у меня есть некоторые сомнения...

Структура принимает URL-адрес таким образом: http:/web.com/site/index

Он принимает первый параметр для загрузки контроллера (site), а затем загружает определенное действие (index).

Если вы установили фреймворк в базовом URL-адресе, он работает нормально, но если вы установите его в подпапку, например: http://web.com/mysubfolder/controller/action

My script анализирует его как 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);

    }
}

Спасибо за любую помощь, которую вы можете дать.

Ответ 1

Вам не хватает базового пути. Теперь маршрутизация script должна начинаться с момента обнаружения обнаруженного шаблона или маршрута.

Псевдокод:

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

Вы можете использовать это с RewriteBase или без него для вашего .htaccess, если они используют ту же самую привязку - index.php.

Кроме того, вы можете улучшить процедуру сопоставления маршрута с помощью функции регулярных выражений, таких как preg_match и preg_match_all. Они позволяют вам определить шаблон для сопоставления и результатов для массива совпадающих строк - см. http://php.net/manual/en/function.preg-match.php.

Ответ 2

Да, мне кажется, я знаю, как это исправить.

(Отказ от ответственности: я знаю, что вы знаете большую часть этого, но я собираюсь объяснить все для других, которые могут не знать некоторые из gotchas)

Использование PATH_INFO и .htaccess

В php есть трюк, где, если вы переходите к следующему пути:

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

вы получите "/controller/action" в переменной $_SERVER['PATH_INFO']

Теперь вам нужно взять файл .htaccess(или эквивалент) и сообщить ему текущую глубину папки php script.

Чтобы сделать это, поместите файл .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 в качестве ссылки, также рекомендую использовать html5 шаблон)

В основном вы настроили его для перенаправления всего на index.php в определенной точке URL.

Теперь, если вы посетите: 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, который фактически существует и получает отказ, спасибо yo 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

вы получите только "/test" в переменной $path, чтобы получить строку запроса, используя переменную $_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. Извините за поздний ответ: (

Ответ 3

Даже если вы создаете свою собственную инфраструктуру, нет причин не повторять использование надежного, хорошо протестированного и документированного components, как это Routing.

Просто используйте Composer, который стал стандартом для управления зависимостями в PHP, и все будет в порядке. Добавьте столько компонентов, сколько хотите для своего стека.

И здесь вы должны прочитать руководство о том, как создать свою собственную инфраструктуру.

Ответ 4

В какой-то момент вам нужно будет проверить $_SERVER ['HTTP_HOST'] и конфигурацию var, определенную программистом/пользователем, которая указывает подпапку (ы), где находится приложение, и удалить часть, которая вам не интересна из остальной части URL.

Вы можете проверить этот пост форума на codeigniter formus для некоторых советов.

CodeIgniter использует другой способ маршрутизации контроллера/метода внутри. Вы выполняете маршрутизацию с помощью значения $_SERVER['PATH_INFO']. Вы используете такие URL-адреса: myapp.com/index.php/controller/method.

Чтобы не показывать index.php на uri, вы должны полагаться на правило перезаписи Apache, но даже с этим я считаю, что CI-это хорошее решение, как только у вас есть место в вашем индексном файле, вы можете избежать всех хлопот разбора URL-адреса.

Ответ 5

Если я правильно понимаю, что вы после, то одно решение может продолжать выполнять то, что вы делаете, но также получить путь к основной маршрутизации script (например, с помощью realpath()).

Если последняя папка (или папка до этого и т.д.) соответствует начальному URL-адресу (или другому разделу), вы игнорируете его и переходите к следующему.

Только мои 2 цента: -).

Ответ 6

В конфигурации приложения script поместите переменную, которая будет установлена ​​в путь, на который выполняется приложение.

Альтернативой является динамический набор этого пути.

Перед частью

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

удалите путь местоположения (подпапки) из строки $url, используя предопределенный путь приложения.

Ответ 7

Вот как я реализовал 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();

Я не использовал понятие модулей. Выполняются действия и представление контроллера.

Ответ 8

Вы уверены, что у вас есть htaccess правильно?

Я предполагаю, что если вы размещаете свою фреймворк на subfolder, то вам нужно изменить свой RewriteBase в htaccess файле с / на /subfolder/. это было бы примерно так:

# on root
RewriteBase /

#on subfolder
RewriteBase /subfolder/

что только я мог бы знать об этом в вашем случае...

Ответ 9

Я не использую 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);    
}

Я надеюсь, что это поможет и может дать вам некоторое вдохновение.

Ответ 10

Забудьте о требованиях "изобретать колесо неправильно". Им не нужно использовать наши колеса. Некоторое время назад я шел по тому же пути, и я полностью благодарен за то, что получаю... надеюсь, вы тоже будете

Когда дело доходит до ответа на вашу конкретную проблему, которая, если она встречается слишком, - это очень простое решение. это новая строка в .htaccess в корневой папке...

Просто добавьте строку ниже в свой файл .htaccess. (если ваш подпапок является "подпапкой" )

RewriteRule subfolder/ - [L]

Это позволит оставить эту папку от переписывающих директив

Используя этот способ, вы можете установить как можно больше экземпляров вашей инфраструктуры. Но если вы хотите, чтобы это было основано на каркасе, вам нужно создать/изменить .htaccess в своей структуре.

Ответ 11

Создайте каталог /myBaseDirectory/public и поместите туда ваши файлы - например index.php.
Это работает, потому что Apache видит этот каталог как базовый каталог.

Ответ 12

в основном захватить строку url после вашей первой косой черты, а затем взорвать ее в массив (я использую '/' в качестве разделителя).

затем аккуратно массируйте_shift с ваших элементов и сохраните их как переменные

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