예제 #1
0
 public function testDoesNotLimitDispatchErrorEventToOnlyOneListener()
 {
     $eventManager = new EventManager();
     $application = $this->prophesize(Application::class);
     $application->getEventManager()->willReturn($eventManager);
     $event = new MvcEvent();
     $event->setApplication($application->reveal());
     $guard = new DummyGuard();
     $guard->attach($eventManager);
     $eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, function (MvcEvent $event) {
         $event->setParam('first-listener', true);
     });
     $eventManager->attach(MvcEvent::EVENT_DISPATCH_ERROR, function (MvcEvent $event) {
         $event->setParam('second-listener', true);
     });
     // attach listener with lower priority than DummyGuard
     $eventManager->attach(MvcEvent::EVENT_ROUTE, function (MvcEvent $event) {
         $this->fail('should not be called, because guard should stop propagation');
     }, DummyGuard::EVENT_PRIORITY - 1);
     $event->setName(MvcEvent::EVENT_ROUTE);
     $eventManager->triggerEvent($event);
     $this->assertTrue($event->getParam('first-listener'));
     $this->assertTrue($event->getParam('second-listener'));
     $this->assertTrue($event->propagationIsStopped());
 }
예제 #2
0
 public function testOnBootstrapListenersWithHttpRequest()
 {
     $module = new Module();
     $application = $this->createApplication();
     $sm = $application->getServiceManager();
     $sm->setService('FilterManager', new FilterPluginManager());
     foreach ($module->getServiceConfig()['invokables'] as $key => $value) {
         $sm->setInvokableClass($key, $value);
     }
     foreach ($module->getServiceConfig()['factories'] as $key => $value) {
         $sm->setFactory($key, $value);
     }
     $sm->get('ViewHelperManager')->setService('bodyClass', $this->getMock(BodyClass::class));
     $event = new MvcEvent();
     $event->setApplication($application);
     $em = $application->getEventManager();
     $em->getSharedManager()->clearListeners(LayoutUpdater::class);
     $module->onBootstrap($event);
     $layoutUpdater = $sm->get(LayoutUpdaterInterface::class);
     $this->assertEquals(['default'], $layoutUpdater->getHandles());
     $mvcEvent = new MvcEvent();
     $mvcEvent->setApplication($application);
     $mvcEvent->setName(MvcEvent::EVENT_DISPATCH_ERROR);
     $mvcEvent->setError('test-error');
     $em->triggerEvent($mvcEvent);
     $this->assertEquals(['default', 'test-error'], $layoutUpdater->getHandles());
 }
예제 #3
0
 /**
  * Listen to the "route" event and attempt to route the request
  *
  * If no matches are returned, triggers "dispatch.error" in order to
  * create a 404 response.
  *
  * Seeds the event with the route match on completion.
  *
  * @param  MvcEvent $event
  * @return null|RouteMatch
  */
 public function onRoute(MvcEvent $event)
 {
     $request = $event->getRequest();
     $router = $event->getRouter();
     $routeMatch = $router->match($request);
     if ($routeMatch instanceof RouteMatch) {
         $event->setRouteMatch($routeMatch);
         return $routeMatch;
     }
     $event->setName(MvcEvent::EVENT_DISPATCH_ERROR);
     $event->setError(Application::ERROR_ROUTER_NO_MATCH);
     $target = $event->getTarget();
     $results = $target->getEventManager()->triggerEvent($event);
     if (!empty($results)) {
         return $results->last();
     }
     return $event->getParams();
 }
예제 #4
0
 /**
  * @param MvcEvent $e
  */
 public function onBootstrap(MvcEvent $e)
 {
     ini_set('display_errors', 'off');
     $errorCallback = function () use($e) {
         // Fetching the error's information
         $error = error_get_last() ?: func_get_args();
         $error = array_values($error);
         if (!$error) {
             return;
         }
         // Create exception ans associated event
         $exception = new ErrorException($error[1], 0, $error[0], $error[2], $error[3], isset($error[4]) && $error[4] instanceof \Exception ? $error[4] : null);
         $e->setParam('exception', $exception);
         $e->setError(Application::ERROR_EXCEPTION);
         $e->setName(MvcEvent::EVENT_RENDER_ERROR);
         /** @var Application $application */
         $application = $e->getApplication();
         $application->getEventManager()->triggerEvent($e);
     };
     // Set error handlers
     set_error_handler($errorCallback, \E_ALL);
     register_shutdown_function($errorCallback);
 }
