Files
php-doc-ru/language/types/declarations.xml
2024-01-18 12:02:03 +03:00

807 lines
25 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: e612d76529255bc8db91d0f87e5636e3a405dd38 Maintainer: rjhdby Status: ready -->
<!-- Reviewed: no -->
<sect1 xml:id="language.types.declarations">
<title>Объявления типов</title>
<para>
Объявления типов разрешено добавлять к аргументам функции, возвращаемым значениям
и свойствам класса (последнее начиная с PHP 7.4.0).
Объявленные типы гарантируют, что во время вызова значение принадлежит тому типу, который для него указан,
иначе будет выброшено исключение <classname>TypeError</classname>.
</para>
<para>
Каждый тип, который поддерживает PHP, за исключением ресурсов (<type>resource</type>),
разрешено указывать при пользовательском объявлении типа.
На этой странице приведён журнал изменений доступности отдельных типов
и документация о том, как их применять в объявлениях типов.
</para>
<note>
<para>
Когда класс реализует метод интерфейса или переопределяет метод,
который уже был определён родительским классом,
вновь определяемый метод должен быть совместим с определением,
которое было сделано ранее.
Метод совместим, если он соблюдает
<link linkend="language.oop5.variance">правила вариантности</link>.
</para>
</note>
<sect2 role="changelog">
&reftitle.changelog;
<informaltable>
<tgroup cols="2">
<thead>
<row>
<entry>&Version;</entry>
<entry>&Description;</entry>
</row>
</thead>
<tbody>
<row>
<entry>8.3.0</entry>
<entry>
Добавлена поддержка типизации констант классов, интерфейсов, трейтов и перечислений.
</entry>
</row>
<row>
<entry>8.2.0</entry>
<entry>
Добавлена поддержка типов <acronym>DNF</acronym>.
</entry>
</row>
<row>
<entry>8.2.0</entry>
<entry>
Добавлена поддержка типа <type>true</type>.
</entry>
</row>
<row>
<entry>8.2.0</entry>
<entry>
Типы <type>null</type> и <type>false</type> теперь можно использовать автономно.
</entry>
</row>
<row>
<entry>8.1.0</entry>
<entry>
Добавлена поддержка пересечений типов.
</entry>
</row>
<row>
<entry>8.1.0</entry>
<entry>
Возврат по ссылке из функции с типом возвращаемого значения <type>void</type> устарел.
</entry>
</row>
<row>
<entry>8.1.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения <type>never</type>.
</entry>
</row>
<row>
<entry>8.0.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения <type>mixed</type>.
</entry>
</row>
<row>
<entry>8.0.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения <type>static</type>.
</entry>
</row>
<row>
<entry>8.0.0</entry>
<entry>
Добавлена поддержка объединения типов.
</entry>
</row>
<row>
<entry>7.4.0</entry>
<entry>
Добавлена поддержка типизации свойств классов.
</entry>
</row>
<row>
<entry>7.2.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения <type>object</type>.
</entry>
</row>
<row>
<entry>7.1.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения <type>iterable</type>.
</entry>
</row>
<row>
<entry>7.1.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения <type>void</type>.
</entry>
</row>
<row>
<entry>7.1.0</entry>
<entry>
Добавлена поддержка типа возвращаемого значения nullable.
</entry>
</row>
</tbody>
</tgroup>
</informaltable>
</sect2>
<sect2 xml:id="language.types.declarations.base">
<title>Примечание по использованию атомарных типов</title>
<simpara>
У атомарных типов прямолинейное поведение с незначительными оговорками,
которые описаны в этом разделе.
</simpara>
<sect3 xml:id="language.types.declarations.base.scalar">
<title>Скалярные типы</title>
<warning>
<para>
Псевдонимы имён для скалярных типов (<type>bool</type>, <type>int</type>,
<type>float</type>, <type>string</type>) не поддерживаются.
Вместо этого они рассматриваются как имена классов или интерфейсов.
Наример, когда в качестве типа указан <literal>boolean</literal>, ожидается,
что значение выполняет условие &instanceof; в отношении класса или интерфейса
<literal>boolean</literal>, а не значение типа <type>bool</type>:
</para>
<example>
<programlisting role="php">
<![CDATA[
<?php
function test(boolean $param) {}
test(true);
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
Warning: "boolean" will be interpreted as a class name. Did you mean "bool"? Write "\boolean" to suppress this warning in /in/9YrUX on line 2
Fatal error: Uncaught TypeError: test(): Argument #1 ($param) must be of type boolean, bool given, called in - on line 3 and defined in -:2
Stack trace:
#0 -(3): test(true)
#1 {main}
thrown in - on line 2
]]>
</screen>
</example>
</warning>
</sect3>
<sect3 xml:id="language.types.declarations.void">
<title>void</title>
<note>
<para>
Возврат по ссылке из <type>void</type>-функции устарел начиная с PHP 8.1.0,
поскольку такая функция противоречива.
Ранее при её вызове выдавалась ошибка уровня <constant>E_NOTICE</constant>:
<computeroutput>Только ссылки на переменные должны возвращаться по ссылке</computeroutput>.
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
function &test(): void {}
?>
]]>
</programlisting>
</informalexample>
</para>
</note>
</sect3>
<sect3 xml:id="language.types.declarations.base.function">
<title>Тип Callable</title>
<para>
Этот тип нельзя объявлять в качестве типа свойства класса.
</para>
<note>
<simpara>
Этот тип невозможно указать в качестве названия функции.
</simpara>
</note>
</sect3>
<sect3 xml:id="language.types.declarations.references">
<title>Объявление типов в параметрах передачи по ссылкам</title>
<simpara>
Если у параметра, передаваемого по ссылке, объявляется тип возвращаемого значения,
тип переменной проверяется <emphasis>только</emphasis> при входе в функцию, в начале вызова,
но не при возврате функции.
То есть функция может изменить тип ссылки на переменную.
</simpara>
<example>
<title>Типизированные параметры, передаваемые по ссылке</title>
<programlisting role="php">
<![CDATA[
<?php
function array_baz(array &$param)
{
$param = 1;
}
$var = [];
array_baz($var);
var_dump($var);
array_baz($var);
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
int(1)
Fatal error: Uncaught TypeError: array_baz(): Argument #1 ($param) must be of type array, int given, called in - on line 9 and defined in -:2
Stack trace:
#0 -(9): array_baz(1)
#1 {main}
thrown in - on line 2
]]>
</screen>
</example>
</sect3>
</sect2>
<sect2 xml:id="language.types.declarations.composite">
<title>Примечание по составным типам</title>
<para>
На объявления составных типов распространяется ряд ограничений
и во время компиляции PHP выполнит проверку объявленных типов на избыточность,
чтобы предотвратить простые ошибки.
</para>
<caution>
<simpara>
До PHP 8.2.0 и появления <acronym>DNF</acronym>-типов, было невозможно
комбинировать пересечённые и объединённые типы.
</simpara>
</caution>
<sect3 xml:id="language.types.declarations.composite.union">
<title>Объединение типов</title>
<warning>
<simpara>
Невозможно комбинировать два типа значений — <type>false</type> и <type>true</type> — вместе в объединённом типе.
Вместо этого указывают <type>bool</type>.
</simpara>
</warning>
<caution>
<simpara>
Поскольку до PHP 8.2.0 нельзя было определять <type>false</type> и <type>null</type> как отдельные типы,
объединённый тип, который состоял только из этих типов, был недопустим.
Сюда входят типы: <type>false</type>, <literal>false|null</literal>
и <literal>?false</literal>.
</simpara>
</caution>
<sect4 xml:id="language.types.declarations.nullable">
<title>Синтаксический сахар типа Nullable</title>
<para>
Объявление одного базового типа может быть помечено как nullable путём
добавления к типу префикса в виде
вопросительного знака (<literal>?</literal>).
Поэтому <literal>?T</literal> и <literal>T|null</literal> идентичны.
</para>
<note>
<simpara>
Этот синтаксис поддерживается с PHP 7.1.0
и предшествует поддержке объединения типов.
</simpara>
</note>
<note>
<para>
Ещё один способом добиться nullable-аргументов —
указать <literal>null</literal> значением по умолчанию.
Такой способ не рекомендован, поскольку если значение по умолчанию
будет изменено в дочернем классе, возникнет нарушение совместимости типов,
так как в объявление типа нужно будет добавить тип <type>null</type>.
</para>
<example>
<title>Старый способ указания nullable-аргументов</title>
<programlisting role="php">
<![CDATA[
<?php
class C {}
function f(C $c = null) {
var_dump($c);
}
f(new C);
f(null);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
object(C)#1 (0) {
}
NULL
]]>
</screen>
</example>
</note>
</sect4>
</sect3>
<sect3 xml:id="language.types.declarations.composite.redundant">
<title>Повторяющиеся и избыточные типы</title>
<para>
Избыточные типы, которые можно обнаружить без выполнения загрузки класса,
приведут к ошибке во время компиляции,
чтобы отловить неточности в объявлениях составных типов. В них включены:
<itemizedlist>
<listitem>
<simpara>
Каждое имя после разрешения внутренними средствами языка, может встречаться только один раз.
Например, типы <literal>int|string|INT</literal> или <literal>Countable&amp;Traversable&amp;COUNTABLE</literal>
приведут к ошибке.
</simpara>
</listitem>
<listitem>
<simpara>
Указание типа <type>mixed</type> (с другими типами) приведёт к ошибке.
</simpara>
</listitem>
<listitem>
<simpara>Для объединённых типов:</simpara>
<itemizedlist>
<listitem>
<simpara>
Если указан тип <type>bool</type>, то <type>false</type> или <type>true</type>
не могут быть указаны дополнительно.
</simpara>
</listitem>
<listitem>
<simpara>
Если указан тип <type>object</type>, типы классов нельзя указывать дополнительно.
</simpara>
</listitem>
<listitem>
<simpara>
Если указан тип <type>iterable</type>, то <type>array</type> и <classname>Traversable</classname>
нельзя указывать дополнительно.
</simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<simpara>Для пересечённых типов:</simpara>
<itemizedlist>
<listitem>
<simpara>
Указание типа, который не относится к типу класса, приведёт к ошибке.
</simpara>
</listitem>
<listitem>
<simpara>
Указание <type>self</type>, <type>parent</type>
или <type>static</type> приведёт к ошибке.
</simpara>
</listitem>
</itemizedlist>
</listitem>
<listitem>
<simpara>Для <acronym>DNF</acronym>-типов:</simpara>
<itemizedlist>
<listitem>
<simpara>
Если указан более общий тип, то более строгий тип будет избыточным.
</simpara>
</listitem>
<listitem>
<simpara>
Дублирование членов в пересечённых типах.
</simpara>
</listitem>
</itemizedlist>
</listitem>
</itemizedlist>
</para>
<note>
<simpara>
Это не гарантирует, что тип «минимальный»,
поскольку для этого пришлось бы загрузить все указанные типы классов.
</simpara>
</note>
<para>
Например, если <literal>A</literal> и <literal>B</literal> — это псевдонимы классов,
то <literal>A|B</literal> остаётся корректным объединением типов,
даже если его можно свести либо к <literal>A</literal>, либо к <literal>B</literal>.
Аналогично, если класс <code>B extends A {}</code>, то <literal>A|B</literal> также является
корректным объединением типов, даже если его можно свести к просто <literal>A</literal>.
<informalexample>
<programlisting role="php">
<![CDATA[
<?php
function foo(): int|INT {} // Запрещено
function foo(): bool|false {} // Запрещено
function foo(): int&Traversable {} // Запрещено
function foo(): self&Traversable {} // Запрещено
use A as B;
function foo(): A|B {} // Запрещено («use» — часть разрешения имён)
function foo(): A&B {} // Запрещено («use» — часть разрешения имён)
class_alias('X', 'Y');
function foo(): X|Y {} // Разрешено (избыточность известна только во время выполнения)
function foo(): X&Y {} // Разрешено (избыточность известна только во время выполнения)
?>
]]>
</programlisting>
</informalexample>
</para>
</sect3>
</sect2>
<sect2 xml:id="language.types.declarations.examples">
&reftitle.examples;
<example>
<title>Пример объявления типа класса</title>
<programlisting role="php">
<![CDATA[
<?php
class C {}
class D extends C {}
// Не наследует C.
class E {}
function f(C $c) {
echo get_class($c)."\n";
}
f(new C);
f(new D);
f(new E);
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
C
D
Fatal error: Uncaught TypeError: f(): Argument #1 ($c) must be of type C, E given, called in /in/gLonb on line 14 and defined in /in/gLonb:8
Stack trace:
#0 -(14): f(Object(E))
#1 {main}
thrown in - on line 8
]]>
</screen>
</example>
<example>
<title>Пример объявления типа интерфейса</title>
<programlisting role="php">
<![CDATA[
<?php
interface I { public function f(); }
class C implements I { public function f() {} }
// Не реализует I.
class E {}
function f(I $i) {
echo get_class($i)."\n";
}
f(new C);
f(new E);
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
C
Fatal error: Uncaught TypeError: f(): Argument #1 ($i) must be of type I, E given, called in - on line 13 and defined in -:8
Stack trace:
#0 -(13): f(Object(E))
#1 {main}
thrown in - on line 8
]]>
</screen>
</example>
<example>
<title>Пример объявления типа возвращаемого значения</title>
<programlisting role="php">
<![CDATA[
<?php
function sum($a, $b): float {
return $a + $b;
}
// Обратите внимание, что будет возвращено значение float.
var_dump(sum(1, 2));
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
float(3)
]]>
</screen>
</example>
<example>
<title>Возвращение объекта</title>
<programlisting role="php">
<![CDATA[
<?php
class C {}
function getC(): C {
return new C;
}
var_dump(getC());
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
object(C)#1 (0) {
}
]]>
</screen>
</example>
<example>
<title>Объявление аргумента с типом Nullable</title>
<programlisting role="php">
<![CDATA[
<?php
class C {}
function f(?C $c) {
var_dump($c);
}
f(new C);
f(null);
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
object(C)#1 (0) {
}
NULL
]]>
</screen>
</example>
<example>
<title>Объявление типа возвращаемого значения Nullable</title>
<programlisting role="php">
<![CDATA[
<?php
function get_item(): ?string {
if (isset($_GET['item'])) {
return $_GET['item'];
} else {
return null;
}
}
?>
]]>
</programlisting>
</example>
<example>
<title>Объявление типа свойства класса</title>
<programlisting role="php">
<![CDATA[
<?php
class User {
public static string $foo = 'foo';
public int $id;
public string $username;
public function __construct(int $id, string $username) {
$this->id = $id;
$this->username = $username;
}
}
?>
]]>
</programlisting>
</example>
</sect2>
<sect2 xml:id="language.types.declarations.strict">
<title>Строгая типизация</title>
<para>
По умолчанию PHP будет преобразовывать значения неправильного типа в ожидаемые.
Например, если в строковый (<type>string</type>) параметр функции передать целое число (<type>int</type>),
то оно преобразуется в строку (<type>string</type>).
</para>
<para>
Можно включить режим строгой типизации на уровне файла. В этом
режиме, тип значения должен строго соответствовать объявленному, иначе будет выброшено
исключение <classname>TypeError</classname>.
Единственное исключение из этого правила — передача целочисленного значения (<type>int</type>)
туда, где ожидается число с плавающей точкой (<type>float</type>).
</para>
<warning>
<simpara>
На вызовы из внутренних функций действие <literal>strict_types</literal> не распространяется.
</simpara>
</warning>
<para>
Для включения строгой типизации указывают оператор &declare; с объявлением
<literal>strict_types</literal>:
</para>
<note>
<para>
Строгая типизация распространяется на вызовы функций, которые сделаны
<emphasis>изнутри</emphasis> файла с включённой строгой типизацией,
а не к функциям, объявленным в этом файле. Если из файла без включённой строгой
типизации вызывается функция, которая была определена в файле со строгой типизацией,
то будут использованы установки по типизации вызывающей стороны — т. е. правила строгой типизации
будут проигнорированы и для значений будет применяться приведение типов.
</para>
</note>
<note>
<para>
Строгая типизация определяется только для объявлений скалярных типов.
</para>
</note>
<example>
<title>Строгая типизация для значений аргументов</title>
<programlisting role="php">
<![CDATA[
<?php
declare(strict_types=1);
function sum(int $a, int $b) {
return $a + $b;
}
var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5));
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
int(3)
Fatal error: Uncaught TypeError: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 9 and defined in -:4
Stack trace:
#0 -(9): sum(1.5, 2.5)
#1 {main}
thrown in - on line 4
]]>
</screen>
</example>
<example>
<title>Приведение типов для значений аргументов</title>
<programlisting role="php">
<![CDATA[
<?php
function sum(int $a, int $b) {
return $a + $b;
}
var_dump(sum(1, 2));
// Переданные значения будут приведены к целым числам: обратите внимание на вывод ниже!
var_dump(sum(1.5, 2.5));
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
int(3)
int(3)
]]>
</screen>
</example>
<example>
<title>Строгая типизация для возвращаемых значений</title>
<programlisting role="php">
<![CDATA[
<?php
declare(strict_types=1);
function sum($a, $b): int {
return $a + $b;
}
var_dump(sum(1, 2));
var_dump(sum(1, 2.5));
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
int(3)
Fatal error: Uncaught TypeError: sum(): Return value must be of type int, float returned in -:5
Stack trace:
#0 -(9): sum(1, 2.5)
#1 {main}
thrown in - on line 5
]]>
</screen>
</example>
</sect2>
</sect1>
<!-- 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
-->