PHP в деталях

         

Имитация файлов и директорий


DL
16.1.2001

Адрес вашего сайта появляется на пользовательском экране одновременно с дизайном и контентом. Поэтому адрес является полноправной частью сайта. Адрес типа www.фирма.ру (www.фирма.город.ру), естественно, гораздо лучше, чем www.geocities.com/Gonduras/San-Pedrillio/~наша_фирма, кто спорит. А вот по вопросу понятных человеку адресов внутри сайта общественность четкого консенсуса пока не нашла.

Однако пользователю приятнее было бы видеть адрес типа /services/special/ чем /content.phtml?q=e23908a234cc239b3445127.

Лирическое отступление. Помню, на Интернити-99 мне показали флэш-ролик [] Laser Jet 3100. Через пару недель я вспомнил про него и решил скачать его из дома. Я бы долго бродил в бесполезных поисках по сайту [] (чего вы смеетесь, это так и было!), если бы не их адреса. На HP адреса были понятные? что-то вроде "/products/printers/laserjet/3100", а на сайте Лексмарка было вот именно это непонятное "q=492898748273". Я был в сомнениях, но через день вспомнил-таки, что это был HP :).

Кстати, на этом сайте адреса выпусков, версий для печати и всех информационных страниц (ссылки, файлы и т.д.) виртуальные, файлов с такими названиями не существует.

Делается это достаточно просто. В файле .htaccess пишутся строчки, например

ErrorDocument 404 all.php

ErrorDocument 403 all.php

ErrorDocument 401 all.php

Файл all.php обрабатывает переменную $REQUEST_URI и, если нужная информация найдена, выдет команду

header ("HTTP/1.0 200 Ok");

Это необходимо для того, чтобы броузер IE 4 считал, что страница найдена, а не подставлял вместо нее свою служебную вывеску "адрес не найден". В остальных случаях, даже если запрошен адрес "all.php", пользователю будет выдаваться сообщение о том, что файл не найден.

Если при вызове функции header сервер ругается матом "Error 500" - смотрите [] и [].

Конечно же, выдать заголовок и нарисовать страницу ? нехитрое дело. Отслеживание результатов запросов, проверка на ошибки ? это скорее рутина. Самое ответственное дело ? разбор запрашиваемого адреса.


Что такое title и rub_text ? объяснять не надо. Поле address ? это адрес, по которому будет запрашиваться рубрика (новости нужно сделать рубрикой первого уровня, и в поле address будет "news"). Поле parent_id ? идентификатор рубрики уровнем выше.

Теперь, если рубики заведомо не могут быть ниже второго уровня, анализ адреса не составит особого труда.

$url = $REQUEST_URI;

// убираем слэши из начала и конца адреса

$url = ereg_replace("^/", "", $url);



$url = ereg_replace("/$", "", $url);

$dir = explode("/", $url)

// случай, когда запрошена рубрика второго уровня.

if (sizeof($dir)==2) {

// составляется запрос, объединяющий таблицу rubrika саму с собой. Здесь надо заметить только, что таблица first подразумевает рубрику второго уровня, а second, наоборот, первого.

  $request = "SELECT first.id, first.title, first.rub_text FROM rubrika first, rubrika second WHERE first.parent_id=second.id AND first.address='". $dir[1]. "' AND second.address='". $dir[0]. "'";

// Отправляем запрос в базу, а потом обрабатываем результат.

  $result = mysql_query($request);

  if (!mysql_error() && @mysql_num_rows($result)==1) {

    }

// Это на случай, если запрос прошел успешно, но ничего не найдено

  elseif (!mysql_error()) {

    }

// ...и на случай, если произошла ошибка.

  else

    die ("Ошибка БД. MySQL пишет: ". mysql_error());

  }

// Запрошена рубрика первого уровня. тут и делать-то нечего :)

elseif (!ereg("/", $url)) {

  $request = "SELECT id, title, rub_text FROM rubrika WHERE address='$url'";

  ...

  };

Хватит примеров? Дальше ? дело фантазии. Подведем итоги, распишем положительные и отрицательные моменты.

+ Красивые адреса, возможность зайти в рубрику, набрав ее адрес на клавиатуре. Благодарность от фанатов клавиатуры.



+ Уменьшение количества файлов, уменьшение количества повторяющихся операций в разных файлах.

+ Централизация вывода. Сбор большинства операций в единой точке входа.

+ Скрытие некоторой технологической части сайта.

- Увеличение ресурсоемкости за счет проверки адреса и компиляции большого файла вместо нескольких маленьких.

