PHP 4 на практике

         

Альтернативное ограничение блоков


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

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

блок

else :

блок

endif;

Следовательно, две приведенных ниже команды if полностью эквивалентны:

if ($а== $b) {

print "Equivalent values!";

}

if ($a == $b) :

print "Equivalent values!";

endif;



Break


Команда break немедленно прерывает выполнение той конструкции while, for или switch, в которой она находится. Эта команда уже упоминалась в предыдущем разделе, однако прерывание текущего цикла не исчерпывает возможностей команды break. В общем виде синтаксис break выглядит так:

break n;

Необязательный параметр n определяет количество уровней управляющих конструкций, завершаемых командой break. Например, если команда break вложена в две команды while и после break стоит цифра 2, происходит немедленный выход из обоих циклов. По умолчанию значение n равно 1; выход на один уровень может обозначаться как явным указанием 1, так и указанием команды break без параметра. Обратите внимание: команда i f не относится к числу управляющих конструкций, прерываемых командой break. Об этом следует помнить при использовании необязательного параметра п.

Рассмотрим пример использования команды break в цикле foreach:

$arr = array(14, 12, 128, 34, 5);



$magic number = 128:

foreach ($arr as $val) :

if (Sval == $magic_number) :

print "The magic number is in the array!";

break;

endif;

print "val is Sval <br>";

endforeach;

Если значение $magic_number присутствует в массиве $аrr (как в приведенном примере), поиск прерывается. Результат выглядит так:

val is 14

val is 12

The magic number is in the array!

Приведенный пример всего лишь демонстрирует использование команды break. В РНР существует стандартная функция in_array( ), предназначенная для поиска заранее заданной величины в массиве; эта функция подробно описана в главе 5.



Continue


Остается рассмотреть еще одну управляющую конструкцию РНР — continue. При выполнении команды continue в цикле пропускаются все оставшиеся команды текущей итерации и немедленно начинается новая итерация. Синтаксис команды continue в общем виде:

continue n;

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

Рассмотрим пример использования команды continue. Допустим, вы хотите сосчитать простые числа в интервале от 0 до некоторой заданной границы. Простоты ради предположим, что у нас имеется функция is_prime(), которая проверяет, является число простым или нет:

$boundary = 558;

for ($i = 0; $i <= $boundary: $i++) :

if (! is_prime($i)) :

continue;

endif;

$prime_counter++;

endfor;

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

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

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



Do. .while


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

do:

блок

while (выражение);

Давайте пересмотрим пример с вычислением факториала и перепишем его с использованием конструкции do. .while:

$n = 5:

$ncopy = $n;

$factorial = 1; // Установить начальное значение факториала

do {

$factorial = $n * $factorial;

$n--: // Уменьшить Sn на 1

} while (Sn > 0);

print "The factorial of Sncopy is $factorial.";

При выполнении этого примера будет получен тот же результат, что и при выполнении его прототипа из предыдущего раздела.

В

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



For


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

управляющей конструкции, а не где-то внутри блока команд. Как и в случае с циклом while, цикл выполняется до тех пор, пока проверяемое условие остается истинным. Общая форма конструкции for выглядит так:

for (инициализация: условие; приращение) {

блок

}

Условная часть цикла for в действительности состоит из трех компонентов. Инициализация выполняется всего один раз и определяет начальное значение управляющей переменной цикла. Условие проверяется в начале каждой итерации и определяет, должна ли выполняться текущая итерация или нет. Наконец, приращение определяет изменение управляющей переменной при каждой итерации. Возможно, термин «приращение» в данном случае неточен, поскольку переменная может как увеличиваться, так и уменьшаться в соответствии с намерениями программиста. Следующий пример демонстрирует простейший случай применения цикла for:

for ($i = 10; $1 <- 100: $1 +=10) : // Обратная косая черта предотвращает

print "\$i = $i <br>"; endfor; // возможную интерполяцию переменной $1

Выполнение этого фрагмента дает следующий результат:

$i = 10

$i = 20

$i = 30

$i = 40

$i - 50

$i = 60

$i = 70

$i = 80

$i = 90

$i = 100

В этом примере управляющая переменная $i инициализируется значением 10. Условие заключается в том, что цикл продолжается до тех пор, пока $i не достигнет или не превысит пороговую величину 100. Наконец, при каждой итерации значение $i увеличивается на 10. В результате команда print выполняется 10 раз, каждый раз выводя текущее значение $i. Обратите внимание: для увеличения $i на 10 используется оператор сложения с присваиванием. Для этого есть веские причины, поскольку циклы for в РНР не поддерживают более традиционной записи $i = $i + 10.

Кстати, этот пример можно записать и в другом виде, но с теми же результатами:

for ($i = 10; $i <= 100; print "\$i - $i <br>". $i+=10);


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

Например, вы можете инициализировать несколько переменных одновременно, разделяя команды инициализации запятыми:

for ($x=0,$y=0: $x+$y<10; $x++) :

$У += 2;                    // Увеличить $у на 2 

print "\$y = $y <BR>";      // Вывести значение $у

$sum = $x + $y;

print "\surn = $sum<BR>";   // Вывести значение $sum

endfor;

Результат:

$y = 2 

$sum = 2 

Sy = 4 

$sum = 5 

$y = 6 

$sum = 8 

$y = 8 

$sum = 11

Этот пример выводит текущие значения $y и суммы $х и $у. Как видно из приведенных результатов, выводится значение $sum = 11, хотя эта сумма выходит за границы условия цикла ($х + $у < 10). Это происходит из-за того, что при входе в данную итерацию переменная $у была равна 6, а переменная $х была равна 2. Значения переменных соответствовали условию цикла, поэтому $х и $у были присвоены новые значения, в результате чего была выведена сумма И. При очередной проверке условия сумма 11 превысила пороговое значение 10 и цикл завершился.

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

$х = 5:

for (: : $х +=2) :

print " $х ";

if ($x == 15) :

break; // Выйти из цикла for

endif;

endfor;

Результат выглядит так:

5 7 9 11 13 15

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


Foreach


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

foreach (массив as $элемент) {

блок

}

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

блок

}

Например, при выполнении следующего фрагмента:

$menu = аrrау("pasta", "steak", "potatoes", "fish", "fries");

foreach ($menu as $item) {

print "$item <BR>";

}

будет выведен следующий результат:

pasta

steak

potatoes

fish

fries

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

Второй вариант используется при работе с ассоциативными массивами:

$wine_inventory = array {

"merlot" => 15,

"zinfandel" => 17,

"sauvignon" => 32

}

foreach ($wine_inventory as $i => $item_count) {

print "$item_count bottles of $i remaining<BR>";

}

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

15 bottles of merlot remaining

17 bottles of zinfandel remaining

32 bottles of sauvignon remaining

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



В этой главе были представлены


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


операторы;

операнды;

приоритет операторов;

ассоциативность операторов;

управляющие структуры (If, while, do. .while, for, foreach, switch, break, continue).
В первых трех главах книги вы познакомились с базовыми компонентами языка РНР. Остальные пять глав первой части расширяют этот материал; в них вы найдете дополнительную информацию о работе с массивами, объектно-ориентированных возможностях, файловом вводе/выводе и строковых операциях РНР. Весь этот материал готовит читателя ко второй части книги, причем особое внимание уделяется средствам РНР, часто используемым при построении приложений. Итак, держитесь покрепче и не сходите с дистанции!

Операнды


Операнд

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

$а++; // $а - операнд

$sum = $val1 + $val2; // $sum. $val1 и $val2 - операнды



Операторы


Оператор

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

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

Таблица 3.1.

Операторы РНР

Логическое отрицание, поразрядное отрицание

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

$а = 5;   // Присвоить целое число 5 переменной $а

$а = "5":   // Присвоить строковую величину "5" переменной $а

$sum = 50 + $some_int;  // Присвоить сумму 50 + $some_int переменной $sum

Swine = "Zinfandel";   // Присвоить строку "Zinfandel" переменной $wine


$inventory++:   // Увеличить значение $inventory на 1

Объединяя операторы и операнды, вы получите более сложные выражения для выполнения нетривиальных вычислений. Пример:

$total_cost = $cqst + (Scost * 0.06): // прибавить к цене 6-процентный налог

Приоритет операторов

Приоритет

является характеристикой операторов, определяющей порядок выполнения действий с окружающими операндами. В РНР используются те же правила приоритета, что и в школьном курсе математики. Пример:

$total_cost = $cost + $cost * 0.06;

Приведенная команда эквивалентна следующей:

$total cost = $cost + ($cost * 0.06);

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

Ассоциативность операторов

Ассоциативность

оператора определяет последовательность выполнения операторов с одинаковым приоритетом (см. табл. 3.1). Выполнение может происходить в двух направлениях: либо слева направо, либо справа налево. При ассоциативности первого типа операции, входящие в выражение, выполняются слева направо. Например, команда

$value = 3*4*5*7*2;

эквивалентна следующей команде:

$value = ((((3 * 4) * 5) * 7) * 2);

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

$с = 5;

$value = $а - $b - $с;

эквивалентен фрагменту

$c = 5;

$value = ($а - ($b - $с));

При обработке этого выражения переменным $value, $a, $b и $с будет присвоено значение 5. Это объясняется тем, что оператор присваивания (=) обладает правосторонней ассоциативностью.

Математические операторы

Математические операторы

(табл. 3.2) предназначены для выполнения различных математических операций и часто применяются в большинстве программ РНР. К счастью, их использование обходится без проблем.

Таблица 3.2.

Математические операторы

Оператор Ассоциативность Цель
( ) - Изменение приоритета
new - Создание экземпляров объектов
! ~ П
++ -- П Инкремент, декремент
@ П Маскировка ошибок
/ * % Л Деление, умножение, остаток
+ - . Л Сложение, вычитание, конкатенация
<< >> Л Сдвиг влево, сдвиг вправо (поразрядный)
< <=  >  >= - Меньше, меньше или равно, больше, больше или равно
== !=  ===  <> - Равно, не равно, идентично, не равно
&  ^  | Л Поразрядные операции AND, XOR и OR
&& || Л Логические операции AND и OR
?: П Тернарный оператор
=  +=  *=  /=  .= П Операторы присваивания
%=  &=  |=  ^=    
<<=  >>=    
AND XOR OR Л Логические операции AND, XOR и OR
Пример

