injectableFactory = $injectableFactory; $this->classFinder = $classFinder; } public function process(string $controllerName, string $actionName, Request $request, Response $response) { $controller = $this->createController($controllerName); $requestMethod = $request->getMethod(); if ($actionName == 'index') { $actionName = $controller::$defaultAction ?? 'index'; } $actionNameUcfirst = ucfirst($actionName); $actionMethodName = 'action' . $actionNameUcfirst; $fullActionMethodName = strtolower($requestMethod) . ucfirst($actionMethodName); if (method_exists($controller, $fullActionMethodName)) { $primaryActionMethodName = $fullActionMethodName; } else { $primaryActionMethodName = $actionMethodName; } if (!method_exists($controller, $primaryActionMethodName)) { throw new NotFound( "Action {$requestMethod} '{$actionName}' does not exist in controller '{$controllerName}'." ); } if ( $this->useShortParamList($controller, $primaryActionMethodName) ) { return $controller->$primaryActionMethodName($request, $response); } // Below is a legacy way. $data = $request->getBodyContents(); if ($data && $request->getContentType() === 'application/json') { $data = json_decode($data); } $params = $request->getRouteParams(); $beforeMethodName = 'before' . $actionNameUcfirst; if (method_exists($controller, $beforeMethodName)) { $controller->$beforeMethodName($params, $data, $request, $response); } $result = $controller->$primaryActionMethodName($params, $data, $request, $response); $afterMethodName = 'after' . $actionNameUcfirst; if (method_exists($controller, $afterMethodName)) { $controller->$afterMethodName($params, $data, $request, $response); } return $result; } protected function useShortParamList(object $controller, string $methodName) : bool { $class = new ReflectionClass($controller); $method = $class->getMethod($methodName); $params = $method->getParameters(); if (count($params) === 0) { return false; } $type = $params[0]->getType(); if (!$type || $type->isBuiltin()) { return false; } $firstParamClass = new ReflectionClass($type->getName()); if ( $firstParamClass->getName() === Request::class || $firstParamClass->isSubclassOf(Request::class) ) { return true; } return false; } protected function getControllerClassName(string $name) : string { $className = $this->classFinder->find('Controllers', $name); if (!$className) { throw new NotFound("Controller '{$name}' does not exist."); } if (!class_exists($className)) { throw new NotFound("Class not found for controller '{$name}'."); } return $className; } protected function createController(string $name) : object { return $this->injectableFactory->createWith($this->getControllerClassName($name), [ 'name' => $name, ]); } }