Upgrading from 2.16.3 to 2.17.0 breaks serialization when using symfony/var-exporter #7245

Closed
opened 2026-01-22 15:47:49 +01:00 by admin · 76 comments
Owner

Originally created by @alcohol on GitHub (Nov 16, 2023).

BC Break Report

Could not find anything in release notes so far that predicted this would break. Was also not yet able to determine which, if any, of the changes listed cause this behaviour. Will update this issue as I get further along.

Stacktrace is as follows (omitted a few lines at the bottom for brevity):

TypeError:
Doctrine\DBAL\Connection::executeQuery(): Argument #1 ($sql) must be of type string, null given, called in /srv/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php on line 33

  at vendor/doctrine/dbal/src/Connection.php:1071
  at Doctrine\DBAL\Connection->executeQuery(null, array(), array(), null)
     (vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php:33)
  at Doctrine\ORM\Query\Exec\SingleSelectExecutor->execute(object(Connection), array(), array())
     (vendor/doctrine/orm/lib/Doctrine/ORM/Query.php:327)
  at Doctrine\ORM\Query->_doExecute()
     (vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1212)
  at Doctrine\ORM\AbstractQuery->executeIgnoreQueryCache(null, 3)
     (vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1166)
  at Doctrine\ORM\AbstractQuery->execute(null, 3)
     (vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:937)
  at Doctrine\ORM\AbstractQuery->getScalarResult()
     (vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php:153)
  at Doctrine\ORM\Tools\Pagination\Paginator->getIterator()
  at iterator_to_array(object(Paginator))
     (src/<redacted>/Doctrine/Repository/CategoryRepository.php:46)
  at <redacted>\Doctrine\Repository\CategoryRepository->getRootCategories(25, 0)
     (src/<redacted>/CategoryBundle/Menu/MenuBuilder.php:119)
  at <redacted>\CategoryBundle\Menu\MenuBuilder->getCategoryTreeMenu(object(MenuItem), true)
     (src/<redacted>/CategoryBundle/Menu/MenuBuilder.php:114)
  at <redacted>\CategoryBundle\Menu\MenuBuilder->createMobileCategoryMenu(object(RequestStack))
     (var/cache/nl/docker/ContainerKpuxm9P/getPsCategory_Menu_MobileCategoriesService.php:23)
  at ContainerKpuxm9P\getPsCategory_Menu_MobileCategoriesService::do(object(AppKernelDockerDebugContainer), true)
     (var/cache/nl/docker/ContainerKpuxm9P/AppKernelDockerDebugContainer.php:898)
  at ContainerKpuxm9P\AppKernelDockerDebugContainer->load('getPsCategory_Menu_MobileCategoriesService.php')
     (var/cache/nl/docker/ContainerKpuxm9P/getKnpMenu_MenuProvider_LazyService.php:22)
  at ContainerKpuxm9P\getKnpMenu_MenuProvider_LazyService::ContainerKpuxm9P\{closure}(array())
     (vendor/knplabs/knp-menu/src/Knp/Menu/Provider/LazyProvider.php:39)
  at Knp\Menu\Provider\LazyProvider->get('mobile_categories', array())
     (vendor/knplabs/knp-menu/src/Knp/Menu/Provider/ChainProvider.php:26)
  at Knp\Menu\Provider\ChainProvider->get('mobile_categories', array())
     (vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php:43)
  at Knp\Menu\Twig\Helper->get('mobile_categories', array())
     (vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php:142)
  at Knp\Menu\Twig\Helper->castMenu('mobile_categories')
     (vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php:75)
Q A
BC Break yes
Version 2.17.0

Summary

See stacktrace above.

Previous behavior

No exception was thrown.

Current behavior

Exception is thrown.

How to reproduce

Hard to tell, might be related to our code, might not be? Still debugging.


Update(s)

It seems that turning on lazy ghosts resolves the issue. However, that does not seem to be the default configuration in our symfony application. Is this the right way to deprecate something?

Turning it back off does not break things. Looking more likely to be cache related, making this a non-issue. Will try a new deployment with some more rigorous cache purging.

Sigh. Works on development environment now, broken on production still. More debugging needed.

Originally created by @alcohol on GitHub (Nov 16, 2023). <!-- Before reporting a BC break, please consult the upgrading document to make sure it's not an expected change: https://github.com/doctrine/orm/blob/2.9.x/UPGRADE.md --> ### BC Break Report Could not find anything in release notes so far that predicted this would break. Was also not yet able to determine which, if any, of the changes listed cause this behaviour. Will update this issue as I get further along. Stacktrace is as follows (omitted a few lines at the bottom for brevity): ``` TypeError: Doctrine\DBAL\Connection::executeQuery(): Argument #1 ($sql) must be of type string, null given, called in /srv/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php on line 33 at vendor/doctrine/dbal/src/Connection.php:1071 at Doctrine\DBAL\Connection->executeQuery(null, array(), array(), null) (vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php:33) at Doctrine\ORM\Query\Exec\SingleSelectExecutor->execute(object(Connection), array(), array()) (vendor/doctrine/orm/lib/Doctrine/ORM/Query.php:327) at Doctrine\ORM\Query->_doExecute() (vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1212) at Doctrine\ORM\AbstractQuery->executeIgnoreQueryCache(null, 3) (vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1166) at Doctrine\ORM\AbstractQuery->execute(null, 3) (vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:937) at Doctrine\ORM\AbstractQuery->getScalarResult() (vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php:153) at Doctrine\ORM\Tools\Pagination\Paginator->getIterator() at iterator_to_array(object(Paginator)) (src/<redacted>/Doctrine/Repository/CategoryRepository.php:46) at <redacted>\Doctrine\Repository\CategoryRepository->getRootCategories(25, 0) (src/<redacted>/CategoryBundle/Menu/MenuBuilder.php:119) at <redacted>\CategoryBundle\Menu\MenuBuilder->getCategoryTreeMenu(object(MenuItem), true) (src/<redacted>/CategoryBundle/Menu/MenuBuilder.php:114) at <redacted>\CategoryBundle\Menu\MenuBuilder->createMobileCategoryMenu(object(RequestStack)) (var/cache/nl/docker/ContainerKpuxm9P/getPsCategory_Menu_MobileCategoriesService.php:23) at ContainerKpuxm9P\getPsCategory_Menu_MobileCategoriesService::do(object(AppKernelDockerDebugContainer), true) (var/cache/nl/docker/ContainerKpuxm9P/AppKernelDockerDebugContainer.php:898) at ContainerKpuxm9P\AppKernelDockerDebugContainer->load('getPsCategory_Menu_MobileCategoriesService.php') (var/cache/nl/docker/ContainerKpuxm9P/getKnpMenu_MenuProvider_LazyService.php:22) at ContainerKpuxm9P\getKnpMenu_MenuProvider_LazyService::ContainerKpuxm9P\{closure}(array()) (vendor/knplabs/knp-menu/src/Knp/Menu/Provider/LazyProvider.php:39) at Knp\Menu\Provider\LazyProvider->get('mobile_categories', array()) (vendor/knplabs/knp-menu/src/Knp/Menu/Provider/ChainProvider.php:26) at Knp\Menu\Provider\ChainProvider->get('mobile_categories', array()) (vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php:43) at Knp\Menu\Twig\Helper->get('mobile_categories', array()) (vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php:142) at Knp\Menu\Twig\Helper->castMenu('mobile_categories') (vendor/knplabs/knp-menu/src/Knp/Menu/Twig/Helper.php:75) ``` | Q | A |------------ | ------ | BC Break | yes | Version | 2.17.0 #### Summary See stacktrace above. #### Previous behavior No exception was thrown. #### Current behavior Exception is thrown. #### How to reproduce Hard to tell, might be related to our code, might not be? Still debugging. --- **Update(s)** ~~It seems that turning on lazy ghosts resolves the issue. However, that does not seem to be the default configuration in our symfony application. Is this the right way to deprecate something?~~ ~~Turning it back off does not break things. Looking more likely to be cache related, making this a non-issue. Will try a new deployment with some more rigorous cache purging.~~ Sigh. Works on development environment now, broken on production still. More debugging needed.
admin closed this issue 2026-01-22 15:47:49 +01:00
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

2a9390d seems to be causing this

@kerbert101 commented on GitHub (Nov 16, 2023): 2a9390d seems to be causing this
Author
Owner

@itarcontact commented on GitHub (Nov 16, 2023):

Hi. I have the same error. Reverting to 2.16.3 solves the problem.

@itarcontact commented on GitHub (Nov 16, 2023): Hi. I have the same error. Reverting to 2.16.3 solves the problem.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

For me it was resolved by purging our cache more explicitly.

@alcohol commented on GitHub (Nov 16, 2023): For me it was resolved by purging our cache more explicitly.
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

I can fix the issue by replacing the code that returns the properties to be serialized.

In AbstractSqlExecutor in the __sleep method:

return array_values(array_diff(array_keys(get_object_vars($this)), ["_sqlStatements"]));
@kerbert101 commented on GitHub (Nov 16, 2023): I can fix the issue by replacing the code that returns the properties to be serialized. In `AbstractSqlExecutor` in the __sleep method: ```php return array_values(array_diff(array_keys(get_object_vars($this)), ["_sqlStatements"])); ```
Author
Owner

