Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель по PHP 4

.pdf
Скачиваний:
82
Добавлен:
02.05.2014
Размер:
4.36 Mб
Скачать

Глава 9. Конструкции языка

161

Конструкция 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, который рассматривается

162

Часть III. Основы языка PHP

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

(листинги 9.4, 9.5 и 9.6):

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

<html>

<head><title>Title!</title></head> <body bgcolor=yellow>

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

©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 (по одной цифре в каждом файле). Запустим такую программу:

Глава 9. Конструкции языка

163

for($i=0; $i<10; $i++) { include "file$i.php";

}

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

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

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

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

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

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

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

164

Часть III. Основы языка 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-программистов, что скоро все стали их внедрять в свои программы. При этом, конечно же, никому нет дела до того, как эти библиотеки на самом деле устроены — все просто подключают

Глава 9. Конструкции языка

165

их к своим сценариям при помощи require, не задумываясь о возможных последствиях.

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

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

<?

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 включать не нужно. Но согласитесь — это не выход. Действительно, при косвенном подключении файлов третьего и выше уровней вполне могут возникнуть ситуации, когда без модификации кода библиотек будет уже не обойтись. А это недопустимо. Как же быть?

166

Часть III. Основы языка 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. Это во многом упростит разбиение большой и сложной программы на относительно независимые модули.

Глава 10

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

массивы

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

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

for($i=0; ; $i++) { $v="a$i"; if(!isset($$v)) break;

..делаем что-нибудь с $$v

}

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

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

168

Часть III. Основы языка 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>";

Создание массива "на лету". Автомассивы

В примере из листинга 10.1, казалось бы, все гладко. За исключением одного небольшого недостатка: каждый раз, добавляя имя, мы должны были выбирать для него номер и заботиться, чтобы ненароком не указать уже существующий. Чтобы этого избежать, можно написать те же команды так:

$NamesList[]="Dmitry"; $NamesList[]="Helen"; $NamesList[]="Sergey";

Глава 10. Ассоциативные массивы

169

В этом случае PHP сам начнет (конечно, если переменная $NamesList еще не существует) нумерацию с нуля и каждый раз будет прибавлять к счетчику по единичке, создавая список. Согласитесь, довольно удобно. Разумеется, можно использовать [] и не только в таком простом контексте, очень часто они применяются для более общего действия — добавления элемента в конец массива, например:

Unset($FNames); // на всякий случай стираем массив while($f=очередное_имя_файла_в_текущем каталоге)

if(расширение_$f_есть_txt) $FNames[]=$f;

// теперь $FNames содержит список файлов с расширением txt

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

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

$Names["Koteroff"] = "Dmitry"; $Names["Ivanov"] = "Ivan"; $Names["Petrov"] = "Peter";

Далее, мы можем распечатать имя любого абонента командой:

echo $Names["Ivanov"]; $f="Koteroff";

echo $Names[$f];

Как видите, тут никаких особенностей нет, все работает совершенно аналогично спискам, только с нецифровыми ключами. Возможно, вы скажете, что это не совсем так: например, нельзя воспользоваться циклом for, как мы это делали раньше, для вывода всех персоналий, и окажетесь правы. Вскоре мы рассмотрим целых три приема, с помощью которых можно перебрать все элементы массива. Вы, скорее всего, будете применять их даже и для списков — настолько они удобны и универсальны, а к тому же и работают быстрее, чем последовательный перебор в цикле for с использованием $i.

170

Часть III. Основы языка PHP

Инструкция list()

Пусть у нас есть некоторый массив-список $List с тремя элементами: имя человека, его фамилия и возраст. Нам бы хотелось присвоить переменным $name, $surname и $age эти величины. Это, конечно, можно сделать так:

$name=$List[0]; $surname=$List[1]; $age=$List[2];

Но гораздо изящнее будет воспользоваться инструкцией list(), предназначенной как раз для таких целей:

list($name,$surname,$age)=$List;

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

Что, если нам нужны только второй и третий элемент массива $List? В этом случае имеет смысл пропустить первый параметр в инструкции list(), вот так:

list(,$surname,$age)=$List;

Таким образом, мы получаем в $surname и $age фамилию и возраст человека, не обращая внимания на его имя в первом аргументе.

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

Списки и ассоциативные массивы: путаница?..

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