是否可以键入不止一种类型的提示?

Is it possible to type hint more than one type?

我可以使用类型提示允许两种不同的类型吗?

例如参数 $requester 可以是 UserFile:

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,并为 UserFile 实现它,然后使用该接口作为 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 需要 intfloat

另外几点:

可为空性

此外,您可以声明与 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 { /** **/ }
}