injectableFactory = $injectableFactory; $this->classFinder = $classFinder; } public function process( string $controllerName, string $requestMethod, string $actionName, array $params, $data, Request $request, Response $response ) { $controller = $this->createController($controllerName); if ($actionName == 'index') { $actionName = $controller::$defaultAction ?? 'index'; } $actionNameUcfirst = ucfirst($actionName); $beforeMethodName = 'before' . $actionNameUcfirst; $actionMethodName = 'action' . $actionNameUcfirst; $afterMethodName = 'after' . $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}'." ); } $this->processContentTypeCheck($controller, $primaryActionMethodName, 1); if (method_exists($controller, $beforeMethodName)) { $controller->$beforeMethodName($params, $data, $request, $response); } $result = $controller->$primaryActionMethodName($params, $data, $request, $response); if (method_exists($controller, $afterMethodName)) { $controller->$afterMethodName($params, $data, $request, $response); } return $result; } protected function processContentTypeCheck(object $controller, string $primaryActionMethodName, int $parameterIndex) { $class = new ReflectionClass($controller); $method = $class->getMethod($primaryActionMethodName); $args = $method->getParameters(); if (count($args) <= $parameterIndex) { return; } $param = $args[$parameterIndex]; if (! $param->hasType()) { return; } $dataClass = $param->getClass(); if (!$dataClass) { return; } if (strtolower($dataClass->getName()) !== strtolower(StdClass::class)) { return; } if (! $data instanceof StdClass) { throw new BadRequest( "{$controllerName} {$requestMethod} {$actionName}: Content-Type should be 'application/json'." ); } } 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, ]); } }