Название

Результат


/p>

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

Операторы присваивания

Операторы присваивания

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

Таблица 3.3.

Операторы присваивания

 Название

Переменная $а равна частному отделения $а на 5

Переменная $а равна конкатенации $а и 5

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

Строковые операторы

Строковые операторы

РНР (табл. 3.4) обеспечивают удобные средства конкатенации (то есть слияния) строк. Существует два строковых оператора: оператор конкатенации (.) и оператор конкатенации с присваиванием (.=), описанный в предыдущем разделе «Операторы присваивания».

Конкатенацией называется объединение двух и более объектов в единое целое.

Таблица 3.4.

Строковые операторы

Пример Результат
$а = 5;   Присваивание Переменная $а равна 5 
$а += 5;   Сложение с присваиванием  Переменная $а равна сумме $а и 5
$а *= 5;  Умножение с присваиванием Переменная $а равна произведению $а и 5
$а/=5; Деление с присваиванием
$а .= 5;   Конкатенация с присваиванием
Пример

Название

Результат

Конкатенация

 $а - "ghijkl"

 Конкатенация с присваиванием

Переменной $а присваивается результат конкатенации ее текущего значения со строкой "ghijkl"

Пример использования строковых операторов:

// $а присваивается строковое значение "Spaghetti & Meatballs" $а = "Spaghetti" . "& Meatballs"



// $а присваивается строковое значение "Spaghetti & Meatballs are delicious" $a .= "are delicious";

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

Операторы инкремента и декремента

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

Таблица 3.5.

Операторы инкремента и декремента

$a = "abc"."def" Переменной $а присваивается результат конкатенации $а и $b
Результат

Интересный факт: эти операторы могут располагаться как слева, так и справа от операнда. Действия, выполняемые оператором, зависят от того, с какой стороны от операнда он находится. Рассмотрим следующий пример:

$inventory = 15;  // Присвоить Sinventory целое число 15

$old_inv = Sinventory--;  // СНАЧАЛА присвоить $old_inv значение

// Sinventory. а ЗАТЕМ уменьшить Sinventory.

$orig_iinventory = ++inventory;// СНАЧАЛА увеличить Sinventory. а ЗАТЕМ

// присвоить увеличенное значение Sinventory

// переменной $orig_inventory.

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

Логические операторы

Логические операторы

(табл. 3.6) наряду с математическими операторами играют важную роль в любом приложении РНР, обеспечивая средства для принятия решений в зависимости от значения переменных. Логические операторы позволяют управлять порядком выполнения команд в программе и часто используются в управляющих конструкциях (таких, как условная команда i f, а также циклы for и while).

Таблица 3.6.

Логические операторы

Пример Название
++$а, $а++ Инкремент Переменная $а увеличивается на 1
--$а, $а-- Декремент Переменная $а уменьшается на 1
Пример

Название

Результат

$а && $b

Конъюнкция

Истина, если истинны оба операнда ,

$aAND$b

Конъюнкция



/p>

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

file_exists("filename.txt") OR print "File does not exist!";

Возможен один из двух вариантов:





файл filename.txt существует;



будет выведено сообщение: «File does not exist!».

Операторы равенства

Операторы равенства

(табл. 3.7) предназначены для сравнения двух величин и проверки их эквивалентности.

Таблица 3.7.

Операторы равенства

Пример

Название

Результат 

$а === $b

Проверка идентичности

Истина, если $а и $b равны и имеют одинаковый тип

Даже опытные программисты часто допускают одну распространенную ошибку — они пытаются проверять равенство двух величин, используя всего один знак равенства (например, $а = $b). Помните, при такой записи значение $b присваивается $а, и желаемый результат не будет достигнут.

Операторы сравнения

Операторы сравнения

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

Таблица 3.8.

Операторы сравнения

$a==$b Проверка равенства Истина, если $а и $b равны
$а != $b Проверка неравенства Истина, если $а и $b не равны
Пример

Название

Результат

$a<$b

Меньше

Истина, если переменная $а меньше $b

$a>$b

Больше

Истина, если переменная $а больше $b

$a <= $b

Меньше или равно

Истина, если переменная $а меньше или равна $b

$a >= $b

Больше или равно

Истина, если переменная $а больше или равна $b

($a-12)?5: -1

Тернарный оператор

Если переменная $а равна 12, возвращается значение 5, а если не равна — возвращается 1

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



Поразрядные операторы

Поразрядные операторы

выполняют операции с целыми числами на уровне отдельных битов, составляющих число. Чтобы лучше понять принцип их работы, необходимо иметь хотя бы общее представление о двоичном представлении десятичных чисел. В табл. 3.9 приведены примеры десятичных чисел и соответствующих им двоичных представлений.

Таблица 3.9.

Десятичные числа и их двоичные представления

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

Таблица 3.10.

Поразрядные операторы

Десятичное целое Двоичное представление
2 10
5 101
10 1010
12 1100
145 10010001
1 452 012 1011000100111111101100
Пример

Название

Результат

$а&$b

Конъюнкция

С битами, находящимися в одинаковых разрядах $а и $b, выполняется

операция конъюнкции

$а|$Ь

Дизъюнкция

С битами, находящимися в одинаковых разрядах $а и $b, выполняется операция дизъюнкции

$а^$b

Исключающая

С битами, находящимися в одинаковых разрядах $а и $b, выполняется операция исключающей дизъюнкции

~$b

Отрицание

Все разряды переменной $b инвертируются

$а << $b

Сдвиг влево

Переменной $а присваивается значение $b, сдвинутое влево на два бита

$а >> $b

Сдвиг вправо

Переменной $а присваивается значение $b, сдвинутое вправо на два бита

Если вам захочется больше узнать о двоичном представлении и поразрядных операторах, я рекомендую обратиться к обширному электронному справочнику Рэндалла Хайда (Randall Hyde) «The Art of Assembly Language Programming», доступному по адресу http://webster.cs.ucr.edu/Page_asm/Page_asm.html. Это лучший из ресурсов, которые мне когда-либо встречались в Web.


Проект: календарь событий


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

Информация о событиях хранится в обычном текстовом файле и выглядит примерно так:

July 21, 2000|8 p. m.|Cooking With Rasmus|PHP creator Rasmus Lerdorf discusses the wonders of cheese.

July 23, 2000|11 a. m.|Boxed Lunch|Valerie researches the latest ham sandwich making techniques (documentary)

July 31, 2000|2:30p.m.|Progressive Gourmet|Forget the Chardonnay: iced tea is the sophisticated gourmet's beverage of choice.

August 1, 2000|7 p.m.|Coder's Critique|Famed Food Critic Brian rates NYC's hottest new Internet cafes.

August 3, 2000|6 p.m.|Australian Algorithms|Matt studies the alligator's diet.

На рис. 3.1 изображен результат работы сценария РНР, приведенного в листинге 3.1.

Рис. З.1.

Примерный вид календаря

Прежде чем переходить к подробному анализу кода, потратьте немного времени на изучение алгоритма:

Открыть файл, содержащий информацию о событиях.

Разделить каждую строку на 4 элемента: дату, время, название и краткое описание мероприятия.

Отформатировать и вывести данные.

Закрыть файл.

Листинг 3.1.

Сценарий для вывода содержимого events.txt в браузере

<?

// Приложение: календарь

// Назначение: чтение и анализ содержимого файла

// с последующим форматированием для вывода в браузере

// Открыть файловый манипулятор Sevents для файла events.txt

$events - fopen ("events.txt". "r");

print "<table border = 0 width = 250>"

print""<tr><td valign=top";

print "<h3>Events Calendar:</h3>";

// Читать, пока не будет найден конец файла


while (! feof(Sevents)) :

// Прочитать следующую строку файла

events.txt $event = fgets($events. 4096);

// Разделить компоненты текущей строки на элементы массива

$event_info = explode("|". Jevent);

// Отформатировать и вывести информацию о событии

print "$event_info[0] ( $event_info[1] ) <br>";

print "<b>$event_info[2]</b> <br>";

print "$event_info[3] <br> <br>";

endwhile;

// Завершить таблицу

print "</td></tr></table>";

fclose ($events);

?>

Этот короткий пример убедительно доказывает, что РНР позволяет даже неопытным программистам создавать реальные приложения с минимальными усилиями и затратами времени. Если какие-нибудь из представленных концепций покажутся непонятными, не огорчайтесь — на самом деле они очень просты и будут подробно описаны в следующих главах. А если вам не терпится узнать побольше об этих вопросах, обратитесь к главе 7 «Файловый ввод/вывод и файловая система» и главе 8 «Строки и регулярные выражения» поскольку большая часть незнакомого синтаксиса описана именно там.


Проверка условий


Управляющие конструкции обычно проверяют условия на истинность или ложность, и в зависимости от результата проверки выполняется то или иное действие. Рассмотрим выражение $а == $b. Это выражение истинно, если $а равно $b, и ложно в противном случае. Результат истинного выражения считается равным 1, а результат ложного выражения равен 0. Рассмотрим следующий фрагмент:

$а = 5;

$b = 5;

print $а == $b;

В результате выводится значение 1. Если изменить $а или $Ь и присвоить переменной значение, отличное от 5, выводится 0.

if

Команда if представляет собой разновидность команды выбора, которая вычисляет значение выражения и в зависимости от того, будет ли полученный результат истинным или ложным, выполняет (или не выполняет) блок программного кода. Существует две общих формы команды i f:

if (выражение) {

блок

}

и

if (выражение) {

блок

}

else {

блок

}

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

if ($cooking_weight < 200) {

print "This is enough pasta (< 200g) for 1-2 people";

}

else {

print "That's a lot of pasta. Having a party perhaps?";

}

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

if ($cooking_weight < 100) print "Are you sure this is enough?";

elseif

Команда elseif добавляет в управляющую конструкцию if дополнительный уровень проверки и увеличивает количество условий, на основании которых принимается решение:

if (выражение) {

блок

}

elseif (выражение) {

блок

}

