§5. Функции
5.1. Понятие функции и ее синтаксис.
В PHP имеется около двух тысяч готовых к использованию встроенных функций, однако их описанию будет посвящена отдельная часть учебника. Теперь же мы рассмотрим лишь общие вопросы, касающиеся самого понятия функции и ее синтаксиса, а также правил использования функций. Что же такое функция?
Функция – это блок программного кода на языке PHP, который определяется один раз и далее может быть использован многократно.
Рассмотрим создание функции на примере 5.1.
<?php $n_1=5; //Присвоили первой переменной значение $n_2=10; //Присвоили второй переменной значение function my_func($arg_1, $arg_2) { //Объявили функцию с 2 аргументами $sum = $arg_1+$arg_2; return $sum; //Значение возвращаемое функцией при ее вызове } echo my_func($n_1,$n_2); //Вызываем функцию и выводим ее значение, т.е. 15 /* Условно объявление функции и ее вызов можно представить так */ /* function my_func($arg_1, $arg_2, ..., $arg_n){ 'исполняемый код' return 'возвращаемое значение'; } my_func($arg_1, $arg_2, ...); */ ?>
Пример 5.1. Создание функций в PHP
Таким образом, чтобы объявить функцию, необходимо использовать ключевое слово function, затем указать имя функции, после чего в круглых скобках перечислить принимаемые аргументы, и при помощи фигурных скобок сформировать тело функции.
Имена функций в PHP являются регистронезависимыми (в отличие от имен переменных), но следуют тем же правилам, что и другие идентификаторы, т.е. корректное имя функции должно начинаться с буквы или знака подчеркивания, за которым может следовать любое количество букв, цифр или знаков подчеркивания. Об аргументах мы поговорим чуть ниже, а что касается тела функции, то в нем можно использовать любой корректный php-код, включая другие функции и объявления классов. При этом, если оператор return отсутствует, то функция не имеет возвращаемого значения. Она просто выполняет указанный кусок программного кода.
Следует отметить, что в PHP объявления функций поднимаются в начало кода скрипта, поэтому их разрешается использовать еще до момента определения за исключением случаев, когда функции объявляются условно или внутри других функций и, как следствие, должны быть определены до момента их вызова (см. пример 5.2).
<?php $n_1=5; //Присвоили первой переменной значение $n_2=10; //Присвоили второй переменной значение //echo my_func($n_1,$n_2); //Выведет ошибку, т.к. функция определяется условно //и вызывается до ее объявления if($n_1<=$n_2){ function my_func($arg_1,$arg_2) { //Объявили функцию внутри $sum = $arg_1+$arg_2; //условного оператора return $sum; //Значение возвращаемое функцией при ее вызове } } echo my_func($n_1,$n_2).'<br>'; //Выведет 15, т.к. условие истинно и //функция была определена /* В случае, если бы условие оператора if (например, при $arg_1>$arg_2) не выполнилось, то и последний вызов функции также вызвал бы ошибку, поскольку функция не была бы определена */ function func_out(){ //Объявили внешнюю функцию function my_func_2($arg_1, $arg_2){ //Объявили функцию внутри другой функции $mult = $arg_1*$arg_2; return $mult; //Значение возвращаемое функцией при ее вызове } } //echo my_func_2($n_1,$n_2); //Выведет ошибку, т.к. функция вызывается до вызова //внешней функции, когда она еще не была определена func_out(); //Вызвали внешнюю функцию, теперь определена и вложенная функция echo my_func_2($n_1,$n_2); //Выведет 50 ?>
Пример 5.2. Условное объявление функций
Добавим, что:
- все функции и классы PHP имеют глобальную область видимости, т.е. они могут быть вызваны вне функции, даже если были определены внутри и наоборот;
- PHP не поддерживает перегрузку функций;
- в PHP отсутствует возможность переопределить или удалить объявленную ранее функцию;
- также в PHP допустимы рекурсивные вызовы функций (см. пример 5.3).
<?php function recursion($a){//Определяем рекурсивную функцию if($a<20){ echo "$a"; recursion($a+1); //Вызываем саму функцию } } recursion(1); //Выведет 12345678910111213141516171819 ?>
Пример 5.3. Использование рекурсивных функций
Следует избегать вызовов рекурсивных функций и методов с более чем 100-200 уровней рекурсии, т. к. это может привести к сбою в работе выполняемого скрипта.
5.2. Аргументы функции.
Аргументы функции перечисляются через запятую в круглых скобках после имени определяемой функции и являются ее локальными переменными. В качестве значений аргументов могут использоваться любые выражения, которые передаются функции для обработки и могут быть вычислены. При этом функция может вообще не принимать аргументов, но если аргументы присутствуют, то они вычисляются слева направо.
По умолчанию аргументы передаются функции по значению, но также поддерживается передача аргументов по ссылке и значения по умолчанию.
При передаче аргументов по значению исходное значение внешней переменной остается постоянным при изменении значения аргумента внутри функции. Если же требуется, чтобы функция могла влиять на значение внешней переменной, необходимо передавать ей аргументы по ссылке. Делается это при помощи символа амперсанда '&' перед именем аргумента в описании функции (см. пример 5.4).
<?php $n_1=1; //Присвоили первой переменной значение $n_2=2; //Присвоили второй переменной значение function my_func(&$a_1,$a_2){ //Объявили функцию с 2 аргументами, первый из //которых передается по ссылке, а второй по значению $a_1=10; $a_2=20; } my_func($n_1,$n_2); //Вызвали функцию echo $n_1." ".$n_2; //Выведет 10 2 ?>
Пример 5.4. Передача аргументов функции по значению и по ссылке
Также в PHP имеется возможность использовать для аргументов функции значения по умолчанию, которые представляют собой значения, используемые в случае, если данному аргументу при вызове функции не будет передано вообще никакого значения. Для того, чтобы задать значение аргумента по умолчанию, необходимо в определении функции присвоить желаемое значение данному аргументу (см. пример 5.5). При этом значения по умолчанию могут иметь как аргументы, передаваемые по значению, так и аргументы, передаваемые по ссылке. Однако в любом случае все аргументы, которым присваиваются значения по умолчанию, должны идти в списке после аргументов, у которых значения по умолчанию отсутствуют. Кроме того, в качестве значений по умолчанию могут использоваться только константные выражения, а также массивы и значение NULL. Использовать, например, переменные или вызовы функций нельзя.
<?php $n_1=1; $n_2=7; function my_func($a_1,$a_2=2,$a_3=3){ //Объявили функцию с 3 аргументами, два //из которых имеют значения по умолчанию $sum=$a_1+$a_2+$a_3; //1+7+3=11, третье значение использовано по умолчанию return $sum; //Возвращаем сумму аргументов } echo my_func($n_1,$n_2); //Выведет 11 ?>
Пример 5.5. Использование значений аргументов по умолчанию
Если точное количество аргументов, передаваемых функции, заранее не известно, можно использовать список аргументов переменной длины. Такой список формируется при помощи специальной переменной, перед которой ставится многоточие '...'. В результате аргументы будут переданы в указанную переменную в виде массива (см. пример 5.6).
<?php function m_sum(...$args){ //Указали, что функция принимает аргументы //в массив $args переменной длины $sum=0; //Присвоили начальное значение сумме аргументов foreach($args as $value){ //Перебираем все значения массива аргументов $sum+=$value; } return $sum; // Возвращаем сумму аргументов функции } echo m_sum(1,2,3,4,5); //Выведет 15 ?>
Пример 5.6. Использование списка аргументов переменной длины
Перед многоточием можно указывать обычные аргументы, при этом все остальные аргументы, переданные функции будут занесены в массив. Более того, перед многоточием можно указывать тип аргументов, которые могут быть занесены в массив, а также знак амперсанда '&' для передачи аргументов по ссылке (см. пример 5.7).
<?php $sum=0; $a_1=1; $a_2=2; function m_sum($sum, &...$args){ //Указали, что функция принимает аргументы //по ссылке в массив $args переменной длины foreach($args as $value){ //Перебираем все значения массива аргументов $sum+=$value; } $args[0]=10; //Изменили значение первого аргумента массива $args return $sum; //Возвращаем сумму аргументов массива $args } echo m_sum($sum,$a_1,$a_2).'<br>'; //Выведет 3 (1+2) echo $a_1; //Выведет 10, т.к. значение было передано функции по ссылке и //затем изменено функцией на 10 ?>
Пример 5.7. Особенности использования списка аргументов переменной длины
Разрешается использовать многоточие '...' для разворачивания массива, передаваемого в качестве аргумента функции, в аргументы функции в виде его элементов (см. пример 5.8).
<?php function m_sum_2($a,$b){ //Создаем функцию с двумя аргументами return $a+$b; //Возвращаем сумму аргументов } echo m_sum_2(...[1, 2]).'<br>'; //Разворачиваем переданный функции массив. Выведет 3 $a_3=[1, 2]; //Присваиваем массив переменной echo m_sum_2(...$a_3); //Разворачиваем переданный функции массив. Выведет 3 ?>
Пример 5.8. Разворачивание массива аргументов, переданного функции при вызове
Отметим, что доступ к аргументам функции можно получить также при помощи специальных функций в особенности, если функция принимает большее количество аргументов, чем она ожидает получить (см. пример 5.9):
- func_get_args() – возвращает массив, состоящий из аргументов функции;
- func_get_arg(n) – возвращает указанный аргумент функции, где n=0,1,2,... – номер аргумента в списке, который начинается с нуля (напомним, что аргументы вычисляются слева направо);
- func_num_args() – возвращает количество реально переданных функции аргументов.
<?php function m_sum($a) { //Указали, что функция ожидает получить один аргумент $sum = 0; $m_arg=func_get_args(); //Присвоили массив переданных функции аргументов foreach($m_arg as $value){ //Цикл по массиву аргументов, переданных функции $sum += $value; } echo func_num_args().'<br>'; //Выведет количество аргументов, переданных функции echo func_get_arg(2); //Выведет значение третьего аргумента, переданного функции return $sum; //Возвращаем сумму аргументов функции } m_sum(1,2,8); //Вызвали функцию. Выведет 38, т.е. количество переданных //аргументов равно 3, а значение третьего аргумента равно 8 //(нумерация элементов начинается с нуля) ?>
Пример 5.9. Использование специальных функций для работы аргументами
Отметим, что доступ к аргументам функции можно получить также при помощи специальных функций в особенности, если функция принимает большее количество аргументов, чем она ожидает получить (см. пример 5.9):
5.3. Контроль типа аргументов.
Для того, чтобы объявить тип передаваемого функции аргумента, необходимо указать его перед самим аргументом. В результате, если передаваемый аргумент будет иметь тип отличный от указанного, PHP по умолчанию будет пытаться привести его значение к скалярному типу. Если же преобразование окажется невозможным, интерпретатор сообщит об ошибке (см. пример 5.10).
<?php function my_sum(int $a,int $b,int $c=null){ //Функция ожидает три целых числа $sum=$a+$b; if($c===null){ //Убеждаемся, что null принято функцией echo 'null'.'<br>'; } return $sum; //Возвращаем сумму аргументов функции } echo my_sum('5',2); //Т.к. данная строка может быть преобразована //в число, ошибки не возникнет и будет выведено '7' //echo my_sum('Строка',2); //Будет выведена ошибка //echo my_sum([2,5],2); //Будет выведена ошибка ?>
Пример 5.10. Контроль типа аргументов функции
В качестве разрешенных для контроля типов могут использоваться следующие типы данных: array, object (рассмотрим чуть позже), callable, bool, float, integer, string. Здесь все типы, перечисленные после callable, стали доступны только в PHP 7.0.0. Если аргументу в качестве значения по умолчанию назначается NULL, то оно будет использовано при вызове функции без передачи аргументу значения в независимости от объявленного типа переменной.
Также в PHP 7.0.0 для отдельных php-файлов добавлена возможность устанавливать режим строгой типизации для скалярных типов данных, при которой аргументам функции разрешается передавать значения только объявленных типов. Исключение составляет передача значения целочисленного типа функции, которая ожидает получить значение типа float. Во всех остальных случаях интерпретатор будет выдавать ошибки. Для включения режима строгой типизации используется конструкция 'declare(strict_types=1);' (см. пример 5.11).
<?php declare(strict_types=1); //Задаем режим строгой типизации для скрипта function my_sum(int $a,int $b,int $c=null){ //Функция ожидает три целых числа $sum=$a+$b; if($c===null){ //null по-прежнему принят без проблем echo 'null'.'<br>'; } return $sum; //Возвращаем сумму аргументов функции } echo my_sum(5,2); //Выведет 7 //echo my_sum('5',2); //Выведет ошибку, т.к. включен режим строгой типизации /* Закомментируйте строку с объявлением режима строгой типизации и посмотрите как интерпретатор отреагирует на строку 12 */ ?>
Пример 5.11. Использование строгой типизации
Отметим, что режим строгой типизации распространяется на вызовы функций совершенные из файла, в котором этот режим включен, а не на функции, которые в этом файле были объявлены. Это значит, что если файл, в котором режим строгой типизации выключен, вызывает функцию, которая была объявлена в файле с включенным режимом, значения аргументов будут приведены к нужным типам обычным способом по умолчанию, как-будто функция была объявлена в этом же файле.
5.4. Возврат значения функцией.
Довольно часто при вызове функции она не только выполняет указанный фрагмент программного кода, но и возвращает некоторое значение, которое является результатом ее работы. Осуществляется операция возврата при помощи необязательного оператора return, который немедленно прекращает выполнение текущей функции и возвращает свой аргумент как значение данной функции. При этом оператор return может быть расположен в любом месте функции и, когда до него доходит управление, функция возвращает значение и завершает свое выполнение, передавая управление обратно к той строке кода, в которой данная функция была вызвана. Если оператор return не указан или не указано возвращаемое им значение, то функция вернет значение NULL. (см. пример 5.12).
<?php function m_sum($a_1,$a_2){ //Создаем функцию, принимающую два аргумента $sum = $a_1+$a_2; //Вычисляем сумму и присваиваем переменной return $sum; //Возвращаем сумму в качестве значения функции $sum=100; //Эта инструкция выполнена не будет, т.к. //находится после оператора return } echo m_sum(5,10); //Выведет 15 ?>
Пример 5.12. Возврат значений функции
Так как оператор return является языковой конструкцией, а не функцией, то использовать круглые скобки не нужно, иначе это может привести к нежелательным эффектам.
В PHP функции не могут возвращать несколько значений, но аналогичного результата можно добиться, возвращая массив (см. пример 5.13).
<?php function m_sum(...$sum){ //Используем список аргументов переменной длины $i=0; //Задаем стартовое значение счетчика foreach($sum as $value){ //Перебираем все значения массива аргументов $new_array[$i]=$value; //Помещаем аргументы функции в массив $i+=1; //Увеличиваем значение счетчика на единицу } return $new_array; //Возвращаем полученный массив } echo m_sum(5,10,15,20)[1]; //Выведет второй элемент массива, т.е. 10 /* Таким образом, данная функция принимает аргументы, помещает их в массив и затем возвращает этот массив в качестве своего значения */ ?>
Пример 5.13. Возврат значения функции в виде массива значений
В PHP 7.0.0 была добавлена возможность объявлять тип возвращаемого значения, аналогично объявлению типов принимаемых функцией аргументов. Соответственно, особенности, касающиеся как режима слабой типизации, так и режима строгой типизации, справедливы и для возвращаемых функцией значений. Это значит, что в случае несоответствия возвращаемого значения указанному типу в режиме слабой типизации делается попытка привести значение к корректному типу, а при включенном режиме строгой типизации, сразу же выводится ошибка.
Указывается тип возвращаемого значения при объявлении функции после круглых скобок с параметрами и двоеточия перед открывающей скобкой тела функции (см. пример 5.14).
<?php declare(strict_types=1); //Задаем режим строгой типизации для скрипта function my_sum(int $a,int $b): string{ //Задали тип string возвращаемого значения $sum=$a+$b; return $sum; //Возвращаемое значение должно быть типа string } echo my_sum(5,2); //При вызове функции выведет ошибку /* Напомним, что исключение составляет передача значения целочисленного типа функции, которая ожидает получить значение типа float. */ ?>
Пример 5.14. Объявление типа возвращаемого функцией значения
В конце добавим, что оператор return может использоваться и в глобальной области видимости, т.е. вне функций. В таком случае он прекращает выполнение всего текущего файла скрипта.
5.5. Обращение к функциям через переменные.
В PHP поддерживается концепция переменных функций. Это означает, что если к имени переменной, значением которой является строка, присоединены круглые скобки, PHP ищет функцию, имя которой совпадает с этой строкой, и в случае успеха пытается выполнить найденную функцию (см. пример 5.15).
<?php function my_func($a){ echo 'Таймер установлен на '.$a.' секунд.'; } $set_time='my_func'; //Теперь можно вызывать функцию my_func() при помощи //переменной $set_time, которая хранит имя указанной //функции в виде строки $set_time(5); //Выведет строку 'Таймер установлен на 5 секунд.' ?>
Пример 5.15. Обращение к функциям через переменные
Отметим, что переменные функции не будут работать со встроенными языковыми конструкциями или функциями вроде echo или unset(). В таких случаях следует создавать свои функции-обертки, которые позволят работать со встроенными конструкциями наподобие пользовательских функций (см. пример 5.16).
<?php function my_echo($str){ //Функция-обертка для echo echo $str; } $my_func = 'my_echo'; $my_func('test'); //Вызывает функцию my_echo() ?>
Пример 5.16. Обращение к встроенным функциям через переменные при помощи функций-оберток
5.6. Анонимные функции.
Анонимные функции, также известные в программировании как замыкания (от англ. closures), представляют собой функции, не имеющие определенных имен. Замыкания определяются и присваиваются переменным как обычные значения, поэтому в конце операции присвоения анонимной функции нужно не забывать ставить точку с запятой. Кроме того, анонимные функции, в отличие от именованных, создаются только в тот момент, когда до них доходит выполнение, соответственно, и воспользоваться ими можно только после их определения (см. пример 5.17).
<?php //$my_func('test'); //Выведет ошибку, т.к. анонимные функции определяются только //в момент их первого использования, в отличие от именованных //функций, которые доступны из любой точки программы, за //исключением случаев, когда они, например, определены условно $my_func = function($str){ //Определяем анонимную функцию echo $str; }; //Обязательно ставим точку с запятой $my_func('test'); //Выведет строку 'test' ?>
Пример 5.17. Создание анонимных функций
Важной особенностью замыканий является то, что они могут наследовать переменные из родительской области видимости. Делается это при помощи служебного слова use (см. пример 5.18).
<?php $str='test №1'; //Создаем переменную в родительской области видимости $my_func_1 = function() use ($str){ //Наследуем внешнюю переменную echo $str.'<br>'; }; $my_func_1(); //Выведет также строку 'test №1' $str='test №2'; //Изменяем переменную в родительской области видимости $my_func_1(); //Выведет унаследованное значение переменной $str, т.е. //строку 'test №1', т.к. значение унаследованной переменной //задается там, где функция определена, а не там, где вызвана!!! $my_func_2 = function() use (&$str){ //Наследуем внешнюю переменную по ссылке echo $str.'<br>'; }; $my_func_2($str); //Выведет строку 'test №2' $str='test №3'; //Изменяем переменную в родительской области видимости $my_func_2(); //Выведет строку 'test №3', т.к. переменная наследовалась по ссылке $my_func_3 = function($str_1) use ($str){ //Аргументы передаются обычным способом echo $str_1.$str.'<br>'; }; $str_1='Это '; //Создаем переменную в родительской области видимости $my_func_3($str_1);//Выведет строку 'Это test №3' ?>
Пример 5.18. Наследование переменных замыканиями из родительской области видимости
Как видно из примера, значение унаследованной переменной задается там, где функция определяется, а не там, где вызывается. Кроме того, если внешняя переменная была унаследована не по ссылке, то при изменении ее значения в родительской области видимости, унаследованная копия остается неизмененной.
Следует отметить, что наследование переменных из родительской области видимости не то же самое, что использование глобальных переменных, поскольку глобальные переменные существуют в глобальной области видимости, которая не меняется вне зависимости от того, какая функция выполняется в данный момент. В тоже время родительская область видимости не обязательно совпадает с глобальной областью и вполне может оказаться локальной областью функции, в которой было объявлено данное замыкание (см. пример 5.19).
<?php function sale_func($price){ //Внешняя функция $start_price = function() use ($price){ //Наследуем локальную переменную return $price; //При использовании вернет начальное значение $price }; $price*=0.2; //Вычисляем 20% от начальной цены $price=$start_price()-$price; //Получаем цену со скидкой echo 'Цена со скидкой в 20% составит '.$price.' у.е.'; } sale_func(50); //Выведет строку 'Цена со скидкой в 20% составит 40 у.е.' /* В примере замыкание использовалось в локальной области видимости, которая является для нее родительской областью видимости. При использовании же ключевого слова global, ссылка всегда ведет к переменной в глобальной области видимости */ ?>
Пример 5.19. Использование замыканий в локальной области видимости
Контрольные вопросы и задания
- Что в программировании называют функцией?
- Опишите синтаксис создания пользовательских функций в PHP.
- Перечислите особенности функций в PHP.
- Каким образом аргументы функции передаются по ссылке?
- Как задать для аргументов значения по умолчанию?
- Опишите синтаксис для использования списка аргументов переменной длины.
- Перечислите три функции, которые предназначены для работы с аргументами.
- Для чего предназначен оператор return?
- В чем заключается контроль типа аргументов?
- Каким образом можно осуществить возврат функцией нескольких значений?
- Опишите концепцию переменных функций в PHP.
- Что такое замыкание? Как оно создается?
- Каким образом замыкания наследуют переменные из родительской области видимости?
- Как влияет изменение значения переменой в родительской
области видимости на значение
унаследованной замыканием ее копии?
Словарь новых английских слов
function [ˈfʌŋ(k)ʃ(ə)n] – функция. return [rɪˈtəːn] – возвращать. |
argument [ˈɑːɡjʊm(ə)nt] – аргумент. closure [ˈkləʊʒə] – замыкание. |