From 12051ce9e1b7cc873520fa3eb5f3dbbf617a1486 Mon Sep 17 00:00:00 2001 From: Albert Bakker Date: Thu, 16 Nov 2023 18:03:22 +0100 Subject: [PATCH] fix: return property names in AbstractSqlExecutor::__sleep Property names as returned by a cast to array are mangled, and that mangling is not documented. Returning unprefixed produces the same result, and is more likely to be supported by external tools relying on the documented possible return values of __sleep. For instance symfony/var-exporter does not support mangled names, which leads to issues when caching query parsing results in Symfony applications. --- .../ORM/Query/Exec/AbstractSqlExecutor.php | 6 +++- .../ParserResultSerializationTest.php | 28 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php index 2ac9f7d59fe..0348446a97c 100644 --- a/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php @@ -11,7 +11,9 @@ use function array_diff; use function array_keys; +use function array_map; use function array_values; +use function str_replace; /** * Base class for SQL statement executors. @@ -84,7 +86,9 @@ public function __sleep(): array serialized representation becomes compatible with 3.0.x, meaning there will not be a deprecation warning about a missing property when unserializing data */ - return array_values(array_diff(array_keys((array) $this), ["\0*\0_sqlStatements"])); + return array_values(array_diff(array_map(static function (string $prop): string { + return str_replace("\0*\0", '', $prop); + }, array_keys((array) $this)), ['_sqlStatements'])); } public function __wakeup(): void diff --git a/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php b/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php index 55a9fc11b0b..a6111c0c661 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php @@ -4,6 +4,7 @@ namespace Doctrine\Tests\ORM\Functional; +use Closure; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Exec\SingleSelectExecutor; use Doctrine\ORM\Query\ParserResult; @@ -12,6 +13,7 @@ use Generator; use ReflectionMethod; use ReflectionProperty; +use Symfony\Component\VarExporter\VarExporter; use function file_get_contents; use function rtrim; @@ -27,14 +29,18 @@ protected function setUp(): void parent::setUp(); } - public function testSerializeParserResult(): void + /** + * @param Closure(ParserResult): ParserResult $toSerializedAndBack + * + * @dataProvider provideToSerializedAndBack + */ + public function testSerializeParserResult(Closure $toSerializedAndBack): void { $query = $this->_em ->createQuery('SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u.name = :name'); $parserResult = self::parseQuery($query); - $serialized = serialize($parserResult); - $unserialized = unserialize($serialized); + $unserialized = $toSerializedAndBack($parserResult); $this->assertInstanceOf(ParserResult::class, $unserialized); $this->assertInstanceOf(ResultSetMapping::class, $unserialized->getResultSetMapping()); @@ -42,6 +48,22 @@ public function testSerializeParserResult(): void $this->assertInstanceOf(SingleSelectExecutor::class, $unserialized->getSqlExecutor()); } + /** @return Generator */ + public function provideToSerializedAndBack(): Generator + { + yield 'native serialization function' => [ + static function (ParserResult $parserResult): ParserResult { + return unserialize(serialize($parserResult)); + }, + ]; + + yield 'symfony/var-exporter' => [ + static function (ParserResult $parserResult): ParserResult { + return eval('return ' . VarExporter::export($parserResult) . ';'); + }, + ]; + } + public function testItSerializesParserResultWithAForwardCompatibleFormat(): void { $query = $this->_em