В РНР существует альтернативное представление команды elself — в виде двух отдельных слов else if. Оба варианта приводят к одинаковым результатам, а альтернативное представление поддерживается исключительно для удобства. Команда elself особенно полезна в тех случаях, когда происходит последовательное уточнение проверяемых условий. Обратите внимание: условие elself вычисляется лишь в том случае, если все предшествующие условия if и elself оказались ложными.


if ($cooking_weight < 200) {

print "This is enough pasta (< 200g) for 1-2 people";

}

elseif ($cooking_weight < 500) {

print "That's a lot of pasta. Having a party perhaps?"; }

}

else {

print "Whoa! Who are you cooking for, a football team?";

}

Вложенные команды if

Вложение команд i f обеспечивает максимальный контроль над проверкой условий. Давайте исследуем эту возможность, усовершенствовав пример из предыдущих разделов. Предположим, вес продукта должен проверяться лишь в том случае, если речь идет о пасте (макаронных изделиях):

// Проверить значение $pasta

if ($food == "pasta") {

// Проверить значение $cooking_weight

if ($cooking_weight < 200) {

print "This is enough pasta (< 200g) for 1-2 people";

}

elseif ($cooking_weight < 500) {

print "That's a lot of pasta. Having a party perhaps?";

}

else {

print "Whoa! Who are you cooking for. a football team?";

}

}

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

Вычисление нескольких условий

При выборе логики работы программы в управляющей структуре можно проверять комбинацию сразу нескольких условий:

if ($cooking_weight < 0) {

print "Invalid cooking weight!";

}

if ( ($cooking_weight > 0) && ($cooking_weight < 200) ) {

   print "This is enough pasta (< 200g) for 1-2 people";

}

elseif ( ($cooking_weight > 200) && ($cooking_weight < 500) ) {

   print "That's a lot of pasta. Having a party perhaps?";

}

else {

   print "Whoa! Who are you cooking for, a football team?";

}

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


Switch


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

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

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

case (условие):

блок

case (условие):

блок

...

default:

блок

}

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

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

$user_input = "recipes"; // Команда,выбранная пользователем

switch ($user_input) :

case("search") :

print "Let's perform a search!";

break;

case("dictionary") :

print "What word would you like to look up?";

break;

case("recipes") :

print "Here is a list of recipes...";

break;

default :

print "Here is the menu...";

break;

endswitch;

Как видно из приведенного фрагмента, команда switch обеспечивает четкую и наглядную организацию кода. Переменная, указанная в условии switch (в данном примере — $user_input), сравнивается с условиями всех последующих секций case. Если значение, указанное в секции case, совпадает Со значением сравниваемой переменной, выполняется блок этой секции. Команда break предотвращает проверку дальнейших секций case и завершает выполнение конструкции switch. Если ни одно из проверенных условий не выполняется, активизируется необязательная секция default. Если секция default отсутствует и ни одно из условий не выполняется, команда switch просто завершается и выполнение программы продолжается со следующей команды.


Вы должны помнить, что при отсутствии в секции case команды break (см. следующий раздел) выполнение switch продолжается со следующей команды до тех пор,

пока не встретится команда break или не будет достигнут конец конструкции switch. Следующий пример демонстрирует последствия отсутствия забытой команды break: $value = 0.4;

switch($value) :

case (0.4) :

print "value is 0.4<br>";

case (0.6) :

print "value is 0.6<br>";

break;

case (0.3) :

print "value is 0.3<br>";

break;

default :

print "You didn't choose a value!";

break;

endswitch;

Результат выглядит так:

value is 0.4

value is 0.6

Отсутствие команды break привело к тому, что была выполнена не только команда print в той секции, где было найдено совпадение, но и команда print в следующей секции. Затем выполнение команд конструкции switch прервалось из-за команды switch, следующей за второй командой print.

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


Управляющие конструкции


Управляющие конструкции

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



Выражения


Выражение

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



While


Конструкция while предназначена для многократного (циклического) выполнения блока команд. Блок команды while выполняется до тех пор, пока условие цикла остается истинным. Общая форма цикла while выглядит так:

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

блок

endwhile;

Рассмотрим использование цикла while на примере вычисления факториала (n!), где n = 5:

$n = 5;

$nсору = $n;

$factorial = 1; // Установить начальное значение факториала

while ($n > 0) :

$factorial - $n * $factorial;

$n--; // Уменьшить $n на 1

endwhile;

print "The factorial of $ncopy is $factorial.";

Программа выводит следующий результат:

The factorial of 5 is 120.

В этом примере $n уменьшается в конце каждой итерации. Условие цикла не должно быть истинным в тот момент, когда переменная $n станет равна 0, поскольку величина $factorial умножится на 0 — конечно, этого быть не должно.

В приведенном примере условие цикла следовало бы оптимизировать и привести его к виду $n > 1, поскольку умножать $factorial на 1 бессмысленно — число от этого не изменится. Хотя ускорение работы программы будет ничтожно малым, такие факторы всегда принимаются во внимание с ростом объема и сложности программ.



Что такое функция?


Функцией

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



Функции-переменные


Одной из интересных возможностей РНР являются функции-переменные (variable functions), то есть динамические вызовы функций, имена которых определяются во время выполнения программы. Хотя в большинстве web-приложений можно обойтись и без функций-переменных, они значительно сокращают объем и сложность программного кода, а также часто снимают необходимость в условных командах if.

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

$имя_функции( );

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

Создать сообщение для итальянского языка в функции с именем italian.

Создать сообщение для английского языка в функции с именем english.

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

Переменная $language используется для выполнения функции-переменной (в приведенном примере — italian()).

Листинг 4.6. Выбор функции в зависимости от пользовательского ввода

// Приветствие на итальянском языке, function italian( ) {

" print "Benvenuti al PHP Recipes.";

}

// Приветствие на английском языке

function english( ) {

print "Welcome to PHP Recipes.";

}

// Выбрать итальянский язык

$language = "italian":

// Выполнить функцию-переменную

$language( );

Листинг 4.6 демонстрирует интересную концепцию функций-переменных и наглядно показывает, что функции-переменные способствуют уменьшению объема программного кода. Если бы не эта возможность, функцию пришлось бы выбирать командой if или switch; это привело бы к заметному увеличению объема программного кода и риску появления дополнительных ошибок при кодировании.



В частности, мы рассмотрели следующие


Эта глава посвящена функциям и их применению в РНР. В частности, мы рассмотрели следующие темы:


определение и вызов функций;

вложенные функции;

возврат значений из функции;

рекурсивные функции;

функции-переменные;

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

Определение и вызов функций


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

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

Определение функции обычно состоит из трех частей:

имени функции;

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

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

Обобщенный синтаксис функций РНР выглядит так:

function имя_функции ([$параметр1. $параметр2, .... $параметрn]) {

тело функции

}

Имя функции должно подчиняться условиям, приведенным для идентификаторов в главе 2. После имени функции следуют обязательные круглые скобки, в которые заключается необязательный список входных параметров ($параметр1, $параметр2, .... $параметрn). Вследствие относительно либеральных принципов определения переменных в РНР указывать тип входных параметров не нужно. Хотя такой подход имеет свои преимущества, следует помнить, что механизм РНР не проверяет аргументы на соответствие тем типам, которые должны обрабатываться функцией. Случайные ошибки в использовании входных параметров могут привести к неожиданным последствиям (чтобы убедиться в том, что параметр относится к нужному типу, можно проверить его стандартной функцией gettype( )). После закрывающей круглой скобки следуют фигурные скобки, в которые заключается программный код, ассоциируемый с именем функции.

Рассмотрим простой пример использования функции. Предположим, вы хотите создать функцию для вывода лицензионной информации на web-странице:


function display_copyright() {

print "Copyright &copy; 2001 PHP-Powered Recipes. All Rights Reserved.";

}

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

Рассмотрим разновидность функции display_copyright(), которой при вызове передается параметр. Предположим, вы отвечаете за администрирование нескольких web-сайтов, каждому из которых присвоено отдельное имя. На каждом сайте имеется собственный административный сценарий с несколькими переменными, относящимися к этому сайту; к их числу принадлежит переменная $site_name с именем

сайта. В этом случае функцию display_copyright() можно записать следующим образом:

function display_copyright($site_name) {

print "Copyright &copy; 2001 $site_name. All Rights Reserved.";

}

Переменная $site_name, значение которой присваивается за пределами display_copy-right(), передается функции в качестве параметра. Переданное значение можно использовать и модифицировать в любом месте функции, однако любые изменения будут действовать лишь внутри этой функции. Впрочем, специальные ключевые слова позволяют сделать так, чтобы изменения параметров распространялись и за пределы display_copyright(). Эти ключевые слова были представлены в главе 2, в общем обзоре области видимости переменных и ее отношения к функциям.


Построение библиотек функций


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

Листинг 4.7.

Пример библиотеки функций (array_sorting.inc)

<?

// Файл: array_sorting.inc

// Назначение: библиотека функций для сортировки массивов.

// Дата: 17 июля 2000 г.

