Функции в PHP
Всем привет! В этом уроке мы познакомимся с таким понятием как функции в языке PHP. Функции – вещь довольно простая. Она представляет собой кусок кода, который принимает определенные параметры и на выходе возвращает какой-либо результат. Можно написать функцию один раз, а затем использовать её в различных местах. Таким образом вам не нужно будет дублировать код, если что-то нужно сделать дважды, или трижды, или сколько угодно раз. Вообще, функции в PHP можно сравнить с функциями в математике.
В PHP изначально содержится огромное число встроенных функций. Это очень сильная сторона этого языка – почти под любую вашу потребность имеется уже готовая функция. Давайте попробуем несколько функций на практике.
Например, нам нужен косинус числа 3.14. Легко! Для этого в PHP есть функция cos.
<?php
echo cos(3.14);
Результат:
-0.99999873172754
Есть очень много функций для работы со строками. Например, можно в строке заменить одни символы другими, для этого есть функция str_replace. Например, давайте в строке “abracadabra” заменим все буквы “a” на “o”.
<?php
$string = 'abracadabra';
echo str_replace('a', 'o', $string);
Результат:
obrocodobro
Документация по стандартной библиотеке функций (SPL) доступна на официальном сайте - http://php.net/manual/ru/funcref.php. Большинство из них имеет описание на русском языке. Разумеется, все их запомнить невозможно, однако нужно уметь их находить. И это, как правило, не составляет труда. Например, если вбить в google “заменить символы в строке php” то в выдаче будет ссылка на официальную документацию, там и смотрите.
Вообще, гуглить и брать уже готовый код – это хороший подход, который экономит ваше время. В этом нет ничего плохого – скорее всего вы найдёте лучшее решение задачи, чем то, которое бы написали сами. Научитесь на готовых примерах, а со временем запомните наиболее частые подходы для разных случаев. Здесь главное – постоянная практика.
Пользовательские функции: пишем свою первую функцию
Думаю, о том, что такое функции и где найти уже готовые, я объяснил. Но самое крутое то, что функции можно определять самому! В целом определение любой функции выглядит следующим образом:
function имяФункции (аргумент1, аргумент2)
{
какие-то действия;
return результат;
}
У функции обязательно должно быть имя. У всех функций должны быть разные имена и имя вашей функции не может называться так же, как и встроенная в PHP функция. Аргументов может быть сколько угодно, в том числе и 0. Функция может что-то возвращать, для этого перед результатом пишется слово return. После него функция завершает свою работу. А может и не возвращать, а, например, изменять свои аргументы, или выводить информацию на экран. Именуются функции так же, как и переменные - camelCase`ом. Подробнее об этом я писал тут - https://php.zone/post/726. Именовать их нужно так же осмысленно – то что функция делает, то и нужно писать в имени.
Давайте рассмотрим пример создания простейшей функции в PHP. Пусть она принимает на вход два числа и возвращает их сумму.
<?php
function getSum($x, $y)
{
return $x + $y;
}
Окей, функцию написали, теперь можно её вызвать и посмотреть на результат её работы.
<?php
function getSum($x, $y)
{
return $x + $y;
}
$a = 5;
$b = 10;
echo getSum($a, $b) . '<br>';
echo getSum(-3, 4);
Результат:
15
1
Как мы видим, функция успешно отработала. Вот так, написав её лишь один раз мы можем передавать в неё разные аргументы, и получать разные значения. Использовать функцию можно только после кода её определения!
Области видимости
Давайте теперь поговорим об области видимости функции. Тут на самом деле всё просто, и всё сводится к тому, что переменные внутри функции недоступны извне и называются локальными. После завершения работы функции все объявленные внутри неё переменные перестанут существовать. При этом переменные за пределами функции называются глобальными и недоступны внутри функции. Таким образом, можно внутри функции использовать такие же имена переменных, как и снаружи, но при этом это будут совершенно разные переменные.
<?php
function getSum($x, $y)
{
return $x + $y;
}
$x = 3;
$y = 5;
echo getSum($x, $y);
Данный код абсолютно рабочий. Здесь переменные $x и $y внутри функции getSum живут сами по себе, а переменные с такими же именами за пределами функции – отдельно, и они ничего друг о друге не знают. При вызове функции значение передаваемого аргумента будет скопировано в локальную переменную, а после выхода из функции это значение будет удалено. То есть здесь при вызове getSum мы просто копируем значения 3 и 5 в локальные переменные $x и $y. С этого момента внутри функции внешние переменные перестают существовать, и она знает только о своих переменных.
Параметры функции: передача аргументов по ссылке и по значению
До сих пор в рассматриваемых нами случаях аргументы в функцию передавались по значению. Это значит, что при вызове функции, значение, передаваемое в качестве аргумента, просто копировалось в локальную переменную.
Передача параметров по ссылке
Однако можно передать аргумент по ссылке. Делается это при помощи знака & перед именем аргумента.
function func(&$x)
При этом не происходит копирования значения в локальную переменную. Появляется ещё одна связь для этого же значения, но с другим именем. И теперь, изменяя значение этой переменной внутри функции, мы будем изменять исходное, переданное значение. Давайте рассмотрим пример передачи значения по ссылке.
<?php
function plus5(&$x)
{
$x = $x + 5;
}
$a = 3;
plus5($a);
echo $a;
Результат:
8
То есть для значения переменной $a внутри функции появилось ещё одно имя - $x. Сейчас оба этих имени указывают на одно и то же значение. И если мы изменим значение переменной внутри функции, то оно изменится также и снаружи!
А если бы мы передали переменную в функцию по значению, без использования знака &, то получили бы другой результат.
<?php
function plus5($x)
{
$x = $x + 5;
}
$a = 3;
plus5($a);
echo $a;
Будет выведено:
3
Здесь при вызове функции значение переменной $a было просто скопировано в локальную переменную $x. Внутри функции мы изменили $x, прибавив к нему число 5, а затем работа функции была завершена. Локальная переменная $x перестала существовать, а мы никак не повлияли на переменную $a.
Функции, в которых все аргументы передаются по значению, называются чистыми. Стоит по возможности использовать их, так как в абсолютном большинстве случаев передача значений по ссылкам не требуется и приводит к запутанности кода.
Начинаем использовать тайп-хинтинг (type hinting) в PHP
В PHP 7 в функциях появилась возможность указывать типы аргументов, в том числе для скалярных типов (строки, числа). При этом при передаче аргумента в функцию, передаваемое значение будет автоматически приведено к нужному типу. Давайте перепишем нашу функцию, учитывая современные возможности PHP.
<?php
function getSum(int $x, int $y)
{
return $x + $y;
}
Вот так, теперь при передаче любых значений в функцию, они автоматически будут приведены к числам. Давайте это проверим – вставим вызов функции var_dump внутри нашей функции, и посмотрим, что получится.
<?php
function getSum(int $x, int $y)
{
var_dump($x);
var_dump($y);
return $x + $y;
}
echo getSum(5, 10);
Результат:
int 5
int 10
15
Вполне ожидаемо, скажете вы. Мы ведь передали в функцию целые числа, и даже если бы у аргументов не было приведения типов, то там точно так же были бы целые числа. И будете правы. Но давайте попробуем в качестве одного из аргументов передать число с плавающей запятой.
<?php
function getSum(int $x, int $y)
{
var_dump($x);
var_dump($y);
return $x + $y;
}
echo getSum(12.5, 10);
После выполнения мы увидим следующее:
int 12
int 10
22
То есть в тот момент, когда аргумент пришёл в функцию, он был уже нужного типа. Давайте попробуем убрать указание типа из аргумента:
<?php
function getSum($x, $y)
{
var_dump($x);
var_dump($y);
return $x + $y;
}
echo getSum(12.5, 10);
Теперь дробное число не будет приведено к целому, и мы получим другой результат:
float 12.5
int 10
22.5
В этом курсе мы всегда будем использовать указание типов. Это помогает писать более строгий код и позволяет допускать меньше ошибок. Посудите сами – если мы работаем с конкретным типом, мы более уверены в том, как будет работать тот или иной код.
Строгая типизация
Как я уже сказал, по умолчанию значение аргумента будет автоматически приведено к указанному типу. Однако в PHP есть возможность указать явно, что аргумент, передаваемый в функцию, будет иметь конкретный тип. Это делается с помощью директивы:
declare(strict_types=1);
Она указывается в начале PHP-файла. Наш код примет следующий вид:
<?php
declare(strict_types=1);
function getSum(int $x, int $y)
{
var_dump($x);
var_dump($y);
return $x + $y;
}
echo getSum(12.5, 10);
При этом, если его сейчас запустить, то произойдёт ошибка с типом TypeError.
Как тут написано, первый аргумент, переданный в getSum() обязан быть типа integer, а передан с типом float. Это и есть строгая типизация в PHP. Использовать её в рамках данного курса мы не будем, да и в профессиональной разработке на PHP этот функционал пока не используют. Но не рассказать о ней я не мог, вполне возможно, что это могут спросить при собеседовании на работу. А сейчас убираем строку declare(strict_types=1); из нашего кода и идём дальше.
Функции без аргументов
Как я говорил, функция может вообще не иметь аргументов. Таковой, например, является стандартная функция rand() – она просто возвращает случайное число.
Давайте её попробуем в действии:
<?php
echo 'Случайное число: ' . rand();
Результат:
Случайное число: 9582
Давайте запустим ещё раз этот же самый код:
Случайное число: 7324
Работает. А давайте теперь напишем свою функцию, которая тоже не принимает аргументов, и возвращает синус случайного числа:
<?php
function getSinOfRandom()
{
return sin(rand());
}
echo 'Синус случайного числа: ' . getSinOfRandom();
Результат:
Синус случайного числа: 0.6586666790617
Функция внутри функции
Давайте теперь рассмотрим, что ещё позволяют нам делать функции. Функции в PHP можно вызывать внутри других функций. Давайте приведём абсолютно простейший пример. Например, нам нужно найти сумму косинусов двух чисел. Напишем для этого функцию:
<?php
function getSumOfCos(float $x, float $y)
{
$cosX = cos($x);
$cosY = cos($y);
return $cosX + $cosY;
}
echo getSumOfCos(1.44, 2);
Здесь мы использовали функцию cos() из стандартной библиотеки внутри нашей собственной функции. А можно ли вызывать свои же функции из других своих функций? Да легко!
<?php
function getSum(float $x, float $y)
{
return $x + $y;
}
function getSumOfCos(float $x, float $y)
{
$cosX = cos($x);
$cosY = cos($y);
return getSum($cosX, $cosY);
}
echo getSumOfCos(1.44, 2);
Здесь мы определили 2 разные функции, и во второй начали использовать первую. Разумеется, эти примеры довольно искусственны. И вы можете подумать, что использовать здесь функции излишне. Ведь можно же просто заменить этот код следующим выражением:
<?php
echo cos(1.44) + cos(2);
И вы будете совершенно правы. Но я привожу такие лёгкие примеры специально, чтобы вам было проще понять, как вообще писать функции. Давайте рассмотрим более реальный пример. Напишем функцию, которая будет возвращать большее из двух переданных чисел.
<?php
function getMax(int $x, int $y)
{
if ($x > $y) {
return $x;
} else {
return $y;
}
}
$a = 5;
$b = 8;
echo 'Наибольшее число: ' . getMax($a, $b);
Результат:
Наибольшее число:8
Код данной функции можно упростить, так как если $x не больше $y, то мы в любом случае вернём $y. Поэтому можно убрать блок else:
<?php
function getMax(int $x, int $y)
{
if ($x > $y) {
return $x;
}
return $y;
}
$a = 5;
$b = 8;
echo 'Наибольшее число:' . getMax($a, $b);
Результат будет таким же. Так происходит, как я уже говорил, что как только в функции выполнится return, значение будет тут же возвращено, а функция завершит свою работу.
Давайте передадим первым аргументом большее число:
<?php
function getMax(int $x, int $y)
{
if ($x > $y) {
return $x;
}
return $y;
}
$a = 10;
$b = 8;
echo 'Наибольшее число: ' . getMax($a, $b);
Результат:
Наибольшее число: 10
Выполнилось первое условие и вызвался return $x. Всё, до return $y дело не дошло.
Упрощение заключается в том, что чем меньше уровней вложенности в коде, тем проще его читать и понимать. Видите, как плотно пересекается весь изученный материал? В данном случае с темой условий в PHP.
Рекурсивные функции
Ох, чувствую, сейчас повеселимся. Осторожно! Если при чтении этого раздела у вас начнут закипать мозги - переходите к следующему уроку, рекурсию можно освоить и в конце курса. Не парьтесь, в общем, если не будет получаться, вы такие не одни =)
Вот мы узнали, что можно вызывать одну функцию внутри другой. Однако бывают ситуации, когда нужно вызвать одну и ту же функцию внутри себя самой.
Например, нам нужно возвести число в степень. Предположим, у нас для этого есть определённая функция. Назовём её power. И пусть она принимает 2 аргумента:
- число, которое нужно возвести;
- степень, в которую нужно возвести.
То есть, для того, чтобы возвести число 2 в третью степень, нам нужно вызвать эту функцию следующим образом:
power(2, 3)
Согласитесь, что power(2, 3) равносильно 2 * power(2, 3 - 1). То есть мы можем умножить число, возводимое в степень, на вызов этой функции, и уменьшить при этом степень на единицу.
Таким образом, получается, что мы можем вызывать эту же функцию с некоторыми дополнительными операциями.
Получаем, что
power($x, $n) = $x * power($x, $n – 1)
Тогда для возведения 2 в степень 3 получим:
- power(2, 3) = 2 * power(2, 2)
- power(2, 2) = 2 * power(2, 1)
- power(2, 1) = 2 * power(2, 0)
Число в нулевой степени равно единице. Здесь нам нужно остановиться.
Давайте реализуем это с помощью кода:
<?php
function power(int $x, int $n)
{
// Если сейчас степень равна нулю, то возвращаем единицу
if ($n === 0) {
return 1;
}
// В остальных случаях - умножаем число на возведённое в степень n - 1 и возвращаем его
return $x * power($x, $n - 1);
}
echo power(2, 3);
Да, функция будет вызывать сама себя. Таким образом, можно прийти к тому, что рекурсия полезна, когда вычисление функции можно свести к её более простому вызову, а его – ещё к более простому, и так далее, пока мы не достигнем в аргументах какого-то конечного значения (в нашем примере - число 0). Чтобы понять, как всё работает пошагово, можно добавить вызов var_dump в начале функции и посмотреть, какие аргументы ей передаются на каждом из шагов.
<?php
function power(int $x, int $n)
{
var_dump($x, $n);
// Если сейчас степень равна нулю, то возвращаем единицу
if ($n === 0) {
return 1;
}
// В остальных случаях - умножаем число на возведённое в степень n - 1 и возвращаем его
return $x * power($x, $n - 1);
}
echo 'Результат: ' . power(2, 3);
Вывод получится следующим:
int 2 int 3
int 2 int 2
int 2 int 1
Результат: 8
Таким образом, мы видим, что функция была вызвана трижды, и мы видим, какими были значения аргументов на каждом вызове. Надеюсь, тут всё понятно. Если же нет (а при изучении рекурсии это происходит довольно часто) – пишите ваши вопросы в комментариях, помогу. А ещё можете пошагово разбирать ход программы на листочке – тоже часто помогает.
Давайте для закрепления разберём ещё один пример с рекурсией. Например, мы будем передавать в функцию какое-то число $n, а она будет считать сумму всех чисел от единицы до этого числа. Назовём эту функцию getSumOfNumbersFromZero($n).
Предположим, нам нужно посчитать сумму всех чисел от 1 до 5. Тогда вызов функции будет следующим:
getSumOfNumbersFromZero(5);
Как можно догадаться, этот вызов можно представить как
5 + getSumOfNumbersFromZero(4)
Или в общем случае
getSumOfNumbersFromZero($n) = $n + getSumOfNumbersFromZero($n - 1)
И так до тех пор, пока $n не станет равен 1.
Давайте реализуем это с помощью рекурсивной функции:
<?php
function getSumOfNumbersFromZero(int $n)
{
// Если сейчас 1, то просто возвращаем 1
if ($n == 1) {
return 1;
}
// В остальных случаях - прибавляем текущее число к сумме всех предыдущих
return $n + getSumOfNumbersFromZero($n - 1);
}
echo 'Результат: ' . getSumOfNumbersFromZero(5);
Запускаем:
Результат: 15
Всё верно! Ну, хватит рекурсии, а то я уже слышу, как у вас закипают мозги.
Очень надеюсь, что вам понравился урок. Если это так – буду рад, если вы поделитесь им в социальных сетях или расскажете друзьям. Это лучшая поддержка проекта. Спасибо тем, кто это делает. Если у вас возникли какие-то вопросы или замечания – напишите об этом в комментариях. А сейчас – делаем домашнее задание, варианты ваших решений можете писать в комментариях, я их проверю и скажу, всё ли правильно. Всем пока!
Комментарии