- Сложность с введением новых параметров (я, можно сказать, удачно вывернулся с версией для печати, но было бы более логично видеть адреса типа /13/print). Кое-что придется сбрасывать, например в куки.

- Кое-что, например, поиск, так и останется вне "точки входа" (хотя... "" [] делает поиск в адресе, но для более-менее сложного сайта это будет неудобно или невозможно).

- Дополнительные сложности с адресами картинок и навигации по сайту (броузер-то мерит все адреса относительно открытого документа, пусть даже из несуществующего адреса).

Итак, что мы получаем в итоге? Красота, даже адресов, требует жертв.

Напоследок: в этом выпуске я использовал кучу регулярных выражений, поэтому (и по просьбам читателей) обещаю в скором времени затронуть эту тему.


Комментарии к статье ""


17.1.2001 14:13  cat  []

На самом деле ты для этой статьи выбрал самый плохой способ для реализации несуществующего файла или каталога. Я имею ввиду то, что ты прописал в .htaccess

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

Да и как разобрать $REQUEST_URI это вообще не вопрос для обсуждения. Надо было предложить несколько вариантов написания .htaccess это было бы гораздо интереснее и было бы о чем поговорить.

Если интересно обращайся на мыло расскажу подробнее.

И конечно тебе не ответят на ленте ру как сделали они. Хотя большой тайны здесь нет они думают, что фантазия есть только у них :))

Ответ DL:

Большое спасибо! Выпуск #14 целиком и полностью твой :)

18.1.2001 19:00  Vitaliy  []

:) А интересно, что скажут поисковики на етот мнимый ErrorDocument?

Слышал, что для этой цели можно применить Апачевский mod_rewrite..Было бы интересно про это где-то почитать на понятном русском языке...или УРЛ..Спасибо.

Ответ DL:

Поисковики считают такие адреса нормальными - они делят всё на 200 Ok и 404 Not Found. А остальное им по барабану. mod_rewrite сейчас проходит у меня испытания.

19.1.2001 17:17  Dema

To: bigcat@

Ну выложи для всех твои решения...

Чаво умничать, а то мож они не такие красивые....

Ответ DL:

Он мне по почте описал решения, завтра-послезавтра выложу описание.

19.1.2001 20:46  ELis  []

На мой взгляд такая метода достаточно удобна и дает красивые адреса. Но есть один неприятный момент: выдача существующего документа апачем производится гораздо быстрее, чем узнавание его адреса скриптом и последующая сборка. Посему использование ее для информационных сайтов с большим числом посетителей будет сильно грузить сервер.

Ответ DL:

Естественно.

21.1.2001 15:23  camel

редкостно кривой способ. есть apache, есть mod_rewrite для него - зачем изобретать велосипед?

Ответ DL:

Тем не менее, этот метод используют, и даже рекомендуют (). А про нормальный метод ? смотри выпуск #14.

22.1.2001 14:24  Роман  []