function merge_sort($array. $tmparray, $right, $left) {

...

function bubble_sort($array. $n) {

...

}

function quicksort ($array. $right. $left) {

...

}

?>

Библиотека array_sorting.inc служит накопителем для всех функций сортировки. Это удобно, поскольку функции фактически группируются по своему назначению и при необходимости можно легко найти нужную функцию. Как видно из листинга 4.7, в начало библиотеки обычно включается заголовок из нескольких строк комментария, чтобы при открытии файла библиотеки можно было сразу получить краткую сводку его содержимого. После собственной библиотеки функций можно включить ее в сценарий при помощи команд РНР include( ) и require( ), в результате чего все функции библиотеки становятся доступными. В общем виде синтаксис этих команд выглядит так:

include(путь/имя_файла);

require(путь/имя_файла);

Также существует альтернативный вариант:

include "путь/имя_файла";

require "путь/имя_файла";

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

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

Листинг 4.8.

Включение библиотечного файла (array_sorting.inc) в сценарий

// Предполагается, что библиотека array_sorting.inc

// находится в одном каталоге со сценарием.

include("array_sorting.inc");

// Теперь вы можете использовать любые функции из array_sorting.inc

$some_array = array (50, 42. 35, 46);

// Использовать функцию bubble_sort()

$sorted_array = bubble_sort($some_array, 1);



Рекурсивные функции


Ситуация, при которой функция многократно вызывает сама себя, пока не будет выполнено некоторое условие, открывает замечательные возможности. При правильном использовании рекурсивные функции уменьшают объем программы и делают ее более выразительной. Рекурсивные функции особенно часто используются при выполнении повторяющихся действий — например, при поиске в файлах/массивах и построении графических изображений (например, фракталов). Классическим примером рекурсивных функций, встречающимся во многих курсах программирования, является суммирование чисел от 1 до N. Программа, приведенная в листинге 4.5, суммирует все целые числа от 1 до 10.

Листинг 4.5.

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

function summation ($count) {

if ($count != 0) :

return $count + summation($count-1);

endif;

}

$sum = summation(10);

print "Summation = $sum";

В результате выполнения листинга 4.5 будет выведен следующий результат:

Summation = 55

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



Вложенные функции


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

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

function display_copyright($site_name) {

print "Copyright &copy". date("Y"). "$site_name. All Rights Reserved.";

}

Параметр Y функции date( ) указывает, что возвращаемое значение представляет собой текущий год, отформатированный в виде четырех цифр. Если системная дата установлена правильно, РНР при каждом выполнении сценария будет выводить год. Функция РНР date( ) отличается исключительной гибкостью и поддерживает 25 разных флагов форматирования даты и времени.

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

Листинг 4.1.

Эффективное использование вложенных функций

function display_footer($site_name) {

function display_copyright($site_name) {

print "Copyright &сору". date("Y").

$site_name. All Rights Reserved.";

print "<center>

<a href = \"\">home</a> | <a href = \"\">recipes</a> | <a href = \"\">events</a><br>

<a href = \"\">tutorials</a> | <a href = \"\">about</a> I <a href = \"\">contact us</a><br>";


display_copyright($site_name);

print "</center>";

}

$site_name = "PHP Recipes":

display_footer($site_name);

display_copyhght($site_name);

Сценарий выводит следующий результат:

home | recipes | events

tutorials | about | contact us

Copyright © 2001 PHP Recipes. All Rights Reserved

Обратите внимание: функцию display_copyright( ) можно вызвать и за пределами display_footer( ) по аналогии стем, как функция display_footer( ) использовалась в предыдущем примере. Концепция защищенных функций в РНР не поддерживается.

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


Возврат значений из функции


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

Перед вызовом функции задать значения переменных: $price (цена товара) и $tax (налоговая ставка).

Объявить функцию calculate_cost( ). При вызове функция получает два параметра: налоговую ставку и цену товара.

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

Вызвать calculate_cost() и присвоить значение, возвращенное функцией, переменной $total_cost.

Вывести соответствующее сообщение.

Листинг 4.2.

Создание функции для вычисления налога

$price = 24.99; $tax = .06;

function calculate_cost($tax, $price) {

$sales_tax = $tax;

return $price + ($price * $sales_tax);

}

// Обратите внимание на возврат значения функцией calculate_cost(). $total_cost = calculate_cost ($tax. $price);

// Округлить цену до двух десятичных цифр.

$total_cost = round($total_cost. 2);

print "Total cost: $".$total_cost;

// $total cost = 26.49

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

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

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

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




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



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

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

Листинг 4.3.

Сравнение текущей суммы счета пользователя с предельным размером кредита

$cost = 1456.22;

$limit = 1000.00;

function check_limit($total_cost. $credit_limit)

if ($total_cost > $credit_limit) :

return 0;

endif;

return 1;

}

if (check_limit($cost. $limit)) :

// Продолжить закупки

print "Keep shopping!";

else :

print "Please lower your total bill to less than $".$limit."!";

endif;

При выполнении листинга 4.3 будет выведено сообщение об ошибке, поскольку значение $cost превышает $limit.

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





Объявить функцию best_years( ), вызываемую с одним параметром. Параметр $label определяет сорт вина, для которого пользователь хотел бы узнать три рекомендуемых года.



Объявить два массива, $merlot и $zinfandel. В каждом массиве хранится три рекомендуемых года для соответствующего сорта вина.



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





Вывести сообщение с информацией о рекомендуемых годах.

Листинг 4.4. Возвращение функцией нескольких величин

// Сорт вина, для которого выводятся лучшие годы

$label = "merlot";

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

// для возвращения нескольких значений.

function best_years($label) {

$merlot = array("1987", "1983", "1977");

$zinfandel = array("1992", "1990", "1989");

return $$label;

}

// Функция list( ) используется получения возвращаемых значений.

list ($yr_one, $yr_two. $yr_three) = best_years($label);

print "$label had three particularly remarkable years: $yr_one. $yr_two, and $yr_three.";

Программа выводит следующий результат:

merlot has three particularly remarkable years: 1987, 1983 and 1977.


Добавление и удаление элементов


К счастью, в РНР при создании массива не нужно указывать максимальное количество элементов. Это увеличивает свободу действий при операциях с массивами, поскольку вам не приходится беспокоиться о случайном выходе за границы массива, если количество элементов превысит ожидаемый порог. В РНР существует несколько функций для увеличения размеров массива. Некоторые из них были созданы для удобства программистов, привыкших работать с различными типами очередей и стеков (FIFO, FILO и т. д.), что отражается в названиях функций (push, pop, shift и unshift). Но даже если вы не знаете, что такое «очередь» или «стек», не огорчайтесь — в этих функциях нет ничего сложного.

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

array_push( )

Функция array_push( ) присоединяет (то есть дописывает в конец массива) один или несколько новых элементов. Синтаксис функции array_push( ):

int array_push(array массив, mixed элемент [, ...])

Длина массива возрастает прямо пропорционально количеству его элементов. Это продемонстрировано в следующем примере:

$languages = array("Spanish", "English", "French");

array_push($languages, "Russian", "German", "Gaelic");

// $languages = array("Spanish", "English", "French",

// "Russian", "German", "Gaelic")

У функции array_push( ), как и у многих стандартных функций РНР, существует «двойник» — функция аrrау_рор( ), предназначенная для извлечения элементов из массива. Главное различие между этими функциями заключается в том, что array_push( ) может добавлять несколько элементов одновременно, а аrrау_рор( ) удаляет элементы только по одному.

аrrау_рор( )

Результат работы функции аrrау_рор( ) прямо противоположен array_push( ) — эта функция извлекает (то есть удаляет) последний элемент из массива. Извлеченный элемент возвращается функцией. Синтаксис функции аrrау_рор( ):


аrrау_рор(аrrау массив)

При каждом выполнении аrrау_рор( ) размер массива уменьшается на 1. Рассмотрим пример:

$languages = array("Spanish", "English", "French",

"Russian", "German", "Gaelic");

$a_language = array_pop($languages):

// $a_language = "Gaelic"

$a_language = array_pop($languages):

// $a_language = "German"

// $languages = array ("Spanish", "English", "French", "Russian");

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

array_shift( )

Функция array_shift( ) аналогична аrrау_рор( ) с одним отличием: элемент удаляется из начала (левого края) массива. Все остальные элементы массива сдвигаются на одну позицию к началу массива. У функции array_shift( ) такой же синтаксис, как и у аггау_рор( ):

array_shift(array массив)

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

$languages = array("Spanish", "English", "French", "Russian");

$a_language = array_shift($languages); // $a_language = "Spanish";

// $languages = array("English", "French", "Russian");

array_unshift( )

Функция array_unshift( ) дополняет array_shift( ) — новый элемент вставляется в начало массива, а остальные элементы сдвигаются на одну позицию вправо. Синтаксис команды array_unshift( ):

1nt array_unshift(array массив, mixed переменная1 [....переменная2])

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



$languages = array("French", "Italian", "Spanish");

array_unshift($languages, "Russian", "Swahili", "Chinese");

// $languages = array("Russian", "Swahili", "Chinese",

// "French", "Italian", "Spanish");

array_pad( )

Функция array_pad( ) позволяет быстро увеличить массив до желаемого размера посредством его дополнения стандартными элементами. Синтаксис функции array_pad( ):

array arrap_pad(array массив, int размер, mixed значение):

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

Если размер положителен, массив дополняется справа, а если отрицателен — слева.

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

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

Пример дополнения массива с конца:

$weights = array(1, 3, 5, 10, 15, 25, 50);

$weights = array_pad($weights. 10, 100);

// Результат: $weights = array(1, 3, 5, 10, 15, 25, 50, 100, 100, 100);

Пример дополнения массива с начала:

$weights = array(1, 3, 5, 10, 15, 25, 50);

$weights = array_pad($weights, -10, 100);

// Результат: $weights = array(100, 100, 100, 1, 3, 5, 10, 15, 25, 50);

Неправильная попытка дополнения массива:

$weights = array(1, 3, 5, 10, 15, 25, 50);

$weights = array_pad($weigtits, 3, 100);

// Массив $weights не изменяется:

// $weights = array(1, 3, 5, 10, 15, 25, 50);


Другие полезные функции


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

array_merge( )

Функция arrayjnerge( ) сливает от 1 до N массивов, объединяя их в соответствии с порядком перечисления в параметрах. Синтаксис функции array_merge( ):

array array_merge(array массив1, array массив2, ..., array массивN]

Рассмотрим пример простого объединения массивов функцией arrayjnerge( );

$arr_1 = array("strawberry", "grape", "lemon");

$arr_2 = array("banana", "cocoa", "lime");

$arr_3 = array("peach", "orange");

$arr_4 = array_merge ($arr2, $arr_1, $arr_3):

// $arr_4 = array("banana", "cocoa", "lime", "strawberry", "grape", "lemon", "peach", "orange");

array_slice( )

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

array array_slice(array массив, int смещение [, int длина])

Значения параметров задаются по определенным правилам:

Если смещение положительно, начальная позиция возвращаемого фрагмента отсчитывается от начала массива.

Если смещение отрицательно, начальная позиция возвращаемого фрагмента отсчитывается от конца массива.

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

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

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

array_splice( )

Функция array_spl ice( ) отдаленно напоминает array_slice( ) — она заменяет часть массива, определяемую начальной позицией и необязательной длиной, элементами необязательного параметра-массива. Синтаксис функции array_splice( ):

array_splice(array входной_массив, int смещение, [int длина], [array заменяющий_массив]);


Значения параметров задаются по определенным правилам:



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

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

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

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

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

Если заменяющий_массив не указан, то элементы, заданные смещением и необязательной длиной, удаляются из массива.

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

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

Удаление всех элементов с пятой позиции до конца массива:

$pasta = array_splice($pasta, 5);

Удаление пятого и шестого элементов:

$pasta = array_splice($pasta. 5, 2);

Замена пятого и шестого элементов новыми значениями:

$pasta = array_splice($pasta, 5, 2, array("element1", "element2"));

Удаление всех элементов, начиная с пятого, до третьего элемента с конца массива:

$pasta = array_splice($pasta, 5, -3);

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

shuffle( )

Функция shuffle( ) сортирует элементы массива в случайном порядке. Синтаксис функции shuffle( ):

void shuffle(array массив);


В этой главе рассматривались массивы


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

создание индексируемых и ассоциативных массивов;
многомерные массивы;
отображение содержимого многомерных массивов;
поиск элементов;
добавление и удаление элементов;
размер массива;
сортировка;
другие полезные функции для работы с массивами.
Массивы являются очень удобными и универсальными средствами для работы с данными в web-приложениях. В примерах дальнейших глав массивы будут неоднократно использоваться для повышения эффективности и наглядности кода.
В главе 6 мы продолжим знакомиться с базовыми средствами РНР. На этот раз речь пойдет об объектно-ориентированных возможностях языка.

Многомерные массивы


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

$chessboard [1] [4] = "King"; // Двухмерный массив

$capitals["USA"] ["Ohio"] = "Columbus": // Двухмерный массив

$streets["USA"]["Ohio"]["Columbus"] = "Harrison"; // Трехмерный массив

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

$desserts = аrrау(

"Fruit Cup" => array (

"calories" => "low",

"served" -> "cold",

"preparation" => "10 minutes"

),

"Brownies" => array (

"calories" -> "high",

"served" => "piping hot",

"preparation" => "45 minutes"

)

);

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

$desserts["Fruit Cup"]["preparation"] // возвращает "10 minutes"

$desserts["Brownies"]["calories"] // возвращает "high"

Присваивание значений элементам многомерных массивов выполняется так же, как и в одномерных массивах:

$desserts["Cake"]["calories"] = "too many";

// Присваивает свойству "calories" объекта "Cake" значение "too many"

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



Перебор элементов


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

reset( )

Функция reset( ) переводит внутренний указатель текущей позиции в массиве к первому элементу. Кроме того, она возвращает значение первого элемента. Синтаксис функции reset( ):

mixed reset (array массив)

Рассмотрим следующий массив:

$fruits = array("apple", "orange", "banana");

Допустим, указатель текущей позиции в этом массиве установлен на элемент "orange". Команда:

$a_fruit = reset($fruits);

вернет указатель в начало массива, то есть на элемент "apple", и вернет это значение, если результат вызова reset( ) используется в программе. Возможен и упрощенный вариант вызова:

reset($fruits);

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

each ( )

Функция each( ) при каждом вызове выполняет две операции: она возвращает пару «ключ/значение», на которую ссылается указатель текущей позиции, и перемещает указатель к следующему элементу. Синтаксис функции each( ):

array each (array массив)

Для удобства each ( ) возвращает ключ и значение в виде массива из четырех элементов; ключами этого массива являются 0, 1, value и key. Возвращаемый ключ ассоциируется с ключами 0 и key, а возвращаемое значение — с ключами 1 и value.

В следующем примере функция each ( ) возвращает элемент, находящийся в текущей позиции:

// Объявить массив из пяти элементов

$spices = array("parsley", "sage", "rosemary", "thyme", "pepper");

// Установить указатель на первый элемент массива

reset($spices);

// Создать массив $a_sp1ce. состоящий из четырех элементов

$a_spice = each($spices);


В результате выполнения приведенного фрагмента массив $a_spice будет содержать следующие пары «ключ/значение»:



0 => 0;

1 => "parsley";

key => 0;

value => "parsley".

После этого строку "parsley" можно вывести любой из следующих команд:

print $a_spice[1]: print $a_spice["value"];

Функция each() обычно используется в сочетании с list( ) в циклических конструкциях для перебора всех или некоторых элементов массива. При каждой итерации each( ) возвращает либо следующую пару «ключ/значение», либо логическую ложь при достижении последнего элемента массива. Вернемся к массиву $spices; чтобы вывести все элементы на экран, можно воспользоваться следующим сценарием:

// Сбросить указатель текущей позиции

reset($spices);

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

while (list ($key, $val) = each ($spices) ) :

print "$val <br>"

endwhile;

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

Листинг 5.1.

Построение таблицы HTML по содержимому массива

// Объявить ассоциативный массив стран и языков $languages = array ("Country" => "Language",

"Spain" => "Spanish",

"USA" => "English",

"France" => "French",

"Russia" => "Russian");

