65

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

例如参数$requester可以是UserFile

function log (User|File $requester) {

}
4

6 回答 6

66

从 PHP 8.0 开始,这将可以通过包含联合类型来实现。

提案以 61 票赞成、5 票反对票通过,实施已准备就绪。

之前有一个RFC提出了这个建议,在另一个答案中提到,但最终被拒绝了。

它将与您问题中的示例完全相同:

class F
{
   public function foo (File|Resource $f) : int|float { /** implement this**// }
}

这意味着F::foo()需要 aFile或资源,并将返回 aint或 a float

还有几点:

可空性

此外,您可以使用null. A|null等价于?A,但更复杂的声明A|B|null也是可能的。

“假”伪类型

也可以将false类型用作联合类型声明的一部分。例如int|false。这主要是由于历史原因,因为某些内部函数false会在某些类型的错误条件下返回。以strpos ()为例。

在这些情况下,更现代的函数可能应该返回null或抛出异常,但包含此替代方案是为了解决遗留代码。

在继承上添加和删除联合类型的一部分

为参数类型提示添加联合类型是合法的(因此,使函数的限制更少),并为返回类型提示删除联合类型(使返回类型更具体)。

鉴于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 { /** **/ }
}
于 2019-11-12T20:23:05.497 回答
57

在学术上,这被称为类型联合

PHP中的联合类型

正如其他答案中提到的,您可以通过创建接口、父类型等来作弊,但是除了为您的项目增加复杂性和 LoC 之外,还有什么意义呢?另外,这不适用于标量类型,因为您无法扩展/实现标量类型。

你会得到相反的结果,而不是使代码更具可读性。除非这些类/接口已经存在并且它们是因为 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 支持。在处理私人项目、网站等时已经足够了。

在设计公共 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 提出了联合类型PHP RFC。经过讨论和投票,它被拒绝了,18个“不”对11个“是”。

如果 RFC 已被接受,PHP 将拥有与您显示的完全相同的联合类型 ( User|File)。

RFC 有一些缺陷,但它被拒绝的主要原因是维护者投票者非常抗拒改变,尤其是在涉及类型严格性和其他编程范式时(例如,“为什么我们需要类型联合,当默认值占尽一切时)类型的值”“这对性能不利”)。

于 2017-10-16T19:09:12.987 回答
19

目前在 PHP 中是不可能的。但是,您可以拥有一个, 并为andinterface实现它,然后使用该接口作为类型提示:UserFilelog()

<?php
interface UserFile {
}

class User implements UserFile {
}
class File implements UserFile {
}

// snip

public function log (UserFile $requester) {
}
于 2017-10-16T17:09:12.380 回答
6

您可以检查函数内部的类型。

function log ($requester) {
    if ($requester instanceof User || $requester instanceof File) {
        //Do your job
    }
}
于 2017-10-16T17:06:22.937 回答
4

或者您可以为每种方法使用 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();
}
于 2019-01-30T00:28:02.530 回答
1

您可以为这两个创建父类:

abstract class Parent_class {}
class User extends Parent_class {
    // ...
}
class File extends Parent_class {
    // ...
}

并在函数中使用它

function log (Parent_class $requester) {
    // ... code
}
于 2017-10-16T17:08:59.747 回答