public static function subscribe(string $hook, callable $callback, int $precedence = 0)
    {
        $parts = explode(".", $hook);
        if (count($parts) < 2)
            throw new InvalidArgumentException("Hook name must consist of at least two parts");

        if ($hook === Hook::SHUTDOWN_HOOK)
            self::registerShutdownHook();

        // Make sure the callback is appropriate
        if (is_object($callback))
            $callback = [$callback, '__invoke'];

        $refl = is_array($callback) ? new \ReflectionMethod($callback[0], $callback[1]) : new \ReflectionFunction($callback);
        $params = $refl->getParameters();
        if (count($params) !== 1)
            throw new InvalidArgumentException("Hook must accept exactly one argument of type Dictionary");
        
        if ($params[0]->getType() === null || $params[0]->getType()->__toString() !== Dictionary::class)
            throw new InvalidArgumentException("Hook must accept exactly one argument of type Dictionary");

        $ref = ++self::$sequence;
        self::$hooks[$hook][] = ['precedence' => $precedence, 'callback' => $callback, 'ref' => $ref];
        usort(
            self::$hooks[$hook], 
            function (array $l, array $r) { 
                if ($l['precedence'] !== $r['precedence'])
                    return $l['precedence'] - $r['precedence']; 
                return $l['ref'] - $r['ref'];
            }
        );

        return $ref;
    }