public function defineFunction(
        string $name,
        string $namespace,
        $callback
    ) {
        $signature = $this->signatureInspector->signature(
            $this->invocableInspector->callbackReflector($callback)
        );
        $fullName = $namespace . '\\' . $name;
        $key = strtolower($fullName);

        if (isset(self::$hooks[$key])) {
            if ($signature !== self::$hooks[$key]['signature']) {
                throw new FunctionSignatureMismatchException($fullName);
            }

            $replaced = self::$hooks[$key]['callback'];
        } else {
            $replaced = null;

            if (function_exists($fullName)) {
                throw new FunctionExistsException($fullName);
            }

            $source = $this->hookGenerator
                ->generateHook($name, $namespace, $signature);
            $reporting = error_reporting(E_ERROR | E_COMPILE_ERROR);

            try {
                eval($source);
            } catch (ParseError $e) {
                throw new FunctionHookGenerationFailedException(
                    $fullName,
                    $callback,
                    $source,
                    error_get_last(),
                    $e
                );
            } finally {
                error_reporting($reporting);
            }

            if (!function_exists($fullName)) {
                // @codeCoverageIgnoreStart
                throw new FunctionHookGenerationFailedException(
                    $fullName,
                    $callback,
                    $source,
                    error_get_last()
                );
                // @codeCoverageIgnoreEnd
            }
        }

        self::$hooks[$key] =
            ['callback' => $callback, 'signature' => $signature];

        return $replaced;
    }