0

Let's say I have static connector that allows to proxy instances of different adapters:

$m = Connector::take('mcrouter');
$db = Connector::take('production_database');

Connector must init and handle connections during the runtime:

protected $connection;
abstract protected function openConnection($config);

Somewhere inside adapter:

$this->connection = $this->openConnection($config);

The connection is an object and could be an instance of Memcached, MySQLi etc. or NULL. So logically I want to do this:

protected ?object $connection;
abstract protected function openConnection($config):?object;

But at the same time connection is not really instance of "object", it is instance of Memcached for example, and the result is:

Catchable fatal error: Hack type error: Invalid assignment

The only solution works in this case is to not define the type at all. Is there are some trick for defining universal object?

wake-up-neo
  • 814
  • 7
  • 9

1 Answers1

1

The universal type in Hack is what you get when you omit a type annotation; it's compatible with everything. object is not a type known to the typechecker, and so it assumes you have a class object somewhere.

The connection is an object and could be an instance of Memcached, MySQLi etc. or NULL.

The right way to do this is define an interface that both of those objects implement, and use that interface as the type here.

Having a generic object type gives no information to the typechecker; it still doesn't know what methods are valid to call on that object. The interface gives the typechecker that information.

Note that "what methods are safe to call when" is something that's implicitly codified in your application -- the code knows through some external means when it's safe to call certain methods for Memcached, MySQLi, etc, otherwise your code wouldn't work! Hack's type system, and type systems in general, just force you to make this explicit.

As an aside, you really shouldn't be getting your type errors as catchable fatals from HHVM; this is a last resort sort of check. Try running the hh_client checker directly, maybe even showing its result in your IDE; it will give you a much faster iteration cycle, and much more information than HHVM provides.

Josh Watzman
  • 7,060
  • 1
  • 18
  • 26
  • Thanks for the detailed answer! I played with interfaces and abstract classes, however I realized the approach itself is not suitable with strict typing, seems to be normal design for PHP, and bad design for Hack. I can't extend the default Memcached class with some AdapterInterface for only open/close connection methods, the same way I can't do "MemcachedAdapter extends \Memcached implements AdapterInterface" due to many reasons incl. interception of method names, or I must combine methods for both Memcached and Mysql in one interface in this case, which is strange and poor design – wake-up-neo Aug 15 '15 at 19:07