PHP 7是否可以指定多个返回类型?

87

我有一些方法可以返回两种类型中的一种。我正在使用一个利用MVC框架,因此特别是重构这几个函数并不具吸引力。

是否有可能声明返回其中一种类型并在其他任何情况下失败?

function test(): ?
{
    if ($this->condition === false) {
        return FailObject;
    }

    return SucceedObject;
}

4
仅当FailObjectSucceedObject共享一个接口或继承自公共父类时,条件才成立。 - Mark Baker
5个回答

159

从PHP 8+开始,您可以使用联合类型

function test(): FailObject|SuccessObject {}

另一种自PHP 4版本以来所有版本都可用的方法是,两个对象共享一个接口。示例:

interface ReturnInterface {}
class FailObject implements ReturnInterface {}
class SuccessObject implements ReturnInterface {}
function test(): ReturnInterface {}

在这个例子中,ReturnInterface 是空的。它的存在仅支持所需的返回类型声明。
你也可以使用一个基础的、可能是抽象的类。
对于这种用例,接口比联合类型更清晰、更可扩展。例如,如果我以后想要一个WarnObject,我只需要将其定义为扩展ReturnInterface,而不是去更新所有签名并将其更新为FailObject|SuccessObject|WarnObject

2
在这里,您也可以使用一个“抽象类”。 - Andrea
12
这真的很限制:想想实体(例如Doctrine使用的实体):无法提示返回 null|Entity。非常受限制... - Aerendir
2
@Aerendir,解决/解决方法是使存储库方法返回一个包装了null类型或实体的Option。这是一个提供Option类型的包的示例:https://github.com/schmittjoh/php-option - John Hall
4
正如答案中所述: 不,RFC被拒绝了。 - bishop
20
在 PHP 7.1 中,使用可为空类型 ?Entity 可以获得 null|Entity。请参阅 https://secure.php.net/manual/en/migration71.new-features.php。 - 0b10011
显示剩余4条评论

83

正如主教所指出的那样,有一个添加多个返回类型的RFC。然而,我想补充一下,在PHP7.1中您现在可以像这样指定可为空的返回类型:

function exampleFunction(string $input) : ?int
{
    // Do something
}

这个函数需要一个字符串作为输入,并在int前面添加问号,从而允许它返回null或整数。

下面是官方文档链接: http://php.net/manual/en/functions.returning-values.php

这里引用了该页面的一段话来解释用法: PHP 7.1可以通过在类型声明前加上?来允许void和null返回类型——(例如:function canReturnNullorString(): ?string)

此外,这里还有一个与此相关的主题:Nullable return types in PHP7


"允许使用void和null返回类型",返回null对我有效,但void会抛出异常。"...或null,没有返回值..." - Matt Kenefick
如果我使用"return;",错误会变成:"带有返回类型的函数必须返回一个值"。 - Matt Kenefick

26

12
自 PHP 8.0 起,这是可能的。现在您可以使用联合类型来指定此内容:
function test(): Success|Failure
{
    if ($this->condition === false) {
        return new Failure();
    }

    return new Success();
}

这种做法的可行性并不意味着总是明智的选择。在许多情况下(可能是大多数情况),像其他答案建议的使用接口(例如Result,它们都会实现FailureFailure)仍然更可取。

但是,在其他一些情况下,联合类型作为弱类型的替代品可能是有意义的。例如,一个既接受string又接受int的方法,或者描述类似于stripos()这样返回int|false的函数的返回类型。


-9

这不是正确的方式:

function test(): ?
{
    if ($this->condition === false) {
        return FailObject;
    }

    return SucceedObject;
}

多返回类型是一种不好的实践。 好的实践:

你应该定义一个异常:

class FailObjectException extends \Exception
{
    private $exampleExtraInfo;

    public function __construct($exampleExtraInfo, $message)
    {
        parent::__construct($message);
        $this->exampleExtraInfo = $exampleExtraInfo;
    }

    public function exampleExtraInfo(): int
    {
        return $this->exampleExtraInfo;
    }
}

现在,你可以像这样定义函数:

function test(): SucceedObject
{
    if ($this->condition === false) {
        throw new FailObjectException(...,...);
    }

    return SucceedObject;
}

并使用带有try/catch的此函数:

try{
    $succeedObject = $any->test();
} catch (FailObjectException $exception){
    //do something
}

7
异常不应该用于控制应用程序的流程。在这里,您实际上是将异常当作if语句来处理,这并不是异常的正确用法。 - Jack hardcastle
你是说失败情况不是异常吗?嗯...最好返回一个“FailObject”并控制多个输出:if,elseif,elseif,else。(NO) - calmohallag
我建议代码结构正确,这样你就不需要依赖任何一种方式 :) - Jack hardcastle
设置严格类型以编写干净、可维护和强大的代码是必要的。描述案例(使用“FailObject”)是手动异常。它不是控制流程的异常。流程将继续执行成功案例。您不必在应用程序控制器之前控制异常。 - calmohallag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接