PHP 7返回类型提示


PHP 7 return type hinting

根据PHP 7的返回类型提示,已经有很多问题,以及如果类被定义为返回类型,为什么不可能返回null。像这样:处理PHP7返回类型的正确方法。答案通常说这不是问题,因为如果一个函数应该返回一个类,那么返回null可能无论如何都是一个例外。也许我错过了什么,但我真的不明白为什么。例如,让我们看到一个简单的用户类:

class User
{
    private $username;  // mandatory
    private $password;  // mandatory
    private $realName;  // optional
    private $address;   // optional
    public function getUsername() : string {
        return $this->username;
    }
    public function setUsername(string $username) {
        $this->username = $username;
    }
    public function getPassword() : string {
        return $this->password;
    }
    public function setPassword(string $password) {
        $this->password = $password;
    }
    public function getRealName() : string {
        return $this->realName;
    }
    public function setRealName(string $realName = null) {
        $this->realName = $realName;
    }
    public function getAddress() : Address {
        return $this->address;
    }
    public function setAddress(Address $address = null) {
        $this->address = $address;
    }
}

在我们的应用程序中,用户没有realName和/或地址是完全合法的。我甚至可以将realName和address字段设置为null,即使上面的解决方案不是最优的。在我们的配置文件页面中,如果地址为null(空),我想显示一个提示。

但这只是一个例子。我们的应用程序有100多个数据库表和相应的PHP类,几乎所有这些表都有可选字段。总是写一个try-catch块,而不是简单地检查null来处理可选字段,这对我来说似乎有点不太理想

那么,对于上面的例子,什么是好的解决方案呢?

问题是您的对象没有遵循OOP规则。

首先,getter和setter是邪恶的,因为它们暴露了对象的内部结构。您只需要引入无限的可能性,就可以在系统的其余部分依赖于内部用户对象结构。而对象的目的是隐藏实现细节,并提供操作这些隐藏数据的接口。

null是邪恶的(或价值10亿美元的错误),因为它使代码的可靠性大大降低。它引入了特殊情况(null返回值),并且有必要在使用对象的代码中处理这种特殊情况。更糟糕的是,如果你忘记处理这个"null"特殊情况(或者如果你不知道有特殊情况),你不会立即得到错误。因此,你会得到隐藏的错误,这可能是非常耗时的发现和修复。

实际上,我看到了以下选项(在所有情况下-删除getter):

选项1)以某种统一形式获取配置文件数据的快照

$user->getProfileData() {
    return [
        'username': $this->username,
        'realName': $this->realName ? $this->realName : '',
        'address': $this->address ? $this->address : 'Not specified'
        ...
    ];
}

这仍然是一种getter,但您可以将所有数据转换为统一的形式(字符串)。即使您的数据库中有一些整数的浮点字段(如年龄或身高),您仍然会在这里返回字符串,因此系统的其余部分不依赖于对象的实际内部。

可选字段的空字符串(或特殊值,如"未指定")也充当一种"null对象"。还可以将返回的值封装到小字段对象中,并对可选字段实际使用null对象模式。

在使用该类的代码中,您不应该处理特殊的"null"情况,只需在字段上循环并显示字符串(包括那些为空的字符串)。

选项2)使对象本身负责呈现

$user->showProfile($profileView) {
    $profileView->addLabel('First Name');
    $profileView->addString($this->username);
    ...
}

在这里,您将内部细节保留在对象内部,但现在它做得太多了,所以有人可能会说它现在违反了SRP。

选项3)创建负责演示的特殊对象

$userPresentation = $user->createPresentation()
// internally it will return new UserPresentation($this->username, this->realName, $this->address, ...);
// now display it - generate the template and insert it into the view
<? echo $userPresentation->getHtml(); ?>

在这里,您可以将表示逻辑移动到单独的对象中。用户对象和它的表示是紧密耦合的,但系统的其他部分现在对用户对象内部一无所知(也没有机会知道)。

我最近接受了null意味着有问题的想法,所以我知道当我看到它时,就意味着出了问题。

在这些情况下,我倾向于使用空对象设计模式
因此,为了处理地址,您需要创建以下内容:

class NullAddress implements AddressInterface { }

Address类还需要实现AddressInterface
然后,getAddress()看起来像:

public function getAddress(): AddressInterface {
    return $this->address ?? new NullAddress();
}

调用getAddress()的函数可以通过以下方式检查空值:

if ($user->getAddress() instanceof NullAddress) {
    // Implement result of empty address here.
}

对于处理字符串,到目前为止,一个返回的空字符串对我很有用。我只是用empty()来检查它。