예제 #5
0
 /**
  * Marshal a middleware not callable exception event
  *
  * @param  string $type
  * @param  string $middlewareName
  * @param  MvcEvent $event
  * @param  Application $application
  * @param  \Exception $exception
  * @return mixed
  */
 protected function marshalMiddlewareNotCallable($type, $middlewareName, MvcEvent $event, Application $application, \Exception $exception = null)
 {
     $event->setName(MvcEvent::EVENT_DISPATCH_ERROR);
     $event->setError($type);
     $event->setController($middlewareName);
     $event->setControllerClass('Middleware not callable: ' . $middlewareName);
     if ($exception !== null) {
         $event->setParam('exception', $exception);
     }
     $events = $application->getEventManager();
     $results = $events->triggerEvent($event);
     $return = $results->last();
     if (!$return) {
         $return = $event->getResult();
     }
     return $return;
 }
예제 #6
0
 /**
  * Marshal a bad controller exception event
  *
  * @param  string $controllerName
  * @param  MvcEvent $event
  * @param  Application $application
  * @param  \Throwable|\Exception $exception
  * @return mixed
  */
 protected function marshalBadControllerEvent($controllerName, MvcEvent $event, Application $application, $exception)
 {
     $event->setName(MvcEvent::EVENT_DISPATCH_ERROR);
     $event->setError($application::ERROR_EXCEPTION);
     $event->setController($controllerName);
     $event->setParam('exception', $exception);
     $events = $application->getEventManager();
     $results = $events->triggerEvent($event);
     $return = $results->last();
     if (!$return) {
         return $event->getResult();
     }
     return $return;
 }
 /**
  * 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;
 }
예제 #8
0
 /**
  * @todo handle with view strategy
  * @param Application $app
  * @param MvcEvent $event
  * @return void
  */
 protected function triggerUnauthorizedError(Application $app, MvcEvent $event)
 {
     $app->getServiceManager()->get('response')->setStatusCode(403);
     $event->setName(MvcEvent::EVENT_DISPATCH_ERROR);
     $event->setError('error-unauthorized-route');
     $event->setParam('exception', new PermissionDeniedException('You are not authorized to access this page.', 403));
     return $app->getEventManager()->triggerEvent($event);
 }
예제 #9
0
 /**
  * Handle an exception/throwable.
  *
  * @param Throwable|Exception $exception
  * @param MvcEvent $event
  * @param EventManagerInterface $events
  * @return self
  */
 private function handleException($exception, MvcEvent $event, EventManagerInterface $events)
 {
     $event->setName(MvcEvent::EVENT_DISPATCH_ERROR);
     $event->setError(self::ERROR_EXCEPTION);
     $event->setParam('exception', $exception);
     $result = $events->triggerEvent($event);
     $response = $result->last();
     if ($response instanceof ResponseInterface) {
         $event->setName(MvcEvent::EVENT_FINISH);
         $event->setTarget($this);
         $event->setResponse($response);
         $this->response = $response;
         $events->triggerEvent($event);
         return $this;
     }
     return $this->completeRequest($event);
 }
 /**
  * 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));
 }
예제 #11
0
 /**
  * Complete the request
  *
  * Triggers "render" and "finish" events, and returns response from
  * event object.
  *
  * @param  MvcEvent $event
  * @return Application
  */
 protected function completeRequest(MvcEvent $event)
 {
     $events = $this->events;
     $event->setTarget($this);
     $event->setName(MvcEvent::EVENT_RENDER);
     $event->stopPropagation(false);
     // Clear before triggering
     $events->triggerEvent($event);
     $event->setName(MvcEvent::EVENT_FINISH);
     $event->stopPropagation(false);
     // Clear before triggering
     $events->triggerEvent($event);
     return $this;
 }