我正在将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()
或Container::getNew()
时,由于返回类型为mixed
,因此当我尝试执行时会抛出此错误对这些对象返回的对象的方法:
您正在尝试访问成员x,但这不是对象,而是混合值
现在我知道这是有道理的,因为混合显然允许非对象,所以我必须确保将这些代码包装在is_object()
中,但这样做似乎并没有抑制错误类型检查器。有没有更好的方法来确认某些东西是Hack中的对象,类型检查器会理解?
此外,这个IoC容器类在很大程度上依赖于混合类型,这对我来说有点难看。在运行时也必须确保其返回是对象并不理想。有没有更好的方法可以做到这一点?我确实尝试过将混合更改为接口(如IContainable或其他东西)的想法,并且我想在容器中存储任何类实现这个,但是类型检查器抱怨IContainable接口不包含该方法我试图调用从容器返回的对象(所以代码中的同一点出错,但原因不同)。也许这种方法接近成功了?
感谢您的帮助。
答案 0 :(得分: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
。 (因为混合可以是任何东西,它也可以是null。当你写?mixed
时生成错误在我的某些事情列表上。:))
此外,这个IoC容器类在很大程度上依赖于混合类型,这对我来说有点难看。
你的直觉很好。在Facebook,我们发现在大多数情况下我们想要输入mixed
,或者1)我们应该写一个更具体的类型,或者2)代码结构非常糟糕我们应该重新思考如何构建它以便更好地打字。
也许你应该使用更具体的类型。正如您所指出的那样,使用界面会有所帮助。如果事实证明您需要做的大部分事情实际上都是该界面的一部分,并且您偶尔需要做其他事情,则可以将其与上述instanceof
功能结合起来。您可能还想查看using generics,这可能有意义,也可能没有意义,具体取决于您使用Container
的具体程度。如果Container
被多次实例化,每个用于不同的特定子类或接口,那么泛型可能就是你想要的。如果它是单身,那么也许可能不是。 (另外,虽然您未在此处显示,但如果Container
以任何方式与其所持有的类进行交互,则可能需要constraint on the generic。)
但我怀疑你因为结构问题而遇到了这个问题。以下是我遇到的一些事情:为什么在第一个实例化类时需要这一层抽象 - 它提供了什么功能,你可以使这个功能同质化(即,可打字) )并从异构类实例化问题中分离出来?暂时忽略正式类型(即,如果你用vanilla PHP编写这个类型),使用Container
的类如何知道它正在做正确的事情?必须知道进出的内容,无论是非正式的还是以其他方式捆绑在调用代码中,否则这不会起作用!想一想这些信息的编制地点和方式,然后看看你是否能找到一种方法来明确这些信息。
希望这有帮助!如果我能澄清任何事情,请在评论中告诉我。当然,随时可以继续询问SO问题,很多FB Hack工程师以及知识渊博的社区成员密切关注“hacklang”标签:)(我们真的很兴奋人们正在尝试Hack BTW!希望你是享受它。)
答案 1 :(得分:0)
我知道我已经迟到了6个月,但我想提供解决这个问题的方法。
我通过创建一个脚本来解决这个问题,该脚本将扫描项目中的工厂方法并将它们编译成一个类。
我通过扫描带有两个参数的user defined property provides
来实现这一点:工厂的别名和工厂生成的对象类型的全名。编译脚本还确保每个工厂只接受一个参数,即已编译的工厂类。
本质上,不是在运行时填充容器,而是在编译时填充它。容器类定义是容器,而不是作为容器的容器类的实例。
它仍在进行中,但您可以在github
上看到我的解决方案