Skip to content

[Validator] Throw exception if caching a non-serializable constraint #12302

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

Closed
webmozart opened this issue Oct 23, 2014 · 6 comments
Closed

[Validator] Throw exception if caching a non-serializable constraint #12302

webmozart opened this issue Oct 23, 2014 · 6 comments

Comments

@webmozart
Copy link
Contributor

Origin of this ticket:

Currently, metadata caching fails if a constraint is not serializable. The only core constraint that is susceptible to this problem is the Callback constraint when used with an object method or closure:

// OK
new Callback(array('SomeClass', 'validateMe'));
new Callback('validateMeFunction');

// Not OK
new Callback(array($this, 'validateMe'));
new Callback(function ($value, $context) { ... });

When trying to serialize the last two constraints, an error is raised (#10083) that is a little hard to debug.

Two solutions come to mind:

  • Disallow closures/objects methods in Callback and require all constraints to always be serializable. I don't quite like this, since I find passing closures to Callback very useful (e.g. in the "constraints" option of the Form component). After all, caching is not always necessary.
  • Add a method isSerializable() to Constraint. This would return true by default and false in edge cases. If caching is turned on, GenericMetadata::addConstraint() checks isSerializable() and fails if an unserializable constraint is added.

The second solution adds minor overhead (the additional function call in addConstraint()), but only if metadata is not cached. I prefer that solution since it maintains the current flexibility, but returns a useful error message when a constraint is not serializable.

What do you think?

@4rthem
Copy link
Contributor

4rthem commented Oct 23, 2014

The second solution is better but means you cannot enable cache if using SonataAdminBundle (see code)?

Why not having two different constraint containers :

  • The cacheable one with serializable constraints (coming from configuration: YAML, Annotation ...). All constraints from configuration are serializable because they have no context ($this) and cannot contain a Closure.
  • The not-cached one with constraints instantiated in user code. I can't see the interest of caching these ones.
GenericMetadata::$constraints = [];
GenericMetadata::$notCacheableConstraints = [];

GenericMetadata::addConstraint(Constraint $constraint)
{
    if ($constraint->isSerializable()) {
        $this->constraints[] = $constraint;
    } else {
        $this->notCacheableConstraints[] = $constraint;
    }
    // ...
}

@webmozart
Copy link
Contributor Author

The second solution is better but means you cannot enable cache if using SonataAdminBundle

That's not true. getMetadataFor() already returns the cached metadata. If you add a constraint in this way, that constraint is not going to be cached.

@4rthem
Copy link
Contributor

4rthem commented Oct 24, 2014

Yes indeed, but the error is raised when trying to get metadata for a class (like a Doctrine proxy: #10083) inheriting from another one containing constraint (with Closure).

// You have these classes
class Item {}
class SubItem extends Item {}

$metadata = getMetadataFor(Entity\Item); // OK returns the metadata (written to the cache)
$metadata->addConstraint(new Callback(Closure));

getMetadataFor(Entity\SubItem); // KO Tries to cache the parent class also and hits the Closure

@webmozart
Copy link
Contributor Author

I see, thanks for the clarification! This should indeed be fixed.

@uwej711
Copy link
Contributor

uwej711 commented Dec 3, 2016

See #20740 for a possible fix

nicolas-grekas added a commit that referenced this issue Jan 12, 2017
…rializable parents (uwej711)

This PR was squashed before being merged into the 2.7 branch (closes #20793).

Discussion
----------

[Validator] Fix caching of constraints derived from non-serializable parents

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #12302
| License       | MIT
| Doc PR        | -

This change allows to still cache constraints even when an uncachable constraint (i.e. Callback)
is added to a parent class at runtime.

This is achived by caching only the constraints that are loaded for the class in question only
and merging parent and interface constraints after reading from cache.

Commits
-------

9dd6b0c [Validator] Fix caching of constraints derived from non-serializable parents
@nicolas-grekas
Copy link
Member

I'm closing this one, because #20793 fixes the Symfony related part, and the remaining part is fixed by using Symfony Cache, which handles serialization failures properly (no exception, no write).

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

4 participants