PHP в деталях

         

Файл srm.ini


####### modules { path = "/usr/local/srm/lib/"; load "auth"; load "http"; load "php4"; }

srm { sockname = "/tmp/srm.socket"; port = 7777; log_level = 100; logfile = "/var/log/srm"; }

store { application_items = 2048; session_items = 4096; application_lock_items = 1024; session_lock_items = 1024; session_save_path = "/var/state"; }

http { port = 7780; }

banana { class_path = "/usr/local/srm/banana/lib"; function_library = "/usr/local/srm/banana/lib/library.php"; } #######

Пояснения:

modules? подгружаемый модули SRM. Для работы с PHP нужен специфически собранный PHP (об этом позже). Без него работают модули auth & http.

srm ? надеюсь, что тут комментарии не нужны.

store ? установки количества элементов, хранимых в памяти и в сессиях (у SRM свои собственные сессии, в которые он пишет дамп при shutdown и откуда читает при старте).

banana ? настройки виртуального класса, которыя является родителем для всех классов, которые можно использовать с SRM.



Как поладить дизайнеру с программистом


DL
29.5.2002

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

Понятно, что XSL - это небольшой язык программирования, который неопытные программисты глубоко освоить не могут. Если для программиста XSL - это ещё один роман, то для верстальщика - проблема. Но давайте не будем кидаться определениями. Вместе с программистами и дизайнерами могут работать также верстальщики и кодеры.

Разделим технологии на несколько ступенек:

дизайн - графические пакетыHTMLсопутствующие HTML языки - JavaScript, VB Script, CSSXSLPHPнастройка и администрирование сервера

Автор работает в фирме, где есть только программисты и дизайнеры. При этом дизайнеры имеют широкие обязанности - не только делать рыбу в HTML, но и вставляют форматирование в код. Обязанности распределяются следующим образом:

дизайнHTMLJS, VBS, CSSPHPсерверпрограммистдизайнер

 

Схема дизайнер-верстальщик-программист. Здесь дизайнер не знает HTML, либо знает на слабом уровне. Макеты форматирует верстальщик. Программист занимается скриптами и сервером. Структура, свойственная большим фирмам.

дизайнHTMLJS, VBS, CSSPHPсерверпрограммистверстальщикдизайнер

 
  
 

В совсем больших фирмах среди программистов выделяют также кодеров.

дизайнHTMLJS, VBS, CSSPHPсерверпрограммисткодерверстальщик (есть ли?)дизайнер

 
  
  
 

Разговоры о том, что дескать ни верстальщик, ни дизайнер не знают и не захотят изучать XSL, бессмысленны. Проблемы начинаются из-за неправильного распределения обязанностей.

Пытаемся сделать сайт на XML+XSLT, отводим под это экспериментальный или малозначимый проект, делаем соответствующую организацию этого проекта (плохую то есть). Потом, когда ничего не работает, никто не может сделать то, что нужно, оставляем затею с XSL, переделываем всё старыми испытанными способами. После этого пишем или .

А в начале надо ответить для себя на вопрос: надо ли пробовать делать сайт на XML+XSLT? Если вы, не дай бог, ответили "да", то молитесь! Шутка.


Дальше, поскольку это новая технология для фирмы, надо определить, кто будет заниматься ей каждодневно. После этого идёт анализ, по силам ли такое фирме. Всё.

Проще всего с применением XSL будет большим фирмам, где программисты разделяются на разработчиков и кодеров. Превращение HTML-рыбы и образца XML-документа в работающий XSLT-лист - работа именно по части кодера. На первой схеме я поместил XSL между "приладами" HTML и PHP - и идея поручить XSL кодеру в эту схему красиво вписывается (сперва предположил про кодера, а схему нарисовал потом, честно! :).

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

Другая фирма: дизайнер рисует макет внешнего вида страницы, остальное делают несколько программистов. Парни эти - мастера на все руки, причём очень высокого уровня. Над одним проектом работает один человек. Тоже нет смысла применять XML, потому что одному человеку, наверное, проще разобраться в одном программном "слое" - php смешанном с HTML, - чем с двумя или тремя (php + шаблон XML + XSL).

Выводы:

1. XML+XSL применимы там, где есть разделение труда

2. где разделение труда хорошо организовано

3. проект соответствующего масштаба (мелкий можно и в виде HTML смешанного с PHP, для крупного XSL тоже не очень подходит)


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


16.5.2001 12:08  kostysh  []
...это, конечно метод, но как тогда быть с корпоративными юзерами, сидящими в маскарадных сетках?

у них же у вес один IP, хоть даже если их 100, и поллучается, что если среди них затесался

хакерюга и лезет подбирать пароль к тебе на сервер, то после н-ного кол-ва попыток, он

просто напросто отрубит всю свою сетку от доступа к твоему серверу...

я думаю, что здесь нет единого оптимального решения.

того, кто серьезно задумал поломать твой сервис остановить почти невозможно.

для пользователей не-вебовских интерфейсов доступа к сервисам есть одни методы

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

я думаю, что розумнее всего контролировать вэбовского юзера с использованием

сессий... в таком случае мы имеем дело с действительно уникальным параметром

- ID сессии... ограничить число попыток входа в систему в рамках одной сессии

это очень простая задача, но расчитывать на то, что хакер будет сидеть за окном

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

это будет специальный софт, который без проблем начнет новую сессию, когда ты

закроешь предудущую... ты с ним в этом случае ничего не сделаешь.

есть один выход - анализ трафика. Что я имею ввиду - а вот что:

необходимо контролировать и анализировать общее количество сессий открытых

в системе с определенного IP, причем не всех сессий а только тех

которые были закрыты по причине ошибок ввода пароля.

необходимо либо на угад, либо опытным путем установить критическое число для

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

Анализируя трафик, нужно не забывать о распределении отказов доступа во

определенном временном промежутке, чтобы не перепутать обычный всплеск пользовательской

активности с атакой хакера... короче здесь еще говорить и говорить.

вот, такая получилась маленькая статья. Я, кстати, сейчас работаю именно над

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

------------------------------

mailto:army@mail.od.ua

KOSTYSH
Ответ DL:

Спасибо!
<

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


14.6.2001 18:14  Andrew  []
Что ж, решение стандартное, каждый веб-программист с такого и начинает :). (нечто подобное делал и я) А что будет с сайтом у которого большой траффик?? Через какое время БД логов забьется под завязку? Сколько потребуется времени MySql-у чтобы переварить свои сотни тысяч записей, для составления статистики? Может необходимо подумать о вычислении каких-то промежуточных результатов, и хранить только их? Скажу, что я эту проблемму еще не решил... Хотелось бы услышать мнения на этот счет
20.6.2001 10:57  Iv An  []
1. Берётся халявная Виртуоза ( http://www.openlinksw.com ), а вовсе не MySql. Хотя бы потому, что primary key будет расти абсолютно монотонно, и MySql "заболеет" после right-edge вставки 100000 записей.

2. Не берётся PHP вообще. (Ау, модератор, здесь дальше сплошной оффтопик).

3. Импорт данных не есть проблема, даже если их глотать просто из Apache-вского лог-файла. Для разбиения каждой строки на поля есть напр. split_and_decode(...), а для особенно изощрённой обработки HTTP-USER-AGENT есть regexp_...(...).

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

4. Пишется набор тривиальных VSP-страничек. Ну то есть совсем тривиальных. Данные для них можно собрать "на лету", можно готовить при помощи триггера (обновлять табличку с готовыми статистиками after insert of записи из лога с номером кратным 1000), можно опять же повесить функцию на таймер.

Поскольку это Виртуоза, о скорости можно особенно не думать. Специальные хитрости действительно необходимы только при годовом логе по мегабайту в день.
27.6.2001 17:08  kosha
По моему этот способ очень хорош для тех случаев когда логи за год просто не требуются. У меня например давно работает подобная вещь и лог чистится еженелдельно потому что мне больше не надо.
17.8.2001 12:00  Bronislav  []
Во многом согласен с Андреем. Вычисление статистики делается не для пользователя, а я подожду ;)) Несколько слов об объеме базы. При посещаемости 250 стр/день за год не наберется и одной сотни тысяч. Но для сайтов с иной посещаемостью эта проблема имеется. C желательностью сотавления "промежуточных результатов" (с) я уже столкнулся. На мой взгляд, здесь нет ничего принципиального. Такого рода архивация предусматривает потерю некоторых данны, и, следовательно, невозможности получения некоторых отчетов. Но так ли нам нужны ВСЕ виды отчетов за предыдущий год (полугодие) ? Некоторое неудобство - немного иная структура таблицы и другие программы. Оговорюсь, что программу эту я пока не писал.
архив | ссылки | форумы | что такое php
© , 2000-2002
© , 1999-2002

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


30.5.2001 22:26  KVN  []
Хм , статья то в принципе правильная, но это не подход!

Потому, что ты обругал что-то, НЕ предложив ничего взамен.

Икак же нам теперь учиться PHP?
Ответ DL:

Да, возможно, конструктива мало. Это больше крик души: не делайте так, как я сделал однажды. А чтобы учиться, надо писать то, что требуется, и изучать новое на простейших программках.
30.5.2001 22:30  Spectator
Ну не то, чтобы я совсем со всем согласен ;) Желание написать сразу большие вещи - это как графоманство: все хотят написать именно РОМАН. С первого раза. Реально же - ни у кого роман сразу не получается ;) Да, пробовать лучше с маленьких вещей. Хотя когда я программировал Spectator.ru, начал сразу с большого. Но: а) я отдельные куски столько раз переписывал, что старых просто не осталось б) у меня уже был опыть программирования. в) новые фичи добавлялись постепенно, а не все сразу.

Поэтому я согласен с Дмитрием - не надо сразу бросаться делать большой проект. Скромнее надо быть. И поковырятся в чужих скриптах тоже очень полезно - чужие ошибки просто виднее, чисто психологически ;) Да, и главная проблема - если вы беретесь за большой проект, не умея при этом программировать, главная проблема - то, что вы просто не видите и не представляете истиный объем работы - вы же не умеете программировать...

Короче, все правильно ;)
31.5.2001 16:59  tony2001  []
to KVN:

> НЕ предложив ничего взамен

А тебе надо было подкинуть задачку? пожалуйста: а не напишешь ли ты портальчик маленький такой... с нуля.. ??? Или Чат.ру сделай заново =)))

Учись на тех задачах, которые встают каждый день!

А эту можно решить с помощью Пфорума - если что-то не понравится в нем, то когда разберешься более менее, можно будет и подпатчить.

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

Глупо, ИМХО. Так что статья в тему и по делу.
1.6.2001 06:03  Nina
Тогда уж не "простейшие программки", а модули, классы и функции. Их можно переписывать хоть до упаду, а потом вставлять в шахматном порядке в большущий проект :).

Неплохо было бы упомянуть и о том, что собственно кодирование отнимает едва ли не 10% всего времени. 50% - планирование, остальное - отладка, тестирование, размышления на тему "а нужно ли оно". Суровые программистские будни :).
Ответ DL:

Да, естественно :)
<

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


27.5.2002 15:34  tony2001  []
если кто собирал ман от РНР из XML - поделитесь, плз, опытом.

если получится - будет и русская версия мана от SRM =)
4.6.2002 20:31  tony2001  []
Поздно, уже разобрался.

Перевод мана от SRM ~25% готовности...

Estimated time - 2-3 weeks.
архив | ссылки | форумы | что такое php
© , 2000-2002
© , 1999-2002

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


3.6.2002 21:43  webdeveloper
Я бы еще сказал, что XML+XSL очень хорошо будут работать на разных content managers. Там где требуется полностью отделить код от дизайна. В принципе не важно РНР это или ASP или CFML. Главное это идея. И то что это все таки стандарт. Что тоже значительно упрощает поддержку готового приложения жругими програмистами. (В отличии от многочисленных классов шаблонов)
20.6.2002 13:06  DenisK  []
по поводу выводов: пункт 1, 2 - согласен

пункт 3: я его несколько раз прочитал, так и не врубился - это почему же XSL не для крупных проектов?

а на фига он нужен в домашних страницах?

а так все хорошо начиналось...
Ответ DL:

Насчёт того что не для крупных проектов - это, конечно, спорно. Здесь я просто ретранслировал мнение одного человека, который работал в крупных проектах, и кому доверяю.
архив | ссылки | форумы | что такое php
© , 2000-2002
© , 1999-2002

Пароль на страницу. Часть 2. Блокировка подбора (отредактированная)


DL
15.5.2001

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

Но сначала о блокировке подбора. Банальности, но всё-таки. Пароль длинной десять символов из букв латиницы и цифр - это очень много вариантов. Если подбирать пароль по 1000 000 вариантов в секунду, понадобится несколько тысяч лет. Но поскольку такую абракадабру запомнить сложно, мы чаще делаем пароль из осмысленных слов. Несколько лет назад оказалось, что большинство паролей можно подобрать при помощи словаря из 10 000 слов. В своё время в сети появился червь (вирус такой), который лазил по юниксовым серверам, используя их дырки в защите, и подбирал пароли привелигированых пользователей при помощи... системного орфографического словаря Юникса. Ничего таскать не надо было!

Каждый пользователь, пока он не ввёл правильный логин и пароль, считается злобным хакером. С чем же мы имеем дело, когда пользователь вводит что-либо неправильно?

1. забывчивость (на это на приличных сайтах есть формочка "забыл пароль", чтобы отправить на введёный в системных настройках email этот самый пароль)

2. баловство ("ибо нефиг")

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

4. DoS-атака (чтобы не перегрузить сервер, надо минимизировать действия, которые будет выполнять скрипт в таком случае)

Я долго думал, как можно вызвать перегрузку на сервере, если механизм защиты стоит на файлах. Оказалось, несложно (сколько это будет стоить - другой вопрос). Итак, допустим, сервер не выдержит, если скрипт будет пытаться 1000 раз в секунду открывать файлы на запись и писать в них данные. Поскольку после 5 неудачных попыток войти в систему пользователь будет сразу получать отказ в доступе (без какой-либо записи данных в файл), надо найти 200 уникальных IP, с которых по пять раз и обратиться. Это возможно. Вешаем в баннерокрутилке html-баннер с пятью тегами:

<img src="http://user:password@www.host.ru/secret/absent.gif" width=1 height=1>


Пользователь моментально делает пять обращений сервер пять раз пишет в файл (кстати, в некоторых броузерах, возможно, выскочит окно для ввода логина и пароля). Можно сделать html-страницу с пятью такими картинками, а саму страницу вставить через iframe на посещаемый сайт (через iframe - чтобы по полю referer не нашли. Вряд ли служба поддержки халявного хостинга будет заниматься такими вещами как копание в лог-файлах в поисках рефереров). Те примеры, которые я привёл, разумеется, натянуты, но сам факт того, что можно воспользоваться таким недостатком системы, доказан. Кстати, нечто подобное уже было.

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

order deny,allow

deny from all

allow from xxx.xxx.xxx

А вот код программы:

$errors = 0;

$fn = "ignore/". preg_replace("[^\d\.]", "", $REMOTE_ADDR. ".". $HTTP_FORWARDED_FOR);

