是否可以键入不止一种类型的提示?
Is it possible to type hint more than one type?
我可以使用类型提示允许两种不同的类型吗?
例如参数 $requester
可以是 User
或 File
:
function log (User|File $requester) {
}
您可以在函数内部检查类型。
function log ($requester) {
if ($requester instanceof User || $requester instanceof File) {
//Do your job
}
}
您可以为这两个人创建 parent class:
abstract class Parent_class {}
class User extends Parent_class {
// ...
}
class File extends Parent_class {
// ...
}
并在函数中使用它
function log (Parent_class $requester) {
// ... code
}
目前在 PHP 中是不可能的。然而,你可以有一个 interface
,并为 User
和 File
实现它,然后使用该接口作为 log()
:
中的类型提示
<?php
interface UserFile {
}
class User implements UserFile {
}
class File implements UserFile {
}
// snip
public function log (UserFile $requester) {
}
在学术上,这被称为 type union。
PHP
中的联合类型
您可以通过创建接口、父类型等来作弊,如其他答案中所述,但除了为您的项目增加复杂性和 LoC 之外,还有什么意义呢?另外,这不适用于标量类型,因为您不能 extend/implement 标量类型。
您不会使代码更具可读性,反而会适得其反。除非那些 classes/interfaces 已经存在并且因为 OOP 而存在,而不是为了解决类型提示问题。
解决方法
PHP 中的规范答案是...好吧,只是不要提供类型提示。该语言不被认为具有复杂而强大的类型系统,试图解决该语言的缺陷并不是一个好的答案。
相反,正确记录您的函数:
/**
* Description of what the function does.
*
* @param User|File $multiTypeArgument Description of the argument.
*
* @return string[] Description of the function's return value.
*/
function myFunction($multiTypeArgument)
{
这至少会带来 IDE 对自动完成和静态代码分析的支持。在私人项目、网站等工作时就足够了
在设计 public API(PHP 库等)时,有时您可能希望对 API 消费者的输入更具防御性。
那么@tilz0R 的答案就是要走的路:
function log($message) {
if (!is_string($message) && !$message instanceof Message) {
throw new \InvalidArgumentException('$message must be a string or a Message object.');
}
// code ...
}
那天PHP(几乎)有联合类型
2015 年 2 月 14 日,针对 PHP 7.1 提出了 Union Types PHP RFC。经讨论投票否决,18"no"反对11"yes".
如果 RFC 被接受,PHP 的联合类型将与您所展示的完全相同 (User|File
)。
RFC 有一些缺陷,但它被拒绝的主要原因是 维护者 投票者非常抵制更改,尤其是在类型严格性和其他编程范例方面(例如 "why would we need type unions when the default takes all types of values" 和 "that's not good for performance")。
或者您可以为每个方法和一个动态方法设置 2 个方法:
function logUser($requester)
{
//
}
function logFile($requester)
{
//
}
和动态的
function log($requester)
{
if ($requester instanceof File) {
return $this->logFile($requester);
}
if ($requester instanceof User) {
return $this->logUser($requester);
}
throw new LogMethodException();
}
从 PHP 8.0 开始,这将成为可能 包含联合类型。
proposal has been voted 61 in favour to 5 against,执行准备就绪。
有一个 previous RFC 提出这个建议,在另一个答案中提到,但那个最终被拒绝了。
它将完全按照您问题中的示例工作:
class F
{
public function foo (File|Resource $f) : int|float { /** implement this**// }
}
这意味着 F::foo()
需要 File
或资源,并且 return 需要 int
或 float
。
另外几点:
可为空性
此外,您可以声明与 null
的联合。 A|null
等同于 ?A
,但更复杂的声明如 A|B|null
也是可能的。
“假”伪类型
也可以使用 false
类型作为联合类型声明的一部分。例如。 int|false
。这主要是由于历史原因,因为一些内部函数 return false
在某些类型的错误条件下。以 strpos() 为例。
更现代的功能可能应该 return null
或在这些情况下抛出异常,但包含此替代方案是为了解决遗留代码。
在继承时添加和删除联合类型的一部分
为参数类型提示添加联合类型是合法的(因此,使函数的限制更少),删除联合return 类型提示的类型(使 return 类型更具体)。
鉴于上面的 class F
,这是合法的:
class G extends F
{
public function foo(File|Resource|string $f) : int { /** **/ }
}
但这不是:
class H extends F
{
public function foo(File $f) : int|float|bool { /** **/ }
}
我可以使用类型提示允许两种不同的类型吗?
例如参数 $requester
可以是 User
或 File
:
function log (User|File $requester) {
}
您可以在函数内部检查类型。
function log ($requester) {
if ($requester instanceof User || $requester instanceof File) {
//Do your job
}
}
您可以为这两个人创建 parent class:
abstract class Parent_class {}
class User extends Parent_class {
// ...
}
class File extends Parent_class {
// ...
}
并在函数中使用它
function log (Parent_class $requester) {
// ... code
}
目前在 PHP 中是不可能的。然而,你可以有一个 interface
,并为 User
和 File
实现它,然后使用该接口作为 log()
:
<?php
interface UserFile {
}
class User implements UserFile {
}
class File implements UserFile {
}
// snip
public function log (UserFile $requester) {
}
在学术上,这被称为 type union。
PHP
中的联合类型您可以通过创建接口、父类型等来作弊,如其他答案中所述,但除了为您的项目增加复杂性和 LoC 之外,还有什么意义呢?另外,这不适用于标量类型,因为您不能 extend/implement 标量类型。
您不会使代码更具可读性,反而会适得其反。除非那些 classes/interfaces 已经存在并且因为 OOP 而存在,而不是为了解决类型提示问题。
解决方法
PHP 中的规范答案是...好吧,只是不要提供类型提示。该语言不被认为具有复杂而强大的类型系统,试图解决该语言的缺陷并不是一个好的答案。
相反,正确记录您的函数:
/**
* Description of what the function does.
*
* @param User|File $multiTypeArgument Description of the argument.
*
* @return string[] Description of the function's return value.
*/
function myFunction($multiTypeArgument)
{
这至少会带来 IDE 对自动完成和静态代码分析的支持。在私人项目、网站等工作时就足够了
在设计 public API(PHP 库等)时,有时您可能希望对 API 消费者的输入更具防御性。
那么@tilz0R 的答案就是要走的路:
function log($message) {
if (!is_string($message) && !$message instanceof Message) {
throw new \InvalidArgumentException('$message must be a string or a Message object.');
}
// code ...
}
那天PHP(几乎)有联合类型
2015 年 2 月 14 日,针对 PHP 7.1 提出了 Union Types PHP RFC。经讨论投票否决,18"no"反对11"yes".
如果 RFC 被接受,PHP 的联合类型将与您所展示的完全相同 (User|File
)。
RFC 有一些缺陷,但它被拒绝的主要原因是 维护者 投票者非常抵制更改,尤其是在类型严格性和其他编程范例方面(例如 "why would we need type unions when the default takes all types of values" 和 "that's not good for performance")。
或者您可以为每个方法和一个动态方法设置 2 个方法:
function logUser($requester)
{
//
}
function logFile($requester)
{
//
}
和动态的
function log($requester)
{
if ($requester instanceof File) {
return $this->logFile($requester);
}
if ($requester instanceof User) {
return $this->logUser($requester);
}
throw new LogMethodException();
}
从 PHP 8.0 开始,这将成为可能 包含联合类型。
proposal has been voted 61 in favour to 5 against,执行准备就绪。
有一个 previous RFC 提出这个建议,在另一个答案中提到,但那个最终被拒绝了。
它将完全按照您问题中的示例工作:
class F
{
public function foo (File|Resource $f) : int|float { /** implement this**// }
}
这意味着 F::foo()
需要 File
或资源,并且 return 需要 int
或 float
。
另外几点:
可为空性
此外,您可以声明与 null
的联合。 A|null
等同于 ?A
,但更复杂的声明如 A|B|null
也是可能的。
“假”伪类型
也可以使用 false
类型作为联合类型声明的一部分。例如。 int|false
。这主要是由于历史原因,因为一些内部函数 return false
在某些类型的错误条件下。以 strpos() 为例。
更现代的功能可能应该 return null
或在这些情况下抛出异常,但包含此替代方案是为了解决遗留代码。
在继承时添加和删除联合类型的一部分
为参数类型提示添加联合类型是合法的(因此,使函数的限制更少),删除联合return 类型提示的类型(使 return 类型更具体)。
鉴于上面的 class F
,这是合法的:
class G extends F
{
public function foo(File|Resource|string $f) : int { /** **/ }
}
但这不是:
class H extends F
{
public function foo(File $f) : int|float|bool { /** **/ }
}