src/Dispatch/AppRunner.php
protected function reflect($object)
    {
        // Instantiate an object when a classname is specified
        if (is_string($object) && class_exists($object))
            $object = DI::getInjector()->newInstance($object, $this->variables);

        // Find the correct method in the object
        $controller = $this->findController($object);
        
        // Fill public member variables of controller
        $this->injectVariables($object);

        $method = new ReflectionMethod($object, $controller);
        $parameters = $method->getParameters();

        // No required parameters, call the method!
        if (count($parameters) === 0)
            return call_user_func(array($object, $controller));

        // Iterate over the parameters and assign a value to them
        $args = array();
        $arg_cnt = 0;
        $urlargs = $this->arguments;
        $vars = $this->variables;
        $vars['arguments'] = $this->variables;

        foreach ($parameters as $cnt => $param)
        {
            $tp = $param->getType();
            if ($tp === null)
            {
                ++$arg_cnt;
                if (!$urlargs->has(0))
                    throw new HTTPError(400, "Invalid arguments - expecting argument $arg_cnt");

                $args[] = $urlargs->shift();
                continue;
            }

            $tp = (string)$tp;

            // Check if a variable by the correct name was set
            $name = $param->getName();
            if (isset($vars[$name]))
            {
                $val = $vars[$name];
                if ((is_object($val) && is_a($val, $tp)) || (gettype($val) === $tp))
                {
                    $args[] = $val;
                    continue;
                }
            }

            // Arguments may be passed in by having a Dictionary parameter as the last parameter
            if ($tp === Dictionary::class)
            {
                if ($cnt !== (count($parameters) - 1))
                    throw new HTTPError(500, "Dictionary must be last parameter");

                $args[] = $urlargs;
                break;
            }

            if (class_exists($tp) && isset($this->instances[$tp]))
            {
                $args[] = $this->instances[$tp];
                continue;
            }

            ++$arg_cnt;
            if ($tp === "int")
            {
                if (!$urlargs->has(0, Type::INT))
                    throw new HTTPError(400, "Invalid arguments - missing integer as argument $cnt");
                $args[] = (int)$urlargs->shift();
                continue;
            }

            if ($tp === "string")
            {
                if (!$urlargs->has(0, Type::STRING))
                    throw new HTTPError(400, "Invalid arguments - missing string as argument $cnt");
                $args[]  = (string)$urlargs->shift();
                continue;
            }

            if (class_exists($tp) && is_subclass_of($tp, Model::class))
            {
                if (!$urlargs->has(0))
                    throw new HTTPError(400, "Invalid arguments - missing identifier as argument $cnt");
                $object_id = $urlargs->shift();    
                $dao = $tp::getDAO();
                try
                {
                    $obj = $dao->getByID($object_id);
                }
                catch (DAOException $e)
                {
                    $namespace_parts = explode("\\", $tp);
                    $base_class_name = end($namespace_parts);
                    throw new HTTPError(404, "Unable to load {$base_class_name} with ID $object_id", "Invalid entity");
                }
                $args[] = $obj;
                continue;
            }

            throw new HTTPError(500, "Invalid parameter type: " . $tp);
        }

        return call_user_func_array([$object, $controller], $args);
    }
protected function reflect($object)
    {
        // Instantiate an object when a classname is specified
        if (is_string($object) && class_exists($object))
            $object = DI::getInjector()->newInstance($object, $this->variables);

        // Find the correct method in the object
        $controller = $this->findController($object);
        
        // Fill public member variables of controller
        $this->injectVariables($object);

        $method = new ReflectionMethod($object, $controller);
        $parameters = $method->getParameters();

        // No required parameters, call the method!
        if (count($parameters) === 0)
            return call_user_func(array($object, $controller));

        // Iterate over the parameters and assign a value to them
        $args = array();
        $arg_cnt = 0;
        $urlargs = $this->arguments;
        $vars = $this->variables;
        $vars['arguments'] = $this->variables;

        foreach ($parameters as $cnt => $param)
        {
            $tp = $param->getType();
            if ($tp === null)
            {
                ++$arg_cnt;
                if (!$urlargs->has(0))
                    throw new HTTPError(400, "Invalid arguments - expecting argument $arg_cnt");

                $args[] = $urlargs->shift();
                continue;
            }

            $tp = (string)$tp;

            // Check if a variable by the correct name was set
            $name = $param->getName();
            if (isset($vars[$name]))
            {
                $val = $vars[$name];
                if ((is_object($val) && is_a($val, $tp)) || (gettype($val) === $tp))
                {
                    $args[] = $val;
                    continue;
                }
            }

            // Arguments may be passed in by having a Dictionary parameter as the last parameter
            if ($tp === Dictionary::class)
            {
                if ($cnt !== (count($parameters) - 1))
                    throw new HTTPError(500, "Dictionary must be last parameter");

                $args[] = $urlargs;
                break;
            }

            if (class_exists($tp) && isset($this->instances[$tp]))
            {
                $args[] = $this->instances[$tp];
                continue;
            }

            ++$arg_cnt;
            if ($tp === "int")
            {
                if (!$urlargs->has(0, Type::INT))
                    throw new HTTPError(400, "Invalid arguments - missing integer as argument $cnt");
                $args[] = (int)$urlargs->shift();
                continue;
            }

            if ($tp === "string")
            {
                if (!$urlargs->has(0, Type::STRING))
                    throw new HTTPError(400, "Invalid arguments - missing string as argument $cnt");
                $args[]  = (string)$urlargs->shift();
                continue;
            }

            if (class_exists($tp) && is_subclass_of($tp, Model::class))
            {
                if (!$urlargs->has(0))
                    throw new HTTPError(400, "Invalid arguments - missing identifier as argument $cnt");
                $object_id = $urlargs->shift();    
                $dao = $tp::getDAO();
                try
                {
                    $obj = $dao->getByID($object_id);
                }
                catch (DAOException $e)
                {
                    $namespace_parts = explode("\\", $tp);
                    $base_class_name = end($namespace_parts);
                    throw new HTTPError(404, "Unable to load {$base_class_name} with ID $object_id", "Invalid entity");
                }
                $args[] = $obj;
                continue;
            }

            throw new HTTPError(500, "Invalid parameter type: " . $tp);
        }

        return call_user_func_array([$object, $controller], $args);
    }
If you prefer to encapsulate your controllers in classes, you can
have your app files return an object instead of a response.

Create a class and add methods to this class with names corresponding to
the first unmatched part of the route. E.g., if your controller is
/image and the called route is /image/edit/3, your class should contain
a method called 'edit' that accepts one argument of int-type.

The parameters of your methods are extracted using reflection and
matched to the request. You can use string or int types, or subclasses
of Wedeto\DB\Model. In the latter case, the object will be instantiated
using the parameter as identifier, that will be passed to the
DAO::get method.

It is also possible to set variables, using AppRunner#setVariable.
Variables set this way will be assigned to corresponding parameters of the
method. These parameters must match in type and name.

Finally, all remaining arguments can be passed to a parameter called
$arguments with type Dictionary, or, a Dictionary parameter that is the
last parameter.