php类型提示我可以允许两种类型吗?

时间:2017-10-16 17:04:13

标签: php type-hinting

我可以使用类型提示允许两种不同的类型吗?例如。参数1可以是2个类

// Zoom by 98%
panZoomTiger.zoomBy(0.98)

6 个答案:

答案 0 :(得分:18)

在学术上,这被称为type union

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日,Union Types PHP RFC被提议用于PHP 7.1。经过讨论和投票后,它被拒绝,18“不”反对11“是”。

如果RFC已被接受,那么PHP的联合类型将完全按照您显示的方式(User|File)。

RFC存在一些缺陷,但其被拒绝的主要原因是主要者选民对变化具有相当的抵抗力,特别是当它涉及类型严格性和其他编程范例时(例如“当默认值采用所有类型的值”时,为什么我们需要类型联合“”这对性能不利

答案 1 :(得分:8)

目前在PHP中无法实现。但是,您可以拥有interface,并为UserFile实施,然后将该界面用作log()中的类型提示:

<?php
interface UserFile {
}

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

// snip

public function log (UserFile $requester) {
}

答案 2 :(得分:6)

自2020年11月发布的PHP 8.0起,这将成为可能,其中包含Union Type。

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或资源,并将返回intfloat

其他几点:

可空性

此外,您可以使用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 { /** **/ }
}

答案 3 :(得分:3)

您可以检查内部功能类型。

function log ($requester) {
    if ($requester instanceof User || $requester instanceof File) {
        //Do your job
    }
}

答案 4 :(得分:2)

或者每种方法可以有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();
}

答案 5 :(得分:1)

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

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

并在功能

中使用它
function log (Parent_class $requester) {
    // ... code
}