if (is_file($fn)) {

  if (filectime($fn) < time()-3600)

    unlink($fn);

  else

    $errors = fread(fopen($fn, "r"), 2);

  };

if ($errors>5) {

  print ("Доступ закрыт. Зайдите через час.");

  exit();

  };

// здесь происходит установка связи с сервером БД. чтобы не трогать зря, если пользователя сразу же "отлупили".

$result = mysql_query("SELECT * FROM user WHERE login='". preg_replace("/[^\w_\-]/", "", $PHP_AUTH_USER). "' AND pass='". md5($PHP_AUTH_PW). "'");

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

  header("WWW-Authenticate: Basic realm=\"secret area\"");

  header("HTTP/1.0 401 Unauthorized");

  print ("Authorization required");

  fwrite(fopen($fn, "w"), ++$errors);



  exit();

  };

$current_user = mysql_fetch_array($result);

mysql_free_result($result);

Впрочем, грех работать с файлами, если есть база. Шутка.

Для непрошедших авторизаций создаём таблицу:

CREATE TABLE unauth (username VARCHAR(64) NOT NULL, pass VARCHAR(64) NOT NULL, ip VARCHAR(255), logintime TIMESTAMP)

И вместо обращения к файлам работаем с базой.

$errors = @mysql_result(mysql_query("SELECT count(username) as falses FROM unauth WHERE logintime>DATE_SUB(NOW(),INTERVAL 1 HOUR) AND ip='$REMOTE_ADDR $HTTP_X_FORWARDED_FOR'"),0);

if (mysql_error())

die(mysql_error());

if ($errors>5) {

  print ("Доступ закрыт. Зайдите через час.");

  exit();

  };

$result = mysql_query("SELECT * FROM user WHERE login='". preg_replace("/[^\w_\-]/", "", $PHP_AUTH_USER). "' AND pass='". md5($PHP_AUTH_PW). "'");

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

  header("WWW-Authenticate: Basic realm=\"secret area\"");

  header("HTTP/1.0 401 Unauthorized");

  print ("Authorization required");

  mysql_query("INSERT INTO unauth (username, pass, ip) VALUES ('$PHP_AUTH_USER', '$PHP_AUTH_PW', '$REMOTE_ADDR $HTTP_X_FORWARDED_FOR')");

  exit();

  };

$current_user = mysql_fetch_array($result);

mysql_free_result($result);

Хранить ли старые записи для статистики или нет - дело хозяйское. Если что, их можно удалять, выполняя перед авторизацией запрос:

DELETE FROM unauth WHERE logintime<DATE_SUB(NOW(),INTERVAL 1 HOUR)

Такой механизм при больших нагрузках будет работать быстрее и надёжнее, чем файлы - в базе часто используемые данные буферизуются и обрабатываются непосредственно в оперативной памяти.

На этом позвольте попрощаться.

P.S. Хотел начать писать про механизм сессий, но меня опередил DiMA.


Php в деталях // полная версия статьи:



© , 2000-2001
© , 1999-2001
document.write('');



Пишем свой форум?


DL
30.5.2001

В форумах регулярно появляются сообщения, в которых автор мимоходом роняет: "хочу написать форум/доску объявлений/контент-менеджер/т.п., чтобы научиться программировать на php". Тем, кто хочет делать то же самое, собирается или уже делает, советую: плюньте на эту затею. Не получится!

Вы либо почти ничему новому не научитесь, либо не сделаете свой форум. Либо вы и более-менее изучите язык и сделаете систему, потратив на это прорву времени.

Конечно же, вас не убедит аргумент, что дорожные знаки и ПДД учат до того как садятся за руль (по крайней мере в автошколах). Вы на это скажете, что берётесь за большое дело только для себя. Не, если за это обучение платят ("покопайся, напиши форум, если получится")? я молчу, флаг в руки!

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

Опасность утонуть в рутине

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

Разве можно грамотно спланировать проект, не имея представление о том, как он будет работать внутри?!Ворох старых ошибок и неэффективных решений

Обязательно придётся переписывать готовые фрагменты (ой, как неохота! они ведь уже работали!). Если переделывать ничего не надо, значит вы не видите структурных ошибок.

Ладно, пусть вы своевременно прочитали "Разработку Web-приложений" Ратшиллера и Геркена, и чего-то выяснили, на ходу поменяли структуру таблиц БД, выделили что-то в отдельный модуль, какой-то модуль упразднили. Всё равно, в приложении остаются множественные ошибки, неэффективные приёмы. Их отлавливать сложнее."Работает ? не трогай!"


Ещё один соблазн ? забыть про изучение нового и писать, писать, писать свой проект. Новых знаний нет, поэтому мировоззрение не меняется, никаких дилемм, конфликтов подходов, и вам кажется, что всё сделано правильно.

Сам сделал похожую вещь ? большой проект, "не догоню, так согреюсь". Убил кучу времени, не дописал проекта, бросил. А затем, спустя месяц обнаружил колоссальный рывок в изучении php.

Отсюда и получаются те три "либо", которые я упомянул выше: не научился, зато доделал до конца (потому что новые знания не мешали, и проект делался, как по маслу), научился, но не написал (понял, что надо всё переписывать почти с нуля и бросил), или и научился и написал (потратив кучу времени на переделку готовых частей).

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

Буквально вчера в [] один человек сказал, что хочет написать гостевую книгу, потому что устанавливать готовую очень сложно. Не факт. Способность прикрутить чужую программу целиком, частично или взять оттуда нужные функции ? ценная вещь в работе программистом. Экономия большого количества времени. Зачем изобретать велосипед, когда можно взять готовый работающий модуль, который распространяется бесплатно? Для того, чтобы сделать возможным пользователям в доске сообщений выделять текст полужирным шрифтом или курсивом, или выделять цитаты, преформатированный текст (<pre>), я не стал писать свой механизм, а скачал форум [] и взял из него готовые функции (в их лицензионном соглашении такое разрешено. Кстати, смотрите в эти соглашения, чтобы не иметь проблем с копирайтами!).

Написано много . Написать такой класс самому ? несложно (правда, в опубликованных, как я писал, бывают грубые ошибки). Многие из нас пользуются собственными классами шаблонов. А что же всё-таки делать с форумом? Поставьте []. Очень удобный форум, поддерживает несколько баз данных, модерирование, отправка ответов на email (а не просто уведомления), и никаких картинок-смайликов!!!
Проект open source, поэтому ошибки быстро находятся и исправляются. Скачайте этот форум, посмотрите на объём файлов. Вам всё ещё хочется писать свой?


Работа с БД. Анализ логов.


DL
17.5.2001

Хочу снова похвалиться своим "творчеством".

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

Что там было в таблице? Дата, адрес от корня виртуального хоста ($PHP_SELF), броузер, реферер, ip-адрес пользователя и имя хоста. Строка вставлялась так:

@mysql_query("INSERT INTO logs (date, ip, host, address, referer, browser) VALUES (NOW(), '$REMOTE_ADDR', '". gethostbyaddr($REMOTE_ADDR). "', '$PHP_SELF', '$HTTP_REFERER', '$HTTP_USER_AGENT')");

А что мы хотим видеть в статистике? Просто посмотрим разные варианты: посещения по дням, распределение посещений по времени суток. Затем эти же две выборки, только не для всех логов, а для главной страницы. Распределение по дням недели, посещаемость за последние недели и месяцы. Количество посетителей, пришедших с других сайтов. Бывает интересно посмотреть, сколько посетителей приходило с определённой ссылки в разные дни. Распределение по времени за определённый день (неделю, месяц). Адреса, куда уходят с такой-то страницы.

Систематизировать это не так сложно, как кажется. Всё вышеописаное укладывается в восемь вариантов группировки таблицы. Адрес, реферер, броузер (увы, тут получается сравнение строчек "HTTP_USER_AGENT", включающих в себя и версии, и ОС, а не отдельных броузеров. Тут без обработки на входе не обойтись), день, неделя, месяц, день недели, час.

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

Добавим к выбору группировки выбор ограничения по дням (последние n дней) а так же условия выборки для поля WHERE, которые можно ввести в текстовое поле, и получим систему, в которую укладывается те выборки и распределения, которые я описал.

На самом деле, не так просто оказалось составить список выборок и привести его к удобному для обработки в программе виду. В конце концов, я взял и запихал все запросы в массив, элемент которого выглядит так:


$selection[0] = array(

  "name" => "график по дням",

  "select" => array("date_format(date,'%e.%m.%Y') as dday", "count(date) as visits"),

  "group" => array("dday"),

  "order" => array("date DESC"),

  "type" => 1

  );

Элемент "name" ? это название для крутилки, "type" ? тип таблицы. Типов таблиц два ? просто список и список со "столбиками". Если список сортируется по количеству посещений, то столбики в принципе не нужны, а для удобства восприятия, например, графика посещений по дням, график желателен. Остальные элементы можно не комментировать.

В запросе учитываются так же и временные ограничения, и условие, которое ввёл пользователь. Переменная $type ? номер запрашиваемой выборки.

$days = intval($days);

if ($days>0)

  $selection[$type]["where"][] = "date>DATE_SUB(NOW(),INTERVAL $days DAY)";

$where = stripslashes(trim($where));

if (strlen($where)>0)

  $selection[$type]["where"][] = "($where)";

После этого рисуется форма (в крутилках первыми строчками выводятся выбранные значения. Затем строится запрос, которым узнаётся общее количество строк. Это, надо признать, скользкое место, потому что в нём никаких упрощений, просто в отличие от основного запроса, здесь просто отсутствует сортировка. Но количество строк узнаётся "в лоб" ? выбирается всё то же самое и потом делается mysql_num_rows. Если у кого будут идеи, можете прислать мне или публиковать свой анализатор (только ссылку на меня поставьте, пожалуйста).

$amount_request = "SELECT ". implode(", ", $selection[$type]["select"]). " FROM logs ";

if (sizeof($selection[$type]["where"])>0)

  $amount_request .= " WHERE ". implode(" AND ", $selection[$type]["where"]);



$amount_request .= " GROUP BY ". implode(", ", $selection[$type]["group"]);

$request = "SELECT ". implode(", ", $selection[$type]["select"]). " FROM logs ";

if (sizeof($selection[$type]["where"])>0)

 $request .= " WHERE ". implode(" AND ", $selection[$type]["where"]);

$request .= " GROUP BY ". implode(", ", $selection[$type]["group"]). " ORDER BY ". implode(", ", $selection[$type]["order"]). " ". get_limit($page, $amount, $in_page);

Как видите, я использую описанный в предыдущем выпуске модуль постраничного вывода запросов (get_limit).

Но мало дать неограниченные возможности построения запросов. Вот, допустим, смотрю я на статистику рефереров по популярности и хочу посмотреть, как народ шёл ко мне с []. Что делать? Выбирать нужные параметры крутилок и писать в текстовом поле "referer like '%hackzone%'" ну просто влом!

Оказывается, это тоже несложно сделать. Два часа мыслительных усилий и редактирования текста, и вот вышла некое подобие возможности детализации выборки. Нажимаю "список рефереров" в закладках (в закладках, потому что в адресе передаётся параметр "referer not like 'http://detail.phpclub.net%'"), получаю таблицу, в ней в строке "hackzone.ru" нажимаю на ссылку и вот оно, распределение зашедших с этого сайта по дням. Можно нажать на другую ссылку и получить распределение по времени суток ? как угодно.

Какие ещё делать выборки ? зависит от вашей фантазии. Если написанного не хватает, можно добавить свои. Обещаю по мере сил обновлять и улучшать программу.

В принципе, имея логи у себя на офисной или домашней машине, можно делать с ними всё, что душе угодно. Надо только написать обработчик. Делов-то! :) На [] я не претендую. По крайней мере, для себя я написал такую вещь, которая позволяет делать многое из того, что угодно душе. И, кстати, не грузить лишнюю информацию и рекламу Спайлог-информера и прочего.


Relax this is PHP


Junkie Doodle Profile
27.5.2002

Message # 1010670:

Date: 04/06/02 20:42

By: Junkie Doodle Profile

Subject: RE: What's wrong with XML?

I am working as a coder. And [please understand me] what a pitty i have to work with designers too. And we used to have the same problem in our projects.

1. XSL is not for designers. XSL requires programming knowledge, which is NOT for designers. And I dont want to do it myself or be bugged once in ten minutes to help a designer about it.

2. Using FastTemplate like solutions can be problemful sometimes. And if we have to think about the best way, the very best intelligent way for our projects, sometimes it may be much better to not to use external components, for example if you need to code a critical mission. Then i'm telling you what we are doing...

- Have meetings with designers before the project is started and documentate your projects. Let everyone knows what will be published on each page. Give the names of variables you will insert in HTML [$this_var_is_dummy]. Do NOT write guerilla code. Just code for efficiency of project and do NOT add extra items just because you CAN. Dont forget that nobody is judging your abilities, people just look for their job is done AS they wanted.

- Start coding on your dummy pages with real page names. And use your documentation to see what datas you need to create on each page. Use "br"s instead of tables, seperate your rows with "hr"s... forget about design and think about creating your datas. - DO ALL YOU HAVE TO DO ABOVE HTML and just put your variables inside html like <td><?=$this_var_is_dummy?></td>

- Train your designers about loops that we usually use to publish query results. And prepare your code for embedding as little packages.

- Tell designers to put the variable names instead of live data on their designs like <td>this_var_is_dummy</td>

- Make your code running on your own dummy interface

- Leave it to your designers to embed it for you.

- And go out, have fun, call your girl/boy friend

- Relax this is PHP

--

Спасибо Юрию Бохонковичу за присланный текст.



Хранение переменных, глобальных для всех


Демон и экстеншен с API для него к PHP.

Разработчик: [] () [].

Язык: C

Предназначение:
Поддержка сессий;Вызов удаленных функций; Хранение переменных, глобальных для всех приложений;Сервис для работы с удаленными глобальными объектами.
Те, кто работал в ASP с объектом Application (я не работал), сразу поймут, зачем нужен SRM. Действительно, для ASP программеров это вещь привычная. Не работавшим с ним постараюсь объяснить. Через пару часов разбирания с SRM у меня появилась четкая ассоциация ? "хранение на Слове" (кто читал С.Лукьяненко "Холодные берега" ? тот поймет сразу).

Действительно четкая аналогия:
SRM хранит объекты у себя "в холоде", однако доступен список только уже загруженных объектов, а не всех доступных;
удаленный объект ведет себя точно так же, как объект этого класса, объявленный в скрипте, однако его свойства через var_dump не увидишь (ориентация наощупь);
объект грузится один раз при первом обращении, после этого он начинает свое "удаленное существование";
свойства и методы объекта доступны любому скрипту;
кроме объектов, SRM хранит библиотеку функций и глобальные для всех скриптов переменные;
с помощью SRM можно "кэшировать" соединение с базой (по предварительным тестам это дает ощутимое ускорение), одно на все скрипты;
Варианты использования SRM:
счетчик юзеров на сайте;
кэширование соединений с базой и часто выбираемых данных из базы;
глобальные настройки сайта и темплейты;
общие для всех объекты;
ваш вариант =).
Принцип работы SRM:
демон при запуске читает , подбирает из function_library глобальные функции и слушает указанный порт и сокет.
клиентский скрипт обращается к демону и тот отдает ему результаты выполнения функций. Именно отдает результаты, функции типа echo, print в удаленных объектах не выведут на экран ничего (а вот в лог демона ? выведут).
Объекты классов-детей Banana не подбираются сразу (), а только при первом обращении. При этом, как и у обычных объектов, выполняется конструктор и др. Далее доступны методы и свойства этого объекта, однако через var_dump вы их не увидите, как я уже сказал.