Проблема такая. Прописываю в htaccess ErrorDocument 404 all.php3 и у меня по 404 ошибке перескакивает на тот файл целиком. В нем же REQUEST_URI равен all.php3 а не виртуальному пути :( Если можно сразу ответить, пишите на arauf@mail.ru -- а то проблема висит :(

<
table width="100%" cellpadding="0" cellspacing="0" >25.1.2001 00:55  eXpert  []
Кто нибудь разобрался, как в методе "404" передать $QUERY_STRING?

Ну никак у меня не получилось. а нужно. например для поиска.
8.2.2001 15:31  Sasha  []
Может поможите люди добрые, у меня проблема. Я пишу в .htaccess вот это

ErrorDocument 404 /all.html

ErrorDocument 403 /all.html

ErrorDocument 402 /all.html

и пишу какой- то не правильный урл на моем сайте

http://www.mysite.com/blablabla а мне все равно выдается страничка браузера
Ответ DL:

Черным по белому написано же: header ("HTTP/1.0 200 Ok").
8.2.2001 22:55  Sasha  []
аааа, делал переадресацию и на PHP файл в котором в самом

начале было вот такое header ("HTTP/1.0 200 Ok")

но проблема еще вот в чем, под виндой эта фича не работает,

а пробовали тот же httpd.conf в линксе там заработало =(

немогу понять в чем дело
9.2.2001 00:30  Sasha  []
Я вот тут все еще сижу и тра*аюсь над всем этим делом.

Попробовал я все это дело в Netscape и он умница поведал мне

что я имею кроме Нот Фаунд еще и 500-ую ошибку связанную с ErrorDocument. Я тогда отключил хидер который посылает OK-OB :)

И у меня на время все заработало, но ИЕ делал переадресацию на файл который я подставляю, а нетскейп все красиво как я ввел (http://127.0.0.1/go/to/news) вот этот баг мне не понравился. После этого я подумал "Если работает без хидера

подставлю ка я .html файл" и тут все началось с начала :(

Возвращаю все обратно и в нетскейпе работает, а в эксплорере

нифига :( Ну что это такое и как с ним бороться???
Ответ DL:

Значит у тебя под линуксом php установлен как модуль (а иначе там нельзя, наверное), а под виндой - как CGI. "Отдельно стоящий" php не может посылать такие заголовки правильно. Выход ? либо поставить модуль, либо закомментировать строки с хедэрами (насколько я понимаю, под виндой ты тестируешь), а перед закачкой на сервер - раскомментировать. Остальное из того, что ты пишешь, для меня покрыто туманом ? выражайся яснее, а лучше пиши в форум.
14.2.2001 16:12  Sasha  []
Хе-хе. А у експлорера есть настройка которая

перехватвает все коды ошибок

Show friendly HTTP error messages

как с ней бороться? Не могу же я каждому пользователю говорить

что вот надо зайти и выключить эту фишку и у вас будет все работать.

Как вы от этого избавились?
Ответ DL:

№;%$^@! HEARED("HTTP/1.0 OK!!!");!!!
<


table width="100%" cellpadding="0" cellspacing="0" >18.2.2001 02:06  Konstantin
DL: Черным по белому написано же: header ("HTTP/1.0 200 Ok").

Это надо поставить строкой в .htaccess или куда то еще?
Ответ DL:

header - это функция php, значит надо писать это в скрипте-обработчике запросов.
18.2.2001 03:14  Константин
Немного в вдогонку. Как я понял, необходимо создать файл на PHP, который будет печатать необходимый хеадер и загружать страничку. Многие посетители Вашего сервера не знакомы даже с основами PHP, поэтому не могли бы Вы привести самый простой пример решения довольно таки актуальной для многих проблемы обработки ошибок експлорером?
Ответ DL:

Это здесь же и написано! :)
18.2.2001 14:27  Константин
Это правильно?

----- missing.html -----

<?php header("http/1.0 200 Ok"); ?>

<html>

<head>

<title>HTTP 404 - File not found</title>

</head>

HTTP 404 - File not found

</body>

</html>

----- missing.html -----
Ответ DL:

Да. Только в директории, в которой надо открывать missing.html по неправильному запросу, надо положить файл .htaccess с такой строкой:

ErrorDocument 404 missing.html

Кстати, расширение лучше не html, а php. Тут надо выполнять php-код, а файлы .html такую обработку обычно не проходят (настройки в mime.types).
1.3.2001 18:48  Максим  []
Если PHP стоит как модуль апача, то URL типа /somepage/foo/bar не вызывает ошибок, в случае если существует файл /somepage.php (расширение в URL тоже можно пропустить). Будет вызван /somepage.php, при этом $SCRIPT_NAME будет содержать "/somepage" (не somepage.php !!), в $PATH_INFO будет /foo/bar . Дальше с этим можно делать все что заблагорассудится. Полная иллюзия каталогов и файлов.
Ответ DL:

Такое вроде бы пройдет, но кое у кого возникают ошибки, если вызвать одновременно MultiViews и RewriteRule.
7.3.2001 10:21  Sasha  []
Спасибо тебе за помощь. Я вот по твоей статье сделал и все работает. Можно посмотреть здесь http://ezone.com.ua/news/
Ответ DL:

Душа радуется :)
21.3.2001 15:41  David Mzareulyan  []
2eXpert: Элементарно, Ватсон! $HTTP_SERVER_VARS["REDIRECT_QUERY_STRING"]

Парсить, увы, ручками...
<


table width="100%" cellpadding="0" cellspacing="0" >25.3.2001 20:09  seagulls  []
Спасибо большое за подробную полезную статью!
27.3.2001 00:18  ukrhotel  []
Спасибо!! Так долго об этом думал, мечтал. !!! И вчера прочитал вашу статью и сделал все точь в точь. Теперь жизнь новая начинается!!! www.ukrhotel.com User friendly URL URA
Ответ DL:

угу :)
27.3.2001 12:11  Sergei  []
Насчет - [14.02.2001 16:12] не совсем понятно. совмещать 2 headers ????
6.4.2001 12:33  Sergei  []
Ну не получается заставить работать 404..=( перепробовал все. В в конфиге апача не нужно ничего прописывать ? Насчет M$ не понятно.
Ответ DL:

В конфиге апача ничего прописывать не надо. Всё - в .htaccess
11.4.2001 20:28  eagle
Не совсем понятен совет писать в .htaccess, а не в апачевский конфиг.

Конфиг читается только призапуске сервера, а .htaccess при каждом обращении

к серверу - зачем сервер лишней работой грузить?
23.4.2001 11:00  Smith  []
Все типа правильно, но есть одно но... На access_log посмотрите, одни 404 :)
22.5.2001 19:15  Muxa  []
Нашёл статью.. и мею что сказать.

По наивности я тоже хотел сделать себе сайт на таком движке (сам изобрёл и был жутко горд).

Увы, обломал меня подлый IE 5.5. Он отказался работать с файлом (в вашем примере all.php) в случае если этот файл имеет размер меньше 512 байт.

Смириться с подобным траблом я не мог, бо никто не знает что ещё может выкинуть броузер.

Так что не всё так просто.

З.Ы. Впоследствии я узнал что изобретал собственный rewrite модуль. так что rtfm. :)
13.10.2001 10:01  Taras  []
А как, в таком случае получать данные из формы, если она была послана методом POST?

