Skip to content

[Serializer] Integrate the PropertyInfo Component (recursive denormalization and hardening) #17660

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

Merged
merged 2 commits into from
Apr 19, 2016

Conversation

dunglas
Copy link
Member

@dunglas dunglas commented Feb 2, 2016

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #16143, #17193, #14844
License MIT
Doc PR todo

Integrates the PropertyInfo Component in order to:

  • denormalize a graph of objects recursively (see tests)
  • harden the hydratation logic

The hardening part is interesting. Considering the following example:

class Foo
{
    public function setDate(\DateTimeInterface $date)
    {
    }
}

// initialize $normalizer
$normalizer->denormalize(['date' => 1234], Foo::class);

Previously, a PHP error was thrown because the type passed to the setter (an int) doesn't match the one checked with the typehint. With the PropertyInfo integration, an UnexpectedValueExcption is throw instead.
It's especially interesting for web APIs dealing with JSON documents. For instance in API Platform, previously a 500 error was thrown, but thanks to this fix a 400 HTTP code with a descriptive error message will be returned. (/cc @csarrazi @mRoca @blazarecki, it's an alternative to https://github.com/dunglas/php-to-json-schema for protecting an API).

/cc @mihai-stancu

* @throws UnexpectedValueException
* @throws LogicException
*/
protected function validateAndDenormalize($currentClass, $attribute, $data, $format, array $context) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ should be on next line.

