diff --git a/.travis.yml b/.travis.yml index 94c38873b..05be3715e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,6 @@ matrix: env: COMPOSER_FLAGS="--prefer-lowest" SYMFONY_DEPRECATIONS_HELPER=weak - php: 7.0 env: SYMFONY_VERSION='2.8.*' - - php: 7.0 - env: SYMFONY_VERSION='3.0.*' - php: 7.0 env: SYMFONY_VERSION='3.1.*' diff --git a/Controller/ExceptionController.php b/Controller/ExceptionController.php index 76980462b..6a5ada2cd 100644 --- a/Controller/ExceptionController.php +++ b/Controller/ExceptionController.php @@ -114,10 +114,7 @@ protected function getStatusCode(\Exception $exception) } /** - * Determines the parameters to pass to the view layer. - * - * Overwrite it in a custom ExceptionController class to add additionally parameters - * that should be passed to the view layer. + * Determines the template parameters to pass to the view layer. * * @param string $currentContent * @param int $code diff --git a/DependencyInjection/FOSRestExtension.php b/DependencyInjection/FOSRestExtension.php index 17b8de982..b47757322 100644 --- a/DependencyInjection/FOSRestExtension.php +++ b/DependencyInjection/FOSRestExtension.php @@ -16,6 +16,8 @@ use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\FormType; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\HttpFoundation\Response; @@ -88,7 +90,16 @@ private function loadForm(array $config, XmlFileLoader $loader, ContainerBuilder { if (!empty($config['disable_csrf_role'])) { $loader->load('forms.xml'); - $container->getDefinition('fos_rest.form.extension.csrf_disable')->replaceArgument(1, $config['disable_csrf_role']); + + $definition = $container->getDefinition('fos_rest.form.extension.csrf_disable'); + $definition->replaceArgument(1, $config['disable_csrf_role']); + + // BC for Symfony < 2.8: the extended_type attribute is used on higher versions + if (!method_exists(AbstractType::class, 'getBlockPrefix')) { + $definition->addTag('form.type_extension', ['alias' => 'form']); + } else { + $definition->addTag('form.type_extension', ['extended_type' => FormType::class]); + } } } diff --git a/README.md b/README.md index 6e6c70a09..5bd0b5312 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,11 @@ applications with Symfony. Features include: [![Latest Stable Version](https://poser.pugx.org/FriendsOfSymfony/rest-bundle/v/stable.svg)](https://packagist.org/packages/FriendsOfSymfony/rest-bundle) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/0be23389-2e85-49cf-b333-caaa36d11c62/mini.png)](https://insight.sensiolabs.com/projects/0be23389-2e85-49cf-b333-caaa36d11c62) +Note +---- + +FOSRestBundle 1.x is no longer maintained, 1.8 only receives security fixes. Please upgrade to FOSRestBundle 2.x as soon as possible. + Documentation ------------- diff --git a/Request/ParamFetcher.php b/Request/ParamFetcher.php index ea026df5d..24840362b 100644 --- a/Request/ParamFetcher.php +++ b/Request/ParamFetcher.php @@ -101,17 +101,19 @@ public function get($name, $strict = null) /** @var ParamInterface $param */ $param = $params[$name]; $default = $param->getDefault(); + $default = $this->resolveValue($this->container, $default); $strict = (null !== $strict ? $strict : $param->isStrict()); $paramValue = $param->getValue($this->getRequest(), $default); - return $this->cleanParamWithRequirements($param, $paramValue, $strict); + return $this->cleanParamWithRequirements($param, $paramValue, $strict, $default); } /** * @param ParamInterface $param * @param mixed $paramValue * @param bool $strict + * @param mixed $default * * @throws BadRequestHttpException * @throws \RuntimeException @@ -120,11 +122,8 @@ public function get($name, $strict = null) * * @internal */ - protected function cleanParamWithRequirements(ParamInterface $param, $paramValue, $strict) + protected function cleanParamWithRequirements(ParamInterface $param, $paramValue, $strict, $default) { - $default = $param->getDefault(); - $default = $this->resolveValue($this->container, $default); - $this->checkNotIncompatibleParams($param); if ($default !== null && $default === $paramValue) { return $paramValue; diff --git a/Resources/config/forms.xml b/Resources/config/forms.xml index fd42a663c..aede2bf94 100644 --- a/Resources/config/forms.xml +++ b/Resources/config/forms.xml @@ -6,8 +6,6 @@ - - diff --git a/Resources/doc/2-the-view-layer.rst b/Resources/doc/2-the-view-layer.rst index a01564d13..35bd1bdb2 100644 --- a/Resources/doc/2-the-view-layer.rst +++ b/Resources/doc/2-the-view-layer.rst @@ -185,7 +185,7 @@ Then: leads to a "validation failed" response. - In a rendered template, the form is passed as 'form' and ``createView()`` is called automatically. -- ``$form->getData()`` is passed into the view as template as ``'data'`` if the +- ``$form->getData()`` is passed into the view template as ``'data'`` if the form is the only view data. - An invalid form will be wrapped into an exception. diff --git a/Routing/Loader/Reader/RestActionReader.php b/Routing/Loader/Reader/RestActionReader.php index 5486c3dde..491b76e1e 100644 --- a/Routing/Loader/Reader/RestActionReader.php +++ b/Routing/Loader/Reader/RestActionReader.php @@ -16,6 +16,7 @@ use FOS\RestBundle\Inflector\InflectorInterface; use FOS\RestBundle\Request\ParamReaderInterface; use FOS\RestBundle\Routing\RestRouteCollection; +use Psr\Http\Message\MessageInterface; use Symfony\Component\Routing\Route; /** @@ -476,6 +477,7 @@ private function getMethodArguments(\ReflectionMethod $method) \FOS\RestBundle\Request\ParamFetcherInterface::class, \Symfony\Component\Validator\ConstraintViolationListInterface::class, \Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter::class, + MessageInterface::class, ]; $arguments = []; diff --git a/Tests/Fixtures/Controller/TypeHintedController.php b/Tests/Fixtures/Controller/TypeHintedController.php new file mode 100644 index 000000000..1b1cc7c7e --- /dev/null +++ b/Tests/Fixtures/Controller/TypeHintedController.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace FOS\RestBundle\Tests\Fixtures\Controller; + +use FOS\RestBundle\Controller\Annotations as Rest; +use FOS\RestBundle\Controller\FOSRestController; +use FOS\RestBundle\Routing\ClassResourceInterface; +use Psr\Http\Message\MessageInterface; +use Psr\Http\Message\ServerRequestInterface; +use Symfony\Component\HttpFoundation\Request; + +/** + * @Rest\RouteResource("Article") + */ +class TypeHintedController implements ClassResourceInterface +{ + public function cgetAction(Request $request) + { + } + + public function cpostAction(MessageInterface $request) + { + } + + public function getAction(Request $request, $id) + { + } + + public function postAction(ServerRequestInterface $request, $id) + { + } +} diff --git a/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php b/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php index fe988f164..d495968cb 100644 --- a/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php +++ b/Tests/Functional/Bundle/TestBundle/Controller/ParamFetcherController.php @@ -25,18 +25,18 @@ class ParamFetcherController extends FOSRestController { /** - * @RequestParam(name="raw", requirements=@IdenticalTo({"foo"="raw", "bar"="foo"}), default="invalid") - * @RequestParam(name="map", map=true, requirements=@IdenticalTo({"foo"="map", "foobar"="foo"}), default="%invalid% %%") - * @RequestParam(name="bar", map=true, requirements="%foo% foo", strict=true) + * @RequestParam(name="raw", requirements=@IdenticalTo({"foo"="raw", "bar"="foo"}), default="invalid", strict=false) + * @RequestParam(name="map", map=true, requirements=@IdenticalTo({"foo"="map", "foobar"="foo"}), default="%invalid2% %%", strict=false) + * @RequestParam(name="bar", nullable=true, requirements="%bar%\ foo") */ public function paramsAction(ParamFetcherInterface $fetcher) { - return new JsonResponse($fetcher->all(false)); + return new JsonResponse($fetcher->all()); } /** * @QueryParam(name="foo", default="invalid") - * @RequestParam(name="bar", default="foo") + * @RequestParam(name="bar", default="%foo%") */ public function testAction(Request $request, ParamFetcherInterface $fetcher) { diff --git a/Tests/Functional/ParamFetcherTest.php b/Tests/Functional/ParamFetcherTest.php index 08b59f208..33266e727 100644 --- a/Tests/Functional/ParamFetcherTest.php +++ b/Tests/Functional/ParamFetcherTest.php @@ -52,23 +52,9 @@ public function testValidMapParameter() 'foo' => $this->validMap, 'bar' => $this->validMap, ]; - $this->client->request('POST', '/params', ['raw' => 'bar', 'map' => $map]); + $this->client->request('POST', '/params', ['raw' => 'bar', 'map' => $map, 'bar' => 'bar foo']); - $this->assertEquals(['raw' => 'invalid', 'map' => $map, 'bar' => null], $this->getData()); - } - - public function testFooParameter() - { - $value = ['bar foo', 'bar foo']; - $this->client->request('POST', '/params', ['foo' => $value]); - - $map = array( - 'foo' => $this->validMap, - 'bar' => $this->validMap, - ); - $this->client->request('POST', '/params', array('raw' => 'bar', 'map' => $map)); - - $this->assertEquals(array('raw' => 'invalid', 'map' => $map, 'bar' => null), $this->getData()); + $this->assertEquals(['raw' => 'invalid', 'map' => $map, 'bar' => 'bar foo'], $this->getData()); } public function testWithSubRequests() diff --git a/Tests/Functional/app/ParamFetcher/config.yml b/Tests/Functional/app/ParamFetcher/config.yml index 8cb4b91d4..77993d2ee 100644 --- a/Tests/Functional/app/ParamFetcher/config.yml +++ b/Tests/Functional/app/ParamFetcher/config.yml @@ -2,8 +2,9 @@ imports: - { resource: ../config/default.yml } parameters: - foo: bar - invalid: invalid2 + bar: bar + foo: foo + invalid2: invalid2 framework: serializer: diff --git a/Tests/Request/ParamFetcherTest.php b/Tests/Request/ParamFetcherTest.php index b7a118ef3..4199cde07 100644 --- a/Tests/Request/ParamFetcherTest.php +++ b/Tests/Request/ParamFetcherTest.php @@ -138,10 +138,6 @@ public function testDefaultReplacement() public function testReturnBeforeGettingConstraints() { $param = $this->getMockBuilder(\FOS\RestBundle\Controller\Annotations\ParamInterface::class)->getMock(); - $param - ->expects($this->once()) - ->method('getDefault') - ->willReturn('default'); $param ->expects($this->never()) ->method('getConstraints'); @@ -150,7 +146,7 @@ public function testReturnBeforeGettingConstraints() $this->assertEquals( 'default', - $method->invokeArgs($fetcher, array($param, 'default', null)) + $method->invokeArgs($fetcher, array($param, 'default', null, 'default')) ); } @@ -161,7 +157,7 @@ public function testReturnWhenEmptyConstraints() $this->assertEquals( 'value', - $method->invokeArgs($fetcher, array($param, 'value', null)) + $method->invokeArgs($fetcher, array($param, 'value', null, null)) ); } @@ -198,7 +194,7 @@ public function testNoValidationErrors() ->with('value', array('constraint')) ->willReturn(array()); - $this->assertEquals('value', $method->invokeArgs($fetcher, array($param, 'value', null))); + $this->assertEquals('value', $method->invokeArgs($fetcher, array($param, 'value', null, null))); } public function testValidationErrors() @@ -218,7 +214,7 @@ public function testValidationErrors() ->with('value', ['constraint']) ->willReturn($errors); - $this->assertEquals('default', $method->invokeArgs($fetcher, array($param, 'value', false))); + $this->assertEquals('default', $method->invokeArgs($fetcher, array($param, 'value', false, 'default'))); } public function testValidationException() @@ -250,7 +246,7 @@ public function testValidationException() ->willReturn($errors); try { - $method->invokeArgs($fetcher, array($param, 'value', true)); + $method->invokeArgs($fetcher, array($param, 'value', true, 'default')); $this->fail(sprintf('An exception must be thrown in %s', __METHOD__)); } catch (InvalidParameterException $exception) { $this->assertSame($param, $exception->getParameter()); @@ -287,7 +283,7 @@ public function testValidationErrorsInStrictMode() ->with('value', array('constraint')) ->willReturn($errors); - $method->invokeArgs($fetcher, array($param, 'value', true)); + $method->invokeArgs($fetcher, array($param, 'value', true, null)); } protected function getFetcherToCheckValidation($param, array $constructionArguments = null) diff --git a/Tests/Routing/Loader/RestRouteLoaderTest.php b/Tests/Routing/Loader/RestRouteLoaderTest.php index 23a756009..186e8c527 100644 --- a/Tests/Routing/Loader/RestRouteLoaderTest.php +++ b/Tests/Routing/Loader/RestRouteLoaderTest.php @@ -342,6 +342,24 @@ public function testNameMethodPrefixIsPrependingCorrectly() $this->assertNotNull($collection->get('post_users_bar'), 'route for "post_users_bar" does not exist'); } + /** + * RestActionReader::getMethodArguments should ignore certain types of + * parameters. + */ + public function testRequestTypeHintsIgnoredCorrectly() + { + $collection = $this->loadFromControllerFixture('TypeHintedController'); + + $this->assertNotNull($collection->get('get_articles'), 'route for "get_articles" does not exist'); + $this->assertEquals('/articles.{_format}', $collection->get('get_articles')->getPath()); + $this->assertNotNull($collection->get('post_articles'), 'route for "post_articles" does not exist'); + $this->assertEquals('/articles.{_format}', $collection->get('post_articles')->getPath()); + $this->assertNotNull($collection->get('get_article'), 'route for "get_article" does not exist'); + $this->assertEquals('/articles/{id}.{_format}', $collection->get('get_article')->getPath()); + $this->assertNotNull($collection->get('post_article'), 'route for "post_article" does not exist'); + $this->assertEquals('/articles/{id}.{_format}', $collection->get('post_article')->getPath()); + } + /** * Load routes collection from fixture class under Tests\Fixtures directory. * diff --git a/Validator/Constraints/Regex.php b/Validator/Constraints/Regex.php index 8c2c0dda0..377e02345 100644 --- a/Validator/Constraints/Regex.php +++ b/Validator/Constraints/Regex.php @@ -20,7 +20,7 @@ * * @author Ener-Getick */ -class Regex extends BaseRegex +class Regex extends BaseRegex implements ResolvableConstraintInterface { use ResolverTrait; diff --git a/composer.json b/composer.json index 8b8e8834b..26552c0d2 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,17 @@ "require": { "php": "^5.5.9|~7.0", "psr/log": "^1.0", - "symfony/framework-bundle": "^2.7|^3.0", + "symfony/config": "^2.7|^3.0", + "symfony/debug": "^2.7|^3.0", + "symfony/dependency-injection": "^2.7|^3.0", + "symfony/event-dispatcher": "^2.7|^3.0", "symfony/finder": "^2.7|^3.0", + "symfony/framework-bundle": "^2.7|^3.0", + "symfony/http-foundation": "^2.7|^3.0", + "symfony/http-kernel": "^2.7|^3.0", "symfony/routing": "^2.7|^3.0", + "symfony/security-core": "^2.7|^3.0", + "symfony/templating": "^2.7|^3.0", "doctrine/inflector": "^1.0", "willdurand/negotiation": "^2.0", "willdurand/jsonp-callback-validator": "^1.0" @@ -46,7 +54,8 @@ "symfony/expression-language": "~2.7|^3.0", "symfony/css-selector": "^2.7|^3.0", "phpoption/phpoption": "^1.1", - "jms/serializer-bundle": "^1.0" + "jms/serializer-bundle": "^1.0", + "psr/http-message": "^1.0" }, "suggest": {