@itarcontact commented on GitHub (Nov 16, 2023):

@alcohol Which version do you finally have?

@itarcontact commented on GitHub (Nov 16, 2023): @alcohol Which version do you finally have?
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

I take it back, development release works (but runs in development mode). Production broken again. Manually purging caches did not resolve it there. Diving back into what the cause is.

@alcohol commented on GitHub (Nov 16, 2023): I take it back, development release works (but runs in development mode). Production broken again. Manually purging caches did not resolve it there. Diving back into what the cause is.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Weird, locally and on development we use

framework:
  annotations:
    cache: php_array

On production we use

framework:
  annotations:
    cache: file

Switching to file locally, installing 2.16.3 and then upgrading to 2.17.0 breaks things. But removing the cache directories solves it. We perform the same step during a production deployment though. So I'm confused why that does not resolve it. Maybe fpm file cache related? Going to see if a stop, cache purge, start - solves it.


Update

Still broken on production. Working fine locally and on development. Investigating further..

@alcohol commented on GitHub (Nov 16, 2023): Weird, locally and on development we use ``` framework: annotations: cache: php_array ``` On production we use ``` framework: annotations: cache: file ``` Switching to `file` locally, installing `2.16.3` and then upgrading to `2.17.0` breaks things. But removing the cache directories solves it. We perform the same step during a production deployment though. So I'm confused why that does not resolve it. Maybe fpm file cache related? Going to see if a stop, cache purge, start - solves it. --- **Update** Still broken on production. Working fine locally and on development. Investigating further..
Author
Owner

@KhorneHoly commented on GitHub (Nov 16, 2023):

Upgrade to 2.17.0 also broke our code, downgrading to 2.16.3 now.

@KhorneHoly commented on GitHub (Nov 16, 2023): Upgrade to 2.17.0 also broke our code, downgrading to 2.16.3 now.
Author
Owner

@RobinDev commented on GitHub (Nov 16, 2023):

Same here in prod : purging the cache offers me a few request working, then same error. Investigating too.

@RobinDev commented on GitHub (Nov 16, 2023): Same here in prod : purging the cache offers me a few request working, then same error. Investigating too.
Author
Owner

@PhilETaylor commented on GitHub (Nov 16, 2023):

For me it was resolved by purging our cache more explicitly.

This is what initially fixed it for me when I mentioned it here https://github.com/doctrine/orm/pull/11027#issuecomment-1793871887 2 weeks ago.

By "more explicitly", I mean I had to physically flush the Redis cache manually by flushing the whole redis db - for some reason the cli commands just did not clear the cache "enough".

Ive been running the new code in production for 2 weeks now with no issues since that day 2 weeks ago https://github.com/doctrine/orm/pull/11048

Is Redis the clue here? 🤷‍♂️

@PhilETaylor commented on GitHub (Nov 16, 2023): > For me it was resolved by purging our cache more explicitly. This is what initially fixed it for me when I mentioned it here https://github.com/doctrine/orm/pull/11027#issuecomment-1793871887 2 weeks ago. By "more explicitly", I mean I had to physically flush the Redis cache manually by flushing the whole redis db - for some reason the cli commands just did not clear the cache "enough". Ive been running the new code in production for 2 weeks now with no issues since that day 2 weeks ago https://github.com/doctrine/orm/pull/11048 Is Redis the clue here? 🤷‍♂️
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

I would prefer not to clear our entire Redis cache.. but it is worth investigating perhaps.

@alcohol commented on GitHub (Nov 16, 2023): I would prefer not to clear our **entire** Redis cache.. but it is worth investigating perhaps.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Clear the query cache (not the result cache, just the query cache) should be sufficient. What happened is I made the serialization format compatible with 3.0.x, so now all mentions of _sqlStatements in the cache should be replaced with sqlStatements, but 2.17.0 should still be able to deserialize an old version of the cache, thanks to e8afa9f80c/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php (L92-L94)

My assumption when writing this piece of code is that you cannot have both properties null at the same time after deserialization.

@greg0ire commented on GitHub (Nov 16, 2023): Clear the query cache (not the result cache, just the query cache) should be sufficient. What happened is I made the serialization format compatible with 3.0.x, so now all mentions of `_sqlStatements` in the cache should be replaced with `sqlStatements`, but 2.17.0 should still be able to deserialize an old version of the cache, thanks to https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/lib/Doctrine/ORM/Query/Exec/AbstractSqlExecutor.php#L92-L94 My assumption when writing this piece of code is that you cannot have both properties null at the same time after deserialization.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

That is a part of our release workflow though; so that is not sufficient unfortunately 😞

@alcohol commented on GitHub (Nov 16, 2023): That is a part of our release workflow though; so that is not sufficient unfortunately :disappointed:
Author
Owner

@itarcontact commented on GitHub (Nov 16, 2023):

@PhilETaylor probably yes. We also use redis and cannot do flush it in the prod environment. This is why we have reverted to 2.16.3.

@itarcontact commented on GitHub (Nov 16, 2023): @PhilETaylor probably yes. We also use redis and cannot do flush it in the prod environment. This is why we have reverted to 2.16.3.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Is null the correct value to check though? What about using isset() instead?

@alcohol commented on GitHub (Nov 16, 2023): Is `null` the correct value to check though? What about using `isset()` instead?
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

I think so yes

@greg0ire commented on GitHub (Nov 16, 2023): [I think so yes](https://3v4l.org/ni43f)
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

If anyone can reproduce this with a test, it would help a lot. The test could be contributed here: https://github.com/doctrine/orm/blob/2.17.x/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php

@greg0ire commented on GitHub (Nov 16, 2023): If anyone can reproduce this with a test, it would help a lot. The test could be contributed here: https://github.com/doctrine/orm/blob/2.17.x/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

I'm at a loss then kind of.

Our own code where the issue starts is:

    /** @return Category[] */
    public function getRootCategories(?int $limit = null, ?int $offset = null): array
    {
        $builder = $this->createQueryBuilder('c');
        $builder
            ->select('c', 't')
            ->leftJoin('c.translations', 't')
            ->where($builder->expr()->isNull('c.parent'))
            ->orderBy('c.weight', Criteria::ASC)
            ->setMaxResults($limit)
            ->setFirstResult($offset)
        ;

        return iterator_to_array(new Paginator($builder->getQuery()));
    }

This is a custom repository class that extends Gedmo\Tree\Entity\Repository\NestedTreeRepository and implements Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface

The entity it manages implements Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface and uses Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait, hence the translations being left joined in the builder.

From there on onwards though it is all Doctrine vendor code.

It even explicitly calls executeIgnoreQueryCache so I don't think the query cache is related to this.

@alcohol commented on GitHub (Nov 16, 2023): I'm at a loss then kind of. Our own code where the issue starts is: ``` /** @return Category[] */ public function getRootCategories(?int $limit = null, ?int $offset = null): array { $builder = $this->createQueryBuilder('c'); $builder ->select('c', 't') ->leftJoin('c.translations', 't') ->where($builder->expr()->isNull('c.parent')) ->orderBy('c.weight', Criteria::ASC) ->setMaxResults($limit) ->setFirstResult($offset) ; return iterator_to_array(new Paginator($builder->getQuery())); } ``` This is a custom repository class that extends `Gedmo\Tree\Entity\Repository\NestedTreeRepository` and implements `Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface` The entity it manages implements `Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface` and uses `Knp\DoctrineBehaviors\Model\Translatable\TranslatableTrait`, hence the translations being left joined in the builder. From there on onwards though it is all Doctrine vendor code. It even explicitly calls `executeIgnoreQueryCache` so I don't think the query cache is related to this.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

I would be very surprised if this is unrelated to my change wrt the query cache.

Here is what a cache entry should look like: e8afa9f80c/tests/Doctrine/Tests/ORM/Functional/ParserResults/single_select_2_17_0.txt (L1)

Can somebody produce a cache entry that produces a broken SingleSelectExecutor object?

@greg0ire commented on GitHub (Nov 16, 2023): I would be very surprised if this is unrelated to my change wrt the query cache. Here is what a cache entry should look like: https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/tests/Doctrine/Tests/ORM/Functional/ParserResults/single_select_2_17_0.txt#L1 Can somebody produce a cache entry that produces a broken `SingleSelectExecutor` object?
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

It seems like the PhpFilesAdapter used in (our) production environment cannot serialize the properties returned by __sleep in AbstractSqlExecutor, which will result in null values in the cache. Changing the code in AbstractSqlExecutor to return the properties without the \0*\0 prefix seems to fix the issue. I'm not sure if this will cause other issues, but this seems to fix it:

return array_values(array_diff(array_keys(get_object_vars($this)), ["_sqlStatements"]));
@kerbert101 commented on GitHub (Nov 16, 2023): It seems like the `PhpFilesAdapter` used in (our) production environment cannot serialize the properties returned by `__sleep` in `AbstractSqlExecutor`, which will result in null values in the cache. Changing the code in `AbstractSqlExecutor` to return the properties without the \0*\0 prefix seems to fix the issue. I'm not sure if this will cause other issues, but this seems to fix it: ```php return array_values(array_diff(array_keys(get_object_vars($this)), ["_sqlStatements"])); ```
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

With the code above:

➜  system git:(master) grep -ir "SqlStatemen" *                                                               
6R70MIgpdM/Z/P/18gNFO5JUiaR9dDGtcRg:            'sqlStatements' => [

The faulty code does not return anything, meaning the sqlStatements key is not being filled at all.

@kerbert101 commented on GitHub (Nov 16, 2023): With the code above: ```shell ➜ system git:(master) grep -ir "SqlStatemen" * 6R70MIgpdM/Z/P/18gNFO5JUiaR9dDGtcRg: 'sqlStatements' => [ ``` The faulty code does not return anything, meaning the sqlStatements key is not being filled at all.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

@kerbert101 what's weird is that the piece of code you're modifying is the one that governs serialization. Its goal is to ensure _sqlStatements is not serialized (since we have sqlStatements). Why are you switching to get_object_vars(), BTW?

@greg0ire commented on GitHub (Nov 16, 2023): @kerbert101 what's weird is that the piece of code you're modifying is the one that governs serialization. Its goal is to ensure `_sqlStatements` is _not_ serialized (since we have `sqlStatements`). Why are you switching to `get_object_vars()`, BTW?
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

@greg0ire Yes, I'm aware of the fact that it governs what to serialize. I'm switching to get_object_vars to get rid of the \0x\0 prefixes, which I thought was causing the issue.

As you can see in the grep result, my modification will still ensure sqlStatements is serialized, not _sqlStatements.

Anyways, i'm not saying this is the fix, but this is what I see

@kerbert101 commented on GitHub (Nov 16, 2023): @greg0ire Yes, I'm aware of the fact that it governs what to serialize. I'm switching to `get_object_vars` to get rid of the \0x\0 prefixes, which I thought was causing the issue. As you can see in the grep result, my modification will still ensure sqlStatements is serialized, not _sqlStatements. Anyways, i'm not saying this is the fix, but this is what I see
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Ok… I'm trying to simplify, but both seem to work the same:

So why did I use (array) $this? Well, get_object_vars() does not seem to work with inheritance: https://3v4l.org/ce10i

@greg0ire commented on GitHub (Nov 16, 2023): Ok… I'm trying to simplify, but both seem to work the same: - https://3v4l.org/MTP9k - https://3v4l.org/4N37P So why did I use `(array) $this`? Well, `get_object_vars()` does not seem to work with inheritance: https://3v4l.org/ce10i
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

The PhpFilesAdapter uses VarExporter.

The result of VarExporter::export in PhpFilesAdapter with my change:

string(2103) "\Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
    $o = [
        clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')),
        clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')),
        clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')),
    ],
    null,
    [
        'Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor' => [
            'sqlStatements' => [
                1 => 'Ive removed info here',
            ],
        ],
        'stdClass' => [
            'scalarMappings' => [2 => ['I removed info here']],
            'typeMappings' => [
                2 => ['Ive removed info here'],
            ],
        ],
    ],
    $o[0],
    [
        [
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [
                'asdf' => [
                    0,
                ],
            ],
        ],
        1,
    ]
)"

Result of unmodified code:

string(1560) "\Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
    $o = [
        clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')),
        clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')),
        clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')),
    ],
    null,
    [
        'stdClass' => [
            'scalarMappings' => [
                2 => ['ive removed info here'],
            ],
            'typeMappings' => [
                2 => ['ive removed info here'],
            ],
        ],
    ],
    $o[0],
    [
        [
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [
                'asdfasdf' => [
                    0,
                ],
            ],
        ],
        1,
    ]
)"

