|   
| Для зарегистрированных пользователей |        | FAQ по ЧПУ (человекопонятные URL)
 Как разбить URL на переменные?Вводная:
 Помогите разбить URL на переменные: http://name.ru/content/article/1/
 Ответ:Получить URL так $url = $_SERVER['REQUEST_URI'];
 Далее, воспользоваться командой explode
 Вариант от Нечто (mod_rewrite и разбор пути средствами PHP):
 Правила mod_rewrite:Благодаря вышеприведенным правилам, вместо виртуального /some/interesting/page/ будет вызван URI /index.php/some/interesting/page, а обращения к реально существующим файлам обрабатываться не будут.
 Разбор пути:
 
  php// Достаем переменную окружения, содержащую путь; если она не задана, находимся в корне.
 $URI = (isset( $_SERVER [ 'PATH_INFO' ])) ? $_SERVER [ 'PATH_INFO' ] : '' ;
 // Разбиваем путь на элементы, фильтруя пустые значения и// восстановливая численную последовательность индексов.
 // Пустые значения образуются при разбивке путей типа //another///interesting/page///.
 $URIelements = array_values ( array_filter ( explode ( '/' , $URI )));
 print_r ( $URIelemets );?>
Впоследствии элементы запроса можно перебрать на предмет каких-то ключевых системных значений, а также склеить обратно с помощью implode. Как удобнее хранить данные о разделах сайта 
 Вариант 1
 Базовые поля таблицы:
 id - int - идентификатор страницы
 title - varchar - заголовок страницы
 url - varchar - полный URL страницы
 Вообщем-то, весь вариант заключается в том, что сохраняются все полные URL для всех существуют страниц, то есть нет страниц с параметром, который может принимать любое значение(числовое, например), либо все допустимые значения строго описаны.
 ИТОГО:* Метод прост тем, что не нужны большие усилия для его реализации, достаточно взять пришедший URL и попыпаться выбрать его из базы
 * Он не может быть использован при большом числе динамического контента(например, лента новостей), так как в таком случае получается довольно большая база и необходимо контролировать все изменения, влекущие к созданию нового URL
 Вариант 2Базовые поля таблицы(pages):
 id - int - идентификатор страницы
 url - varchar - URL страницы
 handler - varchar - файл-обработчик
 Довольно распространена ситуация, когда вывод подкатегорий почти не отличается друг от друга. Единственным отличием является группа, к которой принадлежит контент.Например, /news/sport и /news/tv из раздела /news различаются только тем, что, например, в sql-запросе в условии WHERE стоят разные группы(sport и tv, соответственно).
 Тогда как в /news группы вообще не учитываются.
 Из-за этого совершенно излишне делать отдельные обработчики для каждого URL, точнее, создание обработчиков сведётся к копированию и изменению 1 запроса(не забывайте про то, что при таком копировании при обнаружении какой-то ошибки вам придётся копировать всё заново).
 При таком подходе необходимо добавить в БД 1 запись:url handler
 /news/ news.php
 
 При загрузке страницы для выбора нужного файла-обработчика действуем по следующей схеме:пытаемся выбрать из БД полный
 если нет такого URL, то отрезаем от него часть и возвращаемся в п.1 для полученного URL
 отдаём управление handler'у
 ИТОГО:Удобно для хранения страниц с параметрами (дата, номер поиска и т.д.), так как парамеры будут извлечены в отдельный массив
 Под опредeлённые условиях можно создать отдельный обработчик(например, существует категория новостей, где используется отличный от остальных новостей формат, либо данные хранятся в другой таблице)
 В случае, если не существует страница, например, /shop, то отобразаится главная страница, что может быть нежелательные, хотя и можно модифицировать код, чтобы при определённых условиях(например, нет страницы в "первом уровне") выдавалась страница с 404 ошибкой
 Примеры поиска:
 
  php// Удаляем лишние слеши, в начале и конце $url точно будет /
 $url = preg_replace ( '#/+#' , '/' , '/' . $_SERVER [ 'REQUEST_URI' ]. '/' );
 $flag = FALSE ; // Найдем обработчик?
 $param = array(); // Массив для параметров
 while ( $url != '/' && ! $flag ) {// Пытаемся выбрать обработчик для страницы
 $result = mysql_query ( "SELECT `handler` FROM `pages` WHERE `url`='" . $url . "' LIMIT 1" , $db );
 if (! mysql_num_rows ( $result )) {
 $url = substr ( $url , 0 , strlen ( $url ) - 1 ); // Отрезаем / на конце $url
 $pos = strrpos ( $url , '/' ) + 1 ;
 $param [] = substr ( $url , $pos ); // Отрезаем параметр
 $url = substr ( $url , 0 , $pos ); // Получаем новый $url
 } else {
 $row = mysql_fetch_assoc ( $result ); // Получаем строку
 $handler = $row [ 'handler' ]; // Помещяем обработчик в $handler
 $flag = TRUE ;
 }
 unset( $result );
 }
 // "Переворачиваем" массив с параметрами, чтобы они шли по порядку следования в URL
 $param = array_reverse ( $param );
 ?>
