论坛PHP 2024-巴黎(法国)

类型声明

类型声明可以添加到函数参数、返回值、,以及从PHP 7.4.0开始的类属性。他们确保价值在调用时为指定类型,否则为类型错误被抛出。

PHP支持的每种类型,除了资源可以在user-land类型声明中使用。此页面包含不同类型可用性的更改日志以及关于在类型声明中使用它们的文档。

注释:

当类实现接口方法或重新实现已经由父类定义,它必须与上述定义。如果方法遵循方差规则。

变更日志

版本 描述
8.3.0 添加了对类、接口、特征和枚举常量类型的支持。
8.2.0 支持地下城与勇士类型已添加。
8.2.0 支持文字类型真的已添加。
8.2.0 类型无效现在可以独立使用。
8.1.0 添加了对交叉点类型的支持。
8.1.0 通过引用从返回无效函数现在已弃用。
8.1.0 支持仅返回类型从未已添加。
8.0.0 支持混合的已添加。
8.0.0 支持仅返回类型静止的已添加。
8.0.0 已添加对联合类型的支持。
7.4.0 添加了对类属性类型的支持。
7.2.0 支持对象已添加。
7.1.0 支持可迭代的已添加。
7.1.0 支持无效已添加。
7.1.0 添加了对可为null的类型的支持。

原子类型使用说明

原子类型具有直接的行为,但有一些小的警告在本节中进行了描述。

标量类型

警告

标量类型的名称别名(布尔,整数,浮动,一串)不支持。相反,它们被视为类或接口名。例如,使用布尔值作为类型声明将要求该值为运算符类或接口布尔值,而不是类型布尔:

<?php(电话)
功能测试(布尔$param) {}
测试(真的);
?>

PHP 8中上述示例的输出:

警告:“boolean”将被解释为类名。你是说“bool”吗?在第2行的/in/9YrUX中写入“\boolean”以禁止显示此警告致命错误:Uncaught TypeError:test():参数#1($param)的类型必须为boolean,给定bool,在第3行的-中调用,在-:2中定义堆栈跟踪:#0-(3):测试(真)#1{主}在第2行抛出

无效

注释:

通过引用从返回无效从PHP 8.1.0开始,函数已弃用,因为这样的功能是矛盾的。以前,它已经发出了以下信号E_通知呼叫时:只能通过引用返回变量引用.

<?php(电话)
功能&测试():无效{}
?>

可调用类型

此类型不能用作类属性类型声明。

注释:无法指定函数的签名。

pass-by-reference参数的类型声明

如果pass-by-reference参数具有类型声明,则变量是只有在函数项上选中,位于调用的开始,但不是函数返回时。这意味着函数可以更改变量引用的类型。

示例#1键入的pass-by-reference参数

<?php(电话)
功能数组_baz(数组&$参数)
{
$参数=1;
}
$变量=[];
数组_baz($变量);
变量转储($变量);
数组_baz($变量);
?>

上述示例将输出类似于:

整数(1)致命错误:Uncaught TypeError:array_baz():参数#1($param)必须是数组类型,给定int,在第9行的-中调用,在-:2中定义堆栈跟踪:#0-(9):array_baz(1)#1{主}投入-第2行

复合类型使用说明

复合类型声明受到一些限制将在编译时执行冗余检查,以防止出现简单的错误。

注意安全

在PHP8.2.0之前地下城与勇士类型,无法将交集类型与并集类型组合在一起。

工会类型

警告

无法组合这两种值类型真的以联合类型组合在一起。使用布尔而不是。

注意安全

在PHP8.2.0之前无效无法用作独立类型,联合类型仅由这些类型是不允许的。这包括以下类型:,false |空,?.

可空类型句法糖

单个基类型声明可以通过在用问号键入(?).因此?T|空都是相同的。

注释:PHP 7.1.0支持此语法,并且早于通用联合类型支持。

注释:

也可以通过生成无效默认值。不建议这样做,因为在子级中更改了默认值类a类型兼容性冲突将作为无效类型需要添加到类型声明中。

示例#2使参数可为null的旧方法

<?php(电话)
C类{}

功能
(f)(加元=无效) {
变量转储(加元);
}

(f)(新C类);
(f)(无效);
?>

上述示例将输出:

对象(C)#1(0){}无效的

重复和冗余类型

