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


Конструкции языка


Ну вот мы и подобрались к языковым конструкциям. Некоторые из них нами уже применялись, и не раз — например, инструкция if. В этой главе приводится полное описание всех языковых конструкций PHP. Их не так много, и это достоинство PHP [AL1] . Как показывает практика, чем более лаконичен синтаксис языка, тем проще его использовать в повседневной практике. PHP — отличный пример этому.

О терминологии

Иногда я применяю слово "конструкция", а иногда — "инструкция". В данной книге эти два термина совершенно эквивалентны. Наоборот, термины "опе­ратор" и "операция" несут разную смысловую нагрузку: любая операция есть оператор, но не наоборот. Например, echo — оператор, но не операция, а ++ — операция.

Инструкция if-else

Начнем с самой простой инструкции — условного оператора. Его формат таков:

if(ëîãè÷åñêîå_âûðàæåíèå)

  èíñòðóêöèÿ_1;

else

  èíñòðóêöèÿ_2;

Действие его следующее: если логическое_выражение истинно, то выполняется инструкция_1, а иначе — инструкция_2. Как и в любом другом языке, конструкция else может опускаться,

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

Пример:

if($a>=1&&$b<=10) echo "Âñå OK";



  else echo "Íåâåðíîå çíà÷åíèå â ïåðåìåííîé!";

Если инструкция_1 или инструкция_2 должны состоять из нескольких ко­манд, то они, как всегда, заключаются в фигурные скобки. Напри­мер:

if($a>$b) { print "a áîëüøå b"; c=$b; }

elseif($a==$b) { print "a ðàâíî b"; $c=$a; }


else { print "a ìåíüøå b"; $c=$a; }

Это не опечатка[AL2] : elseif слитно, вместо else if. Так тоже можно писать, хотя это, по-моему, и не удобочитаемо.

Конструкция if-else имеет еще один альтернативный синтаксис:

if(ëîãè÷åñêîå_âûðàæåíèå):

  êîìàíäû;

elseif(äðóãîå_ëîãè÷åñêîå_âûðàæåíèå):

  äðóãèå_êîìàíäû;

else:

  èíà÷å_êîìàíäû;

endif

Обратите внимание на расположение двоеточия (:)! Если его про­пустить, будет сгенерировано сообщение об ошибке. И еще: как обычно, блоки elseif и else можно опускать.

Использование альтернативного

синтаксиса


В предыдущих главах нами уже неоднократно рассматривался пример вставки HTML-кода в тело сценария. Для этого достаточно было просто закрыть скобку ?>, написать этот код, а затем снова открыть ее при помощи <?, и продол­жать программу.

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

if-else и других конструкций языка.

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

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



Вот, например, как будет выглядеть наш старый знакомый сценарий, который приветствует пользователя по имени, с использованием альтернативного синтаксиса if-else (листинг 9.1):

Листинг 9.1. Альтернативный синтаксис if-else

<?if(@$go):?>

  Ïðèâåò, <?=$name?>!

<?else:?>

  <form action=<?=$REQUEST_URI?> method=post>

  Âàøå èìÿ: <input type=text name=name><br>

  <input type=submit name=go value="Îòîñëàòü!">

<?endif?>

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

Цикл с предусловием while

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

while(ëîãè÷åñêîå_âûðàæåíèå)

  èíñòðóêöèÿ;

где, как обычно, логическое_выражение — логическое выражение, а

инструкция — простая или составная инструкция тела цикла. (Очевидно, что внутри последнего должны производиться какие-то манипуляции, которые будут иногда изменять значение нашего выражения, иначе оператор зациклится. Это может быть, например, простое увеличение некоего счетчика, участвующего в выражении, на единицу.) Если выражение с самого начала ложно, то цикл не выполнится ни разу. Например:

$i=1; $p=1;

while($i<32) {

  echo $p," ";

  $p=$p*2; // ìîæíî áûëî áû íàïèñàòü $p*=2



  $i=$i+1; // ìîæíî áûëî áû íàïèñàòü $i+=1 èëè äàæå $i++

}

Данный пример выводит все степени двойки до 31-й включительно.