Данный код является ТОЛЬКО примеромВ итоге мы получаем все параметры в массиве $param, файл-обработчик в $handler.
 Вариант 3Базовые поля таблицы(pages):
 id - int - идентификатор страницы
 parent_id - int - родитель
 handler - varchar - обработчик, занимающийся страницей
 url - varchar - часть URL между //
 Метод заключается в том, что используется древовидная структура и построение дерева происходит в PHP-скрипте.
 Допустим, есть URL: /news/sport/smthВыбираем из базы:
 Дальше остается только немного обработать результаты выборки.
 ИТОГО:* При большой вложенности метод должен быть довольно медленным
 * Во многом он зависит от реализации
 * В зависимости от реализации, метод позволяет разбирать структуры подобные такой: //
 Вариант 4
 Базовые поля таблицы аналогичны Варианту 3.
 Вариант работоспособен, если для хранения разделов будет использоваться Adjacency List.Вариант генерации запроса, не требующего дальнейшего разбора:
 
  php$url = trim ( strtolower ( $url ), '/' );
 $_url = explode ( '/' , $url );
 $_url = array_filter ( $_url , 'strlen' );
 $sql1 = 't0.id' ;$sql2 = $sql3 = '' ;
 for( $i = 1 , $c = sizeof ( $_url ); $i < $c ; $i ++)
 {
 $sql1 = "IF (t" . $i . ".id IS NOT NULL, t" . $i . ".id, " . $sql1 . ")" ;
 $sql2 .= "+IF(t" . $i . ".id IS NULL,0,1)" ;
 $sql3 .= "LEFT JOIN structure t" . $i . " ON (t" . $i . ".parent_id = t" . ( $i - 1 ) . ".id AND t" .
 $i . ".url = " . $this -> db -> Quote ( $_url [ $i ]) . ") " ;
 }
 $pageId = $this -> db -> _Query ( "SELECT " . $sql1 . " AS id, 1" . $sql2 . " AS count FROM structure t0 " .
 $sql3 . " WHERE t0.parent_id = 0 AND t0.url = " . $this -> db -> Quote ( $_url [ 0 ]));
 $this -> debug -> Trace_R ( $pageId );
 ?>
Получаем:
 
 Array(
 [ id ] => 3
 [ param ] => 1
 )
 Я думаю надо помимо id еще сразу тянуть все нужные данные (название раздела, обработчик и т.д.).
 ИТОГО:* Максимальное число вложенностей ограничено засчёт того, что существует максимальное число JOIN в БД
 * Метод может быть медлительным, так как используется JOIN, при больших объёмах базы и большой вложенности скрипт может вылезти за временное ограничение
 Вариант 5Базовые поля страницы(pages):
 id - int - идентификатор страницы
 handler - varchar - обработчик
 url - varchar - регулярное выражение для URL, соответствующее обработчику
 Лирическое отступление: вообще, мне этот метод был навеян python'овским django, в котором именно при помощи регулярных выражений перечислялась обработка всех допустимых URL. Данный вариант является реализацией части функционала, предоставляемым Django с использованием БД.
 Суть метода заключается в том, что в базе хранятся регулярные выражения, которые соответствуют URL. Выборка нужных страниц осуществляется посредством использования MySQL'оного REGEXP.
 Если немного расширить данный способ, то в PHP при помощи одного preg_match можно получить все параметры. А дальше уж насколько фантазии хватит.
 ИТОГО:* Говорят, что регулярные выражения в исполнение My SQL не так уж быстры, а при большом числе страниц, это медлительность может стать катастрофической
 * Метод позволяет обрабатывать URL, построенных абсолютно любым способом, который можно описать при помощи регулярных выражений
 Почти все приведённые варианты(кроме 3 и 5) страдают 1 проблемой: они не могут разбирать URL формата: //// и подобные
 Добавление слeша в конце Вводная:
 Нужно добавлять слeш в конце URL, если он отсутствует.
 Решение:
 RewriteCond %{REQUEST_FILENAME} -dRewriteRule ^(.+[^/])$ $1/ [R]
 ЧПУ при помощи ошибки 404 и POST данные Вводная:
 Сегодня стал детально разбираться с ЧПУ через обработку 404 ошибки. Вроде все замечательно работает, но вот я не могу получить данные из $_POST.
 Ответ:Суть заключается в использовании директивы Error Document? для перенаправления всех несуществующих запросов на один скрипт.
 Главный существенный недостаток Error Document? - невозможность перехвата POST-данных!
 Чтобы получить POST данные формируйте action формы на реально существующий URL.
 Также, к недостаткам относится то, что засоряется error.log сервера, что мешает выявлению настоящих ошибок, которые необходимо исправить.
 ЧПУ при момощи Multi Views? или Force Type? Apache 
 Вводная:Слышал, что существует способ "скрытия" расширения PHP-файлов при помощи конфигурирования непосредственно Apache.
 Ответ:Первым является способ с использованием Mutli Views?.
 Сам способ заключается в том, что у реальносуществующего файла скрывается расширение. То есть до какого-то момента URL реальный, а после - нет.
 Он "включается" добавлением в .htaccess или к настройкам сервера:
 На самом деле, это более понятно на примере.Допустим, у нас есть файл article.php, лежащий в корне Web-сервера. Тогда запросы вида /article/php_security1, /article/sessions будут обрабатываться скриптом article.php. Далее, уже всё зависит от разбора в скрипте остальной части URL(если требуется).
 Второй способ основывается на Force Type?.Способ позволяет добиться аналогичного эффекта при использовании Force Type?. Мы называем файл просто article, а не article.php.
 И в .htaccess или конфигурации сервера добавляем:
 Так что теперь файл article будет интерпретироваться PHP.
 
 |