Notice the missing sqlStatements key.

You can check this by dumping the result of VarExporter::export in PhpFilesAdapter on line 227

@kerbert101 commented on GitHub (Nov 16, 2023): The `PhpFilesAdapter` uses `VarExporter`. The result of `VarExporter::export` in `PhpFilesAdapter` with my change: ``` string(2103) "\Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')), clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')), clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')), ], null, [ 'Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor' => [ 'sqlStatements' => [ 1 => 'Ive removed info here', ], ], 'stdClass' => [ 'scalarMappings' => [2 => ['I removed info here']], 'typeMappings' => [ 2 => ['Ive removed info here'], ], ], ], $o[0], [ [ "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [ 'asdf' => [ 0, ], ], ], 1, ] )" ``` Result of unmodified code: ``` string(1560) "\Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')), clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')), clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')), ], null, [ 'stdClass' => [ 'scalarMappings' => [ 2 => ['ive removed info here'], ], 'typeMappings' => [ 2 => ['ive removed info here'], ], ], ], $o[0], [ [ "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [ 'asdfasdf' => [ 0, ], ], ], 1, ] )" ``` Notice the missing `sqlStatements` key. You can check this by dumping the result of `VarExporter::export` in `PhpFilesAdapter` on line 227
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Ah, so no serialize() here maybe? I think you're on to something :)

@greg0ire commented on GitHub (Nov 16, 2023): Ah, so no `serialize()` here maybe? I think you're on to something :)
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

I see that in another class, @derrabus implemented 2 methods instead of one for unserialization: e8afa9f80c/lib/Doctrine/ORM/Query/ParserResult.php (L134-L140)

Maybe VarExporter calls one, and unserialize calls the other?

@greg0ire commented on GitHub (Nov 16, 2023): I see that in another class, @derrabus implemented 2 methods instead of one for unserialization: https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/lib/Doctrine/ORM/Query/ParserResult.php#L134-L140 Maybe `VarExporter` calls one, and `unserialize` calls the other?
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

I could not find any mention of SingleSelectExecutor in our Redis database. I did find it however inside many cached files on disk in var/cache/prod/pools/system, indeed making use of the PhpFilesAdapter format.

Related config

framework:
  cache:
    pools:
      doctrine.query.cache:
        adapters: cache.system
      doctrine.metadata.cache:
        adapters: cache.system

and

doctrine:
  orm:
    entity_managers:
      default:
        metadata_cache_driver:
          type: pool
          pool: doctrine.metadata.cache
        query_cache_driver:
          type: pool
          pool: doctrine.query.cache

cache.system is either a PhpFilesAdapter or a chained adapter using both ApcuAdapter and PhpFilesAdapter, see cf8a997114/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php (L104)

@alcohol commented on GitHub (Nov 16, 2023): I could not find any mention of `SingleSelectExecutor` in our Redis database. I did find it however inside many cached files on disk in `var/cache/prod/pools/system`, indeed making use of the `PhpFilesAdapter` format. Related config ``` framework: cache: pools: doctrine.query.cache: adapters: cache.system doctrine.metadata.cache: adapters: cache.system ``` and ``` doctrine: orm: entity_managers: default: metadata_cache_driver: type: pool pool: doctrine.metadata.cache query_cache_driver: type: pool pool: doctrine.query.cache ``` `cache.system` is either a `PhpFilesAdapter` or a chained adapter using both `ApcuAdapter` and `PhpFilesAdapter`, see https://github.com/symfony/symfony/blob/cf8a99711425df0cb6cc978f64e9bee6e796d7d2/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php#L104
Author
Owner

@lostfocus commented on GitHub (Nov 16, 2023):

Not sure if it is the same issue but updating from 2.16.* to 2.17.* also breaks PHPstan tests in interesting ways.

@lostfocus commented on GitHub (Nov 16, 2023): Not sure if it is the same issue but updating from 2.16.* to 2.17.* also breaks PHPstan tests in interesting ways.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Possible next step: reproduce the issue inside a test. Copy/pasting https://github.com/doctrine/orm/blob/2.17.x/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php#L45-L67 , then replacing the calls to serialize/unserialize with equivalent VarExporter calls (VarExporter and… eval 🙈 ?) should do the trick.

@greg0ire commented on GitHub (Nov 16, 2023): Possible next step: reproduce the issue inside a test. Copy/pasting https://github.com/doctrine/orm/blob/2.17.x/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php#L45-L67 , then replacing the calls to `serialize`/`unserialize` with equivalent `VarExporter` calls (`VarExporter` and… `eval` :see_no_evil: ?) should do the trick.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

@lostfocus definitely not the same.

@greg0ire commented on GitHub (Nov 16, 2023): @lostfocus definitely not the same.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

I can't figure out why I don't have these cached files on my local development environment though. I use the same cache adapter, but nothing seems to be generated.

@alcohol commented on GitHub (Nov 16, 2023): I can't figure out why I don't have these cached files on my local development environment though. I use the same cache adapter, but nothing seems to be generated.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

From what I can tell though, it breaks on production when storing and retrieving the ParserResult instance. So production works on first deploy (no cache). Then as soon as cache items get generated, things break.

@alcohol commented on GitHub (Nov 16, 2023): From what I can tell though, it breaks on production when storing and retrieving the `ParserResult` instance. So production works on first deploy (no cache). Then as soon as cache items get generated, things break.
Author
Owner

@dsands commented on GitHub (Nov 16, 2023):