Скрипты, в которых объявляются классы удаленных объектов имеют требование к наименованию: так, класс Users должен содержаться в Users.banana.php и быть наследником класса Banana. Так же в конце класса добавляются две строчки (см. пример), необходимые для инициализации удаленного объекта.

Наглядный пример: класс удаленного объекта:
<? /* Club.banana.php */ /* Класс Club */ Class Club extends Banana {     var $members;
    function Club () {         $this->members="0";     }
    function increase () {         $this->members++;     } }
$club = new Club(); $club->run(); ?>
и клиентский скрипт:
<? /* club.php */     $srm = new SRM ("/tmp/srm.socket", 7777);
    $t = new SRMApp ($srm,"Club");     $t->increase();     echo $t->members."_n";
print_r($t); ?>
Кладем класс в class_path, рестартим srmd и выполняем клиентский скрипт. На выходе получем:
--=-- [цифра] srmapp Object ( [conn_id] => Resource id #2 [handle] => 1 ) --=--
Цифра будет увеличиваться с каждым рефрешем. Естественно, если закомментить строку с increase(), то ее значение останется прежним. Похоже на сессию, основное различие в том, что она одна на всех.

Подобный класс можно прикрутить к форуму, вместо просто присвоения свойства members в конструкторе можно сделать подсчет членов клуба в базе (незачем эту выборку делать каждому посетителю на каждой странице), в случае добавления или удаления ? вызывать соответствующий метод класса ? inrease или decrease (увеличение на 1 и уменьшение соотв-но).

По-моему очень удобно.

Аналогично организуется класс UsersOnSite. Принцип прост ? выставлять человеку уникальную куку, проверять есть ли она у нас, нет ? +1 человек, проверяем таймауты у юзеров, кого-то удаляем и т.п.

Пока не работает, но в ближайшем будущем обещается (по словам Derick Rethans):
например, сохранение и поднятие дампа между перезапусками демона);
удаленные объекты не могут содержать другие удаленные объекты и ссылки на них;
и еще много полезных features;
ещё нет версии для Win(и вряд-ли появится в ближайшее время).
Также НЕ поддерживается (и скорее всего не будет): видимость переменных среды PHP ($_SERVER, $_COOKIE etc.) в удаленных объектах по чисто техническим причинам ? демон srmd работает, не зная об апаче ничего.

Не знаю как вас, а меня появление такого extension, несмотря на его сырость, само по себе уже очень радует. Может, и вправду "PHP goes to the enterprise level"?

Если это кого-то кроме меня радует ? могу написать также, как я ставил и настраивал демона и экстеншен к ПХП.

(1) если объектов нет в дампе после последнего шатдауна, которые он при прошлом выходе записал (srmd дампит свой "карман" при выходе и при запуске его подхватывает)

Ручная сортировка в веб-интерфейсе


DL
30.5.2002

Для новостного сайта или каталога чаще всего применяется структура базы данных с отдельной таблицей рубрик. Это удобно и просто: автоматически считается количество статей (сайтов) в рубриках, рубрики легко редактируются, создаются или удаляются. Идилию нарушает часто возникающая необходимость сортировки списка рубрик в заданном порядке.

Приходится либо писать этот список в HTML вручную, либо создавать в таблице в базе данных дополнительное поле для сортировки, в которое нужно писать числа опять же вручную. Это делается либо через интерфейс управления базой данных, либо дополнительное текстовое поле делается в форме редактирования рубрики. Это, конечно, неудобно для пользователя, а тем более если пользователей, которым иногда требуется менять порядок сортировки, несколько.

Решения для данной задачи есть. Из того, что я видел, это [] фирмы []. Однако он требует настройки ActiveX или VB Script и работает только в IE 6.0.

На самом деле, можно обойтись меньшими жертвами, используя простой JavaScript, который будет работать и в IE 5.0, и в Опере 5.12.

Итак, что нужно для удобного управления сортировкой рубрик? Впрочем, не только рубрик, сортировать вручную данные из базы приходится часто, просто это не делается из-за отсутствия удобного инструмента. Во-первых, порядок сортировки задаётся в отдельном поле в таблице. Во-вторых, его содержимое должно меняться автоматически, скриптами, а не людьми. Пользователям гораздо удобнее видеть не цифры, а список рубрик и кнопки "вверх" и "вниз". Эта схема широко применяется в самых разных программах:

Ещё один пример потенциального использования. На ныне почившем сайте [] был конкурс "10 слов, которые потрясли мир". Надо было в нормальном предложении, состоящем из 10 слов так переставить их, чтобы получилось смешно. Реализовано это было неважно: пользователю приходилось вводить в текстовое поле номера слов, причём нумерация шла с 0(!), что вызывало большие затруднения.


Рисуем вот такую форму:

рот

командира

отдающего

приказ

должен

быть

открыт

на

ширину

приклада

( форма работает, можете поиграться со списком и посмотреть результат, нажав кнопку "отправить")

При нажатии кнопки "Отправить" запускается скрипт, который записывает в спрятанную переменную id_set значения из списка через запятую. Именно переменную $id_set разбирает получающий данные скрипт.

В демонстрационном скрипте разбирается и проверяются все значения. Но для закрытого механизма администрирования этого не нужно.

В MySQL есть очень полезная функция FIND_IN_SET. Возвращает номер значения в ряде. Вот пример её использования:

mysql> SELECT FIND_IN_SET('b','a,b,c,d'); -> 2

mysql> SELECT FIND_IN_SET('e','a,b,c,d'); -> NULL

Таким образом можно записать в поле sort_order результат выполнения функции FIND_IN_SET(id,'id1,id2,id3,...'). При этом если в списке не окажется какого-то идентификатора, функция вернёт пустое значение (NULL). При сортировке значения NULL окажутся вверху. Чтобы этого избежать, добавляем функцию IFNULL, которая будет заменять NULL на количество id в строке + 1. Получаем простой скрипт:

if (strlen(trim($id_set))>0) {

    $id_set = preg_replace("/,\$/", "", trim($id_set));

    mysql_query("UPDATE heading SET sort_order=IFNULL(FIND_IN_SET(id, '{$id_set}'),". (substr_count($id_set, ",") + 2). ")");

форма, которая приведена здесь, работает в IE 5.0 и выше, а так же в Опере 5.12 и более новых. На NN и Mozilla не проверялось.


Script Running Magic или хранение объектов на Слове


Антон Довгаль
26.5.2002

В качестве эпиграфа:

?Тяжело тащить?

? Да нет, что ты. На Слово можно что угодно подвесить, разницы не чувствуешь.

(с) С.Лукьяненко, "Холодные берега"



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


17.11.2001 07:51  Madmann
Круто
архив | ссылки | форумы | что такое php
© , 2000-2002
© , 1999-2002

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


14.6.2001 16:38  Тимур  []
В общем интересно. У меня был аналогичный случай с рекурсией. Создал файл index.php и в нем задал комманду $im=ImageCreateFromPng("www.имя_хоста.ru/моя_папка"); Других файлов там просто небыло. Скрипт был в состоянии отладки и имя открываемого файла-картинки я просто забыл вписать. В результате скрипт открыл то, что у него прописано открывать по умолчанию index.*. И так до бесконечности. Сервер очень быстро повесился.
14.6.2001 17:56  leosha  []
Орет - потому что писать то надо о чем-то... Деньги платят за килобайты. =)