В какие переменные Апач засовывает эти данные?

Пока я только разобрался, что форма присланая методом GET лежит в REDIRECTED_QUERY_STRING (вообщем-то можно и через REQUEST_URI, но это чуть сложнее).
11.12.2001 08:43  DiMA  []
> Спасибо тебе за помощь. Я вот по твоей статье сделал и все работает. Можно посмотреть здесь

> http://ezone.com.ua/news/

Смотрим.

Warning: Unable to jump to row 0 on MySQL result index 2 in bynews.inc on line 18

Warning: Unable to jump to row 0 on MySQL result index 2 in bynews.inc on line 19
Ответ DL:

Оно там работало полгода назад, я сам видел :)
<


table width="100%" cellpadding="0" cellspacing="0" >6.2.2002 18:04  arhip  []
Есть еще один вариант извращенной емуляции mod_rewrite (проверено только в Апаче). Говорите Апачу, что файлы с расширением .news и .article - это ПХП скрипты. После этого создаете файлики (именно .news) а параметры передаете через $PATH_INFO. И получается, что при запроче урл вида www.site.com/.article/enot/2002-01-01/krasivo выполняется файлик .article, а /enot/2002-01-01/krasivo будет лежать в переменной окружения с именем PATH_INFO. О мне кажется, что это все для извращенцев или для людей, которые не могут по тем либо другим причинам поставить mod_rewrite.

Замечание: данная реализация изврата не будет работать, если ві используете CGI-ерсию ПХП.<


table width="100%" cellpadding="0" cellspacing="0" >11.3.2002 10:45  lauri  []
BTW: проблема с HREF в A и SRC у картинок IMHO решается введением в <head></head> докуменка <base href=http://www.mysite.com/>.<


table width="100%" cellpadding="0" cellspacing="0" >21.4.2002 18:15  Farid  []
Privet vsem. Sdelal kak napisano v stat'e no u menya nichego ne poluchilos

naprimer u menya takaya ssilka

news.php?id=3&date=01.01.2000

kak mne ukazat ssilku v html

i v php.<


table cellpadding="4" cellspacing="0" >архив | ссылки | форумы | что такое php<


table cellpadding="4" cellspacing="0" bgcolor="#e1e1e1">© , 2000-2002
© , 1999-2002

Сервер ищет файл с тем же именем


Оказывается, достаточно прописать в установках директории (httpd.conf или .htaccess) строку

Options Multiviews

или, если директива Options уже есть, добавить MultiViews к ней. Тогда если пользователь набирает "/foo/bar", сервер будет искать файл с именем "foo" и с любым расширением. Найденный с наибольшим совпадением (вот это для меня загадка) файл он обработает с его типом mime, то есть если есть news.php, а набран адрес news/, то сервер отдаст адрес на обработку php. Если это картинка, то сервер отдаст ее броузеру именно как картинку (послав соотвествующий заголовок content-type). А в news.php разбираем $REQUEST_URI. Например, если новости выводятся целой лентой, либо за определенную дату, разбор можно сделать таким:

/* Первый вариант ? когда набран адрес типа "/news/010120", возможно с дробью на конце. Символы ^ и $ здесь обозначают привязку к началу и концу строки. Подстрока [0-9]{6} означает 6 цифр (если у вас новости могут быть датированы 1999-м годом и раньше, используйте адреса с полным форматом года и 8 цифр вместо 6). */

if (ereg("^/news/([0-9]{6})$", $REQUEST_URI, $match) ereg("^/news/([0-9]{6})/$", $REQUEST_URI, $match)) {

  }