// Начать новую таблицу

print "<table border=l>";

// Переместить указатель к позиции первого элемента

reset ($languages);

// Прочитать первый ключ и элемент

$hdl = key ($languages);

Shd2 = $languages[$hd1];

// Вывести первый ключ и элемент в виде заголовков таблицы

print "<tr><th>$hd1</th><th>$hd2</th></tr>";

next($languages);

// Выводить строки таблицы с ключами и элементами массива

while (list ($ctry,$lang) = each ($languages)) :



print "<tr><td>Sctry</td><td>$lang</td></tr>";

endwhile;

// Завершить таблицу print "</table>";

?>

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

Country

Language

Spain

Spanish

USA

English

France

French

Russia

Russian

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

end( )

Функция end( ) перемещает указатель к позиции последнего элемента массива. Синтаксис функции end( ):

end (array массив)

next( )

Функция next ( ) смещает указатель на одну позицию вперед, после чего возвращает элемент, находящийся в новой позиции. Если в результате смещения

указатель выйдет за пределы массива, next ( ) возвращает ложное значение. Синтаксис функции next ( ):

mixed next (array массив)

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

prev( )

Функция prev( ) аналогична next ( ) за одним исключением: указатель смещается на одну позицию к началу массива, после чего возвращается элемент, находящийся в новой позиции. Если в результате смещения указатель окажется перед первым элементом массива, prev( ) вернет ложное значение. Синтаксис функции prev( ):

mixed prev (array массив)

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

array_walk( )

Функция array_walk( ) позволяет применить функцию к нескольким (а возможно, всем) элементам массива. Синтаксис функции array_walk( ):

int array_walk(array массив, string имя_функции [, mixed данные])

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



function delete_dupes($element) {

static $last=""; if ($element == $last)

unset($element); else . ''

$last=$element:

}

$emails = array("blah@blah.com", "chef@wjgilmore.com", "blah@blah.com");

sort($emails);

reset($emails);

array_walk($emails,"delete_dupes");

// $emails = array("chef@wjgilmore.com", "blah@blah.com");

array_reverse( )

Функция array_reverse( ) позволяет легко перейти к противоположному порядку элементов, составляющих массив. Синтаксис функции array_reverse( ):

array array_reverse(array массив)

Рассмотрим пример использования функции array_reverse( ):

$us_wireproducers = array ("California", "Oregon", "New York". "Washington");

$us_wine_producers - array_reverse (Sus_wine_producers);

// $us_wine_producers = array ("Washington". "New York", "Oregon". "California");

При вызове array_reverse( ) для ассоциативного массива пары «ключ/значение» сохраняются, изменяется только порядок элементов массива.

array_flip( )

Функция array_flip( ) меняет местами ключи и значения элементов массива. Синтаксис функции array_flip( ):

array array_flip(array массив)

В следующем примере функция array_flip( ) меняет местами все ключи и значения элементов:

$languages = array("Spain" => "Spanish", "France" => "French", "Italy" => "Italian");

$languages = array_flip($languages);

// $languages = array("Spanish" => "Spain", // "French" => "France", // "Italian" => "Italy");

Помните: функция array_flip( ) не изменяет порядок элементов массива. Для этой цели используется функция array_reverse( ).


Поиск элементов массива


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

in_array( )

Функция i n_array ( ) проверяет, присутствует ли в массиве заданный элемент. Если поиск окажется удачным, функция возвращает TRUE, в противном случае возвращается FALSE. Синтаксис функции in_array( ):

bool in_array(mixed элемент, array массив)

Эта функция особенно удобна тем, что вам не приходится в цикле перебирать весь массив в поисках нужного элемента. В следующем примере функция in_array( ) ищет элемент "Russian" в массиве $languages:

$languages = array("English", "Gaelic", "Spanish"):

$exists = in_array("Russian", $languages); // $exists присваивается FALSE

$exists = in_array("English", $languages): // $exists присваивается TRUE

Функция in_array( ) часто встречается в управляющих конструкциях, когда ее возвращаемое значение (TRUE/FALSE) используется для выбора одного из двух вариантов продолжения. В следующем примере функция in_array( ) используется для выбора одного из двух вариантов в условной команде if:

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

$language = "French"; $email = "wjgilmore@hotmail.com";

// Если язык присутствует в массиве

if (in_array($language. $languages)) :

// Подписать пользователя на бюллетень.

// Обратите внимание: в РНР нет стандартной функции с именем

// subscribe_user(). В данном примере эта функция просто имитирует

// процесс подписки.

subscribe_user($email, $language);

print "You are now subscribed to the $language edition of the newsletter.";

// Язык отсутствует в массиве

else :

print "We're sorry, but we don't yet offer a $language edition of the newsletter".

endif;

Что происходит в этом примере? Допустим, переменные $language и $email содержат данные, введенные пользователем. Вы хотите убедиться в том, что указанный язык поддерживается вашей системой, и используете для этой цели функцию in_array( ). Если название языка присутствует в массиве, пользователь подписывается на бюллетень и получает соответствующее сообщение. В противном случае программа сообщает, что на указанном языке бюллетень не распространяется. Конечно, в настоящей программе пользователь не должен гадать, какие языки поддерживаются вашей программой. Задача решается при помощи раскрывающегося списка — эта тема подробно рассматривается в главе 10. Здесь этот пример всего лишь демонстрирует возможности работы с массивами.