Вот и пишет всякий бред.
15.6.2001 09:46  Дмитрий  []
И про килобайты написано правильно... Увы, и это часто встретишь... И не только на dz.yandex.ru :=((((((((((

Но вот в чем автор прав, так это в том, что инструмент надо использовать по назначению: тогда и надежность выше и всем удобнее. Об этом, кстати, свидетельствуют отзывы на оригинальную статью. А то, что сейчас стало *модно* лепить PHP где надо и, чаще всего!!!!, где НЕ НАДО и это у кого-то может вызывать раздражение, причем по разной причине, - тоже правильно.
16.6.2001 00:06  nant  []
"Зная архитектуру системы всегда можно найти способ ее уронить" (с) не помню.

Это относится и к флудерам и к mod_rewrite.

mod_rewrite весьма функциональная штука с довольно большой документацией (такой sed и awk для приходящих запросов), которую тоже стоит читать.... Там описано, что rewrite будет идти пока не надоест, или пока дополнительное условие не перестанет выполняться. Очень помог бы, я думаю, флаг 'last' у правильно заданного RewriteCond.

А отслеживать работу mod_rewrite лучше через директиву RewriteLog.

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

Про то, что человек орет... это все-таки он зря. Если ты заинтересован в посещении своего сайта, тогда ты краулерам yandex, google и всем прочим будешь даже рад нагенерировать нужный контент (что и происходит :))). Именно для этого краулеры и светятся. Потому как можно создать проблемы тем людям, которые добросовестно публикуют информацию на своих сайтах и не занимаются накруткой релевантности своих страниц.

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

Так что лучше всего краулерам представляться через поле User Agent заголовка HTTP. Тогда если тебе надо будет добавить новый поисковик, которому нужен особый контент, то ты всего лишь добавишь еще одно сравнение по полю User Agent.

Стандарты в поисковых системах вряд ли появятся. Если только де-факто.

А то что платят за килобайты, так рыба ищет где глубже, а человек - где рыба :))

wbr

/nant
<

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


20.6.2001 17:24  ilya  []
Кстати, насчет обработки XSLT на клиентской стороне - IE5.5 это уже умеет.

По всей видимости, у такого способа обработки есть важное преимущество - сервер не загружен этой работой.
Ответ DL:

Гы! Даже не слышал. :)
20.6.2001 22:17  Виктор
Умеет и IE5.0 и весьма неплохо.

Во всяком случае в обработке XML на клиенте нет ничего смешного и это никакой не маразм:))

В интранете самый что ни на есть рулез!!!
Ответ DL:

Ясно. Не, если он сам обрабатывает XML+XSL, это здорово. Мне не понравился метод на яваскрипте.
21.6.2001 01:40  Wedr0  []
теже ощущения - супер вещь !!!
21.6.2001 10:38  Oll  []
А как на счет тегов ,хотя бы и в IE 5.02, - парсер сам IE, данные можно получать даже из сервлета. Да, XML+СSS+HTML - не бог весть что, но принцип тот - же : представление(форма) - отдельно, данные - XML - отдельно.
21.6.2001 11:25  unreal  []
кстати, XSLT в php4.0.5 уже пишет не просто "Fatal error in line #n", а с пояснениями.

Например я специально внес ошибку и вот что он выдал:

Fatal error: XSL element 'template' can only be used at the top level in /xslt.html on line 42
22.6.2001 11:41  Balbes
А ведь, вроде бы с русскими KOI8 или windows-1251 in/out encoding Sablotron не работает?

Или это у меня глюки :-(
Ответ DL:

Это специально скомпилированная версия.
18.7.2001 16:22  Nexus  []
Я делал как-то халтурку XML+XSLT на IE5, условие было, что бы работало в IE локально ...

Во-первых XSLT - не единый стандарт для всех на планете - я лично сталкивался с разными реализациями

у того же Microsoft :) они же начинают сразу давить и расширять своими тегами.

Во-вторых накладывать на клиента ограничения на браузер - это неуважение к пользователям сайта

ведь для просмотра таких сайтов обязательно должен быть типа плагин msxml 3.0 -

а что делать чувакам с Линуксами и нетскейпами ( mozzila'ми и т.д. ) ?

Делайте выводы ...
24.7.2001 20:36  Max  []
А можно как-то Sablotron вылечить, чтобы он русский понимал?
Ответ DL:

Танцы с бубном. :) Я скачал где-то Саблотрон с win1251 и знакомый прислал мне свой вариант. Несколько файлов, поочереди подставлял файлы из разных версий. Работает, но не понимает кириллицу в XSLT-документе.
30.7.2001 08:58  Mamba  []
На клиентской стороне обрабатывать XML+XSL(T) умеет даже IE5.0. Нo в интернет проектах что делать с 12% пользующихся не IE5.0 и выше. так что обработка на серверной стороне выглядит заманчивей. + в случае если используютcz xslt для сортировки/фильтрации данных и отображения результата то придется тащить всю информацию на клиента и сортировать|фильтровать на клиенте что приведет к увеличению трафика в сторону клиента.
<

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


4.7.2001 08:17  Balbes
Все хорошо, но вот если пароль запоминается с md5(), то какже, братцы, сделать сервис "Забыли пароль?", который есть на "приличных" сайтах? Ну, который потом отсылает по мейлу.
Ответ DL:

Генерить новый пароль.
4.7.2001 08:52  YEAA  []
Цитата:[22.05.01] Небольшая новость. Вот уже как 3 недели юзаю свежескаченную версию IE 6.xx.xxxx. Да, все-таки куки по умолчанию там отключены. А заставить пользователя что-либо сделат в настройках, чтобы попасть к вам на сайт, сами понимате невозможно. Да еще это будет назвываться "понижение уровня безопасности", чего от бедных пользователей добиться, все равно что пароль от Интернета добыть. Некоторые очень смелые пользователи и пароль от Инета сообщат, но

вот всех остальных и под пыткой не заставить установить пониженный уровень. Короче, очень скоро наконец-то накроются сайты использующие куки. Ибо кривая это технология...
Ответ DL:

Откуда цитата? Кто такой бред пишет? Я не знаю, какая моча в голову БГ ударила, но куки - это нормальная технология. Что, всё передавать через адресную строку, что ли?!
4.7.2001 17:59  Максим Деркачев  []
> Кстати, именно так можно залазить под паролем и без ведома друга

> в web-интерфейсы, строящие авторизацию на сессиях.

Не совсем корректно.

Системы аутентификации, построенные на сессиях обычно вообще не используют логины/пароли в куке. Вместо этого в сессии регистрируется флажок, залогинился ли пользователь или нет. При этом, если хэшик идентификатора сессии будет светиться в параметрах GET, и есть возможность их перехватить во время, когда сессия (и аутентификация) активны, то можно залезть "под юзера" даже не зная его логина и пароля. Однако совсем не обязательно этому хэшику появляться в параметрах GET.

При работающих куках id сессии появится в QUERY_STRING один раз, а именно - на втором клике (если его туда принудительно не пихать). Только в этот момент его можно поймать в логах прокси, и и "сесть" на сессию без использования сниффера. При этом, можно запретить светить session id в QUERY_STRING вообще.

Подслушивание сниффером мы не рассматриваем, т.к. это лечится SSL-ем. Кроме того, можно предусмотреть принудительный логин даже зарегистрированного пользователя, если данные, которые он собирается посмотреть, очень важны.

Т.о. для увеличения безопасности системы построенной на сессиях есть несколько решений. Наиболее простое - обязать клиентов пользоваться куками (в целях их же безопасности). Т.к. куки включены примерно у 95% всех пользователей, то этот недостаток не является критичным. Поймать session id можно будет только сниффером.

Другое решение - ограничить сессию одним адресом. Действенно, но хуже. Куча людей может сидеть за одним прокси, и они смогут подглядывать в сессии друг к другу. С другой стороны, один пользователь может кидать запросы с совершенно разных адресов, не вставая с кресла - если его провайдер использует load balancing, или динамически переключается с магистрали на магистраль в зависимости от тарифных планов. Так что это не очень хороший выход.

Так что аутентификация, построенная на сессиях, совсем не обязательно ломается при помощи простого copy/paste.
<

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


4.6.2002 11:46  Spectator
Спасибо, как раз пару недель назад я над этим думал - прикрутить древовидную структуру к моим комментариям, и тоже пришел к выводу, что для этого достаточно ввести всего еще один парамерт - level.
12.6.2002 17:50  anton
Это известная фенька: чтобы быстрее работало, нужно больше памяти для хранения, и наоборот, если важен размер базы, можно выкрутиться засчет потери скорости.
13.6.2002 03:19  ColD.  []
а как на счет етого???

http://dev.e-taller.net/dbtree/
18.6.2002 16:04  Алексей
Для каталога сайтов м.б. чего попроще? Редко где больше 2-3 уровней бывает.
21.6.2002 16:50  diRectoR  []
Подход довольно интересный, НО...

Все бы ничего, но если мне вдруг надо перенести 7 элемент с сохранением ( или без сохранения) всей структуры, скажем под 5-ый элемент. Этож сколько переправлять придется??? а если элементов структуры 7ки довольно много?

Я, естественно, имею в виду только тот случай, когда сортировка важна и ей можно управлять.
25.6.2002 00:34  ssa
А зачем так мучаться? по-моему, даже level'a не нужно
Ответ DL:

В принципе - да, но лучше не считать отступ при _каждом_ выводе. Получается экономия счётных ресурсов, а объём такое поле занимает небольшой.
27.6.2002 17:38  bunya  []
>>Отступ слева делается, учитывая поле level.

А зачем? Вполне возможно отступ можно рассчитать по формуле length(sortorder)/2 (для данного случая)

То есть поле level , похоже, не нужно.

А еще, для уменьшения поля sortorder, можно каждое добавляемое к строке число конвертить в другую систему счисления (напр. с основанием не 10 а 128) - строка немного уменьшится
архив | ссылки | форумы | что такое php
© , 2000-2002
© , 1999-2002

Новый проект: экономическая игра "Монополист"


DL
13.6.2001

Друзья! Представляю вам свой новый проект. Экономическая игра [] (не надо плеваться на название, уж какое есть, лучше не придумал :). Разумеется, только с живыми участниками, и, разумеется, на PHP. Задумывалась она ещё полгода-год назад, писалась в общей сложности неделю, и вот запущена в работу.

Что из себя представляет игра []? Игрок имеет фирму на рынке, у фирмы есть производственные мощности. Каждый ход вы указываете, какой объем товаров производить, по какой цене продавать, расходы на рекламу, и информационную безопасность. Программа подсчитывает спрос на товары фирм, продажи, затраты и доходы. Задача игрока - опережать конкурентов по объёмам продаж (цена * количество), а в идеале - победить всех и стать монополистом. Впрочем, конкуренты могут появляться когда угодно - по умолчанию в играх вход открыт в любое время. Фирмы, которые долго сидят в долгах, банкротятся. Общий спрос на товары фирм определяется... правильно, экономической коньюнктурой. Естественно, этот показатель не будет виден никому из игроков, а игроки делают прогнозы, читая новости. Грубо говоря, хорошие новости - спрос вырастет, плохие - упадёт.

И, конечно же, поскольку игра сетевая, для общения участников игр есть форум и приват. Да, кстати! Разумеется, можно переводить друг другу наличные средства, делиться технологиями и объединяться в лиги.

На сервере сейчас []. Каждая игра будет запущена, когда в неё запишутся десять участников.

В Сети подавляющее большинство игр - военные. В принципе, если в игре нет рулетки артефактов или магий, военная игра от экономической не отличается. Война - это управление особым предприятем. Управление предприятием - это особая война.

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


Но что я всё о печальном? Чего я нигде ещё не видел, так это четкой градации научных уровней и гибкости цен исследований. Как бывает в военных играх? "Апгрейд такого-то оружия стои столько-то." И так всегда, независимо от твоего или чужого уровня развития. Разве что генератор случайных чисел прикрутят и цены будут время от времени колебаться. Этому есть рациональное объяснение: никто не делится секретами с врагами. Но почему тех же правил придерживаются и авторы экономических игр? Ведь если какой-то предприниматель берётся производить сумки-тележки, он не изобретает колесо заново. А если одна автомобильная фирма представляет автомобиль с новой системой, конкуренты быстренько покупают его, изучают устройство этой системы и патенты и думают, как им сделать подобное. Ноу-хау производства тоже не вечны - ушел специалист к конкурентам, и они получили вашу технологию. Одним словом, идти в разработках впереди всех дорого. Дешевле догонять. Исходя из этого в моей игре и подсчитываются расходы на исследования.

Научные исследования сделаны просто: есть один единственный показатель - "технический уровень". Для каждой фирмы стоимость достижения n-го уровня своя. Фирма тратит деньги на исследования, и её уровень постепенно повышается (если денег на достижение очередного уровня не хватает, они всё равно зачтутся - есть отдельный показатель "прогресс исследований"). В зависимости от технического уровня определяется спрос на товары и себестоимость.

И ещё одна вещь, которую я нигде не видел - это системный шпионаж (может, мало играл?). Если подумать, то всё, что есть у соперника, нам интересно. И его технологии, и экономические показатели, и вообще всё, что он сам знает. В общем, чем больше платит фирма на шпионаж, и чем меньше платят конкуренты, тем больше ей известно о них. При определенном соотношении затрат можно и украсть технологию (бывает дешевле, чем исследовать самому). Военные ведомства большинства стран называются министерствами обороны, поэтому в игре эта графа расходов названа "информационная безопасность".

[]


Пароль на страницу. Часть 3. Пароль от базы


DL
24.6.2001

Была у меня в своё время проблема: надо закрыть администрационную часть сайта, но при этом я не могу положить файл .htpasswd выше корневой директории сайта. Врождённая подозрительность не позволяла положить файл с паролем и отдельную директорию и заблокировать доступ к ней по http. Решил попробовать сделать защиту как в phpMyAdmin: у пользователя спрашиваются логин и пароль, с которыми скрипт соединяется с базой. В своём я сделал именно так. Удобство метода в том, что файл можно складывать куда угодно? никаких кук, никаких директив сервера для директории. Заодно, если поменяется пароль в базе данных, не надо ничего исправлять в скрипте.

Распишу метод на примере MySQL.

Пишем функцию, например, mysql_die:

function mysql_die() {

  header("HTTP/1.0 401 Unauthorized");

  header("WWW-authenticate: basic realm=\"Statistics\"");

  print ("Access denied. User name and password required.");

  exit();

  }

В начале программы указываются хост сервера БД и, если надо, имя базы:

$db_host = "localhost";

$db_name = "somedatabase";

А для соединения с базой берутся переменные сервера: $PHP_AUTH_USER и $PHP_AUTH_PW.

$db_connect = @mysql_connect($db_host, $PHP_AUH_USER, $PHP_AUTH_PW)

  or mysql_die();

И всё. Теперь о недостатках. Разумеется, с такой защитой можно пробовать подбирать пароль (в принципе, можно приделать , но тогда потерятеся вся красота метода). Пароль, как и в случае защитой средствами сервера, пересылается в открытом виде. Но для простых задач такое вполне сгодится.

На днях в форуме разгорелась дискуссия, что лучше где применять ? защиту через 401-й код, свои куки или сессии. Я не стал ввязываться, чтобы не растерять красноречия, но в ближайших выпусках опишу в общих чертах последние два метода (а пока рекомендую читать ) [].



Пароль на страницу. Часть 4. Печенюшки


DL
26.6.2001

Способ этот применим там, где, во-первых, пользователей много, и их контингент постоянно меняется. Во-вторых, где нужно сделать удобный вход - чтобы можно было зайти в систему, введя логин и пароль в форме на странице.

Рисуем форму и делаем файл, который получает логин и пароль (защиту от подборки я уде описывал, допишите её сюда сами).

// обработка строки с логином

$login = str_repalce("'", "", $login);

$login_result = mysql_query("SELECT id FROM user WHERE login='$login' AND pass='". md5($pass). "'");

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

/* выдача кук. Имена кук и путь лучше во избежание путаницы определять в едином подключаемом файле. */

setcookie($COOKIE_LOGIN_NAME, $login, time()+3600, $COOKIE_PATH);

  setcookie($COOKIE_PASSW_NAME, $pass, time()+3600, $COOKIE_PATH);

/* Сразу же после входа пользователя перенаправляют на закрытый паролем адрес. */

  header("Location: /somepath/");

  exit;

  }

elseif (!mysql_error()) {

/* вывод сообщения об ошибке и формы для повторного ввода */

  print ("Неправильный логин или пароль.");

  }

else

  print (mysql_error());

Все закрытые страницы вызывают файл, в котором проверяется правильность пароля, полученного из куки:

$login = str_repalce("'", "", $HTTP_COOKIE_VARS[$COOKIE_LOGIN_NAME]);

$login_result = mysql_query("SELECT id FROM user WHERE login='$login' AND pass='". md5($HTTP_COOKIE_VARS[$COOKIE_PASSW_NAME]). "'");

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

/* Если такой строки в таблице нет, пользователь перенаправляется на страицу входа в систему. */

  header("Location: /login.php");

  exit;

  }

else

  print (mysql_error());

Имена кук будут использоваться в нескольких местах, поэтому лучше заранее поместить их в одном месте (например, объявив константы), чтобы потом не исправлять по нескольку раз.


Как видите, пароль будет бегать по каналу и лежать в файле с куками в открытом незакодированном виде. Это очень небезопасно. В отсутствие хозяина можно подойти к компьютеру, заглянуть в файл, где броузер держит куки, и записать пароль на бумажку (а если в локальной сети всё общее, то и подходить не надо, и стащить пароль можно прямо при хозяине).

Чтобы этого не произошло, пароль нужно кодировать. Как приемлемый вариант, хэш md5. Тут уже нельзя увидеть пароль и зайти в систему, записав его на бумажку или copy-paste-нув. Кстати, именно так можно залазить под паролем и без ведома друга в web-интерфейсы, строящие авторизацию на сессиях. Поэтому последнее, что можно сделать в этом направлении - это менять куку при каждой загрузке страницы.

Сам когда-то делал такую схему: в таблице пользователей есть колонка с датой последнего обращения. Эту дату последнего обращения и пароль, закодированные через md5, пользователь получает при каждом обращении. Система берёт куку с логином, вытаскивает из базы эту строку, генерирует хэш от полей last_log и passwd и сравнивает его с полученным. Если они совпадают, значит посетителя можно впускать. Для пущей безопасности можно добавить проверку на истечение куки - кука должна истечь после получаса неактивности, и, соответсвенно, в базе дата последнего лога должна быть менее чем полчаса назад.

$login = str_repalce("'", "", $HTTP_COOKIE_VARS[$COOKIE_LOGIN_NAME]);

$login_result = mysql_query("SELECT * FROM user WHERE login='$login' AND last_log>DATE_SUB(NOW(), INTERVAL 30 MINUTE)");

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

/* Получаем строку таблицы и формируем хэш от нужных полей. */

  $current_user = mysql_fetch_array($login_result);

  $hash_to_check = md5($current_user["passwd"]. " Ы - чтоб никто не догадался ". $current_user[log_time]);

  if ($hash_to_check == $HTTP_COOKIE_VARS[$COOKIE_HASH_NAME]) {

    $current_time = time();



/* Обновление поля последнего входа и выдача новой куки. */

    mysql_query("UPDATE user SET last_log='". date("Y-m-d H:i:s", $current_time). "' WHERE login='$login'");

    setcookie($COOKIE_HASH_NAME, md5(date("Y-m-d H:i:s", $current_time). " Ы - чтоб никто не догадался ". $current_user["passwd"]), $current_time + 1800, $COOKIE_PATH);

    }

  else {

/* В случае несовпадения хэша пользователь перенаправляется на страицу входа в систему. */

    header ("Location: /login.php");

    exit;

    };

  }

elseif (!mysql_error() && @mysql_num_rows($log_result)!=1) {

  header("Location: /login.php");

  exit;

  }

else

  print (mysql_error());

Разумеется, " Ы - чтоб никто не догадался " лучше тоже выделить в отдельную переменную, а лучше использовать вместо этой строки ip-адрес посетителя (или, для обрывающегося диалапа, первые два/три числа ip-адреса).

Кстати, насчёт IP-адреса. Его лучше проверять, но не весь адрес, а только первые два (для ip, начинающихся на число меньше 127) или три (соответственно, больше 127) числа адреса. Это спасёт пользователей плохого и обрывающегося диалапа от необходимости заново авторизовыватсья после обрыва связи, и в то же время, не даст зайти взломщику, укравшему куку. Конечно же, он не сможет перезвонить и зайти через другого провайдера - адрес пула не тот, но это не наши проблемы ("в такую погоду свои дома сидят"). Как не наша проблема и воровство паролей внутри фирмы. Мы защитили от любопытных товарищей и неграмотных взломщиков, а против троянов и снифферов, которые можно поставить жертве, ничего сделать не можем.

На этом "навороты" закончились. Надёжнее защиту уже не сделать. Никто не будет лазить в файл кук за хэшем и подбирать его. Проще будет поместить между пользователем и веб-интерфейсом сниффер и при помощи него найти пароль. Можно поместить трояна, который будет запоминать всё, что пользователь ввёл на клавиатуре, но это уже не наши проблемы. Чтобы защититься от прослушивания канала, надо использовать соединения типа SSL или шифрование данных.


Работа с MySQL. Часть 7. Деревья


DL
3.6.2002

Работа с MySQL. Часть 7. Деревья

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

Структуру данных лучше взять общепринятую - в записи сообщения или рубрики форума содержится идентификатор родителя. Для организации вывода дерева напрашивается рекурсивная функция. Именно так сделано в []. Файл include/multi-threads.php содержит функцию thread, которая строит вызывается для каждого корневого сообщения и рекурсивно вызывает себя для ответов на них:

function thread ($seed = 0) {

...

if(@is_array($messages[$seed]["replies"])) {

        $count = count($messages[$seed]["replies"]);

        for($x = 1;$ x

            $key = key($messages[$seed]["replies"]);

            thread ($key);

            next ($messages[$seed]["replies"]);

        }

    }

}

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

Для построения дерева достаточно знать последовательность вывода рубрик и их уровень в дереве. Добавим два поля с этими данными в таблицу: level (TINYINT(4). 127 уровней - хватит?) и sortorder (VARCHAR(128)).

Всё, что нам нужно для построения дерева - это идентификатор рубрики и её родителя. Допустим, мы имеем в каталоге несколько рубрик такого содержания:

