Skip to content

[Form] Add magic getter method support to Entities #5309

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
wants to merge 5 commits into from
Closed

[Form] Add magic getter method support to Entities #5309

wants to merge 5 commits into from

Conversation

Bogdanp
Copy link

@Bogdanp Bogdanp commented Aug 21, 2012

When using Symfony Forms, it permits the implementation of magic getter methods in those Entities by using the __call magic method.

It basically allows one to do the following in an Entity:

public function __call($method, $arguments) {
    if (preg_match('/^(get|is|has)([A-Z].*)$/', $method, $matches)) {
        return $this->{lcfirst($matches[2])};
    }
    throw new \Exception(sprintf(
        'Method does not exist for class "%s".', get_class($this)
    ));
}

When using Symfony Forms, it permits the implementation of magic getter methods in those Entities by using the `__call` magic method.

It basically allows one to do the following in an Entity:

    public function __call($method, $arguments) {
        if (preg_match('/^(get|is|has)([A-Z].*)$/', $method, $matches)) {
            return $this->{lcfirst($matches[2])};
        }
        throw new \Exception(sprintf(
            'Method does not exist for class "%s".', get_class($this)
        ));
    }
@travisbot
Copy link

This pull request fails (merged 6b92474 into 173f23d).

@Bogdanp
Copy link
Author

Bogdanp commented Aug 21, 2012

All three travisbot machines seems to have failed due to powerDown and not the patch. Is there any way to have it re-run them?

@stof
Copy link
Member

stof commented Aug 21, 2012

Please add some tests (for each cases of your elseif)

$result[self::VALUE] = $objectOrArray->$hasser();
} else {
throw new InvalidPropertyException(sprintf('Neither property "%s" nor method "%s()" nor method "%s()" exists in class "%s"', $property, $getter, $isser, $reflClass->name));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Exception message is wrong (property).

Copy link
Author

Choose a reason for hiding this comment

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

Would you mind explaining? I don't understand.

Edit: Actually, I think I get what you mean.

Copy link
Contributor

Choose a reason for hiding this comment

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

I just meant, that the exception message mentions the "property %s" but __call is not for properties but methods.
Btw, why do you need the is_callable test when you already have tested for __call?

Copy link
Author

Choose a reason for hiding this comment

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

The is_callable calls are needed to be able to tell if those magic "methods" were actually implemented or not on that class. You should see the latest commit, it's probably a bit clearer in that :).

Edit: For clarification,method_exists or .hasMethod can't tell whether or not those methods exist.
Edit2: Thinking about it, that was a bit of a brain fart since Is_callable will always return true due to the __call method.

@Bogdanp
Copy link
Author

Bogdanp commented Aug 21, 2012

Added some tests.

A note: even after running composer install I was unable to run the tests since PHP would fail because it would try to load something from Doctrine/orm/lib which isn't listed as a dependency in composer.json. In the end, I added it as a dependecy and everything ran smoothly. Am I missing something?

@travisbot
Copy link

This pull request passes (merged 3252a3b into 173f23d).

@stof
Copy link
Member

stof commented Aug 21, 2012

@Bogdanp to run the testsuite, you need to ask composer to install the dev requirements too by adding the --dev option (dev requirements are dependencies needing by the testsuite without being mandatory dependencies of the library)

This allows Entities to have __call methods that don't necessarily
"implement" any of the magic getter methods.
@Bogdanp
Copy link
Author

Bogdanp commented Aug 21, 2012

Alright, got it :). Thanks. Is there some place where this stuff is documented? I had checked the getting started contributing page but it said nothing about the --dev option.

@stof
Copy link
Member

stof commented Aug 21, 2012

then it should be added in the doc.

Btw, a good way to know what is required to setup the testsuite when a project uses travis is to look at the .travis.yml file :)

@Bogdanp
Copy link
Author

Bogdanp commented Aug 21, 2012

Good to know!

@travisbot
Copy link

This pull request passes (merged 2894aa0 into 173f23d).

@stof
Copy link
Member

stof commented Aug 21, 2012

Please also test the case of an unsupported method (which will not behave properly IMO) and fix the coding standards

@Bogdanp
Copy link
Author

Bogdanp commented Aug 21, 2012

You are right, it won't. I hadn't considered that is_callable will always pass because of the __call method (I actually had a "d'oh" moment when I realized that). I'll have to come up with a better solution after I get home since I'm at the office right now.

@stof
Copy link
Member

stof commented Aug 21, 2012

