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

Котеров Д. В., Костарев А. Ф. - PHP 5. 2-е издание (В подлиннике) - 2008

.pdf
Скачиваний:
6114
Добавлен:
29.02.2016
Размер:
11.36 Mб
Скачать

214

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

Например:

$i = 0; $j = 0; $k = "Points"; while ($i<100) {

$k.=".";

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

}

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

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

for (инициализирующие_команды; условие_цикла; команды_после_прохода):

операторы;

endfor;

Инструкции break и continue

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

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

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

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

for ($j=0; $j<count($matrix[$i]); $j++) { if ($matrix[$i][$j] == 0) break(2);

}

}

if ($i < 10) echo 'Найден нулевой элемент в матрице!';

Как видите, инструкцию break удобно использовать для циклов поисков: как только

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

массиве (прямоугольной матрице).

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

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

новой (конечно, если выполняется условие цикла для цикла с предусловием). Точно так же, как и для break, для continue можно указать уровень вложенности цикла,

который будет продолжен по возврату управления.

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

увеличить его удобочитаемость. Это чаще всего бывает нужно в циклах-фильтрах,

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

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

215

цикл, который печатает только те элементы массива $files (имена файлов и катало-

гов), которые являются файлами:

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

if ($files[$i] == ".") continue;

if ($files[$i] == "..") continue;

if (is_dir($files[$i])) continue;

echo "Найден файл: $files[$i]<br>";

}

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

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

Есть один интересный побочный эффект, возникающий при применении инструкции break, который довольно удобно использовать для обхода "лишних" операторов.

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

граммировании сценариев. Рассмотрим соответствующий пример (листинг 9.2).

Листинг 12.4. Файл dowhile.php

<!-- Модель сценария для обработки формы --> <?php

$WasError = 0; // индикатор ошибки — если не 0, то была ошибка

// Если нажали кнопку Submit (с именем $doSubmit)...

if (isset($_REQUEST['doSubmit'])) do { // Проверка входных данных

if ($_REQUEST['reloads'] != 1+1+7) { $WasError = 1; break; } if ($_REQUEST['loader'] != "source") { $WasError = 1; break; }

//и т. д. — здесь может быть множество других проверок.

//...

//Здесь данные точно в порядке. Обрабатываем их.

echo "Вы внимательный человек, поздравляем!<br>"; // Можно записать данные в файл.

exit();

} while (0);

// Произошла ли ошибка? if ($WasError) {

echo "Вы ответили неверно, попробуйте еще раз.";

}

?>

216

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

<!-- Выводим форму, через которую пользователь будет запускать этот сценарий, и, возможно, отображаем сообщение об ошибке в случае, если $WasError != 0. -->

<form action="<?=$_SERVER['REQUEST_URI']?>" method=post> Число перезагрузок: <input type=text name="reloads"><br> Загрузочная программа: <input type=text name="loader"><br>

<input type=submit name="doSubmit" value="Ответить на вопросы"> </form>

Здесь представлен самый обычный способ для организации сценариев-диалогов.

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

(и где-нибудь красным цветом сообщение об ошибке), в противном случае сценарий

завершается и выдает страницу с результатом.

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

if (что_то) do { ... } while (0);

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

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

Öèêë foreach

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

foreach (массив as $ключ=>$значение)

команды;

Здесь команды циклически выполняются для каждого элемента массива, при этом очередная пара ключ=>значение оказывается в переменных $ключ и $значение. Давайте рассмотрим пример (листинг 12.5), где покажем, как мы можем отобразить содер-

жимое всех переменных окружения при помощи цикла foreach.

Листинг 12.5. Файл foreach.php

<?php ## Вывод всех переменных окружения. foreach($_SERVER as $k=>$v)

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

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

217

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

нас не интересует значение ключа очередного элемента. Выглядит она так:

foreach ($массив as $значение)

команды;

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

Вследующей главе мы рассмотрим ассоциативные массивы и все, что к ним отно-

сится, гораздо более подробно.

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

один раз — до начала цикла, а затем работа станет производиться с копией возвращенного значения.)

Для того чтобы иметь возможность изменять массив изнутри тела цикла, в PHP 5

можно использовать ссылочный синтаксис:

foreaсh ($массив as $ключ=>&$значение) {

//здесь можно изменять $значение, при этом изменяются элементы

//исходного массива $массив.

}

Конструкция 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, но и все нижележащие.

218

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

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

switch (выражение):

case значение1: команды1; [break;]

. . .

case значениеN: командыN; [break;] [default: команды_по_умолчанию; [break]]

endswitch;

Инструкции require и include

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

require имя_файла;

При запуске программы интерпретатор просто заменит инструкцию на содержимое