--------- id parent --------- 3 0 5 0 7 0 10 3 11 7 12 5 13 3 16 10 21 16 26 11 30 3 47 7 60 10 73 13 75 47 ---------


Структура дерева, подобие которой мы хотим получить такова:

o- 3 | +-o- 10 | | | +-o- 16 | | | | | +-o- 21 | | | +-o- 60 | +-o- 13 | | | +-o- 73 | +-o- 30

o- 5 | +-o- 12

o- 7 | +-o- 11 | | | +-o- 26 | +-o- 47 | +-o- 75

Правда, данный алгоритм позволит нарисовать дерево, но без веток виде линий, как сделано на этом рисунке. Структура дерева будет нарисована при помощи отступов слева.

Вернёмся ещё раз к таблице id-parent. Это рубрики, уже отсортированные по некоторому признаку, по которому мы хотим сортировать элементы одинакового уровня. Например, по убыванию числа сайтов. Кроме id и родительской рубрики мы знаем и номер каждой из них в данном списке. Выровняем эти номера до нужной длины, добавив слева нули. После этого для каждой рубрики сделаем текстовую строку с номерами всех её родителей от самого корня:

------------------------------ id sort parent sortorder level ------------------------------ 3 1 0 01 0 5 2 0 02 0 7 3 0 03 0 10 4 3 0104 1 11 5 7 0305 1 12 6 5 0206 1 13 7 3 0107 1 16 8 10 010408 2 21 9 16 01040809 3 26 10 11 030510 2 30 11 3 0111 1 47 12 7 0312 1 60 13 10 010413 2 73 14 13 010714 2 75 15 47 031215 2 ------------------------------

При сортировке по полю sortorder мы получим именно то, что нам нужно:

------------------------------ id sort parent sortorder level ------------------------------ 3 1 0 01 0 10 4 3 0104 1 16 8 10 010408 2 21 9 16 01040809 3 60 13 10 010413 2 13 7 3 0107 1 73 14 13 010714 2 30 11 3 0111 1 5 2 0 02 0 12 6 5 0206 1 7 3 0 03 0 11 5 7 0305 1 26 10 11 030510 2 47 12 7 0312 1 75 15 47 031215 2 ------------------------------

Отступ слева делается, учитывая поле level.

Важно так же отметить, что нам не нужно ничего сортировать в самом скрипте. Для формирования полей sortorder и level нужно заблокировать таблицу от записи (чтобы не произошло изменения структуры веток), выбрать из базы идентификатор рубрики и её родителя, отсортировав по нужному признаку, и записать их в простой двухмерный массив. Затем обработать массив последовательно от первого до последнего уровня и записать поля sortorder и level в таблицу.



Для формирования sortorder не нужно рекурсии (хотя можно, и, вероятно, она работать будет даже быстрее). Достаточно пройтись по массиву одним и тем же циклом. В нём, если рубрика не обработана, для неё формируется поле sortorder из поля sort и родительского sortorder. Если родительская рубрика ещё не обработана, поднимается флаг $unprocessed_rows_exist и цикл запускается ещё раз.

mysql_query("LOCK TABLES dir WRITE");

$result = mysql_query("SELECT id, IFNULL(parent,0) as parent FROM dir ORDER BY sites DESC, title");

while ($row = mysql_fetch_array($result)) {

    $count++;

    $data["parent"][$row["id"]] = $row["parent"];

    $data["sort"][$row["id"]] = $count;

}

reset($data);

$unprocessed_rows_exist = true;

while($unprocessed_rows_exist) {

    $unprocessed_rows_exist = false;

    while (list($i, $v) = each($data["parent"])) {

        if(($data["parent"][$i] == 0 !isset($data["sort"][$data["parent"][$i]])) && !isset($data["sortorder"][$i])) {

            $data["sortorder"][$i] = str_pad($data["sort"][$i], $max, "0", STR_PAD_LEFT);

            $data["level"][$i] = 0;

        }

        elseif(!isset($data["sortorder"][$i]) && isset($data["sortorder"][$data["parent"][$i]])) {

            $data["sortorder"][$i] = $data["sortorder"][$data["parent"][$i]]. str_pad($data["sort"][$i], $max, "0", STR_PAD_LEFT);



            $data["level"][$i] = $data["level"][$data["parent"][$i]] + 1;

        }

        elseif(!isset($data["sortorder"][$i]) && isset($data["sort"][$data["parent"][$i]])) {

            $unprocessed_rows_exist = true;

        }

    }

reset($data);

Отмечу, что данный алгоритм не зацикливается при наличии строк с битым полем parent и не пропускает их, а делает корневыми. Рекурсивный алгоритм их просто пропустит.

После выполнения этого цикла мы имеем массивы "id => level" и "id => sortorder". Отправляем в базу всего один запрос, пользуясь внутренними функциями MySQL ELT и FIND_IN_SET:

mysql_query("UPDATE dir SET sortorder=ELT(FIND_IN_SET(id,'". implode(",", array_keys($data["sortorder"])). "'),". implode(",", $data["sortorder"]). "), level=ELT(FIND_IN_SET(id,'". implode(",", array_keys($data["level"])). "'),". implode(",", $data["level"]). ") WHERE id IN (". implode(",", array_keys($data["sortorder"])). ")");

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

В форумах чаще всего используется сортировка по дате написания сообщения. Поэтому поле sortorder можно смело делать из своего и родительского timestamp'а, выровненного функцией [] до 11-значной длины.


Валим свой сервер и флудим поисковики


DL
14.6.2001

Баннер "Поговори со мной о PHP", который красовался три дня на всех страницах сайта, как вы поняли, был шуткой. Началось это с того, что в форуме появилась жалоба, что от рекламы книг разъезжаются таблицы, и предложение повесить баннер с голой бабой и надписью "поговори со мной о php". А как раз в эти дни постоянные участики IRC-канала #phpclub открыли [].

Я решил поизголяться (не, на баннере был не я!) и похвастаться баннером на новостной ленте []. Признаться, такого количества интересующихся рекламой я не ожидал. Две недели назад я подправил код страницы с выпуском, чтобы там выводились только проверенные отзывы. И правильно сделал? находятся хулиганы, которые почти на каждый выпуск постят флуд или абракадабру из букв. Если вы всё-таки хотите увидеть все отзывы на выпуск, включая ещё не модерированные, допишите в адресной строке "comment/" ? они все там. Привести в нерабочее состояние сервер Apache ? нет ничего проще! Разумеется, испытания рекомендуется проводить на домашнем или офисном компьютере, который не жалко "повесить". Методика простая: кладёте в какую-нибудь директорию файл .htaccess следующего содержания:

Options All

RewriteEngine On

RewriteRule [a-z_.]+/?$ /путь к директории/somefile.php

И набираете в адресной строке

http://хост/путь к директории/bla_bla_bla

Сервер ничего не выдаёт броузеру, потому что... запрос "/путь к директории/bla_bla_bla" он переписал, получил "/путь к директори/somefile.php", снова глянул в .htaccess и снова переписал запрос на "/путь к директори/somefile.php". И так до бесконечности. При этом сервер быстренько забивает память компьютера.

Веб-сервер хостера вам "завалить" вряд ли удастся, но попортить _себе_ жизнь легко. Первый раз столкнувшись с такой проблемой, я бился, наверное, час. Поэтому если при отладке директив Rewrite* сервер перестаёт выдавать документы, проверьте, доходят ли запросы до "адресатов". [] Вот читаю и думаю, чего орёт человек... У меня ВСЁ лежит в базе данных, движок ? даже не от «php-nuke» до «parser», а самопальный!


Правда, в большинстве случаев, автору сайта это скорее на руку, т.к. он получает лишнюю возможность дать проиндексировать свой ресурс именно так, как он хочет и никак иначе.

Ну, это вообще можно записывать в коллекцию перлов! Кто угодно в сети может заставить поисковик проиндексировать себя как хочется, знать бы только ip-адреса роботов.

Между тем, Яндекс уже две недели индексирует не совсем то, что вы видите на моём сайте. На страницах с выпусками мой самопальный движок выдаёт ему только текст выпуска, никаких отзывов, никакой навигации (она есть на других страницах, в частности, в архиве). Судя по статистике, мои материалы стали больше находить по правильным запросам, а не по "как открыть в php новое окно". Вообще, непонятно, зачем поисковый движок Яндекса откровенно "светится", имея читаемый адрес (slovo.yandex.ru) и "Yandex" в поле USER_AGENT. Чтобы вебмастеру было виднее, что заботливый Яндекс индексирует его раз в две недели?

Лично мне от этого теплее не становится, зато становится теплее тем, кто хочет "зафлудить" поисковую машину (вспомним сайт Zhopa.ru, который в поисковиках на самые популярные запросы появлялся в первой строчке, пока его админы не забанили _ручками_). Сейчас Яндекс заявляет, что сделал добавление к поисковому механизму, которое не позволяет "флудильным" страницам вылезать в первых строках (сам проверил по запросу "реферат", вроде работает), но я могу флудить с умом. Например, смотреть, что Яндекс крутит в рекламе ("все вопросы кЫ..."), и держать табличку в базе данных с этими вопросами... Так или иначе, пока нет специального удобоваримого для поисковиков формата, им пристало маскироваться под обычного посетителя.


XML: свет в конце туннеля


DL
19.6.2001

В январе я написал на тему классов шаблонов. В ответ мне настоятельно порекомендовали изучить XML и XSL, и не прошло и полгода, как я попробовал таки работать с XML.

Первое, что предлагают желающим? это функции парсинга XML. Я спросил работающий пример. Код своего xml-парсера мне прислал Михаил Владимиров (автор сайта "How IT works"). Принцип работы этих функций такой: вы пишете функцию, например, для тегов, открывающих контейнер ? <font> ? функция start_elem, которая определяет, на что заменить данный тег. Сообщаете php, что именно её надо использовать для обработки XML, а php, скомпилировав код, будет вызывать её сам. Сама функция состоит из одной большой конструкции "switch $tag { ... case ... }". Мне такое не очень понравилось (наверное, я бы проще ориентироватлся в своём шаблоне). Я спросил, нет ли чего более удобного. "Используй Sablotron, там всё цивильно." ? ответил мне Михаил.

Sablotron ? это XML-процессор, который из XML-документа и таблицы стилей XSLT (это не как CSS, а набор специальных тегов) делает документ любой разметки, какой пожелаете (можно даже текстовый файл сделать). Просто берёте и пишете XML-документ, используя удобную вам разметку, а затем для этой структуры пишете XSLT. XML можно формировать скриптом динамически, а можно опять же через шаблоны. Думаю, что если я в скором времени переделаю этот сайт под Sablotron, от шаблонов по-прежнему не откажусь. Лень мне два раза в разных файлах написать один и тот же SQL-запрос. Пользовался когда-то FastTemplate, но слишком глупое это занятие объявлять в разных скриптах одни и те же блоки, поэтому я написал свой собственный класс, который считывает запросы из самих же шаблонов. А типовые схемы XSLT можно сохранить в файлы. В скрипте вызывается обработчик XML+XSLT и результат его работы выдаётся пользователю. Никаких проблем с совместимостью броузеров, XSLT, как и PHP хранится только на сервере (читал где-то вариант работы с XML, где он разбирается на компьютере у пользователя при помощи яваскрипта ? но это же маразм!).


Содержимое отделено от его представления. Давняя мечта программиста. В принципе, ничего нового по сравнению с классами шаблонов нет ? те же места вставки переменных или дочерних шаблонов, те же блоки строк, однако это прорыв. Прорыв по следующм причинам:

1. Обработчик шаблонов ? бинарная программа, она не компилируется при каждом запуске скрипта, а постоянно сидит в памяти, причём в одном экземпляре (если php установлен как модуль).

2. Sablotron ? программа с открытым кодом, и поэтому в ней (по идее) должно быть меньше багов на килобайт кода, чем в самопальном классе.

3. Стандарт XSLT един для всех людей на планете.

4. Консоль Sablotron-а может внятно сообщить о возникшей ошибке, значит отлаживать XSLT гораздо проще.

Недостатки XML+XSLT есть, и для маленьких проектов они могут даже перевесить достоинства.

1. Ещё один язык разметки, который надо изучить (на самом деле, не так уж сложно)

2. Ещё один модуль в архитектуре сервера. Больше времени на настройку, больше багов.

3. Фривар ? это никакой гарантии работоспособности.

4. Да, кстати! Отлаживать схемы XSLT можно только на саблотроновской консоли ? сервер будет просто говорить "Fatal error in line #n".

Из очень полезного в данной схеме работы ? значительно проще реализовать разные варианты представления данных, проще говоря скины. Если на сайте есть неколько типов страниц (как на этом), приходится разделять шаблоны на уровни. Один ? для всего сайта, содержит таблицу стилей, шапку страницы, служебную информацию. А вот в колонке где сейчас вы видите текст, может быть информация совершенно другой структуры. Под каждую структуру свой файл с шаблоном. В каждом скрипте (разумеется, их количество близко к количеству этих самых структур данных) выбирается нужный шаблон второго уровня. На этом сайте файлов шаблонов всего 14. Плюс к тому часть шаблонов лежит в базе данных. С файлами можно изловчитсья и разделить их, скажем, на директории. А как быть с заготовками в базе? Вводить дополнительные поля? Или держать в одном и разделять спецсимволами? В общем, чтобы сделать несколько видов страницы, придётся сильно попотеть.



Вспоминаю, как в конце 99-го года я думал, как сделать на своём сайте выбор цветовой схемы ? как в WebClub.ru ? и прикидывал, как лучше объявлять переменные для разных цветов и как найти и заменить все цвета в странице. Разумеется, выбор цвета я тогда не сделал, не сделал и сейчас, хотя и подумывал.

В этом плане XSLT значительно упрощает жизнь. Если типовые страницы не сильно отличаются друг от друга (например, как на "php в деталях"), можно ограничиться одним XSLT-файлом для всего скина. Тогда будет гораздо проще реализовать выбор скина для броузера или цветовой схемы.

Дизайнеры могут смело редактировать XSLT, не боясь испортить вам ? программистам ? php-код. Так же отныне такую вещь как подсветка строк то белым то серым цветом можно спихнуть на XSLT ? в описании есть пример. Если номер строки нужен только ради самого себя, нумерацию строк тоже можно отдать обработчику. Операции "если есть такая-то значение, вывести её с таким обрамлением" забываем ? на это есть кострукция "<xsl:if", а мы можем думать о более глобальных проблемах.

Я сознательно не стал давать рекомендации, где применять XML и XSLT. Так же я не стал давать в тексте примеров кода. Я лишь постарался описать видимые удобства и недостатки этой схемы обработки данных, потому что более детально рассказывать не позволяет совесть ? слишком мало знаю. Результаты моих опытов и ссылки на документацию прилагаются.


Не прощаемся


DL
3.7.2002

"Юбилейный" 60-й выпуск.

Ничто на свете не вечно, творческий порыв тоже когда-нибудь заканчивается. Проект "php в деталях", как я считаю, на сегодня исчерпал себя.

Если говорить о причинах, то главная? моя собственная оценка полезности сайта. Несмотря на название сайта ? "в деталях" ? и моё сильное желание писать подробно и раскрывать тему глубоко, статьи всегда получались довольно поверхностными. Нет такой темы, которую я освещал, чтобы можно было сказать "Я знаю её хорошо." Знания поверхностны, и на вопросы в комментариях мне отвечать затруднительно. За полтора года работы сайта появилось множество сайтов, где пишут о программировании на php. Немного среди них хороших, но таких, которые можно читать вместо моего ? достаточно.

Теперь можно свободно вздохнуть и расслабиться. Учить тому, чем профессионально не занимаешься ? очень сложно. :) Да, я забыл написать, что я не программист. Не заканчивал спецшкол, не участвовал в олимпиадах по программированию, не учусь на такой специальности в универе, не работаю программистом. В данный момент я учусь (точнее, сдав сессию, прохлаждаюсь) на экономическом факультете НГУ.