Как и инструкция if, цикл while имеет альтернативный синтаксис, что упрощает его применение вперемешку с HTML-кодом:

while(ëîãè÷åñêîå_âûðàæåíèå):

  êîìàíäû;

endwhile;

Цикл с постусловием do-while

В отличие от цикла while, этот цикл проверяет значение выражения не до, а после

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

do {

  êîìàíäû;

} while(ëîãè÷åñêîå_âûðàæåíèå);

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

Альтернативного синтаксиса для do-while разработчики PHP не предусмотрели (видимо, из-за того, что, в отличие от прикладного программирования, этот цикл довольно редко используется при программировании сценариев).

Универсальный цикл for

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

не такие тривиальные, как простая переборка значения счетчика (а именно для этого используется for в Паска­ле и чаще всего в Си). Формат конструкции такой:

for(èíèöèàëèçèðóþùèå_êîìàíäû; óñëîâèå_öèêëà; êîìàíäû_ïîñëå_ïðîõîäà)



  òåëî_öèêëà;

Работает он следующим образом. Как только управление доходит до цикла, первым делом выполняются операторы, включенные в инициализирующие_команды (слева направо). Эти команды перечисляются там через запятую, например:

for($i=0,$j=10,$k="Test!; ......)

Затем начинается итерация. Первым делом проверяется, выполняется ли условие_цикла (как в конструкции while). Если да, то все в порядке, и цикл продолжается. Иначе осуществляется выход из конструкции. Например:

// ïðèáàâëÿåì ïî îäíîé òî÷êå

for($i=0,$j=0,$k="Test"; $i<10; .....) $k.=".";

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

(их формат тот же, что и у ини­циализирующих операторов). Например:

for($i=0,$j=0,$k="Points"; $i<100; $j++,$i+=$j) $k=$k.".";

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

$i=0; $j=0; $k="Points";

while($i<100) {

  $k.=".";

  $j++; $i+=$j;

}

Вот, собственно говоря, и все... Хотя нет. Попробуйте угадать: сколько точек добавится в конец переменной $k после выполнения цикла?

Как обычно, имеется и альтернативный синтаксис конструкции:

for(èíèöèàëèçèðóþùèå_êîìàíäû; óñëîâèå_öèêëà; êîìàíäû_ïîñëå_ïðîõîäà):

  îïåðàòîðû;

endfor;

Инструкции break

и continue




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

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

  for($j=0; $j<10; $j++) {

    If($A[$i]==$A[$j]) break(2);

  }

}

if($i<10) echo 'Íàéäåíû ñîâïàäàþùèå ýëåìåíòû â ìàòðèöå \$A!';

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



Применение такой формы записи break — новинка PHP версии 4. Честно говоря, я не встречал ни одного другого языка, который бы использовал подобный (на мой взгляд, крайне удачный) синтаксис. Спасибо вам, разработчики PHP!

Инструкцию break удобно использовать для циклов поисков: как только очередная итерация цикла удовлетворяет поисковому условию, поиск обрывается. Например, вот цикл, который ищет в массиве $A первый нулевой элемент:

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

  if($A[$i]==0) break;

if($i<count($A)) echo "Íóëåâîé ýëåìåíò íàéäåí: i=$i";

Стандартная функция count(), которую мы еще не рассматривали, просто возвращает число элементов в массиве $A.

Инструкция continue так же, как и break, работает только "в паре" с циклическими конструкциями. Она немедленно завершает текущую итера­цию цикла и переходит к новой (конечно, если выполняется условие цикла для цикла с предусловием). Точно так же, как и для break, для continue можно указать уровень вложенности цикла, который будет продолжен по возврату управления.



В основном continue позволяет вам сэконо­мить количество фигурных скобок в коде и увеличить его удобочитаемость. Это чаще всего бывает нужно в циклах-фильтрах, когда требуется перебрать некоторое количество объектов и выбрать из них только те, которые удовлетворяют определенным условиям. Например, вот цикл, который обнуляет те элементы массива $A, которые удовлетворяют нескольким условиям:

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

  if(!óñëîâèå1($A[$i])) continue;

  . . .

  if(!óñëîâèåN($A[$i])) continue;

  $A[$i]=0;

}