array_keys( )

Функция array_keys( ) возвращает массив, содержащий все ключи исходного массива, переданного в качестве параметра. Если при вызове передается дополнительный параметр искомый_элемент, возвращаются только ключи, которым соответствует заданное значение; в противном случае возвращаются все ключи массива. Синтаксис функции array_keys( ):

array array_keys (array массив [, mixed искомый_элемент])

Рассмотрим пример использования функции array_keys( ) для получения ключа заданного элемента:

$great_wines = array ("Australia" => "Clarendon Hills 96",

"France" => "Comte George de Vogue 97",

"Austria" => "Feiler Artinger 97");

$great_labels = array_keys($great_wines);

// $great_labels = array("Australia", "France", "Austria");

$great_labels = array_keys($great_wines, "Clarendon Hills 96");

// $great_labels = array("Australia");

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

array_values( )

Функция array_values( ) возвращает массив, состоящий из всех значений исходного массива, переданного в качестве параметра. Синтаксис функции array_values( ):

array array_values(array массив)

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

// $great_wines = array ("Australia" => "Clarendon Hills 96",

// "France" => "Comte George de Vogue 97",

// "Austria" => "Feiler Artinger 97");

$great_labels = array_values($great_wines);

// $great_labels = аrrау("Clarendon Hills 96",

// "Comte George de Vogue 97",

// "Feiler Artinger 97");

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


Размер массива


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

$us_wine_producers = array ("Washington". "New York", "Oregon", "California");

for (Si = 0; Si < sizeof ($us_wine_producers); $i++) :

print "$us_wine_producers[$i]";

endfor;

Поскольку массив $us_wine_producers индексируется целыми числами, мы можем воспользоваться циклом for для циклического увеличения переменной-счетчика ($i) и вывода каждого элемента в массиве.

sizeof( )

Функция sizeof ( ) возвращает количество элементов в массиве. Синтаксис функции sizeof ( ):

int sizeof (array массив)

Вероятно, функция sizeof ( ) будет часто встречаться в ваших web-приложениях. Ниже приведен краткий пример ее использования (кстати, предыдущий пример тоже относится к числу стандартных применений sizeof ( )):

$pasta = array("bowties", "angelhair", "rigatoni");

$pasta_size = sizeof($pasta);

// $pasta_size = 3

У функции sizeof ( ) существует и другая, расширенная форма — count ( ) (см. ниже).

count( )

Функция count( ) выполняет ту же операцию, что и sizeof ( ), — она возвращает количество значений, содержащихся в массиве. Синтаксис функции count ( ):

int count (mixed переменная)

Единственное различие между sizeof ( ) и count( ) заключается в том, что в некоторых ситуациях count ( ) возвращает дополнительную информацию:

если переменная существует и является массивом, count ( ) возвращает количество элементов в массиве;

если переменная существует, но не является массивом, функция возвращает значение 1;

если переменная не существует, возвращается значение 0.

array_count_values( )

Функция array_count_values( ) является разновидностью sizeof ( ) и count ( ). Вместо общего количества элементов она подсчитывает количество экземпляров каждого значения в массиве. Синтаксис функции array_count_values( ):

array array_count_values(array массив):

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

$states = аrrау("ОН", "ОК", "СА", "РА", "ОН", "ОН", "РА", "АК");

$state_freq = array_count_values($states);

Массив $state_freq заполняется следующими ассоциативными парами «ключ/значение»:

$state_freq = аrrау("ОН" => 3, "ОК" => 1, "СА" => 1, "РА" => 2, "АК" => 1);



Сортировка массивов


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

Таблица 5.1.

Функции сортировки

Функция

Сортировка

Обратный порядок

Сохранение пар «ключ/значение»

sort

Значение

Нет

Нет

rsort

Значение

Да

Нет

asort

Значение

Нет

Да

arsort

Значение

Да

Да

ksort

Ключ

Нет

Да

krsort

Ключ

Да

Да

usort

Значение

?

Нет

uasort

Значение

?

Да

uksort

Ключ

?

Да

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

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

sort( )

Простейшая функция sort( ) сортирует элементы массива по возрастанию (от меньших к большим). Синтаксис функции sort ( ):

void sort (array массив)

Нечисловые элементы сортируются в алфавитном порядке в соответствии с ASCII-кодами. Следующий пример демонстрирует применение функции sort( ) при сортировке:

// Создать массив городов.

$cities = array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio");

// Отсортировать города по возрастанию

sort($cities);

// Перебрать содержимое массива и вывести все пары "ключ/значение".

for (reset($cities); $key = key ($cities); next ($cities)):

print("cities[$key] = $cities[$key] <br>";

endfor;

Этот фрагмент выводит следующий результат:

cities[0] = Anzio

cities[1] = Aprilia


cities[2] = Nettuno

cities[3] = Roma

cities[4] = Venezia

Как видите, массив $cities сортируется в алфавитном порядке. Одна из разновидностей этого способа сортировки реализована в функции asort( ), описанной ниже.

rsort( )

Функция rsort ( ) работает точно так же, как функция sort ( ), за одним исключением: элементы массива сортируются в обратном порядке. Синтаксис функции rsort ( ):

void rsort (array массив)

Вернемся к массиву $cities из предыдущего примера:

$cities array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio");

rsort($cities);

В результате сортировки массива $cities функцией rsort( ) элементы будут расположены в следующем порядке:

cities[0] = Venezia

cities[1] = Roma

cities[2] = Nettuno

cities[3] = Aprilia

cities[4] = Anzio

Массив $cities также сортируется, но на этот раз в порядке, обратном алфавитному. Одна из разновидностей этого способа сортировки реализована в функции arsort( ), описанной ниже.

asort( )

Функция asort( ) работает почти так же, как упоминавшаяся выше функция sort( ), однако она сохраняет исходную ассоциацию индексов с элементами независимо от нового порядка элементов. Синтаксис функции asort( ):

void asort(array массив)

Вернемся к массиву $cities:

$cities = array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio");

asort($cities):

В результате сортировки массива $cities функцией rsort() элементы будут расположены в следующем порядке:

cities[4] = Anzio

cities[0] = Aprilia

cities[1] = Nettuno

cities[2] = Roma

cities[3] = Venezia

Обратите внимание на индексы и сравните их с приведенными в описании функции sort ( ). Именно в этом и состоит различие между двумя функциями.

arsort( )

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

void arsort (array

массив)

Воспользуемся функцией arsort( ) для сортировки массива $cities:



$cities = array("Aprilia", "Nettuno", "Roma", "Venezia", "Anzio");

arsort($cities);

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

cities[3] = Venezia

cities[2] = Roma

cities[l] = Nettuno

cities[0] = Aprilia

cities[4] = Anzio

Обратите внимание на индексы и сравните их с приведенными в описании функции rsort( ). Именно в этом и состоит различие между двумя функциями.

ksort( )

Функция ksort( ) сортирует массив по ключам, сохраняя исходные ассоциации ключей со значениями. Синтаксис функции ksort( ):

void ksort (array массив)

Для примера рассмотрим массив, слегка отличающийся от исходного массива

$cities:

$wine_producers = array ("America" => "Napa Valley",

"Italy" => "Tuscany",

"Australia" => "Ruthgerlen",

"France" => "Loire",

"Chile" => "Rapel Valley");

В результате сортировки массива функцией ksort( ) элементы будут расположены в следующем порядке:

"America" => "Napa Valley"

"Australia" => "Ruthgerlen"

"Chile" => "Rapel Valley"

"France" => "Loire"

"Italy" => "Tuscany"

Сравните с результатами сортировки $wine_producers функцией sort ( ):

"America" => "Napa Valley"

"Australia" => "Tuscany"

"Chile" => "Ruthgerlen"

"France" => "Loire"

"Italy" => "Rapel Valley"

Более чем сомнительный результат!

krsort( )

Функция krsort( ) почти аналогична ksort( ), однако ключи сортируются в обратном порядке. Синтаксис функции krsort( ):

void krsort (array $массив)

Рассмотрим сортировку массива $wi reproducers функцией krsort( ):

$wine_producers = array ("America" => "Napa Valley",

"Italy" => "Tuscany",



"Australia" => "Ruthgerlen",

"France" => "Loire".

"Chile" => "Rapel Valley");

krsort($wine_producers);

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

"Italy" => "Tuscany"

"France" => "Loire"

"Chile" => "Rapel Valley"

"Australia" => "Ruthgerlen"

"America" => "Napa Valley"

Вероятно, описанных выше функций сортировки будет вполне достаточно для большинства случаев. Тем не менее, в некоторых ситуациях может возникнуть необходимость в определении собственных критериев сортировки. В РНР такая возможность реализована в трех стандартных функциях: usort( ), uasort( ) и uksort( ).

usort( )

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

void usort (array массив, string имя_функции)

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

Листинг 5.2.

Определение критерия сортировки для функции usort( )

$vocab = аrrау( "Socrates", "Aristophanes", "Plato", "Aeschylus", "Thesmophoriazusae");

function compare_length($str1, $str2) {

// Получить длину двух следующих слов

$length1 = strlen($str1);

$length2 = strlen($str2);

// Определить, какая строка имеет меньшую длину

if ($length1 == $length2) :

return 0;

elseif ($length1 < $length2) :



return -1;

else :

return 1;

endif;

}

// Вызвать usort() с указанием функции compare_length()

// в качестве критерия сортировки

usort ($vocab, "compare_length") :

// Вывести отсортированный список

while (list ($key, $val) = each ($vocab)) {

echo "$val<br>";

}

В листинге 5.2 функция compare_length ( ) определяет критерий сортировки массива. В данном случае это делается посредством сравнения длины передаваемых элементов. Функция-критерий должна получать два параметра, представляющих

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

Функции uasort( ) и uksort( ) представляют собой разновидности usort( ) с тем же синтаксисом. Функция uasort() сортирует массив по пользовательскому критерию с сохранением ассоциаций «ключ/значение». Функция uksort( ) также сортирует массив по пользовательскому критерию, однако сортируются не значения, а ключи.


Создание массивов


Массив

представляет собой совокупность объектов, имеющих одинаковые размер и тип. Каждый объект в массиве называется элементом массива. Создать новый массив в РНР несложно. При объявлении индексируемого массива после имени переменной ставится пара квадратных скобок ([ ]):

$languages [ ] = "Spanish";

// $languages[0] = "Spanish"

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

$languages[ ] = "English"; // $1anguages[l] = "English"; 

$languagest ] = "Gaelic"; // $languages[2] = "Gaelic";

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

