На главную Назад
Добро пожаловать, уважаемый посетитель!

 

ГЛАВА 4

Функции

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

Данная глава посвящена функциям РНР, ихопределению и применению на практике. Хотяосновное внимание в ней уделяетсяопределению и вызову пользовательскихфункций, необходимо помнить и о том, что вРНР существуют сотни стандартных функций.Стандартные функции работают точно так же,как пользовательские, и обеспечиваютзаметную экономию времени при созданииновых приложений. Обновленный списокстандартных функций РНР можно найти поадресу http://www.php.net.

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

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

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

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

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

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

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

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

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

тело функции

}

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

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

function display_copyright() {

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

}

Если ваш 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, в общем обзоре области видимостипеременных и ее отношения к функциям.

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

Функции можно вызывать внутри другихфункций - по аналогии с тем, как однауправляющая конструкция (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 c 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.

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

Ситуация, при которой функция многократновызывает сама себя, пока не будет выполненонекоторое условие, открывает замечательныевозможности. При правильном использовании рекурсивныефункцииуменьшают объем программы и делают ее болеевыразительной. Рекурсивные функцииособенно часто используются при выполненииповторяющихся действий - например, припоиске в файлах/массивах и построенииграфических изображений (например,фракталов). Классическим примеромрекурсивных функций, встречающимся вомногих курсах программирования, являетсясуммирование чисел от 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

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

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

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

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

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

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

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

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

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

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

" print "Benvenuti al PHP Recipes.";

}

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

function english( ) {

print "Welcome to PHP Recipes.";

}

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

$language = "italian":

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

$language( );

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

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

Библиотеки функций - одно из самыхэффективных средств экономии времени припостроении приложений. Предположим, вынаписали серию функций для сортировкимассива. Вероятно, эти функции будутнеоднократно использоваться в разныхприложениях. Вместо того чтобы постояннопереписывать эти функции в новый сценарийили копировать их через текстовый буфер,гораздо удобнее разместить все функциисортировки в отдельном файле и присвоитьему легко узнаваемое имя (например,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);

Итоги

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

  • определение и вызов функций;
  • вложенные функции;
  • возврат значений из функции;
  • рекурсивные функции;
  • функции-переменные;
  • построение библиотек функций.

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

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