/** * @param MvcEvent $evt * @return mixed * @throws \Exception */ protected function getDispatchedController(MvcEvent $evt) { $controllerName = $evt->getRouteMatch()->getParam('controller'); if (!$controllerName) { throw new \Exception('Unable to parse ZendRestModule annotation:' . " The route match for \"{$evt->getName()}\" is missing a \"controller\" param."); } return $controllerName; }
/** * Render the view * * @param MvcEvent $e * @return Response */ public function render(MvcEvent $e) { $result = $e->getResult(); if ($result instanceof Response) { return $result; } // Martial arguments $request = $e->getRequest(); $response = $e->getResponse(); $viewModel = $e->getViewModel(); if (!$viewModel instanceof ViewModel) { return; } $view = $this->view; $view->setRequest($request); $view->setResponse($response); try { $view->render($viewModel); } catch (\Exception $ex) { if ($e->getName() === MvcEvent::EVENT_RENDER_ERROR) { throw $ex; } $application = $e->getApplication(); $events = $application->getEventManager(); $e->setError(Application::ERROR_EXCEPTION)->setParam('exception', $ex); $events->trigger(MvcEvent::EVENT_RENDER_ERROR, $e); } return $response; }
/** * Render the view * * @param MvcEvent $e * @return Response|null * @throws \Exception */ public function render(MvcEvent $e) { $result = $e->getResult(); if ($result instanceof Response) { return $result; } // Martial arguments $request = $e->getRequest(); $response = $e->getResponse(); $viewModel = $e->getViewModel(); if (!$viewModel instanceof ViewModel) { return; } $view = $this->view; $view->setRequest($request); $view->setResponse($response); $caughtException = null; try { $view->render($viewModel); } catch (\Throwable $ex) { $caughtException = $ex; } catch (\Exception $ex) { // @TODO clean up once PHP 7 requirement is enforced $caughtException = $ex; } if ($caughtException !== null) { if ($e->getName() === MvcEvent::EVENT_RENDER_ERROR) { throw $caughtException; } $application = $e->getApplication(); $events = $application->getEventManager(); $e->setError(Application::ERROR_EXCEPTION); $e->setParam('exception', $caughtException); $e->setName(MvcEvent::EVENT_RENDER_ERROR); $events->triggerEvent($e); } return $response; }
/** * @param MvcEvent $event */ public function handleError(MvcEvent $event) { // Do nothing if no error in the event $error = $event->getError(); if (empty($error)) { return; } switch ($error) { case Application::ERROR_CONTROLLER_NOT_FOUND: case Application::ERROR_CONTROLLER_INVALID: // Specifically not handling these return; break; case Application::ERROR_ROUTER_NO_MATCH: if ($event->getName() == MvcEvent::EVENT_DISPATCH_ERROR) { // add dummy 'no-route' route to silent routeMatch errors $noRoute = 'no-route'; $event->getRouter()->addRoute($noRoute, Router\Http\Literal::factory(['route' => ''])); $event->setRouteMatch((new Router\RouteMatch([]))->setMatchedRouteName($noRoute)); } break; case Application::ERROR_EXCEPTION: default: $exception = $event->getParam('exception'); $logMessages = array(); do { $priority = Logger::ERR; $extra = array('file' => $exception->getFile(), 'line' => $exception->getLine(), 'trace' => $exception->getTrace()); if (isset($exception->xdebug_message)) { $extra['xdebug'] = $exception->xdebug_message; } $logMessages[] = array('priority' => $priority, 'message' => $exception->getMessage(), 'extra' => $extra); $exception = $exception->getPrevious(); } while ($exception); foreach (array_reverse($logMessages) as $logMessage) { $this->log->log($logMessage['priority'], $logMessage['message'], $logMessage['extra']); } break; } }
/** * Attempt to validate the incoming request * * If an input filter is associated with the matched controller service, * attempt to validate the incoming request, and inject the event with the * input filter, as the "ZF\ContentValidation\InputFilter" parameter. * * Uses the ContentNegotiation ParameterDataContainer to retrieve parameters * to validate, and returns an ApiProblemResponse when validation fails. * * Also returns an ApiProblemResponse in cases of: * * - Invalid input filter service name * - Missing ParameterDataContainer (i.e., ContentNegotiation is not registered) * * @param MvcEvent $e * @return null|ApiProblemResponse */ public function onRoute(MvcEvent $e) { $request = $e->getRequest(); if (!$request instanceof HttpRequest) { return; } $routeMatches = $e->getRouteMatch(); if (!($routeMatches instanceof RouteMatch || $routeMatches instanceof V2RouteMatch)) { return; } $controllerService = $routeMatches->getParam('controller', false); if (!$controllerService) { return; } $method = $request->getMethod(); $inputFilterService = $this->getInputFilterService($controllerService, $method); if (!$inputFilterService) { return; } if (!$this->hasInputFilter($inputFilterService)) { return new ApiProblemResponse(new ApiProblem(500, sprintf('Listed input filter "%s" does not exist; cannot validate request', $inputFilterService))); } $dataContainer = $e->getParam('ZFContentNegotiationParameterData', false); if (!$dataContainer instanceof ParameterDataContainer) { return new ApiProblemResponse(new ApiProblem(500, 'ZF\\ContentNegotiation module is not initialized; cannot validate request')); } $data = in_array($method, $this->methodsWithoutBodies) ? $dataContainer->getQueryParams() : $dataContainer->getBodyParams(); if (null === $data || '' === $data) { $data = []; } $isCollection = $this->isCollection($controllerService, $data, $routeMatches, $request); $files = $request->getFiles(); if (!$isCollection && 0 < count($files)) { // File uploads are not validated for collections; impossible to // match file fields to discrete sets $data = ArrayUtils::merge($data, $files->toArray(), true); } $inputFilter = $this->getInputFilter($inputFilterService); if ($isCollection && !in_array($method, $this->methodsWithoutBodies)) { $collectionInputFilter = new CollectionInputFilter(); $collectionInputFilter->setInputFilter($inputFilter); $inputFilter = $collectionInputFilter; } $e->setParam('ZF\\ContentValidation\\InputFilter', $inputFilter); $currentEventName = $e->getName(); $e->setName(self::EVENT_BEFORE_VALIDATE); $events = $this->getEventManager(); $results = $events->triggerEventUntil(function ($result) { return $result instanceof ApiProblem || $result instanceof ApiProblemResponse; }, $e); $e->setName($currentEventName); $last = $results->last(); if ($last instanceof ApiProblem) { $last = new ApiProblemResponse($last); } if ($last instanceof ApiProblemResponse) { return $last; } $inputFilter->setData($data); $status = $request->isPatch() ? $this->validatePatch($inputFilter, $data, $isCollection) : $inputFilter->isValid(); if ($status instanceof ApiProblemResponse) { return $status; } // Invalid? Return a 422 response. if (false === $status) { return new ApiProblemResponse(new ApiProblem(422, 'Failed Validation', null, null, ['validation_messages' => $inputFilter->getMessages()])); } // Should we use the raw data vs. the filtered data? // - If no `use_raw_data` flag is present, always use the raw data, as // that was the default experience starting in 1.0. // - If the flag is present AND is boolean true, that is also // an indicator that the raw data should be present. $useRawData = $this->useRawData($controllerService); if (!$useRawData) { $data = $inputFilter->getValues(); } // If we don't have an instance of UnknownInputsCapableInterface, or no // unknown data is in the input filter, at this point we can just // set the current data into the data container. if (!$inputFilter instanceof UnknownInputsCapableInterface || !$inputFilter->hasUnknown()) { $dataContainer->setBodyParams($data); return; } $unknown = $inputFilter->getUnknown(); if ($this->allowsOnlyFieldsInFilter($controllerService)) { if ($inputFilter instanceof CollectionInputFilter) { $unknownFields = []; foreach ($unknown as $key => $fields) { $unknownFields[] = '[' . $key . ': ' . implode(', ', array_keys($fields)) . ']'; } $fields = implode(', ', $unknownFields); } else { $fields = implode(', ', array_keys($unknown)); } $detail = sprintf('Unrecognized fields: %s', $fields); $problem = new ApiProblem(Response::STATUS_CODE_422, $detail); return new ApiProblemResponse($problem); } // The raw data already contains unknown inputs, so no need to merge // them with the data. if ($useRawData) { $dataContainer->setBodyParams($data); return; } // When not using raw data, we merge the unknown data with the // validated data to get the full set of input. $dataContainer->setBodyParams(array_merge($data, $unknown)); }
/** * Create and return a 400 view model * * @param MvcEvent $e * @return void */ public function prepareBadRequestViewModel(MvcEvent $e) { $firephp = \FirePHP::getInstance(true); $firephp->info(__METHOD__); $firephp->info($e->getName(), 'event name'); $vars = $e->getResult(); if ($vars instanceof Response) { // Already have a response as the result return; } $response = $e->getResponse(); if ($response->getStatusCode() != 400) { // Only handle 404 responses return; } if (!$vars instanceof ViewModel) { $firephp->info('creating new ViewModel'); $model = new ViewModel(); if (is_string($vars)) { $model->setVariable('message', $vars); } else { $model->setVariable('message', 'Bad request.'); } } else { $firephp->info('updating existing view model'); $model = $vars; if ($model->getVariable('message') === null) { $model->setVariable('message', 'Bad request.'); } } $firephp->info($model->getTemplate(), 'view model template'); $model->setTemplate($this->getBadRequestTemplate()); $firephp->info($model->getVariable('reason'), 'before injecting reason'); // If displaying reasons, inject the reason $this->injectBadRequestReason($model, $e); $firephp->info($model->getVariable('reason'), 'reason'); // If displaying exceptions, inject $this->injectException($model, $e); $firephp->info($model->getVariable('exception'), 'exception'); // Inject controller if we're displaying either the reason or the exception $this->injectController($model, $e); $firephp->info($model->getVariable('controller'), 'controller'); $e->setResult($model); }
public function renderAssets(MvcEvent $e) { $sm = $e->getApplication()->getServiceManager(); /** @var Configuration $config */ $config = $sm->get('AsseticConfiguration'); if ($e->getName() === MvcEvent::EVENT_DISPATCH_ERROR) { $error = $e->getError(); if ($error && !in_array($error, $config->getAcceptableErrors())) { // break if not an acceptable error return; } } $response = $e->getResponse(); if (!$response) { $response = new Response(); $e->setResponse($response); } /** @var $asseticService \AsseticBundle\Service */ $asseticService = $sm->get('AsseticService'); // setup service if a matched route exist $router = $e->getRouteMatch(); if ($router) { $asseticService->setRouteName($router->getMatchedRouteName()); $asseticService->setControllerName($router->getParam('controller')); $asseticService->setActionName($router->getParam('action')); } // Create all objects $asseticService->build(); // Init assets for modules $asseticService->setupRenderer($sm->get('ViewRenderer')); }
/** * @param \Zend\Mvc\MvcEvent $object * @param array $options * * @return array */ public function format($object, array $options) { $data['route']['name'] = $object->getRouteMatch()->getMatchedRouteName(); $data['route']['params'] = $object->getRouteMatch()->getParams(); $parts = explode('/', $data['route']['name']); $route = $object->getRouter(); $config = $object->getApplication()->getServiceManager()->get('config'); $config = isset($config['router']['routes']) ? $config['router']['routes'] : []; while ($part = array_shift($parts)) { $route->hasRoute($part) and $route = $route->getRoute($part); isset($config[$part]) and $config = $config[$part]; } $data['route']['class'] = get_class($route); $data['route']['assembled'] = $route->getAssembledParams(); $data['event']['error'] = $object->getError(); $data['event']['name'] = $object->getName(); $controllers = []; $definitions = []; $title = '404 Error'; $subtitle = 'Unknown Error'; $context = null; $manager = $object->getApplication()->getServiceManager()->get('ControllerLoader'); switch ($object->getError()) { case Application::ERROR_CONTROLLER_NOT_FOUND: $definitions = $config; $title = $object->getControllerClass(); $subtitle = 'The requested controller cannot be found'; $controllers = $manager->getCanonicalNames(); array_pop($controllers); // because the Sm add the wrong into the list break; case Application::ERROR_CONTROLLER_INVALID: $title = $object->getControllerClass(); $subtitle = $object->getParam('exception')->getMessage(); break; case Application::ERROR_CONTROLLER_CANNOT_DISPATCH: $context = $this->getControllerContext($manager, $data['route']['params']); $subtitle = 'The controller cannot dispatch the request'; $title = $data['route']['params']['controller']; break; } $data['title'] = $title; $data['subtitle'] = $subtitle; $data['route']['definition'] = $definitions; $data['controller']['names'] = $controllers; $data['controller']['context'] = $context; return $data; }