【问题标题】:php function implementation overrides non-nullable argumentphp 函数实现覆盖不可为空的参数
【发布时间】:2019-04-21 22:02:20
【问题描述】:

我今天使用 PHP(至少 7.1 和 7.2)遇到了这个问题,代码如下:

namespace PlaceHolderX\Tests\PHPUnit\Unit;

use PHPUnit\Framework\TestCase;

final class BreakingClassesTest extends TestCase
{

    public function testBreak(): void
    {
        $tester = new SomeClassA();
        $tester->test();
        $this->assertNull($tester->get());
    }

}

interface InterfaceA {

    public function test(string $testString): void;

}

class SomeClassA implements InterfaceA
{
    /** @var null|string */
    private $testString;

    public function test(string $testString = null): void
    {
        $this->testString = $testString;
    }

    public function get(): ?string
    {
        return $this->testString;
    }
}

所以我有一个接口 (InterfaceA),它有一个需要字符串的方法。此参数不可为空,因为如果我想将其指定为:

public function test(?string $testString): void;

但是在实现类 (SomeClassA) 中,我可以使用默认值 null 覆盖参数定义,这会导致我的接口不打算出现的行为。

所以我的主要问题是:为什么会这样?当然,我们需要在代码审查中检查这一点,但很容易错过。

我尝试搜索导致此行为的原因,但无法找到解释。也许我的搜索条件已关闭。

【问题讨论】:

    标签: php type-hinting


    【解决方案1】:

    在 PHP7.2 中实现了参数类型扩展。这是某种逆方差。遗憾的是,PHP 目前不支持参数的反方差,但草案中也有 rfc 来支持这一点。

    主要思想是:如果你有一个子类,你可以在子类中使用“更宽”的参数类型。对于返回类型,相反是有效的(协方差)。

    这是一个好还是坏的做法,取决于您的需求。据我所知,其他语言的行为方式相同。

    为了进一步阅读,有两个 rfc:

    【讨论】:

    • 我实际上不知道 7.2 启用了此功能,但我认为这与 OP 的要求无关。声明中的类型没有改变或扩大,只是分配了默认值。
    • 恕我直言,可空类型比不可空类型更广泛 - 没关系,是否有默认值
    • 但是这种行为并没有随着你引用的 rfc 而改变。自引入类型提示以来,始终允许使用默认值 null。 (您的回答没有解释 7.0 和 7.1 的这种行为)
    • @Philipp 感谢您的回答,但我认为我必须同意 Devon 的观点,即扩大不是这种行为的原因。主要是因为它也存在于 7.1 中。成为 PHP 向后兼容功能之一似乎更有可能。不过这真的让我很烦;)
    【解决方案2】:

    超类应该可以被它的子类替换。所以子类必须能够做超类所做的一切,但它还可以做得更多。

    在您的示例中,超类/接口不知道如何处理 null。但是子类可以,这很好,因为超类的用户只会传递非空值,因为他们认为超类合同有效。

    【讨论】:

      【解决方案3】:

      只要类型匹配,PHP 就允许您在实现中设置或修改默认值。需要注意的是,所有提示类型都允许 null 作为默认值。

      如果有人对此找到具体解释,那么我可以更新这个答案,但在 7.1 之前,声明可选参数的唯一方法是分配默认值 null。 ?string 语法不存在,因此此行为可能源于此,并且仍然存在以实现向后兼容性。

      如果您尝试设置一个默认值,比如一个整数,您将看到一条错误消息,显示:

      Fatal error: Default value for parameters with a string type can only be string or NULL
      

      截至目前,确保实现的默认值与可空或不可空的接口声明相匹配似乎是开发人员的责任。

      【讨论】:

      • 感谢您的回答。这似乎是 PHP 为保持向后兼容性完整所做的许多事情之一。我只是希望它有更好的记录。
      • @RickVH,对。我认为大多数语言都允许空值代替其他类型,所以这并不少见。随着 Philip 指出在 7.2 中实现的 rfc,看起来您的担忧会更加严重,因为它们实际上可以使接口的类型无效并允许传递字符串/null 以外的类型,所以接下来,开发人员甚至如果他们想忠实于界面,则需要承担更多责任。
      • 我同意。但这很高兴知道。我会将这篇文章设置为我的问题的答案。非常感谢@Phillip 指出情况会变得更糟。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-02
      • 2011-06-11
      • 1970-01-01
      • 1970-01-01
      • 2018-05-05
      • 2019-07-21
      相关资源
      最近更新 更多