/** * {@inheritDoc} */ public function extractResponse(ActionInterface $action, CallableInterface $callable) { $response = $action->getResponse(); if (!$response) { return null; } if (!is_object($response) || !$response instanceof ObjectResponse) { throw new ResponseExtractorNotSupportException(sprintf('The response must be ObjectResponse for extract, but "%s" given.')); } $class = $response->getClass(); if (substr($class, strlen($class) - 2) == '[]') { $type = 'collection'; $class = substr($class, 0, strlen($class) - 2); } else { $type = 'object'; } if (!$class) { return null; } if (!class_exists($class)) { throw new ResponseExtractException(sprintf('Can not extract response from class "%s" for callable "%s". Class not found.', $class, Reflection::getCalledMethod($callable->getReflection()))); } // Get properties from object $properties = $this->getPropertiesForClass($action, $callable, $class); $responseProperties = []; foreach ($properties as $property) { $responseProperties[] = $this->extractResponseProperty($action, $callable, $property); } return new Response($type, $class, $responseProperties); }
/** * {@inheritDoc} */ public function convertParameter(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $method, $value, $group, array $attributes = []) { $converter = $this->getConverter($group); if (!$converter->isSupported($parameter, $method)) { throw new ConverterNotSupportedException(sprintf('The parameter converter "%s" not supported in function "%s".', $parameter->getName(), Reflection::getCalledMethod($method))); } $value = $converter->convert($parameter, $method, $value, $attributes); return $value; }
/** * {@inheritDoc} */ public function convert(\ReflectionParameter $parameter, \ReflectionFunctionAbstract $method, $value, array $attributes = []) { foreach ($this->converters as $converter) { if (true === $converter->isSupported($parameter, $method)) { return $converter->convert($parameter, $method, $value, $attributes); } } throw new ConverterNotFoundException(sprintf('Not found parameter converter for argument "%s" in function "%s".', $parameter->getName(), Reflection::getCalledMethod($method))); }
/** * {@inheritDoc} */ protected function execute(InputInterface $input, OutputInterface $output) { $handlerRegistry = $this->getContainer()->get('api.handler_registry'); $callableResolver = $this->getContainer()->get('api.callable_resolver'); $projectDirectory = realpath($this->getContainer()->getParameter('kernel.root_dir') . '/..'); $handlerKeys = $handlerRegistry->getHandlerKeys(); if ($input->getArgument('handler')) { $handlerKeys = array_diff($handlerKeys, array_diff($handlerKeys, $input->getArgument('handler'))); } if (!count($handlerKeys)) { $output->writeln('Not found handlers.'); return 0; } foreach ($handlerKeys as $handlerKey) { $handler = $handlerRegistry->getHandler($handlerKey); $actions = $handler->getActions(); /** @var \Symfony\Component\Console\Helper\Table $table */ $table = $this->getHelper('table'); $table->setHeaders(['Name', 'Callable', 'File / Line']); $rows = []; foreach ($actions as $action) { try { $callable = $callableResolver->resolve($action); } catch (\Exception $e) { $rows[] = [$action->getName(), '<error>Not supported</error>', '']; continue; } $reflection = $callable->getReflection(); $file = str_replace($projectDirectory, '', $reflection->getFileName()); $callableName = Reflection::getCalledMethod($callable->getReflection(), false); $fileAndLine = sprintf('%s on lines %d:%d', ltrim($file, '/'), $reflection->getStartLine(), $reflection->getEndLine()); $rows[] = [$action->getName(), $callableName, $fileAndLine]; } $output->writeln(sprintf('Action list for handler <info>%s</info>:', $handlerKey)); $table->setRows($rows); $table->render($output); $output->writeln([null, null]); } return 0; }
/** * Resolve request parameter * * @param ActionInterface $action * @param CallableInterface $callable * @param array $inputArguments * @param \ReflectionParameter $parameter * * @return object * * @throws \Exception */ private function resolveRequest(ActionInterface $action, CallableInterface $callable, array $inputArguments, \ReflectionParameter $parameter) { $class = $parameter->getClass(); if ($class->isInterface()) { throw new \RuntimeException(sprintf('Could not create instance via interface for parameter "%s" in method "%s". ' . 'You must set the class for type hinting.', $parameter->getName(), Reflection::getCalledMethod($callable->getReflection()))); } if ($class->isAbstract()) { throw new \RuntimeException(sprintf('Could not create instance via abstract class for parameter "%s" in method "%s". ' . 'You must set the real class for type hinting.', $parameter->getName(), Reflection::getCalledMethod($callable->getReflection()))); } /** @var RequestInterface $request */ $request = $class->newInstance(); // Map arguments $this->objectMapper->map($request, $inputArguments, $action->getRequestMappingGroup()); // First step validation: Strict mode if ($this->validator && $action->isStrictValidation()) { $this->strictRequestValidate($request); } // Second step validation: Base validation if ($this->validator && $action->getValidationGroups()) { $violationList = $this->validator->validate($request, null, $action->getValidationGroups()); if (count($violationList)) { throw ViolationListException::create($violationList); } } // Convert request properties if ($this->propertyConverter) { try { $this->propertyConverter->convertProperties($request, PropertyConverterManagerInterface::GROUP_DEFAULT); } catch (InvalidArgumentException $e) { $constraintViolation = new ConstraintViolation($e->getMessage(), null, [], null, $e->getName(), $e->getInvalidValue()); $constraintViolationList = new ConstraintViolationList([$constraintViolation]); throw ViolationListException::create($constraintViolationList); } catch (ConverterNotFoundException $e) { if ($this->logger) { $this->logger->warning(sprintf('Could not convert properties with message: %s.', rtrim($e->getMessage(), '.'))); } // Nothing action } } return $request; }
/** * Get converter annotation * * @param \ReflectionParameter $parameter * @param \ReflectionMethod $method * * @return ORM * * @throws ORMAnnotationNotFoundException */ private function getConverterAnnotation(\ReflectionParameter $parameter, \ReflectionMethod $method) { $annotations = $this->reader->getMethodAnnotations($method); foreach ($annotations as $annotation) { if ($annotation instanceof ORM) { // Check parameter name if ($annotation->name === $parameter->name) { return $annotation; } // Check classes if ($class = $parameter->getClass()) { if (is_a($class->getName(), $annotation->entityClass, true)) { return $annotation; } } if (!$annotation->name && !$annotation->entityClass) { throw new \RuntimeException(sprintf('Invalid @ORM annotation. The name or entity class must be specified in method "%s".', Reflection::getCalledMethod($method))); } } } throw new ORMAnnotationNotFoundException(sprintf('Not found ORM annotation for parameter "%s" in method "%s".', $parameter->getName(), Reflection::getCalledMethod($method))); }
/** * {@inheritDoc} */ public function handle($method, array $parameters) { try { // Get action and callable $action = $this->actionManager->getAction($method); $callable = $this->callableResolver->resolve($action); // Resolve parameters $parameters = $this->parameterResolver->resolve($action, $callable, $parameters); // Dispatch "pre dispatch" event $event = new ActionDispatchEvent($action, $callable, $parameters); $this->eventDispatcher->dispatch(ApiEvents::ACTION_PRE_DISPATCH, $event); // Call to API method $response = $callable->apply($parameters); if (null === $response) { throw new \RuntimeException(sprintf('The callable "%s" should be return Response or any values. Can not be empty.', Reflection::getCalledMethod($callable->getReflection()))); } if (!$response instanceof ResponseInterface) { // Try transform in listeners. $event = new ActionViewEvent($action, $callable, $parameters, $response); $this->eventDispatcher->dispatch(ApiEvents::ACTION_VIEW, $event); $response = $event->getResponse(); if (!$response) { throw new \RuntimeException(sprintf('Not found response after dispatch view event in API method "%s". ' . 'You must return response or transform response in view event. ' . 'Maybe not found transformer for object?', Reflection::getCalledMethod($callable->getReflection()))); } if (!$response instanceof ResponseInterface) { throw new \RuntimeException(sprintf('The response after dispatch view event must be a ResponseInterface instance, but "%s" given ' . 'for method "%s".', is_object($response) ? get_class($response) : gettype($response))); } } $event = new ActionDispatchEvent($action, $callable, $parameters, $response); $this->eventDispatcher->dispatch(ApiEvents::ACTION_POST_DISPATCH, $event); return $response; } catch (\Exception $e) { $event = new ActionExceptionEvent(isset($action) ? $action : null, $e); $this->eventDispatcher->dispatch(ApiEvents::ACTION_EXCEPTION, $event); if ($event->hasResponse()) { return $event->getResponse(); } throw $e; } }
/** * On pre dispatch event * * @param ActionDispatchEvent $event */ public function onPreDispatch(ActionDispatchEvent $event) { $message = sprintf('Match callable "%s" for action "%s".', Reflection::getCalledMethod($event->getCallable()->getReflection()), $event->getAction()->getName()); $this->logger->debug($message); }