为了捕获复合类型声明中的简单错误在不执行类加载的情况下检测到将导致编译时错误。这包括:

  • 每个名称解析类型只能出现一次。类型,如int|string|int可计数、可遍历和可计数导致错误。
  • 使用混合的导致错误。
  • 对于联合类型:
  • 对于交叉点类型:
    • 使用不是类类型的类型会导致错误。
    • 使用其中之一自己,起源,或静止的导致错误。
  • 对于地下城与勇士类型:
    • 如果使用更通用的类型,则限制性更强的类型是多余的。
    • 使用两种相同的交点类型。

注释:这并不能保证类型是“最小的”,因为这样做会需要加载所有使用的类类型。

例如,如果A类B类是班级别名,然后A | B仍然是合法的结合类型,甚至虽然可以简化为A类B类.类似地,如果类B扩展了A{},然后A | B也是一种合法的结合类型,尽管可以简化为A类.

<?php(电话)
功能foo公司():整数|国际的{}//不允许的
功能foo公司():布尔|{}//不允许的
功能foo公司():整数&可穿越的{}//不允许的
功能foo公司():自己&可穿越的{}//不允许的

使用A类作为B类;
功能
foo公司():A类|B类{}//不允许(“使用”是名称解析的一部分)
功能foo公司():A类&B类{}//不允许(“使用”是名称解析的一部分)

类别名(_A)(“X”,“是”);
功能
foo公司():X(X)|Y(Y){}//允许(冗余仅在运行时已知)
功能foo公司():X(X)&Y(Y){}//允许(冗余仅在运行时已知)
?>

示例

示例#3基本类类型声明

<?php(电话)
C类{}
D类延伸C类{}

//这并不能扩展C。
E类{}

功能
(f)(加元) {
回声
获取类(_C)(加元).“\n”;
}

(f)(新C类);
(f)(新D类);
(f)(新E类);
?>

PHP 8中上述示例的输出:

C类D类致命错误:未捕获的类型错误:f():参数#1($c)必须是c类型,给定E,在第14行的/in/gLonb中调用,并在/in/gLonb:8中定义堆栈跟踪:#0-(14):f(对象(E))#1{主}投入-第8行

示例#4基本接口类型声明

<?php(电话)
接口{公共职能(f)(); }
C类实施{公共职能(f)(){}}

//这并没有实现I。
E类{}

功能
(f)(I美元) {
回声
获取类(_C)($i美元).“\n”;
}

(f)(新C类);
(f)(新E类);
?>

PHP 8中上述示例的输出:

C类致命错误:Uncaught TypeError:f():参数#1($i)必须是给定的i、E类型,在第13行的-中调用,在-:8中定义堆栈跟踪:#0-(13):f(对象(E))#1{主}投入-第8行

示例#5基本返回类型声明

<?php(电话)
功能总和(美元,十亿美元):浮动{
返回
美元+十亿美元;
}

//请注意,将返回一个浮点。
变量转储(总和(1,2));
?>

上述示例将输出:

浮子(3)

示例#6返回对象

<?php(电话)
C类{}

功能
获取C():C类{
返回新的
C类;
}

变量转储(获取C());
?>

上述示例将输出:

对象(C)#1(0){}

示例#7可为Null的参数类型声明

<?php(电话)
C类{}

功能
(f)(?加元) {
变量转储(加元);
}

(f)(新C类);
(f)(无效);
?>

上述示例将输出:

对象(C)#1(0){}无效的

示例#8可为null的返回类型声明

<?php(电话)
功能获取项目(_I)(): ?一串{
如果(isset(
$_GET(获取)['项目'])) {
返回
$_GET(获取)['项目'];
}其他{
返回
无效;
}
}
?>

示例#9类属性类型声明

<?php(电话)
用户{
公共静电
字符串$foo=“foo”;

公众的
整数$id;
公众的
字符串$用户名;

公共职能
__构造(整数$id,字符串$用户名) {
$这个->身份证件=$id(美元);
$这个->用户名=$用户名;
}
}
?>

严格键入

默认情况下,PHP会将错误类型的值强制转换为预期的标量类型声明(如果可能)。例如,给定的函数一个整数对于需要一串将获取类型为的变量一串.

可以在每个文件的基础上启用严格模式。严格来说模式下,只有与类型声明完全对应的值才是接受,否则为类型错误将被抛出。此规则的唯一例外是整数价值将传递a浮动类型声明。

警告

内部函数中的函数调用不会受到这个严格类型宣言。

要启用严格模式声明语句与严格类型宣言:

