Самый простой и логичный ЧПУ для PHP
Решил написать эту заметку, потому как надоело отвечать 100500 раз одно и то же на ВиО.
Многие начинающие веб-программисты рано или поздно сталкиваются с задачей внедрения в свой сайт человеко-понятных линков (ЧПУ). До внедрения ЧПУ все ссылки имеют вид /myscript.php или даже /myfolder/myfolder2/myscript3.php, что тяжело для запоминания и ещё хуже для SEO. После внедрения ЧПУ линки принимают вид /statiya-o-php или даже на кириллице /статья-о-пхп.
Кстати о SEO. Человекопонятные линки на САМОМ деле придумали не для удобного запоминания, а в основном для повышения индексируемости сайта, потому что совпадение поискового запроса и части URL даёт хорошее преимущество в рейтинге поиска.
Эволюция начинающего PHP-программиста может быть выражена следующей последовательностью шагов:
- Размещение plain-PHP кода в отдельных файлах и доступ к этим файлам через линки вида /myfolder/myscript.php
- Понимание, что все скрипты имеют значительную часть общего (например, создание подключения к БД, чтение конфигурации, запуск сессии и проч.) и как следствие создание общей начальной точки «входа», некоторого скрипта, который принимает ВСЕ запросы, а потом выбирает — какой внутренний скрипт подключить. Обычно этот скрипт имеет имя index.php и лежит в корне, вследствие чего все запросы (они же URLы) выглядят так: /index.php?com=myaction&com2=mysubaction
- Необходимость внедрения роутера и переход к человекопонятным линкам.
Замечу, что между пунктами 2 и 3 большинство программистов делают очевидную ошибку. Я не ошибусь, если назову это значением около 95% программистов. Даже большинство известных фреймворков содержат эту ошибку. И заключается она в следующем.
Вместо того, чтобы реализовывать принципиально новый способ обработки линков, ошибочно делается концепция «заплаток и редиректов» на базе .htaccess, которая заключается в том, чтобы с помощью mod_rewrite создавать множество правил редиректа. Эти строки сравнивают URL с каким-либо регулярным выражением и при совпадении расталкивают выуженные из URL значения по GET-переменным, в дальнейшем вызывая всё тот же index.php.
#Неправильный метод ЧПУ RewriteEngine On RewriteRule ^\/users\/(.+)$ index.php?module=users&id=$1 [QSA] #. Ещё куча подобных правил.
У данной концепции множество недостатков. Один из них — трудность создания правил, большой процент человеческих ошибок при добавлении правил, которые сложно выявить, но они приводят к ошибке сервера 500.
Другой недостаток в том, что часто правится по сути конфига сервера, что само по себе нонсенс. И если в Apache конфиг можно «пропатчить» с помощью .htaccess, то в популярном nginx такой возможности нет, там всё находится в общем файле конфигурации в системной зоне.
И ещё один недостаток, вероятно, наиболее важный, что при таком подходе невозможно динамически конфигурировать роутер, то есть «на лету», алгоритмически менять и расширять правила выбора нужного скрипта.
Предлагаемый ниже способ избавлен от всех этих недостатков. Он уже используется в большом количестве современных фреймворков.
Суть заключается в том, что начальный запрос всегда хранится в переменной $_SERVER[‘REQUEST_URI’], то есть его можно считать внутри index.php и разобрать как строку средствами PHP со всеми обработками ошибок, динамическими редиректами и проч и проч.
При этом в файле конфигурации можно создать только одно статичное правило, которое будет все запросы к несуществующим файлам или папкам редиректить на index.php.
RewriteEngine On RewriteCond % !-f #Если файл не существует RewriteCond % !-d #И если папка не существует RewriteRule ^.*$ index.php [QSA,L]
Причём это правило можно разместить как в .htaccess, так и в основном файле конфигурации Apache.
Для nginx соответствующее правило будет выглядеть вот так:
Теперь рассмотрим кусок кода PHP в index.php, который анализирует ссылки и принимает решение — какой скрипт запускать.
В общем случае ссылка из $_SERVER[‘REQUEST_URI’] выглядит так
Первое, что приходит в голову — разбить её с помощью explode(‘/’, $uri) и сделать сложный роутер на основе switch/case, анализирующий каждый кусок запроса. Не делайте этого! Это сложно и в итоге приводит код в ужасный непонимабельный и неконфигурабельный вид!
Я предлагаю более лаконичный способ. Лучше не описывать словами, а сразу показать код.
'page404.php', // Страница 404 '/' => 'mainpage.php', // Главная страница '/news' => 'newspage.php', // Новости - страница без параметров '/stories(/6+)?' => 'storypage.php', // С числовым параметром // Больше правил ); // Код роутера class uSitemap < public $title = ''; public $params = null; public $classname = ''; public $data = null; public $request_uri = ''; public $url_info = array(); public $found = false; function __construct() < $this->mapClassName(); > function mapClassName() < $this->classname = ''; $this->title = ''; $this->params = null; $map = &$GLOBALS['sitemap']; $this->request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $this->url_info = parse_url($this->request_uri); $uri = urldecode($this->url_info['path']); $data = false; foreach ($map as $term => $dd) < $match = array(); $i = preg_match('@^'.$term.'$@Uu', $uri, $match); if ($i >0) < // Get class name and main title part $m = explode(',', $dd); $data = array( 'classname' =>isset($m[0])?strtolower(trim($m[0])):'', 'title' => isset($m[1])?trim($m[1]):'', 'params' => $match, ); break; > > if ($data === false) < // 404 if (isset($map['_404'])) < // Default 404 page $dd = $map['_404']; $m = explode(',', $dd); $this->classname = strtolower(trim($m[0])); $this->title = trim($m[1]); $this->params = array(); > $this->found = false; > else < // Found! $this->classname = $data['classname']; $this->title = $data['title']; $this->params = $data['params']; $this->found = true; > return $this->classname; > > $sm = new uSitemap(); $routed_file = $sm->classname; // Получаем имя файла для подключения через require() require('app/'.$routed_file); // Подключаем файл // P.S. Внутри подключённого файла Вы можете использовать параметры запроса, // которые хранятся в свойстве $sm->params
Несмотря на то, что код довольно длинный, он прост логически. Мне не хочется его объяснять, я считаю любой код на PHP самообъясняющим, если он правильно написан. Учитесь читать код.
Ещё статьи в этом разделе:
ЧПУ с помощью PHP для чайников
В большинстве современных CMS, где с помощью плагинов, а где и в самом ядре системы, реализована поддержка ЧПУ – человекопонятных адресов. Если вы посмотрите на адресную строку моего блога, то увидите что-то типа этого
https://upread.ru/blog/notes/chpu-s-pomoshhyu-php-dlya-chajnikov
А действительно, зачем это делать? Далеко не каждый человек вообще обращает внимание на адресную строку, тем более пытается там что-то разобрать. Дадим слово гуглу:
Структура URL сайта должна быть предельно простой. Попробуйте организовать контент так, чтобы URL имели логическую структуру и были понятны для человека (по возможности используйте слова, а не идентификаторы, состоящие из множества цифр). Например, при поиске информации об авиации URL типа http://ru.wikipedia.org/wiki/aviacia поможет оценить релевантность ссылки. Гораздо сложнее привлечь внимание пользователей с помощью URL-адреса типа http://www.example.com/index.php?id_sezione=360&sid=3a5ebc944f41daa6f849f730f1.
То есть, применительно к нашему блогу разбиваем url на части:
- https:// — защищенный протокол передачи данных;
- upread.ru – домен, адрес блога;
- blog – раздел, показывающий, что это блог;
- notes – раздел, показывающий, что это заметки;
- chpu-s-pomoshhyu-php-dlya-chajnikov – название заметки, которую, собственного говоря, вы и читаете.
Однако, если в КМС есть уже поддержка ЧПУ, транслитерация и другие инструменты, то, например, на моем блоге этого нет – у меня самописная CMS. Как же реализовано? Все просто, ниже покажу как.
Сначала небольшая ремарка. Если у вас есть какой-то опыт в сайтостроении, но вы не умеете делать ЧПУ с помощью .htaccess и php, то можете подумать, что я создал три каталога, а в них поместил файл index.html. Действительно, в принципе можно сделать каталоги blog, notes и nc-php и в последний положить индексный файл – и это будет работать! Но в реальности никто так не делает, все работают с .htaccess и php.
Итак, для начала нам надо переправить все запросы, которые не являются реальными файлами и каталогами в какой-то определенный файл, чаще всего это корень сайта, но никто вам не мешает создать и любой другой. Например, job.php. Добавьте в ваш .htaccess следующие строки:
RewriteEngine On Options +FollowSymlinks RewriteBase / RewriteCond % !-f RewriteCond % !-d RewriteRule ^(.*)$ job.php [L,QSA]
Теперь нам надо работать уже с PHP. Я снова возьму в пример свой сайт. Каждый материал на моем блоге доступен по адресу upread/art.php?id=xxx, где xxx – некоторое число, идентификатор материала. Как же нам передать идентификатор в файл job.php? Ну на самом деле способов немало. Можно распарсить url и в зависимости от него выводить нужную информацию:
if ($_SERVER['REQUEST_URI'] == '/blog/notes/chpu-s-pomoshhyu-php-dlya-chajnikov') < //тут выводим текст статьи >
Однако правильнее будет создать в базе данных отдельное поле для ЧПУ каждой записи и сравнивать перед выводом. Кстати, именно так реализован вывод, к примеру, в одной из самых распространенных CMS – WordPress. На картинке видно, что ЧПУ записан в поле post_name, а тот, из которого берется id материала — guid.
После того, как вы создали ЧПУ на своем сайте, вы можете увидеть, что у вас слетели стили. Почему так происходит? Все просто: если прописаны относительные пути к стилям скриптам, то браузер и пытается их загрузить из текущей директории. А не находя, естественно, выдает ошибку 404. Решается проблема просто: добавляем тэг base. Например, для моего блога он выглядит таким образом:
Вот так просто может создать свой человекопонятный урл даже самый чайник в сайтостроении. Если вам что-то непонятно или необходимо настроить систему URL на вашем сайте, то пишите мне – за небольшое вознаграждение я вам помогу.
Автор этого материала — я — Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML — то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
заметки, php, чпу, htaccess