Функции в PHP

12.06.2017 в 13:37
66055
+5194

Всем привет! В этом уроке мы познакомимся с таким понятием как функции в языке 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” то в выдаче будет ссылка на официальную документацию, там и смотрите.

Поиск функций в 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 получим:

  1. power(2, 3) = 2 * power(2, 2)
  2. power(2, 2) = 2 * power(2, 1)
  3. 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

Всё верно! Ну, хватит рекурсии, а то я уже слышу, как у вас закипают мозги.
Очень надеюсь, что вам понравился урок. Если это так – буду рад, если вы поделитесь им в социальных сетях или расскажете друзьям. Это лучшая поддержка проекта. Спасибо тем, кто это делает. Если у вас возникли какие-то вопросы или замечания – напишите об этом в комментариях. А сейчас – делаем домашнее задание, варианты ваших решений можете писать в комментариях, я их проверю и скажу, всё ли правильно. Всем пока!

loader
12.06.2017 в 13:37
66055
+5194
Домашнее задание
  • Напишите функцию, которая будет принимать на вход 3 аргумента с типом float и возвращать минимальное значение.
  • Напишите функцию, которая принимает на вход два аргумента по ссылкам и умножает каждый из них на 2.
  • Напишите функцию, считающую факториал числа (произведение целых чисел от единицы до переданного). Реализуйте с помощью рекурсии.
  • Напишите функцию, которая будет выводить на экран целые числа от 0 до переданного значения. И да, тут тоже нужно реализовать с помощью рекурсии (чтобы лучше с ней познакомиться, несмотря на то что вариант с циклом проще).
Комментарии
Этот урок набрал набрал достаточно большое количество комментариев и дальнейшее его комментирование отключено. Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку, посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали. Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
Логические задачи с собеседований