1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-23 23:32:16 +01:00
Files
archived-doc-ru/language/functions.xml
2026-01-08 22:34:35 +03:00

1871 lines
66 KiB
XML
Raw Permalink 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: dd87866772c31671146ff778140dc0955c55005c 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>
Функции определяют ключевым словом <literal>function</literal>,
за которым через пробел идёт название функции и круглые скобки.
Круглые скобки оставляют пустыми или определяют в скобках список параметров; параметры разделяют символом <literal>,</literal>.
За круглыми скобками идёт пара фигурных скобок — тело функции. Приведём пример определения функции:
</para>
<example>
<title>Объявление новой функции с названием <literal>foo</literal></title>
<programlisting role="php">
<![CDATA[
<?php
function foo($arg_1, $arg_2, /* ..., */ $arg_n)
{
echo "Пример функции.\n";
return $retval;
}
]]>
</programlisting>
</example>
<note>
<para>
С PHP 8.0.0 список параметров допускает завершающую запятую:
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
function foo($arg_1, $arg_2,) {}
]]>
</programlisting>
</informalexample>
</para>
</note>
<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>
<!-- Note: this paragraph feels like it should be moved to the syntax part? -->
<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>
<note>
<para>
С PHP 7.3.0 список аргументов при вызове функций допускает
завершающую запятую:
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
$v = foo(
$arg_1,
$arg_2,
);
]]>
</programlisting>
</informalexample>
</para>
</note>
<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; по умолчанию делает тип неявно обнуляемым.
Такое определение устарело с PHP 8.4.0
и теперь <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) {} // С PHP 8.1.0 параметр $a неявно обязателен, поскольку идёт перед обязательным,
// но неявно обнуляем, поскольку для параметра определили значение по умолчанию null.
// Объявления значения по умолчанию null
// без указания обнуляемого типа устарело с PHP 8.4.0
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>
При передаче именованных аргументов с одним и тем же названием параметра
выбрасывается ошибка <classname>Error</classname>.
</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>
Функция вернёт значение &null;,
если инструкцию <function>return</function> не указали.
</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>8.3.0</entry>
<entry>
Замыкания, которые создали из <link linkend="language.oop5.magic">магических методов</link>,
научились принимать именованные аргументы.
</entry>
</row>
<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($x): 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-синтаксис со строками и массивами.
Преимущество нового синтаксиса состоит в доступности для статического анализа
и наследовании новым синтаксисом области видимости переменных в точке
получения callable-выражения.
</para>
<para>
Синтаксис <code>CallableExpr(...)</code> создаёт объект
<classname>Closure</classname> из выражения, доступного для вызова,
где <code>CallableExpr</code> — элемент синтаксиса, который принимает выражение,
доступное для прямого вызова в терминах PHP-грамматики:
<example>
<title>Пример создания первоклассных вызываемых значений синтаксисом с многоточием</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">с null-безопасным оператором</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
-->