diff --git a/UPGRADE-3.2.md b/UPGRADE-3.2.md index 6f2788070d..24b9813e2e 100644 --- a/UPGRADE-3.2.md +++ b/UPGRADE-3.2.md @@ -41,6 +41,29 @@ FrameworkBundle * The service `serializer.mapping.cache.doctrine.apc` is deprecated. APCu should now be automatically used when available. +HttpFoundation +--------------- + + * Extending the following methods of `Response` + is deprecated (these methods will be `final` in 4.0): + + - `setDate`/`getDate` + - `setExpires`/`getExpires` + - `setLastModified`/`getLastModified` + - `setProtocolVersion`/`getProtocolVersion` + - `setStatusCode`/`getStatusCode` + - `setCharset`/`getCharset` + - `setPrivate`/`setPublic` + - `getAge` + - `getMaxAge`/`setMaxAge` + - `setSharedMaxAge` + - `getTtl`/`setTtl` + - `setClientTtl` + - `getEtag`/`setEtag` + - `hasVary`/`getVary`/`setVary` + - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` + - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` + Validator --------- diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index 7c56e2571f..07f71dfee4 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -121,6 +121,29 @@ FrameworkBundle * The `Controller::getUser()` method has been removed in favor of the ability to typehint the security user object in the action. +HttpFoundation +--------------- + + * Extending the following methods of `Response` + is no longer possible (these methods are now `final`): + + - `setDate`/`getDate` + - `setExpires`/`getExpires` + - `setLastModified`/`getLastModified` + - `setProtocolVersion`/`getProtocolVersion` + - `setStatusCode`/`getStatusCode` + - `setCharset`/`getCharset` + - `setPrivate`/`setPublic` + - `getAge` + - `getMaxAge`/`setMaxAge` + - `setSharedMaxAge` + - `getTtl`/`setTtl` + - `setClientTtl` + - `getEtag`/`setEtag` + - `hasVary`/`getVary`/`setVary` + - `isInvalid`/`isSuccessful`/`isRedirection`/`isClientError`/`isServerError` + - `isOk`/`isForbidden`/`isNotFound`/`isRedirect`/`isEmpty` + HttpKernel ---------- diff --git a/src/Symfony/Component/HttpFoundation/Response.php b/src/Symfony/Component/HttpFoundation/Response.php index b2e4498959..e7f9589a13 100644 --- a/src/Symfony/Component/HttpFoundation/Response.php +++ b/src/Symfony/Component/HttpFoundation/Response.php @@ -186,6 +186,30 @@ class Response 511 => 'Network Authentication Required', // RFC6585 ); + private static $deprecatedMethods = array( + 'setDate', 'getDate', + 'setExpires', 'getExpires', + 'setLastModified', 'getLastModified', + 'setProtocolVersion', 'getProtocolVersion', + 'setStatusCode', 'getStatusCode', + 'setCharset', 'getCharset', + 'setPrivate', 'setPublic', + 'getAge', 'getMaxAge', 'setMaxAge', 'setSharedMaxAge', + 'getTtl', 'setTtl', 'setClientTtl', + 'getEtag', 'setEtag', + 'hasVary', 'getVary', 'setVary', + 'isInvalid', 'isSuccessful', 'isRedirection', + 'isClientError', 'isOk', 'isForbidden', + 'isNotFound', 'isRedirect', 'isEmpty', + ); + private static $deprecationsTriggered = array( + __CLASS__ => true, + BinaryFileResponse::class => true, + JsonResponse::class => true, + RedirectResponse::class => true, + StreamedResponse::class => true, + ); + /** * Constructor. * @@ -201,6 +225,23 @@ class Response $this->setContent($content); $this->setStatusCode($status); $this->setProtocolVersion('1.0'); + + // Deprecations + $class = get_class($this); + if ($this instanceof \PHPUnit_Framework_MockObject_MockObject || $this instanceof \Prophecy\Doubler\DoubleInterface) { + $class = get_parent_class($class); + } + if (isset(self::$deprecationsTriggered[$class])) { + return; + } + + self::$deprecationsTriggered[$class] = true; + foreach (self::$deprecatedMethods as $method) { + $r = new \ReflectionMethod($class, $method); + if (__CLASS__ !== $r->getDeclaringClass()->getName()) { + @trigger_error(sprintf('Extending %s::%s() in %s is deprecated since version 3.2 and won\'t be supported anymore in 4.0 as it will be final.', __CLASS__, $method, $class), E_USER_DEPRECATED); + } + } } /** diff --git a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php index 2df2d61cad..c25b66aa2a 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/ResponseTest.php @@ -11,6 +11,9 @@ namespace Symfony\Component\HttpFoundation\Tests; +use Response\DefaultResponse; +use Response\ExtendedResponse; +use Symfony\Bridge\PhpUnit\ErrorAssert; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -843,6 +846,34 @@ class ResponseTest extends ResponseTestCase } } + /** + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + */ + public function testNoDeprecations() + { + ErrorAssert::assertDeprecationsAreTriggered(array(), function () { + new DefaultResponse(); + $this->getMock(Response::class); + }); + } + + /** + * @requires function Symfony\Bridge\PhpUnit\ErrorAssert::assertDeprecationsAreTriggered + */ + public function testDeprecations() + { + $deprecationMessages = array(); + foreach (array('getDate', 'setLastModified') as $method) { + $deprecationMessages[] = sprintf('Extending %s::%s() in Response\ExtendedResponse is deprecated', Response::class, $method); + } + ErrorAssert::assertDeprecationsAreTriggered($deprecationMessages, function () { + new ExtendedResponse(); + + // Deprecations should not be triggered twice + new ExtendedResponse(); + }); + } + public function validContentProvider() { return array( @@ -891,3 +922,22 @@ class StringableObject return 'Foo'; } } + +namespace Response; + +use Symfony\Component\HttpFoundation\Response; + +class DefaultResponse extends Response +{ +} + +class ExtendedResponse extends Response +{ + public function setLastModified(\DateTime $date = null) + { + } + + public function getDate() + { + } +}