файла имя_файла (этот файл может также содержать сценарий на PHP, обрамленный,

как обычно, тегами <? и ?>). Это бывает довольно удобно для включения в вывод

сценария всяких "шапок" с HTML-кодом. Например, рассмотрим листинги 12.6—

12.8.

Листинг 12.6. Файл require/head.html

<!-- "Шапка". -->

<html>

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

<body bgcolor=black text=#00FF00>

<b><pre>

Листинг 12.7. Файл require/script.php

<?php ## Тело скрипта.

require "head.html";

print_r($GLOBALS);

require "foot.html";

?>

Листинг 12.8. Файл require/foot.html

<!-- "Подвал". --> </pre></b>

©Warner Bros., 1999. </body></html>

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

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

219

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

трех файлов! А как было сказано выше, чем меньше файлов использует программа, тем легче с ней будет работать вашему дизайнеру и верстальщику (которые о PHP

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

Инструкция include практически идентична require, за исключением того, что в случае невозможности включения файла работа сценария не завершается немедлен-

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

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

Старайтесь не использовать инструкции require и include для подключения других частей кода к PHP-программе! Применяйте их только в целях разделения HTML-страниц на "шапки" и "подвалы". Для того чтобы подключить другую часть скрипта, используйте инструкцию require_once, описанную в разд. "Решение: require_once" далее в этой главе.

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

Большие и сложные сценарии обычно состоят из не одного десятка файлов, вклю-

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

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

Суть проблемы

Чтобы стало яснее, мы расскажем вам притчу. Как-то раз разработчик Билл написал

несколько очень полезных функций для работы с файлами Microsoft Excel и решил объединить их в библиотеку — файл xllib.php (листинг 12.9).

Листинг 12.9. Файл trouble/xllib.php

<?php ## Библиотека для работы с Excel.

function LoadXlDocument($filename) { /* . . . */ } function SaveXlDocument($filename,$doc) { /* . . . */ } ?>

Разработчик Вася захотел сделать то же самое для работы с документами Microsoft

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

Листинг 12.10. Файл trouble/wlib.php

<?php ## Библиотека для работы с Word.

require "xllib.php";

220

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

function LoadWDocument($filename) { /* . . . */ }

function SaveWDocument($filename,$doc) { /* . . . */ }

?>

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

Но в один прекрасный день одному неизвестному программисту потребовалось работать и с документами Word, и с документами Excel. Он, не долго думая, подклю-

чил к своему сценарию обе библиотеки (листинг 12.11).

Листинг 12.11. Файл trouble/aargh.php

<?php ## Возникает ошибка! require "wlib.php"; require "xllib.php";

$wd = LoadWDocument("document.doc"); $xd = LoadXlDocument("document.xls"); ?>

Каково же было его удивление, когда при запуске этого сценария он получил сооб-

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

Cannot redeclare loadxldocument() (previously declared in xllib.php:2) in xllib.php on line 2

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

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

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

221

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

будет уже не обойтись. А это недопустимо. Как же быть?

Решение: require_once

Что ж, после столь длительного вступления (возможно, слишком длительного?) на-

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

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

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

имен всех подсоединенных файлов где-то в недрах интерпретатора. Так он, собственно говоря, и поступает.

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

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

медленно.

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

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

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

Другие инструкции

В PHP существует еще масса других инструкций:

function — объявление функции;

class — объявление класса;

var, private, static, public — определение свойства класса;

throw — генерация исключения;

try-catch — перехват исключения

и т. д.

Почти все эти инструкции мы рассмотрим позже, в части V. Инструкцию function мы опишем в гл. 14.

222

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

Резюме

В данной главе мы познакомились со вторым "китом" программирования на PHP —

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

сающимися оптимального использования инструкций и операторов.

ГЛАВА 13

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

Листинги данной главы можно найти в подкаталоге arrays.

Возможно, вы уже догадались, что ассоциативные массивы — один из самых мощных инструментов в PHP. Массивы довольно часто реализуются в интерпретаторах типа

PHP (в Perl ассоциативные массивы устроены даже немного хуже, чем в PHP). Да-

вайте рассмотрим подробнее, как с ними работать.

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

ких величин, к которым можно затем быстро и удобно обратиться. Конечно, никто не запрещает вам вообще их не использовать, а, например, давать своеобразные имена переменным, такие как $a1, $a2 и т. д. Но представьте, что получится в том

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

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

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

$v = "a$i";

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

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

}

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

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

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

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

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

$namesList[0] = "Yuen Wo Ping";

$namesList[1] = "Geofrey Darrow";

$namesList[2] = "Hugo Weaving";

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.