首页 > 解决方案 > PHP 类型化返回混合内容

问题描述

作为一个好习惯,我已经开始将我的代码转换为类型化属性/类型化函数返回。但我有一个关于混合内容的问题。

我知道返回混合内容通常不是一个好习惯,但我有一种情况是不可避免的。我有一个用作数据容器的类(它具有多种功能,但我会跳到基础知识):

class Container {
  private array $list = [];

  public set(string $key, ?? $value): void {
    $this->list[$key] = $value;
  }

  public get(string $key): ?? {
    return $this->list[$key];
  }
}

我跳过了所有检查和所有其他功能。关键是它list可以容纳任何类型的东西(int,boolean,string,其他类对象......)所以我应该如何处理这个?

我知道 PHP 8 将有联合返回类型,所以至少我可以缩小它的范围 (int|float|string...) 但由于这是相当新的,我不知道我应该怎么做。

标签: phpstrong-typing

解决方案


保持简短。你不能用 PHP7 做到这一点。正如您已经说过的,联合类型声明将在当天尚未发布的 PHP8 中可用。

另一种方法是为您正在使用的每种类型编写集合。您可以使用接口来识别类型。在完美世界中,一切都是对象,如下例所示。

首先是收藏。

<?php
declare(strict_types=1);
namespace YourApp\Model;

use InvalidArgumentException;
use SplObjectStorage;

class TeamCollection extends SplObjectStorage
{
    public function attach(object $object, $data = null): void
    {
        if ($object instanceof Team) {
            throw new InvalidArgumentException(sprintf(
                'The given object is not a team. %s given.',
                get_class($object)
            ));
        }

        parent::attach($object, $data);
    }
}

这是 Team 类的集合。仅在将某些内容附加到此集合时才接受 Team 类。否则会抛出异常。由于继承,您不能向 Team 键入提示对象,因为这会引发另一个错误。SplObjectCollection 类将$object参数定义为对象类型。你不能覆盖它。因此,如果给定了 Team 类,我们可以检查 attach 方法。

<?php
declare(strict_types=1);
namespace YourApp\Model;

use YourApp\Model\TeamMemberCollection;
use YourApp\Model\TeamMember;

interface Team
{
    public function getName(): ?string;
    public function setName(string $name): self;
    public function addMember(TeamMember $member): self
    public function getMembers(): TeamMemberCollection;
}

接口定义了我们团队需要的方法。现在我们可以编写一个 Team 类。

<?php
declare(strict_types=1);
namespace YourApp\Model;

class SoccerTeam implements Team
{
    protected TeamMemberCollection $members;
    protected string $name;

    public function __construct()
    {
        $this->members = new TeamMemberCollection();
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function addTeamMember(TeamMember $member): self
    {
        $this->members->attach($member);
        return $this;
    }

    public function getMembers(): TeamMemberCollection
    {
        return $this->members;
    }
}

这个团队类的例子可以转移到 TeamMember 类。原则上,它会完全像这样。

现在让我们看看我们的团队收藏以及它是如何工作的。

$teams = new TeamCollection();

$team = (new SoccerTeam())
    ->setName('Borussia Dortmund');

$teams->attach($team);

这是一个正面的例子。因为 SoccerTeam 类实现了 Team 接口,所以它会被团队集合所接受。集合本身检查是否附加了 Team 实例。任何其他情况都会导致异常。


推荐阅读