А как всё красиво начиналось! В начале 99-го я скачал мануал и стал писать скрипты для своего сайта и отлаживать их на веб-сервере по халявному диалапу. В августе того же года попробовал искать книги по PHP (напомню, система появилась в 96-м году). Это, по сути, баловство проолжалось до начала 2000-го, когда я поставил сервер apache дома и несколько поменял сайт, в т.ч. наконец-то перевёл данные в MySQL. Дело пошло живее. Осенью я решил, что имею некоторый опыт, которым могу делиться с другими. Сферы php-программирования, которые я более-менее освоил, были неважно описаны в сети. Например, инструкция как создать свой каталог сайтов на базе данных была очень простой ? большие куски кода с описаниями вроде "надо создать базу, вот скрипт, который это делает", "вот скрипт, который выводит записи из каталога". Захотелось это описать человеческим языком (возможно, тогда таких описаний было уже некоторое количество, но найти их было сложно).


Самое приятное впечатление и самое большое событие связанное с сайтом ? это поездка на празднование двухлетия PHP-Клуба 18 мая 2001 года в Москву, которую спонсировали Александр Смирнов и Антон Калмыков. Ребята, это был самый большой подарок мне, ещё раз огромное спасибо!

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

Чем займусь дальше? Во-первых, сделаю, наконец, свою домашнюю страничку. С июля 98-го в Сети, а так её и не сделал ? непорядок! Но это не будет авторским проектом ? чур меня! Продолжит свою работу игра [], доведу до человеческой кондиции игру "Пьяница". Ещё несколько задумок в стадии разработки, которые рано открывать.

Поимённые благодарности:

[]

[]

[]

[]

[]

[]

Филипп Воробьёв (Voodoo)

Haba-Haba

Евгений Климов (Slach)

[]

Антон Довгаль (tony2001)

[]

[]

[]

[]
[]

[]

[]

Наиль Кашпаров (Nail)

Алексей Тарков (grizly)

Константин Шевченко (Сat)

Идейная поддержка и ссылки:

[]

[]

[]

[]

Кого забыл, звиняйте.


Дата и время


Функций много, отмечу только некоторые самые важные: неправда, что MySQL считает дни недели только с воскресенья, как принято в Америке. Нужно использовать не функцию DAYOFWEEK, а WEEKDAY, тогда понедельнику будет соответствовать номер 0, вторнику? 1, воскресенью ? 6.

Для сложного форматирования даты (например, для вывода даты в виде 18.08.01), есть функции DATE_FORMAT (для даты и времени) и TIME_FORMAT (только для времени). Работа с этими функциями удобнее, чем использование своих собственных (потому что это средство стандартное и универсальное, чего в самопальном приспособлении добиться очень сложно), а так же быстрее (используются встроенные функции mysql-сервера, которые уже сидят в памяти, вместо компиляции при каждом запуске скорипта собственного кода).

Юниксовский timestamp MySQL тоже поддерживает ? переводы в него и из него через функции UNIX_TIMESTAMP и FROM_UNIXTIME:

UNIX_TIMESTAMP([дата-время]) ? выдаёт дату в юниксовом формате (если аргумент пропущен ? текущую дату).

FROM_UNIXTIME(дата [, формат]) ? выдаёт дату в обычном формате (во втором аргументе может быть указан формат по правилам как в DATE_FORMAT).

Кроме того, основные параметры даты ? число, день недели и месяц (возможно и словом), год, квартал (!), неделя и многое другое доступно не только через общую функцию DATE_FORMAT, но и через отдельные специальные функции.



Фильтруй базар?


DL
10.8.2001

- Застрахерите мою жизнь.

- Не "застрахерите", а "застрахуйте"...

- Не вижу в этом принципиальной разницы.

Анекдот

Разгорелась дискуссия в форуме? "как отслеживать маты". Вопрос этот обсуждался уже не раз, и сейчас снова пошёл по классическому сценарию:

 ? Как отслеживать маты?

 ? Регулярным выражением можно. А лучше брось эту затею.

 ? Дайте регулярное выражение.

 ? А если он введёт "с.л.о.в.о", что тогда делать будешь? Ещё сложнее. Плюнь на это дело.

 ? Дайте регулярное выражение.

 ? А если латиницей? Брось.

 ? Дайте регулярное выражение.

 ? Брось эту затею.

 ? Дайте регулярное выражение.

 ? Брось эту затею.

(и так далее)

Вопрос, конечно же, не праздный, и многие умы человечества над ним бьются, но какого-нибудь удачного решения я не видел. Грустно читать форум по Формуле-1 с именами "Михаэль Шума***" или "Джонни ***берт". Интересно, а как этот форум будет реагировать на слово "застрахуйте"?

Появилась как-то в ураинском интернете страница "проверка слова на маты". Предлагалось ввести слово или фразу и посмотреть, что скажет программа, а если программа неправильно определяла результат, написать авторам. И таким образом авторы хотели силами веба протестировать программу, найти-таки универсальный алгоритм распознавания русских матов. Больше об этом тестере никто ничего не слышал... Сам проверял ? Шумахера и Херберта считает матерками, почта к авторам не идёт ? не может соединиться с их сервером.

Читал документацию по White Tiger WWW Board ? там предусматривалась защита от матов включая ввод латинскими буквами и, например, двух слэшей вместо буквы "л": "/\". Не думаю, что это сильно поможет. Через такой фильтр спокойно пройдёт слово через точки.

Стоит ли говорить про то, что замена слов на звёздочки подтолкнёт посетителей на разные изыскания с целью обойти защиту. Поведение целиком зависит от манер участников и атмосферы форума. Стоит сравнить хотя бы форум о Формуле-1 сайта телекомпании [] и сайта f1news.ru ? форум f1news.ru, по-моему, самый уютный форум данной тематики, а в ПТП ? проходной двор и грызня по мелочам. Так что программы-резалки облико морале не поднимут это точно.


И, всё-таки, попробуем написать и поймать им хоть что-то. Первое, что приходит в голову:

"сука|блядь|мудак"

Вспоминаем про латиницу. Та-ак, лучше сразу применить другой подход: писать слова в массив, а из него формировать строку регулярного выражения. Делаем так же массив одинаковых символов кириллицы и латиницы и заменяем через preg_replace:

$letter_cyr[] = "/а/"; $letter_lat[] = "[аa]";

$letter_cyr[] = "/л/"; $letter_lat[] = "[". preg_quote("л/\"). "]";

$letter_cyr[] = "/к/"; $letter_lat[] = "([kк]|\|

Последний вариант ? буква к в виде "|

"[cс][yу]([kк]|\|

Почти все символы имеют латинский аналог. По-моему, этого достаточно, чтобы отказаться от такой затеи. Нет? Пойдём дальше. Перед тем, как заменять русские символы на комбинации, предусмотим возможность ввода слова через точки, тире, подчёркивания или пробелы:

$word = preg_replace("/./", "\\0[^\w]*", $word);

Слова, написанные с ошибками ? "блять" ? просто внесём в словарь вместе с правильными (кстати, у немецких шифровальщиков во Вторую Мировую Войну проблема была ? русские шифровки раскодировались в несколько раз дольше, чем шифровки англичан и французов, потому что наши шифровальщики часто совершали ошибки). Правда, есть ещё возможность написать мат заглавными буквами и разделить его строчными: "БаЛЯТЬ", "МАаНДаАВОШКА" ? простор для фантазии богатый, правда? Прикрыть такое уже невозможно ? если сделать, скажем

$word = preg_replace("/./", "\\0[^ ]*", $word);

То будет коцать уже приличные слова ? как в анекдоте про Вовочку "вообще-то я имел в виду фамилию Хэммингуэй, но ход ваших мыслей мне нравится". Дальше фантазировать можно, но это уже граничит с маразмом. Если дискуссии в вашем форуме говнистые, резалка не поможет. Разве что можно попробовать сделать некоторую разумную проверку сообщения на входе и посылать сообщения модератору с подсветкой подозрительных слов. Однако фильтр может пропустить какое-то слово, а говнюки из форума будут специально маскировать маты в сообщениях, чтобы модератор их не увидел.

Итак, резюмируя всё это и выводя мораль, скажу, что не вижу смысла в такой проверялке матов. Она будет работать ненадёжно. А если матёрые матершинники узнают, что таковая работает, то держитесь крепче ? будут маскироваться, искать пробелы в вашем фильтре. Пока никакой алгоритм не может определять лучше человека такие вещи как употребление табуированной лексики или ругань, а в правилах большинства конференций есть так же запрет на личные оскорбления, вызывающее поведение, спам ? без модератора всё равно не обойтись


Функции условий


IFNULL(x,y) ? если x не NULL, тогда выдаёт x, иначе ? y.

NULLIF(x,y) ? если x и y равны, выдаёт NULL, если не равны ? x.

IF(x,y,z) ? если x = true (вернее, если x не равен 0 и не NULL), выдаёт y, если нет ? z.

К примеру, в форуме хранится информация о пользователях и есть возможность не показывать другим пользователям свой Email. Делается поле show_email, в котором лежит 0, если пользователь не хочет показывать адрес, и 1, если разрешает.

SELECT ..., IF (show_email,CONCAT(''),'адрес не указан') AS email, ...



Эхо боя, оптимизация логов


DL
16.8.2001

Дал знакомому посмотреть сайт, который дома тестирую. Он посмотрел, похвалил, а я решил посмотреть, что он видел. Открываю логи Апача, смотрю и через некоторое время обнаруживаю, что кроме товарища ещё кто-то лазил на мой домашний сервер.

Вот он, вирус CodeRed, о котором так долго твердили большевики.

"GET /default.ida?XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX%u9090%u6858%ucbd3%u780

1%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u90

90%u9090%u8190%u00c3%u0003%u8b00%u531b%u53ff%u0078%u0

000%u00=a HTTP/1.0" 404 270

В те моменты, когда я подключался к сети по диалапу со включенным Апачем, ко мне, словно звуки далёкой канонады, приходили такие запросы. Учитывая то, что в IPv4 2^32 адресов, по которым вирус рассылается случайным образом, можно восхищаться масштабами его распространения.

Заодно перенастроил правила в Atguard, чтобы не разрешать соединяться с Апачем откуда-то ни было кроме 127.0.0.1.

Кстати, именно как по эху? вернее как по кругам на воде ? недавно замерили активность и направленность DoS-атак во всём интернете (русский комментарий к результатам ? ) []. При отправке запроса на соединение хулиган, чтобы не быть пойманным, меняет обратный адрес IP-пакета на случайный. Имея много ip-адресов, можно собирать приходящие ответы от атакуемых серверов и делать расчёты. Интересно, можно ли, слушая запросы из сети и имея алгоритм выбора случайного числа вируса CodeRed (насколько я понял из сетевых обзоров, две его версии можно отличить по строке запроса), подсчитывать количество заражённых? RomikChef прислал описание оптимизации работы с логами.

О! Какая статья интересная, а я и не читал :-( А может, и хорошо, что не читал, потому что всё по-другому сделал. Мне кажется, что у Димы не очень удачная организация базы.

У меня запись занимает ровно 20 байт. может быть, это тоже не самая удачная, но, сами понимаете, размер на порядок, а то и на два меньше.


id ? 4 байта

ip ? 4 байта

таймстамп ? 4 байта

дальше идут ссылки на словари, каждая по 2 байта

page ? $PHP_SELF

agent ? бровзер

реферер ? реферер

баннер ? баннер

Теперь по пунктам.

- ip, упакованный в 4 байта, это, конечно, короче, чем хост. Тем более, что толку от хоста в базе нет. И группировать по int, мне кажется, база будет быстрее, чем по строкам. Да, вывод информации происходит медленнее, поскольку надо ресолвить сразу кучу адресов. Но я могу и подождать ? при просмотре торопиться некуда. Имхо. А вот при записи на ресолвинг времени не тратится.

- таймстамп не родной, а юниксовский. И не потому, что родной занимает на 1 байт больше, а потому, что когда пришлось логи малость подправить, сами понимаете, стампы все сбились.

- page. Это понятно. Словарь всех страниц сайта. Таблица небольшая, всё летает.

- агент. То же самое.

- баннер ? тоже понятно. Какой баннер был показан.

- реферер. Самое шаткое место. Да, таблица растет. но, как вы понимаете, 90% рефереров ? со своего сайта. В принципе, одиночные реферера можно и почистить. Query_string у внутренних рефереров отсекается. У чужих ? отсекается тоже и пишется отдельно, для анализов.

Теперь по замечаниям в обсуждениях. Да, я пишу все хиты. Во-первых, при моем размере записи это не напряжно. во-вторых, если чувак нажал релоад, ему показался другой баннер, мне это надо учитывать.

И ещё. Вопрос. Кто как отличает роботов от людей? Я определяю по включенной графике. И в принципе, моя статистика не сильно разнится со спайлоговской.

Что там у меня было раньше?

CREATE TABLE logs ( date datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, (можно TIMESTAMP)

ip varchar(16) NOT NULL, host varchar(255) NOT NULL, browser varchar(64) NOT NULL, referer varchar(255) NOT NULL, request_uri varchar(255) NOT NULL, );

Признаться, я делал эту систему, когда не знал существования функций [] и []. Потом переделывать руки не доходили. А преобразуется ip-адрес в целое число переведением всех четырёх чисел адреса в двоичный код (с дополнительными нулями: 255 переводится в 11111111, а 4 ? в 00000100), убираются точки, и получившееся число переводится обратно в десятичный. В IPv4 может быть 2^32 адресов, т.е. (2^8)^4 ? итого число занимает четыре байта.



Timestamp из Юникса пишется просто ? функция UNIX_TIMESTAMP. Выбирается так же просто ? для группировки по дням, неделям, дням недели или чему-то ещё используется стандартная функция MySQL FROM_UNIXTIME:

SELECT DATE_FORMAT(FROM_UNIXTIME(date),'%e.%m.%Y') AS date_group, COUNT(ip) AS visits FROM logs GROUP BY date_group ORDER BY DATE_FORMAT(FROM_UNIXTIME(date),'%Y%m%d');

Чем больше узнаёшь возможностей MySQL, тем больше кажется, что ты ничего не знаешь. Каких там функций только нет!

В чём я не уверен, так это в целесообразности держания словарей адресов, рефереров и броузеров. Надо при записи лога выбирать идентификаторы, проверять, вставлять новую запись, если таковой не нашлось.

$page_result = mysql_query("SELECT id FROM page WHERE address='$PHP_SELF'");

if (@mysql_num_rows($page_result)==1)

  $page_id = mysql_result($page_result, 0);

elseif (!mysql_error()) {

  mysql_query("INSERT INTO page (address) VALUES ('$PHP_SELF')");

  $page_id = mysql_insert_id();

  };

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


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


10.8.2001 07:30  Kemp  []
Мне очень понравилась статья: коротко, ясно и понятно (не смотряна то, что я начал заниматься PHP совсем недавно).

With best regards, Kemp.
10.8.2001 14:01  five  []
Вы уж простите меня, чайника, но не могли ли бы вы разбить исходник на файлы (какой кусок исходника в каком файле должен находиться). Заранее спасибо.
Ответ DL:

Ок. Положу zip с тремя файлами.
14.8.2001 18:59  Гончар
Как много нового я узнал на ваших страницах!

Жаль только, что с моей базой данных на PostgreSQL( или с моим Internet Explorer ) функция HEADER() не cовмещается.
Ответ DL:

Вы ещё многого, видно, не знаете. Не работать из-за PostgreSQL функция header не может.
16.8.2001 19:44  Seva
А почему при логине просто не создать переменную сес. юзер в которой будет логин? и не делать проверку в базе данных на каждой странице, а только смотреть есть ли в переменной сессии "юзер" логин или нет? Чтбы нельзя было подставить переменную юзер через форму возьмем ее из массива HTTP_SESSION_VARS[].
17.8.2001 18:20  Seva
Гончар приколист такой:)

