Skip to content

Commit

Permalink
fix: return property names in AbstractSqlExecutor::__sleep
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kerbert101 authored and greg0ire committed Nov 16, 2023
1 parent e8afa9f commit 12051ce
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 4 deletions.
6 changes: 5 additions & 1 deletion lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,6 +13,7 @@
use Generator;
use ReflectionMethod;
use ReflectionProperty;
use Symfony\Component\VarExporter\VarExporter;

use function file_get_contents;
use function rtrim;
Expand All @@ -27,21 +29,41 @@ 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());
$this->assertEquals(['name' => [0]], $unserialized->getParameterMappings());
$this->assertInstanceOf(SingleSelectExecutor::class, $unserialized->getSqlExecutor());
}

/** @return Generator<string, array{Closure(ParserResult): ParserResult}> */
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
Expand Down

0 comments on commit 12051ce

Please sign in to comment.