注释:

严格类型应用于从在内部启用了严格类型的文件,而不是在该文件中声明的函数。如果文件不严格键入enabled将调用文件中定义的函数使用严格的键入,呼叫者的偏好(强制键入)将是尊重,价值就会被胁迫。

注释:

严格类型仅为标量类型声明定义。

示例#10参数值的严格键入

<?php(电话)
声明(严格类型=1);

功能
总和(整型$a,整数$b) {
返回
美元+十亿美元;
}

变量转储(总和(1,2));
变量转储(总和(1.5,2.5));
?>

PHP 8中上述示例的输出:

整数(3)致命错误:未捕获的类型错误:sum():参数#1($a)的类型必须为int,给定浮点值,在第9行的-中调用,并在-:4中定义堆栈跟踪:#0-(9):总和(1.5,2.5)#1{主}投入-第4行

示例#11强制键入参数值

<?php(电话)
功能总和(整型$a,整数$b) {
返回
美元+十亿美元;
}

变量转储(总和(1,2));

//这些将被强制为整数:注意下面的输出!
变量转储(总和(1.5,2.5));
?>

上述示例将输出:

整数(3)整数(3)

示例#12严格键入返回值

<?php(电话)
声明(严格类型=1);

功能
总和(美元,十亿美元):整数{
返回
美元+十亿美元;
}

变量转储(总和(1,2));
变量转储(总和(1,2.5));
?>

上述示例将输出:

整数(3)致命错误:Uncaught TypeError:sum():返回值必须是int类型,在-:5中返回的浮点值堆栈跟踪:#0-(9):总和(1,2.5)#1{主}投入-第5行
添加备注

用户贡献的笔记2条注释

托尼凯特(塔塔)
2年前
在等待对类型化数组的本机支持的同时,这里有两种替代方法,可以通过滥用变量函数来确保数组的强类型化。这些方法的性能对于作者来说是一个谜,因此对它们进行基准测试的责任落在了读者身上。

PHP 5.6添加了splat操作符(…),用于将数组解压缩为函数参数。PHP 7.0添加了标量类型提示。PHP的最新版本进一步改进了类型系统。通过这些添加和改进,可以对类型化数组提供适当的支持。

<?php(电话)
声明(严格类型=1);

功能
typeArrayNullInt类型(?整数...$参数):无效{
}

功能
做点什么(数组$整数):无效{
(功能(?
整数...$参数) {})(...$整数);
//或者,
(fn(?)?整数...$参数) =>$参数)(...$整数);
//或者避免内存中出现过多的闭包
类型数组NullInt(...$整数);

/* ... */
}

功能
做些其他事情(?整数...$整数):无效{
/* ... */
}

$整数= [1,2,,4,无效];
做点什么($整数);
做些其他事情(...$整数);
?>

这两种方法都适用于所有类型声明。这里的关键思想是让函数在遇到输入冲突时抛出一个运行时错误。doSomethingElse中使用的类型方法比这两种方法更干净,但它不允许在变参数之后有任何其他参数。它还要求调用站点了解此类型实现并解压缩数组。doSomething中使用的方法比较混乱,但它不要求调用站点知道键入方法,因为解包是在函数中执行的。由于doSomethingElse还可以接受n个单独的参数,而as-doSomething只接受一个数组,因此它的含义也不那么模糊。如果在PHP中添加了对本机类型数组的支持,doSomething的方法也更容易剥离。这两种方法都只适用于输入参数。需要在调用站点进行数组返回值类型检查。

如果未启用strict_types,则可能需要从类型检查函数返回强制标量值(例如,浮点和字符串变为整数),以确保正确的类型。
崩溃
2年前
文档中缺少这样的信息,即当接口的方法返回类型定义为“mixed”时,可以更改接口中定义的方法的返回类型。

来自RFC:

“混合返回类型可以在子类中缩小,因为这是协变的,并且在LSP中是允许的。”(https://wiki.php.net/rfc/mixed-type_v2(网址:https://wiki.php.net/rfc/mixed-type_v2))

这意味着以下代码在PHP 8.0中有效:

<?php(电话)

接口IT测试
{
公共职能
阿普费尔():混合的;//自8.0起有效
}

测试实施IT测试
{
公共职能
阿普费尔():数组//更加明确
{
返回[];
}
}

变量转储((新测试())->阿普费尔());
?>

您可以在此处查看结果:https://3v4l.org/PXDB6
到顶部