- Как перенаправлять все запросы на index.php (.htaccess)?
- ЧПУ, роутинг, единая точка входа на PHP
- Плюсы единой точки входа
- Единая точка входа с Apache
- Единая точка входа с Nginx
- Простой роутинг
- Роутинг средствами htaccess
- Структура URL адресов в админке
- Добавление префикса /admin/ в URL
- Продвинутый роутер FastRoute
Как перенаправлять все запросы на index.php (.htaccess)?
Например www.site.com/faq-one?id=5 — все работает хорошо
www.site.com/catalog?id=10 — 404 ошибка
Однако, если убрать catalog.php в корне, то все работает.
Как сделать, чтобы www.site.com/catalog?id=10 перенаправлялся на index.php и при существующем файле catalog.php?
RewriteEngine On RewriteCond % !-d RewriteCond % !-f RewriteRule ^(.*)$ ./index.php?route=$1
Все после www.site.com/ попадает в $_GET[‘route’]
Не работает, если есть catalog.php. И в данном случае в $_GET массиве есть только ‘route’ => ‘catalog’, а где ‘id’ ?
Я говорю, что если я обращаюсь www.site.com/catalog?id=10 и есть файл catalog.php, то появляется ошибка 404. Если файл catalog.php переименовать в catalog1.php, то все работает.
чтото ты делаешь не так
создал новый хост с такой структурой папок
содержимое файлов
.htaccess
RewriteEngine On
RewriteRule ^.*$ index.php [NC,L]
/catalog/index.php
echo ‘catalog index file’;
при открытии страниц
/ — попадаю на /index.php
/catalog — попадаю на /index.php
/catalog.php — попадаю на /index.php
/catalog/asdasd — попадаю на /index.php
/catalog/index.php — попадаю на /index.php
@shendel у вас директория называется «catalog», а у автора такой директории нет. Но есть файл «catalog.php» в корне.
@Xu4 директория «catalog» для наглядности, что «Нужно, чтобы все запросы, которые приходят на сайт, перенаправлялись на index.php»
@shendel хотя, да, действительно. Тут разницы нет, потому что в случае чего должен был бы «/catalog» как директория отработать, но он нормально перенаправился в корневой index.php.
У меня тоже так работает. А попробуйте удалить категорию catalog и добавить в корень catalog.php.
При обращении www.site.com/catalog?id=10 появляется 404 ошибка
если удалить папку и создать файл, то точно также — все идет в index.php
Если у вас не работает, то может быть несколько возможных проблем
1. У вас там точно стоит апачи? не nginx+php-fm?
2. Если все-таки стоит апачи, перед ним нет прокси (nginx) — если да, то запрос может перехватывать nginx — 404 ошибку кто сообщает (внизу обычно пишется сигнатура веб-сервера — 404 точно апачи отдает?)
3. Может у вас не включен модуль mod_rewrite. Как проверить? создать файл php c содержимым
phpinfo();
?>
в листинге в секции «apache2handler» напротив графы «Loaded Modules» должен присутствовать «mod_rewrite»
4. Файл .htacces у вас точно такой же как я написал, потому что тот файл что привел @Satanpit , не отвечает требованиям вашей задачи
Логика его файла такая
(включить реврайт)
RewriteEngine On
(Если uri не является папкой)
RewriteCond % !-d
(Если uri не является файлом)
RewriteCond % !-f
(Перенаправить на index.php)
RewriteRule ^(.*)$ ./index.php?route=$1
Логика моего же файла такая
(включить реврайт)
RewriteEngine On
(перенаправить на /index.php и сделать правило финальным (остальные правила будут отброшены))
RewriteRule ^.*$ index.php [NC,L]
5. Что прописано в конфиге домена по поводу правил обработки файлов .htaccess?
в секции
AllowOverride All есть?
— по умолчанию обычно там стоит
AllowOveride None
ЧПУ, роутинг, единая точка входа на PHP
Веб-сервер настраивается так, чтобы все HTTP-запросы, вне зависимости от их URL, обрабатывались одним и тем же скриптом index.php .
Текущий URL можно получить из переменной $_SERVER[‘REQUEST_URI’] . Дальше останется только написать свои правила обработки URL-адресов. Упрощённый пример:
Однако в схеме выше есть одно упущение. Ведь если на сервер пришёл запрос к существующему файлу (style.css, script.js, logo.png и т.д) — сервер должен отдать этот файл, а не перенаправлять его.
Вот и весь принцип единой точки входа. Именно так она работает в популярных CMS вроде WordPress и Opencart, в фреймворках Laravel, Symfony и т.д.
Единственный вопрос, который вам останется решить — что делать с запросами к существующим папкам.
Лично я предпочитаю также перенаправлять их на index.php.
На самом деле на сайтах часто используются 2 точки входа.
Первая — index.php, вторая — отдельный скрипт, предназначенный для работы с сайтом через консоль.
Плюсы единой точки входа
- Позволяет использовать ЧПУ
- Позволяет полностью управлять URL-адресами в PHP, в том числе хранить URL-адреса в базе данных
- Скрипты с конфигами, важными функциями и библиотеками подключаются только 1 раз и становятся доступны везде. Не нужно дублировать их подключение где-либо ещё.
Единая точка входа с Apache
Для настройки единой точки входа необходимо добавить несколько строк в конфиг веб-сервера. Проще всего это сделать с помощью файла .htaccess .
Этот файл позволяет переопределять настройки Apache для определённых сайтов и папок.
Добавляем следующие настройки в .htaccess:
# Включаем перенаправление RewriteEngine On # Не применять к существующим файлам файлам RewriteCond % !-f # Не применять к существующим директориям RewriteCond % !-d # Редирект всех запросов на index.php # L означает Last, нужен чтобы на этом этапе mod_rewrite сразу остановил работу. # Короче, небольшое увеличение производительности. RewriteRule .* index.php [L]
Чтобы перенаправление срабатывало для существующих директорий, удаляем строку с !-d в конце, вот так:
RewriteEngine On RewriteCond % !-f RewriteRule .* index.php [L]
Готово. Получить URL адрес текущей страницы можно из переменной $_SERVER[‘REQUEST_URI’] .
Также в интернете часто можно встретить другой вариант конфига, отличается он только последней строкой:
RewriteRule ^(.*)$ index.php?url_param=$1 [L,QSA]
Главное отличие в том, что URL-адрес текущей страницы будет храниться как в $_SERVER[‘REQUEST_URI’] , так и в отдельном GET-параметре, в нашем случае $_GET[‘url_param’] , причём этот URL будет очищен от GET-параметров.
Флаг QSA нужен, поскольку без него GET-параметры не будут работать, т.е. массив $_GET будет содержать только url_param и больше ничего.
Какой из двух вариантов выбрать — решать вам, лично мне больше нравится первый.
Единая точка входа с Nginx
Открываем конфиг домена и внутри секции server прописываем следующее правило:
Простой роутинг
Если единая точка входа настроена правильно, то при заходе по любому несуществующему URL-адресу, например /test должен запуститься файл index.php.
URL текущей страницы находится в переменной $_SERVER[‘REQUEST_URI’]
Теперь мы можем написать очень простой роутер, который смотрит на текущий URL и подключает соответствующий скрипт:
Внесём ещё пару доработок. Во-первых, зачастую URL-адреса должны работать вне зависимости от наличия GET-параметров, поэтому вырежем их из URI:
// /about?id=5 превратится в /about $uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
Кроме этого, часто требуется получить доступ к определённой части URL. Для этого разобьём URL на части по слешу:
$segments = explode('/', trim($uri, '/'));
В переменной $segments для URL /products/15 будет лежать массив вида [0 => ‘products’, 1 => ’15’] .
Теперь мы можем легко добавить маршруты для админки:
Это самый простой вариант роутинга. Не идеальный, конечно, но и не требующий знания регулярных выражений (хотя никто не мешает их использовать) и подключения сторонних библиотек.
При хранении URL адресов в базе данных роутинг будет выглядеть примерно так (реальный код зависит от библиотеки, которую вы используете для взаимодействия с БД):
select('SELECT * FROM `pages` WHERE `url` = ?', [$uri])->first(); // Если статьи нет - показываем ошибку if(!$current_page) require 'pages/404.php'; // Если статья есть - подключаем шаблон, в котором будет доступна переменная $current_page else require 'pages/article.php';
Роутинг средствами htaccess
Какое-то время назад было популярно прописывать правила роутинга прямо в htaccess, вот несколько примеров:
RewriteRule ^/product-(.*)_(8+).php /redirectold.php?productid=$2 RewriteRule ^products/([^/]+)/([^/]+)/([^/]+) product.php?category=$1&brand=$2&product=$3 RewriteRule ^news/204/7/2/[^/]+\.html index.php RewriteCond %/name/$1.php -f RewriteRule ^([^/]+)/([^/]+)/?$ $.php1?action=$2 [L,NC,QSA] RewriteCond %/name/$1.php -f RewriteRule ^([^/]+)/([^/]+)/([^/]+)/?$ $1.php?action=$2&id=$3 [L,NC,QSA]
У этого подхода есть несколько недостатков:
- Плохая читаемость правил
- Нужно хорошо знать регулярки
- Хранение правил роутинга в настройках веб-сервера — концептуально не очень хорошая идея
Короче, не используйте этот подход.
Структура URL адресов в админке
Обычно URL адреса в админке формируются по одной из следующих схем:
/модуль/действие/параметр1/значение1/параметр2/значение2 /модуль/действие/значение1/значение2
И сразу рассмотрим простой пример:
/products - просмотр каталога /products/add - добавление товара /products/update - обновление товара /products/delete - удаление товара
Итак, мы видим, что модулем здесь является products , а действием, к примеру, add . Что теперь с этим делать?
Если вы знакомы с ООП и MVC, тогда модулем для вас будет название класса, а действием — метод этого класса, который нужно запустить. Если действие не указано, то принято запускать метод под названием index.
Если вы ничего не поняли — воспринимайте модуль как название файла, который нужно подключить, а действие — как, собственно, действие, которое нужно выполнить.
Перепишем пример, написанный нами в единой точке входа, под новую схему URL:
Итак, мы берём 1-ый фрагмент URL и проверяем, существует ли в папке pages файл с таким названием.
Т.е. при переходе на страницу /test/test2 скрипт проверит существование файла /pages/test.php . Если файл есть — PHP выполнит этот файл, в противном случае выполнится файл /pages/404.php .
Как видите, при таком подходе нам больше не нужно прописывать соответствие URL-адресов и PHP-файлов. PHP сам будет искать нужный файл в папке pages по первому фрагменту URL.
Теперь осталось только создать файл pages/products.php . Сделаем небольшую заготовку:
elseif($segments[1] === 'add') < // Если запрос пришёл методом POST if($_SERVER['REQUEST_METHOD'] === 'POST') < // добавляем новый товар в базу >// Если запрос пришёл методом GET else < // отображаем форму добавления товара >>
Вот так выглядит обработка действий. Мы смотрим на второй фрагмент URL и ищем обработчик этого действия. Для каждого действия (add, update, delete) нужно прописать отдельный блок elseif.
Внутри обработчика add мы смотрим на то, каким методом пришёл запрос, GET или POST. Если GET — отображаем форму, если POST — добавляем товар.
Если вам не нравится вложенная проверка метода, можно сделать иначе. В файле index.php сохраним метод в отдельную переменную:
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $segments = explode('/', trim($uri, '/')); $method = $_SERVER['REQUEST_METHOD']; // .
elseif($segments[1] === 'add' && $method === 'GET') < // отображаем форму добавления товара >elseif($segments[1] === 'add' && $method === 'POST') < // добавляем новый товар в базу >
Готово. Да, если вам не нравится, что в коде 2 раза встречается одно и то же действие, только с разными методами, можете использовать немного упрощённую схему URL-адресов из фреймворка Laravel:
(GET) /products - отображение товаров (GET) /products?id=5 - отображение 5-ой страницы товаров (GET) /products/create - отображение формы добавления товара (POST) /products/store - сохранение товара из формы добавления (GET) /products/edit/15 - отображение формы редактирования товара с /products/update - сохранение товара из формы редактирования (POST) /products/destroy - удаление товара по его идентификатору в базе
Добавление префикса /admin/ в URL
Немного изменим код index.php :
Теперь при запросе страницы /admin/products PHP будет искать файл с названием не products.php , а admin_products.php .
Переименуйте файл и не забудьте заменить в нём все $segments[1] на $segments[2], поскольку в $segments[1] теперь лежит модуль, а в $segments[2] действие.
Продвинутый роутер FastRoute
Если вы ищете более серьёзную систему роутинга, рекомендую изучить библиотеку FastRoute. Это очень мощный роутер, идеально подходящий для сложных приложений, особенно если вы используете ООП.
Если вы хотите, чтобы я написал отдельную статью по работе с FastRoute — пишите об этом в комментариях.