1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-25 16:22:18 +01:00
Files
archived-doc-ru/language/functions.xml
2024-04-23 03:30:08 +03:00

1923 lines
63 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: f94d903985119d3ac00f4528551df947f57b667f Maintainer: sergey Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="language.functions" xmlns="http://docbook.org/ns/docbook">
<title>Функции</title>
<sect1 xml:id="functions.user-defined">
<title>Пользовательские функции</title>
<para>
Приведём пример синтаксиса для описания функций:
</para>
<para>
<example>
<title>Псевдокод описания функции</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
echo "Пример функции.\n";
return $retval;
}
?>
]]>
</programlisting>
</example>
</para>
<simpara>
Внутри функции разрешается любой корректный PHP-код,
включая другие функции и даже объявления <link linkend="language.oop5.basic.class">классов</link>.
</simpara>
<para>
Имена функций следуют тем же правилам, что и другие метки в PHP.
Корректное имя функции начинается с буквы или знака подчёркивания,
за которым идёт любое количество букв, цифр или знаков
подчёркивания. В виде регулярного выражения имя выражается так:
<code>^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$</code>.
</para>
&tip.userlandnaming;
<simpara>
Требования объявлять функции раньше появления ссылки на функции — нет,
<emphasis>за исключением</emphasis> условного определения функции,
как показывают два следующих примера.
</simpara>
<para>
Определение функции описывают <emphasis>до</emphasis> вызова функции,
когда функцию определяют по условию, как в двух следующих примерах.
</para>
<para>
<example>
<title>Условные функции</title>
<programlisting role="php">
<![CDATA[
<?php
$makefoo = true;
/* Нельзя вызвать функцию foo() в этом месте,
поскольку функцию ещё не определили, но можно
обратиться к функции bar() */
bar();
if ($makefoo) {
function foo()
{
echo "Я не существую, пока выполнение программы не дойдёт до меня.\n";
}
}
/* Теперь безопасно вызываем функцию foo(),
поскольку выражение переменной $makefoo язык интерпретировал как true */
if ($makefoo) foo();
function bar()
{
echo "Я существую сразу после запуска программы.\n";
}
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Вложенные функции</title>
<programlisting role="php">
<![CDATA[
<?php
function foo()
{
function bar()
{
echo "Я не существую пока не вызовут функцию foo().\n";
}
}
/* Пока нельзя обратиться к функции bar(),
поскольку функцию ещё не определили */
foo();
/* Теперь можно вызвать функцию bar(),
обработка функции foo() сделала её доступной */
bar();
?>
]]>
</programlisting>
</example>
</para>
<para>
У функций и классов PHP глобальная область видимости —
их разрешается вызывать за пределами функции, даже если их определили
внутри, и наоборот.
</para>
<simpara>
PHP не поддерживает перегрузку функций, также невозможно
переопределить или удалить функции, которые объявили раньше.
</simpara>
<note>
<simpara>
Имена функций не зависят от регистра символов в кодировке ASCII
от <literal>A</literal> до <literal>Z</literal>, хотя хорошим тоном
будет вызов функций в том виде, в каком функцию указали в объявлении.
</simpara>
</note>
<simpara>
Функции PHP поддерживают как <link linkend="functions.variable-arg-list">
списки аргументов переменной длины</link>, так и
<link linkend="functions.arguments.default">значения аргументов по умолчанию</link>.
Подробнее об этом рассказывают описания функций
<function>func_num_args</function>,
<function>func_get_arg</function>
и <function>func_get_args</function>.
</simpara>
<para>
В PHP функции разрешается вызывать рекурсивно.
<example>
<title>Рекурсивные функции</title>
<programlisting role="php">
<![CDATA[
<?php
function recursion($a)
{
if ($a < 20) {
echo "$a\n";
recursion($a + 1);
}
}
?>
]]>
</programlisting>
</example>
<note>
<simpara>
Рекурсивный вызов функций и методов с глубиной более 100-200 уровней
рекурсии может вызвать переполнение стека и привести к аварийному
завершению скрипта. В частности, бесконечную рекурсию PHP считает
программной ошибкой.
</simpara>
</note>
</para>
</sect1>
<sect1 xml:id="functions.arguments">
<title>Аргументы функции</title>
<simpara>
Функция принимает информацию в виде списка аргументов —
выражений через запятую. Аргументы
вычисляются слева направо перед действительным вызовом функции.
Такое вычисление называют <emphasis>энергичным</emphasis>.
</simpara>
<para>
PHP поддерживает передачу аргументов по значению (по умолчанию), <link
linkend="functions.arguments.by-reference">передачу аргументов по ссылке</link>,
и <link linkend="functions.arguments.default">значения по умолчанию</link>.
<link linkend="functions.variable-arg-list">Списки аргументов
переменной длины</link> и <link linkend="functions.named-arguments">именованные
аргументы</link> также поддерживаются.
</para>
<para>
<example>
<title>Передача массивов в функции</title>
<programlisting role="php">
<![CDATA[
<?php
function takes_array($input)
{
echo "$input[0] + $input[1] = ", $input[0]+$input[1];
}
?>
]]>
</programlisting>
</example>
</para>
<para>
Начиная с PHP 8.0.0 списку аргументов функции разрешается включать конечную
запятую, которую парсер проигнорирует. Это полезно, когда список аргументов
длинный или содержит длинные имена переменных, что в целях удобства подталкивает
к вертикальному перечислению аргументов.
</para>
<example>
<title>Список аргументов функции с конечной запятой</title>
<programlisting role="php">
<![CDATA[
<?php
function takes_many_args(
$first_arg,
$second_arg,
$a_very_long_argument_name,
$arg_with_default = 5,
$again = 'a default string', // Конечная запятая не допускалась до PHP 8.0.0
) {
// ...
}
?>
]]>
</programlisting>
</example>
<sect2 xml:id="functions.arguments.by-reference">
<title>Передача аргументов по ссылке</title>
<simpara>
По умолчанию аргументы функции передаются по значению (поэтому
если значение аргумента внутри функции изменится, значение
не изменится за пределами функции). Аргументы передают по ссылке,
чтобы разрешить функции изменять значения аргументов.
</simpara>
<para>
В описании функции перед именем параметра указывают амперсанд &amp;,
когда требуется передача аргумента по ссылке:
</para>
<para>
<example>
<title>Передача аргументов по ссылке</title>
<programlisting role="php">
<![CDATA[
<?php
function add_some_extra(&$string)
{
$string .= 'и кое-что ещё.';
}
$str = 'Это строка, ';
add_some_extra($str);
echo $str; // Выведет «Это строка, и кое-что ещё.»
?>
]]>
</programlisting>
</example>
</para>
<para>
Передача в виде аргумента значения в параметр, который ждёт ссылку, — ошибка.
</para>
</sect2>
<sect2 xml:id="functions.arguments.default">
<title>Значения по умолчанию для параметров</title>
<para>
Функция умеет определять для параметров значения по умолчанию,
в этом помогает синтаксис, который похож на синтаксис присваивания значения переменной.
Функция присвоит параметру значение по умолчанию, только если
в параметр не передали аргумент;
обратите внимание, что функция <emphasis>не</emphasis> присваивает
параметру значение по умолчанию при передаче в параметр аргумента со значением &null;.
</para>
<para>
<example>
<title>Значения по умолчанию для параметров в функциях</title>
<programlisting role="php">
<![CDATA[
<?php
function makecoffee($type = "капучино")
{
return "Готовим чашку $type.\n";
}
echo makecoffee();
echo makecoffee(null);
echo makecoffee("эспрессо");
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Готовим чашку капучино.
Готовим чашку .
Готовим чашку эспрессо.
]]>
</screen>
</example>
</para>
<para>
Значениями по умолчанию для параметров разрешается указывать скалярные значения,
массивы (<type>array</type>), специальный тип &null; и начиная с PHP 8.1.0 объекты,
которые создают синтаксисом <link linkend="language.oop5.basic.new">new ClassName()</link>.
</para>
<para>
<example>
<title>
Нескалярные типы как значения по умолчанию
</title>
<programlisting role="php">
<![CDATA[
<?php
function makecoffee($types = array("капучино"), $coffeeMaker = NULL)
{
$device = is_null($coffeeMaker)
? "вручную"
: $coffeeMaker
;
return "Готовлю чашку " . join(", ", $types) . " $device. \n";
}
echo makecoffee();
echo makecoffee(array("капучино", "лавацца"), "в чайнике");
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Готовлю чашку капучино вручную.
Готовлю чашку капучино, лавацца в чайнике.
]]>
</screen>
</example>
</para>
<para>
<example>
<title>
Объекты как значения по умолчанию, с PHP 8.1.0
</title>
<programlisting role="php">
<![CDATA[
<?php
class DefaultCoffeeMaker
{
public function brew()
{
return "Приготовление кофе.\n";
}
}
class FancyCoffeeMaker
{
public function brew()
{
return "Приготовление прекрасного кофе только для вас.\n";
}
}
function makecoffee($coffeeMaker = new DefaultCoffeeMaker)
{
return $coffeeMaker->brew();
}
echo makecoffee();
echo makecoffee(new FancyCoffeeMaker);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Приготовление кофе.
Приготовление прекрасного кофе только для вас.
]]>
</screen>
</example>
</para>
<simpara>
Значение по умолчанию должно быть константным выражением, а не,
например, переменной, вызовом функции или метода класса.
</simpara>
<para>
Обратите внимание, что необязательные аргументы требуется указывать
после обязательных аргументов, иначе необязательные аргументы
не получится опустить при вызове.
Рассмотрим следующий пример:
</para>
<para>
<example>
<title>
Неправильное определение значений по умолчанию
для параметров функции
</title>
<programlisting role="php">
<![CDATA[
<?php
function makeyogurt($container = "миску", $flavour)
{
return "Делаем $container с $flavour йогуртом.\n";
}
echo makeyogurt("малиновым"); // Значение «малиновым» получит параметр $container, а не $flavour
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Fatal error: Uncaught ArgumentCountError: Too few arguments
to function makeyogurt(), 1 passed in example.php on line 42
]]>
</screen>
</example>
</para>
<para>
Теперь сравним приведённый пример со следующим примером:
</para>
<para>
<example>
<title>
Правильное определение значений по умолчанию
для параметров функции
</title>
<programlisting role="php">
<![CDATA[
<?php
function makeyogurt($flavour, $container = "миску")
{
return "Делаем $container с $flavour йогуртом.\n";
}
echo makeyogurt("малиновым"); // Значение «малиновым» получит параметр $flavour
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Делаем миску с малиновым йогуртом.
]]>
</screen>
</example>
</para>
<para>
Начиная с PHP 8.0.0 для пропуска нескольких необязательных параметров
передают <link linkend="functions.named-arguments">именованные аргументы</link>.
</para>
<para>
<example>
<title>
Правильное определение значений по умолчанию
для параметров функции
</title>
<programlisting role="php">
<![CDATA[
<?php
function makeyogurt($container = "миску", $flavour = "малиновым", $style = "греческим")
{
return "Делаем $container с $flavour $style йогуртом.\n";
}
echo makeyogurt(style: "натуральным");
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Делаем миску с малиновым натуральным йогуртом.
]]>
</screen>
</example>
</para>
<para>
С PHP 8.0.0 объявление обязательных аргументов после необязательных
<emphasis>устарело</emphasis>.
Обычно из таких ситуаций выходят за счёт отказа от значения по умолчанию,
поскольку функция никогда не присвоит параметру, в который передали аргумент,
значение по умолчанию.
Исключение из этого правила — аргументы вида <code>Type $param = null</code>,
где &null; по умолчанию делает тип неявно обнуляемым.
Такое определение остаётся допустимым, хотя рекомендуется указывать
явный <link linkend="language.types.declarations.nullable">обнуляемый тип</link>.
<example>
<title>
Объявление необязательных аргументов после обязательных аргументов
</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($a = [], $b) {} // Функция не присвоит значение по умолчанию; устарело с PHP 8.0.0
function foo($a, $b) {} // Функционально эквивалентны, без уведомления об устаревании
function bar(A $a = null, $b) {} // Всё ещё разрешается; переменная $a обязательна, но допускает значение null
function bar(?A $a, $b) {} // Рекомендуется
?>
]]>
</programlisting>
</example>
</para>
<note>
<simpara>
Начиная с PHP 7.1.0 пропуск параметра, для которого не задали
значение по умолчанию, выбрасывает исключение
<classname>ArgumentCountError</classname>;
в предыдущих версиях это выдавало предупреждение.
</simpara>
</note>
<note>
<simpara>
Для параметров, которые ждут, что аргумент передадут по ссылке,
разрешается устанавливать значения по умолчанию.
</simpara>
</note>
</sect2>
<sect2 xml:id="functions.variable-arg-list">
<title>Списки аргументов переменной длины</title>
<simpara>
PHP поддерживает в пользовательских функциях списки аргументов
переменной длины, параметры для которых определяют оператором из трёх точек
<literal>...</literal>, который называется spread-оператором.
</simpara>
<para>
Списку параметров разрешается содержать оператор
<literal>...</literal>, чтобы показать, что функция принимает переменное
количество аргументов. Переменная получит аргументы как массив:
<example>
<title>
Оператор <literal>...</literal> для доступа к аргументам
</title>
<programlisting role="php">
<![CDATA[
<?php
function sum(...$numbers) {
$acc = 0;
foreach ($numbers as $n) {
$acc += $n;
}
return $acc;
}
echo sum(1, 2, 3, 4);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
10
]]>
</screen>
</example>
</para>
<para>
Spread-оператор <literal>...</literal> указывают также при вызове функции,
чтобы распаковать в список аргументов массив (<type>array</type>),
или распаковать переменную или литерал, которые принадлежат типу
<classname>Traversable</classname>:
<example>
<title>
Передача аргументов через spread-оператор <literal>...</literal>
</title>
<programlisting role="php">
<![CDATA[
<?php
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n";
$a = [1, 2];
echo add(...$a);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
3
3
]]>
</screen>
</example>
</para>
<para>
Разрешается указывать стандартные позиционные параметры, а за ними
параметр с оператором <literal>...</literal>, тогда оператор <literal>...</literal>
сгенерирует и добавит в массив только конечные аргументы, позиции которых
не совпали с позиционными аргументами.
</para>
<para>
Разрешается также добавлять перед оператором <literal>...</literal>
<link linkend="language.types.declarations">объявление типа</link>.
Тогда аргументы, которые захватил оператор <literal>...</literal>,
должны соответствовать типу параметра.
<example>
<title>Аргументы с подсказкой типа</title>
<programlisting role="php">
<![CDATA[
<?php
function total_intervals($unit, DateInterval ...$intervals) {
$time = 0;
foreach ($intervals as $interval) {
$time += $interval->$unit;
}
return $time;
}
$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';
// Это не сработает, поскольку null — не объект класса DateInterval
echo total_intervals('d', null);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
3 days
Catchable fatal error: Argument 2 passed to total_intervals() must be an instance of DateInterval, null given, called in - on line 14 and defined in - on line 2
]]>
</screen>
</example>
</para>
<para>
В финале добавим, что разрешается также передавать аргументы переменной длины
<link linkend="functions.arguments.by-reference">по ссылке</link>,
для этого перед оператором <literal>...</literal> указывают амперсанд
(<literal>&amp;</literal>).
</para>
</sect2>
<sect2 xml:id="functions.named-arguments">
<title>Именованные аргументы</title>
<para>
В PHP 8.0.0 как расширение позиционных параметров появились именованные аргументы.
Именованные аргументы передают в функцию на основе имени, а не позиции параметра.
Назначение аргумента документирует само себя, аргументы перестают зависеть от порядка,
в котором их передают, и разрешается произвольно пропускать значения по умолчанию.
</para>
<para>
Именованные аргументы передают по имени параметра, за которым идёт
двоеточие и значение аргумента.
Разрешается указывать зарезервированные ключевые слова как имена параметров.
Имя параметра должно быть идентификатором, нельзя указывать динамические имена
параметров.
</para>
<example>
<title>Синтаксис именованного аргумента</title>
<programlisting role="php">
<![CDATA[
<?php
myFunction(paramName: $value);
array_foobar(array: $value);
// НЕ поддерживается
function_name($variableStoringParamName: $value);
?>
]]>
</programlisting>
</example>
<example>
<title>
Позиционные аргументы по сравнению с именованными аргументами
</title>
<programlisting role="php">
<![CDATA[
<?php
// Передача позиционных аргументов:
array_fill(0, 100, 50);
// Передача именованных аргументов:
array_fill(start_index: 0, count: 100, value: 50);
?>
]]>
</programlisting>
</example>
<para>
Порядок передачи именованных аргументов неважен.
</para>
<example>
<title>
Тот же пример, но с другим порядком аргументов
</title>
<programlisting role="php">
<![CDATA[
<?php
array_fill(value: 50, count: 100, start_index: 0);
?>
]]>
</programlisting>
</example>
<para>
Разрешается комбинировать именованные аргументы с позиционными.
Тогда именованные аргументы должны идти после позиционных.
Разрешается также передавать только часть необязательных аргументов
функции, независимо от порядка аргументов.
</para>
<example>
<title>
Объединение именованных аргументов с позиционными аргументами
</title>
<programlisting role="php">
<![CDATA[
<?php
htmlspecialchars($string, double_encode: false);
// То же самое
htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8', false);
?>
]]>
</programlisting>
</example>
<para>
Передача одного и того же аргумента больше одного раза
выбросит исключение Error.
</para>
<example>
<title>
Ошибка, которая возникает при передаче одного и того же параметра
больше одного раза
</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($param) {}
foo(param: 1, param: 2);
// Error: Named parameter $param overwrites previous argument
foo(1, param: 2);
// Error: Named parameter $param overwrites previous argument
?>
]]>
</programlisting>
</example>
<para>
Начиная с PHP 8.1.0 разрешается передавать именованные аргументы
после распаковки аргументов. Именованный аргумент <emphasis>не должен</emphasis>
переопределять распакованный аргумент.
</para>
<example>
<title>
Пример передачи именованных аргументов после распаковки
</title>
<programlisting role="php">
<![CDATA[
<?php
function foo($a, $b, $c = 3, $d = 4) {
return $a + $b + $c + $d;
}
var_dump(foo(...[1, 2], d: 40)); // 46
var_dump(foo(...['b' => 2, 'a' => 1], d: 40)); // 46
var_dump(foo(...[1, 2], b: 20)); // Fatal error: Named parameter $b overwrites previous argument
?>
]]>
</programlisting>
</example>
</sect2>
</sect1>
<sect1 xml:id="functions.returning-values">
<title>Возврат значений</title>
<para>
Значения возвращаются необязательной инструкцией возврата.
Разрешается возвращать любой тип, включая массивы и объекты.
Возврат немедленно завершает выполнение функции и передаёт
управление обратно, к той строке кода, в которой вызвали функцию.
Подробную информацию о возврате значений даёт описание инструкции
<function>return</function>.
</para>
<note>
<para>
Если инструкцию <function>return</function> не указали, функция вернёт
значение &null;.
</para>
</note>
<sect2>
<title>Инструкция return</title>
<para>
<example>
<title>
Пример функции с инструкцией <function>return</function>
</title>
<programlisting role="php">
<![CDATA[
<?php
function square($num)
{
return $num * $num;
}
echo square(4); // Выводит 16
?>
]]>
</programlisting>
</example>
</para>
<para>
Функция не умеет возвращать больше одного значения, но аналогичного
результата добиваются возвратом массива.
</para>
<para>
<example>
<title>Возврат нескольких значений в виде массива</title>
<programlisting role="php">
<![CDATA[
<?php
function small_numbers()
{
return [0, 1, 2];
}
// Деструктуризация массива будет собирать каждый элемент массива индивидуально
[$zero, $one, $two] = small_numbers();
// До версии 7.1.0 единственной эквивалентной альтернативой была языковая конструкция list()
list($zero, $one, $two) = small_numbers();
?>
]]>
</programlisting>
</example>
</para>
<para>
Оператор &amp; указывают и в описании функции,
и в момент присваивания возвращаемого значения переменной,
чтобы функция возвращала результат по ссылке:
</para>
<para>
<example>
<title>Возврат результата по ссылке</title>
<programlisting role="php">
<![CDATA[
<?php
function &returns_reference()
{
return $someref;
}
$newref =& returns_reference();
?>
]]>
</programlisting>
</example>
</para>
<simpara>
Дополнительную информацию о ссылках даёт
раздел документации «<link linkend="language.references">Объяснение ссылок</link>».
</simpara>
</sect2>
</sect1>
<sect1 xml:id="functions.variable-functions">
<title>Функции переменных</title>
<para>
PHP поддерживает концепцию функций переменных. Это означает, что
если к имени переменной присоединили круглые скобки, PHP ищет
функцию с тем же именем, что и результат вычисления переменной,
и пробует выполнить функцию. Этим синтаксисом
описывают callback-функции, таблицы функций и т. д.
</para>
<para>
Функции переменных не работают с языковыми конструкциями
<function>echo</function>, <function>print</function>,
<function>unset</function>, <function>isset</function>,
<function>empty</function>, <function>include</function>,
<function>require</function> и т. п.
Напишите функцию-обёртку, чтобы эти конструкции работали как функции переменных.
</para>
<para>
<example>
<title>Пример функции переменной</title>
<programlisting role="php">
<![CDATA[
<?php
function foo() {
echo "В foo()<br />\n";
}
function bar($arg = '')
{
echo "В bar(); аргумент был '$arg'.<br />\n";
}
// Функция-обёртка для echo
function echoit($string)
{
echo $string;
}
$func = 'foo';
$func(); // Вызывает функцию foo()
$func = 'bar';
$func('test'); // Вызывает функцию bar()
$func = 'echoit';
$func('test'); // Вызывает функцию echoit()
?>
]]>
</programlisting>
</example>
</para>
<para>
Синтаксис функций переменных работает также для вызова методов объектов.
<example>
<title>Пример метода переменной</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
function Variable()
{
$name = 'Bar';
$this->$name(); // Вызывает метод Bar()
}
function Bar()
{
echo "Это Bar";
}
}
$foo = new Foo();
$funcname = "Variable";
$foo->$funcname(); // Вызывает $foo->Variable()
?>
]]>
</programlisting>
</example>
</para>
<para>
При вызове статических методов вызов функции «сильнее»,
чем оператор доступа к статическому свойству:
<example>
<title>Пример вызова метода переменной со статическим свойством</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
static $variable = 'статическое свойство';
static function Variable()
{
echo 'Вызов метода Variable';
}
}
echo Foo::$variable; // Это выведет «статическое свойство». В области видимости класса нужна переменная $variable
$variable = "Variable";
Foo::$variable(); // Вызывает $foo->Variable() после прочтения переменной $variable в текущей области видимости
?>
]]>
</programlisting>
</example>
</para>
<para>
<example>
<title>Сложные callable-функции</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
static function bar()
{
echo "bar\n";
}
function baz()
{
echo "baz\n";
}
}
$func = array("Foo", "bar");
$func(); // Выведет "bar"
$func = array(new Foo(), "baz");
$func(); // Выведет "baz"
$func = "Foo::bar";
$func(); // Выведет "bar"
?>
]]>
</programlisting>
</example>
</para>
<sect2 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>is_callable</function></member>
<member><function>call_user_func</function></member>
<member><function>function_exists</function></member>
<member><link linkend="language.variables.variable">Переменные переменных</link></member>
</simplelist>
</para>
</sect2>
</sect1>
<sect1 xml:id="functions.internal">
<title>Внутренние, или встроенные, функции</title>
<para>
PHP поставляется с набором языковых конструкций и встроенных функций.
Отдельные функции требуют, чтобы PHP собрали с конкретными модулями,
иначе PHP генерирует фатальные ошибки о неизвестной функции.
Например, чтобы использовать <link linkend="ref.image">функции для работы с изображениями</link>
наподобие <function>imagecreatetruecolor</function>, требуется собрать PHP
с поддержкой модуля <productname>GD</productname>.
Требуется собрать PHP с поддержкой модуля <link linkend="book.mysqli">MySQLi</link>,
чтобы использовать функцию <function>mysqli_connect</function>.
Ядро каждой версии PHP включает
<link linkend="ref.strings">функции обработки строк</link>
и <link linkend="ref.var">функции для работы с переменными</link>.
Вызов функции <function>phpinfo</function> или <function>get_loaded_extensions</function>
покажет, какие модули загрузил PHP.
Часть модулей PHP включает по умолчанию. Документация описывает каждый модуль отдельно.
О настройке PHP подробнее рассказывают разделы «<link linkend="configuration">Конфигурация</link>»,
«<link linkend="install">Установка</link>»
и разделы, которые описывают отдельные модули.
</para>
<para>
Раздел
«<link linkend="about.prototypes">Как читать определения функции</link>»
объясняет, как читать и интерпретировать прототипы функций.
Программист должен понимать, что возвращает функция, или как функция модифицирует аргументы,
которые передали в функцию. Функция <function>str_replace</function>, например,
вернёт изменённую строку, тогда как функция <function>usort</function>
работает непосредственно с переменной, которую передали в функцию.
О каждой функции рассказывает отдельная страница документации: описывает параметры,
изменения поведения, значения, которые возвращает функция в случае успешного выполнения
или когда возникает ошибка, доступность функции в одной или другой версии PHP.
Знание важных и часто тонких различий — ключ к правильному PHP-коду.
</para>
<note>
<simpara>
Неясно, какое значение вернёт функция, если аргументы, которые передали в функцию,
не соответствуют ожиданиям функции; например, передали
массив (<type>array</type>) вместо строки (<type>string</type>).
Скорее всего, функция вернёт &null;, но это только соглашение,
на него нельзя полагаться.
Начиная с PHP 8.0.0 в таком случае требуется выбрасывать исключение <classname>TypeError</classname>.
</simpara>
</note>
<note>
<para>
В нестрогом режиме типизации скалярные типы встроенных функций по умолчанию обнуляемы, —
принимают значение &null;.
Начиная с PHP 8.1.0 передача значения &null; в параметр встроенной функции,
который не объявили обнуляемым, не рекомендуется, и в принудительном режиме
выдаёт уведомление об устаревании, чтобы соответствовать поведению пользовательских функций,
в которых требуется явно помечать скалярные типы как обнуляемые.
</para>
<para>
Функция <function>strlen</function>, например, ожидает, что параметр
<literal>$string</literal> будет необнуляемой строкой (&string;).
По историческим причинам в принудительном режиме PHP разрешает передавать
значение &null; для этого параметра,
и параметр неявно приводится к строке (<type>string</type>). В результате
получается значение <literal>""</literal>.
В строгом режиме выбрасывается исключение <classname>TypeError</classname>.
</para>
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
var_dump(strlen(null));
// "Deprecated: Passing null to parameter #1 ($string) of type string is deprecated" начиная с PHP 8.1.0
// int(0)
var_dump(str_contains("foobar", null));
// "Deprecated: Passing null to parameter #2 ($needle) of type string is deprecated" начиная с PHP 8.1.0
// bool(true)
?>
]]>
</programlisting>
</informalexample>
</note>
<sect2 role="seealso">
&reftitle.seealso;
<para>
<simplelist>
<member><function>function_exists</function></member>
<member><link linkend="funcref">справочник функций</link></member>
<member><function>get_extension_funcs</function></member>
<member><function>dl</function></member>
</simplelist>
</para>
</sect2>
</sect1>
<sect1 xml:id="functions.anonymous">
<title>Анонимные функции</title>
<simpara>
Анонимные функции, которые также знают как замыкания (<literal>closures</literal>) —
функции без имени.
Анонимные функции вызывают или передают как значения
параметрам с типом <type>callable</type>.
</simpara>
<simpara>
PHP создаёт анонимные функции через класс
<link linkend="class.closure"><classname>Closure</classname></link>.
</simpara>
<example>
<title>Пример анонимной функции</title>
<programlisting role="php">
<![CDATA[
<?php
echo preg_replace_callback(
'~-([a-z])~',
function ($match) {
return strtoupper($match[1]);
},
'hello-world'
);
// Выведет helloWorld
?>
]]>
</programlisting>
</example>
<simpara>
Замыкания также присваивают как значения переменным; PHP автоматически
преобразовывает такие выражения в экземпляры внутреннего класса
<classname>Closure</classname>.
Замыкания присваивают переменной тем же синтаксисом,
что и для другого присваивания, включая конечную точку с запятой:
</simpara>
<example>
<title>
Пример присваивания анонимной функции как значения переменной
</title>
<programlisting role="php">
<![CDATA[
<?php
$greet = function($name) {
printf("Привет, %s\r\n", $name);
};
$greet('Мир');
$greet('PHP');
?>
]]>
</programlisting>
</example>
<simpara>
Замыкания также наследуют переменные из родительской
области видимости. Каждая такая переменная должна быть
передана в языковую конструкцию <literal>use</literal>. Начиная с
PHP 7.1 эти переменные не должны включать &link.superglobals;,
переменную <varname>$this</varname> и переменные с именами, которые
совпадают с названиями параметров функции.
Объявление типа для значения, которое возвращает функция,
указывают <emphasis>после</emphasis> конструкции <literal>use</literal>.
</simpara>
<example>
<title>
Пример наследования переменных из родительской области видимости
</title>
<programlisting role="php">
<![CDATA[
<?php
$message = 'привет';
// Без конструкции use
$example = function () {
var_dump($message);
};
$example();
// Наследуем переменную $message
$example = function () use ($message) {
var_dump($message);
};
$example();
// Анонимная функция наследует переменную с тем значением, которое переменная
// содержала перед определением функции, а не в месте вызова функции
$message = 'мир';
$example();
// Сбросим message
$message = 'привет';
// Наследование по ссылке
$example = function () use (&$message) {
var_dump($message);
};
$example();
// Значение, которое изменили в родительской области видимости,
// отражается внутри вызова функции
$message = 'мир';
echo $example();
// Замыкания умеют принимать обычные аргументы
$example = function ($arg) use ($message) {
var_dump($arg . ', ' . $message);
};
$example("привет");
// Объявление типа значения, которое вернёт функция, идёт после конструкции use
$example = function () use ($message): string {
return "привет, $message";
};
var_dump($example());
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
Notice: Undefined variable: message in /example.php on line 6
NULL
string(12) "привет"
string(12) "привет"
string(12) "привет"
string(6) "мир"
string(20) "привет, мир"
string(20) "привет, мир"
]]>
</screen>
</example>
<para>
Начиная с PHP 8.0.0 списку переменных, которые функция
наследует из области видимости, разрешается включать конечную запятую,
которую парсер проигнорирует.
</para>
<simpara>
Наследование переменных из родительской области видимости
<emphasis>отличается</emphasis> от наследования глобальных переменных.
Глобальные переменные существуют в глобальной области видимости,
которая остаётся прежней, какая бы функция ни выполнялась.
Родительская область видимости замыкания —
функция, в которой объявили замыкание; не обязательно
функция, из которой замыкание вызвали. Смотрите следующий
пример:
</simpara>
<example>
<title>Замыкания и область видимости</title>
<programlisting role="php">
<![CDATA[
<?php
// Базовая корзина покупок, которая содержит список
// продуктов и количество каждого продукта. Включает метод,
// который вычисляет общую цену элементов корзины через
// callback-замыкание
class Cart
{
const PRICE_BUTTER = 1.00;
const PRICE_MILK = 3.00;
const PRICE_EGGS = 6.95;
protected $products = array();
public function add($product, $quantity)
{
$this->products[$product] = $quantity;
}
public function getQuantity($product)
{
return isset($this->products[$product]) ? $this->products[$product] :
FALSE;
}
public function getTotal($tax)
{
$total = 0.00;
$callback = function ($quantity, $product) use ($tax, &$total)
{
$pricePerItem = constant(
__CLASS__ . "::PRICE_" . strtoupper($product)
);
$total += ($pricePerItem * $quantity) * ($tax + 1.0);
};
array_walk($this->products, $callback);
return round($total, 2);
}
}
$my_cart = new Cart;
// Добавляем элементы в корзину
$my_cart->add('butter', 1);
$my_cart->add('milk', 3);
$my_cart->add('eggs', 6);
// Выводим общую сумму с налогом 5 % на продажу
print $my_cart->getTotal(0.05) . "\n";
// Результат будет равен 54.29
?>
]]>
</programlisting>
</example>
<example>
<title>Автоматическое связывание переменной <literal>$this</literal></title>
<programlisting role="php">
<![CDATA[
<?php
class Test
{
public function testing()
{
return function() {
var_dump($this);
};
}
}
$object = new Test();
$function = $object->testing();
$function();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
object(Test)#1 (0) {
}
]]>
</screen>
</example>
<para>
При объявлении замыкания в контексте класса, текущий
класс автоматически связывается с замыканием, а члены функции
получают доступ к переменной <literal>$this</literal> в области
видимости функции. Определяют
<link linkend="functions.anonymous-functions.static">статические анонимные функции</link>,
если не требуется автоматическое связывание с текущим классом.
</para>
<sect2 xml:id="functions.anonymous-functions.static">
<title>Статические анонимные функции</title>
<para>
Анонимные функции разрешается объявлять статически.
Это предотвратит автоматическое связывание замыкания
с текущим классом. Объекты также не будут с связаны с замыканием
во время выполнения.
</para>
<para>
<example>
<title>
Попытка обратиться к переменной <literal>$this</literal>
в статической анонимной функции
</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
function __construct()
{
$func = static function() {
var_dump($this);
};
$func();
}
};
new Foo();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Notice: Undefined variable: this in %s on line %d
NULL
]]>
</screen>
</example>
</para>
<para>
<example>
<title>
Попытка связать объект со статической анонимной функцией
</title>
<programlisting role="php">
<![CDATA[
<?php
$func = static function() {
// Тело функции
};
$func = $func->bindTo(new stdClass);
$func();
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
Warning: Cannot bind an instance to a static closure in %s on line %d
]]>
</screen>
</example>
</para>
</sect2>
<sect2 role="changelog">
&reftitle.changelog;
<para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>&Version;</entry>
<entry>&Description;</entry>
</row>
</thead>
<tbody>
<row>
<entry>7.1.0</entry>
<entry>
Анонимные функции не могут замыкаться вокруг &link.superglobals;,
переменной <varname>$this</varname> или другой переменной,
имя которой совпадает с названием параметра.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
</sect2>
<sect2 role="notes">
&reftitle.notes;
<note>
<simpara>
Внутри замыканий разрешается вызывать функции
<function>func_num_args</function>,
<function>func_get_arg</function> и <function>func_get_args</function>.
</simpara>
</note>
</sect2>
</sect1>
<sect1 xml:id="functions.arrow">
<title>Стрелочные функции</title>
<simpara>
Стрелочные функции появились в PHP 7.4 как
лаконичный синтаксис для <link linkend="functions.anonymous">анонимных функций</link>.
</simpara>
<simpara>
И анонимные, и стрелочные функции реализовали
через класс <link linkend="class.closure"><classname>Closure</classname></link>.
</simpara>
<simpara>
Базовая форма записи стрелочных функций:
<code>fn (argument_list) =&gt; expr</code>.
</simpara>
<simpara>
Стрелочные функции работают так же,
как <link linkend="functions.anonymous">анонимные функции</link>,
за исключением того, что доступ к переменным
родительской области стрелочные функции получают автоматически.
</simpara>
<simpara>
Когда стрелочная функция работает с переменной,
которую определили в родительской области,
переменная неявно захватывается по значению.
В следующем примере функции <varname>$fn1</varname>
и <varname>$fn2</varname> ведут себя одинаково.
</simpara>
<para>
<example>
<title>
Стрелочные функции захватывают переменные по значению автоматически
</title>
<programlisting role="php">
<![CDATA[
<?php
$y = 1;
$fn1 = fn($x) => $x + $y;
// Эквивалентно получению переменной $y по значению:
$fn2 = function ($x) use ($y) {
return $x + $y;
};
var_export($fn1(3));
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
4
]]>
</screen>
</example>
</para>
<simpara>
Это также работает во вложенных стрелочных функциях:
</simpara>
<para>
<example>
<title>
Стрелочные функции захватывают переменные по значению автоматически,
даже когда функции вложены
</title>
<programlisting role="php">
<![CDATA[
<?php
$z = 1;
$fn = fn($x) => fn($y) => $x * $y + $z;
// Выведет 51
var_export($fn(5)(10));
?>
]]>
</programlisting>
</example>
</para>
<simpara>
Как и в анонимных функциях,
синтаксис стрелочных функций допускает произвольные сигнатуры функций,
включая типы параметров и возвращаемых значений, значения по умолчанию,
переменные и передачу и возврат по ссылке.
Корректные примеры стрелочных функций:
</simpara>
<para>
<example>
<title>Примеры определения стрелочных функций</title>
<programlisting role="php">
<![CDATA[
<?php
fn(array $x) => $x;
static fn(): int => $x;
fn($x = 42) => $x;
fn(&$x) => $x;
fn&($x) => $x;
fn($x, ...$rest) => $rest;
?>
]]>
</programlisting>
</example>
</para>
<simpara>
Стрелочные функции привязывают переменные по значению.
Это примерно эквивалентно выполнению конструкции <code>use($x)</code>
для каждой переменной <varname>$x</varname>, с которой стрелочная функция
будет работать внутри.
Привязка по значению означает, что внутри стрелочной функции невозможно
изменить значения из внешней области видимости.
Вместо этого для привязок по ссылкам можно пользоваться
<link linkend="functions.anonymous">анонимными функциями</link>.
</simpara>
<para>
<example>
<title>
Стрелочные функции не умеют изменять значения из внешней области видимости
</title>
<programlisting role="php">
<![CDATA[
<?php
$x = 1;
$fn = fn() => $x++; // Ничего не изменит
$fn();
var_export($x); // Выведет 1
?>
]]>
</programlisting>
</example>
</para>
<sect2 role="changelog">
&reftitle.changelog;
<para>
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>&Version;</entry>
<entry>&Description;</entry>
</row>
</thead>
<tbody>
<row>
<entry>7.4.0</entry>
<entry>
Появились стрелочные функции.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</para>
</sect2>
<sect2 role="notes">
&reftitle.notes;
<note>
<simpara>
Разрешается вызывать изнутри стрелочной функции
функции <function>func_num_args</function>,
<function>func_get_arg</function> и <function>func_get_args</function>.
</simpara>
</note>
</sect2>
</sect1>
<sect1 xml:id="functions.first_class_callable_syntax">
<title>Синтаксис callable-объектов первого класса</title>
<para>
Синтаксис callable-функций как объектов первого класса
представили в PHP 8.1.0 как способ, которым
<link linkend="functions.anonymous">анонимные функции</link>
создают из <link linkend="language.types.callable">callable-объектов</link>.
Он заменяет существующий синтаксис вызываемых объектов
со строками и массивами.
Преимущество синтаксиса состоит в том,
что он доступен для статического анализа
и использует область видимости точки, в которой получили callable-объект.
</para>
<para>
Синтаксис <code>CallableExpr(...)</code>
создаёт объект <classname>Closure</classname> из callable-объекта.
Часть <code>CallableExpr</code> принимает любое выражение,
которое можно непосредственно вызвать в грамматике PHP:
<example>
<title>Простой пример синтаксиса callable-объекта первого класса</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public function method() {}
public static function staticmethod() {}
public function __invoke() {}
}
$obj = new Foo();
$classStr = 'Foo';
$methodStr = 'method';
$staticmethodStr = 'staticmethod';
$f1 = strlen(...);
$f2 = $obj(...); // Вызываемый объект
$f3 = $obj->method(...);
$f4 = $obj->$methodStr(...);
$f5 = Foo::staticmethod(...);
$f6 = $classStr::$staticmethodStr(...);
// Традиционный callable-синтаксис со строками и массивами
$f7 = 'strlen'(...);
$f8 = [$obj, 'method'](...);
$f9 = [Foo::class, 'staticmethod'](...);
?>
]]>
</programlisting>
</example>
</para>
<note>
<para>
Оператор <code>...</code> — часть синтаксиса, а не пропуск.
</para>
</note>
<para>
У выражения <code>CallableExpr(...)</code> та же семантика,
что и у метода <methodname>Closure::fromCallable</methodname>.
То есть, в отличие от callable-синтаксиса
со строками и массивами, синтаксис <code>CallableExpr(...)</code>
учитывает область видимости в той точке, в которой его создали:
<example>
<title>
Сравнение области действия синтаксиса <code>CallableExpr(...)</code>
и традиционного callable-синтаксиса
</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public function getPrivateMethod()
{
return [$this, 'privateMethod'];
}
private function privateMethod()
{
echo __METHOD__, "\n";
}
}
$foo = new Foo();
$privateMethod = $foo->getPrivateMethod();
$privateMethod();
// Fatal error: Call to private method Foo::privateMethod() from global scope
// Причина фатальной ошибки в том, что вызов выполнили за пределами класса Foo,
// и с этого момента будет проверяться видимость.
class Foo1
{
public function getPrivateMethod()
{
// Использует область видимости, в которой получен callable-объект
return $this->privateMethod(...); // Идентично вызову Closure::fromCallable([$this, 'privateMethod']);
}
private function privateMethod()
{
echo __METHOD__, "\n";
}
}
$foo1 = new Foo1();
$privateMethod = $foo1->getPrivateMethod();
$privateMethod(); // Foo1::privateMethod
?>
]]>
</programlisting>
</example>
</para>
<note>
<para>
Создать объект этим синтаксисом (например, <code>new Foo(...)</code>)
нельзя, поскольку синтаксис <code>new Foo()</code>
не признаётся вызовом.
</para>
</note>
<note>
<para>
Синтаксис callable-объектов первого класса
нельзя комбинировать
с <link linkend="language.oop5.basic.nullsafe">оператором Nullsafe</link>.
Оба следующих результата приводят к ошибке времени компиляции:
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$obj?->method(...);
$obj?->prop->method(...);
?>
]]>
</programlisting>
</informalexample>
</para>
</note>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->