из-за постгреса хеадер не работает:--)))
21.8.2001 14:43  kosha  []
Postgres тут действительно не при чём..если я правлинь понял до функции хеадер в браузер ничего не должно передаваться в том числе не олжны устанавливаться/сниматься никакие куки, то есть в твоём примере header(Location: /file.ext); работать не должно.... хотя может я и ошибаюсь...пойду проверю
22.8.2001 15:59  kosha  []
Хотя нет..гоню я , всё работает.
26.8.2001 23:23  Stas  []
Дмитрий,

а исходники в zip-файле с тремя файлами уже куда-то положили, или еще нет?
Ответ DL:

Пока нет.
30.8.2001 12:09  Артем  []
Привет Дмитрий ! У меня вопрос. Я испоьзовал схему с куками описанную тобой в предыдущей статье. Добавил ко всему этому скрипт logout.php, который обнуляет все куки. ( типа cookie_login, cookie_passwd). Но если после logout.php нажать кнопку Back, то я опять получаю доступ к предыдущей странице, хотя этого быть не должно. Помоги плиз, что делать
Ответ DL:

Это лечится отправкой со всех закрытых страниц header("Expires: Thu, 01 Jan 1970 00:00:01 GMT");
22.10.2001 08:38  Johny  []
Переписал данный пример под Postgress SQL. Кому нужно - можно брать 3 файла по адресу http://www.andromeda.ru/rtfm/php_auth_session/
<

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


17.8.2001 10:48  Slach  []
SELECT DATE_FORMAT(FROM_UNIXTIME(date),'%e.%m.%Y') AS date_group, COUNT(ip) AS visits FROM logs GROUP BY date_group ORDER BY DATE_FORMAT(FROM_UNIXTIME(date),'%Y%m%d');

надо переделать в

SELECT DATE_FORMAT(FROM_UNIXTIME(date),'%e.%m.%Y') AS date_group, COUNT(ip) AS visits FROM logs GROUP BY date_group ORDER BY date DESC

ибо в противном случае сортировка у тебя будет производиться гораздо медленнее без использования индексов
17.8.2001 16:32  Romik Chef  []
Несколько цифр по рабочей базе.

записей за 4 месяца - 125000 (соответственно, размер базы за месяц при 1000 хитов в день составляет 600к)

Запрос к словарю страниц не отнимает времени вообще.

База юзер-агентов насчитывает 500 записей. Запрос отнимает 0.1 секунды.

10 наиболее популярных броузеров составляют 50% от общего количества.

База рефереров сейчас не в том идеальном виде, который описан - это идеи по доработке.

но тем не менее работает все очень быстро. Нагрузка на сайт не очень большая - порядка 100 посетителей в рабочий день, 1000 хитов (включая роботов).

Теперь по статье. Мелочь, правда. FROM_UNIXTIME не нужно форматировать с помощью DATE_FORMAT - она понимает вторым параметром стандартный шаблон даты.

Селектить дату очень удобно. сортировать - тоже. Работает все быстро. правда, не во всех выборках можно обойтись единственным селектом, но и простые селекты в цикле совсем не тормозят.

собственно, есдинственный плюс моей организации базы логов - размер. Если для кого-то это критично - я думаю, он использует. Если нет - то конечно удобнее писать все в кучу.

Одно я хочу сказать - смысла в половинчатых мерах я не вижу. Если писать ip и дату в int, то это экономить 20 байт на килобайте.
Ответ DL:

Там просто не самый удачный пример с DATE_FORMAT. Иногда нужен не системный формат.
17.8.2001 21:36  DL  []
Мда... вот сам и лажанулся. В FROM_UNIXTIME вторым аргументом можно использовать формат даты, как в DATE_FORMAT.
30.9.2001 03:55  Alex EXEcuter  []
>Вот он, вирус CodeRed, о котором так долго твердили большевики.

Я у себя создал файл default.ida в корне, который записывает все попытки обращения к себе.

Гораздо хуже с этим... новым, нимдой:

[Sat Sep 29 17:00:41 2001] [error] [client 213.160.196.203] File does not exist: s:/public_html/c/winnt/system32/cmd.exe

и т.п.

Не создавать же ему ехе файл %)
<

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


23.8.2001 01:58  laborant  []
Вот как оказывается... жизнь на самом деле никак и не меняется: старого RTFM никто не отменял :о))
Ответ DL:

Эт о была попытка сподвигнуть остальных на RTFM. :)
25.8.2001 14:46  hsw  []
Большинство встроенных функций MySql имеет смысл использовать только в WHERE при объеденении таблиц.

В остальных случаях это лишняя нагрузка на СУБД.
Ответ DL:

И уменьшение нагрузки на php за счёт упрощения синтаксических конструкций.
21.9.2001 21:04  Lina  []
Благородные доны, подскажите, плз, обязателен ли при вызове mysql_query второй параметр под юниксом:

$res=mysql_query($qstring)

$res=mysql_query($qstring,$conn)

Под виндой у меня первая строка работает, а под юниксом меня ткнули носом во вторую, хотя она там тоже работает :), с заявлением "за такое руки отрывать надо"...
28.9.2001 13:43  BOLK
2Lina: смотрим доку. Видим:

--------------------------------

resource mysql_query (string query [, resource link_identifier])

mysql_query() sends a query to the currently active database on the server that's associated with the specified link identifier. If link_identifier isn't specified, the last opened link is assumed. If no link is open, the function tries to establish a link as if mysql_connect() was called with no arguments, and use it.

--------------------------

какие могут быть вопросы? Просто, при использовании второй формы при просмотре текста может быть несколько неочевиднр, какой ресурс используется. Но, мне как хорошо знакомому с языком Perl, где часто используются умолчания, такая ситуация кажется вполне нормальной.
30.10.2001 14:56  Nexus  []
А вот деревце построить одной выборкой ?

типа :

id,pid,text

pid=Parent Id

слабо ?
31.10.2001 21:09  Сергей  []
Люди! Тут у нас в городе (город маленький), возникла такая ситуация: когда покупаешь катрочку на инет, пишешь в форме тыры-пыры, то идет ссылка на card.php3. Кому не лень, еси время бут, то поюзайте. Вот адрес: ppp.migsv.ru/card/card.php3,

ppp.migsv.ru/card
архив | ссылки | форумы | что такое php
© , 2000-2002
© , 1999-2002

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


1.9.2001 22:04  Victor
похоже на флейм.

Но если по существу XML+XSL - это очень удобно
3.9.2001 11:33  Nexus  []
Нет, что ни говорите, а XSLT - правильный подход :)

вот эта строка,благодаря классам и xslt у меня показывает табличку последних 5 новостей :)

echo $Page->_Go_Base("select * from news order by n_id DESC limit 5","news/news");

по select строим xml документ, и накладываем xslt стили из xsl/news/news.xsl

Вот только проблема с которой я еще не справился, в Sablot есть lt;amp;