/* второй вариант ? набран адрес просто "/news" или "/news/" */

elseif (ereg("^/news/$", $REQUEST_URI) ereg("^/news$", $REQUEST_URI)) {

  }

/* запросы ко всем остальным адресам (в этом файле) считаются попытками взлома сайта =) */

else

  die ("Error 404 Not found");

То же самое можно сделать, например, с каталогом продукции фирмы ? вынести все это дело в отдельный файл catalogue.php, а адреса сделать вида "/catalogue/rubrik1/rubrik2/rubrik3". При этом в файле catalogue.php начало строки будет откусываться, а дальше можно обработать по принципу, описанному в предыдущем выпуске. Остальное же можно отправить, опять же, в ErrorDocument.



Сервер переписывает запросы


Очень полезная вещь mod_rewrite. Ею можно сделать все вышеописанное, и много другого.

К сожалению, моя битва с mod_rewrite не увенчалась успехом (Костя пишет, что нижеописанного достаточно для работы Rewrite Engine под Unix. У меня под win98? ни в какую...). Поэтому описываю очевидные вещи и то, что описал .

Для начала надо раскомментировать строку

LoadModule mod_rewrite

в httpd.conf. В конфигурации директории пишем строку "RewriteEngine On". Затем ? команду RewriteRule:

RewriteRule <шаблон> <замена>

Например RewriteRule ^(.*).html$ /otherdir/$1.html (все без кавычек). Вот, собственно, и все. Все, что я так и не смог проверить : Я спросил у ясеня, я спросил у тополя, я спросил у форума... форум не ответил мне. (мелодично) Я спрошу у публики... На всякий случай, спрашиваю у уважаемой публики: как запустить Rewrite Engine под win98 se + apache/1.3.14 + php/4.0.4-Antonio (установлен как модуль) ?

А пока еще один пример (опять же от Шевченко):

RewriteEngine On

RewriteRule ^(.*).htm$ /portal/$1

<FilesMatch "(portal)$">

ForceType application/x-httpd-php

</FilesMatch>

Там лежит один файл с именем "portal", в который перенаправляются все запросы к html-файлам в данной директории. И получается, как будто там лежат файлы.

Напоследок вспомним [].

Вот [], в са-а-амом конце Носик говорит про ресурсоемкость их технологии. "Издательская машинка, написанная Максимом Евгеньевичем Мошковым, (который ) [] интересна тем, что она весит около 60Kb"

Не знаю, что из себя представляет эта система (скорее всего, скомнилированный ), гадать не буду. Мне хотелось бы прикинуть, как динамическую адресацию можно реаизовать через php. Впрочем, тут не в нем дело ? был бы Апач. Итак, схема адресов <рубрика>/<год>/<месяц>/<день>/<новость>. Если отрезать имя новости, получим материалы рубрики за день. Если отрезать день, месяц и год, получим последние материалы рубрики.

RewriteRule ^([a-z]+)/$ rubika_last/$1

RewriteRule ^([a-z]+)/([0-9]{4})/([0-9]{2})/([0-9]{2})/$ rubrika_date/$1/$2-$3-$4

RewriteRule ^([a-z]+)/([0-9]{4})/([0-9]{2})/([0-9]{2})/([a-z]+)/$ rubrika_news/$1/$2-$3-$4/$5

<FilesMatch "^rubrika">

ForceType application/x-httpd-php

</FilesMatch>

Преимущества этого метода по сравнению с единой точкой входа EerrorDocument очевидны: движок php интерпретирует только то, что будет выполняться. Никаких switch/case или if/elseif/else, никаких лишних строк.

Все остальное ? отдаем ErrorDocument "как в предыдущей задаче".



Сервер разбирает запрос


Метод похожий, но на вскидку менее ресурсоемкий, потому что не приходится искать файлы по директории.

В установках директории (опять же httpd.conf или .htaccess) пишем:

<FilesMatch "^(news)$">

ForceType application/x-httpd-php

</FilesMatch>

В директории лежит файл с именем "news" (именно "news", без расширения). Когда запрашивается адрес "/news", либо "/news/bla-bla-bla", сервер выполняет файл news как php-скрипт. А внутри него производится обработка переменной $REQUEST_URI.

Чтобы не писать для каждого подобного файла свой блок FilesMatch, нужно немного изменить строку шаблона. Пусть сервер ищет файлы без расширения, то есть те, у которых в имени нет точки:

<FilesMatch "^([^\.]+)$">

Очень удобно! Когда-нибудь поставлю такое же и себе.