From a93f05987850b0f63dc0ca79a720d176ddf7215e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Sat, 25 Mar 2017 13:43:01 +0100 Subject: [PATCH] [FrameworkBundle] Introduce AbstractController, replacing ControllerTrait --- .../Bundle/FrameworkBundle/CHANGELOG.md | 8 +- .../Controller/AbstractController.php | 69 ++ .../FrameworkBundle/Controller/Controller.php | 381 +--------- .../Controller/ControllerResolver.php | 3 + .../Controller/ControllerTrait.php | 252 +++---- .../Controller/AbstractControllerTest.php | 64 ++ .../Controller/ControllerResolverTest.php | 39 + .../Tests/Controller/ControllerTest.php | 669 +----------------- .../Tests/Controller/ControllerTraitTest.php | 571 +++++++++++---- .../UseControllerTraitController.php | 182 ----- 10 files changed, 736 insertions(+), 1502 deletions(-) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php delete mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Controller/UseControllerTraitController.php diff --git a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md index b740b333acbc..f08aa13ce52e 100644 --- a/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md +++ b/src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG * Added a new new version strategy option called json_manifest_path that allows you to use the `JsonManifestVersionStrategy`. + * Added `Symfony\Bundle\FrameworkBundle\Controller\AbstractController`. It provides the same helpers than the `Controller` class, + but does not allow accessing the dependency injection container, in order to encourage explicit dependency declarations. * Added support for the `controller.service_arguments` tag, for injecting services into controllers' actions * Deprecated `cache:clear` with warmup (always call it with `--no-warmup`) * Deprecated the "framework.trusted_proxies" configuration option and the corresponding "kernel.trusted_proxies" parameter @@ -26,12 +28,6 @@ CHANGELOG Use `Symfony\Component\Console\DependencyInjection\ConfigCachePass` instead. * Deprecated `PropertyInfoPass`, use `Symfony\Component\PropertyInfo\DependencyInjection\PropertyInfoPass` instead * Deprecated extending `ConstraintValidatorFactory` - * Added `Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait` (requires PHP 7). Unlike the `Symfony\Bundle\FrameworkBundle\Controller\Controller` - class, this trait does not have access to the dependency injection container. Its dependencies are explicitly and lazily - injected using getter injection. - `render()`, `renderView()` and `stream()` methods can only use Twig (using the Templating component is not supported). - The `json()` method requires the Serializer component (use `Symfony\Component\HttpFoundation\JsonResponse` directly if - you do not want to use the Serializer). * Deprecated `ControllerArgumentValueResolverPass`. Use `Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass` instead * Deprecated `RoutingResolverPass`, use `Symfony\Component\Routing\DependencyInjection\RoutingResolverPass` instead diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php new file mode 100644 index 000000000000..1055b00e3b1b --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Controller; + +use Psr\Container\ContainerInterface; +use Doctrine\Common\Persistence\ManagerRegistry; +use Symfony\Component\DependencyInjection\ServiceSubscriberInterface; +use Symfony\Component\Form\FormFactoryInterface; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; +use Symfony\Component\Serializer\SerializerInterface; +use Symfony\Component\Templating\EngineInterface; + +/** + * Provides common features needed in controllers. + * + * @author Fabien Potencier + */ +abstract class AbstractController implements ServiceSubscriberInterface +{ + use ControllerTrait; + + private $container; + + /** + * @internal + * @required + */ + public function setContainer(ContainerInterface $container) + { + $previous = $this->container; + $this->container = $container; + + return $previous; + } + + public static function getSubscribedServices() + { + return array( + 'router' => '?'.RouterInterface::class, + 'request_stack' => '?'.RequestStack::class, + 'http_kernel' => '?'.HttpKernelInterface::class, + 'serializer' => '?'.SerializerInterface::class, + 'session' => '?'.SessionInterface::class, + 'security.authorization_checker' => '?'.AuthorizationCheckerInterface::class, + 'templating' => '?'.EngineInterface::class, + 'twig' => '?'.\Twig_Environment::class, + 'doctrine' => '?'.ManagerRegistry::class, + 'form.factory' => '?'.FormFactoryInterface::class, + 'security.token_storage' => '?'.TokenStorageInterface::class, + 'security.csrf.token_manager' => '?'.CsrfTokenManagerInterface::class, + ); + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index 90daa3eeadd0..870965201e39 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -13,21 +13,6 @@ use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; -use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; -use Symfony\Component\Security\Csrf\CsrfToken; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\Form\Form; -use Symfony\Component\Form\FormBuilder; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Doctrine\Bundle\DoctrineBundle\Registry; /** * Controller is a simple implementation of a Controller. @@ -39,354 +24,7 @@ abstract class Controller implements ContainerAwareInterface { use ContainerAwareTrait; - - /** - * Generates a URL from the given parameters. - * - * @param string $route The name of the route - * @param mixed $parameters An array of parameters - * @param int $referenceType The type of reference (one of the constants in UrlGeneratorInterface) - * - * @return string The generated URL - * - * @see UrlGeneratorInterface - */ - protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) - { - return $this->container->get('router')->generate($route, $parameters, $referenceType); - } - - /** - * Forwards the request to another controller. - * - * @param string $controller The controller name (a string like BlogBundle:Post:index) - * @param array $path An array of path parameters - * @param array $query An array of query parameters - * - * @return Response A Response instance - */ - protected function forward($controller, array $path = array(), array $query = array()) - { - $request = $this->container->get('request_stack')->getCurrentRequest(); - $path['_forwarded'] = $request->attributes; - $path['_controller'] = $controller; - $subRequest = $request->duplicate($query, null, $path); - - return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); - } - - /** - * Returns a RedirectResponse to the given URL. - * - * @param string $url The URL to redirect to - * @param int $status The status code to use for the Response - * - * @return RedirectResponse - */ - protected function redirect($url, $status = 302) - { - return new RedirectResponse($url, $status); - } - - /** - * Returns a RedirectResponse to the given route with the given parameters. - * - * @param string $route The name of the route - * @param array $parameters An array of parameters - * @param int $status The status code to use for the Response - * - * @return RedirectResponse - */ - protected function redirectToRoute($route, array $parameters = array(), $status = 302) - { - return $this->redirect($this->generateUrl($route, $parameters), $status); - } - - /** - * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. - * - * @param mixed $data The response data - * @param int $status The status code to use for the Response - * @param array $headers Array of extra headers to add - * @param array $context Context to pass to serializer when using serializer component - * - * @return JsonResponse - */ - protected function json($data, $status = 200, $headers = array(), $context = array()) - { - if ($this->container->has('serializer')) { - $json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array( - 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, - ), $context)); - - return new JsonResponse($json, $status, $headers, true); - } - - return new JsonResponse($data, $status, $headers); - } - - /** - * Returns a BinaryFileResponse object with original or customized file name and disposition header. - * - * @param \SplFileInfo|string $file File object or path to file to be sent as response - * @param string|null $fileName File name to be sent to response or null (will use original file name) - * @param string $disposition Disposition of response ("attachment" is default, other type is "inline") - * - * @return BinaryFileResponse - */ - protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) - { - $response = new BinaryFileResponse($file); - $response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFilename() : $fileName); - - return $response; - } - - /** - * Adds a flash message to the current session for type. - * - * @param string $type The type - * @param string $message The message - * - * @throws \LogicException - */ - protected function addFlash($type, $message) - { - if (!$this->container->has('session')) { - throw new \LogicException('You can not use the addFlash method if sessions are disabled.'); - } - - $this->container->get('session')->getFlashBag()->add($type, $message); - } - - /** - * Checks if the attributes are granted against the current authentication token and optionally supplied object. - * - * @param mixed $attributes The attributes - * @param mixed $object The object - * - * @return bool - * - * @throws \LogicException - */ - protected function isGranted($attributes, $object = null) - { - if (!$this->container->has('security.authorization_checker')) { - throw new \LogicException('The SecurityBundle is not registered in your application.'); - } - - return $this->container->get('security.authorization_checker')->isGranted($attributes, $object); - } - - /** - * Throws an exception unless the attributes are granted against the current authentication token and optionally - * supplied object. - * - * @param mixed $attributes The attributes - * @param mixed $object The object - * @param string $message The message passed to the exception - * - * @throws AccessDeniedException - */ - protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') - { - if (!$this->isGranted($attributes, $object)) { - $exception = $this->createAccessDeniedException($message); - $exception->setAttributes($attributes); - $exception->setSubject($object); - - throw $exception; - } - } - - /** - * Returns a rendered view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * - * @return string The rendered view - */ - protected function renderView($view, array $parameters = array()) - { - if ($this->container->has('templating')) { - return $this->container->get('templating')->render($view, $parameters); - } - - if (!$this->container->has('twig')) { - throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available.'); - } - - return $this->container->get('twig')->render($view, $parameters); - } - - /** - * Renders a view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param Response $response A response instance - * - * @return Response A Response instance - */ - protected function render($view, array $parameters = array(), Response $response = null) - { - if ($this->container->has('templating')) { - return $this->container->get('templating')->renderResponse($view, $parameters, $response); - } - - if (!$this->container->has('twig')) { - throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available.'); - } - - if (null === $response) { - $response = new Response(); - } - - $response->setContent($this->container->get('twig')->render($view, $parameters)); - - return $response; - } - - /** - * Streams a view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param StreamedResponse $response A response instance - * - * @return StreamedResponse A StreamedResponse instance - */ - protected function stream($view, array $parameters = array(), StreamedResponse $response = null) - { - if ($this->container->has('templating')) { - $templating = $this->container->get('templating'); - - $callback = function () use ($templating, $view, $parameters) { - $templating->stream($view, $parameters); - }; - } elseif ($this->container->has('twig')) { - $twig = $this->container->get('twig'); - - $callback = function () use ($twig, $view, $parameters) { - $twig->display($view, $parameters); - }; - } else { - throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available.'); - } - - if (null === $response) { - return new StreamedResponse($callback); - } - - $response->setCallback($callback); - - return $response; - } - - /** - * Returns a NotFoundHttpException. - * - * This will result in a 404 response code. Usage example: - * - * throw $this->createNotFoundException('Page not found!'); - * - * @param string $message A message - * @param \Exception|null $previous The previous exception - * - * @return NotFoundHttpException - */ - protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) - { - return new NotFoundHttpException($message, $previous); - } - - /** - * Returns an AccessDeniedException. - * - * This will result in a 403 response code. Usage example: - * - * throw $this->createAccessDeniedException('Unable to access this page!'); - * - * @param string $message A message - * @param \Exception|null $previous The previous exception - * - * @return AccessDeniedException - */ - protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) - { - return new AccessDeniedException($message, $previous); - } - - /** - * Creates and returns a Form instance from the type of the form. - * - * @param string $type The fully qualified class name of the form type - * @param mixed $data The initial data for the form - * @param array $options Options for the form - * - * @return Form - */ - protected function createForm($type, $data = null, array $options = array()) - { - return $this->container->get('form.factory')->create($type, $data, $options); - } - - /** - * Creates and returns a form builder instance. - * - * @param mixed $data The initial data for the form - * @param array $options Options for the form - * - * @return FormBuilder - */ - protected function createFormBuilder($data = null, array $options = array()) - { - return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); - } - - /** - * Shortcut to return the Doctrine Registry service. - * - * @return Registry - * - * @throws \LogicException If DoctrineBundle is not available - */ - protected function getDoctrine() - { - if (!$this->container->has('doctrine')) { - throw new \LogicException('The DoctrineBundle is not registered in your application.'); - } - - return $this->container->get('doctrine'); - } - - /** - * Get a user from the Security Token Storage. - * - * @return mixed - * - * @throws \LogicException If SecurityBundle is not available - * - * @see TokenInterface::getUser() - */ - protected function getUser() - { - if (!$this->container->has('security.token_storage')) { - throw new \LogicException('The SecurityBundle is not registered in your application.'); - } - - if (null === $token = $this->container->get('security.token_storage')->getToken()) { - return; - } - - if (!is_object($user = $token->getUser())) { - // e.g. anonymous authentication - return; - } - - return $user; - } + use ControllerTrait; /** * Returns true if the service id is defined. @@ -423,21 +61,4 @@ protected function getParameter($name) { return $this->container->getParameter($name); } - - /** - * Checks the validity of a CSRF token. - * - * @param string $id The id used when generating the token - * @param string $token The actual token sent with the request that should be validated - * - * @return bool - */ - protected function isCsrfTokenValid($id, $token) - { - if (!$this->container->has('security.csrf.token_manager')) { - throw new \LogicException('CSRF protection is not enabled in your application.'); - } - - return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); - } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php index a6a7fe8a550d..355f526fdc95 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerResolver.php @@ -62,6 +62,9 @@ protected function instantiateController($class) if ($controller instanceof ContainerAwareInterface) { $controller->setContainer($this->container); } + if ($controller instanceof AbstractController && null !== $previousContainer = $controller->setContainer($this->container)) { + $controller->setContainer($previousContainer); + } return $controller; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php index d67c67c90659..f138186c34d0 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/ControllerTrait.php @@ -11,120 +11,31 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; -use Doctrine\Common\Persistence\ManagerRegistry; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormFactoryInterface; -use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpFoundation\StreamedResponse; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Routing\RouterInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Csrf\CsrfToken; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Form; +use Symfony\Component\Form\FormBuilder; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; -use Symfony\Component\Serializer\SerializerInterface; +use Doctrine\Bundle\DoctrineBundle\Registry; /** * Common features needed in controllers. * - * The recommended way of injecting dependencies is through getter injection. - * - * @author Kévin Dunglas * @author Fabien Potencier * - * @experimental in version 3.3 + * @internal */ trait ControllerTrait { - /** - * @required - */ - protected function getRouter(): RouterInterface - { - } - - /** - * @required - */ - protected function getRequestStack(): RequestStack - { - } - - /** - * @required - */ - protected function getHttpKernel(): HttpKernelInterface - { - } - - /** - * @required - */ - protected function getSerializer(): SerializerInterface - { - } - - /** - * @required - */ - protected function getSession(): SessionInterface - { - } - - /** - * @required - */ - protected function getAuthorizationChecker(): AuthorizationCheckerInterface - { - } - - /** - * @required - */ - protected function getTwig(): \Twig_Environment - { - } - - /** - * @required - */ - protected function getDoctrine(): ManagerRegistry - { - } - - /** - * @required - */ - protected function getFormFactory(): FormFactoryInterface - { - } - - /** - * @required - */ - protected function getTokenStorage(): TokenStorageInterface - { - } - - /** - * @required - */ - protected function getCsrfTokenManager(): CsrfTokenManagerInterface - { - } - /** * Generates a URL from the given parameters. * @@ -136,9 +47,9 @@ protected function getCsrfTokenManager(): CsrfTokenManagerInterface * * @see UrlGeneratorInterface */ - protected function generateUrl(string $route, array $parameters = array(), int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string + protected function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) { - return $this->getRouter()->generate($route, $parameters, $referenceType); + return $this->container->get('router')->generate($route, $parameters, $referenceType); } /** @@ -150,14 +61,14 @@ protected function generateUrl(string $route, array $parameters = array(), int $ * * @return Response A Response instance */ - protected function forward(string $controller, array $path = array(), array $query = array()): Response + protected function forward($controller, array $path = array(), array $query = array()) { - $request = $this->getRequestStack()->getCurrentRequest(); + $request = $this->container->get('request_stack')->getCurrentRequest(); $path['_forwarded'] = $request->attributes; $path['_controller'] = $controller; $subRequest = $request->duplicate($query, null, $path); - return $this->getHttpKernel()->handle($subRequest, HttpKernelInterface::SUB_REQUEST); + return $this->container->get('http_kernel')->handle($subRequest, HttpKernelInterface::SUB_REQUEST); } /** @@ -168,7 +79,7 @@ protected function forward(string $controller, array $path = array(), array $que * * @return RedirectResponse */ - protected function redirect(string $url, int $status = 302): RedirectResponse + protected function redirect($url, $status = 302) { return new RedirectResponse($url, $status); } @@ -182,28 +93,32 @@ protected function redirect(string $url, int $status = 302): RedirectResponse * * @return RedirectResponse */ - protected function redirectToRoute(string $route, array $parameters = array(), int $status = 302): RedirectResponse + protected function redirectToRoute($route, array $parameters = array(), $status = 302) { return $this->redirect($this->generateUrl($route, $parameters), $status); } /** - * Returns a JsonResponse that uses the serializer component. + * Returns a JsonResponse that uses the serializer component if enabled, or json_encode. * * @param mixed $data The response data * @param int $status The status code to use for the Response * @param array $headers Array of extra headers to add - * @param array $context Context to pass to serializer + * @param array $context Context to pass to serializer when using serializer component * * @return JsonResponse */ - protected function json($data, int $status = 200, array $headers = array(), array $context = array()): JsonResponse + protected function json($data, $status = 200, $headers = array(), $context = array()) { - $json = $this->getSerializer()->serialize($data, 'json', array_merge(array( - 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, - ), $context)); + if ($this->container->has('serializer')) { + $json = $this->container->get('serializer')->serialize($data, 'json', array_merge(array( + 'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS, + ), $context)); - return new JsonResponse($json, $status, $headers, true); + return new JsonResponse($json, $status, $headers, true); + } + + return new JsonResponse($data, $status, $headers); } /** @@ -215,7 +130,7 @@ protected function json($data, int $status = 200, array $headers = array(), arra * * @return BinaryFileResponse */ - protected function file($file, string $fileName = null, string $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT): BinaryFileResponse + protected function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) { $response = new BinaryFileResponse($file); $response->setContentDisposition($disposition, $fileName === null ? $response->getFile()->getFilename() : $fileName); @@ -231,13 +146,13 @@ protected function file($file, string $fileName = null, string $disposition = Re * * @throws \LogicException */ - protected function addFlash(string $type, string $message) + protected function addFlash($type, $message) { - $session = $this->getSession(); - if (!$session instanceof Session) { - throw new \LogicException(sprintf('You can not use the addFlash method: "%s" is not an instance of "%s".', get_class($session), Session::class)); + if (!$this->container->has('session')) { + throw new \LogicException('You can not use the addFlash method if sessions are disabled.'); } - $session->getFlashBag()->add($type, $message); + + $this->container->get('session')->getFlashBag()->add($type, $message); } /** @@ -247,10 +162,16 @@ protected function addFlash(string $type, string $message) * @param mixed $object The object * * @return bool + * + * @throws \LogicException */ - protected function isGranted($attributes, $object = null): bool + protected function isGranted($attributes, $object = null) { - return $this->getAuthorizationChecker()->isGranted($attributes, $object); + if (!$this->container->has('security.authorization_checker')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + return $this->container->get('security.authorization_checker')->isGranted($attributes, $object); } /** @@ -263,12 +184,13 @@ protected function isGranted($attributes, $object = null): bool * * @throws AccessDeniedException */ - protected function denyAccessUnlessGranted($attributes, $object = null, string $message = 'Access Denied.') + protected function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') { if (!$this->isGranted($attributes, $object)) { $exception = $this->createAccessDeniedException($message); $exception->setAttributes($attributes); $exception->setSubject($object); + throw $exception; } } @@ -281,9 +203,17 @@ protected function denyAccessUnlessGranted($attributes, $object = null, string $ * * @return string The rendered view */ - protected function renderView(string $view, array $parameters = array()): string + protected function renderView($view, array $parameters = array()) { - return $this->getTwig()->render($view, $parameters); + if ($this->container->has('templating')) { + return $this->container->get('templating')->render($view, $parameters); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "renderView" method if the Templating Component or the Twig Bundle are not available.'); + } + + return $this->container->get('twig')->render($view, $parameters); } /** @@ -295,13 +225,23 @@ protected function renderView(string $view, array $parameters = array()): string * * @return Response A Response instance */ - protected function render(string $view, array $parameters = array(), Response $response = null): Response + protected function render($view, array $parameters = array(), Response $response = null) { + if ($this->container->has('templating')) { + return $this->container->get('templating')->renderResponse($view, $parameters, $response); + } + + if (!$this->container->has('twig')) { + throw new \LogicException('You can not use the "render" method if the Templating Component or the Twig Bundle are not available.'); + } + if (null === $response) { $response = new Response(); } - return $response->setContent($this->getTwig()->render($view, $parameters)); + $response->setContent($this->container->get('twig')->render($view, $parameters)); + + return $response; } /** @@ -313,13 +253,23 @@ protected function render(string $view, array $parameters = array(), Response $r * * @return StreamedResponse A StreamedResponse instance */ - protected function stream(string $view, array $parameters = array(), StreamedResponse $response = null): StreamedResponse + protected function stream($view, array $parameters = array(), StreamedResponse $response = null) { - $twig = $this->getTwig(); - - $callback = function () use ($twig, $view, $parameters) { - $twig->display($view, $parameters); - }; + if ($this->container->has('templating')) { + $templating = $this->container->get('templating'); + + $callback = function () use ($templating, $view, $parameters) { + $templating->stream($view, $parameters); + }; + } elseif ($this->container->has('twig')) { + $twig = $this->container->get('twig'); + + $callback = function () use ($twig, $view, $parameters) { + $twig->display($view, $parameters); + }; + } else { + throw new \LogicException('You can not use the "stream" method if the Templating Component or the Twig Bundle are not available.'); + } if (null === $response) { return new StreamedResponse($callback); @@ -342,7 +292,7 @@ protected function stream(string $view, array $parameters = array(), StreamedRes * * @return NotFoundHttpException */ - protected function createNotFoundException(string $message = 'Not Found', \Exception $previous = null): NotFoundHttpException + protected function createNotFoundException($message = 'Not Found', \Exception $previous = null) { return new NotFoundHttpException($message, $previous); } @@ -359,7 +309,7 @@ protected function createNotFoundException(string $message = 'Not Found', \Excep * * @return AccessDeniedException */ - protected function createAccessDeniedException(string $message = 'Access Denied.', \Exception $previous = null): AccessDeniedException + protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) { return new AccessDeniedException($message, $previous); } @@ -371,11 +321,11 @@ protected function createAccessDeniedException(string $message = 'Access Denied. * @param mixed $data The initial data for the form * @param array $options Options for the form * - * @return FormInterface + * @return Form */ - protected function createForm(string $type, $data = null, array $options = array()): FormInterface + protected function createForm($type, $data = null, array $options = array()) { - return $this->getFormFactory()->create($type, $data, $options); + return $this->container->get('form.factory')->create($type, $data, $options); } /** @@ -384,11 +334,27 @@ protected function createForm(string $type, $data = null, array $options = array * @param mixed $data The initial data for the form * @param array $options Options for the form * - * @return FormBuilderInterface + * @return FormBuilder */ - protected function createFormBuilder($data = null, array $options = array()): FormBuilderInterface + protected function createFormBuilder($data = null, array $options = array()) { - return $this->getFormFactory()->createBuilder(FormType::class, $data, $options); + return $this->container->get('form.factory')->createBuilder(FormType::class, $data, $options); + } + + /** + * Shortcut to return the Doctrine Registry service. + * + * @return Registry + * + * @throws \LogicException If DoctrineBundle is not available + */ + protected function getDoctrine() + { + if (!$this->container->has('doctrine')) { + throw new \LogicException('The DoctrineBundle is not registered in your application.'); + } + + return $this->container->get('doctrine'); } /** @@ -396,11 +362,17 @@ protected function createFormBuilder($data = null, array $options = array()): Fo * * @return mixed * + * @throws \LogicException If SecurityBundle is not available + * * @see TokenInterface::getUser() */ protected function getUser() { - if (null === $token = $this->getTokenStorage()->getToken()) { + if (!$this->container->has('security.token_storage')) { + throw new \LogicException('The SecurityBundle is not registered in your application.'); + } + + if (null === $token = $this->container->get('security.token_storage')->getToken()) { return; } @@ -420,8 +392,12 @@ protected function getUser() * * @return bool */ - protected function isCsrfTokenValid(string $id, string $token): bool + protected function isCsrfTokenValid($id, $token) { - return $this->getCsrfTokenManager()->isTokenValid(new CsrfToken($id, $token)); + if (!$this->container->has('security.csrf.token_manager')) { + throw new \LogicException('CSRF protection is not enabled in your application.'); + } + + return $this->container->get('security.csrf.token_manager')->isTokenValid(new CsrfToken($id, $token)); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php new file mode 100644 index 000000000000..937cbfc7286d --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/AbstractControllerTest.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; + +use Psr\Container\ContainerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\File\File; + +class AbstractControllerTest extends ControllerTraitTest +{ + protected function createController() + { + return new TestAbstractController(); + } +} + +class TestAbstractController extends AbstractController +{ + use TestControllerTrait; + + private $throwOnUnexpectedService; + + public function __construct($throwOnUnexpectedService = true) + { + $this->throwOnUnexpectedService = $throwOnUnexpectedService; + } + + public function setContainer(ContainerInterface $container) + { + if (!$this->throwOnUnexpectedService) { + return parent::setContainer($container); + } + + $expected = self::getSubscribedServices(); + + foreach ($container->getServiceIds() as $id) { + if ('service_container' === $id) { + continue; + } + if (!isset($expected[$id])) { + throw new \UnexpectedValueException(sprintf('Service "%s" is not expected, as declared by %s::getSubscribedServices()', $id, AbstractController::class)); + } + $type = substr($expected[$id], 1); + if (!$container->get($id) instanceof $type) { + throw new \UnexpectedValueException(sprintf('Service "%s" is expected to be an instance of "%s", as declared by %s::getSubscribedServices()', $id, $type, AbstractController::class)); + } + } + + return parent::setContainer($container); + } + + public function fooAction() + { + } +} diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php index 0ccc4f551425..7946a96e8d2c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerResolverTest.php @@ -15,6 +15,7 @@ use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\ControllerNameParser; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; +use Symfony\Component\DependencyInjection\Container; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -67,6 +68,44 @@ public function testGetControllerWithBundleNotation() $this->assertSame('testAction', $controller[1]); } + public function testAbstractControllerGetsContainerWhenNotSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new TestAbstractController(false); + + $container = new Container(); + $container->set(TestAbstractController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); + + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($container, $controller->setContainer($container)); + } + + public function testAbstractControllerGetsNoContainerWhenSet() + { + class_exists(AbstractControllerTest::class); + + $controller = new TestAbstractController(false); + $controllerContainer = new Container(); + $controller->setContainer($controllerContainer); + + $container = new Container(); + $container->set(TestAbstractController::class, $controller); + + $resolver = $this->createControllerResolver(null, $container); + + $request = Request::create('/'); + $request->attributes->set('_controller', TestAbstractController::class.'::fooAction'); + + $this->assertSame(array($controller, 'fooAction'), $resolver->getController($request)); + $this->assertSame($controllerContainer, $controller->setContainer($container)); + } + protected function createControllerResolver(LoggerInterface $logger = null, Psr11ContainerInterface $container = null, ControllerNameParser $parser = null) { if (!$parser) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php index 80908a9e5b91..9fd20649289d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php @@ -11,679 +11,18 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; -use Symfony\Bundle\FrameworkBundle\Tests\TestCase; use Symfony\Bundle\FrameworkBundle\Controller\Controller; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\File\File; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; -use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Serializer\SerializerInterface; -class ControllerTest extends TestCase +class ControllerTest extends ControllerTraitTest { - public function testForward() + protected function createController() { - $request = Request::create('/'); - $request->setLocale('fr'); - $request->setRequestFormat('xml'); - - $requestStack = new RequestStack(); - $requestStack->push($request); - - $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); - $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { - return new Response($request->getRequestFormat().'--'.$request->getLocale()); - })); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('get')->will($this->returnValue($requestStack)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($kernel)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->forward('a_controller'); - $this->assertEquals('xml--fr', $response->getContent()); - } - - public function testGetUser() - { - $user = new User('user', 'pass'); - $token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')); - - $controller = new TestController(); - $controller->setContainer($this->getContainerWithTokenStorage($token)); - - $this->assertSame($controller->getUser(), $user); - } - - public function testGetUserAnonymousUserConvertedToNull() - { - $token = new AnonymousToken('default', 'anon.'); - - $controller = new TestController(); - $controller->setContainer($this->getContainerWithTokenStorage($token)); - - $this->assertNull($controller->getUser()); - } - - public function testGetUserWithEmptyTokenStorage() - { - $controller = new TestController(); - $controller->setContainer($this->getContainerWithTokenStorage(null)); - - $this->assertNull($controller->getUser()); - } - - /** - * @expectedException \LogicException - * @expectedExceptionMessage The SecurityBundle is not registered in your application. - */ - public function testGetUserWithEmptyContainer() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container - ->expects($this->once()) - ->method('has') - ->with('security.token_storage') - ->will($this->returnValue(false)); - - $controller = new TestController(); - $controller->setContainer($container); - - $controller->getUser(); - } - - /** - * @param $token - * - * @return ContainerInterface - */ - private function getContainerWithTokenStorage($token = null) - { - $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage')->getMock(); - $tokenStorage - ->expects($this->once()) - ->method('getToken') - ->will($this->returnValue($token)); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container - ->expects($this->once()) - ->method('has') - ->with('security.token_storage') - ->will($this->returnValue(true)); - - $container - ->expects($this->once()) - ->method('get') - ->with('security.token_storage') - ->will($this->returnValue($tokenStorage)); - - return $container; - } - - public function testJson() - { - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $container - ->expects($this->once()) - ->method('has') - ->with('serializer') - ->will($this->returnValue(false)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->json(array()); - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals('[]', $response->getContent()); - } - - public function testJsonWithSerializer() - { - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $container - ->expects($this->once()) - ->method('has') - ->with('serializer') - ->will($this->returnValue(true)); - - $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); - $serializer - ->expects($this->once()) - ->method('serialize') - ->with(array(), 'json', array('json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS)) - ->will($this->returnValue('[]')); - - $container - ->expects($this->once()) - ->method('get') - ->with('serializer') - ->will($this->returnValue($serializer)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->json(array()); - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals('[]', $response->getContent()); - } - - public function testJsonWithSerializerContextOverride() - { - $container = $this->getMockBuilder(ContainerInterface::class)->getMock(); - $container - ->expects($this->once()) - ->method('has') - ->with('serializer') - ->will($this->returnValue(true)); - - $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); - $serializer - ->expects($this->once()) - ->method('serialize') - ->with(array(), 'json', array('json_encode_options' => 0, 'other' => 'context')) - ->will($this->returnValue('[]')); - - $container - ->expects($this->once()) - ->method('get') - ->with('serializer') - ->will($this->returnValue($serializer)); - - $controller = new TestController(); - $controller->setContainer($container); - - $response = $controller->json(array(), 200, array(), array('json_encode_options' => 0, 'other' => 'context')); - $this->assertInstanceOf(JsonResponse::class, $response); - $this->assertEquals('[]', $response->getContent()); - $response->setEncodingOptions(JSON_FORCE_OBJECT); - $this->assertEquals('{}', $response->getContent()); - } - - public function testFile() - { - /* @var ContainerInterface $container */ - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); - $container->set('kernel', $kernel); - - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(new File(__FILE__)); - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); - } - - public function testFileAsInline() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); - $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); - } - - public function testFileWithOwnFileName() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $fileName = 'test.php'; - $response = $controller->file(new File(__FILE__), $fileName); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains($fileName, $response->headers->get('content-disposition')); - } - - public function testFileWithOwnFileNameAsInline() - { - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $controller = new TestController(); - $controller->setContainer($container); - - /* @var BinaryFileResponse $response */ - $fileName = 'test.php'; - $response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); - $this->assertContains($fileName, $response->headers->get('content-disposition')); - } - - public function testFileFromPath() - { - $controller = new TestController(); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(__FILE__); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); - } - - public function testFileFromPathWithCustomizedFileName() - { - $controller = new TestController(); - - /* @var BinaryFileResponse $response */ - $response = $controller->file(__FILE__, 'test.php'); - - $this->assertInstanceOf(BinaryFileResponse::class, $response); - $this->assertSame(200, $response->getStatusCode()); - if ($response->headers->get('content-type')) { - $this->assertSame('text/x-php', $response->headers->get('content-type')); - } - $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); - $this->assertContains('test.php', $response->headers->get('content-disposition')); - } - - /** - * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException - */ - public function testFileWhichDoesNotExist() - { - $controller = new TestController(); - - /* @var BinaryFileResponse $response */ - $response = $controller->file('some-file.txt', 'test.php'); - } - - public function testIsGranted() - { - $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); - $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($authorizationChecker)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertTrue($controller->isGranted('foo')); - } - - /** - * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException - */ - public function testdenyAccessUnlessGranted() - { - $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); - $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($authorizationChecker)); - - $controller = new TestController(); - $controller->setContainer($container); - - $controller->denyAccessUnlessGranted('foo'); - } - - public function testRenderViewTwig() - { - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->renderView('foo')); - } - - public function testRenderTwig() - { - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - $twig->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->render('foo')->getContent()); - } - - public function testStreamTwig() - { - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(false)); - $container->expects($this->at(1))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(2))->method('get')->will($this->returnValue($twig)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); - } - - public function testRedirectToRoute() - { - $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); - $router->expects($this->once())->method('generate')->willReturn('/foo'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('get')->will($this->returnValue($router)); - - $controller = new TestController(); - $controller->setContainer($container); - $response = $controller->redirectToRoute('foo'); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertSame('/foo', $response->getTargetUrl()); - $this->assertSame(302, $response->getStatusCode()); - } - - public function testAddFlash() - { - $flashBag = new FlashBag(); - $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->getMock(); - $session->expects($this->once())->method('getFlashBag')->willReturn($flashBag); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($session)); - - $controller = new TestController(); - $controller->setContainer($container); - $controller->addFlash('foo', 'bar'); - - $this->assertSame(array('bar'), $flashBag->get('foo')); - } - - public function testCreateAccessDeniedException() - { - $controller = new TestController(); - - $this->assertInstanceOf('Symfony\Component\Security\Core\Exception\AccessDeniedException', $controller->createAccessDeniedException()); - } - - public function testIsCsrfTokenValid() - { - $tokenManager = $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock(); - $tokenManager->expects($this->once())->method('isTokenValid')->willReturn(true); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($tokenManager)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertTrue($controller->isCsrfTokenValid('foo', 'bar')); - } - - public function testGenerateUrl() - { - $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); - $router->expects($this->once())->method('generate')->willReturn('/foo'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('get')->will($this->returnValue($router)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('/foo', $controller->generateUrl('foo')); - } - - public function testRedirect() - { - $controller = new TestController(); - $response = $controller->redirect('http://dunglas.fr', 301); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); - $this->assertSame('http://dunglas.fr', $response->getTargetUrl()); - $this->assertSame(301, $response->getStatusCode()); - } - - public function testRenderViewTemplating() - { - $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); - $templating->expects($this->once())->method('render')->willReturn('bar'); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->renderView('foo')); - } - - public function testRenderTemplating() - { - $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); - $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals('bar', $controller->render('foo')->getContent()); - } - - public function testStreamTemplating() - { - $templating = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->willReturn(true); - $container->expects($this->at(1))->method('get')->will($this->returnValue($templating)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); - } - - public function testCreateNotFoundException() - { - $controller = new TestController(); - - $this->assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $controller->createNotFoundException()); - } - - public function testCreateForm() - { - $form = $this->getMockBuilder('Symfony\Component\Form\FormInterface')->getMock(); - - $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); - $formFactory->expects($this->once())->method('create')->willReturn($form); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('get')->will($this->returnValue($formFactory)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals($form, $controller->createForm('foo')); - } - - public function testCreateFormBuilder() - { - $formBuilder = $this->getMockBuilder('Symfony\Component\Form\FormBuilderInterface')->getMock(); - - $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); - $formFactory->expects($this->once())->method('createBuilder')->willReturn($formBuilder); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('get')->will($this->returnValue($formFactory)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals($formBuilder, $controller->createFormBuilder('foo')); - } - - public function testGetDoctrine() - { - $doctrine = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); - - $container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerInterface')->getMock(); - $container->expects($this->at(0))->method('has')->will($this->returnValue(true)); - $container->expects($this->at(1))->method('get')->will($this->returnValue($doctrine)); - - $controller = new TestController(); - $controller->setContainer($container); - - $this->assertEquals($doctrine, $controller->getDoctrine()); + return new TestController(); } } class TestController extends Controller { - public function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) - { - return parent::generateUrl($route, $parameters, $referenceType); - } - - public function redirect($url, $status = 302) - { - return parent::redirect($url, $status); - } - - public function forward($controller, array $path = array(), array $query = array()) - { - return parent::forward($controller, $path, $query); - } - - public function getUser() - { - return parent::getUser(); - } - - public function json($data, $status = 200, $headers = array(), $context = array()) - { - return parent::json($data, $status, $headers, $context); - } - - public function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) - { - return parent::file($file, $fileName, $disposition); - } - - public function isGranted($attributes, $object = null) - { - return parent::isGranted($attributes, $object); - } - - public function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') - { - parent::denyAccessUnlessGranted($attributes, $object, $message); - } - - public function redirectToRoute($route, array $parameters = array(), $status = 302) - { - return parent::redirectToRoute($route, $parameters, $status); - } - - public function addFlash($type, $message) - { - parent::addFlash($type, $message); - } - - public function isCsrfTokenValid($id, $token) - { - return parent::isCsrfTokenValid($id, $token); - } - - public function renderView($view, array $parameters = array()) - { - return parent::renderView($view, $parameters); - } - - public function render($view, array $parameters = array(), Response $response = null) - { - return parent::render($view, $parameters, $response); - } - - public function stream($view, array $parameters = array(), StreamedResponse $response = null) - { - return parent::stream($view, $parameters, $response); - } - - public function createNotFoundException($message = 'Not Found', \Exception $previous = null) - { - return parent::createNotFoundException($message, $previous); - } - - public function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) - { - return parent::createAccessDeniedException($message, $previous); - } - - public function createForm($type, $data = null, array $options = array()) - { - return parent::createForm($type, $data, $options); - } - - public function createFormBuilder($data = null, array $options = array()) - { - return parent::createFormBuilder($data, $options); - } - - public function getDoctrine() - { - return parent::getDoctrine(); - } + use TestControllerTrait; } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php index ccbd50794bef..2bd18aa44145 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTraitTest.php @@ -11,49 +11,26 @@ namespace Symfony\Bundle\FrameworkBundle\Tests\Controller; -use Doctrine\Common\Persistence\ManagerRegistry; -use PHPUnit\Framework\TestCase as PHPUnitTestCase; -use Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Controller\UseControllerTraitController; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormFactoryInterface; -use Symfony\Component\Form\FormInterface; +use Symfony\Bundle\FrameworkBundle\Tests\TestCase; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Symfony\Component\HttpFoundation\File\File; use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\ResponseHeaderBag; use Symfony\Component\HttpFoundation\Session\Flash\FlashBag; -use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\HttpFoundation\StreamedResponse; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Routing\RouterInterface; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\User\User; -use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; use Symfony\Component\Serializer\SerializerInterface; -/** - * @author Kévin Dunglas - * - * @requires PHP 7 - */ -class ControllerTraitTest extends PHPUnitTestCase +abstract class ControllerTraitTest extends TestCase { - public function testGenerateUrl() - { - $router = $this->getMockBuilder(RouterInterface::class)->getMock(); - $router->expects($this->once())->method('generate')->willReturn('/foo'); - - $controller = new UseControllerTraitController(); - $controller->setRouter($router); - - $this->assertEquals('/foo', $controller->generateUrl('foo')); - } + abstract protected function createController(); public function testForward() { @@ -64,91 +41,96 @@ public function testForward() $requestStack = new RequestStack(); $requestStack->push($request); - $httpKernel = $this->getMockBuilder(HttpKernelInterface::class)->getMock(); - $httpKernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $kernel->expects($this->once())->method('handle')->will($this->returnCallback(function (Request $request) { return new Response($request->getRequestFormat().'--'.$request->getLocale()); })); - $controller = new UseControllerTraitController(); - $controller->setRequestStack($requestStack); - $controller->setHttpKernel($httpKernel); + $container = new Container(); + $container->set('request_stack', $requestStack); + $container->set('http_kernel', $kernel); + + $controller = $this->createController(); + $controller->setContainer($container); $response = $controller->forward('a_controller'); $this->assertEquals('xml--fr', $response->getContent()); } - public function testRedirect() + public function testGetUser() { - $controller = new UseControllerTraitController(); + $user = new User('user', 'pass'); + $token = new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')); - $response = $controller->redirect('http://example.com', 301); + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertSame('http://example.com', $response->getTargetUrl()); - $this->assertSame(301, $response->getStatusCode()); + $this->assertSame($controller->getUser(), $user); } - public function testRedirectToRoute() + public function testGetUserAnonymousUserConvertedToNull() { - $router = $this->getMockBuilder(RouterInterface::class)->getMock(); - $router->expects($this->once())->method('generate')->willReturn('/foo'); - - $controller = new UseControllerTraitController(); - $controller->setRouter($router); + $token = new AnonymousToken('default', 'anon.'); - $response = $controller->redirectToRoute('foo'); + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage($token)); - $this->assertInstanceOf(RedirectResponse::class, $response); - $this->assertSame('/foo', $response->getTargetUrl()); - $this->assertSame(302, $response->getStatusCode()); + $this->assertNull($controller->getUser()); } - public function testGetUser() + public function testGetUserWithEmptyTokenStorage() { - $user = new User('user', 'pass'); + $controller = $this->createController(); + $controller->setContainer($this->getContainerWithTokenStorage(null)); - $tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); - $tokenStorage - ->expects($this->once()) - ->method('getToken') - ->will($this->returnValue(new UsernamePasswordToken($user, 'pass', 'default', array('ROLE_USER')))); + $this->assertNull($controller->getUser()); + } - $controller = new UseControllerTraitController(); - $controller->setTokenStorage($tokenStorage); + /** + * @expectedException \LogicException + * @expectedExceptionMessage The SecurityBundle is not registered in your application. + */ + public function testGetUserWithEmptyContainer() + { + $controller = $this->createController(); + $controller->setContainer(new Container()); - $this->assertSame($controller->getUser(), $user); + $controller->getUser(); } - public function testGetUserAnonymousUserConvertedToNull() + /** + * @param $token + * + * @return Container + */ + private function getContainerWithTokenStorage($token = null) { - $tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); + $tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage')->getMock(); $tokenStorage ->expects($this->once()) ->method('getToken') - ->will($this->returnValue(new AnonymousToken('default', 'anon.'))); + ->will($this->returnValue($token)); - $controller = new UseControllerTraitController(); - $controller->setTokenStorage($tokenStorage); + $container = new Container(); + $container->set('security.token_storage', $tokenStorage); - $this->assertNull($controller->getUser()); + return $container; } - public function testGetUserWithEmptyTokenStorage() + public function testJson() { - $tokenStorage = $this->getMockBuilder(TokenStorageInterface::class)->getMock(); - $tokenStorage - ->expects($this->once()) - ->method('getToken') - ->will($this->returnValue(null)); - - $controller = new UseControllerTraitController(); - $controller->setTokenStorage($tokenStorage); + $controller = $this->createController(); + $controller->setContainer(new Container()); - $this->assertNull($controller->getUser()); + $response = $controller->json(array()); + $this->assertInstanceOf(JsonResponse::class, $response); + $this->assertEquals('[]', $response->getContent()); } public function testJsonWithSerializer() { + $container = new Container(); + $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); $serializer ->expects($this->once()) @@ -156,8 +138,10 @@ public function testJsonWithSerializer() ->with(array(), 'json', array('json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS)) ->will($this->returnValue('[]')); - $controller = new UseControllerTraitController(); - $controller->setSerializer($serializer); + $container->set('serializer', $serializer); + + $controller = $this->createController(); + $controller->setContainer($container); $response = $controller->json(array()); $this->assertInstanceOf(JsonResponse::class, $response); @@ -166,6 +150,8 @@ public function testJsonWithSerializer() public function testJsonWithSerializerContextOverride() { + $container = new Container(); + $serializer = $this->getMockBuilder(SerializerInterface::class)->getMock(); $serializer ->expects($this->once()) @@ -173,8 +159,10 @@ public function testJsonWithSerializerContextOverride() ->with(array(), 'json', array('json_encode_options' => 0, 'other' => 'context')) ->will($this->returnValue('[]')); - $controller = new UseControllerTraitController(); - $controller->setSerializer($serializer); + $container->set('serializer', $serializer); + + $controller = $this->createController(); + $controller->setContainer($container); $response = $controller->json(array(), 200, array(), array('json_encode_options' => 0, 'other' => 'context')); $this->assertInstanceOf(JsonResponse::class, $response); @@ -183,28 +171,129 @@ public function testJsonWithSerializerContextOverride() $this->assertEquals('{}', $response->getContent()); } - public function testAddFlash() + public function testFile() { - $flashBag = new FlashBag(); + $container = new Container(); + $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock(); + $container->set('http_kernel', $kernel); + + $controller = $this->createController(); + $controller->setContainer($container); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(new File(__FILE__)); + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } - $session = $this->getMockBuilder(Session::class)->getMock(); - $session->method('getFlashBag')->willReturn($flashBag); + public function testFileAsInline() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(new File(__FILE__), null, ResponseHeaderBag::DISPOSITION_INLINE); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } - $controller = new UseControllerTraitController(); - $controller->setSession($session); + public function testFileWithOwnFileName() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $fileName = 'test.php'; + $response = $controller->file(new File(__FILE__), $fileName); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains($fileName, $response->headers->get('content-disposition')); + } - $controller->addFlash('foo', 'bar'); + public function testFileWithOwnFileNameAsInline() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $fileName = 'test.php'; + $response = $controller->file(new File(__FILE__), $fileName, ResponseHeaderBag::DISPOSITION_INLINE); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_INLINE, $response->headers->get('content-disposition')); + $this->assertContains($fileName, $response->headers->get('content-disposition')); + } - $this->assertSame(array('bar'), $flashBag->get('foo')); + public function testFileFromPath() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(__FILE__); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains(basename(__FILE__), $response->headers->get('content-disposition')); + } + + public function testFileFromPathWithCustomizedFileName() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file(__FILE__, 'test.php'); + + $this->assertInstanceOf(BinaryFileResponse::class, $response); + $this->assertSame(200, $response->getStatusCode()); + if ($response->headers->get('content-type')) { + $this->assertSame('text/x-php', $response->headers->get('content-type')); + } + $this->assertContains(ResponseHeaderBag::DISPOSITION_ATTACHMENT, $response->headers->get('content-disposition')); + $this->assertContains('test.php', $response->headers->get('content-disposition')); + } + + /** + * @expectedException \Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException + */ + public function testFileWhichDoesNotExist() + { + $controller = $this->createController(); + + /* @var BinaryFileResponse $response */ + $response = $controller->file('some-file.txt', 'test.php'); } public function testIsGranted() { - $authorizationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock(); + $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true); - $controller = new UseControllerTraitController(); - $controller->setAuthorizationChecker($authorizationChecker); + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); $this->assertTrue($controller->isGranted('foo')); } @@ -212,107 +301,327 @@ public function testIsGranted() /** * @expectedException \Symfony\Component\Security\Core\Exception\AccessDeniedException */ - public function testDenyAccessUnlessGranted() + public function testdenyAccessUnlessGranted() { - $authorizationChecker = $this->getMockBuilder(AuthorizationCheckerInterface::class)->getMock(); + $authorizationChecker = $this->getMockBuilder('Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface')->getMock(); $authorizationChecker->expects($this->once())->method('isGranted')->willReturn(false); - $controller = new UseControllerTraitController(); - $controller->setAuthorizationChecker($authorizationChecker); + $container = new Container(); + $container->set('security.authorization_checker', $authorizationChecker); + + $controller = $this->createController(); + $controller->setContainer($container); $controller->denyAccessUnlessGranted('foo'); } - public function testRenderView() + public function testRenderViewTwig() { - $twig = $this->getMockBuilder(\Twig_Environment::class)->disableOriginalConstructor()->getMock(); + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); $twig->expects($this->once())->method('render')->willReturn('bar'); - $controller = new UseControllerTraitController(); - $controller->setTwig($twig); + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); $this->assertEquals('bar', $controller->renderView('foo')); } public function testRenderTwig() { - $twig = $this->getMockBuilder(\Twig_Environment::class)->disableOriginalConstructor()->getMock(); + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); $twig->expects($this->once())->method('render')->willReturn('bar'); - $controller = new UseControllerTraitController(); - $controller->setTwig($twig); + $container = new Container(); + $container->set('twig', $twig); + + $controller = $this->createController(); + $controller->setContainer($container); $this->assertEquals('bar', $controller->render('foo')->getContent()); } public function testStreamTwig() { - $twig = $this->getMockBuilder(\Twig_Environment::class)->disableOriginalConstructor()->getMock(); + $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); - $controller = new UseControllerTraitController(); - $controller->setTwig($twig); + $container = new Container(); + $container->set('twig', $twig); - $this->assertInstanceOf(StreamedResponse::class, $controller->stream('foo')); + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); } - public function testCreateNotFoundException() + public function testRedirectToRoute() { - $controller = new UseControllerTraitController(); + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router->expects($this->once())->method('generate')->willReturn('/foo'); + + $container = new Container(); + $container->set('router', $router); + + $controller = $this->createController(); + $controller->setContainer($container); + $response = $controller->redirectToRoute('foo'); - $this->assertInstanceOf(NotFoundHttpException::class, $controller->createNotFoundException()); + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame('/foo', $response->getTargetUrl()); + $this->assertSame(302, $response->getStatusCode()); + } + + public function testAddFlash() + { + $flashBag = new FlashBag(); + $session = $this->getMockBuilder('Symfony\Component\HttpFoundation\Session\Session')->getMock(); + $session->expects($this->once())->method('getFlashBag')->willReturn($flashBag); + + $container = new Container(); + $container->set('session', $session); + + $controller = $this->createController(); + $controller->setContainer($container); + $controller->addFlash('foo', 'bar'); + + $this->assertSame(array('bar'), $flashBag->get('foo')); } public function testCreateAccessDeniedException() { - $controller = new UseControllerTraitController(); + $controller = $this->createController(); - $this->assertInstanceOf(AccessDeniedException::class, $controller->createAccessDeniedException()); + $this->assertInstanceOf('Symfony\Component\Security\Core\Exception\AccessDeniedException', $controller->createAccessDeniedException()); + } + + public function testIsCsrfTokenValid() + { + $tokenManager = $this->getMockBuilder('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface')->getMock(); + $tokenManager->expects($this->once())->method('isTokenValid')->willReturn(true); + + $container = new Container(); + $container->set('security.csrf.token_manager', $tokenManager); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertTrue($controller->isCsrfTokenValid('foo', 'bar')); + } + + public function testGenerateUrl() + { + $router = $this->getMockBuilder('Symfony\Component\Routing\RouterInterface')->getMock(); + $router->expects($this->once())->method('generate')->willReturn('/foo'); + + $container = new Container(); + $container->set('router', $router); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('/foo', $controller->generateUrl('foo')); + } + + public function testRedirect() + { + $controller = $this->createController(); + $response = $controller->redirect('http://dunglas.fr', 301); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\RedirectResponse', $response); + $this->assertSame('http://dunglas.fr', $response->getTargetUrl()); + $this->assertSame(301, $response->getStatusCode()); + } + + public function testRenderViewTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('render')->willReturn('bar'); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->renderView('foo')); + } + + public function testRenderTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + $templating->expects($this->once())->method('renderResponse')->willReturn(new Response('bar')); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertEquals('bar', $controller->render('foo')->getContent()); + } + + public function testStreamTemplating() + { + $templating = $this->getMockBuilder('Symfony\Bundle\FrameworkBundle\Templating\EngineInterface')->getMock(); + + $container = new Container(); + $container->set('templating', $templating); + + $controller = $this->createController(); + $controller->setContainer($container); + + $this->assertInstanceOf('Symfony\Component\HttpFoundation\StreamedResponse', $controller->stream('foo')); + } + + public function testCreateNotFoundException() + { + $controller = $this->createController(); + + $this->assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $controller->createNotFoundException()); } public function testCreateForm() { - $form = $this->getMockBuilder(FormInterface::class)->getMock(); + $form = $this->getMockBuilder('Symfony\Component\Form\FormInterface')->getMock(); - $formFactory = $this->getMockBuilder(FormFactoryInterface::class)->getMock(); + $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); $formFactory->expects($this->once())->method('create')->willReturn($form); - $controller = new UseControllerTraitController(); - $controller->setFormFactory($formFactory); + $container = new Container(); + $container->set('form.factory', $formFactory); + + $controller = $this->createController(); + $controller->setContainer($container); $this->assertEquals($form, $controller->createForm('foo')); } public function testCreateFormBuilder() { - $formBuilder = $this->getMockBuilder(FormBuilderInterface::class)->getMock(); + $formBuilder = $this->getMockBuilder('Symfony\Component\Form\FormBuilderInterface')->getMock(); - $formFactory = $this->getMockBuilder(FormFactoryInterface::class)->getMock(); + $formFactory = $this->getMockBuilder('Symfony\Component\Form\FormFactoryInterface')->getMock(); $formFactory->expects($this->once())->method('createBuilder')->willReturn($formBuilder); - $controller = new UseControllerTraitController(); - $controller->setFormFactory($formFactory); + $container = new Container(); + $container->set('form.factory', $formFactory); + + $controller = $this->createController(); + $controller->setContainer($container); $this->assertEquals($formBuilder, $controller->createFormBuilder('foo')); } public function testGetDoctrine() { - $doctrine = $this->getMockBuilder(ManagerRegistry::class)->getMock(); + $doctrine = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock(); + + $container = new Container(); + $container->set('doctrine', $doctrine); - $controller = new UseControllerTraitController(); - $controller->setDoctrine($doctrine); + $controller = $this->createController(); + $controller->setContainer($container); - $this->assertSame($doctrine, $controller->getDoctrine()); + $this->assertEquals($doctrine, $controller->getDoctrine()); } +} - public function testIsCsrfTokenValid() +trait TestControllerTrait +{ + public function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) { - $csrfTokenManager = $this->getMockBuilder(CsrfTokenManagerInterface::class)->getMock(); - $csrfTokenManager->expects($this->once())->method('isTokenValid')->willReturn(true); + return parent::generateUrl($route, $parameters, $referenceType); + } - $controller = new UseControllerTraitController(); - $controller->setCsrfTokenManager($csrfTokenManager); + public function redirect($url, $status = 302) + { + return parent::redirect($url, $status); + } - $this->assertTrue($controller->isCsrfTokenValid('foo', 'bar')); + public function forward($controller, array $path = array(), array $query = array()) + { + return parent::forward($controller, $path, $query); + } + + public function getUser() + { + return parent::getUser(); + } + + public function json($data, $status = 200, $headers = array(), $context = array()) + { + return parent::json($data, $status, $headers, $context); + } + + public function file($file, $fileName = null, $disposition = ResponseHeaderBag::DISPOSITION_ATTACHMENT) + { + return parent::file($file, $fileName, $disposition); + } + + public function isGranted($attributes, $object = null) + { + return parent::isGranted($attributes, $object); + } + + public function denyAccessUnlessGranted($attributes, $object = null, $message = 'Access Denied.') + { + parent::denyAccessUnlessGranted($attributes, $object, $message); + } + + public function redirectToRoute($route, array $parameters = array(), $status = 302) + { + return parent::redirectToRoute($route, $parameters, $status); + } + + public function addFlash($type, $message) + { + parent::addFlash($type, $message); + } + + public function isCsrfTokenValid($id, $token) + { + return parent::isCsrfTokenValid($id, $token); + } + + public function renderView($view, array $parameters = array()) + { + return parent::renderView($view, $parameters); + } + + public function render($view, array $parameters = array(), Response $response = null) + { + return parent::render($view, $parameters, $response); + } + + public function stream($view, array $parameters = array(), StreamedResponse $response = null) + { + return parent::stream($view, $parameters, $response); + } + + public function createNotFoundException($message = 'Not Found', \Exception $previous = null) + { + return parent::createNotFoundException($message, $previous); + } + + public function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null) + { + return parent::createAccessDeniedException($message, $previous); + } + + public function createForm($type, $data = null, array $options = array()) + { + return parent::createForm($type, $data, $options); + } + + public function createFormBuilder($data = null, array $options = array()) + { + return parent::createFormBuilder($data, $options); + } + + public function getDoctrine() + { + return parent::getDoctrine(); } } diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Controller/UseControllerTraitController.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Controller/UseControllerTraitController.php deleted file mode 100644 index aa53655a74e5..000000000000 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Fixtures/Controller/UseControllerTraitController.php +++ /dev/null @@ -1,182 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bundle\FrameworkBundle\Tests\Fixtures\Controller; - -use Doctrine\Common\Persistence\ManagerRegistry; -use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait; -use Symfony\Component\Form\FormFactoryInterface; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\HttpFoundation\Session\Session; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Routing\RouterInterface; -use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; -use Symfony\Component\Serializer\SerializerInterface; - -/** - * @author Kévin Dunglas - */ -class UseControllerTraitController -{ - use ControllerTrait { - getRouter as traitGetRouter; - getRequestStack as traitGetRequestStack; - getHttpKernel as traitGetHttpKernel; - getSerializer as traitGetSerializer; - getSession as traitGetSession; - getAuthorizationChecker as traitGetAuthorizationChecker; - getTwig as traitGetTwig; - getDoctrine as traitGetDoctrine; - getFormFactory as traitGetFormFactory; - getTokenStorage as traitGetTokenStorage; - getCsrfTokenManager as traitGetCsrfTokenManager; - - generateUrl as public; - forward as public; - redirect as public; - redirectToRoute as public; - json as public; - file as public; - addFlash as public; - isGranted as public; - denyAccessUnlessGranted as public; - renderView as public; - render as public; - stream as public; - createNotFoundException as public; - createAccessDeniedException as public; - createForm as public; - createFormBuilder as public; - getUser as public; - isCsrfTokenValid as public; - } - - private $router; - private $httpKernel; - private $serializer; - private $authorizationChecker; - private $session; - private $twig; - private $doctrine; - private $formFactory; - - public function setRouter(RouterInterface $router) - { - $this->router = $router; - } - - protected function getRouter(): RouterInterface - { - return $this->router ?? $this->traitGetRouter(); - } - - public function setRequestStack(RequestStack $requestStack) - { - $this->requestStack = $requestStack; - } - - protected function getRequestStack(): RequestStack - { - return $this->requestStack ?? $this->traitGetRequestStack(); - } - - public function setHttpKernel(HttpKernelInterface $httpKernel) - { - $this->httpKernel = $httpKernel; - } - - protected function getHttpKernel(): HttpKernelInterface - { - return $this->httpKernel ?? $this->traitGetHttpKernel(); - } - - public function setSerializer(SerializerInterface $serializer) - { - $this->serializer = $serializer; - } - - protected function getSerializer(): SerializerInterface - { - return $this->serializer ?? $this->traitGetSerializer(); - } - - public function setSession(Session $session) - { - $this->session = $session; - } - - protected function getSession(): Session - { - return $this->session ?? $this->traitGetSession(); - } - - public function setAuthorizationChecker(AuthorizationCheckerInterface $authorizationChecker) - { - $this->authorizationChecker = $authorizationChecker; - } - - protected function getAuthorizationChecker(): AuthorizationCheckerInterface - { - return $this->authorizationChecker ?? $this->traitGetAuthorizationChecker(); - } - - public function setTwig(\Twig_Environment $twig) - { - $this->twig = $twig; - } - - protected function getTwig(): \Twig_Environment - { - return $this->twig ?? $this->traitGetTwig(); - } - - public function setDoctrine(ManagerRegistry $doctrine) - { - $this->doctrine = $doctrine; - } - - public function getDoctrine(): ManagerRegistry - { - return $this->doctrine ?? $this->traitGetDoctrine(); - } - - public function setFormFactory(FormFactoryInterface $formFactory) - { - $this->formFactory = $formFactory; - } - - protected function getFormFactory(): FormFactoryInterface - { - return $this->formFactory ?? $this->traitGetFormFactory(); - } - - public function setTokenStorage(TokenStorageInterface $tokenStorage) - { - $this->tokenStorage = $tokenStorage; - } - - protected function getTokenStorage(): TokenStorageInterface - { - return $this->tokenStorage ?? $this->traitGetTokenStorage(); - } - - public function setCsrfTokenManager(CsrfTokenManagerInterface $csrfTokenManager) - { - $this->csrfTokenManager = $csrfTokenManager; - } - - protected function getCsrfTokenManager(): CsrfTokenManagerInterface - { - return $this->csrfTokenManager ?? $this->traitGetCsrfTokenManager(); - } -}