САМОУЧИТЕЛЬ PHP 4

         

Абсолютный и относительный путь к сценарию


Обратим внимание на поле action тэга <form>. Поскольку он не предваряется слэшем (/), то представляет собой относительный путь к сценарию.

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

script.cgi и расположенного в том же самом каталоге, что и форма (точнее, HTML-документ с формой).

Как вы, наверное, догадались, термин "каталог" здесь весьма условен. На самом-то деле имеется в виду не реальный каталог на сервере (о котором браузер, кстати, ничего не знает), а часть URL, предшествующая последнему символу / в полном URL файла с формой. В нашем случае это просто http://www.somehost.com. Заметьте, что здесь учитывается имя хоста. Как видим, все это мало похоже на обычную файловую спецификацию.

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

<form action="/some/path/script.cgi">

Браузер определит, что это абсолютный путь в пределах текущего хоста (точнее, хоста, на котором расположен документ с формой) по наличию символа / впереди пути. Рекомендуется везде, где только возможно, пользоваться таким определением пути, всячески избегая указания абсолютного URL с именем хоста — конечно, за исключением тех ситуаций, когда ресурс размещен сразу на нескольких хостах (такое тоже иногда встречается).

Во втором случае получится приблизительно следующее:

<form action="http://www.other.com/any/script.cgi">

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



Accept


r    Формат: Accept: text/html, text/plain, image/gif, image/jpeg

r    Переменная окружения: HTTP_ACCEPT

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

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



Адресация с Сети




Машин в Интернете много, это факт. Так что вопрос о том, как можно их эффективно идентифицировать в пределах этой сети, оказывается далеко не праздным. Кроме того, практически все современные операционные системы работают в многозадачном режиме (поддерживают одновременную работу нескольких программ). Это значит, что возникает также вопрос о том, как нам идентифицировать конкретную систему или программу, желающую обмениваться данными через Сеть. Эти две задачи решаются стеком TCP/IP при помощи IP-адреса и номера порта. Давайте посмотрим, как.

Все, о чем рассказано далее, не является непреложной истиной. Скорее даже наоборот. Местами может показаться, что я "ломлюсь в открытую дверь"— пытаюсь доказать существование того, что и так существует. И все-таки, на мой взгляд, чтобы понять что-то, нужно сначала проникнуться мыслью, что основы этого "что-то" довольно просты, пусть даже они и абстрактны.



Арифметические операции


r a + b — сложение

r    a — b — вычитание

r    a * b — умножение

r    a / b — деление

r    a % b — остаток от деления a на b

Операция деления / возвращает целое число (то есть, результат деления нацело), если оба выражения a и b — целого типа (или же строки, выглядящие как целые числа), в противном случае результат будет дробным. Операция вычисления остатка от деления % работает только с целыми числами, так что применение ее к дробным может привести к, мягко говоря, нежелательному результату.



Array


Ассоциативный массив (или, как его часто называют, хэш, хотя для PHP такое понятие совсем не подходит[В.О.17] ). Это набор из нескольких элементов, каждый из которых представляет собой пару вида ключ=>значение (символом[В. О.18]  => я обозначаю соответствие определенному ключу какого-то значения). Доступ к отдельным элементам осуществляется указанием их ключа. В отличие от массивов Си, ключами здесь могут служить не только целые числа, начиная с нуля, но и любые строки. Например, вполне возможно существование таких команд:

// ñîçäàñò ìàññèâ ñ êëþ÷àìè "0", "a", "b" è "c"

$a=array(0=>"zzzz", "a"=>"aaa", "b"=>"bbb", "c"="ccc[В. О.19] ");

echo $a["b"];      // âûâåäåò "bbb"

$a["1"]="qq";      // ñîçäàñò íîâûé ýëåìåíò â ìàññèâå è ïðèñâîèò åìó "qq" $a["a"]="new_aaa"; // ïðèñâîèò ñóùåñòâóþùåìó ýëåìåíòó "new_aaa";

Забегая вперед, скажу, что оператор array() создает массив, элементы которого перечислена в его скобках.


Массив, в общем случае ассоциативный (см. ниже). То есть набор пар ключ=>значение. Впрочем, здесь может быть передан и список list.



Ассоциативные массивы


Возможно, вы уже догадались, что ассоциативные массивы — один из самых мощных инструментов в PHP. Массивы — нечто, что довольно часто реализовывается в интерпретаторах типа PHP (в Perl ассоциативные массивы устроены даже немного хуже, чем в PHP). Давайте рассмотрим чуть подробнее, как с ними работать.

Массивы — это своеобразные контейнеры-переменные для хранения сразу нескольких величин, к которым можно затем быстро и удобно обратиться. Конечно, никто не запрещает вам вообще их не использовать, а, например, давать своеобразные имена переменным, такие как $a1, $a2 и т. д., но представьте, что получится в этом случае, если вам нужно держать в памяти, скажем, тысячу таких переменных. Кроме того, такой способ организации массивов имеет и еще один недостаток — очень трудно перебрать все его значения в цикле, хотя это и возможно:

for($i=0; ; $i++) {

  $v="a$i";

  if(!isset($$v)) break;

..äåëàåì ÷òî-íèáóäü ñ $$v

}

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

Здесь мы используем возможность PHP по работе с ссылочными переменными, которую я категорически не рекомендую где-либо применять. Все это представлено здесь для того, чтобы проиллюстрировать, насколько неудобно бывает работать без массивов.

Давайте теперь начнем с самого начала. Пусть у нас в программе нужно описать список из нескольких человеческих имен. Можно сделать это так (листинг 10.1):

Листинг 10.1. Инициализация массива

$NamesList[0]="Dmitry";

$NamesList[1]="Helen";

$NamesList[2]="Sergey";

. . .

Таким образом, мы по одному добавляем в массив $NamesList