From what I've determined so far, dev env works fine, prod is the problem. I've completely deleted the cache (rm -fR var/cache/*) to be sure it's really gone for each test. The only way to get prod to function is commenting out the query_cache_driver config (which I guess falls back to array type):

when@prod:
    doctrine:
        orm:
            auto_generate_proxy_classes: false
            proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies'
#            query_cache_driver:
#                type: pool
#                pool: doctrine.system_cache_pool
            result_cache_driver:
                type: pool
                pool: doctrine.result_cache_pool
@dsands commented on GitHub (Nov 16, 2023): From what I've determined so far, dev env works fine, prod is the problem. I've completely deleted the cache (rm -fR var/cache/*) to be sure it's really gone for each test. The only way to get prod to function is commenting out the query_cache_driver config (which I guess falls back to array type): ``` when@prod: doctrine: orm: auto_generate_proxy_classes: false proxy_dir: '%kernel.build_dir%/doctrine/orm/Proxies' # query_cache_driver: # type: pool # pool: doctrine.system_cache_pool result_cache_driver: type: pool pool: doctrine.result_cache_pool ```
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

I can't figure out why I don't have these cached files on my local development environment though. I use the same cache adapter, but nothing seems to be generated.

Try using APP_ENV=prod?

@greg0ire commented on GitHub (Nov 16, 2023): > I can't figure out why I don't have these cached files on my local development environment though. I use the same cache adapter, but nothing seems to be generated. Try using `APP_ENV=prod`?
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Yeah unfortunately I cannot do that, that would connect to production database etc. Our configuration setup is still a bit of a env/files spaghetti mix. I can try turning APP_DEBUG off though.

Edit

Jup, instantly crashed. Guess in debug mode these parser results are never cached?

@alcohol commented on GitHub (Nov 16, 2023): Yeah unfortunately I cannot do that, that would connect to production database etc. Our configuration setup is still a bit of a env/files spaghetti mix. I can try turning `APP_DEBUG` off though. **Edit** Jup, instantly crashed. Guess in debug mode these parser results are never cached?
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Definitely related to storing and retrieving the cache item.


// Modified Query.php private parse() method

$this->parserResult = $parser->parse();

dump($this->parserResult);

$queryCache->save($cacheItem->set($this->parserResult)->expiresAfter($this->queryCacheTTL));

dump($queryCache->getItem($this->getQueryCacheId())->get());

yields

Doctrine\ORM\Query\ParserResult {#22063 ▼
  -sqlExecutor: Doctrine\ORM\Query\Exec\SingleSelectExecutor {#30376 ▼
    #_sqlStatements: &1 "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.icon AS icon_3, c0_.datatricsId AS datatricsId_4, c0_.weight AS weight_5, c0_.filteringEnable ▶"
    #sqlStatements: &1 "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.icon AS icon_3, c0_.datatricsId AS datatricsId_4, c0_.weight AS weight_5, c0_.filteringEnable ▶"
    #queryCacheProfile: null
  }
  -resultSetMapping: Doctrine\ORM\Query\ResultSetMapping {#16080 ▶}
  -parameterMappings: array:1 [▶]
}
 Doctrine\ORM\Query\ParserResult {#27606 ▼
  -sqlExecutor: Doctrine\ORM\Query\Exec\SingleSelectExecutor {#16879 ▼
    #_sqlStatements: &1 null
    #sqlStatements: &1 null
    #queryCacheProfile: null
  }
  -resultSetMapping: Doctrine\ORM\Query\ResultSetMapping {#20733 ▶}
  -parameterMappings: array:1 [▶]
}
@alcohol commented on GitHub (Nov 16, 2023): Definitely related to storing and retrieving the cache item. ```php // Modified Query.php private parse() method $this->parserResult = $parser->parse(); dump($this->parserResult); $queryCache->save($cacheItem->set($this->parserResult)->expiresAfter($this->queryCacheTTL)); dump($queryCache->getItem($this->getQueryCacheId())->get()); ``` yields ``` Doctrine\ORM\Query\ParserResult {#22063 ▼ -sqlExecutor: Doctrine\ORM\Query\Exec\SingleSelectExecutor {#30376 ▼ #_sqlStatements: &1 "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.icon AS icon_3, c0_.datatricsId AS datatricsId_4, c0_.weight AS weight_5, c0_.filteringEnable ▶" #sqlStatements: &1 "SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.icon AS icon_3, c0_.datatricsId AS datatricsId_4, c0_.weight AS weight_5, c0_.filteringEnable ▶" #queryCacheProfile: null } -resultSetMapping: Doctrine\ORM\Query\ResultSetMapping {#16080 ▶} -parameterMappings: array:1 [▶] } Doctrine\ORM\Query\ParserResult {#27606 ▼ -sqlExecutor: Doctrine\ORM\Query\Exec\SingleSelectExecutor {#16879 ▼ #_sqlStatements: &1 null #sqlStatements: &1 null #queryCacheProfile: null } -resultSetMapping: Doctrine\ORM\Query\ResultSetMapping {#20733 ▶} -parameterMappings: array:1 [▶] } ```
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

This is the generated cache entry on disk using 2.17.0

<?php //ec5dca27522cfad6c17af2027e1d3000

return [PHP_INT_MAX, static function () { return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
    $o = [
        clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')),
        clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')),
        clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')),
    ],
    null,
    [
        'stdClass' => [
            'aliasMap' => [
                2 => [
                    'node' => '<redacted>\\Entity\\Category',
                ],
            ],
            'fieldMappings' => [
                2 => [
                    'id_0' => 'id',
                    'name_1' => 'name',
                    'slug_2' => 'slug',
                    'icon_3' => 'icon',
                    'datatricsId_4' => 'datatricsId',
                    'weight_5' => 'weight',
                    'filteringEnabled_6' => 'filteringEnabled',
                    'root_7' => 'root',
                    'lvl_8' => 'level',
                    'lft_9' => 'left',
                    'rgt_10' => 'right',
                    'createdAt_11' => 'createdAt',
                    'updatedAt_12' => 'updatedAt',
                ],
            ],
            'typeMappings' => [
                2 => [
                    'parent_id_13' => 'smallint',
                    'seoDetails_id_14' => 'integer',
                ],
            ],
            'entityMappings' => [
                2 => [
                    'node' => null,
                ],
            ],
            'metaMappings' => [
                2 => [
                    'parent_id_13' => 'parent_id',
                    'seoDetails_id_14' => 'seoDetails_id',
                ],
            ],
            'columnOwnerMap' => [
                2 => [
                    'id_0' => 'node',
                    'name_1' => 'node',
                    'slug_2' => 'node',
                    'icon_3' => 'node',
                    'datatricsId_4' => 'node',
                    'weight_5' => 'node',
                    'filteringEnabled_6' => 'node',
                    'root_7' => 'node',
                    'lvl_8' => 'node',
                    'lft_9' => 'node',
                    'rgt_10' => 'node',
                    'createdAt_11' => 'node',
                    'updatedAt_12' => 'node',
                    'parent_id_13' => 'node',
                    'seoDetails_id_14' => 'node',
                ],
            ],
            'declaringClasses' => [
                2 => [
                    'id_0' => '<redacted>\\Entity\\Category',
                    'name_1' => '<redacted>\\Entity\\Category',
                    'slug_2' => '<redacted>\\Entity\\Category',
                    'icon_3' => '<redacted>\\Entity\\Category',
                    'datatricsId_4' => '<redacted>\\Entity\\Category',
                    'weight_5' => '<redacted>\\Entity\\Category',
                    'filteringEnabled_6' => '<redacted>\\Entity\\Category',
                    'root_7' => '<redacted>\\Entity\\Category',
                    'lvl_8' => '<redacted>\\Entity\\Category',
                    'lft_9' => '<redacted>\\Entity\\Category',
                    'rgt_10' => '<redacted>\\Entity\\Category',
                    'createdAt_11' => '<redacted>\\Entity\\Category',
                    'updatedAt_12' => '<redacted>\\Entity\\Category',
                ],
            ],
        ],
    ],
    $o[0],
    [
        [
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [
                'rid' => [
                    0,
                ],
            ],
        ],
        1,
    ]
); }];
@alcohol commented on GitHub (Nov 16, 2023): This is the generated cache entry on disk using `2.17.0` ```php <?php //ec5dca27522cfad6c17af2027e1d3000 return [PHP_INT_MAX, static function () { return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')), clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')), clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')), ], null, [ 'stdClass' => [ 'aliasMap' => [ 2 => [ 'node' => '<redacted>\\Entity\\Category', ], ], 'fieldMappings' => [ 2 => [ 'id_0' => 'id', 'name_1' => 'name', 'slug_2' => 'slug', 'icon_3' => 'icon', 'datatricsId_4' => 'datatricsId', 'weight_5' => 'weight', 'filteringEnabled_6' => 'filteringEnabled', 'root_7' => 'root', 'lvl_8' => 'level', 'lft_9' => 'left', 'rgt_10' => 'right', 'createdAt_11' => 'createdAt', 'updatedAt_12' => 'updatedAt', ], ], 'typeMappings' => [ 2 => [ 'parent_id_13' => 'smallint', 'seoDetails_id_14' => 'integer', ], ], 'entityMappings' => [ 2 => [ 'node' => null, ], ], 'metaMappings' => [ 2 => [ 'parent_id_13' => 'parent_id', 'seoDetails_id_14' => 'seoDetails_id', ], ], 'columnOwnerMap' => [ 2 => [ 'id_0' => 'node', 'name_1' => 'node', 'slug_2' => 'node', 'icon_3' => 'node', 'datatricsId_4' => 'node', 'weight_5' => 'node', 'filteringEnabled_6' => 'node', 'root_7' => 'node', 'lvl_8' => 'node', 'lft_9' => 'node', 'rgt_10' => 'node', 'createdAt_11' => 'node', 'updatedAt_12' => 'node', 'parent_id_13' => 'node', 'seoDetails_id_14' => 'node', ], ], 'declaringClasses' => [ 2 => [ 'id_0' => '<redacted>\\Entity\\Category', 'name_1' => '<redacted>\\Entity\\Category', 'slug_2' => '<redacted>\\Entity\\Category', 'icon_3' => '<redacted>\\Entity\\Category', 'datatricsId_4' => '<redacted>\\Entity\\Category', 'weight_5' => '<redacted>\\Entity\\Category', 'filteringEnabled_6' => '<redacted>\\Entity\\Category', 'root_7' => '<redacted>\\Entity\\Category', 'lvl_8' => '<redacted>\\Entity\\Category', 'lft_9' => '<redacted>\\Entity\\Category', 'rgt_10' => '<redacted>\\Entity\\Category', 'createdAt_11' => '<redacted>\\Entity\\Category', 'updatedAt_12' => '<redacted>\\Entity\\Category', ], ], ], ], $o[0], [ [ "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [ 'rid' => [ 0, ], ], ], 1, ] ); }]; ```
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

On 2.16.3 it yields this file:

<?php //ec5dca27522cfad6c17af2027e1d3000

return [PHP_INT_MAX, static function () { return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
    $o = [
        clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')),
        clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')),
        clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')),
    ],
    null,
    [
        'Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor' => [
            '_sqlStatements' => [
                1 => 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.icon AS icon_3, c0_.datatricsId AS datatricsId_4, c0_.weight AS weight_5, c0_.filteringEnabled AS filteringEnabled_6, c0_.root AS root_7, c0_.lvl AS lvl_8, c0_.lft AS lft_9, c0_.rgt AS rgt_10, c0_.createdAt AS createdAt_11, c0_.updatedAt AS updatedAt_12, c0_.parent_id AS parent_id_13, c0_.seoDetails_id AS seoDetails_id_14 FROM category c0_ WHERE c0_.lft <= 1 AND c0_.rgt >= 2 AND c0_.root = ? ORDER BY c0_.lft ASC',
            ],
        ],
        'stdClass' => [
            'aliasMap' => [
                2 => [
                    'node' => '<redacted>\\Entity\\Category',
                ],
            ],
            'fieldMappings' => [
                2 => [
                    'id_0' => 'id',
                    'name_1' => 'name',
                    'slug_2' => 'slug',
                    'icon_3' => 'icon',
                    'datatricsId_4' => 'datatricsId',
                    'weight_5' => 'weight',
                    'filteringEnabled_6' => 'filteringEnabled',
                    'root_7' => 'root',
                    'lvl_8' => 'level',
                    'lft_9' => 'left',
                    'rgt_10' => 'right',
                    'createdAt_11' => 'createdAt',
                    'updatedAt_12' => 'updatedAt',
                ],
            ],
            'typeMappings' => [
                2 => [
                    'parent_id_13' => 'smallint',
                    'seoDetails_id_14' => 'integer',
                ],
            ],
            'entityMappings' => [
                2 => [
                    'node' => null,
                ],
            ],
            'metaMappings' => [
                2 => [
                    'parent_id_13' => 'parent_id',
                    'seoDetails_id_14' => 'seoDetails_id',
                ],
            ],
            'columnOwnerMap' => [
                2 => [
                    'id_0' => 'node',
                    'name_1' => 'node',
                    'slug_2' => 'node',
                    'icon_3' => 'node',
                    'datatricsId_4' => 'node',
                    'weight_5' => 'node',
                    'filteringEnabled_6' => 'node',
                    'root_7' => 'node',
                    'lvl_8' => 'node',
                    'lft_9' => 'node',
                    'rgt_10' => 'node',
                    'createdAt_11' => 'node',
                    'updatedAt_12' => 'node',
                    'parent_id_13' => 'node',
                    'seoDetails_id_14' => 'node',
                ],
            ],
            'declaringClasses' => [
                2 => [
                    'id_0' => '<redacted>\\Entity\\Category',
                    'name_1' => '<redacted>\\Entity\\Category',
                    'slug_2' => '<redacted>\\Entity\\Category',
                    'icon_3' => '<redacted>\\Entity\\Category',
                    'datatricsId_4' => '<redacted>\\Entity\\Category',
                    'weight_5' => '<redacted>\\Entity\\Category',
                    'filteringEnabled_6' => '<redacted>\\Entity\\Category',
                    'root_7' => '<redacted>\\Entity\\Category',
                    'lvl_8' => '<redacted>\\Entity\\Category',
                    'lft_9' => '<redacted>\\Entity\\Category',
                    'rgt_10' => '<redacted>\\Entity\\Category',
                    'createdAt_11' => '<redacted>\\Entity\\Category',
                    'updatedAt_12' => '<redacted>\\Entity\\Category',
                ],
            ],
        ],
    ],
    $o[0],
    [
        [
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2],
            "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [
                'rid' => [
                    0,
                ],
            ],
        ],
    ]
); }];
@alcohol commented on GitHub (Nov 16, 2023): On `2.16.3` it yields this file: ```php <?php //ec5dca27522cfad6c17af2027e1d3000 return [PHP_INT_MAX, static function () { return \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ clone (($p = &\Symfony\Component\VarExporter\Internal\Registry::$prototypes)['Doctrine\\ORM\\Query\\ParserResult'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ParserResult')), clone ($p['Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\Exec\\SingleSelectExecutor')), clone ($p['Doctrine\\ORM\\Query\\ResultSetMapping'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Doctrine\\ORM\\Query\\ResultSetMapping')), ], null, [ 'Doctrine\\ORM\\Query\\Exec\\AbstractSqlExecutor' => [ '_sqlStatements' => [ 1 => 'SELECT c0_.id AS id_0, c0_.name AS name_1, c0_.slug AS slug_2, c0_.icon AS icon_3, c0_.datatricsId AS datatricsId_4, c0_.weight AS weight_5, c0_.filteringEnabled AS filteringEnabled_6, c0_.root AS root_7, c0_.lvl AS lvl_8, c0_.lft AS lft_9, c0_.rgt AS rgt_10, c0_.createdAt AS createdAt_11, c0_.updatedAt AS updatedAt_12, c0_.parent_id AS parent_id_13, c0_.seoDetails_id AS seoDetails_id_14 FROM category c0_ WHERE c0_.lft <= 1 AND c0_.rgt >= 2 AND c0_.root = ? ORDER BY c0_.lft ASC', ], ], 'stdClass' => [ 'aliasMap' => [ 2 => [ 'node' => '<redacted>\\Entity\\Category', ], ], 'fieldMappings' => [ 2 => [ 'id_0' => 'id', 'name_1' => 'name', 'slug_2' => 'slug', 'icon_3' => 'icon', 'datatricsId_4' => 'datatricsId', 'weight_5' => 'weight', 'filteringEnabled_6' => 'filteringEnabled', 'root_7' => 'root', 'lvl_8' => 'level', 'lft_9' => 'left', 'rgt_10' => 'right', 'createdAt_11' => 'createdAt', 'updatedAt_12' => 'updatedAt', ], ], 'typeMappings' => [ 2 => [ 'parent_id_13' => 'smallint', 'seoDetails_id_14' => 'integer', ], ], 'entityMappings' => [ 2 => [ 'node' => null, ], ], 'metaMappings' => [ 2 => [ 'parent_id_13' => 'parent_id', 'seoDetails_id_14' => 'seoDetails_id', ], ], 'columnOwnerMap' => [ 2 => [ 'id_0' => 'node', 'name_1' => 'node', 'slug_2' => 'node', 'icon_3' => 'node', 'datatricsId_4' => 'node', 'weight_5' => 'node', 'filteringEnabled_6' => 'node', 'root_7' => 'node', 'lvl_8' => 'node', 'lft_9' => 'node', 'rgt_10' => 'node', 'createdAt_11' => 'node', 'updatedAt_12' => 'node', 'parent_id_13' => 'node', 'seoDetails_id_14' => 'node', ], ], 'declaringClasses' => [ 2 => [ 'id_0' => '<redacted>\\Entity\\Category', 'name_1' => '<redacted>\\Entity\\Category', 'slug_2' => '<redacted>\\Entity\\Category', 'icon_3' => '<redacted>\\Entity\\Category', 'datatricsId_4' => '<redacted>\\Entity\\Category', 'weight_5' => '<redacted>\\Entity\\Category', 'filteringEnabled_6' => '<redacted>\\Entity\\Category', 'root_7' => '<redacted>\\Entity\\Category', 'lvl_8' => '<redacted>\\Entity\\Category', 'lft_9' => '<redacted>\\Entity\\Category', 'rgt_10' => '<redacted>\\Entity\\Category', 'createdAt_11' => '<redacted>\\Entity\\Category', 'updatedAt_12' => '<redacted>\\Entity\\Category', ], ], ], ], $o[0], [ [ "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'sqlExecutor' => $o[1], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'resultSetMapping' => $o[2], "\0".'Doctrine\\ORM\\Query\\ParserResult'."\0".'parameterMappings' => [ 'rid' => [ 0, ], ], ], ] ); }]; ```
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

We can see that no properties for the SingleSelectExecutor (nor its parent AbstractSqlExecutor) are exported by the exporter when running 2.17.0.

@alcohol commented on GitHub (Nov 16, 2023): We can see that no properties for the `SingleSelectExecutor` (nor its parent `AbstractSqlExecutor`) are exported by the exporter when running `2.17.0`.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Perhaps @nicolas-grekas has some insights into how/why this change breaks things?

@alcohol commented on GitHub (Nov 16, 2023): Perhaps @nicolas-grekas has some insights into how/why this change breaks things?
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

I'd appreciate a reproducer so that I could quickly reproduce the issue.

@nicolas-grekas commented on GitHub (Nov 16, 2023): I'd appreciate a reproducer so that I could quickly reproduce the issue.
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

@nicolas-grekas in short, this is the problem:

<?php

use Symfony\Component\VarExporter\VarExporter;

require_once '/path/to/project-with-var-exporter/autoload.php';

class T {
  protected $a;

  public function __construct()
  {
    $this->a = '123';
  }

  public function __sleep(): array
  {
    //return ['a'];
    return array_keys((array) $this);
  }
}

var_dump(VarExporter::export(new T()));
var_dump(serialize(new T()));

The (array) $this returns the properties with the internal visibility names, which does not work with VarExporter.

Returning those properties with the internal visibility names and executing serialize on it will work however.

@kerbert101 commented on GitHub (Nov 16, 2023): @nicolas-grekas in short, this is the problem: ```php <?php use Symfony\Component\VarExporter\VarExporter; require_once '/path/to/project-with-var-exporter/autoload.php'; class T { protected $a; public function __construct() { $this->a = '123'; } public function __sleep(): array { //return ['a']; return array_keys((array) $this); } } var_dump(VarExporter::export(new T())); var_dump(serialize(new T())); ``` The (array) $this returns the properties with the internal visibility names, which does not work with VarExporter. Returning those properties with the internal visibility names and executing serialize on it will work however.
Author
Owner

@mbabker commented on GitHub (Nov 16, 2023):

FWIW I just added the query cache to my Symfony app's dev environment after updating the ORM to 2.17 and the profiler now has log messages like this:

Failed to save key "af7aa6b4c18e4470d7048b5425a8b0f3" of type Doctrine\ORM\Query\ParserResult: Cache key "af7aa6b4c18e4470d7048b5425a8b0f3" has non-serializable "Doctrine\ORM\Query\ParserResult" value.

No other changes in the app.

Dumping the exception the VarExporter throws here just gives a "User notice: serialize()" message with this trace:

#0 /app/vendor/symfony/var-exporter/Internal/Exporter.php(185): Symfony\Component\VarExporter\Internal\Exporter::prepare(Array, Object(SplObjectStorage), Array, 0, false)
#1 /app/vendor/symfony/var-exporter/VarExporter.php(53): Symfony\Component\VarExporter\Internal\Exporter::prepare(Array, Object(SplObjectStorage), Array, 0, true)
#2 /app/vendor/symfony/cache/Adapter/PhpFilesAdapter.php(218): Symfony\Component\VarExporter\VarExporter::export(Object(Doctrine\ORM\Query\ParserResult), true)
#3 /app/vendor/symfony/cache/Adapter/AbstractAdapter.php(148): Symfony\Component\Cache\Adapter\PhpFilesAdapter->doSave(Array, 0)
#4 /app/vendor/symfony/cache/Traits/AbstractAdapterTrait.php(239): Symfony\Component\Cache\Adapter\AbstractAdapter->commit()
#5 /app/vendor/symfony/cache/Adapter/TraceableAdapter.php(108): Symfony\Component\Cache\Adapter\AbstractAdapter->save(Object(Symfony\Component\Cache\CacheItem))
#6 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php(278): Symfony\Component\Cache\Adapter\TraceableAdapter->save(Object(Symfony\Component\Cache\CacheItem))
#7 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php(288): Doctrine\ORM\Query->parse()
#8 /app/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1212): Doctrine\ORM\Query->_doExecute()
#9 /app/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1166): Doctrine\ORM\AbstractQuery->executeIgnoreQueryCache(NULL, 3)
#10 /app/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(937): Doctrine\ORM\AbstractQuery->execute(NULL, 3)
#11 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php(153): Doctrine\ORM\AbstractQuery->getScalarResult()
#12 /app/vendor/pagerfanta/doctrine-orm-adapter/QueryAdapter.php(57): Doctrine\ORM\Tools\Pagination\Paginator->getIterator()
#13 /app/vendor/pagerfanta/core/Pagerfanta.php(252): Pagerfanta\Doctrine\ORM\QueryAdapter->getSlice(0, 9999)
#14 /app/vendor/pagerfanta/core/Pagerfanta.php(238): Pagerfanta\Pagerfanta->getCurrentPageResultsFromAdapter()
#15 /app/src/Auction/Controller/API/EventsController.php(325): Pagerfanta\Pagerfanta->getCurrentPageResults()
#16 /app/src/Auction/Controller/API/EventsController.php(281): App\Auction\Controller\API\EventsController->buildEventSectionListResponse(Object(App\Auction\Model\PaginatedEventsListRequestFilters), Object(Doctrine\ORM\QueryBuilder), false, 'upcoming_events...', 'upcoming_events...', 'upcoming_events...', 'upcoming_events...')
#17 /app/vendor/symfony/http-kernel/HttpKernel.php(181): App\Auction\Controller\API\EventsController->getUpcomingEvents(Object(App\Auction\Repository\AuctionRepository), Object(App\Auction\Model\PaginatedEventsListRequestFilters))
#18 /app/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#19 /app/vendor/symfony/http-kernel/Kernel.php(197): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#20 /app/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php(35): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#21 /app/vendor/autoload_runtime.php(29): Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
#22 /app/public/index.php(7): require_once('/app...')
#23 {main}
@mbabker commented on GitHub (Nov 16, 2023): FWIW I just added the query cache to my Symfony app's dev environment after updating the ORM to 2.17 and the profiler now has log messages like this: ```sh Failed to save key "af7aa6b4c18e4470d7048b5425a8b0f3" of type Doctrine\ORM\Query\ParserResult: Cache key "af7aa6b4c18e4470d7048b5425a8b0f3" has non-serializable "Doctrine\ORM\Query\ParserResult" value. ``` No other changes in the app. Dumping the exception the VarExporter throws [here](https://github.com/symfony/cache/blob/v6.3.8/Adapter/PhpFilesAdapter.php#L217-L221) just gives a "User notice: serialize()" message with this trace: ```sh #0 /app/vendor/symfony/var-exporter/Internal/Exporter.php(185): Symfony\Component\VarExporter\Internal\Exporter::prepare(Array, Object(SplObjectStorage), Array, 0, false) #1 /app/vendor/symfony/var-exporter/VarExporter.php(53): Symfony\Component\VarExporter\Internal\Exporter::prepare(Array, Object(SplObjectStorage), Array, 0, true) #2 /app/vendor/symfony/cache/Adapter/PhpFilesAdapter.php(218): Symfony\Component\VarExporter\VarExporter::export(Object(Doctrine\ORM\Query\ParserResult), true) #3 /app/vendor/symfony/cache/Adapter/AbstractAdapter.php(148): Symfony\Component\Cache\Adapter\PhpFilesAdapter->doSave(Array, 0) #4 /app/vendor/symfony/cache/Traits/AbstractAdapterTrait.php(239): Symfony\Component\Cache\Adapter\AbstractAdapter->commit() #5 /app/vendor/symfony/cache/Adapter/TraceableAdapter.php(108): Symfony\Component\Cache\Adapter\AbstractAdapter->save(Object(Symfony\Component\Cache\CacheItem)) #6 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php(278): Symfony\Component\Cache\Adapter\TraceableAdapter->save(Object(Symfony\Component\Cache\CacheItem)) #7 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php(288): Doctrine\ORM\Query->parse() #8 /app/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1212): Doctrine\ORM\Query->_doExecute() #9 /app/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1166): Doctrine\ORM\AbstractQuery->executeIgnoreQueryCache(NULL, 3) #10 /app/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(937): Doctrine\ORM\AbstractQuery->execute(NULL, 3) #11 /app/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/Paginator.php(153): Doctrine\ORM\AbstractQuery->getScalarResult() #12 /app/vendor/pagerfanta/doctrine-orm-adapter/QueryAdapter.php(57): Doctrine\ORM\Tools\Pagination\Paginator->getIterator() #13 /app/vendor/pagerfanta/core/Pagerfanta.php(252): Pagerfanta\Doctrine\ORM\QueryAdapter->getSlice(0, 9999) #14 /app/vendor/pagerfanta/core/Pagerfanta.php(238): Pagerfanta\Pagerfanta->getCurrentPageResultsFromAdapter() #15 /app/src/Auction/Controller/API/EventsController.php(325): Pagerfanta\Pagerfanta->getCurrentPageResults() #16 /app/src/Auction/Controller/API/EventsController.php(281): App\Auction\Controller\API\EventsController->buildEventSectionListResponse(Object(App\Auction\Model\PaginatedEventsListRequestFilters), Object(Doctrine\ORM\QueryBuilder), false, 'upcoming_events...', 'upcoming_events...', 'upcoming_events...', 'upcoming_events...') #17 /app/vendor/symfony/http-kernel/HttpKernel.php(181): App\Auction\Controller\API\EventsController->getUpcomingEvents(Object(App\Auction\Repository\AuctionRepository), Object(App\Auction\Model\PaginatedEventsListRequestFilters)) #18 /app/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1) #19 /app/vendor/symfony/http-kernel/Kernel.php(197): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #20 /app/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php(35): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request)) #21 /app/vendor/autoload_runtime.php(29): Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run() #22 /app/public/index.php(7): require_once('/app...') #23 {main} ```
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

<?php

require 'vendor/autoload.php';

use Symfony\Component\VarExporter\VarExporter;

class Foo
{
    protected $_test;

    public function set(string $value): self
    {
        $this->_test = $value;

        return $this;
    }
}

class Bar
{
    protected $_test;
    protected $test;

    public function __construct()
    {
        $this->_test = &$this->test;
    }

    public function set(string $value): self
    {
        $this->test = $value;

        return $this;
    }

    public function __sleep(): array
    {
        return array_values(array_diff(array_keys((array) $this), ["\0*\0_test"]));
    }

    public function __wakeup(): void
    {
        if ($this->_test !== null && $this->test === null) {
            $this->test = $this->_test;
        }

        $this->_test = &$this->test;
    }
}

echo VarExporter::export((new Foo())->set('foo'));

echo VarExporter::export((new Bar())->set('foo'));
\Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
    $o = [
        clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Foo'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Foo')),
    ],
    null,
    [
        'Foo' => [
            '_test' => [
                'foo',
            ],
        ],
    ],
    $o[0],
    []
)

// Notice: serialize(): " in /workdir/vendor/symfony/var-exporter/Internal/Exporter.php on line 175

\Symfony\Component\VarExporter\Internal\Hydrator::hydrate(
    $o = [
        clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Bar'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Bar')),
    ],
    null,
    [],
    $o[0],
    [
        1 => 0,
    ]
)
@alcohol commented on GitHub (Nov 16, 2023): ```php <?php require 'vendor/autoload.php'; use Symfony\Component\VarExporter\VarExporter; class Foo { protected $_test; public function set(string $value): self { $this->_test = $value; return $this; } } class Bar { protected $_test; protected $test; public function __construct() { $this->_test = &$this->test; } public function set(string $value): self { $this->test = $value; return $this; } public function __sleep(): array { return array_values(array_diff(array_keys((array) $this), ["\0*\0_test"])); } public function __wakeup(): void { if ($this->_test !== null && $this->test === null) { $this->test = $this->_test; } $this->_test = &$this->test; } } echo VarExporter::export((new Foo())->set('foo')); echo VarExporter::export((new Bar())->set('foo')); ``` ```php \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Foo'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Foo')), ], null, [ 'Foo' => [ '_test' => [ 'foo', ], ], ], $o[0], [] ) // Notice: serialize(): " in /workdir/vendor/symfony/var-exporter/Internal/Exporter.php on line 175 \Symfony\Component\VarExporter\Internal\Hydrator::hydrate( $o = [ clone (\Symfony\Component\VarExporter\Internal\Registry::$prototypes['Bar'] ?? \Symfony\Component\VarExporter\Internal\Registry::p('Bar')), ], null, [], $o[0], [ 1 => 0, ] ) ```
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

@greg0ire It seems @kerbert101 hit the nail on the head. Any particular reason you are using the (array) $this approach? The documentation @ https://www.php.net/manual/en/language.oop5.magic.php also merely documents using the exact property names (no matter if they are public, protected or private).

@alcohol commented on GitHub (Nov 16, 2023): @greg0ire It seems @kerbert101 hit the nail on the head. Any particular reason you are using the `(array) $this` approach? The documentation @ https://www.php.net/manual/en/language.oop5.magic.php also merely documents using the exact property names (no matter if they are public, protected or private).
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

One could argue that this method should not return the properties with their internal visibility names, but as @greg0ire mentioned before, get_object_vars is not an option in this case as this will skip the private properties of subclasses. Using Reflection is another option, but I'm not sure if that's the best option in this case neither 😕

@kerbert101 commented on GitHub (Nov 16, 2023): One could argue that this method should not return the properties with their internal visibility names, but as @greg0ire mentioned before, get_object_vars is not an option in this case as this will skip the private properties of subclasses. Using Reflection is another option, but I'm not sure if that's the best option in this case neither 😕
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

get_object_vars is not an option in this case as this will skip the private properties of subclasses.

Yes, that and also I thought I would be consistent with e8afa9f80c/lib/Doctrine/ORM/Query/ParserResult.php#

@greg0ire commented on GitHub (Nov 16, 2023): > get_object_vars is not an option in this case as this will skip the private properties of subclasses. Yes, that and also I thought I would be consistent with https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/lib/Doctrine/ORM/Query/ParserResult.php#
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

Is it legal in PHP to have sleep return property identifiers? I'm not aware of it. The PHP notice also means something is odd for the engine. __serialize can but __sleep???

@nicolas-grekas commented on GitHub (Nov 16, 2023): Is it legal in PHP to have sleep return property identifiers? I'm not aware of it. The PHP notice also means something is odd for the engine. __serialize can but __sleep???
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

What about making the __sleep() method abstract too (in AbstractSqlExecutor) and have each child be explicitly about what to serialize by implementing their own __sleep()?

@alcohol commented on GitHub (Nov 16, 2023): What about making the `__sleep()` method abstract too (in `AbstractSqlExecutor`) and have each child be explicitly about what to serialize by implementing their own `__sleep()`?
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Is it legal in PHP to have sleep return property identifiers? I'm not aware of it. The PHP notice also means something is odd for the engine. __serialize can but __sleep???

That's precisely how it is documented though? https://www.php.net/manual/en/language.oop5.magic.php#object.sleep

"It can clean up the object and is supposed to return an array with the names of all variables of that object that should be serialized."

@alcohol commented on GitHub (Nov 16, 2023): > Is it legal in PHP to have sleep return property identifiers? I'm not aware of it. The PHP notice also means something is odd for the engine. __serialize can but __sleep??? That's precisely how it is documented though? https://www.php.net/manual/en/language.oop5.magic.php#object.sleep "It can clean up the object and is supposed to return an array with the names of all variables of that object that should be serialized."
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

It is not possible for __sleep() to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Use __serialize() instead.

it's not to me

@nicolas-grekas commented on GitHub (Nov 16, 2023): > It is not possible for [__sleep()](https://www.php.net/manual/en/language.oop5.magic.php#object.sleep) to return names of private properties in parent classes. Doing this will result in an E_NOTICE level error. Use [__serialize()](https://www.php.net/manual/en/language.oop5.magic.php#object.serialize) instead. it's not to me
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Weird that there is a notice, I don't see one when running the test that contains this: e8afa9f80c/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php (L36)

@greg0ire commented on GitHub (Nov 16, 2023): Weird that there is a notice, I don't see one when running the test that contains this: https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php#L36
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

But do we have parent classes here? The properties are protected, not private, and they are implemented by children of the abstract class.

@alcohol commented on GitHub (Nov 16, 2023): But do we have parent classes here? The properties are protected, not private, and they are implemented by children of the abstract class.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

Weird that there is a notice, I don't see one when running the test that contains this:

e8afa9f80c/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php (L36)

It's triggered by the exporter using trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $n), \E_USER_NOTICE);.

@alcohol commented on GitHub (Nov 16, 2023): > Weird that there is a notice, I don't see one when running the test that contains this: > > https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/tests/Doctrine/Tests/ORM/Functional/ParserResultSerializationTest.php#L36 It's triggered by the exporter using `trigger_error(sprintf('serialize(): "%s" returned as member variable from __sleep() but does not exist', $n), \E_USER_NOTICE);`.
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

Let me be more explicit: __sleep must return property names, not property identifiers. The difference is the \0-based prefix. __sleep cannot return any of them.

@nicolas-grekas commented on GitHub (Nov 16, 2023): Let me be more explicit: `__sleep` must return property names, not property identifiers. The difference is the `\0`-based prefix. `__sleep` cannot return any of them.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

OK. So this is a Symfony bug, right?

@greg0ire commented on GitHub (Nov 16, 2023): OK. So this is a Symfony bug, right?
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

Nope, it's a Doctrine bug, __sleep must return property names without the \0-based prefix.

@nicolas-grekas commented on GitHub (Nov 16, 2023): Nope, it's a Doctrine bug, `__sleep` must return property names *without* the `\0`-based prefix.
Author
Owner

@alcohol commented on GitHub (Nov 16, 2023):

@nicolas-grekas I do think you are correct in that __sleep should return just their plain names, not magic names. But I believe @greg0ire used this approach for reasons and it somehow works with just plain php serialize/unserialize.

@alcohol commented on GitHub (Nov 16, 2023): @nicolas-grekas I do think you are correct in that `__sleep` should return just their plain names, not magic names. But I believe @greg0ire used this approach for _reasons_ and it somehow works with just plain php serialize/unserialize.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

How is it a Doctrine bug if everything works (there is not even a notice if I understood correctly) when using serialize, but it breaks when using VarExporter? Can somebody reproduce the bug in a test using Doctrine APIs and only them?

@greg0ire commented on GitHub (Nov 16, 2023): How is it a Doctrine bug if everything works (there is not even a notice if I understood correctly) when using `serialize`, but it breaks when using `VarExporter`? Can somebody reproduce the bug in a test using Doctrine APIs and only them?
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

It works because it's broken : __sleep fails and then php likely falls back to standard behavior, ignoring __sleep, thus generating a serialized payload that works, but doesn't achieve the wanted compatibility I'd say. It's my guess at least :)

@nicolas-grekas commented on GitHub (Nov 16, 2023): It works because it's broken : __sleep fails and then php likely falls back to standard behavior, ignoring __sleep, thus generating a serialized payload that works, but doesn't achieve the wanted compatibility I'd say. It's my guess at least :)
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

And the difference of behavior might be related to VarExporter not falling back to the exact same behavior when __sleep returns a broken payload. That'd explain everything. But the root issue is in Doctrine.

@nicolas-grekas commented on GitHub (Nov 16, 2023): And the difference of behavior might be related to VarExporter not falling back to the exact same behavior when __sleep returns a broken payload. That'd explain everything. But the root issue is in Doctrine.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

@nicolas-grekas I think it works just fine: https://3v4l.org/3ckU7 If PHP was falling back to standard behavior then we would see 2 properties, and here there is just sqlStatements.

@greg0ire commented on GitHub (Nov 16, 2023): @nicolas-grekas I think it works just fine: https://3v4l.org/3ckU7 If PHP was falling back to standard behavior then we would see 2 properties, and here there is just `sqlStatements`.
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

Oh, OK! Then you're relying on undocumented behavior of __sleep! (and unimplemented in VarExporter)

@nicolas-grekas commented on GitHub (Nov 16, 2023): Oh, OK! Then you're relying on undocumented behavior of __sleep! (and unimplemented in VarExporter)
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Yeah that sounds more plausible 😅
My process wasn't to go through the docs, but I stumbled upon other occurrences of \0 in the code, then on this: https://www.phpinternalsbook.com/php5/classes_objects/serialization.html

It's not part of the official docs, but I thought it would be good enough.

The other occurrences, for reference: e8afa9f80c/lib/Doctrine/ORM/Query/ParserResult.php (L143-L144)

I think to address this, a possibility might be to iterate of the result of (array) $this and strip the \0*\0 part, before doing the diff with ['_sqlStatements']. That prefix, is probably here for a reason though 🤔 … avoiding collisions?

@greg0ire commented on GitHub (Nov 16, 2023): Yeah that sounds more plausible :sweat_smile: My process wasn't to go through the docs, but I stumbled upon other occurrences of `\0` in the code, then on this: https://www.phpinternalsbook.com/php5/classes_objects/serialization.html It's not part of the official docs, but I thought it would be good enough. The other occurrences, for reference: https://github.com/doctrine/orm/blob/e8afa9f80ce66ac728f63325d102adda01cbd6ff/lib/Doctrine/ORM/Query/ParserResult.php#L143-L144 I think to address this, a possibility might be to iterate of the result of `(array) $this` and strip the `\0*\0` part, before doing the diff with `['_sqlStatements']`. That prefix, is probably here for a reason though :thinking: … avoiding collisions?
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

\0*\0 is needless, __sleep works with protected properties without the prefix. The prefix might be required for private properties of parent classes. If that doesn't apply to this use case, removing the prefix is the right workaround for Doctrine.

@nicolas-grekas commented on GitHub (Nov 16, 2023): `\0*\0` is needless, __sleep works with protected properties without the prefix. The prefix might be required for private properties of *parent* classes. If that doesn't apply to this use case, removing the prefix is the right workaround for Doctrine.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

I think it would work just fine, unless somebody external to Doctrine have their own extending class, with their own _sqlStatements private property defined in it… pretty far-fetched.

@greg0ire commented on GitHub (Nov 16, 2023): I think it would work just fine, unless somebody external to Doctrine have their own extending class, with their own `_sqlStatements` private property defined in it… pretty far-fetched.
Author
Owner

@kerbert101 commented on GitHub (Nov 16, 2023):

@greg0ire https://github.com/doctrine/orm/pull/11065

@kerbert101 commented on GitHub (Nov 16, 2023): @greg0ire https://github.com/doctrine/orm/pull/11065
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

@kerbert101 thanks for working on this!

@greg0ire commented on GitHub (Nov 16, 2023): @kerbert101 thanks for working on this!
Author
Owner

@nicolas-grekas commented on GitHub (Nov 16, 2023):

Fix for Symfony: https://github.com/symfony/symfony/pull/52618

Thank you all for the help on this one!

@nicolas-grekas commented on GitHub (Nov 16, 2023): Fix for Symfony: https://github.com/symfony/symfony/pull/52618 Thank you all for the help on this one!
Author
Owner

@symfonyaml commented on GitHub (Nov 16, 2023):

Upgrading from 2.16.3 to 2.17.0 on my project break only in prod environment (not dev or test) with this error :

Uncaught PHP Exception TypeError: "Doctrine\DBAL\Connection::executeQuery():
Argument #1 ($sql) must be of type string, null given,
called in vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php on line 33

which means $this->_sqlStatements is NULL ... if it helps.

@symfonyaml commented on GitHub (Nov 16, 2023): Upgrading from `2.16.3` to `2.17.0` on my project break only in **prod** environment *(not dev or test)* with this error : ``` Uncaught PHP Exception TypeError: "Doctrine\DBAL\Connection::executeQuery(): Argument #1 ($sql) must be of type string, null given, called in vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php on line 33 ``` which means [`$this->_sqlStatements`](https://github.com/doctrine/orm/blob/2.17.x/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php#L33) is NULL ... if it helps.
Author
Owner

@greg0ire commented on GitHub (Nov 16, 2023):

Thanks, but we have 2 fixes on the way already, and pinpointed the issue a few messages ago. You just need to wait for either a new version of doctrine/orm or symfony/var-exporter.

@greg0ire commented on GitHub (Nov 16, 2023): Thanks, but we have 2 fixes on the way already, and pinpointed the issue a few messages ago. You just need to wait for either a new version of `doctrine/orm` or `symfony/var-exporter`.
Author
Owner

@szczyglis-dev commented on GitHub (Nov 17, 2023):

Hi, I've got the same issue after upgrade to 2.17.0. It only happens in prod env. I had to roll back to 2.16.3.

User Notice: serialize(): at /var/www/html/<site>/vendor/symfony/var-exporter/Internal/Exporter.php:164
Doctrine\DBAL\Connection::handleExceptionDuringQuery(): Argument #2 ($sql) must be of type string, null given, /var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php on line 1538

Stacktrace:

Doctrine\DBAL\Connection::handleExceptionDuringQuery(): Argument #2 ($sql) must be of type string, null given, called in /var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php on line 1538

/var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:2219
/var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:1538
/var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php:45
/var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php:327
/var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1212
/var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1166
/var/www/html/<site>/src/Repository/Status/UpdateTrait.php:62

(rest of trace)
@szczyglis-dev commented on GitHub (Nov 17, 2023): Hi, I've got the same issue after upgrade to `2.17.0`. It only happens in **prod env**. I had to roll back to `2.16.3`. ``` User Notice: serialize(): at /var/www/html/<site>/vendor/symfony/var-exporter/Internal/Exporter.php:164 Doctrine\DBAL\Connection::handleExceptionDuringQuery(): Argument #2 ($sql) must be of type string, null given, /var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php on line 1538 ``` Stacktrace: ``` Doctrine\DBAL\Connection::handleExceptionDuringQuery(): Argument #2 ($sql) must be of type string, null given, called in /var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php on line 1538 /var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:2219 /var/www/html/<site>/vendor/doctrine/dbal/lib/Doctrine/DBAL/Connection.php:1538 /var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/Query/Exec/SingleTableDeleteUpdateExecutor.php:45 /var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/Query.php:327 /var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1212 /var/www/html/<site>/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php:1166 /var/www/html/<site>/src/Repository/Status/UpdateTrait.php:62 (rest of trace) ```
Author
Owner

@greg0ire commented on GitHub (Nov 17, 2023):

@szczyglis-dev I said: YOU JUST NEED TO WAIT

So just wait. No need for more reports.

@greg0ire commented on GitHub (Nov 17, 2023): @szczyglis-dev I said: YOU JUST NEED TO WAIT So just wait. No need for more reports.
Author
Owner

@greg0ire commented on GitHub (Nov 17, 2023):

https://github.com/doctrine/orm/releases/tag/2.17.1 released

@greg0ire commented on GitHub (Nov 17, 2023): https://github.com/doctrine/orm/releases/tag/2.17.1 released
Author
Owner

@alcohol commented on GitHub (Nov 17, 2023):

Awesome, many thanks to everyone who got involved so quickly to resolve this 🥳 !

@alcohol commented on GitHub (Nov 17, 2023): Awesome, many thanks to everyone who got involved so quickly to resolve this :partying_face: !
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7245