3

我正在将 PHP 项目转换为 Hack,但遇到了一些障碍。我正在尝试做的是将 IoC 容器从 PHP 重写为 Hack,我在让所有内容都通过 Hack 类型检查器工具时遇到了一些麻烦。

所以基本上我拥有的是一个容器,它可以让你将字符串注册到闭包映射。这个想法是闭包包含实例化一个类的逻辑。容器还存储它创建的实例,还允许您强制创建新实例。这是我的容器代码:

<?hh // strict
class Container {
    private Map<string, mixed> $instances = Map {};
    private Map<string, (function (Container): mixed)> $registered = Map {};

    public function register(string $alias, (function (Container): mixed) $closure): void
    {       
        $this->registered[$alias] = $closure;
    }

    public function get(string $alias): ?mixed
    {
        if (!$this->registered->contains($alias)) {
            return null;
        }

        $instance = $this->instances->get($alias);
        if ($instance !== null) {
            return $instance;
        }

        $closure = $this->registered->get($alias);
        if ($closure !== null) {
            $this->instances->set($alias, $closure($this));
        }
        return $this->instances->get($alias);
    }

    public function getNew(string $alias): ?mixed
    {
        if (!$this->registered->contains($alias)) {
            return null;
        }

        $closure = $this->registered->get($alias);
        return ($closure !== null) ? $closure($this) : null;
    }
}

这个类本身似乎通过了类型检查器,但是当使用Container::get()or时Container::getNew(),由于返回是 type mixed,当我尝试对这些返回的对象执行方法时,它会抛出此错误:

您正在尝试访问成员 x 但这不是一个对象,它是一个混合值

现在我知道这是有道理的,因为混合显然允许非对象,所以我必须确保将此类代码包装在 a 中is_object(),但是这样做似乎并不能抑制类型检查器中的错误。有没有更好的方法来确认某个东西是 Hack 中的一个对象,类型检查器会理解?

另外,这个 IoC 容器类严重依赖于混合类型,这对我来说有点难看。在运行时必须确保它的返回是对象也不理想。有没有更好的方法可以做到这一点?我确实尝试过将混合更改为接口(如 IContainable 或其他东西)的想法,并让我想存储在容器中的任何类实现这一点,但随后类型检查器抱怨 IContainable 接口不包含该方法我试图调用从容器返回的对象(因此代码中的同一点出现错误,但原因不同)。也许我用这种方法接近成功了?

谢谢你的帮助。

4

2 回答 2

10

有没有更好的方法来确认某个东西是 Hack 中的一个对象,类型检查器会理解?

您可能想通过instanceof. 例如:(警告,代码直接输入浏览器)

<?hh // strict

class C {
  public function f(): void {}
}

function f(): mixed {
  return new C();
}

function g(): void {
  $c = f();
  // $c->f() won't work out here, $c is mixed
  if ($c instanceof C) {
    $c->f(); // This works now
  }
}

另请注意,您不想要?mixed,那是多余的-只需使用mixed. (因为混合可以是任何东西,它也可以是空的。当你写的时候产生一个错误?mixed是我在某个时候要做的事情的清单上。:))

另外,这个 IoC 容器类严重依赖于混合类型,这对我来说有点难看。

你的直觉在这里很好。在 Facebook,我们发现在大多数情况下,我们想要键入mixed的内容为它更好地打字。

也许您应该使用更具体的类型。正如您所指出的,使用界面会有所帮助。instanceof如果事实证明您需要做的大部分事情实际上都是该界面的一部分,并且您只是偶尔需要做一些其他事情,那么您可以将它与上述功能结合起来。您可能还想看看using generics,这可能有意义,也可能没有意义,具体取决于您使用Container. 如果Container多次实例化,每次都针对不同的特定子类或接口,那么泛型可能正是您想要的。如果它是一个单例,那么也许不是。(另外,虽然你没有在这里展示它,但如果Container与它所拥有的类以任何方式交互,你可能需要对泛型的约束。)

但是我怀疑您由于结构问题而遇到了这个问题。当我遇到这个问题时,我想到了以下一些事情:为什么首先需要围绕实例化类的抽象层——它提供什么功能,你能否使该功能同质化(即,可键入) 并与异构类实例化问题分开?暂时忽略形式类型(即,如果您是用普通 PHP 编写的),使用的类如何Container知道这是正确的吗?必须对进出的内容有所了解,无论是非正式的还是以其他方式与调用代码相关,否则这首先不起作用!想想这些信息在哪里以及如何被编码,然后看看你是否能找到一种方法来明确表达。

希望这可以帮助!如果我能澄清任何事情,请在评论中告诉我。当然,请随时提出 SO 问题,许多 FB Hack 工程师以及知识渊博的社区成员密切关注“hacklang”标签 :)(我们真的很高兴人们正在尝试 Hack BTW!希望你享受它。)

于 2014-05-10T17:16:13.723 回答
0

我知道我迟到了 6 个月,但我想为这个问题提供我的解决方案。

我通过创建一个脚本来解决这个问题,该脚本将扫描项目中的工厂方法并将它们编译成一个类。

我通过扫描带有两个参数的用户定义属性 provides来实现这一点:工厂的别名和工厂生产的对象类型的全名。编译脚本还确保每个工厂只接受一个参数,即编译后的工厂类。

本质上,我不是在运行时填充容器,而是在编译时填充它。容器类定义是容器,而不是容器类的实例是容器。

它仍在进行中,但您可以在github上查看我的解决方案

于 2014-12-08T07:56:23.407 回答