* @throws UnexpectedValueException
* @throws LogicException
*/
protected function validateAndDenormalize($currentClass, $attribute, $data, $format, array $context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this method intended to be overridden by subclasses?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes (by ApiBundle especially).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be better to design an extension point based on composition instead of designing for inheritance. Inheritance-based extension points are always a huge pain to maintain BC when refactoring the logic (see the extraction of name converter from this class for instance)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After double checking, this extension point isn't needed anymore for API Platform. As I've no use case anymore, I changed the visibility of this method to private.

fabpot added a commit that referenced this pull request Feb 26, 2016
…n the type do not match (dunglas)

This PR was squashed before being merged into the 3.1-dev branch (closes #17738).

Discussion
----------

[PropertyAccess] Throw an InvalidArgumentException when the type do not match

| Q             | A
| ------------- | ---
| Bug fix?      | no (?)
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

Currently, when the Property Access Component call a setter with a value not matching its typehint, a `\TypeError` is thrown with PHP 7 and a `PHP Catchable fatal error` with PHP 5.

This PR make the component returning an `InvalidArgumentException` with both version. It's a (better) alternative to #17660 (the hardening part) to make the Symfony Serializer (and probably many other pieces of code) more robust when types do not match.

/cc @csarrazi @mRoca @blazarecki

Commits
-------

e70fdbc [PropertyAccess] Throw an InvalidArgumentException when the type do not match
@fabpot
Copy link
Member

fabpot commented Feb 26, 2016

#17738 has been merged now.

@dunglas
Copy link
Member Author

dunglas commented Feb 29, 2016

I've looked at this more deeply and #17959 and this one complement. They don't cover same cases. Both should be merged. (Will need a rebase).

@dunglas
Copy link
Member Author

dunglas commented Mar 6, 2016

ping @symfony/deciders

@@ -24,8 +29,20 @@
const ENABLE_MAX_DEPTH = 'enable_max_depth';
const DEPTH_KEY_PATTERN = 'depth_%s::%s';

/**
* @var PropertyTypeExtractorInterface|null
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docblock is not needed (as the property is private, IDEs should be able to infer the type from the constructor).

@dunglas dunglas added the Ready label Mar 25, 2016
@dunglas
Copy link
Member Author

dunglas commented Mar 25, 2016

All comments of @xabbuh fixed now.

@dunglas
Copy link
Member Author

dunglas commented Mar 31, 2016

Tests should be green now.

@dunglas
Copy link
Member Author

dunglas commented Apr 1, 2016

AppVeyor errors not related.

}
}

throw new UnexpectedValueException(sprintf('The type of the attribute "%s" for the class "%s" must be one of "%s" ("%s" given).', $attribute, $currentClass, implode('", "', array_keys($expectedTypes)), gettype($data)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type of the "%s" attribute for class "%s" must be one of "%s" ("%s" given).?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@dunglas
Copy link
Member Author

dunglas commented Apr 18, 2016

ping @symfony/deciders

@@ -498,6 +501,29 @@ public function testMaxDepth()

$this->assertEquals($expected, $result);
}

public function testDernomalizeRecursive()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo in name (misplaced r)

dunglas added a commit that referenced this pull request Apr 18, 2016
This PR was squashed before being merged into the 3.1-dev branch (closes #17959).

Discussion
----------

[Serializer] Harden the ObjectNormalizer

| Q             | A
| ------------- | ---
| Branch        | master
| Bug fix?      |  no
| New feature?  | yes
| BC breaks?    | yes
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

Transform `\TypeError`s to catchable serializer exceptions.

Follows #17738 and completes #17660.

Commits
-------

26a07fb [Serializer] Harden the ObjectNormalizer
- Refactored PR 14844 "Denormalize with typehinting"
- Now using PropertyInfo to extract type information
- Updated tests
- Updated composer.json
@dunglas dunglas force-pushed the serializer/property-info branch from 8924b23 to 0c10b4e Compare April 18, 2016 20:16
@dunglas
Copy link
Member Author

dunglas commented Apr 18, 2016

Failure not related.

@nicolas-grekas
Copy link
Member

👍

Recursive denormalization handling and hardening.
@dunglas dunglas force-pushed the serializer/property-info branch from b107296 to 5194482 Compare April 19, 2016 15:00
@dunglas dunglas merged commit 5194482 into symfony:master Apr 19, 2016
dunglas added a commit that referenced this pull request Apr 19, 2016
…ursive denormalization and hardening) (mihai-stancu, dunglas)

This PR was merged into the 3.1-dev branch.

Discussion
----------

	[Serializer] Integrate the PropertyInfo Component (recursive denormalization and hardening)

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #16143, #17193, #14844
| License       | MIT
| Doc PR        | todo

Integrates the PropertyInfo Component in order to:

* denormalize a graph of objects recursively (see tests)
* harden the hydratation logic

The hardening part is interesting. Considering the following example:

```php
class Foo
{
    public function setDate(\DateTimeInterface $date)
    {
    }
}

// initialize $normalizer
$normalizer->denormalize(['date' => 1234], Foo::class);
```

Previously, a PHP error was thrown because the type passed to the setter (an int) doesn't match the one checked with the typehint. With the PropertyInfo integration, an `UnexpectedValueExcption` is throw instead.
It's especially interesting for web APIs dealing with JSON documents. For instance in API Platform, previously a 500 error was thrown, but thanks to this fix a 400 HTTP code with a descriptive error message will be returned. (/cc @csarrazi @mRoca @blazarecki, it's an alternative to https://github.com/dunglas/php-to-json-schema for protecting an API).

/cc @mihai-stancu

Commits
-------

5194482 [Serializer] Integrate the PropertyInfo Component
6b464b0 Recursive denormalize using PropertyInfo
@dunglas dunglas deleted the serializer/property-info branch April 19, 2016 15:19
@dunglas
Copy link
Member Author

dunglas commented Apr 19, 2016

Thanks for working on this @mihai-stancu!

@mihai-stancu
Copy link
Contributor

@dunglas thank you for seeing it through!

@fabpot fabpot mentioned this pull request May 13, 2016
xabbuh added a commit to symfony/symfony-docs that referenced this pull request Mar 3, 2017
…glas)

This PR was submitted for the 3.1 branch but it was merged into the 3.2 branch instead (closes #7042).

Discussion
----------

[Serializer] Docs for the PropertyInfo integration

Docs for symfony/symfony#17660.

Commits
-------

8e2deb0 [Serializer] Docs for the PropertyInfo integration
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants