diff --git a/UPGRADE-4.3.md b/UPGRADE-4.3.md index 79b38e2e7d5fe..6c348a3403637 100644 --- a/UPGRADE-4.3.md +++ b/UPGRADE-4.3.md @@ -55,6 +55,8 @@ Routing * The `generator_base_class`, `generator_cache_class`, `matcher_base_class`, and `matcher_cache_class` router options have been deprecated. + * Implementing `Serializable` for `Route` and `CompiledRoute` is deprecated; if you serialize them, please + ensure your unserialization logic can recover from a failure related to an updated serialization format Security -------- diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index 319e5d245bacd..39d1ab4303ab3 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -234,6 +234,8 @@ Routing * The `generator_base_class`, `generator_cache_class`, `matcher_base_class`, and `matcher_cache_class` router options have been removed. + * `Route` and `CompiledRoute` don't implement `Serializable` anymore; if you serialize them, please + ensure your unserialization logic can recover from a failure related to an updated serialization format Security -------- diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php index ce54a88ece170..0c4d03bfc11af 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/AppKernel.php @@ -79,15 +79,14 @@ protected function build(ContainerBuilder $container) $container->register('logger', NullLogger::class); } - public function serialize() + public function __sleep() { - return serialize([$this->varDir, $this->testCase, $this->rootConfig, $this->getEnvironment(), $this->isDebug()]); + return ['varDir', 'testCase', 'rootConfig', 'environment', 'debug']; } - public function unserialize($str) + public function __wakeup() { - $a = unserialize($str); - $this->__construct($a[0], $a[1], $a[2], $a[3], $a[4]); + $this->__construct($this->varDir, $this->testCase, $this->rootConfig, $this->environment, $this->debug); } protected function getKernelParameters() diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 4f620f3453f6d..764a0c0d2002f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -24,7 +24,7 @@ "symfony/dependency-injection": "^4.3", "symfony/event-dispatcher": "^4.1", "symfony/http-foundation": "^4.3", - "symfony/http-kernel": "^4.2", + "symfony/http-kernel": "^4.3", "symfony/polyfill-mbstring": "~1.0", "symfony/filesystem": "~3.4|~4.0", "symfony/finder": "~3.4|~4.0", diff --git a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php index 0eceb6e572b39..f211e0f52ef7b 100644 --- a/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php @@ -215,14 +215,9 @@ public function testPrune() } } -class NotUnserializable implements \Serializable +class NotUnserializable { - public function serialize() - { - return serialize(123); - } - - public function unserialize($ser) + public function __wakeup() { throw new \Exception(__CLASS__); } diff --git a/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php b/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php index 6692d3eb59ace..e56d99e44134f 100644 --- a/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php +++ b/src/Symfony/Component/Cache/Tests/Psr16CacheTest.php @@ -159,14 +159,9 @@ protected function isPruned($cache, $name) } } -class NotUnserializable implements \Serializable +class NotUnserializable { - public function serialize() - { - return serialize(123); - } - - public function unserialize($ser) + public function __wakeup() { throw new \Exception(__CLASS__); } diff --git a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php index 3c8824869bd08..d4b3d07f62729 100644 --- a/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php +++ b/src/Symfony/Component/Cache/Tests/Simple/CacheTestCase.php @@ -132,14 +132,9 @@ public function testPrune() } } -class NotUnserializable implements \Serializable +class NotUnserializable { - public function serialize() - { - return serialize(123); - } - - public function unserialize($ser) + public function __wakeup() { throw new \Exception(__CLASS__); } diff --git a/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt b/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt index b3f0e0eb0995a..1736a3b5f2a73 100644 --- a/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt +++ b/src/Symfony/Component/Debug/Tests/phpt/fatal_with_nested_handlers.phpt @@ -24,7 +24,7 @@ var_dump([ $eHandler[0]->setExceptionHandler('print_r'); if (true) { - class Broken implements \Serializable + class Broken implements \JsonSerializable { } } @@ -37,6 +37,6 @@ array(1) { } object(Symfony\Component\Debug\Exception\FatalErrorException)#%d (%d) { ["message":protected]=> - string(199) "Error: Class Symfony\Component\Debug\Broken contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Serializable::serialize, Serializable::unserialize)" + string(179) "Error: Class Symfony\Component\Debug\Broken contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (JsonSerializable::jsonSerialize)" %a } diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 2328918d7d9d5..f1b399cedc8be 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -15,6 +15,7 @@ CHANGELOG * added `block_prefix` option to `BaseType`. * added `help_html` option to display the `help` text as HTML. * `FormError` doesn't implement `Serializable` anymore + * `FormDataCollector` has been marked as `final` * added `label_translation_parameters`, `attr_translation_parameters`, `help_translation_parameters` options to `FormType` to pass translation parameters to form labels, attributes (`placeholder` and `title`) and help text respectively. The passed parameters will replace placeholders in translation messages. diff --git a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php index 96baf9b58d7f5..df7726a83f019 100644 --- a/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php +++ b/src/Symfony/Component/Form/Extension/DataCollector/FormDataCollector.php @@ -27,6 +27,8 @@ * * @author Robert Schönthal * @author Bernhard Schussek + * + * @final since Symfony 4.3 */ class FormDataCollector extends DataCollector implements FormDataCollectorInterface { @@ -229,7 +231,10 @@ public function getData() return $this->data; } - public function serialize() + /** + * @internal + */ + public function __sleep() { foreach ($this->data['forms_by_hash'] as &$form) { if (isset($form['type_class']) && !$form['type_class'] instanceof ClassStub) { @@ -237,7 +242,9 @@ public function serialize() } } - return serialize($this->cloneVar($this->data)); + $this->data = $this->cloneVar($this->data); + + return parent::__sleep(); } /** diff --git a/src/Symfony/Component/Form/composer.json b/src/Symfony/Component/Form/composer.json index 4f72f19e8e5b4..fd39d77edc19e 100644 --- a/src/Symfony/Component/Form/composer.json +++ b/src/Symfony/Component/Form/composer.json @@ -31,7 +31,7 @@ "symfony/config": "~3.4|~4.0", "symfony/console": "~3.4|~4.0", "symfony/http-foundation": "~3.4|~4.0", - "symfony/http-kernel": "~3.4|~4.0", + "symfony/http-kernel": "~4.3", "symfony/security-csrf": "~3.4|~4.0", "symfony/translation": "~4.2", "symfony/var-dumper": "~3.4|~4.0" @@ -41,7 +41,7 @@ "symfony/dependency-injection": "<3.4", "symfony/doctrine-bridge": "<3.4", "symfony/framework-bundle": "<3.4", - "symfony/http-kernel": "<3.4", + "symfony/http-kernel": "<4.3", "symfony/translation": "<4.2", "symfony/twig-bridge": "<3.4.5|<4.0.5,>=4.0" }, diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 2d1cfc4ba1d9e..8a923812b5285 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -4,9 +4,14 @@ CHANGELOG 4.3.0 ----- + * `KernelInterface` doesn't extend `Serializable` anymore + * deprecated the `Kernel::serialize()` and `unserialize()` methods * increased the priority of `Symfony\Component\HttpKernel\EventListener\AddRequestFormatsListener` * made `Symfony\Component\HttpKernel\EventListenerLocaleListener` set the default locale early * made `FileLinkFormatter` final and not implement `Serializable` anymore + * the base `DataCollector` doesn't implement `Serializable` anymore, you should + store all the serialized state in the data property instead + * `DumpDataCollector` has been marked as `final` 4.2.0 ----- diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php index 3b15868ff5c3e..d3020a312e02b 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php @@ -26,7 +26,7 @@ * @author Fabien Potencier * @author Bernhard Schussek */ -abstract class DataCollector implements DataCollectorInterface, \Serializable +abstract class DataCollector implements DataCollectorInterface { protected $data = []; @@ -35,16 +35,26 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable */ private $cloner; + /** + * @deprecated since Symfony 4.3, store all the serialized state in the data property instead + */ public function serialize() { + @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3, store all the serialized state in the data property instead.', __METHOD__), E_USER_DEPRECATED); + $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); $isCalledFromOverridingMethod = isset($trace[1]['function'], $trace[1]['object']) && 'serialize' === $trace[1]['function'] && $this === $trace[1]['object']; return $isCalledFromOverridingMethod ? $this->data : serialize($this->data); } + /** + * @deprecated since Symfony 4.3, store all the serialized state in the data property instead + */ public function unserialize($data) { + @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3, store all the serialized state in the data property instead.', __METHOD__), E_USER_DEPRECATED); + $this->data = \is_array($data) ? $data : unserialize($data); } @@ -100,4 +110,22 @@ protected function getCasters() return $casters; } + + public function __sleep() + { + if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) { + @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3, store all the serialized state in the "data" property instead.', $c), E_USER_DEPRECATED); + $this->data = $this->serialize(); + } + + return ['data']; + } + + public function __wakeup() + { + if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'unserialize'))->getDeclaringClass()->name) { + @trigger_error(sprintf('Implementing the "%s::unserialize()" method is deprecated since Symfony 4.3, store all the serialized state in the "data" property instead.', $c), E_USER_DEPRECATED); + $this->unserialize($this->data); + } + } } diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index c88c73bb8221f..577eb2ca2e455 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -25,6 +25,8 @@ /** * @author Nicolas Grekas + * + * @final since Symfony 4.3 */ class DumpDataCollector extends DataCollector implements DataDumperInterface { @@ -85,6 +87,9 @@ public function dump(Data $data) $this->isCollected = false; } + if (!$this->dataCount) { + $this->data = []; + } $this->data[] = compact('data', 'name', 'file', 'line', 'fileExcerpt'); ++$this->dataCount; @@ -95,6 +100,10 @@ public function dump(Data $data) public function collect(Request $request, Response $response, \Exception $exception = null) { + if (!$this->dataCount) { + $this->data = []; + } + // Sub-requests and programmatic calls stay in the collected profile. if ($this->dumper || ($this->requestStack && $this->requestStack->getMasterRequest() !== $request) || $request->isXmlHttpRequest() || $request->headers->has('Origin')) { return; @@ -136,28 +145,38 @@ public function reset() $this->clonesIndex = 0; } - public function serialize() + /** + * @internal + */ + public function __sleep() { + if (!$this->dataCount) { + $this->data = []; + } + if ($this->clonesCount !== $this->clonesIndex) { - return 'a:0:{}'; + return []; } $this->data[] = $this->fileLinkFormat; $this->data[] = $this->charset; - $ser = serialize($this->data); - $this->data = []; $this->dataCount = 0; $this->isCollected = true; - return $ser; + return parent::__sleep(); } - public function unserialize($data) + /** + * @internal + */ + public function __wakeup() { - $this->data = unserialize($data); + parent::__wakeup(); + $charset = array_pop($this->data); $fileLinkFormat = array_pop($this->data); $this->dataCount = \count($this->data); + self::__construct($this->stopwatch, $fileLinkFormat, $charset); } @@ -178,6 +197,10 @@ public function getDumps($format, $maxDepthLimit = -1, $maxItemsPerDepth = -1) } $dumps = []; + if (!$this->dataCount) { + return $this->data = []; + } + foreach ($this->data as $dump) { $dumper->dump($dump['data']->withMaxDepth($maxDepthLimit)->withMaxItemsPerDepth($maxItemsPerDepth)); $dump['data'] = stream_get_contents($data, -1, 0); @@ -196,7 +219,7 @@ public function getName() public function __destruct() { - if (0 === $this->clonesCount-- && !$this->isCollected && $this->data) { + if (0 === $this->clonesCount-- && !$this->isCollected && $this->dataCount) { $this->clonesCount = 0; $this->isCollected = true; diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 61236b8c817a5..23cb76a75edaa 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -833,15 +833,48 @@ public static function stripComments($source) return $output; } + /** + * @deprecated since Symfony 4.3 + */ public function serialize() { + @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED); + return serialize([$this->environment, $this->debug]); } + /** + * @deprecated since Symfony 4.3 + */ public function unserialize($data) { + @trigger_error(sprintf('The "%s" method is deprecated since Symfony 4.3.', __METHOD__), E_USER_DEPRECATED); list($environment, $debug) = unserialize($data, ['allowed_classes' => false]); $this->__construct($environment, $debug); } + + public function __sleep() + { + if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) { + @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3.', $c), E_USER_DEPRECATED); + $this->serialized = $this->serialize(); + + return ['serialized']; + } + + return ['environment', 'debug']; + } + + public function __wakeup() + { + if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) { + @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3.', $c), E_USER_DEPRECATED); + $this->unserialize($this->serialized); + unset($this->serialized); + + return; + } + $this->__construct($this->environment, $this->debug); + } } diff --git a/src/Symfony/Component/HttpKernel/KernelInterface.php b/src/Symfony/Component/HttpKernel/KernelInterface.php index 93c4190b9bc97..ead6920e7736f 100644 --- a/src/Symfony/Component/HttpKernel/KernelInterface.php +++ b/src/Symfony/Component/HttpKernel/KernelInterface.php @@ -24,7 +24,7 @@ * * @method string getProjectDir() Gets the project dir (path of the project's composer file) - not defining it is deprecated since Symfony 4.2 */ -interface KernelInterface extends HttpKernelInterface, \Serializable +interface KernelInterface extends HttpKernelInterface { /** * Returns an array of bundles to register. diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index 76c1d96dcc36e..a40a482278155 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -52,9 +52,9 @@ public function testDump() ]; $this->assertEquals($xDump, $dump); - $this->assertStringMatchesFormat('a:3:{i:0;a:5:{s:4:"data";%c:39:"Symfony\Component\VarDumper\Cloner\Data":%a', $collector->serialize()); + $this->assertStringMatchesFormat('%a;a:%d:{i:0;a:5:{s:4:"data";%c:39:"Symfony\Component\VarDumper\Cloner\Data":%a', serialize($collector)); $this->assertSame(0, $collector->getDumpsCount()); - $this->assertSame('a:2:{i:0;b:0;i:1;s:5:"UTF-8";}', $collector->serialize()); + $this->assertSame("O:60:\"Symfony\Component\HttpKernel\DataCollector\DumpDataCollector\":1:{s:7:\"\0*\0data\";a:2:{i:0;b:0;i:1;s:5:\"UTF-8\";}}", serialize($collector)); } public function testDumpWithServerConnection() @@ -72,7 +72,7 @@ public function testDumpWithServerConnection() ob_start(); $collector->collect(new Request(), new Response()); $this->assertEmpty(ob_get_clean()); - $this->assertStringMatchesFormat('a:3:{i:0;a:5:{s:4:"data";%c:39:"Symfony\Component\VarDumper\Cloner\Data":%a', $collector->serialize()); + $this->assertStringMatchesFormat('%a;a:%d:{i:0;a:5:{s:4:"data";%c:39:"Symfony\Component\VarDumper\Cloner\Data":%a', serialize($collector)); } public function testCollectDefault() @@ -90,7 +90,7 @@ public function testCollectDefault() $this->assertSame("DumpDataCollectorTest.php on line {$line}:\n123\n", $output); $this->assertSame(1, $collector->getDumpsCount()); - $collector->serialize(); + serialize($collector); } public function testCollectHtml() @@ -118,7 +118,7 @@ public function testCollectHtml() $this->assertSame($xOutput, trim($output)); $this->assertSame(1, $collector->getDumpsCount()); - $collector->serialize(); + serialize($collector); } public function testFlush() diff --git a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php index 57bd704c92d3e..1b66de40ffaf6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/KernelTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/KernelTest.php @@ -330,9 +330,8 @@ public function testSerialize() $env = 'test_env'; $debug = true; $kernel = new KernelForTest($env, $debug); - - $expected = serialize([$env, $debug]); - $this->assertEquals($expected, $kernel->serialize()); + $expected = "O:57:\"Symfony\Component\HttpKernel\Tests\Fixtures\KernelForTest\":2:{s:14:\"\0*\0environment\";s:8:\"test_env\";s:8:\"\0*\0debug\";b:1;}"; + $this->assertEquals($expected, serialize($kernel)); } /** diff --git a/src/Symfony/Component/Routing/CHANGELOG.md b/src/Symfony/Component/Routing/CHANGELOG.md index d695cd6bd1e91..4a5fba5cb4609 100644 --- a/src/Symfony/Component/Routing/CHANGELOG.md +++ b/src/Symfony/Component/Routing/CHANGELOG.md @@ -8,6 +8,8 @@ CHANGELOG * added `CompiledUrlGenerator` and `CompiledUrlGeneratorDumper` * deprecated `PhpGeneratorDumper` and `PhpMatcherDumper` * deprecated `generator_base_class`, `generator_cache_class`, `matcher_base_class` and `matcher_cache_class` router options + * deprecated implementing `Serializable` for `Route` and `CompiledRoute`; if you serialize them, please + ensure your unserialization logic can recover from a failure related to an updated serialization format 4.2.0 ----- diff --git a/src/Symfony/Component/Routing/CompiledRoute.php b/src/Symfony/Component/Routing/CompiledRoute.php index 8db812b075f66..b8919c56cc444 100644 --- a/src/Symfony/Component/Routing/CompiledRoute.php +++ b/src/Symfony/Component/Routing/CompiledRoute.php @@ -50,7 +50,7 @@ public function __construct(string $staticPrefix, string $regex, array $tokens, } /** - * {@inheritdoc} + * @internal since Symfony 4.3, will be removed in Symfony 5 as the class won't implement Serializable anymore */ public function serialize() { @@ -67,7 +67,7 @@ public function serialize() } /** - * {@inheritdoc} + * @internal since Symfony 4.3, will be removed in Symfony 5 as the class won't implement Serializable anymore */ public function unserialize($serialized) { diff --git a/src/Symfony/Component/Routing/Route.php b/src/Symfony/Component/Routing/Route.php index 4e0463b57578c..8028d3801228d 100644 --- a/src/Symfony/Component/Routing/Route.php +++ b/src/Symfony/Component/Routing/Route.php @@ -63,7 +63,7 @@ public function __construct(string $path, array $defaults = [], array $requireme } /** - * {@inheritdoc} + * @internal since Symfony 4.3, will be removed in Symfony 5 as the class won't implement Serializable anymore */ public function serialize() { @@ -81,7 +81,7 @@ public function serialize() } /** - * {@inheritdoc} + * @internal since Symfony 4.3, will be removed in Symfony 5 as the class won't implement Serializable anymore */ public function unserialize($serialized) { diff --git a/src/Symfony/Component/Security/CHANGELOG.md b/src/Symfony/Component/Security/CHANGELOG.md index 011d802ea1ff7..a04fd47e46ada 100644 --- a/src/Symfony/Component/Security/CHANGELOG.md +++ b/src/Symfony/Component/Security/CHANGELOG.md @@ -4,31 +4,32 @@ CHANGELOG 4.3.0 ----- -* The `Role` and `SwitchUserRole` classes are deprecated and will be removed in 5.0. Use strings for roles - instead. -* The `RoleHierarchyInterface` is deprecated and will be removed in 5.0. -* The `getReachableRoles()` method of the `RoleHierarchy` class is deprecated and will be removed in 5.0. - Use the `getReachableRoleNames()` method instead. -* The `getRoles()` method of the `TokenInterface` is deprecated. Tokens must implement the `getRoleNames()` - method instead and return roles as strings. -* Made the `serialize()` and `unserialize()` methods of `AbstractToken` and + * The `Role` and `SwitchUserRole` classes are deprecated and will be removed in 5.0. Use strings for roles + instead. + * The `RoleHierarchyInterface` is deprecated and will be removed in 5.0. + * The `getReachableRoles()` method of the `RoleHierarchy` class is deprecated and will be removed in 5.0. + Use the `getReachableRoleNames()` method instead. + * The `getRoles()` method of the `TokenInterface` is deprecated. Tokens must implement the `getRoleNames()` + method instead and return roles as strings. + * Made the `serialize()` and `unserialize()` methods of `AbstractToken` and `AuthenticationException` final, use `getState()`/`setState()` instead + * `AuthenticationException` doesn't implement `Serializable` anymore 4.2.0 ----- -* added the `is_granted()` function in security expressions -* deprecated the `has_role()` function in security expressions, use `is_granted()` instead -* Passing custom class names to the - `Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver` to define - custom anonymous and remember me token classes is deprecated. To - use custom tokens, extend the existing `Symfony\Component\Security\Core\Authentication\Token\AnonymousToken` - or `Symfony\Component\Security\Core\Authentication\Token\RememberMeToken`. -* allow passing null as $filter in LdapUserProvider to get the default filter -* accessing the user object that is not an instance of `UserInterface` from `Security::getUser()` is deprecated -* Deprecated `SimpleAuthenticatorInterface`, `SimpleFormAuthenticatorInterface`, - `SimplePreAuthenticatorInterface`, `SimpleAuthenticationProvider`, `SimpleAuthenticationHandler`, - `SimpleFormAuthenticationListener` and `SimplePreAuthenticationListener`. Use Guard instead. + * added the `is_granted()` function in security expressions + * deprecated the `has_role()` function in security expressions, use `is_granted()` instead + * Passing custom class names to the + `Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolver` to define + custom anonymous and remember me token classes is deprecated. To + use custom tokens, extend the existing `Symfony\Component\Security\Core\Authentication\Token\AnonymousToken` + or `Symfony\Component\Security\Core\Authentication\Token\RememberMeToken`. + * allow passing null as $filter in LdapUserProvider to get the default filter + * accessing the user object that is not an instance of `UserInterface` from `Security::getUser()` is deprecated + * Deprecated `SimpleAuthenticatorInterface`, `SimpleFormAuthenticatorInterface`, + `SimplePreAuthenticatorInterface`, `SimpleAuthenticationProvider`, `SimpleAuthenticationHandler`, + `SimpleFormAuthenticationListener` and `SimplePreAuthenticationListener`. Use Guard instead. 4.1.0 ----- diff --git a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php index 700634e7dcdac..b409e4b5635fd 100644 --- a/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php +++ b/src/Symfony/Component/Security/Core/Authentication/Token/AbstractToken.php @@ -151,7 +151,14 @@ public function eraseCredentials() */ public function serialize() { - return $this->doSerialize($this->getState(), \func_num_args() ? \func_get_arg(0) : null); + $serialized = $this->getState(); + + if (null === $isCalledFromOverridingMethod = \func_num_args() ? \func_get_arg(0) : null) { + $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); + $isCalledFromOverridingMethod = isset($trace[1]['function'], $trace[1]['object']) && 'serialize' === $trace[1]['function'] && $this === $trace[1]['object']; + } + + return $isCalledFromOverridingMethod ? $serialized : serialize($serialized); } /** @@ -284,19 +291,6 @@ public function __toString() return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUsername(), json_encode($this->authenticated), implode(', ', $roles)); } - /** - * @internal - */ - protected function doSerialize($serialized, $isCalledFromOverridingMethod) - { - if (null === $isCalledFromOverridingMethod) { - $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3); - $isCalledFromOverridingMethod = isset($trace[2]['function'], $trace[2]['object']) && 'serialize' === $trace[2]['function'] && $this === $trace[2]['object']; - } - - return $isCalledFromOverridingMethod ? $serialized : serialize($serialized); - } - private function hasUserChanged(UserInterface $user) { if (!($this->user instanceof UserInterface)) { diff --git a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php index 2ee412bfd120f..82f6db65d6099 100644 --- a/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php +++ b/src/Symfony/Component/Security/Core/Exception/AuthenticationException.php @@ -19,7 +19,7 @@ * @author Fabien Potencier * @author Alexander */ -class AuthenticationException extends RuntimeException implements \Serializable +class AuthenticationException extends RuntimeException { private $token; @@ -47,7 +47,14 @@ public function setToken(TokenInterface $token) */ public function serialize() { - return $this->doSerialize($this->getState(), \func_num_args() ? \func_get_arg(0) : null); + $serialized = $this->getState(); + + if (null === $isCalledFromOverridingMethod = \func_num_args() ? \func_get_arg(0) : null) { + $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 2); + $isCalledFromOverridingMethod = isset($trace[1]['function'], $trace[1]['object']) && 'serialize' === $trace[1]['function'] && $this === $trace[1]['object']; + } + + return $isCalledFromOverridingMethod ? $serialized : serialize($serialized); } /** @@ -62,6 +69,30 @@ public function unserialize($serialized) $this->setState(\is_array($serialized) ? $serialized : unserialize($serialized)); } + public function __sleep() + { + if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'serialize'))->getDeclaringClass()->name) { + @trigger_error(sprintf('Implementing the "%s::serialize()" method is deprecated since Symfony 4.3, implement the getState() and setState() methods instead.', $c), E_USER_DEPRECATED); + $this->serialized = $this->serialize(); + } else { + $this->serialized = $this->getState(); + } + + return ['serialized']; + } + + public function __wakeup() + { + if (__CLASS__ !== $c = (new \ReflectionMethod($this, 'unserialize'))->getDeclaringClass()->name) { + @trigger_error(sprintf('Implementing the "%s::unserialize()" method is deprecated since Symfony 4.3, implement the getState() and setState() methods instead.', $c), E_USER_DEPRECATED); + $this->unserialize($this->serialized); + } else { + $this->setState($this->serialized); + } + + unset($this->serialized); + } + /** * Returns all the necessary state of the object for serialization purposes. * @@ -103,19 +134,6 @@ protected function setState(array $data) [$this->token, $this->code, $this->message, $this->file, $this->line] = $data; } - /** - * @internal - */ - protected function doSerialize($serialized, $isCalledFromOverridingMethod) - { - if (null === $isCalledFromOverridingMethod) { - $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3); - $isCalledFromOverridingMethod = isset($trace[2]['function'], $trace[2]['object']) && 'serialize' === $trace[2]['function'] && $this === $trace[2]['object']; - } - - return $isCalledFromOverridingMethod ? $serialized : serialize($serialized); - } - /** * Message key to be used by the translation component. * diff --git a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php index e87d25a789f1d..188eeb5ab7def 100644 --- a/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Authentication/Token/AbstractTokenTest.php @@ -278,15 +278,15 @@ public function __construct(array $roles = [], UserInterface $user = null) } } - public function serialize() + protected function getState(): array { - return serialize([$this->credentials, parent::serialize()]); + return [$this->credentials, parent::getState()]; } - public function unserialize($serialized) + protected function setState(array $data) { - list($this->credentials, $parentStr) = unserialize($serialized); - parent::unserialize($parentStr); + [$this->credentials, $parentState] = $data; + parent::setState($parentState); } public function getCredentials() diff --git a/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php b/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php index 32c76a452f702..58eb04efbdce9 100644 --- a/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php +++ b/src/Symfony/Component/Security/Core/Tests/Exception/CustomUserMessageAuthenticationExceptionTest.php @@ -17,16 +17,16 @@ class ChildCustomUserMessageAuthenticationException extends CustomUserMessageAuthenticationException { - public function serialize() + protected function getState(): array { - return serialize([$this->childMember, parent::serialize()]); + return [$this->childMember, parent::getState()]; } - public function unserialize($str) + public function setState(array $data) { - list($this->childMember, $parentData) = unserialize($str); + [$this->childMember, $parentData] = $data; - parent::unserialize($parentData); + parent::setState($parentData); } } diff --git a/src/Symfony/Component/Security/Core/composer.json b/src/Symfony/Component/Security/Core/composer.json index f4128c65c2bc6..3f297cec44f7a 100644 --- a/src/Symfony/Component/Security/Core/composer.json +++ b/src/Symfony/Component/Security/Core/composer.json @@ -28,6 +28,9 @@ "symfony/validator": "~3.4|~4.0", "psr/log": "~1.0" }, + "conflict": { + "symfony/security-guard": "<4.3" + }, "suggest": { "psr/container-implementation": "To instantiate the Security class", "symfony/event-dispatcher": "",