$languages[15] = "Italian"; 

$languages[22] = "French";

Ассоциативные массивы создаются аналогичным образом:

$languages["Spain"] = "Spanish"; 

$languages["France"] = "French";

При создании массивов используются три стандартные языковые конструкции:

аrrау( );

list( );

range( ).

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

аггау( )

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

array array ( [элемент1, элемент2...] )

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

$languages = array ("English". "Gaelic". "Spanish");

// $languages[0] = "English". $languages[1] = "Gaelic",

// $languages[2] = "Spanish"


А вот как array( ) используется при создании ассоциативных массивов:

$languages = array("Spain" => "Spanish",

"Ireland" => "Gaelic".

"United States" => "English");

// $languages["Spain"] = "Spanish"

// $languages["Ireland"] = "Gaelic"

// $languages["United States"] = "English"

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

list( )

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

void list (переменная1 [. переменная2 , ...] )

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

профессия и любимый цвет); компоненты записи разделяются вертикальной чертой (|). Типичная строка выглядит так:

Nino Sanzi|Professional Golfer|green

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

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

while ($line = fgets ($user_file. 4096)) :

// Разделить строку функцией split( ).

// Компоненты присваиваются переменным Sname. $occupation и Scolor.

list ($name, $occupation, $color) = split( "|", $line);

// Отформатировать и вывести данные

print "Name: Sname <br>";

print "Occupation: Soccupation <br>";



print "Favorite color: Scolor <br>";

endwhile;

Каждая строка файла читается, форматируется и выводится в следующем виде:

Name: Nino Sanzi

Occupation: Professional Golfer

Favorite color: green

В приведенном примере применение list( ) зависит от разделения строки на элементы функцией split( ). Элементы, полученные в результате деления, присваиваются, соответственно, переменным $name, $occupation и $color. Дальше все сводится к форматированию данных для вывода в конкретном браузере. Удобные средства лексического анализа текстовых файлов являются одной из сильных сторон РНР. Эта тема подробно рассматривается в главах 7 и 8.

range ( )

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

array range (int нижняя_граница, int верхняя граница)

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

$lottery = range(0,9);

// $lottery = array(0,1,2,3,4,5,6,7,8,9)

Как видно из приведенного фрагмента, в параметрах range( ) был указан интервал от 0 до 9 и массив $lottery был заполнен целыми числами из этого интервала.


Ссылки на многомерные массивы


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

print "Brownies are good, but the calories content is ".

$desserts["Brownies"]["calories"];

Во-вторых, ссылку на элемент многомерного массива можно заключить в фигурные скобки ({ }):

print "Brownies are good, but the calories content is

{$desserts[Brownies][calories]}";

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



Абстрактные классы


В некоторых ситуациях бывает удобно создать класс, объекты которого никогда не создаются (данный класс нужен всего лишь как базовый для создания производных классов). Такие классы называются абстрактными. Абстрактные классы

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

В РНР отсутствует прямая поддержка абстрактных классов, однако существует простое обходное решение — достаточно определить в «абстрактном» классе конструктор и включить в него вызов die( ). Вернемся к классам из листинга 6.4. Скорее всего, вам никогда не придется создавать экземпляры классов Land и Vehicle, поскольку они не могут представлять физические объекты. Для представления реальных объектов (например, автомобилей) следует создать класс, производный от этих классов. Следовательно, чтобы предотвратить возможное создание объектов классов Land и Vehicle, необходимо включить в их конструкторы вызовы die( ), как показано в листинге 6.5.

Листинг 6.5.

Создание абстрактных классов

<?

class Vehicle {

Объявления атрибутов...

function Vehicle() }

die ("Cannot create Abstract Vehicle class!");

}

Объявления других методов...

}

class Land extends Vehicle {

Объявления атрибутов...

function Land() }

die ("Cannot create Abstract Land class!");

}

Объявления других методов. } class Car extends Land {

Объявления атрибутов...

Объявления методов...

}

?>

Попытка создания экземпляра этих абстрактных классов приведет к выдаче сообщения об ошибке и завершению программы.



Деструкторы


Как упоминалось ранее, в РНР отсутствует непосредственная поддержка деструкторов. Тем не менее, вы можете легко имитировать работу деструктора, вызывая функцию РНР unset( ). Эта функция уничтожает содержимое переменной и возвращает занимаемые ею ресурсы системе. С объектами unset( ) работает так же, как и с переменными. Допустим, вы работаете с объектом $Webpage. После завершения работы с этим конкретным объектом вызывается функция

unset($Webpage);

Эта команда удаляет из памяти все содержимое $Webpage. Действуя в духе инкапсуляции, можно поместить вызов unset( ) в метод с именем destroy( ) и затем вызвать его:

$Website->destroy( );

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



Функции для работы с классами и объектами


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

get_class_methods( )

Функция get_class_methods( ) возвращает массив имен методов класса с заданным именем. Синтаксис функции get_class_methods( ):

array get_class_methods (string имя_класса)

Простой пример использования get_class_methods( ) приведен в листинге 6.7.

Листинг 6.7.

Получение списка методов класса

<?

...

class Airplane extends Vehicle { 

var $wingspan;

function setWingSpan($wingspan) { 

$this->wingspan = $wingspan;

}

function getWingSpan() { 

return $this->wingspan;

}

}

$cls_methods = get_class_methods(Airplane);

// Массив $cls_methods содержит имена всех методов,

// объявленных в классах "Airplane" и "Vehicle"

?>

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

get_class_vars( )

Функция get_class_vars( ) возвращает массив имен атрибутов класса с заданным именем. Синтаксис функции get_class_vars( ):

array get_class_vars (string имя_класса)

Пример использования get_class_vars( ) приведен в листинге 6.8.

Листинг 6.8.

Получение списка атрибутов класса функцией get_class_vars( )

<?

class Vehicle {

var $model;

var $current_speed; }

class Airplane extends Vehicle {

var Swingspan; } $a_class = "Airplane";

$attribs = get_class_vars($a_class);

// $attribs = array ( "wingspan", "model", "current_speed")

?>

Массив $attribs заполняется именами всех атрибутов класса Airplane.

get_object_vars( )

Функция get_object_vars( ) возвращает ассоциативный массив с информацией обо всех атрибутах объекта с заданным именем. Синтаксис функции get_object_vars( ):

array get_object_vars (object имя_обьекта)


Пример использования функции get_object_vars( ) приведен в листинге 6.9.

Листинг 6.9.

Получение информации о переменных объекта 

<?

class Vehicle {

var Swheels;

}

class Land extends Vehicle {

var Sengine;

}

class car extends Land {

var $doors:

function car($doors, $eng, $wheels) {

$this->doors = $doors;

$this->engine = $eng;

$this->wheels = $wheels;

}

function get_wheels() {

return $this->wheels;

}

}

$toyota = new car(2,400,4);

$vars = get_object_vars($toyota);

while (list($key, $value) = each($vars)) :

print "$key ==> $value <br>";

endwhile;

// Выходные данные:

// doors ==> 2

// engine ==> 400

// wheels ==> 2

?>

Функция get_object_vars( ) позволяет быстро получить всю информацию об атрибутах конкретного объекта и их значениях в виде ассоциативного массива.

method_exists( )

Функция method_exists( ) проверяет, поддерживается ли объектом метод с заданным именем. Если метод поддерживается, функция возвращает TRUE, в противном случае возвращается FALSE. Синтаксис функции method_exists( ):

bool method_exi sts (object имя_обьекта. string имя_метода)

Пример использования метода method_exists( ) приведён в листинге 6.10.

Листинг 6.10.

Проверка поддержки метода объектом при помощи функции method_exists()

<?

class Vehicle {

...

}

class Land extends Vehicle {

var $fourWheel;

function setFourWheel Drive() {

$this->fourWeel = 1;

}

}

// Создать объект с именем $саr

$car = new Land;

// Если метод "fourWheelDrive" поддерживается классом "Land"

// или "Vehicle", вызов method_exists возвращает TRUE;

// в противном случае возвращается FALSE.

// В данном примере method_exists() возвращает TRUE.

if (method_exists($car, "setfourWheelDrive")) :

print "This car is equipped with 4-wheel drive";

else :

print "This car is not equipped with 4-wheel drive";

endif;

?>

В листинге 6.10 функция method_exists ( ) проверяет, поддерживается ли объектом $car метод с именем setFourWheelDrive( ). Если метод поддерживается, функция возвращает логическую истину и фрагмент выводит соответствующее сообщение. В противном случае возвращается FALSE и выводится другое сообщение.



get_class( )

Функция get_class( ) возвращает имя класса, к которому относится объект с заданным именем. Синтаксис функции get_class( ):

string get_class(object имя_объекта);

Пример использования get_class( ) приведен в листинге 6.11.

Листинг 6.11.

Получение имени класса функцией get_class( )

<?

