I'm in the process of converting a PHP project to Hack, and I've come across a bit of a road block. What I'm trying to do is rewrite a IoC container from PHP to Hack, and I'm having a little trouble getting everything to pass the Hack type checker tool.
So basically what I have is a container that lets you register string to closure mappings. The idea is the closure contains the logic to instantiate a class. The container also stores the instances it creates, and also allows you to force a creation of a new instance. Here is my container code:
<?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;
}
}
This class itself seems to pass the type checker, but when when using Container::get()
or Container::getNew()
, since the return is of type mixed
, it throws this error when I try to execute a method on an object returned by these:
You are trying to access the member x but this is not an object, it is a mixed value
Now I know this makes sense, because mixed obviously allows for non-objects, so I would have to make sure to wrap such code in a is_object()
, however doing this doesn't seem to suppress the error in the type checker. Is there a better way to confirm something is an object in Hack, that the type checker will understand?
Also, this IoC container class relies heavily on the mixed type, which is a bit ugly to me. It isn't ideal to have to make sure its returns are objects at run time either. Is there a better way I could be doing this? I did try playing with the idea of changing mixed to an interface (like IContainable or something), and have any class I want to store in the container implement this, but then the type checker complained that the IContainable interface didn't contain the method I was trying to call on the object returned from the container (so an error at the same point in code, but for a different reason). Perhaps I was close to success with this approach?
Thanks for any help.