Грамотное использование break

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

Нетрадиционное использование

do-while и break


Есть один интересный побочный эффект, который дает нам инструкция break, и который довольно удобно использовать для обхода "лишних" опе­раторов (кстати, его можно применять и в Си). Необходимость такого об­хода возникает довольно часто, причем именно при программировании сценариев. Рассмотрим соответствующий пример (листинг 9.2):

Листинг 9.2. Модель сценария для обработки формы

. . .

$WasError=0; // èíäèêàòîð îøèáêè — åñëè íå 0, òî áûëà îøèáêà

// Åñëè íàæàëè êíîïêó Submit (ñ èìåíåì $doSubmit)...

if(@$doSubmit) do {

  // Ïðîâåðêà âõîäíûõ äàííûõ



  if(íåïðàâèëüíîå èìÿ ïîëüçîâàòåëÿ) { $WasError=1; break; }

  . . . è ò. ä.

  if(íåïðàâèëüíûå äàííûå) { $WasError=1; break; }

  . . . è ò. ä.

  // Äàííûå â ïîðÿäêå. Îáðàáàòûâàåì èõ.

  âûïîëíÿåì äåéñòâèÿ;

  âûâîäèì ðåçóëüòàò;

  çàâåðøàåì ñöåíàðèé;

} while(0);

. . .

Âûâîäèì ôîðìó, ÷åðåç êîòîðóþ ïîëüçîâàòåëü áóäåò çàïóñêàòü ýòîò ñöåíàðèé, è, âîçìîæíî, îòîáðàæàåì ñîîáùåíèå îá îøèáêå â ñëó÷àå, åñëè $WasError!=0.

Здесь представлен наиболее обычный способ для организации сценариев-диалогов. Запустив сценарий без параметров, пользователь видит форму с приглашением ввести свое имя, пароль и некоторые другие дан­ные. При нажатии кнопки запускается тот же самый сценарий, который опре­деляет, что была нажата кнопка doSubmit, и первым делом проверяет имя и пароль. Если они заданы неверно, то отображается опять наша форма (и где-нибудь красным цветом сообщение об ошибке), в противном случае сценарий завершается и выдает страницу с результатом.



Мы видим, что указанный алгоритм можно реализо­вать наиболее удобно, имея какой-то способ обрывания блока "проверки-и-завершения"

и возврата к выводу формы заново. Как раз это и делает конструкция

if(÷òî_òî) do { ... } while(0);

Очевидно, что тело цикла do-while выполняется в любом случае только один раз (так как выражение в while всегда ложно). Тем не менее, такой "вырожденный"

цикл мы можем использовать для быстрого выхода из него посредством break.

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

Цикл foreach

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

foreach(ìàññèâ as $key=>$value)

  êîìàíäû;

Здесь команды

циклически выполняются для каждого элемента массива, при этом очередная пара ключ=>значение

оказывается в переменных $key и $value. Давайте рассмотрим пример (листинг 9.3), где покажем, как мы можем отобразить содержимое всех глобальных переменных при помощи foreach:

Листинг 9.3. Вывод всех глобальных переменных

<?

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

  echo "<b>$k</b> => <tt>$v</tt><br>\n";

?>

У цикла foreach имеется и другая форма записи, которую следует применять, когда нас не интересует значение ключа очередного элемента. Выглядит она так:

foreach(ìàññèâ as $value)

  êîìàíäû;

В этом случае доступно лишь значение

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





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

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

Конструкция switch-case

Часто вместо нескольких расположенных подряд инструкций if-else целесо­образно воспользоваться специальной конструкцией switch-case:

switch(âûðàæåíèå) {

  case çíà÷åíèå1: êîìàíäû1; [break;]

  case çíà÷åíèå2: êîìàíäû2; [break;]

  . . .

  case çíà÷åíèåN: êîìàíäûN; [break;]

  [default: êîìàíäû_ïî_óìîë÷àíèþ; [break]]

}