class Vehicle {

...

class Land extends Vehicle {

...

}

// Создать объект с именем $саr $car = new Land;

// Переменной $class_a присваивается строка "Land"

$class_a = get_class($car);

?>

В результате переменной $class_a присваивается имя класса, на основе которого был создан объект $саr.

get_parent_class( )

Функция get_parent_class( ) возвращает имя родительского класса (если он есть) для объекта с заданным именем. Синтаксис функции get_parent_dass( ):

string get_parent_class (object имя_обьекта);

Листинг 6.12 демонстрирует использование get_parent_class( ).

Листинг 6.12. Получение имени родительского класса функцией get_parent_class( )

<?

class Vehicle {

...

}

class Land extends Vehicle {

...

}

// Создать объект с именем $саr $саr = new Land;

// Переменной $parent присваивается строка "Vehicle"

$parent = get_parent_dass($car);

?>

Как и следовало ожидать, при вызове get_parent_class( ) переменной $parent будет присвоена строка "Vehicle".

is_subclass_of( )

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

bool is_subclass_of (object объект, string имя_класса)

Использование is_subclass_of( ) продемонстрировано в листинге 6.13.

Листинг 6.13.

Использование функции is_subdass_of( )

<?

class Vehicle {

...

}

class Land extends Vehicle {

...

}

$auto = new Land;

// Переменной $is_subclass присваивается TRUE

$is_subclass = is_subclass_of($auto, "Vehicle");

?>

В листинге 6.13 переменной $is_subclass( ) присваивается признак того, принадлежит ли объект $auto к субклассу родительского класса Vehicle. В приведенном фрагменте $auto относится к классу Vehicle; следовательно, переменной $is_subclass( ) будет присвоено значение TRUE.

get_declared_classes( )

Функция get_declared_classes( ) возвращает массив с именами всех определенных классов (листинг 6.14). Синтаксис функции get_declared_classes( ):

array get_declared_classes( )

Листинг 6.14.

Получение списка классов функцией get_declared_classes( )

<?

class Vehicle {

...

}

class Land extends Vehicle {

...

}

$declared_classes = get_declared_classes();

// $declared_classes = array("Vehicle", "Land")

?>


В этой главе были представлены


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

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

Классы, объекты и объявления методов


Классы

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

Чтобы лучше понять связь между классами и объектами, можно представить класс как «чертеж» для создания объектов. По чертежу «изготавливаются» разные объекты, обладающие одними и теми же базовыми характеристиками (например, при строительстве дома — одна дверь, два окна и определенная толщина стены). Тем не менее, каждый объект существует независимо от других — изменение его характеристик никак не влияет на характеристики других объектов; например, в уже построенном доме можно прорубить дополнительное окно. Важно помнить, что у объектов все равно остается общая характеристика — количество окон.

Класс также можно рассматривать как тип данных (см. главу 2), а объект — как переменную (по аналогии с тем, как переменная $counter относится к целому, а переменная $last_name — к строковому типу). Программа может одновременно работать с несколькими объектами одного класса как с несколькими переменными целого типа. Общий формат классов РНР приведен в листинге 6.1.

Листинг 6.1. Объявление классов в РНР

class Class_name {

var $attribute_1;

...

var $attribute_N;

function function1() {

...

}

...

function functionN() {

...

} // end Class_name

Подведем итоги: объявление класса должно начинаться с ключевого слова class (подобно тому, как объявление функции начинается с ключевого слова function). Каждому объявлению атрибута, содержащегося в классе, должно предшествовать ключевое слово van. Атрибуты могут относиться к любому типу данных, поддерживаемых в РНР; их можно рассматривать как переменные с небольшими различиями, о которых вы узнаете в этой главе. После объявлений атрибутов следуют объявления методов, очень похожие на типичные объявления функций.


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

Методы часто используются для работы с атрибутами классов. При ссылках на атрибуты внутри методов используется специальная переменная $this. Синтаксис методов продемонстрирован в следующем примере:

<?

class Webpage {

van $bgcolor;

function setBgColor($color) {

$this->bgcolor = $color;

}

function getBgColor() {

return $this->bgcolor;

}

}

?>

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

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

знак доллара ($) ставится перед переменной $this, но не перед именем атрибута (как у обычной переменной).


Конструкторы


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

Листинг 6.2.

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

<?

class Webpage {

var $bgcolor;

function Webpage($color) {

$this->bgcolor = $color;

}

}

// Вызвать конструктор класса Webpage

$page = new Webpage("brown");

?>

Раньше создание объекта и инициализация атрибутов выполнялись раздельно. Конструкторы позволяют выполнить эти действия за один этап.

Интересная подробность: в зависимости от количества передаваемых параметров могут вызываться разные конструкторы. Например, в листинге 6.2 объекты класса Webpage могут создаваться двумя способами. Во-первых, вы можете вызвать конструктор, который просто создает объект, но не инициализирует его атрибуты:

$page = new Webpage;

Во-вторых, объект можно создать при помощи конструктора, определенного в классе, — в этом случае вы создаете объект класса Webpage и присваиваете значение его атрибуту bgcolor:

$page = new Webpage("brown");



Нарушение инкапсуляции


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

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

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

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



Перегрузка методов


Перегрузкой методов

называется определение нескольких методов с одинаковыми именами, но разным количеством или типом параметров. Как и в случае с абстрактными классами, в РНР эта возможность не поддерживается, но существует простое обходное решение, приведенное в листинге 6.6.

Листинг 6.6.

Перегрузка методов

<?

class Page {

var $bgcolor;

var $textcolor;

function Page() {

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

// и вызвать метод с нужным именем

$name = "Page".func_num_args();

// Call $name with correct number of arguments passed in

if ( func_num_args() == 0 ) :

$this->$name();

else :

$this->$name(func_get_arg(0));

endif;

}

function Page0() {

$this->bgcolor = "white";

$this->textcolor = "black";

print "Created default page";

}

function Page1($bgcolor) {

$this->bgcolor = $bgcolor;

$this->textcolor = "black";

print "Created custom page";

}

}

$html_page - new Page("red");

?>

В этом примере при создании нового объекта с именем $html_page передается один аргумент. Поскольку в классе был определен конструктор по умолчанию (Раgе( )), вызывается именно он. Однако конструктор по умолчанию всего лишь выбирает, какому из конструкторов (Page0( ) или Page1( )) следует передать управление. При выборе конструктора используются функции func_num_args( ) и func_get_arg( ), которые, соответственно, определяют количество аргументов и читают эти аргументы.

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



Простое и иерархическое наследование


Как говорилось выше, класс является шаблоном, по которому создаются реальные объекты с определенными характеристиками и функциями. Нетрудно представить себе ситуацию, при которой такой объект является частью другого объекта. Например, автомобиль можно считать частным случаем категории «транспортное средство», к которой относятся и самолеты. Хотя разные типы транспортных средств сильно отличаются друг от друга, все они характеризуются атрибутами из общего набора (количество колес, мощность, максимальная скорость, модель и т. д.). Пусть конкретные значения этих атрибутов сильно различаются — атрибуты все равно присущи всем транспортным средствам. Таким образом, субклассы «автомобиль» и «самолет» наследуют общий набор базовых характеристик от суперкласса

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

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

class Class_name2 extends Class_name1 {

объявления атрибутов;

объявления методов;

}

Ключевое слово extends говорит о том, что класс Class_name2 наследует все характеристики класса Class_name1.

Помимо возможности многократного использования кода, наследование обладает еще одним важным преимуществом — снижается вероятность ошибок при

модификации программы. Например, в иерархии, изображенной на рис. 6.1, изменения в классе «автомобиль» никак не отразятся на коде (и данных) класса «самолет», и наоборот.

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

Рис. 6.1.

Иерархия транспортных средств

В листинге 6.3 приведены классы, моделирующие иерархию, изображенную на рис. 6.1.


Листинг 6.3.

Представление различных типов транспортных средств при помощи наследования

<?

// Транспортное средство

class Vehicle {

var $model;

var $current_speed;

function setSpeed($mph) {

$this->current_speed = $mph;

}

function getSpeed() {

return $this->current_speed;

}

}

// Автомобиль

class Auto extends Vehicle { 

var $fue1_type;

function setFuelType($fuel) {

$this->fuel_type = $fuel;

}

function getFuelType() {

return $this->fuel_type;

}

}

// Самолет

class Airplane extends Vehicle {

var $wingspan;

function setWingSpan($wingspan) {

$this->wingspan = $wingspan;

}

function getWingSpan() {

return $this->wingspan;

}

}

?>

Объекты этих классов создаются следующим образом:

$tractor = new Vehicle;

$gulfstream = new Airplane;

Приведенные команды создают два объекта. Первый объект, $tractor, относится к классу Vehicle. Второй объект, $gulfstream, относится к классу Airplane и потому обладает как общими характеристиками класса Vehicle, так и уточненными характеристиками класса Airplаne.

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

class Airplane extends Vehicle extends Building...

Многоуровневое наследование

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



Краткий пример, приведенный в листинге 6.4, подчеркивает некоторые важные аспекты многоуровневого наследования в РНР.

Листинг 6.4.

Многоуровневое наследование

<?

class Vehicle {

Объявления атрибутов...

Объявления методов...

}

class Land extends Vehicle {

  Объявления атрибутов...

  Объявления методов...

}

class Саr extends Land {

  Объявления атрибутов...

  Объявления методов...

}

$nissan = new Car;

?>

Объект $nissan содержит все атрибуты и методы классов Саr, Land и Vehicle. Как видите, программа получается исключительно модульной. Допустим, когда-то в будущем вы захотите добавить в класс Land новый атрибут. Нет проблем: внесите соответствующие изменения в класс Land, и этот атрибут немедленно становится доступным для классов Land и Саr, не влияя на функциональность других классов. Таким образом, модульность кода и гибкость относятся к числу основных преимуществ ООП.

 


Рис. 6.2.

Многоуровневое наследование в иерархии Vehicle

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


РНР и ООП


Хотя РНР обладает общими объектно-ориентированными возможностями, он не является полноценным ОО-языком (например, таким, как C++ или Java). В частности, в РНР не поддерживаются следующие объектно-ориентированные возможности:

множественное наследование;

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

абстрактные классы;

перегрузка методов;

перегрузка операторов (это связано с тем, что РНР является языком со свободной типизацией, — за дополнительной информацией обращайтесь к главе 2);

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

деструкторы;

полиморфизм.

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



Создание объектов и работа с ними


Объекты создаются оператором new. Например, объект класса Webpage создается следующей командой:

$home_page = new Webpage;

Новый объект с именем $some_page обладает собственным набором атрибутов и методов, перечисленных в классе Webpage. Для изменения значения атрибута $bgcolor, принадлежащего этому конкретному объекту, можно воспользоваться определенным в классе методом setBgColor( ):

$some_page->setBgColor("black");

Следует помнить, что РНР также позволяет явно получить значение атрибута указанием имен объекта и атрибута:

$some_page->bgcolor;

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