а вот nbsp; нет %(((((

Через DOCTYPE и entities не помогает, или я че не то сделал ?

Короче, помогите с этим, и будет Вам счастье ! :)))
4.9.2001 18:31  Slach  []
Кстати да ! как объявить ENTITY nbsp ???

я тоже не очень понял.

пока пользуюсь  

но это не выход...
5.9.2001 21:21  Максим Деркачев  []
2Nexus:

<!DOCTYPE xsl:stylesheet[

<!ENTITY nbsp " ">

]>

в шаблоне - вот и счастье.

По существу. XSLT, парни - это будущее. Как сказал один умный человек, XSLT немногим лучше разнообразных *Template, за исключением того, что за ним стоит комитет по стандартизации W3C. Этим всё сказано. Дело осталось за малым - придумать как эффективно генерить XML для этих шаблонов. Oracle вроде мало-помалу эту проблему решает, думаю и остальные подтянутся. Кстати, может у кого есть по этому поводу умные мысли? В PHP пока 2 варианта:

1. плохой: $str = '<TAG>something</TAG>

2. не очень хороший - построение DOM-дерева, потом dumpmem.

Что касается "настройки русского Саблотрона" - не забивайте голову ерундой, господа. В природе не существует русского саблотрона и существовать не будет - всё за пределами US-ASCII должно быть кодировано в utf8, а потом обратно, а в какой кодировке это потом показывать - ваше дело. То же касается и © и т.п.
5.9.2001 21:27  Максим Деркачев  []
Дима, я понимаю, секьюрность и всё такое, ну хоть амперсанд-то не режь :)

Достаточно больше-меньше резать strtr-ом, и теги похерятся, а то тут стараешься, lt там, gt, а в результате полная $%#$%#%$% получается :)
Ответ DL:

Это я где-то лишний htmlspecialchars добавил. :(
13.9.2001 20:20  dimzon  []
Насчёт  

Я не знаю с какой спецификацией на XSLT ВЫ работаете, но вот 2 действующих варианта для 2-х спецификаций:

xmlns:xsl="http://www.w3.org/TR/WD-xsl"

xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

<

Математические функции


MOD(N,M) или "%"? остаток от деления N на M.

FLOOR и CEILING ? округление до целого вниз и вверх.

ROUND ? округление до целого или до определенной десятичной дроби.

LEAST (X,Y,...) и GREATEST(X,Y,...) ? минимальное и максимальное числа из указанных.

Не упоминаю стандартные математические функции взятия модуля, знака, работы с углами.



Остальные функции


LAST_INSERT_ID() ? как и [] выдаёт последний идентификатор, который сгенерировала база данных по запросу с данного соединения.

MD5(строка) ? поскольку говорят, что зашифрованный функцией PASSWORD() пароль легко расшифровать, я храню хэш md5 от пароля.

FORMAT(X, D) ? форматировать число X в виде "#,###,###.##", округлённое до D знаков после запятой. Подумал, что неплохо бы в моей [] сделать форматированные для удобного чтения числа, глянул в руководство, вот оно. Всё уже написано.

Ещё две функции, про которые я забыл, когда писал про оптимизацию работы логов.

INET_NTOA(число) ? аналог [].

INET_ATON(ip-адрес) ? аналог [].

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



Пароль на страницу. Часть 5. Сессии


DL
9.8.2001

Зачем я писал заметку про куки? "Не понимаю, зачем писать про куки, когда в php есть сессии?!" Затем, чтобы у читателей не образовывалась перед глазами плоская картина. Не везде ещё стоит php 4-й версии, а в третьей они не поддерживаются. Более того, не везде сессии так необходимы? за редким исключением алгоритм авторизации проверяет правильность логина/пароля и правильность данных сессии, а затем либо отфутболивает клиента на страницу входа, либо берёт массив (или объект) с данными о пользователе.

Случаев, когда работа сессиями необходима, не так уж и часты. Например, в своей игре "Монополист" я сразу стал использовать сессии, потому что пользователь может играть в нескольких играх и одна и та же страница в одном и том же сеансе работы может содержать разные данные. Там лучше данные для одной из игр, в которых пользователь участвует, хранить в сессии и сделать страницу для перехода между играми.

В общем, я не утверждаю, что сессиями пользоваться не нужно. Нужно, только всему своё место. К вопросу применимости трёх способов авторизации ? через 401-й заголовок ("realm"), куки или сессии ? я вернусь позже. Сейчас поговорю о сессиях.

Сессии в php ? это на самом деле не метод авторизации (само понятие неправильное, но в форумах спрашивают именно "как авторизовывать пользователя через сессии?"). Встроенный в php механизм пользовательских сессий лишь идентифицирует этих пользователей, авторизовывать ? опять же, работа вашего скрипта.

Много про механизм сессий рассказывать не буду ? уже рассказано. В самом простом виде (вернее в самом dafault-ном) механизм этот работает так: система держит на сервере файл сессии, в котором содержатся её переменные. Пользователь при запуске сессии получает уникальный идентификатор (обычно через куку), и при обращении к другим страницам отправляет её. При запуске механизма сессий в вашем скрипте обработчик php проверяет, существует ли файл соответствующий пришедшему идентификатору сессии ? если существует, то скрипт сможет прочесть данные из файла, если нет ? будет запущена новая сессия и создан файл. Разумеется, имя данной переменной опеределено в установках php.


Теперь о том, какими функциями мы пользуемся.

session_start(). Запускает сам механизм сессий. От пользователя должна быть переменная и соответствующий ей файл. Если нет файла, он создаётся, и сессия запускается с нуля. Если нет ни файла, ни переменной, то генерируется переменная (например, посылается заголовок с кукой) и создаётся файл.

session_register(имя1, имя2, имя3...). Указание, какие переменные запомнить в файле по окончании работы скрипта. После того как пользователь перейдёт к другой странице, можно запустить механизм сессий, и после вызова данной функции переменные будут доступны.

session_destroy(). Удаляет файл данных сессии (при использовании кук надо удалять их вручную, выставив пустую куку: "setcookie(session_name())").

session_end(). Если после авторизации данные о пользователе менять не надо, лучше сразу "выключить за собой свет" ? закрыть файл и освободить доступ к нему.

session_set_cookie_params(жизнь, путь, домен). Установка параметров куки с идентификатором сессии (по умолчанию кука выставляется на корень сервера и на 0 секунд ? до закрытия браузера).

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

Итак, имеем три файла ? вход (login), проверка (auth) и выход (logout).

// вырезка всех нежелательных символов

$login = preg_replace("/[^\w_\.\-]/", "", $HTTP_POST_VARS["login"]);

$pass = trim($HTTP_POST_VARS["pass"]);

// проверка переменных

if (strlen($login)==0 strlen($pass)==0)

  $error = "Введите логин и пароль";

else {

  // проверка логина и пароля

  $user_result = mysql_query("SELECT * FROM user WHERE login='$login' AND pass='". md5($pass). "'");

  /* если возникла ошибка в базе (например, пользователь всунул в сессию дли-и-инную переменную, которую база переваривать не захотела) или получилась не одна строка, отфутболиваем пользователя */



  if (mysql_error())

    die(mysql_error());

  elseif (@mysql_num_rows($user_result) != 1)

    $error = "Неверное имя пользователя или пароль.";

  // если всё нормально, выбираем данные, запускаем сессию

  else {

    $user = mysql_fetch_assoc($user_result);

    session_set_cookie_params(1800, "/");

    session_start();

    // запоминаем данные о пользователе

    session_register("user");

    // и дальше отправляем его куда-нибудь

    if (isset($HTTP_POST_VARS["return"]))

      header("Location: {$HTTP_POST_VARS['return']}");

    else

      header("Location: /");

    exit();

    };

  };

/* здесь пользователь уже не прошёл авторизацию, но может отправить куку из закрытой сессии. очистим её. */

if (isset($HTTP_COOKIE_VARS[session_name()]))

  setcookie(session_name());

// дальше рисуем форму, это неинтересно.

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

/* убиваем переменную user, чтобы нельзя было, нарисовав форму, отправить данные в post-запросе. */

unset($user);

// флаг "ошибка сессии" ? если он включён, работа прекратится.

$session_error = false;

// если не существует куки с идентификатором сессии, поднять флаг

if (!isset($HTTP_COOKIE_VARS[session_name()]))

  $session_error = true;

// если существует, запускаем механизм сессий и регистрируем переменную $user.

else {



  session_start();

  session_register("user");

  

  /* если случайно в массиве нет логина и пароля, работа тоже прекращается ("ничего не знаем, мы вам их давали") */

  if (!isset($user["login"]) !isset($user["pass"]))

    $session_error = true;

  };

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

if (!$session_error) {

  $check_result = mysql_query("SELECT uid FROM user WHERE login='{$user[login]}' AND pass='{$user[pass]}'");

  if (mysql_error() @mysql_num_rows($user_result) != 1)

    $session_error = true;

  };

// если была какая-то ошибка, то

if ($session_error) {

  

  // уничтожаем данные сессии

  session_destroy();

  // уничтожаем куку, если она была

  if (!isset($HTTP_COOKIE_VARS[session_name()]))

    setcookie(session_name(),"","/");

  /* отправляем пользователя на вход, с возможностью вернуться на запрошенный адрес */

  header("Location: /login.php?return=$REQUEST_URI");

  // прекращаем работу

  exit();

  };

mysql_free_result($check_result);

Пользователь проверен и в массиве $user ? все данные о нём, можно, например, поприветствовать его по имени-отчеству:

<?

include("auth.inc");

?><html>

<head><title><? print ("Здравствуйте, {$user[fname]} {$user[sname]}!"); ?></title></head>

<body>

[skip]

И выход:

if(isset($HTTP_COOKIE_VARS[session_name()])) {

  // запуск механизма сессий

  session_start();

  // удаление файла

  session_destroy();

  // удаление куки

  setcookie(session_name());

  };

// выход со страницы

header("Location: /login.php");

Пара замечаний: закрываемая паролем часть в данном примере - весь сервер (например, service.firm.ru), для закрытия директории нужно исправить пути. Вместо PHPSESSID используется session_name(), чтобы можно было свободно менять имя идентификатора. Кстати, на одном физическом сервере можно делать разные имена идентификаторов сессий - достаточно в нужную часть положить файл .htaccess со строкой php_value session.name "ABRACADABRA".

На этом всё. Ждите статьи со сравнением приведённых способов авторизации (свои куки, сессии и 401-й код) и нескольких статей по сессиям.


Работа с MySQL. Часть 5. Функции обработки данных


DL
21.8.2001

Должен признаться, много времени на изучение руководства по MySQL я не уделял, а брал его только когда было совсем необходимо что-то узнать. Как выяснилось, я многое потерял? вещи, над которыми иногда задумывался, оказывается, уже в базах данных MySQL реализованы.

Посмотрев в перечень математических функций, я несколько переделал подсчёт данных в своей []. Данные вынимались из базы запросом, проводились вычисления, затем возвращались обратно. Теперь количество запросов для этой операции сведено к одному ? отправляется сразу UPDATE-запрос, внутри которого указываются все вычисления и сопутствующие данные.

Пока что расскажу про функции (не все, конечно), которые нужно знать каждому, кто много работает с базой.



Строковые функции


CONV(N,система_из,система_в) ? конвертация числа из ондой системы исчисления в другую:

select CONV("ff",16,10); => 255

Кстати, конвертировать можно не только в стандартных системах (2,8,10,16), но и в любых других от 2 до 36 ? насколько хватает букв латинского алфавита.

CONCAT(X,Y,...) ? объединение строк и чисел в одну строку (пример приведён выше).

CONCAT_WS(разделитель,X,Y,...) ? аналог функции [].

LENGTH(строка) ? [].

LOCATE(подстрока, строка) ? [].

SUBSTRING(строка, отступ, длина) ? [].

TRIM() ? удаление лишних символов из начала и конца строки. В отличие от функции php [], позволяет не только пробелы, а любые символы и даже комбинации символов.

REPLACE (строка, X, Y) ? заменяет в строке X на Y (не перепутайте порядок с порядком параметров в str_replace).



XML + XSLT: ещё раз о специализации программных средств


DL
28.8.2001

На днях на выпуск об XML мне написал свой отзыв человек с ником вЪедливый. Мы решили продолжить обсуждение, и хочу дать ответ на [] в форум.

У xml xsl есть много подводных камней. Грамотно использовать его может лишь уже поднаторевший в этом.

(лирическое отступление? попробуйте поискать {Fatal error: Call, Warning: MySql} в яндексе. Результаты просто поразительны. И после этого вы будете всех перетаскивать на xml xsl ?).

1. Создавать xsl шаблоны ? это изучение верстальщиками еще одного языка. Это для программиста новый язык ? новый роман, верстальщикам сложнее.

2. Дополнительные трудности по написанию в строгом синтаксисе чтобы xml был валидным (кавычки, регистр, слэш на одинарных тагах, закрывать параграф, когда наконец все проги будут понимать русские название элементов и аттрибутов ???)

3. Выдавать xml на php сложнее чем html. На больших проектах это существенно замедляет отладку.

4. Уродская ситуация с xml и русским языком. Примерно такая же как была в jave на заре развития. Хотя конечно принципиально поддержка разных языков продумана, но новичков отпугивают первоначальные сложности. Каждая новая прога начинает с игнорирования всего кроме ascii и utf8. Php "Supported target encodings are ISO-8859-1, US-ASCII and UTF-8". Конечно это не проблема для тех кто уже почуствовал запах xml.

5. Создавать html на лету по xsl это ресурсоемко. Есть принципиальные проблемы с буферированием и получением лишь части xml. Пусть нам нужно выдать большую страницу полученную по большому xml (условно xml ? 1 Мб). Придется загрузить ВЕСЬ xml трансформировать и выдать ВЕСЬ ответ зараз. Без xml мы можем читать файл частями и выдавать по мере прочтения а не все сразу. С xml придется использовать SAX парсер ? опять доп трудности.

6. Вместе с xml все равно необходим скриптовый язык. Простейший пример ? показать N символов текста node, причем N выбирает клиент (т.е. N параметр представления а не структуры), счетчики (цифры либо буквы). Php либо jscript либо ... Кстати xml любит раздувать конструкции xsl


<div>

<xsl:attribute name = "id"><xsl:value-of select = "lev"/></xsl:attribute>

<xsl:attribute name = "class">t<xsl:value-of select = "deep"/></xsl:attribute>

<a><xsl:attribute name = "href">javascript:f('<xsl:value-of select = "lev"/>')</xsl:attribute><img border="0" align="top"><xsl:attribute name = "src">p<xsl:value-of select = "deep"/>.gif</xsl:attribute><xsl:attribute name = "name"><xsl:value-of select = "lev"/></xsl:attribute></img> <xsl:value-of select = "title"/></a>

</div>

<?php

echo <<<CONF

<div id=$lev class=t$deep>

<a href=javascript:f('$lev')><img border=0 align=top src=p$deep.gif> $title </a>

</div>

CONF;

Что из этого проще и понятнее ? Это я еще не сравнивал <xsl:if> <xsl:foreach> и if и foreach или for.

7. Трудности по загонке данных в xml. Например при экспорте в xml из excel я столкнулся с проблемой с символами ® ©. Кроме того xml создан для написания структурированных документов. Даже в ворде не все способны создавать структурированные документы. А сколько человек работают с tex который так же создан для создания структурированных документов?

Примечание. Я использую xml в своей работе. Просто иногда цена за использование xml выше чем отдача от него. Поравьте меня если ошибся. Жду возражений дополнений

И наконец самое главное

8. Ни одна база данных не поддерживает xml на нормальном уровне (мб Оracle9i XMLType ?). XML существует на начальном уровне хранения в виде файлов. Конкретнее нужно чтобы: База данных индексировала xml и позволяла быстро выдернуть часть дерева в естественном для xml синтаксисе не просматривая все. Причем желательно чтобы в случае если xml содержит ссылки на др xml то из них тоже выдиралась лишь необходимая часть. Позволяла заменить/вставить целую часть дерева за раз. Хочется смотреть на базу не как на совокупность плоских таблиц а один иерархический документ. (а лучше обоими способами).



Конечно мы можем хранить xml в clob и blob и т.д. а отдельные элементы, атрибут вытаскивать в поля, но это ИЗВРАТ.

Это нарушение изначальной концепции ? МЕДЛЕННЫЙ (как и положено интерпретатору) php использует быструю написанную на C базу данных для вычленения НЕБОЛЬШИХ кусочков информации, на обработку которых у него хватает сил.

Складываестя впечатление, что мы говорим на разных языках. Особое удивление вызывают пункты 6 и 8. Никто не предлагает заменить языки скриптования XML-ем. Это разделение труда и специализация. Тебе же не придёт в голову придумывать свои форматы записи больших объемов данных, если есть база? Ответ по пунктам приведу ниже, потому что это не самое главное.

Твоя схема работы на подключаемых файлах с верхом, низом страницы и действиями вроде записи логов, конечно удобна на проектах разного масштаба. Но она косвенно ограничивает функциональность сайта. По своему опыту знаю: с увеличением функциональности (например, сделать шапку страницы с подсветкой текущей позиции, сделать там динамические меню, которые появляются не везде и т.п.) начинается путаница. И ладно, если только ты и работаешь над таким проектом. Куда хуже будет, если такой чужой проект дадут тебе или если над ним работают двое. А если смена дизайна всего сайта, что делать? Сидеть и ковырять код? Класс шаблона тоже не очень спасает. Как мне и предсказывали, на сложном проекте я тону в тегах. Верстальщику работать с шаблонами тоже неудобно.

Об XML говорят много, и преимущественно ничего ? пустые фразы. "Отделение контента от дизайна" (или "данных от их представления") превратилось в ничего не значащее заклинание.

Работа с XML позволяет, во-первых, иметь дело в программе только с данными, во-вторых, работать с ними как с []. Вполне естественно, что за это надо платить увеличением ресурсоёмкости. А вот писать код валидно, в строгом синтаксие ? это не проблема, а вопрос культуры производства. Php-скрипт ты же пишешь по всем правилам.

Применение XSLT для трансформации XML в HTML облегчает работу программиста тем, что XSLT как специфический язык программирования берёт на себя такую мелочёвку как подсветку строк через одну, нумерацию списков и многочисленные if?else, необходимые только для правильного вывода информации (например, подсветка в меню навигации текущего места).



Связка XML+ XSLT избавляет тебя, программиста, от такой мелкой рутины и позволяет заняться только обработкой данных (при этом убрав из кода, с которым работаешь, весь шум вёрстки). Она требует большей квалификации от верстальщика и/или дизайнера, но зато теперь они не только придумывают формат документов, но сами и воплощают его в реальность. Больше верстальщик не будет бегать к тебе с просьбой: "Надо, чтоб у нового анонса значок мигал." Теперь он сам этим занимается и не морочит тебе голову этой дурью. :) Ты можешь заняться высшими материями.

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

Разумеется, применять или не применять XML+XSLT ? это личное дело. Просто поражает подход многих людей, которые не попробовали, а заранее говорят: "да нафига нам это?!", "что там такого особенного?!".

Подводя итог, скажу: php давно (больше года назад) вырос из простого интерпретатора. На php делают более сложные задачи, чем "одинамичивание" домашних страниц. Он уже не медленный интерпретатор, как ты сказал. Скрипт компилируется и только после этого запускается ? рост производительности значительный. В дополнение к мощной системе php есть обработчики XML+XSLT, которые снимают с программиста, работающего над большим проектом, заботы о красивом оформлении страниц.

Если ещё остался интерес, отвечаю по пунктам:

1. Пускай верстальщики изучают XSLT ? не такой уж он сложный, да и сами они должны знать не только HTML, но и CSS, а так же немного JavaScript.

2. Разве валидность написания раньше не требовалась? Ну, да, я понимаю, что тег </table> многие писали только из уважения к былому величию компании Netscape. Но что поделаешь ? больше возможностей, нужно больше точности в работе, выше культура производства. Ведь если вы ездите, скажем, на "Тойоте-Марк-2", заправиться бензином на шоссе из ведра у пьянчуги будет плохой приметой, не так ли?



3. Не факт, что применение XML+XSLT замедляет отладку. Во-первых, в отличие от смеси HTML+PHP, на программисте не висит, например, вывод нумерованных списков, подсветка строк и множество прочей мелочёвки. По своему опыту могу сказать, что на эту мелочёвку тратится существенная доля времени. Кстати, XML+XSLT позволяет программисту и верстальщику отлаживать свои части проекта параллельно.

Во-вторых, анализатор синтаксиса в броузере будет молчать, как партизан, и просто ничего не выведет, если будет синтаксическая ошибка. А программа-валидатор умеет грамотно сообщать, что не так.

В-третьих, если XSLT лежит в файле, а XML формируется "на лету", то сделать возможность посмотреть XML-код несложно. Если новая версия сайта делается по адресу test.site.ru, можно сделать виртуальный хост xml-test.site.ru, и пусть php-скрипт смотрит, через какой виртуальный хост его вызывают. Если через "test", то вызывает обработчик XML+XSLT, если "xml-test", тогда выдаёт просто XML.

4. Настройка русского Саблотрона ? это, конечно, шаманство, но не настолько ужасная, как кажется.

5. Кто спорит, что это ресурсоёмко. Наименее ресурсоёмкая для сервера технология ? это Фронтпейдж и заливка готовых html-страниц на сервер. Скпритовые языки, модули Apache (например, mod_rewrite) ? это всё увеличивает ресурсоёмкость, но зато упрощает разработку.

6. Этот пункт вызывает особое удивление. Никто не предлагает заменять скриптовые языки на XSLT.

7. Ну, да, с &nbsp; там надо возиться отдельно (честно говоря, мне не приходилось, надо смотреть документацию). Но, по-моему, под "структурированными документами" ты имеешь в виду сложно структурированные.

8. Будут новые версии баз, будет и возможность работать с иерархическим документом. Пока что доступных технических средств достаточно, чтобы переводить данные из "линейных" таблиц в древовидные документы.