8000 ObjectNormalizer doesn't deal with scope of `get()` functions correctly · Issue #58041 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

ObjectNormalizer doesn't deal with scope of get() functions correctly #58041

New issue

Have a question about this p 8000 roject? 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

Closed
vidarl opened this issue Aug 20, 2024 · 8 comments
Closed

ObjectNormalizer doesn't deal with scope of get() functions correctly #58041

vidarl opened this issue Aug 20, 2024 · 8 comments

Comments

@vidarl
Copy link
vidarl commented Aug 20, 2024

Symfony version(s) affected

5.4.41 and later (5.4.42 is latest ATM):

Description

Introduced in 5.4.41, the ObjectNormalizer do not take scope of get functions properly. Protected properties will be attempt fetched but not included in the exported data anyway

How to reproduce

The following code will work on 5.4.40 and earlier. Inline comment describes what happens when it fails in 5.4.41 and later :

class SomeData
{
    protected string $data;

    public function __construct(string $data)
    {
        $this->data = $data;
    }

    protected function getSomethingProtected(): array
    {
        return "protected";
    }

    public function __get($property)
    {
        if (property_exists($this, $property)) {
            return $this->$property;
        }
        throw new \Exception("Unknown property $property"); // <------- Since v5.4.41 it will fail here because getSomethingProtected() has protected scope
    }

    public function getData(): string
    {
        return $this->data;
    }
}
        $someData = new SomeData('foobar');
        $jsonContent = $this->serializer->serialize($someData, 'json');

If I change __get() to anyway return the property somethingProtected instead of throwing an exception, it will anyway not be included in the serialized data (which is correct and inline with previous versions )

Possible Solution

Problem was introduced by #57187
The change in Normalizer/ObjectNormalizer.php in symfony/serializer@296df0c is causing the problem

Additional Context

FYI : #58012 seems to be related

@mtarld
Copy link
Contributor
mtarld commented Sep 9, 2024

Hi @vidarl!

I'm not sure to understand properly what's wrong.
Below is my attempt to reproduce your issue, and I'm not getting any exception (as expected according to me).
Can you explain what's going wrong in your case?

class ObjectDummyWithMagicGetterAndPrivateProperty
{
    protected bool $private = true;

    public function __get(string $property): mixed
    {
        throw new \LogicException(sprintf('Cannot call "%s" for $%s private property.', __METHOD__, $property));
    }
}

class ObjectDummyWithMagicGetterPrivatePropertyAndGetter
{
    protected bool $private = true;

    protected function getPrivate(): bool
    {
        return $this->private;
    }

    public function __get(string $property): mixed
    {
        throw new \LogicException(sprintf('Cannot call "%s" for $%s private property.', __METHOD__, $property));
    }
}

public function testDoNotCallMagicGetterOnPrivateProperty()
{
    $normalizer = new ObjectNormalizer();

    $this->assertSame([], $normalizer->normalize(new ObjectDummyWithMagicGetterAndPrivateProperty()));
    $this->assertSame([], $normalizer->normalize(new ObjectDummyWithMagicGetterPrivatePropertyAndGetter()));
}

@stof
Copy link
Member
stof commented Sep 9, 2024

@vidarl try implementing __isset and not just __get, to make isset() checks work for your magic properties.

@xabbuh
Copy link
Member
xabbuh commented Sep 18, 2024

I am going to close here for now due to the lack of feedback. Please let us know when you have more information and we can consider to reopen.

@xabbuh xabbuh closed this as not planned Won't fix, can't repro, duplicate, stale Sep 18, 2024
@vidarl
Copy link
Author
vidarl commented Dec 17, 2024

@stof Looks like isset() is never called either on somethingProtected or data so implementing __isset() won't change anything

@mtarld Looks like the problem happens when interacting with the serializer..
If you inject SerializerInterface and use that instead of only the normalizer, your code will blow up too, ie :

        $prop1 = $this->serializer->serialize(new ObjectDummyWithMagicGetterAndPrivateProperty(), 'json');
        $prop2 = $this->serializer->serialize(new ObjectDummyWithMagicGetterPrivatePropertyAndGetter(), 'json');

@mtarld
Copy link
Contributor
mtarld commented Dec 18, 2024

@vidarl, I tried with the following code, and it doesn't seem to fail:

<?php

namespace App\Tests;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Serializer\SerializerInterface;

/**
 * @group 6.4
 */
final class Issue58041Test extends KernelTestCase
{
    public function testBug(): void
    {
        $normalizer = static::getContainer()->get(SerializerInterface::class);

        $this->assertSame([], $normalizer->normalize(new ExampleObject58041WithMagicGetterAndPrivateProperty()));
        $this->assertSame([], $normalizer->normalize(new ExampleObject58041WithMagicGetterPrivatePropertyAndGetter()));
    }
}

class ExampleObject58041WithMagicGetterAndPrivateProperty
{
    protected bool $private = true;

    public function __get(string $property): mixed
    {
        throw new \LogicException(sprintf('Cannot call "%s" for $%s private property.', __METHOD__, $property));
    }
}

class ExampleObject58041WithMagicGetterPrivatePropertyAndGetter
{
    protected bool $private = true;

    protected function getPrivate(): bool
    {
        return $this->private;
    }

    public function __get(string $property): mixed
    {
        throw new \LogicException(sprintf('Cannot call "%s" for $%s private property.', __METHOD__, $property));
    }
}

I'm wondering if maybe you have a custom normalizer, creating that issue.

Could you provide a reproducer Symfony app so that we can take a look, please?

@vidarl
Copy link
Author
vidarl commented Dec 18, 2024

@mtarld Thank you for take the time to look at this..
I now figured out why you are not able to reproduce it...

It works fine on fresh Symfony 7.2. But it does not on fresh Symfony 5.4 which I am currently using.
As mentioned in the beginning of this ticket, this broke in 5.4.41 and later. Any possibility for getting this fixed in 5.4?

@mtarld
Copy link
Contributor
mtarld commented Dec 18, 2024

Unfortunately, we maintain 5.4 for security fixes only.
Time to upgrade 😉

@vidarl
Copy link
Author
vidarl commented Dec 23, 2024

For anyone else hitting the same bug in 5.4.

Even though it is only documented for 6.4 and later, it is also possible to ignore properties in 5.4 as well. This simple code will solve this for the SomeData class in the ticket description:

#config/serializer/some_data.yaml
App\Lib\SomeData:
    attributes:
        somethingProtected:
            ignore: true

Thank you @Steveb-p for pointing this out to me!

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
0