Делает она следующее: вычисляет значение выражения (пусть оно равно,

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

Обратите внимание на операторы break (которые условно заключены в квадратные скобки, чтобы подчеркнуть их необязательность), добавленные после каждой строки команд, кроме последней (для которой можно бы­ло бы тоже указать break, что не имело бы смысла). Если бы не они, то при равенстве V=значение1 сработали бы не только команды1, но и все нижележащие.



Вот альтернативный синтаксис для конструкции switch-case:

switch(âûðàæåíèå):

  case çíà÷åíèå1: êîìàíäû1; [break;]

  . . .

  case çíà÷åíèåN: êîìàíäûN; [break;]

  [default: êîìàíäû_ïî_óìîë÷àíèþ; [break]]

endswitch;

Инструкция require

Эта инструкция позволяет нам разбить текст программы на несколько файлов. Ее формат такой:

require èìÿ_ôàéëà;

При запуске (именно при запуске, а не при исполнении!) программы интерпретатор просто заменит инструкцию на содержимое файла имя_файла (этот файл может также содержать сценарий на PHP, обрамленный, как обыч­но, тэгами <? и ?>). Причем сделает он это только один раз (в от­личие от include, который рассматривается ниже): а именно, непосредс­твенно перед запуском программы. Это бывает довольно удобно для вклю­чения в вывод сценария всяких "шапок"

с HTML-кодом. Например (листинги 9.4, 9.5 и 9.6):

Листинг 9.4. Файл header.htm

<html>

<head><title>Title!</title></head>

<body bgcolor=yellow>

Листинг 9.5. Файл footer.htm

&copy;My company, 1999.

</body></html>

Листинг 9.6. Файл script.php

<?

require "header.htm";

. . . ðàáîòàåò ñöåíàðèé è âûâîäèò ñàìî òåëî äîêóìåíòà

require "footer.htm";

?>

Безусловно, это лучше, чем включать весь HTML-код в сам сценарий вместе с инструкциями программы. Вам скажет спасибо тот, кто будет пользоваться вашей программой и захочет изменить ее внешний вид. Од­нако, несмотря на кажущееся удобство, это все же плохая практика. Дейс­твительно, наш сценарий разрастается аж до трех файлов! А как было ска­зано выше, чем меньше файлов использует программа, тем легче с ней бу­дет работать вашему дизайнеру и верстальщику (которые о PHP имеют слабое представление). О том, как же быть в этой ситуации, я расскажу позже в пятой части книги, в гла­ве, посвященной технике разделения кода и шаблонов.



Инструкция include

Эта инструкция практически идентична require, за исключением того, что вклю­чаемый файл вставляется "в сердце"

нашего сценария не перед его выпол­нением, а прямо во время.

Какая разница? Поясню. Пусть у нас есть 10 текстовых файлов с именами file0.php, file1.php и так далее до file9.php, содержимое которых просто десятичные цифры 0, 1 ...… 9 (по одной цифре в каждом файле). Запустим такую програм­му:

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

  include "file$i.php";

}

В результате мы получим вывод[E3] , состоящий из 10 цифр: "0123456789". Из этого мы можем заключить, что каждый из наших файлов был включен по одному разу прямо во время выполнения цикла! (Попробуй­те теперь вместо include подставить require. Сравните результат.)

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

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

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

А вдруг в этом файле несколько команд? Тогда в цикле выполнится только первая из них, а остальные будут запущены уже после

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



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

Трансляция и проблемы с include

Как мы знаем, перед исполнением PHP транслирует программу во внутреннее представление. Это означает, что в памяти создается как бы "полу­фабрикат", из которого исключены все комментарии, лишние пробелы, некоторые имена переменных и т. д. Впоследствии это внутреннее представление интерпретируется (выполняется). Однако мы знаем также, что в программе могут встретиться такие места, "подводные камни"



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

Одним из таких "камней" как раз и является инструкция include. Как только управление программы доходит до нее, PHP вынужден приостановиться и ждать, пока транслятор не оттранслирует код включаемого файла. А это достаточно отрицательно сказывается на быстродействии программы, особенно большой. Поэтому, если вы пишете большой и сложный сценарий, применяйте инструкцию require вместо include, где только можно.

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

Что же оптимальнее — require или include? Если вы точно уве­рены, что определенный файл нужно присоединить ровно один раз и в точно определенное место, то воспользуйтесь require. В противном случае более удачным выбором будет include.

