Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VarExporter] Cannot access property starting with "\0" #54905

Open
ivanbogomoloff opened this issue May 13, 2024 · 8 comments
Open

[VarExporter] Cannot access property starting with "\0" #54905

ivanbogomoloff opened this issue May 13, 2024 · 8 comments

Comments

@ivanbogomoloff
Copy link

ivanbogomoloff commented May 13, 2024

Symfony version(s) affected

7.0.6

Description

I have entity App\Entity\Organization

namespace App\Entity;

class Organization {
 #[ORM\Column(length: 255)]
    private ?string $title = null;

public function getTitle(): ?string
    {
        return $this->title;
    }
}

and form with data_class = App\Entity\Organization

and route like this

#[Route('/{id}/edit', name: self::ROUTE_APP_ADMIN_ORGANIZATION_EDIT, methods: ['GET', 'POST'])]
    public function edit(Request $request, Organization $organization, EntityManagerInterface $entityManager): Response
    {
       form = $this->createForm(OrganizationForm::class, $organization);
    }

When i change fields in entity ( for example add new field ), add migration, migrate. Then if i don't reset cache manually or do not restart my php-fpm server, i get error Cannot access property starting with "\0"

Error:
Cannot access property starting with "\0"

  at vendor/symfony/var-exporter/LazyGhostTrait.php:207
  at Proxies\__CG__\App\Entity\Organization->__set()
     (vendor/symfony/var-exporter/Internal/Hydrator.php:156)
  at Proxies\__CG__\App\Entity\Organization->Symfony\Component\VarExporter\Internal\{closure}()
     (vendor/symfony/var-exporter/Hydrator.php:72)
  at Symfony\Component\VarExporter\Hydrator::hydrate()
     (vendor/symfony/var-exporter/Internal/LazyObjectState.php:58)
  at Symfony\Component\VarExporter\Internal\LazyObjectState->initialize()
     (vendor/symfony/var-exporter/LazyGhostTrait.php:120)
  at Proxies\__CG__\App\Entity\Organization->__get()
     (src/Entity/Organization.php:117)
  at App\Entity\Organization->getTitle()
     (vendor/symfony/property-access/PropertyAccessor.php:388)
  at Symfony\Component\PropertyAccess\PropertyAccessor->readProperty()
     (vendor/symfony/property-access/PropertyAccessor.php:99)
  at Symfony\Component\PropertyAccess\PropertyAccessor->getValue()
     (vendor/symfony/form/Extension/Core/DataAccessor/PropertyPathAccessor.php:80)
  at Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor->getPropertyValue()
     (vendor/symfony/form/Extension/Core/DataAccessor/PropertyPathAccessor.php:45)
  at Symfony\Component\Form\Extension\Core\DataAccessor\PropertyPathAccessor->getValue()
     (vendor/symfony/form/Extension/Core/DataAccessor/ChainAccessor.php:37)
  at Symfony\Component\Form\Extension\Core\DataAccessor\ChainAccessor->getValue()
     (vendor/symfony/form/Extension/Core/DataMapper/DataMapper.php:50)
  at Symfony\Component\Form\Extension\Core\DataMapper\DataMapper->mapDataToForms()
     (vendor/symfony/form/Form.php:319)
  at Symfony\Component\Form\Form->setData()
     (vendor/symfony/form/Form.php:408)
  at Symfony\Component\Form\Form->initialize()
     (vendor/symfony/form/FormBuilder.php:176)
  at Symfony\Component\Form\FormBuilder->getForm()
     (vendor/symfony/form/FormFactory.php:28)
  at Symfony\Component\Form\FormFactory->create()
     (vendor/symfony/framework-bundle/Controller/AbstractController.php:323)
  at Symfony\Bundle\FrameworkBundle\Controller\AbstractController->createForm()
     (src/Controller/Admin/Organization/OrganizationController.php:98)
  at App\Controller\Admin\Organization\OrganizationController->edit()
     (vendor/symfony/http-kernel/HttpKernel.php:178)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw()
     (vendor/symfony/http-kernel/HttpKernel.php:76)
  at Symfony\Component\HttpKernel\HttpKernel->handle()
     (vendor/symfony/http-kernel/Kernel.php:185)
  at Symfony\Component\HttpKernel\Kernel->handle()
     (vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
  at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
     (vendor/autoload_runtime.php:29)
  at require_once('/var/www/html/vendor/autoload_runtime.php')
     (public/index.php:5)                

If i change code without ParamConverter all works fine. I get not proxied entity and var exporter works fine

How to reproduce

Create route with annotation with #[Route('/{id}/edit', name: 'my_route', methods: ['GET', 'POST'])] with id param to convert param to entity and make entity for this with title property. Then create form with data_class in configureOptions

PHP version is: PHP 8.2.18 (cli) (built: Apr 11 2024 19:20:54) (NTS)

Database is postgresql

Setttings for doctrine.yaml

doctrine:
    dbal:
        url: '%env(resolve:DATABASE_URL)%'
        profiling_collect_backtrace: '%kernel.debug%'
        use_savepoints: true
    orm:
        auto_generate_proxy_classes: true
        enable_lazy_ghost_objects: true
        report_fields_where_declared: true
        validate_xml_mapping: true
        naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
        auto_mapping: true
        mappings:
            App:
                type: attribute
                is_bundle: false
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App
        controller_resolver:
            auto_mapping: true

Possible Solution

Custom ParamConverter or use manually finding entity by params

This works fine.

#[Route('/{id}/edit', name: 'my_route', methods: ['GET', 'POST'])]
public function edit(Request $request, int $id, OrganizationRepository $organizationRepo, EntityManagerInterface $entityManager): Response
  {
$organizationRepo->find($id); 
}

Additional Context

sfbug7

@xabbuh
Copy link
Member

xabbuh commented May 13, 2024

Can you create a small example application that allows to reproduce your issue?

@derrabus
Copy link
Member

Symfony version(s) affected

7.0.6

Does this still happen with 7.0.7? We've fixed some issues in that area lately.

@ivanbogomoloff
Copy link
Author

ivanbogomoloff commented May 13, 2024

Symfony version(s) affected

7.0.6

Does this still happen with 7.0.7? We've fixed some issues in that area lately.

Yes, it does. I upgrade symfony to 7.0.7 and i still stable repeat this error. I can't share source code, and i can't reporudce this on fresh symfony install, but in my project it stable error. Under stable i mean that case: if i changed entity fields and make migration then error occurs . While i don't change entity fields it works wihtout errors.

@xabbuh
Copy link
Member

xabbuh commented May 13, 2024

Without being able to reproduce I am afraid that you will have to debug the root cause yourself.

@ivanbogomoloff
Copy link
Author

ivanbogomoloff commented May 13, 2024

I think it's var-exporter bug because in source code hydrates starts from this and because in 6.x.x symfony version i never seen it before with large of coding in sf 6

Hydrator::hydrate($object, ["\0Bar\0privateBarProperty" => $propertyValue]);

Where "\0" is special property and it doesn't exists for proxy object of doctrine entity where it not correctly reseted (may be it depends on dump/cache component)

And of course i think it's all same errors like this #54477

@nicolas-grekas
Copy link
Member

This code is special handling for SplObjectStorage instances, I doubt it's the culprit. Can you please create a small app or script that'd allow us to reproduce?

@ivanbogomoloff
Copy link
Author

I will try but not now. Sorry.

@ivanbogomoloff
Copy link
Author

ivanbogomoloff commented May 13, 2024

I can give more info if i dumped symfony/vendor/symfony/var-exporter/Hydrator.php like this.

public static function hydrate(object $instance, array $properties = [], array $scopedProperties = []): object
    {
        if ($properties) {
            $class = $instance::class;
            $propertyScopes = InternalHydrator::$propertyScopes[$class] ??= InternalHydrator::getPropertyScopes($class);

            foreach ($properties as $name => &$value) {
                [$scope, $name, $readonlyScope] = $propertyScopes[$name] ?? [$class, $name, $class];
                $scopedProperties[$readonlyScope ?? $scope][$name] = &$value;
            }
            unset($value);
        }
        // DUMP HERE
        var_dump($scopedProperties);
        foreach ($scopedProperties as $scope => $properties) {
            if ($properties) {
                (InternalHydrator::$simpleHydrators[$scope] ??= InternalHydrator::getSimpleHydrator($scope))($properties, $instance);
            }
        }

        return $instance;
    }

I see 2 instances of my entity

/var/www/html/vendor/symfony/var-exporter/Hydrator.php:69:
array (size=1)
  'Doctrine\ORM\EntityManager' => 
    array (size=4)
      'expressionBuilder' => null
      'closed' => boolean false
      'filterCollection' => null
      'cache' => null
/var/www/html/vendor/symfony/var-exporter/Hydrator.php:69:
array (size=2)
  'App\Entity\Organization' => 
    array (size=19)
      'title' => null
      'description' => null
      'headUser' => null
      'photos' => null
      'description2' => null
      'roles' => null
      'rolePermissionsPerStage' => null
      'applicationForAssistanceSettings' => null
      'createdAt' => null
      'updatedAt' => null
      'latitude' => null
      'longitude' => null
      'phoneNumber' => null
      'email' => null
      'address' => null
      'addressDictData' => null
      'photoImagePath' => null
      'author' => null
      'editor' => null
  'Proxies\__CG__\App\Entity\Organization' => 
    array (size=1)
      '�App\Entity\Organization�applicationForAssistanceSettings2' => null

Last entity Proxies\__CG__\App\Entity\Organization doesn't exsist while i don't change (add new fields) if it exists then error occurs.

Then if i changed vendor code ( i did it only for issue purposes) like this

public static function hydrate(object $instance, array $properties = [], array $scopedProperties = []): object
    {
        if ($properties) {
            $class = $instance::class;
            $propertyScopes = InternalHydrator::$propertyScopes[$class] ??= InternalHydrator::getPropertyScopes($class);

            foreach ($properties as $name => &$value) {
                [$scope, $name, $readonlyScope] = $propertyScopes[$name] ?? [$class, $name, $class];
                $scopedProperties[$readonlyScope ?? $scope][$name] = &$value;
            }
            unset($value);
        }
        unset($scopedProperties['Proxies\__CG__\App\Entity\Organization']);
        foreach ($scopedProperties as $scope => $properties) {
            if ($properties) {
                (InternalHydrator::$simpleHydrators[$scope] ??= InternalHydrator::getSimpleHydrator($scope))($properties, $instance);
            }
        }

        return $instance;
    }

then all fine, because $scopedProperties entity is correct.

Hope this helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants