【问题标题】:Is it possible to specify multiple return types on PHP 7?是否可以在 PHP 7 上指定多种返回类型?
【发布时间】:2016-08-30 05:03:52
【问题描述】:

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

是否可以声明返回类型返回一个或另一个并在其他任何事情上失败?

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

    return SucceedObject;
}

【问题讨论】:

  • 仅当FailObjectSucceedObject 都共享一个接口或从一个共同的父级扩展时

标签: php php-7 type-hinting


【解决方案1】:

从 PHP 8+ 开始,您可以使用 union types:

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

【讨论】:

  • 你也可以在这里使用abstract class
  • 这确实是限制性的:想想实体(例如,被 Doctrine 使用):不可能暗示返回到 null|Entity。真的限制...
  • @Aerendir 解决方案/解决方法是让存储库方法返回一个包含空类型或实体的选项。下面是一个提供 Option 类型的包示例:github.com/schmittjoh/php-option
  • @tangobango 如答案所述:不,RFC 被拒绝了。
  • @Aerendir null|Entity 在 PHP 7.1 中可用,具有可空类型:?Entity。见secure.php.net/manual/en/migration71.new-features.php
【解决方案2】:

正如 bishop 所指出的,有一个用于添加多种返回类型的 RFC。但是,我想我会补充一点,从 PHP7.1 开始,您现在可以像这样指定一个可为空的返回类型:

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

所以这个函数将接受一个字符串,并通过在 int 之前添加问号,您允许它返回 null 或整数。

这是文档的链接: http://php.net/manual/en/functions.returning-values.php

下面是该页面的引述,解释了用法: PHP 7.1 允许在类型声明前加上 ? —(例如函数 canReturnNullorString(): ?string)

另外,还有一个与此相关的线程:Nullable return types in PHP7

【讨论】:

  • "允许 void 和 null 返回类型"返回 null 对我有用,但 void 会引发异常。 “...或 null,没有返回...”
  • 如果我使用“return;”错误变为:“具有返回类型的函数必须返回一个值”
【解决方案3】:

PHP 从 7.2 开始支持对象返回类型

http://php.net/manual/en/migration72.new-features.php

function test(object $obj) : object
// return any type of object ...

【讨论】:

    【解决方案4】:

    自 PHP 8.0 this is possible.

    您现在可以使用联合类型来指定:

    function test(): Success|Failure
    {
        if ($this->condition === false) {
            return new Failure();
        }
    
        return new Success();
    }
    

    这是可能的事实并不意味着它总是可取的。在许多(可能是大多数)情况下,使用不同答案中建议的接口(例如ResultFailureFailure 都将实现)仍然是更可取的。

    但在其他情况下,联合类型可以作为弱类型的替代方案。例如。一个同时接受stringint 的方法,或者描述像stripos() 这样的函数的返回类型,它返回int|false

    【讨论】:

      【解决方案5】:

      这是不正确的方法:

      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
      }
      

      【讨论】:

      • 异常应该用于控制应用程序的流程。您本质上将此处的异常视为 if 语句,这不是异常的正确用法。
      • 你是说失败案例也不例外?嗯...最好返回一个“FailObject”并控制多个输出:if、elseif、elseif、else。 (否)
      • 我建议代码结构正确,这样您就不需要依赖任何一个:)
      • 必须设置严格类型来编写干净、可维护和健壮的代码。描述的情况(带有“FailObject”)是手动异常。控制流量也不例外。流程继续成功案例。您不必在应用控制器之前控制异常。
      猜你喜欢
      • 1970-01-01
      • 2016-08-12
      • 1970-01-01
      • 2021-08-30
      • 2019-05-06
      • 1970-01-01
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多