элементы, например, пронумерованные от 0. PHP узнает, что мы хотим создать массив, по квадратным скобкам (нужно заметить, что для этого переменная $NamesList


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

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

Давайте теперь посмотрим, как можно распечатать наш список. Самый простой способ — воспользоваться циклом for:

echo "À âîò ïåðâûé ýëåìåíò ìàññèâà: ".$NamesList[0]."<hr>";

for($i=0; $i<êîë-âî_ýëåìåíòîâ; $i++)

  echo $NamesList[$i]."<br>";

Количество элементов в массиве легко можно определить, задействуя функцию count() или ее синоним sizeof():

for($i=0; $i<count($NamesList); $i++)

  echo $NamesList[$i]."<br>";


Автоматическая генерация названий


Если пользователь находится на сайте "Книжный магазин" в разделе "Философия" на заинтересовавшей его странице "Современность", то, конечно, в заголовке окна браузера ему бы хотелось видеть что-то вроде "Книжный магазин | Философия | Современность", а не просто "Современность". Мы уже договорились хранить название страницы в блоке Title. Но, конечно, мы бы не хотели записывать в каждой странице название полностью, потому что:

r    в будущем мы можем перенести страницу в другой раздел;

r    мы, возможно, захотим сменить разделитель | на /;

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

Специально для решения такого рода задач в нашем шаблонизаторе предусмотрим механизм, который я далее буду называть автоматической склейкой тел блоков. Вот как он работает. Если при обработке очередного блока шаблонизатор видит, что его тело начинается с подстроки [Клей], он определяет, что текст должен быть "пристыкован" к предыдущему телу одноименного блока, но не должен заменить его. В качестве "строки-клея" выступает значение блока с именем Клей. Это позволяет нам в будущем изменить символ "склейки" лишь в одном месте, чтобы это затронуло весь сайт. В случае, если указана пустая пара квадратных скобок [] (то есть имя блока было опущено), используется тело блока с именем DefaultGlue (см. листинг 30.12), а если и он отсутствует — то |.

Теперь при загрузке страницы /phil/index.html из листинга 30.11, пользователь увидит ее полное название, составленное из блоков Title всех "надкаталогов"

и самого файла страницы. Мы добиваемся этого, определив блок Title следующим образом:

<?Block("Title","[]Философия")?>



Автоматическое подключение библиотекаря


Из листинга 29.2 можно видеть, что пока нам не удалось полностью избавиться от указания абсолютного пути к библиотекам. Вот строка, которая мне не нравится:

include "$DOCUMENT_ROOT/lib/librarian.phl"; // подключаем библиотекарь

Действуя привычным способом, нам придется вставлять ее в каждый сценарий, который планирует использовать библиотекаря. Этих сценариев может быть довольно много, так что если мы вдруг захотим изменить lib на, скажем, ../libraries, то придется править все программы. По закону Мэрфи где-нибудь да ошибетесь — обязательно. А значит, такое решение нам, как дотошным программистам, не подходит. К счастью, существует еще по крайней мере два способа решить проблему с абсолютными путями, и который из них выбрать — зависит от ситуации.

Здесь я хочу оговориться: разумеется, где-то

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



Базовые функции


int strlen(string $st)

Одна из наиболее полезных функций. Возвращает просто длину строки, т. е., сколько символов содержится в $st. Как уже упоминалось, строка может содержать любые символы, в том числе и с нулевым кодом (что запрещено в Си). Функция strlen() будет правильно работать и с такими строками.

int strpos(string $where, string $what, int $fromwhere=0)

Пытается найти в строке $where подстроку (то есть последовательность символов) $what и в случае успеха возвращает позицию (индекс) этой подстроки в строке. Первый символ строки, как и в Си, имеет индекс 0. Необязательный параметр $fromwhere можно задавать, если поиск нужно вести не с начала строки $from, а с какой-то другой позиции. В этом случае следует эту позицию передать в $fromwhere. Если подстроку найти не удалось,

функция возвращает false. Однако будьте внимательны, проверяя результат вызова strpos() на false — используйте ля этого только оператор ===.

string substr(string $str, int $from [,int $length])

Данная функция тоже востребуется очень часто. Ее назначение — возвращать участок строки $str, начиная с позиции $start и длиной $length. Если $length не задана, то подразумевается подстрока от $start до конца строки $str. Если $start больше, чем длина строки, или же значение $length равно нулю, то возвращается пустая подстрока.

Однако эта функция может делать и еще довольно полезные вещи. К примеру, если мы передадим в $start отрицательное число, то будет считаться, что это число является индексом подстроки, но только отсчитываемым от конца $str (например, -1 означает "начиная с последнего символа строки"). Параметр $length, если он задан, тоже может быть отрицательным. В этом случае последним символом возвращенной подстроки будет символ из $str с индексом $length, определяемым от конца строки.

int strcmp(string $str1, string $str2)

Сравнивает две строки посимвольно (точнее, побайтово) и возвращает: 0, если строки полностью совпадают; -1, если строка $str1 лексикографически меньше $str2; и 1, если, наоборот, $str1 "больше" $str2. Так как сравнение идет побайтово, то регистр символов влияет на результаты сравнений.[E52] 

int strcasecmp(string $str1, string $str2)

То же самое, что и strcmp(), только при работе не учитывается регистр букв. Например, с точки зрения этой функции "ab" и "AB" равны.

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



Безымянные временные файлы


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

int tmpfile()

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

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

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



Библиотекарь


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

Теперь немного о том, как мы будем реализовывать Uses(). Это довольно несложно. Помните, я подчеркивал, что поскольку PHP является интерпретатором, то на нем осуществимы такие приемы, как описание функций внутри функций и многое другое. Так мы и сделаем: функция Uses() вначале будет проверять, не загружался ли уже модуль с таким именем, затем искать затребованный модуль в специальных "каталогах для модулей", фиксировать во внутреннем массиве факт, что указанный файл загружен, и, наконец,

вызывать include_once для файла с модулем. Кроме того, на время загрузки текущий каталог будет сменяться на тот, в котором находится модуль, чтобы стартовые части всех модулей запускались в "своих" каталогах. Это как раз та возможность, которая отсутствует в Perl, и которая оказывается довольно удобной на практике.

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

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

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

r    Библиотекарь, как никто другой, должен приложить максимум усилий, чтобы сделать сценарии переносимыми с одной платформы на другую. Для нас это будет заключаться в корректировке некоторых переменных, которые PHP создает перед выполнением программы. Первым кандидатом на такую правку будет $SCRIPT_NAME (а также одноименная переменная окружения), которая, как мы знаем, в Windows-версии PHP содержит не то значение, которое мы ожидаем.

r    И еще нам хочется, чтобы на момент загрузки модуля текущий каталог сменялся на тот, в котором расположен файл модуля. Таким образом, стартовая часть библиотеки всегда сможет определить, где она находится, — например, при помощи вызова getcwd().


Вот что у нас получится в результате:

Листинг 29.1. Библиотекарь: librarian.phl

<?if(!defined("LIBRARIAN_LOADED")) {

define("LIBRARIAN_LOADED",1);

// Расширение библиотечных файлов по умолчанию

define("LibExt","phl");

// Пути поиска библиотек. Если начинаются с точки, то поиск

// ВСЕГДА ведется относительно текущего каталога, даже если его

// сменят, в противном случае при следующем вызова Uses() будет

// выполнен перевод пути в абсолютный.

$INC=array(".","./lib");

// Функция преобразует указанный относительный путь в абсолютный.

// Если путь уже является абсолютным (т. е. отсчитывается от корневого

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

// используется имя текущего каталога (или заданного в $cur) с

// необходимыми преобразованиями. Существование файла с полученным полным

// именем не проверяется. Функция лишена некоторых недостатков

// встроенной в PHP realpath() и имеет по сравнению с ней несколько

// большие возможности, работая, правда, чуть медленнее.

function GetAbsPath($name,$cur="") { return abs_path($name,$cur); }

function abs_path($name,$cur="")

{  // Преобразуем обратные слэши в прямые

   $name=strtr(trim($name),"\\","/");

   // Сначала разбиваем путь по знакам "/"

   $Parts=explode("/",$name);

   $Path=($cur===""?getcwd():$cur); // начальный каталог поиска

   foreach($Parts as $i=>$s) if($s!=".") {     

     // Признак корневого каталога?

     if(!$i && (strlen($s)>1&&$s[1]==":"||$s=="")) $Path=$s;

     // Ссылка на родительский каталог?

     elseif($s=="..") {

       // Если это уже корневой каталог, то куда спускаться?..

       if(strlen($Path)>1 && $Path[1]==":") continue;

       // Иначе используем dirname()

       $p=dirname($Path);

       if($p=="/"||$p=="\\"||$p==".") $Path=""; else $Path=$p;



     }

     // Иначе просто имя очередного каталога

     elseif($s!=="") $Path.="/$s";

   }

   return ($Path!==""?$Path:"/");

}

// Преобразует URL в абсолютный файловый путь.

// Т. е. если адрес начинается со слэша, то результат рассматривается

// по отношению к каталогу DOCUMENT_ROOT, а если нет — то относительно

// dirname($SCRIPT_NAME). Конечно, функция не безупречна (например, она

// не умеет обрабатывать URL, заданные Alias-директивами Apache, но в

// большинстве случаев это и не нужно.

function Url2Path($name)

{  $curUrl=dirname($GLOBALS["SCRIPT_NAME"]);

   $url=abs_path(trim($name),$curUrl);

   return getenv("DOCUMENT_ROOT").$url;

}

// Превращает все пути в списке $INC в абсолютные, однако делает это

// не каждый раз, а только если массив изменился с момента последнего

// вызова.

function AbsolutizeINC()

{  global $INC;

   static $PrevINC="";   // значение $INC при предыдущем входе

   // Сначала проверяем — изменился ли $INC. Если да, то преобразуем

   // все пути в массиве в относительные, иначе ничего не делаем.

   // Нам это нужно только из соображений повышения производительности

   // функции.

   if($PrevINC!==$INC) {

     // Мы не можем использовать foreach, т. к. нам надо

     // модифицировать массив

     for($i=0; $i<count($INC); $i++) {

       $v=&$INC[$i];

       if($v[0]=="." && (strlen($v)==1 || $v[1]=='\\' || $v[1]=='/'))

         continue;

       $v=abs_path($v);

     }

     // Запоминаем текущее состояние массива

     $PrevINC=$INC;

   }

  }

// Загружает указанную библиотеку функций. Для поиска файла

// просматривает каталоги в массиве $INC.

function Uses($libname)

{  global $INC;

   static $PrevINC="";   // значение $INC при предыдущем входе

   static $LastFound=0;  // для ускорения работы

   // Переводим все пути в $INC в относительные — вдруг вызывающая

   // программа добавила что-нибудь в массив?..



   AbsolutizeINC();

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

   // найдена какая-нибудь предыдущая загруженная библиотека. Скорее

   // всего, там окажется загружаемый сейчас модуль. Если нет —

   // что же, просмотрим весь список...

   $l=$LastFound;

   do {

     // В очередном каталоге есть файл модуля?..

     $dir=$INC[$LastFound];

     if(@is_file($file="$dir/$libname.".LibExt)) {

       // Сменить каталог на тот, в котором расположен модуль

       $cwd=getcwd();

       chdir(dirname($file));

       // Делаем доступными для модуля все глобальные переменные

       foreach($GLOBALS as $k=>$v) global $$k;

       // Включаем файл

       $ret=include_once($file);

       // Пока не вернулись в предыдущий каталог, перевести

       // добавленные (возможно?) пути в $INC в абсолютные

       AbsolutizeINC();

       // Вернуться

       chdir($cwd);

       return $ret;

     }

     $LastFound=($LastFound+1)%count($INC);

   } while($LastFound!=$l);

   // Ничего не вышло — "умираем"...

   die("Couldn't find library \"$libname\" at ".join(", ",$INC)."!");

}

// Корректируем некоторые переменные окружения, которые могут иметь

// неверные значение, если PHP установлен не как модуль Apache

@putenv("SCRIPT_NAME=".

  $GLOBALS["HTTP_ENV_VARS"]["SCRIPT_NAME"]=

  $GLOBALS["SCRIPT_NAME"]=

  ereg_Replace("\\?.*","",getenv("REQUEST_URI"))

);

@putenv("SCRIPT_FILENAME".

  $GLOBALS["HTTP_ENV_VARS"]["SCRIPT_FILENAME"]=

  $GLOBALS["SCRIPT_FILENAME"]=

  Url2Path(getenv("SCRIPT_NAME"))

);

// На всякий случай включаем максимальный контроль ошибок

Error_reporting(1+2+4+8);

// ВНИМАНИЕ! После следующего закрывающего тэга

// не должно быть НИКАКИХ ПРОБЕЛОВ! В противном случае

// сценарий, подключающий библиотекаря, будет выводить в самом

// начале своей работы этот пробел, что недопустимо при



// работе с Cookies.

}?>

Обратите внимание на то, что весь код библиотекаря помещен в блок оператора if. Это сделано специально, чтобы при возможной (ошибочной) повторной загрузке библиотекаря по include все работало корректно.



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

Пожалуй, в приведенном коде есть и еще одно интересное место. Я имею в виду инструкции, помеченные комментарием: "Делаем доступными для модуля все глобальные переменные". Зачем это нужно? Разве глобальные переменные по определению не доступны подключаемому модулю? К сожалению, это так, и вот почему. Мы вызываем include_once в теле функции Uses(), а не в глобальном контексте. Неудивительно, что подключенный файл работает не в нем, а в области видимости тела функции. Указанный цикл перебора всех глобальных переменных и их "глобализация"

с помощью global решает проблему.



Здесь есть еще одна тонкость. Если модуль "захочет" определить какую-либо новую глобальную переменную, он не сможет сделать это никак иначе, чем через массив $GLOBALS. Однако изменять имеющиеся переменные напрямую он все же способен.


Бинарные данные


Бинарные данные — это почти то же самое, что и данные в формате TEXT, но только при поиске в них учитывается регистр символов ("abc"

и "ABC" — разные строки). Вего имеется  4 типа бинарных данных (табл. 26.4).

Таблица 26.4. Типы бинарных данных, используемые в MySQL

Тип

Описание

TINYBLOB

Может хранить максимум 255 символов

BLOB

Может хранить не более 65 535 символов

MEDIUMBLOB

Может хранить максимум 16 777 215 символов

LONGBLOB

Может хранить 4 294 967 295 символов

BLOB-данные не перекодируются автоматически, если при работе с установленным соединением [E134] включена возможность перекодирования текста "на лету"

(см. ниже).



Битовые операции


Эти операции предназначены для работы (установки/снятия/проверки) групп битов в целой переменной. Биты целого числа— это не что иное[В. О.36] , как отдельные разряды того же самого числа, записанного в двоичной системе счисления. Например, в двоичной системе число 12 будет выглядеть как 1100, а 2 — как 10, так что выражение 12|2 вернет нам число 14 (1110 в двоичной записи). Если переменная не целая, то она вначале округляется, а уж затем к ней применяются перечисленные ниже операторы.

r    a & b  — результат — число, у которого установлены только те биты, которые установлены и у a, и у b одновременно.

r    a | b  — результат — число, у которого установлены только те биты, которые установлены либо в a, либо в b (либо одновременно).

r    ~ a    — результат, у которого на месте единиц в a

стоят нули, и наоборот.

r    a << b — результат — число, полученное поразрядным сдвигом a на b битов влево.

r    a >> b — аналогично, только вправо.



Блочные чтение/запись


string fread(int $f, int $numbytes)

Читает из файла $f $numbytes символов и возвращает строку этих символов. После чтения указатель файла продвигается к следующим после прочитанного блока позициям (это происходит и для всех остальных функций, так что дальше я буду пропускать такие подробности). Разумеется, если $numbytes больше, чем можно прочитать из файла (например, раньше достигается конец файла), возвращается то, что удалось считать. Этот прием можно использовать, если вам нужно считать в строку файл целиком. Для этого просто задайте в $numbytes очень большое число (например, сто тысяч). Но если вы заботитесь об экономии памяти в системе, так поступать не рекомендуется. Дело в том, что в некоторых версиях PHP передача большой длины строки во втором параметре fread() вызывает первоначальное выделение этой памяти в соответствии с запросом (даже если строка гораздо короче). Конечно, потом лишняя память освобождается, но все же ее может и не хватить для начального выделения.

int fwrite(int $f, string $st)

Записывает в файл $f все содержимое строки $st. Эта функция составляет пару для fread(), действуя "в обратном направлении".

Например, с помощью описанных двух функций можно копировать файлы (правда, в PHP есть для этого отдельная функция — copy()), считав файл целиком посредством fread() и затем записав в новое место при помощи fwrite().

[E66] При работе с текстовыми файлами (то есть когда указан символ t

в режиме открытия файла) все \n автоматически преобразуются в тот разделитель строк, который принят в вашей операционной системе.



Блокирование файла


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

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

Здесь на помощь приходит функция flock(), которая устанавливает так называемую "рекомендательную блокировку" для файла. Это означает, что блокирование доступа осуществляется не на уровне ядра системы, а на уровне программы. Поясню на примере.

Однажды я прочитал одно замечательное сравнение рекомендательной блокировки с перекрестком, на котором установилось довольно оживленное движение, регулируемое светофором. Когда горит красный, одни машины стоят, а другие проезжают. В принципе, любая машина может, так сказать, проехать наперекор правилам дорожного движения, не дожидаясь зеленого сигнала, но в таком случае возможны аварии. Рекомендательная блокировка работает точно таким же образом. А именно, процессы, которые ей пользуются, будут работать с разделяемым файлом правильно, а остальные… как-нибудь да будут, пока не произойдет "столкновение".

С другой стороны, "жесткая блокировка"

(которая в PHP не поддерживается) подобна шлагбауму: никто не сможет проехать, пока его не поднимут.

О жесткой блокировке мы в этой книге говорить не будем.

Единственная функция, которая занимается управлением блокировками в PHP, называется flock().

bool flock(int $f, int $operation [, int& $wouldblock])

Функция устанавливает для указанного открытого дескриптора файла $f режим блокировки, который бы хотел получить текущий процесс. Этот режим задается аргументом $operation и может быть одной из следующих констант:


r    LOCK_SH (или 1) — разделяемая блокировка;

r    LOCK_EX (или 2) — исключительная блокировка;

r    LOCK_UN (или 3) — снять блокировку;

r    LOCK_NB (или 4) — эту константу нужно прибавить к одной из предыдущих, если вы не хотите, чтобы программа "подвисала" на flock() в ожидании своей очереди, а сразу возвращала управление.

В случае, если был затребован режим без ожидания, и блокировка не была успешно установлена, в необязательный параметр-переменную $wouldblock будет записано значение истина true.

В случае ошибки функция, как всегда, возвращает false, а в случае успешного завершения — true.



Хотя в документации PHP и сказано, что flock() работает во всех операционных системах (в том числе и под Windows), мои тесты показали, что как раз для Windows это не так. А именно, в этой системе функция всегда возвращает индикатор провала, независимо от того, правильно она вызывается, или нет. Возможно, в будущих версиях PHP это досадное недоразумение будет исправлено.


Блокировки с запретом "подвисания"


Как следует из описания функции flock(), к ее второму параметру можно прибавить константу LOCK_NB для того, чтобы функция не ожидала, когда программа может "двинуться в путь", а сразу же возвращала управление в основную программу. Это может пригодиться, если вы не хотите, чтобы ваш сценарий бесполезно простаивал, ожидая, пока ему разрешат обратиться к файлу. В эти моменты, возможно, лучше будет заняться какой-нибудь полезной работой — например, почистить временные файлы, память, или же просто сообщить пользователю, что файл заблокирован, чтобы он подождал и не думал, что программа зависла. Вот пример использования исключительной блокировки в совокупности с LOCK_NB:

$f=fopen("file.txt","a+");

while(!flock($f,LOCK_EX+LOCK_NB)) {

  echo "Пытаемся получить доступ к файлу <br>";

  sleep(1); // ждем 1 секунду

}

// Ðàáîòàåì

Эта программа основывается на том факте, что выход из flock() может произойти либо в результате отказа блокировки, либо после того, как блокировка будет установлена — но не до того! Таким образом, когда мы наконец-то дождемся разрешения доступа к файлу и произойдет выход из цикла while, мы уже будем иметь исключительную блокировку, закрепленную за нашим файлом.



Bool


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



Целые числа


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

префиксINT [UNSIGNED]

Необязательный флаг UNSIGNED задает, что будет создано поле для хранения беззнаковых чисел (больших или равных 0).

Имена типов, в вобщем виде обозначенные здесь как префиксINT, приводятся в табл. 26.1.

Таблица 26.1. Типы целочисленных данных MySQL.

Тип

Описание

TINYINT

Может хранить числа от –128 до +127

SMALLINT

Диапазон от –32768 до 32 767

MEDIUMINT

Диапазон от –8 388 608 до 8 388 607

INT

Диапазон от –2 147 483 648 до 2 147 483 647

BIGINT

Диапазон от –9 223 372 036 854 775 808 до 9 223 372 036 854 775 807



CGI изнутри


До сих пор мы рассматривали лишь теоретические аспекты CGI. Мы знаем в общих чертах, как и что передается пользователю сервером и наоборот. Однако как же все-таки должна быть устроена CGI-программа (CGI-сценарий), чтобы работать с этой информацией? Откуда она ее вообще получает и куда должна выводить, чтобы переслать текст пользователю?

И это только небольшая часть вопросов, которые пока остаются открытыми. В этой главе я постараюсь вкратце описать, как же должны на самом деле быть устроены внутри CGI-сценарии. На мой взгляд, каждый программист обязан хотя бы в общих чертах знать, как работает то, что он использует — будь то операционная система (ОС) или удобный язык-интерпретатор для написания CGI-сценариев (каким является PHP). А значит, речь пойдет о программировании на Си. Я выбрал Си, т. к. это одно из самых лучших и лаконичных средств; кроме того, именно на Си чаще всего пишут те сценарии, которым требуется максимально критичное быстродействие (базы данных, поисковые системы, системы почтовой рассылки с сотнями тысяч пользователей и др.). В пользу этого языка говорит также и то, что его компиляторы можно встретить практически в любой сколько-нибудь серьезной ОС.

Тем не менее, вы не найдете в этой главе ни одной серьезной законченной программы на Си (за исключением разве что самой простой, типа "Hello, world!"). Несмотря на это, я попытаюсь описать практически все, что может понадобиться при программировании сценариев на Си (кроме работы с сокетами, — это тема для отдельной книги, да и, пожалуй, лишь косвенно примыкает к Web-программированию). По возможности я не буду привязываться к специфике конкретной ОС, ведь для CGI существует стандарт, независимый от операционной системы, на которой будет выполняться сценарий. Вооружившись материалом этой главы, можно написать самые разно­образные сценарии — от простых до самых сложных (правда, для последних потребуется также недюжинная сноровка).

И все-таки, моя цель — набросать общими мазками, как неудобно (повто­рюсь — именно неудобно!) программировать сценарии на языках, обычных для прикладного программиста (в том числе на Си и Си++). Как только вы проникнетесь этой идеей, мы плавно и не торопясь двинемся в мир PHP, где предусмотрены практически все удобства, так необходимые серьезному языку программирования сценариев.


Если вы не знакомы с языком Си, не отчаивайтесь. Все примеры хорошо комментированы, а сложные участки не нуждаются в непременном понимании "с первого прочтения". Еще раз оговорюсь, что материал этой и следующей глав предназначен для того, чтобы вы получили приблизительное представление о том, как же устроен протокол HTTP и как программы взаимодействуют с его помощью. Думаю, что без этих знаний невозможна никакая профессиональная работа на поприще Web-программирования. Так что не особенно расстраивайтесь, если вы совсем не знаете Си — ведь эта глава содержит гораздо больше, нежели просто описание набора Си-функций. В ней представлен материал, являющийся связующим звеном между CGI и HTML, детально описываются тэги форм и их наиболее полезные атрибуты, приемы создания запросов и многое другое. Все это, безусловно, понадобится нам и при программировании на PHP.

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


Чего хочет программист от своей профессии


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

Идем дальше. Может быть, ему нужна известность? Конечно, этот фактор не является третьестепенным, учитывая то, что известность — гарантия, что программист всегда легко сможет найти работу. Однако, как и деньги, слава и известность также не бывают самими по себе — их необходимо заслужить. И, к тому же, много ли вы знаете известных имен программистов, действительно заслуживших свое признание практикой (Билл Гейтс не в счет, потому что он уже давно этим не занимается)? Правильно — ни одного. Разве что, может быть, кто-нибудь вспомнит доблестных создателей игры Doom, ставшей уже историей.

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



Чтение CSV-файла


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

list fgetcsv(int $f, int $length, char $delim=’,’)

Функция читает одну строку из файла, заданного дескриптором $f, и разбивает ее по символу $delim. Параметр $delim должен обязательно быть строкой из одного символа, в противном случае принимается во внимание только первый символ этой строки. Функция возвращает получившийся список или false, если строки кончились. Параметр $length

задает максимальную длину строки точно так же, как это делается в fgets(). Пустые строки в файле не игнорируются, а возвращаются как список из одного элемента — пустой строки.

Функция fgetcsv() работает чуть быстрее пары fgets()/explode(), но зато она, как мы можем видеть, гораздо менее универсальна. Применяйте ее в таком контексте:

$f=fopen("file.csv","r") or die("Ошибка!");

for($i=0; $data=fgetcsv($f, 1000, ";"); $i++) {

  $num = count($data);

  if($num==1 && $data[0]==="") continue;

  echo "<h3>Строка номер $i ($num полей):</h3>";

  for($c=0; $c<$num; $c++)

    print "[$c]: $data[$c]<br>";

}

fclose($f);



Чтение и запись


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

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



Что такое CGI?


Итак, мы набираем в нашем браузере

http://www.somehost.com:80/path/to/document.ext

Мы ожидаем, что сейчас получим HTML-документ (или документ другого формата — например, рисунок). Иными словами, мы рассчитываем, что на хосте в каталоге /path/to/ расположен файл document.ext, который нам сейчас и доставят (передаст его, кстати, Web-сервер, подключенный к порту 80 на сервере).

Однако на самом деле ситуация несколько иная. По двум причинам.

r    Путь /path/to/, ровно как и файл document.ext на хосте может вообще не существовать. Ведь администратор сервера имеет возможность задать псевдоним

(alias) для любого объекта на сервере. Кроме того, даже если и не назначено никакого псевдонима, все равно имеется возможность так написать программы для Web-сервера, что они будут "перехватывать" каждое обращение к таким путям и соответствующим образом реагировать на это (пример рассмотрен в главе 1 ).

r    Файл document.ext может быть вовсе не текстовым документом, а программой, которая в ответ на наш запрос молниеносно запустится, не менее стремительно выполнится и возвратит пользователю результаты своей работы, хотя бы в том же HTML-формате (или, разумеется, в любом другом, — например, это может быть изображение). Пользователь и не догадается, что на самом деле произошло. Для него все равно, загружает ли он документ или невольно запускает программу. Более того, он никак не сможет узнать, что же на самом деле случилось.

Последний пункт особенно впечатляющ. Если вы прониклись его идеей, значит, вы поняли в общих чертах, что такое CGI. Как раз CGI обеспечивает все то, что выглядит так прозрачно для пользователя. Традиционно программы, работающие в соответствии с соглашениями CGI, называют сценариями — скорее всего из-за того, что в большинстве случаев их пишут на языках-интерпретаторах, подобных Basic (например, на Perl или PHP).

Задумаемся на мгновенье. Мы получили довольно мощный механизм, который позволяет нам, в частности, формировать документы "на лету". К примеру, пусть нам нужно, чтобы в каком-то документе проставлялись текущая дата и время. Разумеется, мы не можем заранее прописать их в документе — ведь в зависимости от того, когда он будет загружен пользователем, эта дата должна меняться. Зато мы можем написать сценарий, который вычислит дату, вставит ее в документ и затем передаст его пользователю, который даже ничего и не заметит!

Однако в построенной нами модели не хватает одного звена. Действительно, предположим, нам нужно, чтобы время в нашей странице проставлялось на основе часового пояса пользователя. Но как сценарий узнает, какой часовой пояс у региона, в котором живет этот человек (или какую-нибудь другую информацию, которую может предоставить пользователь)? Видимо, должен быть какой-то механизм, который позволит пользователю не только получать, но также и передавать информацию серверу (в данном случае, например, поправку времени в часах относительно Москвы). И это тоже обеспечивает CGI. Но вернемся прежде снова к URL.



Что такое формы и для чего они нужны


Итак, мы знаем, что наиболее распространенными методами передачи данных между браузером и сценарием являются GET и POST. Однако вручную задавать строки параметров для сценариев и к тому же URL-кодировать их, согласитесь, довольно утомительно. Давайте посмотрим, что же язык HTML предлагает нам для облегчения жизни.

Сначала рассмотрим метод GET. Даже программисту довольно утомительно набирать параметры в URL вручную. Всякие там ?, &, %... Представьте себе пользователя, которого принуждают это делать. К счастью, существуют удобные возможности языка HTML, которые, конечно, поддерживаются браузерами. И хотя я уже замечал, что в этой книге будет лишь немного рассказано о HTML, о формах

мы поговорим очень подробно.

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

Этот сценарий распознает 2 параметра: name и age. Где эти параметры задаются, мы пока не решили. При переходе по адресу http://www.somehost.com/script.cgi он должен отработать и вывести следующую HTML-страницу:

<html><body>

Ïðèâåò, name! ß çíàþ, Âàì age

ëåò!

</body></html>

Разумеется, при генерации страницы нужно name и age заменить на соответствующие значения, переданные в параметрах.



Что такое шаблонизатор?


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

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

Вспомним, что мы сделали тогда, чтобы убрать зависимости. Мы поменяли местами "поставщика" и "исполнителя". Идея выполнить обратную перестановку кажется абсурдной, т.к. мы опять придем к тому, с чего начали. Конечно, мы не будем так делать. Зададимся отвлеченным вопросом: что предпринимает общество, когда перед ним возникает чересчур большое количество нерешенных и необъяснимых задач? Оно придумывает себе богов. В программировании — то же самое. Раз мы не можем больше сослаться ни на генератор данных, ни на шаблон, значит, настало время реализовать нечто третье, "бога", управляющего всей системой в совокупности и распределяющего обязанности. Вы, наверное, догадались, что я снова имею в виду шаблонизатор.

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

Теперь вы почувствовали, почему я применил здесь аналогию с богом? Ведь бог как раз удовлетворяет тем описаниям, которые даны в предыдущем абзаце!

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



Content-length


r    Формат: Content-length: длина

r    Переменная окружения: CONTENT_LENGTH

Заголовок содержит строку, являющуюся десятичным представлением длины данных в байтах, передаваемых методом POST. Если задействуется метод GET, то этот заголовок отсутствует, и значит, переменная окружения не устанавливается.


Количество байтов данных, присланных пользователем. Эту переменную необходимо анализировать, если вы занимаетесь приемом и обработкой POST-формы.



Content-type


r

Формат: Content-Type: application/x-www-form-urlencoded

r    Переменная: CONTENT_TYPE

Данный заголовок идентифицирует тип передаваемых данных. Обычно для этого указывается значение application/x-www-form-urlencoded, что означает формат, в котором все управляющие символы (отличные от алфавитно-цифровых и других отображаемых) специальным образом кодируются. Это тот самый формат передачи, который используется методами GET и POST. Довольно распространен и другой формат, и называется он multipart/form-data. Мы разберем его, когда будем обсуждать вопрос, касающийся загрузки файлов на сервер.

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


r    Формат: Content-type: mime_тип; charset=koi8-r

Задает тип документа и его кодировку. Параметр charset задает кодировку документа (в нашем примере это KOI8-R). Поле mime_тип определяет тип информации, которую содержит документ:

r    text/html — HTML-документ;

r    text/plain — простой текстовый файл;

r    image/gif — GIF-изображение;

r    image/jpeg — JPG-изображение;

r    еще несколько десятков других типов.



Cookie


r Формат: Cookie: значения_Cookies

r    Переменная окружения: HTTP_COOKIE

Здесь хранятся все Cookies в URL-кодировке (о Cookies мы подробнее поговорим в следующей главе).



Дата и время


MySQL поддерживает несколько типов полей, специально приспособленных для хранения дат и времени в различных форматах (табл. 26.5).

Таблица 26.5. Представление дат и времени в базах данных MySQL

Тип

Описание

DATE

Дата в формате ГГГГ-ММ-ДД

TIME

Время в формате ЧЧ:ММ:СС

DATETIME

Дата и время в формате ГГГГ-ММ-ДД ЧЧ:ММ:СС

TIMESTAMP

Время и дата в формате timestamp. Однако при получении значения поля оно отображается не в формате timestamp, а в виде ГГГГММДДЧЧММСС, что сильно умаляет преимущества его использования в PHP

Надо заметить, что в PHP будет проще самостоятельно генерировать дату и время при вставке данных в таблицу, а не задействовать встроенные в MySQL типы. Например, привлекательный с виду тип TIMESTAMP на деле оказывается довольно неудобным, потому что отображается не в том виде, который мы ожидаем.



Date


Формат: Date: Sat, 08 Jan 2000 11:56:26 GMT

Указывает браузеру дату отправки документа.



Действия с переменными


Вне зависимости от типа переменной, с ней можно делать три основных действия.



Деструктор


По аналогии с конструкторами обычно рассматриваются деструкторы. Деструктор— тоже специальный метод объекта, который вызывается при уничтожении этого объекта (например, после завершения программы). Деструкторы обычно выполняют служебную работу — закрывают файлы, запи­сывают протоколы работы, разрывают соединения, "форматируют винчестер" — в общем, освобождают ресурсы. К сожалению, из-за "щедрости" PHP на выделение памяти, которая никогда не будет освобождена, деструкторы в нем не поддерживаются. Так что, если вам нужно выполнить нечто необычное после того, как вы перестали использовать какой-то объект, определите в нем метод, который будет это делать, и вызовите его явно.



Диаграммы двухуровневой и трехуровневой моделей


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

Рис. 30.1.

Двухуровневая схема

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

Легко заметить, что рис. 30.2 гораздо сложнее, чем рис. 30.1. Его "загружен­ность"

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

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

Рис. 30.2.

Трехуровневая схема



Динамическая смена кодировки


Приведенное в предыдущем примере письмо можно будет прочитать в 90% существующих почтовых программ. Для "удовлетворения"

остальных желательно посылать письма не в Windows-кодировке, а в KOI-8R. Для перекодирования можно воспользоваться уже известной нам функцией convert_cyr_string(). И, конечно, нужно в Content-type заменить charset на koi8-r. Вот что у нас получится:

$message=

"From: Лист ðàññûëêè

To: Èâàíîâ Èâàí Èâàíîâè÷ <ivanov@ivan.ivanovich.ru>

Subject: Ïðîáíàÿ ðàññûëêà

Content-type: text/plain; charset=koi8-r

Óâàæàåìûé òîâàðèù! Ýòî ïèñüìî ïîñëàíî ïî÷òîâûì ðîáîòîì.

Âñåãî õîðîøåãî!";

$message=convert_cyr_string($message,"w","k");

Mail("ivanov@ivan.ivanovich.ru","",$message);

Теперь вы понимаете, почему я говорил о том, чтобы все заголовки и Subject находились внутри тела письма? Этим мы достигаем того, что одной командой convert_cyr_string() перекодируется сразу все письмо, включая поля From, To, Subject и др. Иначе пришлось бы применять эту функцию дополнительно для перекодировки параметров $subject и $headers...



Domain


Параметр domain=имя_хоста задает имя хоста, с которого установили Cookie. Ранее я уже говорил про этот параметр. Так вот, оказывается, его можно менять вручную, прописав здесь нужный адрес, и таким образом "подарить" Cookie другому хосту. Только в том случае, если параметр не задан, имя хоста определяется браузером автоматически.



Доменное имя


И все-таки обычным людям довольно неудобно работать с IP-представлением адреса. Действительно, куда как проще запомнить символьное имя, чем набор чисел. Чтобы облегчить простым пользователям работу с Интернетом, придумали систему DNS

(Domain Name System

— Система имен доменов).

Общемировая DNS представляет собой распределенную базу данных, способную преобразовать доменные имена машин в их IP-адреса. Это не так-то просто, учитывая, что скоро Интернет будет насчитывать десятки миллионов компьютеров. Поэтому мы не будем в деталях рассматривать то, как работает служба DNS, а займемся больше практической стороной вопроса.

Итак, при использовании DNS любой компьютер в Сети может иметь не только IP-адрес, но также и символическое имя. Выглядит оно примерно так:

www.somehost.msu.su

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

(напри­мер, su— домен первого уровня, msu.su — второго, somehost.msu.su — третьего и т. д.)

Вообще говоря, полное DNS-имя выглядит немного не так: в его конце обязательно стоит точка, например:

www.somehost.msu.su.

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

Интересно, и почему так популярна в компьютерной технике точка? В именах файлов — точка. В IP- и DNS-адресе — точка. Практически во всех языках программирования для доступа к объединениям данных — тоже точка. Существуют и другие примеры. Похоже, точка прочно въелась в наши умы, и мы уже не представляем, что бы могло ее заменить...

Нужно заметить, что одному и тому же IP-адресу вполне может соответствовать сразу несколько доменных имен. Каждое из них ведет в одно и то же место — к единственному IP-адресу. Благодаря протоколу HTTP 1.1 (мы вскоре кратко рассмотрим его особенности) Web-сервер, установленный на машине и откликающийся на какой-либо запрос, способен узнать, какое доменное имя ввел пользователь, и соответствующим образом среагировать, даже если его IP-адресу соответствует несколько доменных имен. В последнее время HTTP 1.1 применяется практически повсеместно — не то, что несколько лет назад, поэтому все больше и больше серверов используют его в качестве основного протокола для доступа к Web.


Интересен также случай, когда одному и тому же DNS-имени сопоставлены несколько разных IP-адресов. В этом случае служба DNS автоматически выбирает тот из адресов, который, по ее мнению, ближе всего расположен к клиенту, или который давно не использовался, или же наименее загружен (впрочем, последняя оценка может быть весьма и весьма субъективна). Эта возможность часто задействуется, когда Web-сервер становится очень большим (точнее, когда число его клиентов начинает превышать некоторый предел) и его приходится обслуживать сразу нескольким компьютерам. Такая схема используется, например, на сайте компании Netscape.

Как же ведется поиск по DNS-адресу? Для начала он преобразуется специальными DNS-серверами, раскиданными по всему миру, в IP-адрес. Давайте посмотрим, как это происходит. Пусть клиентом выдан запрос на определение IP-адреса машины www.host.ru. (еще раз обратите внимание на завершающую точку! — это не конец предложения).

Чтобы его обработать, первым делом посылается запрос к так называемому корневому домену (точнее, к программе — DNS-серверу, запущенному на этом домене), который имеет имя "." (на самом деле его база данных распределена по нескольким компьютерам, но для нас это сейчас несущественно). Запрос содержит команду: вернуть IP-адрес машины (точнее, IP-адрес DNS-сервера), на котором расположена информация о домене ru. Как только IP-адрес получен, по нему происходит аналогичное обращение с просьбой — определить адрес, соответствующий домену host внутри домена ru внутри корневого домена ".".

В конце у предпоследней машины запрашивается IP-адрес поддомена www в домене somehost.ru.

Важно, что каждый домен "знает"

все о своих поддоменах, а те, в свою очередь — о своих, т. е. система имеет некоторую иерархичность. Корневой домен, как мы уже заметили, принято называть доменом нулевого уровня, домен ru. (в нашем примере) — первого, host.ru. — второго уровня, ну и т. д. При изменении доменов некоторого уровня об этом должны узнать все домены, родительские по отношению к нему, для чего существуют специальные протоколы синхронизации. Нам сейчас нет нужды вникать, как они действуют — скажу только, что распространяются сведения об изменениях не сразу, а постепенно, спустя некоторое время, задаваемое администратором DNS-сервера, и рассылкой также занимаются DNS-серверы.



Просто? Не совсем. Представьте, какое бы произошло столпотворение на корневом домене ".", если бы все запросы на получение IP-адреса проходили через него. Чтобы этого избежать, практически все машины в Сети кэшируют информацию о DNS-запросах, обращаясь к корневому домену (и доменам первого уровня — ru, com и т. д.) лишь изредка для обновления этого кэша. Например, пусть пользователь, подключенный через модем к провайдеру, впервые соединяется с машиной www.host.ru. В этом случае будет передан запрос корневому домену, а затем, по цепочке, поддомену host и, наконец, домену www. Если же пользователь вновь обратится к www.host.ru., то сервер провайдера сразу же вернет ему нужный IP-адрес, потому что он сохранил его в своем кэше запросов ранее. Подобная технология позволяет значительно снизить нагрузку на DNS-серверы в Интернете. В то же время у нее имеются и недостатки, главный из которых — вероятность получения ложных данных, например, в случае, если хост host.ru. только что отключился или сменил свой IP-адрес. Так как кэш обновляется сравнительно редко, мы всегда можем столкнуться с такой ситуацией.

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



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


Дополнительные функции


bool eregi(string $expr, string $str [,list &$Matches])

То же, что и ereg(), только без учета регистра символов.

Хотя регистр и не учитывается при поиске, в карманах $Matches все найденные подстроки все же запишутся с точным сохранением регистра букв.

string eregi_replace(string $expr, strint $str, string $strToChange)

То же, что и ereg_replace(), но без учета регистра

áóêâåííûõ ñèìâîëîâ.

int quotemeta(string $str)

Часто бывает нужно гарантировать, чтобы в какой-то переменной-строке ни один символ не мог трактоваться как метасимвол. Этого можно добиться, предварив каждый из них наклонной чертой, что и делает функция quotemeta(). А именно, она "заслэшивает"

следующие символы: . , \\, +, *, ? , [ ^ ]

, ( $ ).

Перед | слэш почему-то не ставится. Будьте особо внимательны!

list split(string $pattern, string $string [,int $limit])

Эта функция очень похожа на explode().

Она тоже разбивает строку $string на части, но делает это, руководствуясь регулярным выражением $pattern. А именно, те участки строки, которые совпадают с этим выражением, и будут служить разделителями. Параметр $limit, если он задан, имеет то же самое значение, что и в функции explode() — а именно, возвращается список из не более чем $limit элементов, последний из которых содержит участок строки от ($limit-1)-го совпадения до конца строки.

Наверное, вы уже догадались, что функция split()

работает гораздо медленнее, чем explode(). Однако она, вместе с тем, имеет впечатляющие возможности, в чем мы очень скоро убедимся. Тем не менее, не стоит применять split()

там, где прекрасно подойдет explode(). Чаще всего этим грешат программисты, имеющие некоторый опыт работы с Perl, потому что в Perl для разбиения строки на составляющие есть только функция split().

list spliti(string $pattern, string $string [,int $limit])

Аналог функции split(), который делает то же самое, только при сопоставлении с регулярным выражением не учитывается регистр символов.



Доступ объекта к своим свойствам


Как ни странно, но при изучении ООП "с нуля" программисты, привыкшие к структурному программированию, часто с трудом понимают, каким образом объект может добраться до своих собственных свойств. Рассмотрим, например, такую программу:

$Obj1=new Mysqltable;

$Obj2=new MysqlTable;

. . .

echo $Obj1->TableName, " ", $Obj2->TableName;

Здесь никаких проблем не возникает— ясно, что выводятся свойства разных объектов — мы же сами указали их до стрелки. Однако давайте посмотрим, что будет, если вызвать какой-нибудь метод одного из объектов:

$Obj1->Drop();

Как видите, при вызове метода так же, как и при доступе к свойству, нужно указать объект, который должен "откликнуться на запрос". Действительно, этой командой мы удаляем из базы данных таблицу $Obj1, а не $Obj2. Рассмотрим теперь тело метода Drop():

class MysqlTable {

 function Drop()

 { сюда интерпретатор попадет, когда вызовется Drop() для

 какого-то объекта

 }

}

По логике, Drop() — функция. Эта функция, конечно, едина для всех объектов класса MysqlTable. Но как же метод Drop() узнает, для какого объекта он был вызван? Ведь мы не можем Drop() для $Obj1 сделать одним, а для $Obj2 — другим, иначе нарушился бы весь смысл нашей объектной ориентированности. В том-то вся и соль, что два различных объекта-таблицы являются объектами одного и того же класса...

Оказывается, для доступа к свойствам (и методам, т. к.

один метод вполне может вызывать другой) внутри метода используется специальная предопределенная переменная $this, содержащая тот объект, для которого был вызван метод. Теперь мы можем определить Drop() внутри класса так:

function Drop()

{ // сначала удаляем все записи из таблицы

 $this->Delete("1=1"); // всегда истинное выражение

 // а затем удаляем саму таблицу

 mysql_query("drop table ".$this->TableName);

}

Если мы вызвали Drop() как $Obj1->Drop(), то $this будет являться тем же объектом, что и $Obj1 (это будет ссылка на $Obj1), а если бы мы вызвали $Obj2->Drop()[E151] , то $this был бы равен $Obj2. То есть метод всегда знает, для какого объекта он был вызван. Это настолько важно, что я повторю еще раз: метод всегда знает, для какого объекта он был вызван.

Использование ссылок говорит о том, что $this — не просто копия объекта-хозяина, это и есть

хозяин. Например, если бы в $Obj1->Drop() мы захотели изменить какое-то свойство $this, оно поменялось бы и у $Obj1, но не у $Obj2 или других объектов.

В синтаксисе PHP есть один просчет: запись вида

$ArrayOfObjects["obj"]->DoIt();

считается синтаксически некорректной. Вместо нее применяйте следующие две команды:

$obj=&$ArrayOfObjects["obj"]; $obj->DoIt();

Не забудьте про &

сразу после оператора присваивания (то есть создавайте ссылку на элемент массива), иначе метод DoIt()

будет вызван не для самого объекта, присутствующего в массиве, а для его копии, полученной в $obj!



Доступ по ключу


Как мы уже знаем, ассоциативные массивы— объекты, которые наиболее приспособлены для выборки из них данных путем указания нужного ключа. В PHP è для всех массивов, и для списков (которые, еще раз напомню, также являются массивами) используется один и тот же синтаксис, что является очень большим достоинством. Вот как это выглядит:

echo $Arr["anykey"]; // âûâîäèò ýëåìåíò ìàññèâà $Arr ñ êëþ÷îì anykey

echo $Arr["first"]["second"]; // так используются двумерные массивы

echo (SomeFuncThatReturnsArray())[5]; // ОШИБКА! Так нельзя!

// Вот так правильно:

$Arr= SomeFuncThatReturnsArray();

echo $Arr[5];

Последний пример показывает, что PHP сильно отличается от Си с точки зрения работы с массивами: в нем нет такого понятия, как "контекст массива", а значит, мы не можем применить [] непосредственно к значению, возвращенному функцией.

Величина $Arr[ключ]

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

$Arr["anykey"]=array(100,200); // присваиваем элементу массива 100[E47] 

$ref=&$Arr["first"]["second"]; // $ref — синоним элемента массива

$Arr[]="for add"; // добавляем новый элемент



Double


Вещественное число довольно большой точности (ее должно хватить для подавляющего большинства математических вычислений).



Double, float


Вещественное число, или целое число, или строка, содержащая одно из таких чисел.



Дробные числа


Точно так же, как целые числа подразделяются в MySQL на несколько разновидностей, MySQL поддерживает и несколько типов дробных чисел.

В общем виде они записываются так:

ИмяТипа[(length,decimals)] [UNSIGNED]

Здесь length — количество знакомест (ширина поля), в которых будет размещено дробное число при его передаче в PHP, а decimals — количество знаков после десятичной точки, которые будут учитываться. Как обычно, UNSIGNED задает беззнаковые числа.  Строка ИмяТипа замещается на предопределенные значения, соответствующие возможным вариантам представления вещественных чисел (табл. 26.2).

Таблица 26.2. Типы рациональных чисел в MySQL

Тип

Описание

FLOAT

Число с плавающей точкой небольшой точности

DOUBLE

Число с плавающей точкой двойной точности

REAL

Синоним для DOUBLE

DECIMAL

Дробное число, хранящееся в виде строки

NUMERIC

Синоним для DECIMAL



Другие функции


bool ftruncate(int $f, int $newsize)

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

ftruncate($f,0);  // î÷èñòèòü ñîäåðæèìîå ôàéëà

void fflush(int $f)

Заставляет PHP немедленно записать на диск все изменения, которые производились до этого с открытым файлом $f. Что это за изменения? Дело в том, что для повышения производительности все операции записи в файл буферизируются: например, вызов fputs($f,"Это строка!") не приводит к непосредственной записи данных на диск — сначала они попадают во внутренний буфер (обычно размером 8K). Как только буфер заполняется, его содержимое отправляется на диск, а сам он очищается, и все повторяется вновь. Особенный выигрыш от буферизации чувствуется в сетевых операциях, когда просто глупо отправлять данные маленькими порциями. Конечно, функция fflush()

вызывается неявно и при закрытии файла.

int set_file_buffer(int $f, int $size)

Эта функция устанавливает размер буфера, о котором мы только что говорили, для указанного открытого файла $f. Чаще всего она используется так:

set_file_buffer($f,0);

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

Буферизированный ввод/вывод придуман не зря. Не отключайте его без крайней надобности — это может нанести серьезный ущерб производительности.

В крайнем случае используйте fflush().


void usleep(int $micro_seconds)

Вызов этой функции позволяет сценарию "замереть"

не указанное время

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

Существует также функция sleep(), которая принимает в параметрах не микросекунды, а секунды, на которые нужно задержать выполнение программы.

int uniqid(string $prefix)

Функция uniqid() возвращает строку, при каждом вызове отличающуюся от результата предыдущего вызова. Параметр $prefix задает префикс (до 114 символов длиной) этого идентификатора.

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

Чтобы добиться большей уникальности, можно использовать uniqid()

"в связке"

с функциями mt_rand()

и md5(), описанными в предыдущих главах.




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

bool session_is_registered(string $name)

Функция session_is_registered() возвращает значение true, если переменная с именем $name

была зарегистрирована в сессии, иначе возвращается false.

bool session_unregister(struing $name)

Эта функция отменяет регистрацию для переменной с именем $name

для текущей сессии. Иными словами, при завершении сценария все будет выглядеть так, словно переменная с именем $name

и не была никогда зарегистрирована. Возвращает true, если все прошло успешно, и false

— в противном случае.

После вызова функции session_unregister()

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

void session_unset()

Функция session_unset(), в отличие от session_unregister(), не только отменяет регистрацию переменных (кстати говоря, всех

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

string session_save_path([string $path])

Эта функция возвращает имя каталога, в котором будут помещаться фай­лы— временные хранилища данных сессии. В случае, если указан параметр, как обычно, активное имя каталога будет переустановлено на $path. При этом функция вернет предыдущий каталог.

К сожалению, функции, которая бы возвращала список всех зарегистрированных в сессии переменных, почему-то нет. Во всяком случае, в PHP версии 4.0.3.



Дуга сектора


int imageArc(int $im,int $cx,int $cy,int $w,int $h,int $s,int $e,int $c)

Функция imageArc() рисует в изображении $im дугу сектора эллипса от угла $s до $e (углы указываются в градусах против часовой стрелки, отсчитываемых от горизонтали). Эллипс рисуется такого размера, чтобы вписываться в прямоугольник ($x,$y,$w,$h), где $w и $h задают его ширину и высоту, а $x и $y— координаты левого верхнего угла. Сама фигура не закрашивается, обводится только ее контур, для чего используется цвет $c.



Двухуровневая схема


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

построения сценария). Это довольно несложно. Мы уже поступали так в главе 28, когда писали сценарий простейшего фотоальбома. Теперь мы поставим задачу более точно.



Error_reporting


Устанавливает уровень строгости для системы контроля ошибок PHP. Значение этого параметра должно представлять из себя целое число, которое интерпретируется как десятичное представление двоичной битовой маски. Установленные в 1 биты задают, насколько детальным должен быть контроль. Можно также не возиться с битами, а использовать константы.

Таблица 24.1. Биты, управляющие контролем ошибок

Бит

Константа PHP

Назначение

1

E_ERROR

Фатальные ошибки

2

E_WARNING

Общие предупреждения

4

E_PARSE

Ошибки трансляции

8

E_NOTICE

Предупреждения

16

E_CORE_ERROR

Глобальные предупреждения (почти не используются)

32

E_CORE_WARNING

Глобальные ошибки (не используется)

Наиболее часто встречающееся сочетание — 7 (1+2+4), которое, как мы можем видеть, задает полный контроль, кроме некритичных предупреждений интерпретатора (таких, например, как обращение к неинициализированной переменной). Оно часто задается по умолчанию при установке PHP. Я же рекомендую первым делом устанавливать значение этой настройки равным 255 (соответствует битовой маске со всеми единичками), т. е. включить абсолютно все сообщения об ошибках, или же воспользоваться константой E_ALL, делающей то же самое.



Expires


Необязательная пара expires=дата задает время жизни нашего Cookie. Точнее, Cookie самоуничтожится, как только наступит указанная дата. Например, если задать expires=Friday,31-Dec-99 23:59:59 GMT, то "печенье" будет "жить" только до 31 декабря 1999 года. Кстати, вот вам и вторая неприятность: хорошо, если мы знаем наверняка время "смерти" Cookie. А если нам нужно его вычислять на основе текущего времени (например,

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



Файл конфигурации Apache httpd.conf


Это приложение содержит полный текст файла конфигурации сервера Apache httpd.conf

с комментариями на русском языке.

Содержимое листинга П1.1 полностью соответствует указаниям по настройке Apache, приведенным в части II книги. Если у вас по какой-то причине не получится правильно установить Apache и PHP версии 4, руководствуясь этими указаниями, представленный ниже текст файла httpd.conf решит все проблемы.

Несколько слов о формате httpd.conf. Файл состоит из строк, содержащих директивы Apache. В одной строке может быть расположено не более одной директивы. Текст от # äо конца строки считается комментарием и не берется в рассмотрение. Также игнорируются пустые строки.

При изменении начальной конфигурации файла возможно группирование нескольких директив в блоки, или контейнеры. При этом Apache поддерживает только ограниченное количество допустимых типов контейнеров. Любой блок-контейнер начинается строкой вида <ИмяКонтейнера>, расположенной, как обычно, на отдельной строке, и завершается тэгом </ИмяКонтейнера>. Некоторые (но не все) блоки могут быть вложенными.

Директивы, касающиеся индивидуальных настроек для каталогов или файлов, могут также помещаться в специальные файлы .htaccess, расположенные в соответствующих местах дерева каталогов сайта. Эти файлы должны иметь тот же формат, что и httpd.conf. Однако для них имеются особые ограничения на использование директив и блоков — список недопустимых можно найти в документации, поставляемой с Apache.

Листинг П1.1. Файл конфигурации Apache httpd.conf

# Основан на конфигурационных файлах сервера NSCA, созданных

# Робом МакКулом.

#

# Главный файл конфигурации сервера Apache, содержащий директивы,

# управляющие работой сервера. За более детальной информацией

# обращайтесь по адресу http://www.apache.org/docs/.

#

# Не стоит читать эти директивы без понимания их роли. Они

# приведены здесь лишь в качестве примера одного из возможных

# вариантов. В случае сомнений обращайтесь к сопроводительной


# документации. Считайте, что вас предупредили.

#

# После просмотра и анализа файла httpd.conf сервер

# попробует найти и обработать файлы:

# C:/Program Files/Apache Group/Apache/conf/srm.conf, а затем

# C:/Program Files/Apache Group/Apache/conf/access.conf,

# если вы не переопределили эти имена директивами ResourceConfig

# и/или AccessConfig.

#

# Директивы конфигурации сгруппированы в три основных раздела:

#

# 1. Директивы, управляющие процессом Apache в целом (глобальное

#    окружение).

# 2. Директивы, определяющие параметры "главного" сервера, или

#    сервера "по умолчанию", отвечающего на запросы, которые

#    не обрабатываются виртуальными хостами. Эти директивы задают

#    также установки по умолчанию для всех остальных виртуальных хостов.

# 3. Установки для виртуальных хостов, позволяющие обрабатывать

#    запросы Web одним-единственным сервером Apache, но направлять

#    по раздельным IP-адресам или именам хостов.

#

# Файлы конфигурации программы и журналы регистрации событий

# (в программисткой среде они чаще называются "конфигами" и "логами",

# так что, я думаю, ничего страшного не произойдет, если я буду

# придерживаться этой терминологии и здесь).

# Если имена файлов, определенных вами для управления сервером,

# начинаются с символа / (или "диск:/" для Win32), сервер будет

# использовать явно указанный в этом имени полный путь. Если же имена не

# начинаются с "/", то для определения пути будет задействовано значение

# директивы ServerRoot. Так, logs/foo.log при значении ServerRoot,

# равном /usr/local/apache, будет интерпретироваться сервером как

# /usr/local/apache/logs/foo.log.

#

# Внимание: В определении имен файлов вы должны использовать прямые слэши

# вместо обратных (т. е. c:/apache вместо c:\apache). Если не указано

# имя диска, по умолчанию будет выбран диск, на котором размещен

# Apache.exe; тем не менее, во избежание путаницы, рекомендуется, чтобы



# вы всегда явно указывали в абсолютных путях имя диска.

#

### Раздел 1: Глобальное окружение

#

# Директивы в этом разделе определяют общие параметры Apache, такие как,

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

# или где ему искать свои файлы конфигурации.

#

# Директива ServerType может иметь значения inetd или standalone.

# Режим inetd поддерживается только на платформах Unix.

ServerType standalone

#

# ServerRoot: вершина дерева каталогов, в которых содержатся файлы

# конфигурации, регистрации и отслеживания ошибок.

#

# В конце строки добавлять слэш не следует!

ServerRoot "C:/Program Files/Apache Group/Apache"

#

# PidFile: Файл, куда сервер при запуске должен записывать свой

# идентификатор процесса.

PidFile logs/httpd.pid

#

# ScoreBoardFile: Учетный файл, предназначенный для хранения внутренней

# информации процесса сервера. Он необходим не для всех архитектур.

# Если для вашей он нужен (об этом можно судить по тому, будет ли создан

# такой файл, когда вы запустите Apache), то вы должны

обеспечить, чтобы

# никакие два экземпляра процесса Apache не использовали один и тот же

# учетный файл.

ScoreBoardFile logs/apache_runtime_status

#

# В стандартной конфигурации сервер обработает при запуске файлы

# httpd.conf, srm.conf и access.conf (именно в таком порядке).

# Последние два файла в настоящее время поставляются пустыми, поскольку

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

# одном файле (httpd.conf).

# Закомментированные ниже значения встроены в сервер по умолчанию.

# Если вы используете другие имена файлов, отредактируйте и

# раскомментируйте "умолчальные". Если потребуется, чтобы сервер

# проигнорировал эти файлы, вы можете указать значения /dev/null (для

# Unix) или nul (для Win32).

#ResourceConfig conf/srm.conf

#AccessConfig conf/access.conf

#

# Timeout: Время ожидания в секундах, прежде чем сервер примет или

# отправит сообщение о тайм-ауте.



Timeout 300

#

# KeepAlive: Признак, позволено или нет устанавливать долговременные

# соединения (persistent connections) (т.е. когда обрабатывается более

# одного запроса на соединение). Для запрета укажите значение Off.

KeepAlive On

#

# MaxKeepAliveRequests: Максимальное число запросов, допустимое в одном

# долговременном соединении. Для снятия ограничений обнулите параметр,

# но для максимального быстродействия мы рекомендуем указать заведомо

# большое конкретное значение.

MaxKeepAliveRequests 100

#

# KeepAliveTimeout: Время ожидания в секундах следующего запроса от

# одного и того же клиента в одном подключении.

KeepAliveTimeout 15

#

# Для обработки запросов Apache для Win32 всегда порождает один дочерний

# процесс. Если он по каким-либо причинам будет преждевременно завершен,

# другой дочерний процесс создается автоматически. Поступающие запросы

# внутри такого дочернего процесса обрабатываются отдельными потоками.

# Следующие две директивы управляют поведением таких потоков и процессов.

#

# MaxRequestsPerChild: Число запросов, которое позволено обрабатывать

# дочернему процессу до переполнения. При переполнении дочерний процесс

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

# непрерывной работе, если Apache (или используемые им библиотеки),

# допускают утечку памяти или других ресурсов. На большинстве систем

# это не требуется, но некоторые (например, Solaris) имеют заметные

# утечки в библиотеках. Если нет других рекомендаций, для Win32

# установите значение 0 (без ограничений).

#

MaxRequestsPerChild 0

#

# ThreadsPerChild: Число одновременно выполняющихся потоков (т.е.

# запросов), которое допускает сервер. Установите это значение в

# соответствии с требуемой загрузкой сервера (больше активных запросов

# одновременно означает, что они обслуживаются медленнее) и объемом

# системных ресурсов, который вы можете предоставить серверу.

#

ThreadsPerChild 50

#

# Listen: Позволяет привязать Apache к конкретному адресу IP, и/или



# порту, в дополнение к порту, определенному по умолчанию. См. также

# директиву <VirtualHost>.

#

#Listen 3000

#Listen 12.34.56.78:80

#

# BindAddress: Этой опцией вы можете обеспечить поддержку виртуальных

# хостов. Данная директива используется для указания серверу адреса IP,

# который необходимо отслеживать. Она может содержать *, адрес IP или

# полное имя домена Интернета. См. также директивы <VirtualHost> и Listen.

#

#BindAddress *

#

# Поддержка динамически разделяемых объектов (DSO, Dynamic Shared Object)

#

# Для того чтобы иметь возможность использовать модуль, созданный как

# библиотека DSO, вам следует поместить в этом месте соответствующую

# строку LoadModuleТогда модуль будет доступен

# прежде

обращения к нему.

# За детальными разъяснениями механизмов DSO вы можете обратиться к

# файлу README.DSO в дистрибутиве Apache 1.3, а также выполнить

# команду 'apache -l', чтобы получить список уже встроенных

# (статически скомпонованных и таким образом всегда доступных)

# модулей сервера Apache.

#

# Внимание: Порядок, в котором загружаются модули, имеет большое

# значение. Не меняйте нижеследующий порядок без консультации со

# специалистом.

#

#LoadModule anon_auth_module modules/ApacheModuleAuthAnon.dll

#LoadModule dbm_auth_module modules/ApacheModuleAuthDBM.dll

#LoadModule digest_auth_module modules/ApacheModuleAuthDigest.dll

#LoadModule cern_meta_module modules/ApacheModuleCERNMeta.dll

#LoadModule digest_module modules/ApacheModuleDigest.dll

#LoadModule expires_module modules/ApacheModuleExpires.dll

#LoadModule headers_module modules/ApacheModuleHeaders.dll

#LoadModule proxy_module modules/ApacheModuleProxy.dll

#LoadModule rewrite_module modules/ApacheModuleRewrite.dll

#LoadModule speling_module modules/ApacheModuleSpeling.dll

#LoadModule info_module modules/ApacheModuleInfo.dll

#LoadModule status_module modules/ApacheModuleStatus.dll

#LoadModule usertrack_module modules/ApacheModuleUserTrack.dll

#

# Директива ExtendedStatus определяет, будет ли Apache генерировать



# детальную информацию о состоянии (ExtendedStatus On) или только

# общую информацию (ExtendedStatus Off) при обращении к функции

# server-status. Значение по умолчанию — Off.

#

#ExtendedStatus On

### Раздел 2: Конфигурация сервера по умолчанию

#

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

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

# хостами. Эти значения обусловливают также установки по умолчанию для

# любых контейнеров <VirtualHost>, которые вы будете определять

# здесь далее.

#

# Любые из директив раздела могут быть включены в контейнер

# <VirtualHost>; в таком случае установки по умолчанию будут

# переопределены ими для этого виртуального хоста.

#

#

# Если в директиве ServerType (установленной ранее в разделе "Глобальное

# окружение") задано значение inetd, следующие несколько директив не

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

# inetd. Переходите к директиве ServerAdmin.

#

# Port: Номер порта, к которому подключен сервер.

#

Port 80

#

# ServerAdmin: Ваш адрес, по которому следует направлять сообщения о

# проблемах с сервером. Этот адрес появится на некоторых сгенерированных

# сервером страницах, таких, как сообщения об ошибках.

#

ServerAdmin you@your.address

#

# Директива ServerName задает имя хоста, возвращаемое клиенту, если это

# имя отличается от того имени, которое получила программа (например,

# используйте www вместо реального имени хоста).

#

# Внимание: Вы не можете просто выдумывать имена хостов в надежде, что

# это сработает. Имя, которое вы определяете здесь, должно быть

# действительным именем DNS для вашего хоста. В случае затруднений с

# пониманием изложенного справьтесь у

# администратора сети.

# Если ваш хост не имеет зарегистрированного имени DNS, вы можете указать

# здесь его адрес IP. В таком случае вам придется обращаться к хосту по

# адресу (например, http://123.45.67.89/) и это может сильно осложнить



# переадресацию ресурсов.

#

ServerName localhost

#

# DocumentRoot: Каталог, в котором будут находиться ваши документы (т.е.

# Web-страницы). По умолчанию, все запросы выбираются из этого каталога;

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

# (links) и псевдонимы (aliases).

#

DocumentRoot "z:/home/localhost/www"

#

# Каждый каталог, к которому Apache имеет доступ, может быть

# сконфигурирован в отношении свойств и сервисов, которые могут быть

# разрешены и/или запрещены в этом каталоге (и его подкаталогах).

#

# Сначала мы определяем свойства "по умолчанию".

#

<Directory z:/>

  Options Indexes Includes

  AllowOverride All

  allow from all

</Directory>

#

# Обратите внимание, что с этого места и далее вы должны явным образом

# указывать свойства, которые могут быть разрешены, — так что, если что-то

# не работает так, как вы ожидаете, сначала убедитесь, что вы разрешили

# это свойство ниже.

#

# Здесь должен быть указан каталог, который вы установили как

# DocumentRoot.

#

#<Directory "z:/home/localhost/www">;

#

# Опции могут иметь значения None, All или любую комбинацию из

# Indexes, Includes, FollowSymLinks, ExecCGI или MultiViews.

#

# Заметьте, что MultiViews должен быть указан отдельно —

# Options All для этого не достаточно.

#

#   Options Indexes FollowSymLinks MultiViews

#

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

# файлах .htaccess. Значением может быть All или любая комбинация из

# Options, FileInfo, AuthConfig и Limit.

#

#    AllowOverride None

#

# Эти директивы определяют, какие пользователи имеют доступ к информации,

# расположенной на этом сервере.

#

#    Order allow,deny

#    Allow from all

#</Directory>

#

# UserDir: Название каталога, которое прибавляется к именам

# пользовательских домашних каталогов при получении запроса ~user

# (например, http://www.server.com/~username).

#

# Под Win32 мы в настоящее время не пытались устанавливать каталог



# регистрации пользователя, поэтому приходится работать с форматом,

# приведенным ниже.

#

<IfModule mod_userdir.c>

    UserDir "C:/Program Files/Apache Group/Apache/users/"

</IfModule>

#

# DirectoryIndex: Имя файла (или файлов), используемое в качестве

# предопределенной страницы-указателя или оглавления. Если вы указываете

# несколько имен, разделяйте их пробелами.

#

<IfModule mod_dir.c>

        DirectoryIndex index.htm index.html

</IfModule>

#

# AccessFileName: Имя файла, который сервер ищет в каждом каталоге для

# определения прав доступа.

#

AccessFileName .htaccess

#

# Следующие строки предотвращают доступ к файлам .htaccess со стороны

# Web-клиентов. Поскольку файлы .htaccess нередко содержат информацию об

# аутентификации, доступ к ним запрещен из соображений безопасности. Вы

# можете удалить эти строки (или поставить символ комментария),

# если допускаете, чтобы посетители могли просматривать содержимое файлов

# .htaccess из Web. Если вы поменяете значение директивы AccessFileName

# выше, не забудьте внести и сюда соответствующие изменения.

#

<Files ~ "^\.ht">

    Order allow,deny

    Deny from all

</Files>

#

# CacheNegotiatedDocs: По умолчанию с каждым документом Apache отправляет

# инструкцию "Pragma: no-cache", что является указанием proxy-серверам не

# кэшировать данный документ. Если раскрыть следующую строку, то

# поведение proxy-серверов изменится и им будет разрешено кэшировать

# документы.

#

#CacheNegotiatedDocs

#

# UseCanonicalName: (Впервые в версии 1.3.) Если эта директива включена

# (On), то всякий раз, когда Apache требуется создать ссылку на самого

# себя (self-referencing URL, т.е. адрес сервера, с которого поступает

# ответ на запрос), для формирования "канонического имени" он будет

# использовать значения директив ServerName и Port, когда это возможно.

# Если директива выключена (Off), Apache будет по возможности

# использовать значения, предоставленные клиентом. Эта директива влияет



# также на значения переменных SERVER_NAME и SERVER_PORT в CGI-сценариях.

#

UseCanonicalName On

#

# Директива TypesConfig описывает расположение файла mime.types

# (или его эквивалента).

#

<IfModule mod_mime.c>

    TypesConfig conf/mime.types

</IfModule>

#

# Директива DefaultType определяет MIME-тип, который будет использоваться

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

# признакам, например, по расширению имени файла. Если ваш сервер

# содержит по большей части тексты или HTML-документы, text/plain

# является приемлемым решением. Если большая часть содержимого является

# исполняемыми файлами или изображениями, вы можете поменять значение на

# application/octet-stream, чтобы предотвратить попытку браузера

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

#

DefaultType text/plain

#

# Модуль mod_mime_magic позволяет серверу использовать разнообразные

# приемы определения типа файла по его содержимому. Директива

# MIMEMagicFile указывает ему файл, где даны описания таких приемов.

# По умолчанию mod_mime_magic не включен в состав сервера (вы должны

# загрузить его сами с помощью директивы LoadModule — см. абзац DSO в

# разделе "Глобальное окружение", или заново откомпилировать сервер

# с этим модулем), поэтому директива MIMEMagicFile заключена в контейнер

# <IfModule>. Это означает, что она будет обработана только в том случае,

# если модуль mod_mime_magic уже загружен.

#

<IfModule mod_mime_magic.c>

    MIMEMagicFile conf/magic

</IfModule>

#

# Директива HostnameLookups определяет, регистрировать ли клиентов по

# именам, или только по адресам IP, т.е. www.apache.org (On) или

# 204.62.129.132 (Off). По умолчанию — Off, поскольку для снижения

# нагрузки на сеть было бы лучше, если бы вы использовали эту

# возможность, зная о последствиях, т. к. отслеживание по именам означает,

# что каждый клиентский запрос приведет как минимум

к еще одному запросу

# к серверу имен для преобразования IP-адреса в имя.



#

HostnameLookups Off

#

# ErrorLog: Расположение файла регистрации ошибок. Если вы не определяете

# директиву ErrorLog внутри контейнера <VirtualHost>, сообщения об

# ошибках, возникших при работе этого хоста, будут записаны в указанный

# ниже файл. В противном случае все сообщения направятся в специфичный

# для виртуального хоста журнал.

#

ErrorLog logs/error.log

#

# LogLevel: Определение характера ошибок, которые записываются в

# error.log. Возможные значения в порядке убывания количества сообщений:

# debug, info, notice, warn, error, crit, alert, emerg.

#

LogLevel warn

#

# Следующие директивы указывают псевдонимы некоторых форматов, которые

# используются в директиве CustomLog (см. ниже).

#

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{User-Agent}i\"" combined

LogFormat "%h %l %u %t \"%r\" %>s %b" common

LogFormat "%{Referer}i -> %U" referer

LogFormat "%{User-agent}i" agent

#

# Расположение и формат файла регистрации (лога). Если вы не определяете

# никаких лог-файлов внутри контейнера <VirtualHost>, сведения

# будут записываться здесь. Если же вы определяете отдельный лог-файл

# для виртуального хоста, доступ будет отслеживаться в этом логе,

# но не здесь.

#

CustomLog logs/access.log common

#

# Если вы хотите, чтобы имелся агент ссылочных логов (referer logfiles

# agent), раскомментируйте следующие директивы.

#

#CustomLog logs/referer.log referer

#CustomLog logs/agent.log agent

#

# Если вы предпочитаете иметь один лог-файл с информацией о доступе,

# агентах и ссылках (комбинированный формат лог-файла), вы можете

# использовать следующую директиву.

#CustomLog logs/access.log combined

#

# Позволяет добавить дополнительную строку, содержащую версию сервера и

# имя виртуального хоста на страницах, сгенерированных сервером

# (сообщениях об ошибках, листингах каталогов FTP, в вывод модулей

# mod_status и mod_info, но не в CGI-документах). Чтобы дополнительно



# включить ссылку mailto:, содержащую значение директивы ServerAdmin,

# установите значение EMail.

# Допустимые значения: On | Off | Email

#

ServerSignature On

#

# Apache по умолчанию анализирует первую строку каждого CGI-сценария.

# Если эта строка является комментарием и выглядит так: символ (#),

# затем восклицательный знак (!) и, наконец, путь к

# программе-интерпретатору, по которому осуществляется запуск

# сценария, Apache запускает этот интерпретатор.

# Например, для perl-сценариев, стартуемых под управлением perl.exe

# из каталога C:\Program Files\Perl, эта строка должна выглядеть так:

# !c:/program files/perl/perl

# Внимание: вы не должны вставлять пробелы перед символом (#). Êðîìå

# òîãî, указанная специальная строка должна быть именно первой строкой

# файла. Конечно, для запускаемого файла должна быть разрешена обработка

# CGI — например, путем указания директивы ScriptAlias или

# Options ExecCGI.

#

# Тем не менее, Apache для Windows позволяет в дополнение к "магической"

# строке использовать Реестр для поиска ассоциаций с расширениями.

# Команда для запуска файла указанного типа в этом случае ищется в

# Реестре точно так же, как это происходит, например, при работе

# Проводника, когда вы выполняете двойной щелчок на файле. Действия по

# запуску сценария могут быть сконфигурированы из меню Вид Проводника.

# Там необходимо выбрать Свойства папки

и переключиться на вкладку

# Типы файлов. Нажатие на кнопку Изменить позволяет задать действие,

# которое Apache выполнит при попытке открытия файла. Если это не

# удастся, Apache будет искать интерпретатор при помощи "магической"

# строки. Возможно, поведение сервера изменится в Apache версии 2.0.

#

# Чтобы разрешить это специфичное для Windows поведение сервера и, таким

# образом, запретить анализ "магической" строки, удалите комментарий

# со следующей директивы:

#

ScriptInterpreterSource registry



#

# Эта директива может быть помещена в отдельный блок <Directory> или

# в файл .htaccess с указанием в качестве значения registry

# (поведение Windows) или script (анализ "магической" строки, принятый

# в Unix). В таком случае она будет перекрывать директиву, расположенную

# здесь, в главном конфигурационном файле сервера.

#

#

# Псевдонимы: Можно добавлять любое количество псевдонимов (без

# ограничений).

# Формат: Alias псевдоним действительное_имя

#

<IfModule mod_alias.c>

    # Обратите внимание, что если вы включаете завершающий слэш в

    # "псевдоним", то сервер потребует его присутствия и в URL. Так,

    # /icons не будет разыменован в данном примере, только /icons/.

    #

    Alias /icons/ "C:/Program Files/Apache Group/Apache/icons/"

    <Directory "C:/Program Files/Apache Group/Apache/icons">

        Options Indexes MultiViews

        AllowOverride None

        Order allow,deny

        Allow from all

    </Directory>

    #

    # ScriptAlias: Указывает каталог, который содержит серверные

    # сценарии. Свойства ScriptAlias’ов такие же, как и у простых

    # псевдонимов, за исключением того, что документы в каталоге

    # "действительное_имя" считаются приложениями и выполняются

    # на сервере, а не отправляются клиенту. К директиве

    # ScriptAlias применяются те же правила в отношении

    # завершающего /, что и к Alias.

    #

    ScriptAlias /cgi-bin/ "z:/home/localhost/cgi/"

    ScriptAlias /cgi/ "z:/home/localhost/cgi/"

</IfModule>

# Конец определений псевдонимов.

#

# Директива Redirect позволяет сообщить клиенту о документе, который

# существовал некогда в пространстве имен сервера, но был перемещен

# в другое место. Она информирует клиента о его новом адресе.

#

# Формат: Redirect старый_URL новый_URL

#

#

# Директивы, управляющие генерацией сервером  листингов каталогов.

#

<IfModule mod_autoindex.c>



    #

    # FancyIndexing означает, что вы предпочитаете листинги с

    # "украшательствами". О других возможных значениях директивы

    # IndexOptions см. сопроводительную документацию.   

    #

    IndexOptions FancyIndexing

    #

    # Директивы AddIcon* указывают серверу, какими ярлыками

    # будут украшены имена файлов в листинге каталога. Ярлыки    

    # изображаются только в режиме FancyIndexing.

    #

    AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip

    AddIconByType (TXT,/icons/text.gif) text/*

    AddIconByType (IMG,/icons/image2.gif) image/*

    AddIconByType (SND,/icons/sound2.gif) audio/*

    AddIconByType (VID,/icons/movie.gif) video/*

    AddIcon /icons/binary.gif .bin .exe

    AddIcon /icons/binhex.gif .hqx

    AddIcon /icons/tar.gif .tar

    AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv

    AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip

    AddIcon /icons/a.gif .ps .ai .eps

    AddIcon /icons/layout.gif .html .shtml .htm .pdf

    AddIcon /icons/text.gif .txt

    AddIcon /icons/c.gif .c

    AddIcon /icons/p.gif .pl .py

    AddIcon /icons/f.gif .for

    AddIcon /icons/dvi.gif .dvi

    AddIcon /icons/uuencoded.gif .uu

    AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl

    AddIcon /icons/tex.gif .tex

    AddIcon /icons/bomb.gif core

    AddIcon /icons/back.gif ..

    AddIcon /icons/hand.right.gif README

    AddIcon /icons/folder.gif ^^DIRECTORY^^

    AddIcon /icons/blank.gif ^^BLANKICON^^

    #

    # DefaultIcon определяет ярлык для файла по умолчанию

    # если он не задан явно.

    #

    DefaultIcon /icons/unknown.gif

    #

    # AddDescription позволяет размещать краткое описание после имени

    # файла в индексах (листингах каталогов), сгенерированных сервером.

    # Такие описания выводятся только в режиме FancyIndexing.

    #

    # Формат: AddDescription "строка_описания" .расширение_имени_файла

    #

    #AddDescription "GZIP compressed document" .gz



    #AddDescription "tar archive" .tar

    #AddDescription "GZIP compressed tar archive" .tgz

    #

    # ReadmeName задает имя README-файла, который добавляется к листингу

    # каталога по умолчанию.

    #

    # HeaderName указывает имя файла, выводимого в

    # заголовке листингов каталога.   

    #

    # Если задана директива MultiViews в числе значений Options,

    # сначала сервер попытается открыть файл имя.html и включит его в

    # листинг, если файл существует. Если файл имя.html не существует,

    # сервер переориентируется на открытие файла

    # имя.txt и включение его в листинг в виде простого текста.

    #

    ReadmeName README

    HeaderName HEADER

    #

    # IndexIgnore описывает набор имен файлов, которые должны быть

    # исключены из листинга. В именах допустимы метасимволы подстановки

    # в стиле shell.

    IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t

</IfModule>

# Конец секции директив управления листингами.

#

# Типы документов.

#

<IfModule mod_mime.c>

    #

    # AddEncoding позволяет вам заставить определенные браузеры

    # (Mosaic/X 2.1+) распаковывать информацию "на лету".

    # Внимание: это свойство поддерживают не все браузеры. Несмотря

    # на сходство имен, нижеприведенные директивы Add* не

    # имеют ничего общего с директивами оформления FancyIndexing,

    # приведенными выше.   

    #

    AddEncoding x-compress Z

    AddEncoding x-gzip gz tgz

    #

    #

    # AddLanguage позволяет указать язык документа. Вы можете затем

    # использовать протокол обмена (content negotiation) для выдачи

    # браузеру документа на том языке, который он (браузер) предпочитает.

    #

    # Примечание 1: Суффикс не обязательно должен совпадать с буквенным

    # кодом языка — те, у кого есть документы на польском языке

    # (стандартный сетевой буквенный код pl), могут воспользоваться

    # директивой AddLanguage pl .po во избежание конфликта с



    # распространенным суффиксом сценариев на языке Perl.

    #

    # Примечание 2: Нижеследующие примеры показывают, что в нескольких

    # случаях двухбуквенный код языка не совпадает с двухбуквенным кодом

    # страны.

    # Например, "Датский/da" вместо "Дания/dk".

    #

    # Примечание 3: В случае ltz мы нарушаем требования RFC, используя

    # трехбуквенный код. Но уж тут ничего не поделаешь. В будущем,

    # возможно, несоответствия с RFC1766 будут устранены.

    #

    # Коды языков:

    # датский (Danish) da; голландский, Нидерланды (Dutch) nl;

    # английский (English) en; эстонский (Estonian) ee;

    # французский (French) fr; немецкий (German) de;

    # новогреческий (Greek-Modern) el; итальянский (Italian) it;

    # португальский (Portuguese) pt;

    # люксембургский (Luxembourgeois*) ltz;

    # испанский (Spanish) es; шведский (Swedish) sv;

    # каталонский (Catalan) ca; чешский (Czech) cz;

    # русский (Russian) ru.

    #

    AddLanguage da .dk

    AddLanguage nl .nl

    AddLanguage en .en

    AddLanguage et .ee

    AddLanguage fr .fr

    AddLanguage de .de

    AddLanguage el .el

    AddLanguage he .he

    AddCharset ISO-8859-8 .iso8859-8

    AddLanguage it .it

    AddLanguage ja .ja

    AddCharset ISO-2022-JP .jis

    AddLanguage kr .kr

    AddCharset ISO-2022-KR .iso-kr

    AddLanguage no .no

    AddLanguage pl .po

    AddCharset ISO-8859-2 .iso-pl

    AddLanguage pt .pt

    AddLanguage pt-br .pt-br

    AddLanguage ltz .lu

    AddLanguage ca .ca

    AddLanguage es .es

    AddLanguage sv .se

    AddLanguage cz .cz

    AddLanguage ru .ru

    AddLanguage tw .tw

    AddCharset Big5         .Big5    .big5

    AddCharset WINDOWS-1251 .cp-1251

    AddCharset CP866        .cp866

    AddCharset ISO-8859-5   .iso-ru

    AddCharset KOI8-R       .koi8-r

    AddCharset UCS-2        .ucs2

    AddCharset UCS-4        .ucs4

    AddCharset UTF-8        .utf8

    # LanguagePriority позволяет определить первоочередность некоторых



    # языков при установлении протокола обмена.

    #

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

    # перечислите их в порядке убывания приоритета.

    #

    <IfModule mod_negotiation.c>

        LanguagePriority en da nl fr de el it ja no pl pt ru ca es sv tw

    </IfModule>

    #

    # AddType позволяет слегка подправить mime.types, не редактируя его,

    # или объявить конкретные файлы имеющими определенный тип.

    #

    # Например, модуль PHP3 (этот модуль не является частью дистрибутива

    # сервера Apache), обычно использует следующие объявления:   

    #

    #AddType application/x-httpd-php3 .php3

    # AddType application/x-httpd-php3-source .phps

    #

    # В случае PHP 4.x укажите:

    #

    AddType application/x-httpd-php .php

    # AddType application/x-httpd-php-source .phps

    # Следующие строки не относятся к заданию типов документов,

    # но их удобно поместить сюда для подключения PHP:

    #

    ScriptAlias /_php/ "C:/Program Files/PHP4/"

    Action application/x-httpd-php "/_php/php.exe"

    AddType application/x-tar .tgz

    #

    # AddHandler позволяет отобразить определенные расширения имен файлов

    # на обработчиков вне связи с определениями типов файлов. Обработчики

    # могут быть как встроены в сервер, так и объявлены директивой

    # Action (см. ниже).

    #

    # Если вы хотите использовать файлы, вставляемые сервером в ваши

    # документы (SSI — server side includes), снимите комментарий

    # со следующих строк:

    #

    # для использования сценариев CGI —

    #

    AddHandler cgi-script .bat .exe .cgi

    #

    # для HTML-файлов, предварительно обрабатываемых

    # сервером (server-parsed HTML files):

    #

    AddType text/html .shtml

    AddHandler server-parsed .shtml .html .htm

    #

    # Раскомментируйте следующую строку, чтобы разрешить Apache передачу

    # специальных файлов, которые не сопровождаются стандартными



    # заголовками HTTP (send-asis HTTP file).

    #

    # AddHandler send-as-is asis

    #

    # Если вы хотите использовать карты-изображения, обрабатываемые

    # сервером, раскройте следующую директиву:   

    #

    # AddHandler imap-file map

    #

    # Если вы хотите задействовать карты типов (type maps, см.

    # документацию), используйте:   

    #

    # AddHandler type-map var

</IfModule>

# Конец блока директив описания типов документов.

#

# Директива Action позволяет определить приложение, выполняющее сценарии,

# когда запрашиваются содержащие их файлы. Это устраняет необходимость

# многократного упоминания URL часто используемых процессоров

# CGI-сценариев.

# Формат: Action псевдоним_типа /псевдоним_пути/обработчик

#         Action среда/тип /псевдоним_пути/обработчик

#

#

# MetaDir: определяет имя каталога, в котором Apache может найти файлы с

# метаинформацией. Эти файлы содержат дополнительные заголовки HTTP,

# включаемые при отправке определенных документов.

#

# MetaDir .web

#

# MetaSuffix устанавливает суффикс имени файла, содержащего метаинформацию.

#

# MetaSuffix .meta

#

# Настраиваемая реакция на ошибки (собственный стиль Apache) может быть

# трех типов.

#

# 1) простой текст

#    ErrorDocument 500 "Сервер сказал а-я-яй!"

#    Внимание: знак двойной кавычки просто означает, что далее следует

#    текст.

#

# 2) локальная переадресация

#    Чтобы перенаправить на локальный документ:

#    ErrorDocument 404 /missing.html

#    Перенаправлять можно и на сценарий, и на документ, использующий

#    включения на стороне сервера:

#    ErrorDocument 404 /cgi-bin/missing_handler.pl

#

# 3) внешняя переадресация

#    ErrorDocument 402 http://some.other_server.com/info.html

#    Большинство переменных окружения, связанных с исходным запросом,

#    станут недоступны при такой переадресации.

#

# Установки, связанные с браузером пользователя.

#

<IfModule mod_setenvif.c>

    #

    # Следующие директивы отменяют поддержку долговременных соединений



    # (keepalives) и "смывание" заголовков HTTP. Первая директива

    # отменяет их для Netscape 2.x и браузеров, которые "притворяются",

    # что они — Netscape (известны некоторые проблемы с такими 

    # браузерами). Вторая директива предназначена для Microsoft Internet

    # Explorer 4.0b2, реализация HTTP/1.1 которого не полна и не

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

    # откликах 301 или 302 (переадресация).

    #

BrowserMatch "Mozilla/2" nokeepalive

BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0

    #

    # Следующая директива отключает отклики по HTTP/1.1 браузерам,

    # которые нарушают стандарты HTTP/1.0 и не могут разобрать

    # основной отклик 1.1.   

    #

    BrowserMatch "RealPlayer 4\.0" force-response-1.0

    BrowserMatch "Java/1\.0" force-response-1.0

    BrowserMatch "JDK/1\.0" force-response-1.0

</IfModule>

# Конец настроек, связанных с браузерами.

#

# Следующая группа директив управляет отчетами о состоянии сервера,

# имеющего URL http://servername/server-status. Для приведения в

# соответствие с вашими нуждами измените .your_domain.com.

#

# <Location /server-status>

#    SetHandler server-status

#    Order deny,allow

#    Deny from all

#    Allow from .your_domain.com

# </Location>

#

# Эта группа директив управляет отчетами конфигурации удаленного

# сервера http://servername/server-info (требуется, чтобы был загружен

# mod_info.c). Замените .your_domain.com на имя вашего домена.

#

# <Location /server-info>

#    SetHandler server-info

#    Order deny,allow

#    Deny from all

#    Allow from .your_domain.com

# </Location>

#

# Поступали сообщения, что некие люди пытаются злоупотреблять древней

# ошибкой старых версий Apache. Ошибка касалась CGI-сценария,

# поставлявшегося с Apache.

# Раскрыв следующие строки, вы можете переадресовать эти атаки



# на регистрирующий сценарий на phf.apache.org. А можете регистрировать

# их сами, используя сценарий support/phf_abuse_log.cgi.

#

# <Location /cgi-bin/phf*>

#    Deny from all

#    ErrorDocument 403 http://phf.apache.org/phf_abuse_log.cgi

# </Location>

#

# Директивы proxy-сервера.

#

# <IfModule mod_proxy.c>

    # Раскройте следующую строку для того, чтобы разрешить

    # работу с proxy.

    # ProxyRequests On

    # <Directory proxy:*>

    #   Order deny,allow

    #   Deny from all

    #   Allow from .your_domain.com

    # </Directory>

 

    #

    # Разрешить/запретить обработку заголовков HTTP/1.1 Via:.

    # Возможные значения: Off | On | Full | Block. Full добавляет в

    # заголовок версию сервера, Block удаляет все исходящие

    # заголовки Via:.   

    #

    # ProxyVia On

    #

    # Для разрешения также кэширования отредактируйте и раскройте

    # следующие строки (нельзя включать кэширование без указания

    # CacheRoot):   

    #

    # CacheRoot "C:/Program Files/Apache Group/Apache/proxy"

    # CacheSize 5

    # CacheGcInterval 4

    # CacheMaxExpire 24

    # CacheLastModifiedFactor 0.1

    # CacheDefaultExpire 1

    # NoCache a_domain.com another_domain.edu joes.garage_sale.com

# </IfModule>

# Конец настроек proxy-сервера.

### Раздел 3: Виртуальные хосты

#

# Директива VirtualHost: Если вы хотите держать на своей машине несколько

# хостов, следует для каждого из них завести контейнер VirtualHost.

# Прежде чем их устанавливать, обращайтесь за подробными разъяснениями к

# документации по адресу http://www.apache.org/docs/vhosts/. Для проверки

# конфигурации ваших виртуальных хостов вы можете задавать опцию -S

# командной строки.

#

# Если вы хотите использовать именные виртуальные хосты (name-based

# virtual hosts), вам необходимо определить для них как минимум один

# адрес IP (и номер порта).

#

NameVirtualHost 127.0.0.1:80

#

# Пример использования директивы VirtualHost:



# В контейнер VirtualHost может включаться почти любая

# директива Apache.

#

# <VirtualHost ip.address.of.host.some_domain.com>

#    ServerAdmin webmaster@host.some_domain.com

#    DocumentRoot /www/docs/host.some_domain.com

#    ServerName host.some_domain.com

#    ErrorLog logs/host.some_domain.com-error_log

#    CustomLog logs/host.some_domain.com-access_log common

# </VirtualHost>

# <VirtualHost _default_:*>

# </VirtualHost>

# Далее идут настройки для виртуальных хостов, описанных во второй

# части этой книги.

#----localhost

<VirtualHost localhost>

  ServerAdmin webmaster@localhost.ru

  ServerName localhost

  DocumentRoot "z:/home/localhost/www"

  ScriptAlias /cgi/ "z:/home/localhost/cgi/"

  ErrorLog z:/home/localhost/error.log

  CustomLog z:/home/localhost/access.log common

</VirtualHost>

#----hacker

<VirtualHost hacker>

  ServerAdmin webmaster@hacker.ru

  ServerName hacker

  DocumentRoot "z:/home/hacker/www"

  ScriptAlias /cgi/ "z:/home/hacker/cgi/"

  ErrorLog z:/home/hacker/error.log

  CustomLog z:/home/hacker/access.log common

</VirtualHost>

#----cracker

<VirtualHost cracker>

  ServerAdmin webmaster@cracker.ru

  ServerName cracker

  DocumentRoot "z:/home/cracker/www"

  ScriptAlias /cgi/ "z:/home/cracker/cgi/"

  ErrorLog z:/home/cracker/error.log

  CustomLog z:/home/cracker/access.log common

</VirtualHost>

# Конец главного файла конфигурации Apache.

[1] Русский язык, изначально обладающий гигантской свободой в выборе слова, постоянно развивается. То, что казалось неприемлемым вчера, сегодня становится нормой, и наоборот. Безусловно, у любого обратившего внимание на эти строки при прочтении слов "закачать" и "скачать" вряд ли возникнут ассоциации с бригадой мускулистых администраторов, придающих передаваемым по сети файлам необходимую кинетическую энергию для последующего перемещения под напором, или другие неверные мысли, несмотря на большое количество смысловых оттенков употребления этих слов (убаюкивать, вызвать головокружение, или же в понимании "подлого приема садовников, торговцев присадками (раскачивать деревце, не давая ему укорениться)"). Вообще говоря, о твердых правилах в условиях возрастающего слияния разговорных терминов и литературного языка говорить не приходится. В толковом словаре С. И. Ожегова и Н. Ю. Шведовой дано пояснение идиоме "Закачаешься!" как выражения высокой оценки чего-либо. Редакторы вовсе не стремятся убивать живое изложение, и поэтому если вы, уважаемые читатели, также видите эти строки, значит было решено — "быть закачиваемому" (из примеров к статье "Закачать" толкового словаря живого великорусского языка В. И. Даля). — Ред.



 [AL1]Обороты “и это хорошо” и “ и это правильно” очень часто встречаются и мне не кажутся удачными

 [AL2]такой синтаксис отнюдь не исключение, к примеру можно взять макроассемблер

 [E3]это так? в коде нет никаких операторов вывода

 [E4]Иначе надо писать “обеих”, а это — неблагозвучный термин

 [В. О.5]Самодокументруемые?

 [В. О.6]разобраться

 [В. О.7]не что иное?

 [В. О.8]?

 [В. О.9]Запятая?

 [В. О.10]По поводу точек в программировании

 [В. О.11]шести?

 [В. О.12]Мне кажется, это не всем понравится

 [В. О.13]?

 [В. О.14]Вряд ли это понравится читателю, тем более, что Вы очень хорошо изложили суть

 [В. О.15]?

 [В. О.16]Запятая?

 [В. О.17]Почему? Хэш — он и в Африке хэш.

 [В. О.18]!

 [В. О.19]Это как-то уж слишком по-программистки,  но не по-книжному

 [В. О.20]а разве должно быть иначе? Автоинкремент и автодекремент, по логике, разве применимы к булевскому типу?

 [В. О.21]Потенциальной?

 [В. О.22]Прочитал и не понял

 [В. О.23]Извините, но требования издательства не допускают такого оформления

 [В. О.24]ценна ли здесь эта фраза?

 [В. О.25]3.1459…

 [В. О.26]а вдруг 4? Как насчет у-Си-дки и Perlюстрации?

 [В. О.27]Из-за "уменьшив"

 [В. О.28]?

 [В. О.29]Может, изменить название переменной — а то "a" отдельно от последующих слов поначалу не воспринимается?

 [В. О.30]!

 [В. О.31]!

 [В. О.32]Пробел внесен сознательно?

 [В. О.33]Наличие пробелов не вредит смыслу?

 [В. О.34]Си-подобные операторы или нет?

 [В. О.35]Противопоставление Си неверно, Вы, безусловно, это понимаете

 [В. О.36]Ни что иное

 [В. О.37]считаю, что эту формулировку следует исключить или же я бы посоветовал Вам заменить этот жаргон на изначальное название символа — "коммерческое эт". Мне кажется, "оживляжа" и так достаточно



 [В. О.38]может быть, лучше что-то нейтральное?

 [AL39]Я думаю, Вы имели в виду именно это, или же "осознанна"?

 [AL40]такие слова не пропускает корректор

 [AL41]мне кажется, так лучше. Ваше мнение?

 [AL42]ENVIROPMENT?

 [AL43]надежно?

 [AL44]написать "кнопкой" или оставить Ваш стиль?

 [AL45]"Обновите ваш PHP!"

 [AL46]"наша версия PHP отстала от жизни"

 [E47]?

 [E48]?

 [E49]Не понял

 [E50]?

 [E51]в обоих смыслах

 [E52]обычно уточняют что-то вроде “В двух строках разной длины каждый символ более длинной строки без соответствующего символа в более короткой строке принимает значение "больше";

например, 'Xs' больше, чем 'X'. Пустые строки могут быть равны только другим пустым строкам, и они являются наименьшими текстовыми значениями”

 [E53]Для однообразия

 [E54]Для устранения “коридора” (коридоры образуют пробелы между словами в строках, которые следуют одна за другой)

 [E55]первых или последних?

 [E56]?

 [E57]Это особенность алгоритма или тавтология? Если первое, то тогда какой же это хэш?

 [E58]В разделе всего одна функция. Заголовок не совсем соответствует.

 [E59]Скользит, перебирает элементы?

 [E60]Не понял, речь идет о перегрузке функций? Или это не описанная ранее особенность PHP?

 [E61]Плоховато, но более ничего не удалось подобрать

 [E62]При выборке небольших размеров

 [E63]почему же, для интерактивных карт могут применяться (речь идет о больших системах, содержащих мелкомасштабные карты земной поверхности)

 [E64]“…все равно, какой ориентации придерживается слэш”?

 [E65]?

 [E66]Не понял, кажется, здесь нарушена логика

 [E67]блочно?

 [E68]Чем отличается от предыдущего?

 [E69]

 [E70]звучит как “по пути”

 [E71]Может лучше было было бы использовать структурный элемент типа “Совет”? Или оставить до просмотра коректором? Я спрошу, имеется ли что-нибудь типа “заповедь” или “резюме”



 [E72]

 [E73]?

 [E74]Alias – псевдоним или синоним?

 [E75] Разделитель внутри есть? Или он и есть – пробел?

 [E76]Милли или микро?

 [E77]Åñëè àêöåíò äåëàåòñÿ íà ïóòàíèöó ìèëëè-ìèêðî, то может, стоит употребить кавычки?

 [E78]Это ее не касается?

 [E79]Вот-вот, Word, как и справочники, считает, что папу-основоположника звали ГрИгорием… (ñðàâíèòå: Julian и Gregorian)

 [E80]странновато звучит фраза: и конкретно и вместе с тем “кажется”

 [E81]Несколько строк?

 [E82]На что?

 [E83]Ну это Вы зря…

 [E84]желательно бы добавлять номер, поскольку мне нужно выделять ссылки курсивом

 [E85]?

 [E86]Эта “навороченность” имеет место…?

 [E87]?

 [E88]Не удовлетворяет требованиям

 [E89]а у BHV ничего подобного нет?

 [E90]?

 [DK91]Подойдет.

 [E92]4.07 Netscape не поддерживает, 4.6 – не помню.

 [DK93]Правда? Очень странно… Может быть, Вы просто не поставили в нем поддержку PNG при инсталляции?

 [E94]?

 [DK95]Подойдет.

 [E96](на первый взгляд, не “технологичная”) ?

 [DK97]Да. Но нужно ли уточнять?..

 [E98]Может, стоило бы уточнить определение: ведь по сути если цвет, указанный в качестве “прозрачного”, содержится в “не фоновой” части, он выводится. А не отображается от только для фоновой части при равенстве цвета  фона и “прозрачного” цвета. Меня просто несколько коробит от таких определений, поскольку постигая это на практике, я вынужден был все выяснять сам. А извлекать по крохам такие сведения из примеров не есть хорошо

 [DK99]Вы точно в этом уверены? Если у нас есть картинка, и у нее 10-й цвет назначен прозрачным, то В ЛЮБОМ СЛУЧАЕ точки с цветом 10 не будут выведены в браузер (а значит, то, что под ними было, останется нетронутым). И что значит – «фоновая часть»?



 [E100]E9

 [DK101] Вы хотите убрать такую формулировку? Я думал, читатель улыбнется, когда это прочтет – это была моя первоочередная задача. Ведь чем более непринужденно написана книга, тем легче ее читать, не правда ли?..

 [E102]функция идет от API, где именно множество параметров, и во всех языках наследуется именно со множеством, если только не ссылается на массивы координат

 [DK103]Но, все же, использование большого числа параметров в функции – дурной стиль, об этом еще, кажется, Керниган писал.

 [E104]Не только потому. Изначально эта функция на удивление медленная на системном уровне (BIOS).

 [DK105]Думаю, при выводе точки BIOS не используется.

 [E106]моноширинном, монохромном?

 [DK107]Имеется в виду – только 2 цвета: цвет фона и цвет текста, никаких разноцветных букв. Термин «монохномный» слишком часто ассоциируется с «черно-белым».

 [E108]Нет никакого связанного определения

 [DK109]Запямятовал написать.

 [E110]вот именно, что от корня. Возвращаясь к вопросу во второй (кажется), главе.

То есть само наличие слэша перед путем не говорит о том, что этот путь абсолютный

 [DK111]Я всегда считал, что «абсолютный путь» означает «путь от корня»?..

 [DK112]Имеется в виду использование функции realpath().

 [E113]А как насчет переносимости?

 [DK114]Нет, именно «прямоугольника» - все углы прямые (хотя прямоугольник – это тоже параллелограмм). Возможно, он будет наклонен, но все равно останется прямоугольником.

 [E115]почему Вы его не вставили в книгу? Может стоит?

 [DK116]Потому что он будет не осень хорошо смотреться на бумаге, да еще в книге. Кроме того, пользователь не может, задавая различные параметры и наклон текста, «перерисовать» его прямо на странице книги. На всякий случай я высылаю Вам и рисунок (23.1). Доверяю Вашему решению – вставлять его или нет.

 [DK117]В том-то идея, что она ничего не предоставляет программисту, а только выводит «статистику».

 [DK118]Нет. У меня выводится: «4.0.3pl1»



 [E119] Приложения по требованиям издательства нумеруются по-русски, буквами.

У Вас только два приложения (пока?)

 [DK120]Ошибся.

 [E121]«захватывать»?

 [E122]отладке?

 [DK123]Нет, при ПРОГРАММИРОВАНИИ.

 [E124]?

 [E125]Не понял смысла. Кстати, финализатор и шаблонизатор – стандартные термины?

 [DK126]Финализатор – да, шаблонизатор – нет (о том, что это такое, рассказывается пятой части, которую я пока еще не дописал).

 [E127]Качеств?

 [DK128]Правил.

 [E129]??

ILoveYou в разрезе

Дмитрий КИРСАНОВ

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

Сам по себе вирус ILoveYou является текстом программы на языке Visual

Basic. При переименовании кода Visual Basic в файл с расширением VBS

программа становится исполняемой. Это и лежит в основе как этого

вируса, так и Melissa и еще сотен им подобных.

ILoveYou простой, но изощренный вирyс - автор решил оставить свою метку

везде, где он только может :). В то же время он пренебрег простейшим

шифрованием текста, и более того, оставил о себе подпись, составляющyю

первые две строки:

rem barok -loveletter(vbe) <i hate go to school>

rem by: spyder / ispyder@mail.com / @GRAMMERSoft Group / Manila,

Philippines

И дальше, во время анализа текста вирyса родилось чувство, что его

автор не так давно изучил Visual Basic. Настолько детально были

соблюдены книжные правила написания программ.

Вот с чего вирус начинается:

set wscr=CreateObject("WScript.Shell")

rr=wscr.RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows Scripting

Host\Settings\Timeout")

if (rr>=1) then...

ILoveYou читает вышеуказанную ветку реестра, и если значение больше или

равно единице, то обнуляет его.

После этого он узнает все специальные папки системы: Windows, System и

temp. Используя полученные данные, копирует себя в файлы

SYSTEM\MSKernel32.vbs, WINDOWS\Win32DLL.vbs и

SYSTEM\LOVE-LETTER-FOR-YOU.TXT.vbs.

Следующим шагом троянца становится запись в реестр:



"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\

Run\MSKernel32" теперь содержит ссылку на MSKernel32.vbs, а

"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\

RunServices\Win32DLL" на Win32DLL.vbs.

В результате этого вышеуказанные скрипты запускаются каждый раз при

запуске Windows. Но, в чем отличительная черта именно этого вируса, все

повторяется сначала! Опять зараженный компьютер начинает рассылать свои

копии кому попало, опять пишет в реестр... Очевидно, это просто ошибка

автора скрипта.

После этого троянец выясняет, есть ли у пользователя в системной папке

файл WinFAT32.exe. И если есть, то играет сам с собой в рулетку,

получая случайное число от 1 до 4. И вот здесь скрипт делает совсем

неприятную вещь - в зависимости от полученного числа, изменяет

стартовую страницу в Internet Explorer на одно из 4-х значений:

1. "http://www.skyinet.net/~young1s/

HJKhjnwerhjkxcvytwertnMTFwetrdsfmhPnjw6587345gvsdf7679njbvYT/

WIN-BUGSFIX.exe"

2. "http://www.skyinet.net/~angelcat/

skladjflfdjghKJnwetryDGFikjUIyqwerWe546786324hjk4jnHHGbvbmKLJKjhkqj4w/

WIN-BUGSFIX.exe"

3. "http://www.skyinet.net/~koichi/

jf6TRjkcbGRpGqaq198vbFV5hfFEkbopBdQZnmPOhfgER67b3Vbvg/ WIN-BUGSFIX.exe"

4. "http://www.skyinet.net/~chu/

sdgfhjksdfjklNBmnfgkKLHjkqwtuHJBhAFSDGjkhYUgqwerasdjhPhja

sfdglkNBhbqwebmznxcbvnmadshfgqw237461234iuy7thjg/ WIN-BUGSFIX.exe"

Информацию о файле WIN-BUGSFIX.exe вирyс помещает в

"HKEY_Current_User\Software\Microsoft\Internet Explorer\Main\Start Page"

Более того, папкой для загружаемых из Internet файлов становится C:\ ,

а значит, как только пользователь запускает Explorer, броyзер тянется

за Win-Bugsfix.exe. И, если пользователь соглашается на загрузку файла,

вирyс предлагает сохранить его в корневом каталоге диска C. При

известной беспечности пользователя, может сработать. К сожалению,

Win-Bugsfix.exe оказалась для меня недостyпной, поэтомy не удалось



пообщаться с этой программой. Однако, можно предположить, что ее

назначение - уничтожить FAT-32.

Если пользователь загрузил файл в корневой каталог, то при следующем

срабатывании троянец запишет в

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\

CurrentVersion\Run\WIN-BUGSFIX команду запуска этого файла. При

очередной загрузке программа сработает. И тогда же троянец изменит

стартовую страницу Explorer на about:blank.

В продолжение своей деструктивной деятельности он берет список всех

дисков компьютера и один за другим листает все файлы. Все vbs-, vbe-,

js-, jse-, css-, wsh-, sct- и hta-файлы становятся копиями троянца,

также он удаляет файлы jpeg и jpg, заменяя их файлами с тем же именем

но с расширением VBS. Скажем спасибо автору троянца, что он забыл

поставить команду удаления для mp3- и mp2-файлов. Для них будут просто

созданы файлы с расширением vbs. Правда, с атрибутом Read-Only,

наверное, против неопытных пользователей.

Затем, если вирyсy посчастливилось найти папку с mIRC, в файл

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

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

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

под видом LOVE-LETTER-FOR-YOU.HTM.

Ну, и в заключение, троянец рассылает через MS Outlook каждому адресату

в адресной книжке собственнyю копию. Тема письма - "ILOVEYOU",

приложенный файл - LOVE-LETTER-FOR-YOU.TXT.vbs из системной папки,

HTML-послание содержит мета-таги, с указанием на автора, один из

которых, Description, говорит: "Простой, но, я думаю, он хорош...".

Последнее замечание насчет самого послания. Для того чтобы оно

"сработало", необходимо, чтобы в Explorer по умолчанию запускались

активные скрипты и ActiveX контролы, т. к. по умолчанию Outlook Express

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

содержимого.

 [DK130]Спасибо за присланную интересную статью. Думаю, добавления слова «типа» будет достаточно?..



 [E131]Только реляционная

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

 [E133]В конце предыдущего раздела говорится, что “обычно” è “хостинг-провайдер”.

Текст абзаца несколько избыточен

 [E134]При работе с установленным соединением или в принципе?

 [E135]«велик, могуч и богат»?

 [DK136]Ну да, но тут главное – то, что в этой главе описаны далеко не все возможности.

 [DK137]Да, пожалуй.

 [E138]Настоятельно рекомендую как-то продолжить абзац, поскольку слово “результат” ðàñõîäèòñÿ ñ îáùåïðèíÿòîé òåðìèíîëîãèåé. Например: “Я же придерживаюсь и буду придерживаться другой терминологии: результат — “он и в Африке” результат (это, конечно, грубовато)”. Иначе боюсь, с имеющимся количеством правок текст не пройдет дальше. Термин dataset – стандартен, а если взглянуть в PHP Manual (у меня имеется только PHP3), то вот характерный пример: “mysql_result() returns the contents of one cell from a MySQL result set.” Обратите внимание – “результирующего набора”!

 [DK139]Думаю, средний читатель не особо знаком с базами данных, поэтому чем проще терминология, тем, наверное, будет лучше.

 [E140]?

 [DK141]О «текущей строке» говорится чуть позже, так что, думаю, пока будет лучше оставить «очередную». У меня такой метод – от простого к сложному: сначала рассказываю обо всем загрубленно (то есть, на частных примерах), а потом детализирую и исправляюсь.

 [E142]Опять-таки по поводу “результата”: “в результате” чего-то…

 [DK143]Тут «в результате» – не устойчивый оборот, а «в чем?»

 [E144]вот у базы данных синонимов нет

 [E145]а решает ли это задачу именно в данном контексте? Ведь AUTO_INCREMENT

допустим только для одного поля таблицы. А если другой “администратор” опередит?

 [DK146]Безусловно решает. Просто нужно получать идентификатор уже ПОСЛЕ вставки записи. Об этом рассказано ниже.

 [E147]

 [DK148]Мне не очень нравится это слово. Далеко не все читатели поймут, что оно обозначает здесь, в заглавии. Может, лучше вернуть?..

 [DK149]Сыграем на контрастах?..

 [E150]примечание для верстальщика – сохранить интервал (пустую строку)

 [E151]Примечание для верстки – обойтись без переноса


Файл конфигурации PHP php.ini


Приложение 2, которое вы видите перед собой, уважаемый читатель, включает полный перевод на русский язык комментариев внутри файла конфигурации PHP php.ini.

Директивы в листинге П2.1 полностью соответствуют рекомендациям по установке PHP для Windows, представленным в части II книги. Впрочем, чтобы получить этот файл, мне понадобилось всего пара изменений в настройках PHP по умолчанию (настройки по умолчанию хранятся в файле php.ini-dist) — не то, что в случае с Apache.

Если вы установили PHP как модуль Apache, перед вами открываются дополнительные возможности: вы можете задавать значения некоторых директив прямо в файлах httpd.conf

или .htaccess. В силу специфики синтаксиса файлов конфигурации Apache, для отделения имени директивы и ее значения нужно использовать пробел, а не знак =. Кроме того, имена директив PHP должны быть предварены префиксом php_. Например, директива из php.ini

auto_prepend_file=top.html

будет выглядеть в httpd.conf или .htaccess так:

php_auto_prepend_file top.html

Приведенного листинга с комментариями должно быть вполне достаточно для понимания роли большинства директив PHP. Именно поэтому я уделил им так мало страниц в частях IV и V данной книги. И все-таки, если у вас возникнут какие-то затруднения, их легко сможет разрешить документация, которую можно получить, например, с официального сайта PHP: http://www.php.net.

Листинг П2.1. Файл php.ini

[PHP]

;;;;;;;;;;;;;;;;;

; Об этом файле ;

;;;;;;;;;;;;;;;;;

; Этот файл содержит большинство установок PHP. Чтобы PHP смог его

; обнаружить, он должен называться 'php.ini'. Интерпретатор ищет файл в

; текущем каталоге, в случае неудачи — в каталоге, указанном в

; переменной окружения PHPRC, и, наконец, в каталоге, заданном при

; компиляции и сборке PHP (именно в таком порядке).

; В системе Windows путь, указанный при компиляции PHP,

; соответствует каталогу Windows (в большинстве случаев это

; c:\windows). Папка, в которой будет производиться поиск файла

; 'php.ini', может быть также определена с использованием ключа –c


; командной строки.

;

; Синтаксис файла крайне прост.  Пробельные символы (то есть, пробелы,

; символы табуляции и т. д.), строки, начинающиеся с точки с запятой (;)

; игнорируются (как вы, наверное, уже догадались). Заголовки секций

; (например, [Foo]) также пропускаются, но, возможно, будут учитываться

; в будущих версиях PHP.

;

; Директивы задаются примерно так:

; directive=value

; Имена директив чувствительны к регистру символов — foo=bar не то же

; самое, что FOO=bar.

;

; Значение value может быть строкой, числом, константой PHP (например,

; E_ALL или M_PI), одной из INI-констант (On, Off, True, False, Yes, No

; или None), выражением (например, E_ALL & ~E_NOTICE), а также строкой

; в кавычках ("foo").

;

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

; операторы, а также скобки:

; |      поразрядное ИЛИ (OR)

; &      поразрядное И (AND)

; ~      поразрядное НЕ (NOT)

; !      логическое отрицание (NOT)

;

; В качестве логических флагов со значением "истина" могут быть

; использованы значения 1, On, True или Yes. Значение "ложь" дают 0, Off,

; False и No.

;

; Пустая строка может быть задана, если "не указать ничего" после знака

; равенства, или же указать слово None:

;   foo=        ; устанавливаем foo равным пустой сторке

;   foo=none    ; аналогично

;   foo="none"  ; устанавливаем foo равным строке 'none'

;

; Если вы используете константы в качестве части значения директивы и эти

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

; расширении (модуле PHP или Zend), вы можете указывать их только после

; строки, которая загружает расширение.

;

; Все значения в файле php.ini-dist соответствуют встроенным значениям

; по умолчанию. Если php.ini не задействуется, или же вы удалите из него

; некоторые строки,

будут установлены значения по умолчанию.

;;;;;;;;;;;;;;;;;;;

; Настройки языка ;

;;;;;;;;;;;;;;;;;;;



; Разрешает работу PHP для сервера Apache.

engine=On

; Разрешает использовать короткие тэги <?. Иначе будут распознаваться

; только тэги <?php и <script>.

short_open_tag=On

; Позволяет использовать тэги <% %> а-ля ASP.

asp_tags=Off

; Число значащих цифр после запятой, которые отображаются для чисел с

; плавающей точкой.

precision=14

; Признак коррекции дат (проблема 2000 года, которая может

; вызвать непонимание со стороны браузеров, которые

; на это не рассчитывают)

y2k_compliance=Off

; Использование буферизации вывода. Позволяет посылать заголовки (включая

; Cookies) после вывода текста. Правда, это происходит ценой

; незначительного замедления вывода.

; Вы можете разрешить буферизацию во время выполнения сценария путем

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

; следующей директивы:

output_buffering=Off

; Директива неявной отсылки говорит PHP о том, что выводимые данные нужно

; автоматически передавать браузеру после вывода каждого блока данных.

; Ее действие эквивалентно вызовам функции flush() после

; каждого использования print() или echo() и после каждого HTML-блока.

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

; рекомендуется применять лишь в отладочных целях.

implicit_flush=Off

; Параметр определяет, должен ли PHP использовать возможность всегда

; передавать аргументы функциям по ссылке при выполнении сценария.

; Этот метод устарел, и, скорее всего, он не будет

; поддерживаться в будущих версиях PHP/Zend.

; Описание того, каким способом должен быть передан аргумент —

; по ссылке или по значению — рекомендуется указывать при объявлении

; функции. Лучше всего, если вы попробуете установить параметр в Off

; и проверите, все ли сценарии по-прежнему работают. Если это так,

; то все в порядке, и сценарии будут совместимы и с будущими версиями

; PHP. В противном случае вы будете получать предупреждения каждый раз,

; когда аргументы передаются ненадлежащим образом и по значению там,



; где должны передаваться по ссылке.

allow_call_time_pass_reference=On

; Безопасный режим

safe_mode=Off

safe_mode_exec_dir=

; Установка некоторых переменных окружения может потенциально породить

; "дыры" в защите сценариев. Следующая директива содержит разделенный

; запятыми список префиксов. В режиме включенного безопасного режима

; пользователь сможет изменять только те переменные окружения, имена

; которых начинаются с перечисленных префиксов.

; По умолчанию пользователь имеет возможность устанавливать только

; переменные окружения, начинающиеся с PHP_ (например,

; PHP_FOO=something).

; Замечание: если эта директива пуста, PHP позволяет пользователям

; модифицировать любые переменные окружения!

safe_mode_allowed_env_vars=PHP_

; Следующая директива содержит разделенный запятыми список имен

; переменных окружения, которые конечный пользователь не сможет изменять

; путем вызова putenv().

; Эти переменные будут защищены даже в том случае, если директива

; разрешает их использовать.

safe_mode_protected_env_vars=LD_LIBRARY_PATH

; Эта директива позволяет вам запрещать вызовы некоторых функций

; из соображений безопасности. Список задается в виде имен функций,

; разграниченных запятыми. Директива действует независимо от того,

; установлен ли безопасный режим или нет!

disable_functions=

; Цвета для режима раскраски синтаксиса. Любой цвет, допустимый в тэге

; <font color=???>, допустим и здесь.

highlight.string=#DD0000

highlight.comment=#FF8000

highlight.keyword=#007700

highlight.bg=#FFFFFF

highlight.default=#0000BB

highlight.html=#000000

; Другие директивы

; Следующая директива указывает, должен ли PHP добавлять заголовок

; X-Powered-by в заголовки, посылаемые браузеру, и, таким образом,

; обнаруживать себя. Это никак не может повлиять на безопасность

; сценария, однако позволяет пользователю определить, использовался

; ли PHP для генерации страницы, или нет.

expose_php=On

;;;;;;;;;;;;;;;;;;;;;;;;



; Ограничения ресурсов ;

;;;;;;;;;;;;;;;;;;;;;;;;

; Максимальное возможное время выполнения сценария в секундах. Если

; сценарий будет выполняться дольше, PHP принудительно завершит его.

max_execution_time=30

; Максимальный объем памяти, выделяемый сценарию (8MB)

memory_limit=8M

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Обработка ошибок и подключений ;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Директива error_reporting должна задаваться в виде битового

; поля. Его значение можно устанавливать с помощью следующих констант,

; объединенных оператором | (OR):

; E_ALL              - Все предупреждения и ошибки.

; E_ERROR            - Критические ошибки времени выполнения.

; E_WARNING          - Предупреждения времени выполнения.

; E_PARSE            - Ошибки трансляции.

; E_NOTICE           - Замечания времени выполнения (это такие

;                      предупреждения, которые, скорее всего,

;                      свидетельствуют о логических ошибках в

;                      сценарии, — например, использовании

;                      неинициализированной переменной).

; E_CORE_ERROR       - Критические ошибки в момент старта PHP.

; E_CORE_WARNING     - Некритические предупреждения во время старта PHP.

; E_COMPILE_ERROR    - Критические ошибки времени трансляции.

; E_COMPILE_WARNING  - Предупреждения времени трансляции.

; E_USER_ERROR       - Сгенерированные пользователем ошибки.

; E_USER_WARNING     - Сгенерированные пользователем предупреждения.

; E_USER_NOTICE      - Сгенерированные пользователем замечания.

; Пример:

; показывать все ошибки, за исключением замечаний

; error_reporting = E_ALL & ~E_NOTICE

; показывать только сообщения об ошибках

; error_reporting=E_COMPILE_ERROR|E_ERROR|E_CORE_ERROR

; отображать все ошибки, предупреждения и замечания

error_reporting= E_ALL

; Печать ошибок и предупреждений прямо в браузер.

; Для готовых сайтов рекомендуется отключать следующую директиву и

; использовать вместо нее журнализацию (см. ниже). Включенная директива



; display_errors в "рабочих" сайтах может открыть доступ пользователю к

; секретной информации: например, полному пути к документу, используемой

; базе данных и т. д.

display_errors=On

; Даже если display_errors включена, ошибки, возникающие во время старта

; PHP, не отображаются. Рекомендуется устанавливать следующую директиву

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

; ее при отладке.

display_startup_errors=Off

; Сохранять ли сообщения об ошибках в файле журнала. Журнал может

; определяться настройками сервера, быть связанным с потоком stderr

; или же задаваться директивой error_log, описанной ниже. Как уже было

; сказано, в коммерческих проектах желательно использовать именно

; журнализацию, а не отображать ошибки в браузер.

log_errors=Off

; Сохранять ли последнее сообщение об ошибке или предупреждение в

; переменной $php_errormsg

track_errors=On

; Строка, которая выводится перед сообщением об ошибке.

;error_prepend_string="<font color=ff0000>"

; Строка, которая отображается после сообщения.

;error_append_string="</font>"

; Раскомментируйте, чтобы вести журнал в указанном файле.

;error_log=filename;

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

;error_log=syslog

; Предупреждать, когда оператор + применяется к строкам.

warn_plus_overloading=Off

;;;;;;;;;;;;;;;;;;;;

; Обработка данных ;

;;;;;;;;;;;;;;;;;;;;

; Замечание: track_vars всегда включена, начиная с PHP 4.0.3.

; Следующая директива определяет, в каком порядке PHP будет

; регистрировать данные, полученные методами GET, POST, а также

; переменные окружения и встроенные переменные (соответственно, значение

; задается буквами G, P, C, E и S, например, EGPCS или GPC). Регистрация

; производится на основе чтения этой строки слева направо, новые значения

; переопределяют старые.

variables_order="EGPCS"

; Должен ли PHP регистрировать EGPCS-переменные как глобальные

; переменные. Возможно, вы захотите отключить эту возможность, если не



; хотите "засорять" глобальную область видимости сценария. Это имеет

; смысл, если вы используете директиву track_vars — в этом случае вы

; можете получить доступ к GPC-данным через массив $HTTP_???_VARS.

; Желательно так писать сценарии, чтобы они по возможности

; старались обходиться без директивы register_globals. Использование

; данных, поступивших из формы, как глобальных переменных,

потенциально

; может породить проблемы в защите сценария, если программист не особенно

; позаботится об их устранении.

register_globals=On

; Следующая директива указывает PHP, обязан ли он создавать переменные

; $argv è $argc на основе информации, поступившей методом GET. Если вы не

; используете эти переменные, отключите директиву register_argc_argv

для

; небольшого убыстрения работы PHP.

register_argc_argv=On

; Максимальный размер данных POST, который PHP сможет принять.

post_max_size=8M

; Следующая директива устарела — используйте variables_order.

gpc_order="GPC"

; Автоматическая обработка кавычек и апострофов:

; использовать ли автокавычки для входящих GET/POST/Cookie äàííûõ

magic_quotes_gpc=Off

; заключать ли данные в автокавычки во время выполнения, например,

; для данных из SQL, exec() и т. д.

magic_quotes_runtime=Off

; Нужно ли PHP оформлять автокавычки в стиле Sybase-style (заменять '

; на '', à не на \')

magic_quotes_sybase=Off

; Следующие директивы указывают PHP, содержимое каких файлов он должен

; обрабатывать до и после вывода сценария.

auto_prepend_file=

auto_append_file=

; Начиная с версии 4.0b4, PHP всегда сообщает браузеру об используемой

; кодировке в заголовке Content-type. Для того чтобы запретить это,

; просто установите следующую директиву пустой. По умолчанию

; используется text/html без указания кодировки.

default_mimetype="text/html"

;default_charset="iso-8859-1"

;;;;;;;;;;;;;;;;;;;



; Пути и каталоги ;

;;;;;;;;;;;;;;;;;;;

; Для UNIX: "/path1:/path2".

; Для Windows: "\path1;\path2"

include_path=

; Корневой каталог для PHP-сценариев.

; Игнорируется, если значение равно пустому "".

doc_root=

; Каталог, который PHP использует при открытии сценария вида

; /~username. Не оказывает действия, если значение равно "".

user_dir=

; Каталог, в котором хранятся динамически загружаемые расширения.

extension_dir=C:/Program Files/PHP4/extensions

; Следующая директива разрешает или запрещает использование функции dl().

; Функция dl() работает неправильно

в многопоточных Web-серверах,

; например, в IIS или Zeus, и автоматически отключается для них.

enable_dl=On

;;;;;;;;;;;;;;;;;;

; Закачка файлов ;

;;;;;;;;;;;;;;;;;;

; Разрешает PHP обрабатывать закачку файлов

file_uploads=On

; Каталог для временных файлов, в который PHP помещает закачанные

; файлы (используется системный временный каталог, если в директиве

; указана пустая строка)

;upload_tmp_dir=

; Максимальный размер закачанного файла

upload_max_filesize=2M

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Динамически загружаемые расширения ;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Если вы хотите, чтобы какие-то модули загружались автоматически,

; задавайте директиву extension в формате:

; extension=modulename.extension

; Например, для Windows:

; extension=msql.dll

; или для UNIX:

; extension=msql.so

; Должно быть указано только имя, без пути. Чтобы задать

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

; extension_dir, описанную выше.

; Модули для Windows

; Замечание: поддержка MySQL и ODBC теперь включена в ядро PHP, так что

; для нее уже не нужны никакие библиотеки DLL.

;extension=php_cpdf.dll

;extension=php_cybercash.dll

;extension=php_db.dll

;extension=php_dbase.dll

;extension=php_domxml.dll

;extension=php_dotnet.dll

;extension=php_exif.dll

;extension=php_fdf.dll

extension=php_gd.dll



;extension=php_gettext.dll

;extension=php_ifx.dll

;extension=php_imap.dll

;extension=php_interbase.dll

;extension=php_java.dll

;extension=php_ldap.dll

;extension=php_mhash.dll

;extension=php_mssql65.dll

;extension=php_mssql70.dll

;extension=php_oci8.dll

;extension=php_oracle.dll

;extension=php_pdf.dll

;extension=php_pgsql.dll

;extension=php_sablot.dll

;extension=php_swf.dll

;extension=php_sybase_ct.dll

;extension=php_zlib.dll

;;;;;;;;;;;;;;;;;;;;;;;;;

; Установки для модулей ;

;;;;;;;;;;;;;;;;;;;;;;;;;

[Syslog]

; Нужно или нет определять различные переменные Syslog, такие как

; $LOG_PID, $LOG_CRON и т. д. Для ускорения работы рекомендуется

; выключать следующую директиву. Во время выполнения сценария вы

; можете включить или выключить директиву путем вызова

; функции define_syslog_variables().

define_syslog_variables=Off

[mail function]

; Только для Win32 — используемый SMTP-сервер.

SMTP=mail.dklab.ru

; Только для Win32 — поле From: по умолчанию.

sendmail_from= dk@dklab.ru

; Только для UNIX — задает путь и аргументы программы sendmail (по

; умолчанию — 'sendmail -t -i').

;sendmail_path=

[Debugger]

debugger.host=localhost

debugger.port=7869

debugger.enabled=False

[Logging]

; Следующие директивы используются сценарием-примером.

; При потребности в детальном описании см. examples/README.logging.

;logging.method=db

;logging.directory=/path/to/log/directory

[Java]

;java.class.path=.\php_java.jar

;java.home=c:\jdk

;java.library=c:\jdk\jre\bin\hotspot\jvm.dll

;java.library.path=.\

[SQL]

sql.safe_mode=Off

[ODBC]

;uodbc.default_db=Not yet implemented

;uodbc.default_user=Not yet implemented

;uodbc.default_pw=Not yet implemented

; Разрешает или запрещает устойчивые соединения

uodbc.allow_persistent=On

; Проверка доступности соединения перед его использованием.

uodbc.check_persistent=On

; Макс. число устойчивых соединений. -1 означает, что ограничений нет.



uodbc.max_persistent=-1

; Макс. число соединений (устойчивых + неустойчивых).

uodbc.max_links=-1

; Установки для LONG-полей.

uodbc.defaultlrl=4096

; Установки для бинарных данных. 0 означает режим passthru, 1 – режим

; as is, 2 – преобразование в символы.

uodbc.defaultbinmode=1

; См. документацию по odbc_binmode и odbc_longreadlen для более

; детального разъяснения смысла директив uodbc.defaultlrl

и

; uodbc.defaultbinmode.

[MySQL]

mysql.allow_persistent=On

mysql.max_persistent=-1

mysql.max_links=-1

; Порт по умолчанию для функции mysql_connect(). Если не задан, функция

; попытается использовать переменную $MYSQL_TCP_PORT или запись mysql-tcp

; в /etc/services, а также заданную во время компиляции PHP константу

; MYSQL_PORT (именно в таком порядке). К PHP для Win32 применимо только

; последнее.

mysql.default_port=

; Определяет имя сокета для локальных соединений MySQL. Если он не задан,

; использует встроенное значение по умолчанию.

mysql.default_socket=

; Хост по умолчанию для mysql_connect() (не работает в безопасном режиме).

mysql.default_host=

; Пользователь по умолчанию (не работает в безопасном режиме).

mysql.default_user=

; Пароль по умолчанию (не работает в безопасном режиме).

; Замечание: идея хранить пароль в этом файле просто отвратительна. Любой

; пользователь, который может запускать PHP, сможет узнать пароль путем

; выполнения:

; echo cfg_get_var("mysql.default_password")

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

; на чтение для файла php.ini.

mysql.default_password=

[mSQL]

msql.allow_persistent=On

msql.max_persistent=-1

msql.max_links=-1

[PostgresSQL]

pgsql.allow_persistent=On

pgsql.max_persistent=-1

pgsql.max_links=-1

[Sybase]

sybase.allow_persistent=On

sybase.max_persistent=-1

sybase.max_links=-1

;sybase.interface_file="/usr/sybase/interfaces"

; Максимальный уровень серьезности отображаемых ошибок.

sybase.min_error_severity=10



; Минимальный уровень серьезности отображаемых ошибок.

sybase.min_message_severity=10

; Режим совместимости со старыми версиями PHP 3.0.

; Если следующая директива установлена в On, PHP будет автоматически

; присваивать тип результату на основе его типа в Sybase, вместо того,

; чтобы преобразовывать полученные значения в строки. Этот режим

; совместимости, возможно, в будущем не будет поддерживаться, так что

; лучше исправьте свои сценарии, если вам он нужен.

sybase.compatability_mode=Off

[Sybase-CT]

sybct.allow_persistent=On

sybct.max_persistent=-1

sybct.max_links=-1

sybct.min_server_severity=10

sybct.min_client_severity=10

[bcmath]

; Число десятичных цифр для всех bcmath-функций.

bcmath.scale=0

[browscap]

;browscap=extra/browscap.ini

[Informix]

ifx.default_host=

ifx.default_user=

ifx.default_password=

ifx.allow_persistent=On

ifx.max_persistent=-1

ifx.max_links=-1

; Если следующая директива установлена в On, выражение select возвращает

; содержимое поля типа text blob вместо его идентификатора.

ifx.textasvarchar=0

; Заставляет команду select возвращать значение поля типа byte blob

; вместо его идентификатора.

ifx.byteasvarchar=0

; Принуждает PHP удалять завершающие пробелы из колонок с типом char

; фиксированного размера. Может помочь пользователям Informix SE.

ifx.charasvarchar=0

; Если установлена, содержимое полей text и byte сохраняется в файле,

; вместо того, чтобы храниться в памяти.

ifx.blobinfile=0

; Если установлена в 0, значения NULL возвращаются как пустые строки,

; иначе они возвращаются как строки 'NULL'.

ifx.nullformat=0

[Session]

; Определяет режим хранения данных сессий.

session.save_handler=files  

; Следующая директива задает аргумент, передаваемый save_handler-у.

; В случае режима сохранения в файлах здесь должен указываться каталог,

; в который будут помещены файлы сессий.

session.save_path=C:\Program Files\PHP4\sessiondata

; Должен ли PHP использовать Cookies.

session.use_cookies=1



session.name=PHPSESSID 

; Инициализировать ли сессии при старте.

session.auto_start=0      

; Время жизни Cookie для сессии. Если до закрытия браузера, то 0.

session.cookie_lifetime=0      

; Путь для Cookie с идентификатором сессии.

session.cookie_path=/

; Домен для Cookie с идентификатором сессии.

session.cookie_domain=

; Функция, используемая для сериализации данных. Значение php задает

; стандартную функцию.

session.serialize_handler=php

; Вероятность того, что при очередном запуске сценария, работающего с

; сессиями, будет вызвана функция "сборки мусора" для очистки сессий,

; которые пользователь уже покинул.

session.gc_probability=1

; После указанного здесь промежутка времени сохраненные

; данные будут удалены автоматически сборщиком мусора.

session.gc_maxlifetime=1440

; Проверять ли HTTP Referer на предмет того, не является ли ID сессии

; "фальшивым".

session.referer_check=

; Указывает, сколько байтов читать из файла.

session.entropy_length=0      

;session.entropy_length=16

; Файл, используемый для генерации идентификаторов сессии.

session.entropy_file=

;session.entropy_file=/dev/urandom

; Установите одно из значений nocache, private, public для определения

; аспектов кэширования HTTP.

session.cache_limiter=nocache

; Документ будет считаться устаревшим по истечении заданного

; здесь количества минут

session.cache_expire=180

; Использовать ли поддержку "переходящих" SID. Действует, если PHP был

; скомпилирован с включенной опцией --enable-trans-sid.

session.use_trans_sid=1      

[MSSQL]

;extension=php_mssql.dll

mssql.allow_persistent=On

mssql.max_persistent=-1

mssql.max_links=-1

mssql.min_error_severity=10

mssql.min_message_severity=10

; Режим совместимости со старыми версиями PHP 3.0.

mssql.compatability_mode=Off

[Assertion]

;assert.active=On

; Генерирует предупреждения PHP для каждых неудавшихся проверок

; выражений.

;assert.warning=On

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



;assert.bail=Off

; Пользовательская функция, которая будет вызвана при неудаче проверки.

;assert.callback=0

; Вычислять выражения в eval с использованием текущих установок

; error_reporting. Установите в true, если вы хотите, чтобы действие

; режима error_reporting(0) было сохранено и при переходе через

; границу eval().

;assert.quiet_eval=0

[Ingres II]

ingres.allow_persistent=On

ingres.max_persistent=-1

ingres.max_links=-1

; База данных по умолчанию (формат: [node_id::]dbname[/srv_class]

ingres.default_database=

ingres.default_user=

ingres.default_password=

[Verisign Payflow Pro]

pfpro.defaulthost="test.signio.com"

pfpro.defaultport=443

pfpro.defaulttimeout=30

; IP-адрес proxy-сервера по умолчанию (если требуется).

; pfpro.proxyaddress=

; Порт proxy-сервера по умолчанию

; pfpro.proxyport=

; Логин для proxy-сервера по умолчанию

; pfpro.proxylogon=

; Пароль для proxy-сервера по умолчанию

; pfpro.proxypassword=






Фильтры блоков


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

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

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



Финализаторы


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

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

int Register_shutdown_function(string $func)

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

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

Правда, есть одно "но". Финальная функция вызывается уже после закрытия соединения с браузером клиента. Поэтому все данные, выведенные в ней через echo, теряются (во всяком случае, так происходит в Unix-версии PHP, а под Windows CGI-версия PHP и echo работают прекрасно). Так что лучше не выводить никаких данных в такой функции, а ограничиться работой с файлами и другими вызовами, которые ничего не направляют в браузер.

Последнее обстоятельство, к сожалению, ограничивает функциональность финализаторов: им нельзя поручить, например, вывод окончания страницы, если сценарий по каким-то причинам прервался из-за ошибки. Вообще говоря, надо заметить, что в PHP никак нельзя в случае ошибки в некотором запущенном коде проделать какие-либо разумные действия (кроме, разумеется, мгновенного выхода). Это несколько может ограничивать область применимости PHP для написания шаблонизатора (о шаблонах будет подробно рассказано в части V этой книги).[E125] [DK126] 



Формат данных


В свое время я говорил, что все данные из формы при передаче их на сервер упаковываются в строку при помощи символов ?, & и =. Легко видеть, что при загрузке файлов такой способ, хотя и приемлем, но будет существенно увеличивать размер передаваемой информации. Действительно, ведь большинство файлов— бинарные, а мы знаем, что при URL-кодировании данные таких файлов сильно "распухают" — примерно в три раза (например, простой нулевой байт при URL?кодировании превратится в %00). Это сильно замедлит передачу и увеличит нагрузку на канал. И вот, отчасти специально для решения указанной проблемы был изобретен другой формат передачи данных, отличный от того, который мы до сих пор рассматривали.

В нем уже не используются пресловутые символы ? и &. Кроме того, похоже, в случае применения такого формата передачи может быть задействован только метод POST, но не метод GET. Нас это вполне устроит — ведь файлы обычно большие, и доставлять их через GET вряд ли разумно...

Если нужно указать браузеру, что в какой-то форме следует применять другой формат передачи, следует в соответствующем тэге <form> задать атрибут enctype=multipart/form-data. (Кстати говоря, если этот атрибут не указан, то форма считается обычной, что эквивалентно enctype=application/x-www-form-urlencoded — именно так обозначается привычный нам формат передачи.) После этого данные, поступившие от нашей формы, будут выглядеть как несколько блоков информации (по одному на элемент формы). Каждый такой блок очень напоминает HTTP-формат "заголовки-данные", используемый при традиционном формате передачи. Выглядит блок примерно так (\n, как всегда, обозначает символ перевода строки):

-----------------Èäåíòèôèêàòîð_íà÷àëà\n

Content-Disposition: form-data; name="èìÿ"\n

\n

çíà÷åíèå\n


Например, пусть у нас есть форма:

Листинг 3.7. Multipart-форма

<form action=... enctype=multipart/form-data method=post>

Name: <input type=text name="Name" value="Ìîå èìÿ"><br>

Box: <input type=checkbox name="Box" value=1 checked><br>

Area: <input type=textarea name="Area">Ýòî êàêîé-òî òåêñò</textarea><br>

<input type=submit>

</form>

Данные, поступившие по нажатии кнопки submit на сервер, будут иметь

следующий вид:

----------------127462537625367\n

Content-Disposition: form-data; name="Name"\n

\n

Ìîå èìÿ\n

----------------127462537625367\n

Content-Disposition: form-data; name="Box"\n

\n

1\n

----------------127462537625367\n

Content-Disposition: form-data; name="Area"\n

\n

Ýòî êàêîé-òî òåêñò\n

Заметьте, что несколько дефисов и число (которое мы ранее назвали

Идентификатор_начала) предшествуют каждому блоку. Более того, строка из дефисов и этого числа служит своеобразным маркером, который разделяет блоки. Очевидно, эта строка должна быть уникальной во всех данных. Именно так ее и формирует браузер. Правда, сказанное означает, что сегодня идентификатор будет одним, а завтра, возможно, совсем другим. Так что нам придется, прежде чем анализировать данные, считать этот идентификатор в буфер (им будет последовательность символов до первого символа \n).



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

Далее алгоритм разбора должен быть следующим: в цикле мы пропускаем символы идентификатора и перевода строки, извлекаем подстроку имя="что-то"

(не обращая внимания на Content-Disposition), дожидаемся двух символов перевода строки и затем считаем значением соответствующего поля все те данные, которые размещены до строки \nИдентификатор

(или же до конца, если такой строки больше нет). Как видите, все довольно просто.



Стандарт HTTP предписывает, чтобы перевод строки содержал два символа — \r\n, а не один \n. Как вы уже, наверное, чувствуете, существуют браузеры, которые об этом и не догадываются и посылают только один \n. Так что, будьте готовы к тому, чтобы правильно обрабатывать и эту ситуацию.


Функции для преобразований символов


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

string strtr(string $str, string $from, string $to)

Эта функция применяется не столь широко, но все-таки иногда она бывает довольно полезной. Делает она вот что: в строке $str заменяет все символы, встречающиеся в $from, на их "парные"

(то есть расположенные в тех же позициях, что и во $from) из $to. Функция работает существенно быстрее, чем ereg_replace(), которую мы рассмотрим в главе, посвященной регулярным выражениям. Правде, она имеет вместе с тем несколько меньшую функциональность...

Следующие несколько функций предназначены для быстрого URL-кодирования и декодирования.

string UrlEncode(string $st)

Функция URL-кодирует строку $st и возвращает результат. Эту функцию удобно применять, если вы, например, хотите динамически сформировать ссылку <a href=...> на какой-то сценарий, но не уверены, что его параметры содержат только алфавитно-цифровые символы. В этом случае воспользуйтесь функцией так:

echo "<a href=/script.php?param=".UrlEncode($UserData);

Теперь, даже если переменная $UserData

включает символы =, &

или даже пробелы, все равно сценарию будут переданы корректные данные.

string UrlDecode(string $st)

Производит URL-декодирование строки. В принципе, используется значительно реже, чем UrlEncode(), потому что PHP и так умеет перекодировать входные данные автоматически.

string RawUrlEncode(string $st)

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


string RawUrlDecode(string $st)

Аналогична UrlDecode(), но не воспринимает + как пробел.

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

string HtmlSpecialChars(string $str)

Заменяет в строке некоторые символы (такие как амперсант[E53] , кавычки и знаки "больше" и "меньше") на их HTML-эквиваленты, так, чтобы они выглядели на странице "самими собой". Самое типичное применение этой функции — формирование параметра value

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

<?foreach($Book as $k=>$v) {?>

  Èìÿ: <?=$v['name']?><br>

  Òåêñò: <?=HtmlSpecialChars($v['text'])?>

  <hr>

<?}?>

Используя этот незамысловатый прием, вы гарантированно избавите себя от проблем с запретом тэгов.



Начинающие Web-программисты для решения задачи запрета тэгов часто пытаются просто удалить их из строки — например, применив функцию strip_tags(). Это метод довольно плох, потому что всегда существует вероятность того, что злоумышленник сможет "обмануть" эту функцию. Конечно, еще хуже метод с применением регулярных выражений, потому что, как известно, с их помощью вовсе невозможно выделить некоторые тэги из строки — например, тэги такого вида: <a name='a>b'>.

string StripSlashes(string $st)

Заменяет в строке $st íåêîòîðûå предваренные слэшем символы на их однокодовые эквиваленты. Это относится к следующим символам: ", ', \ и никаким другим.

string AddSlashes(string $st)

Вставляет слэши только перед следующими символами: ', "

и \. Функцию очень удобно использовать при вызове eval() (эта функция выполняет строку, переданную ей в параметрах, так, как будто имеет дело с небольшой PHP-программой; о ней (функции) мы еще поговорим, и при том очень подробно).


Функции для работы с DNS


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



Функции для работы с одиночными символами


string chr(int $code)

Возвращает строку из одного символа с кодом $code. Эта функция полезна для вставки каких-либо непечатаемых символов в строку — например, кода нуля или символа прогона страницы, а также при работе с бинарными файлами. Пример из листинга 12.2 позволяет вам просмотреть, какие коды соответствуют всем символам, которые можно отобразить в браузере. Иногда эта программа оказывается очень полезной.

Листинг 12.2. Программа: печать всей таблицы символов

<?

// Сначала создаем массив того, что мы собираемся выводить,

// не заботясь о форматировании (дизайне) информации

for($i=0,$x=0; $x<16; $x++) {

  for($y=0; $y<16; $y++) {

    $Chars[$x][$y]=array($i,chr($i));

    $i++;

  }

}

// Теперь выводим накопленную информацию, используя идеологию

// вставки участков кода в HTML-документ

?>

<table border=1 cellpadding=1 cellspacing=0>

<?for($y=0; $y<16; $y++) {?>

  <tr>

  <?for($x=0; $x<16; $x++) { ?>

    <td>

      <?=$Chars[$x][$y][0]?>:  

      <b><tt><?=$Chars[$x][$y][1]?></tt></b>

    </td>

  <?}?>

  </tr>

<?}?>

</table>

?>

int ord(char $ch)

Эта функция, наоборот, возвращает код символа в $ch. Например, ord(chr($n)) всегда равно $n — конечно, если $n заключено между нулем и числом 255.

int strrpos(string $where, char $what)

Данная функция, хотя и похожа внешне на strpos() (см. ниже), несет несколько иную нагрузку. Она ищет в строке $where последнюю позицию, в которой встречается символ $what (если $what — строка из нескольких символов, то выявляется только первый из них, остальные не играют никакой роли — обратите на это особое внимание!). В случае, если искомый символ не найден, возвращается false

(см. замечание по этому поводу для strpos()). Вообще, могу сказать, что функция strrpos() применяется очень

редко. Слишком уж она не универсальна.