the point is, as soon as __call is used, there is no way to determine if the method can be handled by the magic method or no

@Bogdanp
Copy link
Author

Bogdanp commented Aug 21, 2012

Yes, that is what I was saying in my previous comment.

The fix should be in the last two commits, simply checking for the methods themselves should be fine and moving them to the bottom of the condition stack should prevent any exceptions or errors generated by __call to be thrown before all the other options have been exhausted. There is no need (at least IMO) to check for the case in which __call is defined but its purpose is not generating magic getter methods since that case should be handled by the author of the Entity anyway (they should throw an error if the method call doesn't match their specific case).

@travisbot
Copy link

This pull request passes (merged 73ecbf1 into 173f23d).

@travisbot
Copy link

This pull request passes (merged 616f121 into 173f23d).

@stof
Copy link
Member

stof commented Aug 21, 2012

Please add a test for a class which does not support the magic getter (throwing an exception in it) but support the magic isser. I can assure you it will fail by throwing you the exception

@stof
Copy link
Member

stof commented Aug 21, 2012

in fact, you test does not allow testing all cases: you use the same class supporting all 3 magic methods. But this does not allow you to know which case was called. You need 3 classes, one for each case

@Bogdanp
Copy link
Author

Bogdanp commented Aug 22, 2012

You are correct, there is no proper way to determine which method should be called or if they are callable and in the case that the author of the Entity has written a __call method which throws an error it will fail. The only solution in my mind is to go back to testing if the object has a __call method and calling the getter, isser and hasser each inside of a try-catch block. The problem with this case is that there is no guarantee that the author of the Entity has made is so that an actual exception is thrown when a "method" does not exist. Would you consider this to be a valid solution?

@Tobion
Copy link
Contributor

Tobion commented Aug 22, 2012

Please add a test for a class which does not support the magic getter (throwing an exception in it) but support the magic isser. I can assure you it will fail by throwing you the exception

@stof I don't think we should support a magic isser when is magic getter already throwed an exception. The only solution that would support this, is catching exceptions as @Bogdanp suggested and assuming __call would raise an exception in this case.

I'd suggest to simply invoke _call with the plain property value (no getter or isser variation) when there is a __call defined.

@stof
Copy link
Member

stof commented Aug 22, 2012

@Tobion It makes no sense to call it with the plain property value. If you use magic methods to implement getters, we cannot assume you implemented it by calling the method the same way than the property (which would not be consistent with what we do for other cases btw).

The point is, we cannot support __call without making a bunch of assumptions about the way this method was implemented (expecting it to throw an exception for invalid methods, and being side-effect free)

@Bogdanp
Copy link
Author

Bogdanp commented Aug 22, 2012

My question is, should I implement it the way I described above?

I think that in order to avoid confusion we can either keep the Exception that was thrown by __call and then throw it ourselves after none of the methods were found (though in this case there might be some confusion since the exception thrown for each of the three methods will probably be different) or we can throw a custom one describing what has likely happened.

Regarding the __call method as a whole, simply implementing it in a complex system requires some extra attention on the part of the developer doing it and as such I don't think it's outlandish to expect them to throw an exception when their specific case was not met.

@webmozart
Copy link
Contributor

This implementation unfortunately is obsolete since the PropertyAccess component was introduced. Please feel welcome to reimplement it for that component. Simply open a new PR and we will check it again. :)

See also #7263.

@webmozart webmozart closed this Apr 18, 2013
fabpot added a commit that referenced this pull request Apr 25, 2013
This PR was merged into the master branch.

Discussion
----------

[PropertyAccess] add support for magic call

Hi,

I add support for magic call with the `PropertyAccess`

the is basic implementation, if no `getter`, `isser`, or `hasser` or `_get` is found and there is `__call` then the PropertyAccess call the getter

the same for setter.

This functionality is disable by default

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | seems OK (failure/errors are the same on master)
| Fixed tickets | #4683, #6413, #5309
| License       | MIT
| Doc PR        | symfony/symfony-docs#2472

- [x] submit changes to the documentation

@bschussek is this ok ?

Commits
-------

a785baa [PropertyAccess] add support for magic call, related to #4683
OskarStark added a commit to symfony/symfony-docs that referenced this pull request Dec 17, 2023
This PR was merged into the 7.1 branch.

Discussion
----------

[Notifier] Add Unifonic notifier bridge

Add docs for symfony/symfony#5309

Fixes #19301

Commits
-------

9e47f5f [Notifier] Add Unifonic notifier bridge
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.

5 participants