Files
php-doc-ru/language/types/declarations.xml
Sergey Panteleev 24413478ad docs(ru): Updated to English revision
git-svn-id: https://svn.php.net/repository/phpdoc/ru/trunk@351812 c90b9560-bf6c-de11-be94-00142212c4b1
2020-12-01 07:02:08 +00:00

819 lines
27 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"?>
<!-- $Revision$ -->
<!-- EN-Revision: 1ffbadf2906a5c85d2f9437b87da3e204ea41b7a Maintainer: rjhdby Status: ready -->
<!-- Reviewed: no -->
<sect1 xml:id="language.types.declarations">
<title>Объявление типов</title>
<para>
Объявления типов могут использоваться для аргументов функций, возвращаемых значений и,
начиная с PHP 7.4.0, для свойств класса. Они используются во время исполнения для проверки,
что значение имеет точно тот тип, который для них указан. В противном случае будет выброшено
исключение <classname>TypeError</classname>.
</para>
<!-- Find better place where to put this note -->
<note>
<!-- TODO Link to covariance section -->
<para>
При переопределении родительского метода, тип возвращаемого значения дочернего метода должен
соответствовать любому объявлению возвращаемого типа родительского. Если в родительском
методе тип возвращаемого значения не объявлен, то это можно сделать в дочернем.
</para>
</note>
<sect2 xml:id="language.types.declarations.base">
<title>Одиночные типы</title>
<informaltable>
<tgroup cols="3">
<thead>
<row>
<entry>&Type;</entry>
<entry>&Description;</entry>
<entry>&Version;</entry>
</row>
</thead>
<tbody>
<row>
<entry>Имя класса/интерфейса</entry>
<entry>
Значение должно представлять собой &instanceof; заданного класса или интерфейса.
</entry>
<entry></entry>
</row>
<row>
<entry><type>self</type></entry>
<entry>
Значение должно представлять собой &instanceof; того же класса, в котором определён метод.
Может использоваться только в классах.
</entry>
<entry></entry>
</row>
<row>
<entry><type>array</type></entry>
<entry>
Значение должно быть типа <type>array</type>.
</entry>
<entry></entry>
</row>
<row>
<entry><type>callable</type></entry>
<entry>
Значение должно быть корректным <type>callable</type>.
Нельзя использовать в качестве объявления для свойств класса.
</entry>
<entry></entry>
</row>
<row>
<entry><type>bool</type></entry>
<entry>
Значение должно быть логического типа.
</entry>
<entry></entry>
</row>
<row>
<entry><type>float</type></entry>
<entry>
Значение должно быть числом с плавающей запятой.
</entry>
<entry></entry>
</row>
<row>
<entry><type>int</type></entry>
<entry>
Значение должно быть целым числом.
</entry>
<entry></entry>
</row>
<row>
<entry><type>string</type></entry>
<entry>
Значение должно быть строкой (тип <type>string</type>).
</entry>
<entry></entry>
</row>
<row>
<entry><type>iterable</type></entry>
<entry>
Значение может быть либо массивом (тип <type>array</type>) или представлять собой
&instanceof; <classname>Traversable</classname>.
</entry>
<entry>PHP 7.1.0</entry>
</row>
<row>
<entry><type>object</type></entry>
<entry>
Значение должно быть объектом (тип <type>object</type>).
</entry>
<entry>PHP 7.2.0</entry>
</row>
<row>
<entry><type>mixed</type></entry>
<entry>
Значение может иметь любой тип.
</entry>
<entry>PHP 8.0.0</entry>
</row>
</tbody>
</tgroup>
</informaltable>
<warning>
<para>
Псевдонимы для указанных выше скалярных типов не поддерживаются.
В случае использования они будут считаться за имя класса или интерфейса.
К примеру, при использовании в качестве типа <literal>boolean</literal>, он будет
ожидать, что значение представляет собой &instanceof; класса или интерфейса
<literal>boolean</literal>, а не значение типа <type>bool</type>:
</para>
<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>
</para>
</warning>
<sect3 xml:id="language.types.declarations.mixed">
<title>mixed</title>
<para>
<type>mixed</type> эквивалентен <link linkend="language.types.declarations.union">типу union</link>
<type class="union">
<type>array</type><type>bool</type><type>callable</type><type>int</type><type>float</type>
<type>object</type><type>resource</type><type>string</type><type>null</type>
</type>.
Доступно с PHP 8.0.0.
</para>
</sect3>
<sect3 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;
}
// Обратите внимание, что будет возвращено число с плавающей запятой.
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>
</sect3>
</sect2>
<sect2 xml:id="language.types.declarations.nullable">
<title>Обнуляемые типы</title>
<para>
Начиная с PHP 7.1.0, объявления типов могут быть помечены как обнуляемые, путём
добавления префикса в виде знака вопроса(<literal>?</literal>).
Это означает, что значение может быть как объявленного типа, так и быть равным &null;.
</para>
<para>
<example>
<title>Объявление обнуляемых типов</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>Обнуляемые типы для возвращаемого значения</title>
<programlisting role="php">
<![CDATA[
<?php
function get_item(): ?string {
if (isset($_GET['item'])) {
return $_GET['item'];
} else {
return null;
}
}
?>
]]>
</programlisting>
</example>
</para>
<note>
<para>
До PHP 7.1.0, было возможно задавать обнуляемые типы аргументов функций путём
задания значения по умолчанию равного <literal>null</literal>.
Так делать не рекомендуется, поскольку это может поломать наследование.
</para>
<example>
<title>Старый способ задавать обнуляемые типы для аргументов</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>
</sect2>
<sect2 xml:id="language.types.declarations.union">
<title>Объединённые типы</title>
<para>
Объединённые типы позволяют использовать несколько типов, а не исключительно один.
Для их объявления используется следующий синтаксис <literal>T1|T2|...</literal>.
Объединённые типы доступны начиная с PHP 8.0.0.
</para>
<sect3 xml:id="language.types.declarations.union.nullable">
<title>Обнуляемые объединённые типы</title>
<para>
Тип <literal>null</literal> можно использовать как часть объединений следующим образов:
<literal>T1|T2|null</literal>.
Существующая нотация <literal>?T</literal> рассматривается как сокращение для <literal>T|null</literal>.
</para>
<caution>
<simpara>
<literal>null</literal> не может использоваться как отдельный тип.
</simpara>
</caution>
</sect3>
<sect3 xml:id="language.types.declarations.union.false">
<title>Псевдо-тип false</title>
<para>
Псевдо-тип <literal>false</literal> поддерживается как часть объединённых типов.
Он добавлен по историческим причинам, так как многие встроеные функции возвращают
<literal>false</literal> вместо <literal>null</literal> в случае ошибок.
Классический пример - функция <function>strpos</function>.
</para>
<caution>
<simpara>
<literal>false</literal> нельзя использовать как самостоятельный тип (включая
обнуляемый вариант).
Таким образом, все объявления такого типа недопустимы: <literal>false</literal>, <literal>false|null</literal>
и <literal>?false</literal>.
</simpara>
</caution>
<caution>
<simpara>
Обратите внимание, что псевдо-тип <literal>true</literal> <emphasis>не</emphasis> существует.
</simpara>
</caution>
</sect3>
<sect3 xml:id="language.types.declarations.union.redundant">
<title>Дублирующиеся и повторяющиеся типы</title>
<para>
Для отлова простых ошибок в объединённых объявлениях, повторяющиеся типы, которые
можно отследить без загрузки класса, приведут к ошибке компиляции.
В том числе:
<itemizedlist>
<listitem>
<simpara>
Каждый тип распознаваемый по имени должен встречаться только один раз.
Типы вида <literal>int|string|INT</literal> приведут к ошибке.
</simpara>
</listitem>
<listitem>
<simpara>
Если используется <type>bool</type>, то использовать дополнительно <type>false</type> нельзя.
</simpara>
</listitem>
<listitem>
<simpara>
Если используется тип <type>object</type>, то дополнительное использование имён классов недопустимо.
</simpara>
</listitem>
<listitem>
<simpara>
Если используется <type>iterable</type>, то к нему нельзя добавить <type>array</type>
или <classname>Traversable</classname>.
</simpara>
</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 {} // Запрещено
use A as B;
function foo(): A|B {} // Запрещено ("use" является частью разрешения имён)
class_alias('X', 'Y');
function foo(): X|Y {} // Допустимо (повторение будет определено только во время выполнения)
?>
]]>
</programlisting>
</informalexample>
</para>
</sect3>
</sect2>
<sect2 xml:id="language.types.declarations.return-only">
<title>Типы, подходящие только для возвращаемого значения</title>
<sect3 xml:id="language.types.declarations.void">
<title>void</title>
<para>
Тип <literal>void</literal> означает, что функция ничего не возвращает.
Соответственно он не может быть частью объединения.
Доступно с PHP 7.1.0.
</para>
</sect3>
<sect3 xml:id="language.types.declarations.static">
<title>static</title>
<para>
Значение должно представлять собой &instanceof; того же класса, в котором был вызван метод.
Доступно с PHP 8.0.0.
</para>
</sect3>
</sect2>
<sect2 xml:id="language.types.declarations.strict">
<title>Строгое типизирование</title>
<para>
По умолчанию, PHP будет преобразовывать значения неправильного типа в ожидаемые.
К примеру, если в функцию передать параметр типа <type>int</type> в аргумент,
объявленный как <type>string</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>
<sect2 xml:id="language.types.declarations.union.coercive">
<title>Приведение для объеденённых типов</title>
<para>
Если <literal>strict_types</literal> не разрешено, то объявления скалярных типов
подлежат ограниченному неявному приведению. Если фактический тип не является частью объединения,
то используется следующий порядок приведения типов:
<orderedlist>
<listitem>
<simpara>
<type>int</type>
</simpara>
</listitem>
<listitem>
<simpara>
<type>float</type>
</simpara>
</listitem>
<listitem>
<simpara>
<type>string</type>
</simpara>
</listitem>
<listitem>
<simpara>
<type>bool</type>
</simpara>
</listitem>
</orderedlist>
Если тип присутствует в объединении и значение может быть приведено к нему основываясь на
существующей семантике приведения типов PHP, то поизойдёт приведение к нему. Если нет, то
проверится следующий тип в списке.
</para>
<caution>
<para>
Единственным исключением является приведение строки в случае, если в объединении
одновременно присутствуют и <type>int</type> и <type>float</type>. В таком случае будет
выбрано наиболее подходящий тип по правилу приведения "числовых строк".
К примеру, для <literal>"42"</literal> бдет выбран тип <type>int</type>,
а для <literal>"42.0"</literal> тип <type>float</type>.
</para>
</caution>
<note>
<para>
Типы, не входящие в данный список не подходят для целей неявного приведения.
targets for implicit coercion. В часности, для <literal>null</literal>
и <literal>false</literal> неявного приведения не случится.
</para>
</note>
<example>
<title>Пример приведения для объединённых типов</title>
<programlisting role="php">
<![CDATA[
<?php
// int|string
42 --> 42 // явный тип
"42" --> "42" // явный тип
new ObjectWithToString --> строка с результатом выполнения __toString()
// Объекты никогда не будут приведены к целому числу, даже если вернут "числовую строку"
42.0 --> 42 // float совместим с int
42.1 --> 42 // float совместим с int
1e100 --> "1.0E+100" // float слишком большой для типа int, преобразуется в строку
INF --> "INF" // float слишком большой для типа int, преобразуется в строку
true --> 1 // bool совместим с int
[] --> TypeError // array не совместим ни с int ни со string
// int|float|bool
"45" --> 45 // целочисленная "чистовая строка"
"45.0" --> 45.0 // "чистовая строка" с плавающей запятой
"45X" --> true // не "чистовая строка", приведётся к bool
"" --> false // не "чистовая строка", приведётся к bool
"X" --> true // не "чистовая строка", приведётся к bool
[] --> TypeError // array не совместим ни с int ни с float ни с bool
?>
]]>
</programlisting>
</example>
</sect2>
<!-- TODO figure out what do to with these things -->
<sect2 xml:id="language.types.declarations.misc">
<title>Дополнительно</title>
<example>
<title>Типизированые параметры передаваемые по ссылке</title>
<simpara>
Объявление типов для параметров передаваемых по ссылке проверяется только на этапе вызова функции.
Не никакой гарантии, что после выхода из функции тип переменной останется неизменным.
</simpara>
<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.8;
<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>
<example>
<title>Обработка <classname>TypeError</classname></title>
<programlisting role="php">
<![CDATA[
<?php
declare(strict_types=1);
function sum(int $a, int $b) {
return $a + $b;
}
try {
var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5));
} catch (TypeError $e) {
echo 'Ошибка: ', $e->getMessage();
}
?>
]]>
</programlisting>
&example.outputs.8;
<screen>
<![CDATA[
int(3)
Ошибка: sum(): Argument #1 ($a) must be of type int, float given, called in - on line 10
]]>
</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
-->