Инструкции однократного включения

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

Чтобы стало яснее, я расскажу вам притчу. Как-то раз разработчик Билл написал несколько очень полезных функций для работы с файлами Excel и решил объединить их в библиотеку — файл xllib.php (листинг 9.7):

Листинг 9.7. Библиотека xllib.php

<?

Function LoadXlDocument($filename) { . . . }

Function SaveXlDocument($filename,$doc) { . . . }

?>

Разработчик Вася захотел сделать то же самое для работы с документами Microsoft Word, в результате чего на свет явилась библиотека wlib.php. Так как Word и Excel связаны между собой, Вася использует в своей библиотеке (листинг 9.8) возможности, предоставляемые библиотекой xllib.php — подключает ее командой require:



Листинг 9.8. Библиотека wlib.php

<?

require "xllib.php";

Function LoadWDocument($filename) { . . . }

Function SaveWDocument($filename,$doc) { . . . }

?>

Эти две библиотеки стали настолько популярны в среде Web-програм­мистов, что скоро все стали их внедрять в свои программы. При этом, конечно же, никому нет дела до того, как эти библиотеки на самом деле устроены — все просто подключают их к своим сценариям при помощи require, не задумываясь о возможных последствиях.

Но в один прекрасный день одному неизвестному программисту потребовалось работать и с документами Word, и с документами Excel. Он, не долго думая, подключил к своему сценарию обе эти библиотеки (листинг 9.9):

Листинг 9.9. Подключение библиотек xllib.php

и wlib.php[E4] 

<?

require "wlib.php";

require "xllib.php";

$wd=LoadWDocument("document.doc");

$xd=LoadXlDocument("document.xls");

?>

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

Что же произошло? Нетрудно догадаться, если проследить за тем, как транслятор PHP "разворачивает"

код листинга 9.9. Вот как это происходит:

//require "wlib.php";

  //require "xllib.php";

    Function LoadXlDocument($filename) { . . . }

    Function SaveXlDocument($filename,$doc) { . . . }

  Function LoadWDocument($filename) { . . . }

  Function SaveWDocument($filename,$doc) { . . . }

//require "xllib.php";

  Function LoadXlDocument($filename) { . . . }

  Function SaveXlDocument($filename,$doc) { . . . }

$wd=LoadWDocument("document.doc");

$xd=LoadXlDocument("document.xls");

Как видим, файл xllib.php был включен в текст сценария дважды: первый раз косвенно через wlib.php, и второй раз — непосредственно из программы. Поэтому транслятор, дойдя до выделенной строки, обнаружил, что функция LoadXlDocument() определяется второй раз, на что честно и прореагировал.



Конечно, разработчик сценария мог бы исследовать исходный текст библиотеки wlib.php и понять, что во второй раз xllib.php включать не нужно. Но согласитесь — это не выход. Действительно, при косвенном подключении файлов третьего и выше уровней вполне могут возникнуть ситуации, когда без модификации кода библиотек будет уже не обойтись. А это недопустимо. Как же быть?

Что ж, после столь длительного вступления (возможно, слишком длительного?) наконец настала пора рассказать, что думают по этому поводу разработчики PHP. А они предлагают простое решение: инструкции include_once и require_once.

Инструкция require_once работает точно так же, как и require, но за одним важным исключением. Если она видит, что затребованный файл уже был ранее включен, то она ничего не делает. Разумеется, такой метод работы требует от PHP хранения полных имен всех подсоединенных файлов где-то в недрах интерпретатора. Так он, собственно говоря, и поступает.

Инструкция include_once работает совершенно аналогично, но включает файл во время исполнения программы, а не во время трансляции.



Как я уже говорил, в PHP существует внутренняя таблица, которая хранит полные имена всех включенных файлов. Проверка этой таблицы осуществляется инструкциями include_once и require_once. Однако добавление имени включенного файла производят также и функции require

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

Везде, где только можно, применяйте инструкции с суффиксом once. Постарайтесь вообще отказаться от require

и include. Это во многом упростит разбиение большой и сложной программы на относительно независимые модули.


Содержание раздела