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

Interceptor not working when calling parent::method() #11699

Closed
milansimek opened this issue Oct 24, 2017 · 7 comments
Closed

Interceptor not working when calling parent::method() #11699

milansimek opened this issue Oct 24, 2017 · 7 comments
Labels
Issue: Confirmed Gate 3 Passed. Manual verification of the issue completed. Issue is confirmed Priority: P4 No current plan to fix. Fixing can be deferred as a logical part of more important work. Progress: done Reproduced on 2.1.x The issue has been reproduced on latest 2.1 release Reproduced on 2.2.x The issue has been reproduced on latest 2.2 release Reproduced on 2.3.x The issue has been reproduced on latest 2.3 release Severity: S4 Affects aesthetics, professional look and feel, “quality” or “usability”. stale issue Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it

Comments

@milansimek
Copy link

milansimek commented Oct 24, 2017

Preconditions

Magento 2.1.7 + PHP 7.0.22

Steps to reproduce

  1. Create the following structure:
class A {
  public function doSomething(){
     echo 'I do something! (original)';
  }
}

class B extends class A {
  public function parentDoSomething(){
     parent::doSomething();  
  }
}
  1. Now create an around plugin for the class A doSomething method:
public function aroundDoSomething(\Namespace\A $subject, callable $proceed){
        echo 'I do something else! (plugin)';
}
  1. Now execute $classB->parentDoSomething();

Expected result

I do something else! (plugin) should be printed

Actual result

I do something! (original) is printed

Other information

Calling $this->doSomething() in class B does produce the correct result.

@magento-engcom-team magento-engcom-team added the Issue: Format is valid Gate 1 Passed. Automatic verification of issue format passed label Oct 24, 2017
@magento-engcom-team magento-engcom-team added the Issue: Clear Description Gate 2 Passed. Manual verification of the issue description passed label Nov 10, 2017
@magento-engcom-team magento-engcom-team self-assigned this Nov 10, 2017
@magento-engcom-team
Copy link
Contributor

@milansimek, thank you for your report.
We've created internal ticket(s) MAGETWO-83669 to track progress on the issue.

@magento-engcom-team magento-engcom-team added 2.1.x Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development Issue: Confirmed Gate 3 Passed. Manual verification of the issue completed. Issue is confirmed Reproduced on 2.1.x The issue has been reproduced on latest 2.1 release Reproduced on 2.2.x The issue has been reproduced on latest 2.2 release Reproduced on 2.3.x The issue has been reproduced on latest 2.3 release labels Nov 10, 2017
@magento-engcom-team magento-engcom-team removed their assignment Nov 10, 2017
@lisovyievhenii lisovyievhenii self-assigned this Jan 23, 2019
@magento-engcom-team
Copy link
Contributor

Hi @lisovyievhenii. Thank you for working on this issue.
Looks like this issue is already verified and confirmed. But if your want to validate it one more time, please, go though the following instruction:

  • 1. Add/Edit Component: XXXXX label(s) to the ticket, indicating the components it may be related to.

  • 2. Verify that the issue is reproducible on 2.3-develop branch

    Details- Add the comment @magento-engcom-team give me 2.3-develop instance to deploy test instance on Magento infrastructure.
    - If the issue is reproducible on 2.3-develop branch, please, add the label Reproduced on 2.3.x.
    - If the issue is not reproducible, add your comment that issue is not reproducible and close the issue and stop verification process here!

  • 3. Verify that the issue is reproducible on 2.2-develop branch.

    Details- Add the comment @magento-engcom-team give me 2.2-develop instance to deploy test instance on Magento infrastructure.
    - If the issue is reproducible on 2.2-develop branch, please add the label Reproduced on 2.2.x

  • 4. If the issue is not relevant or is not reproducible any more, feel free to close it.

@lisovyievhenii
Copy link
Contributor

@milansimek ,

Plugin will never work on "parent" call.
At first, "parent" means you want to call parent method immediately. Let's imagine class:

class B extends A {
    public function doSomething(){
        ....
        //some code manipulations
        ....
        parent::doSomething();
        ....
        //some code manipulations after parent code is finished
    }
}

Here I have class B that realize method doSomething. It makes some actions, then calls parent method.

So what would be if plugin will work as you want? As the result we will call B->doSomething() that is wrapped by Interceptor class. Plugin will work, then B->doSomething() will run, and when parent::doSomething() will be called the plugin again will wrap doSomething and we will have a recursive plugin's execution.

Explanation why Magneto doesn't wrap parent method:
you have class A and a plugin for it. Everything works fine - Interceptor extends class A and wraps its functions.
Then you created class B that extends A and call B somewhere in your code (like $b = new B()). Magento will create an Interceptor for class B, but as you can see B extends A, not Interceptor of A.
So in case if you call $b->doSomething(), plugin will be called, because you didn't overridden it
In case if you call $b->parentDoSomething(), plugins on parentDoSomething method will be called. BUT you call a parent method there, so you want to execute a parent's method of class A. Interceptor doesn't have a wrapping method for parent, only for B's method.

So if you are really want to have a plugin to be called on parent method, you can:

  1. extend your class B from Interceptor of the class A - very ugly solution
  2. in the method parentDoSomething call $this, not parent

@milansimek
Copy link
Author

@lisovyievhenii Thanks for your response!

Since I'm using an "around" plugin and not proceeding with the execution, the code acts more as a rewrite of the original method. This means there won't be any recursive execution issues.

Also, in your example you changed the method name that calls the parent method from parentDoSomething to doSomething. Of course that would cause an infinite loop when continuing execution, but my example uses a different method name which calls the parent method.

I appreciate your recommendation regarding using $this instead of parent, but that's not possible when dealing with Magento 2.x code which in many cases hasn't been rewritten and has just been ported from Magento 1.x. As an extension developer, I can't change the core files of course.

To ensure flexibility and no unexpected behavior when using interceptors, it would be more logical if all calls to a specific class method can be intercepted. This is obviously a limitation in the current system.

If it's not possible to intercept parent:: calls, in many cases it will be required to completely rewrite the class which uses the parent:: call. This in turn forces you to duplicate a lot of code, especially when it comes to the ported legacy M1 classes, since these often are very tightly coupled and use multiple parent:: calls in a single huge class method.

If this parent method is being called in 4 different child classes with each method being 20 lines or longer for example, you'd have to create a plugin for each child method, completely copy and rewrite those methods without the possibility to access private properties in a clean way. This will result in a complete unreadable code mess as you can probably imagine.

Of course in an ideal world the Magento 2 core code should be clean and easy to extend with interceptors. Each class method should by tiny and easy to modify / extend. This just isn't the case with the current state of a lot of core modules integrated in Magento 2.

On a side note: When duplicating too much code, extensions submitted won't pass the Marketplace code duplication test.

@milansimek
Copy link
Author

So what would be if plugin will work as you want? As the result we will call B->doSomething() that is wrapped by Interceptor class. Plugin will work, then B->doSomething() will run, and when parent::doSomething() will be called the plugin again will wrap doSomething and we will have a recursive plugin's execution.

This isn't what I'm trying to achieve:

Actually, I'd like to create an interceptor for A::doSomething, not B::doSomething. So B->doSomething() will be called, which in turn calls the parent method. Then then interceptor should kick in and wrap around the A::doSomething method.

A::doSomething doesn't call any parent methods since it's already the method of the parent class. So this shouldn't be an issue I think?

Maybe I'm missing something, if that's the case please let me know.

Thanks!

@lisovyievhenii
Copy link
Contributor

Hello @milansimek ,

When some method calls parent::doSomething, it says to the PHP interpreter to call the parent method (without any possibility to catch this call).
So to do what you want (Interceptor should wrap parent::doSomething), you need to rewrite PHP interpreter.
I guess this feature will never be in Magento.

But seriously, if you need to do some changes in methods, which are called by parent keyword, there are only few ways to modify that parent method: override original class with "preference" in di.xml; create a patch; make an around plugin on child classes, which calls parent method and override all logic and the worst solution - keep Magento code in project/app/Magento and make you changes here.

@ghost ghost removed Issue: Clear Description Gate 2 Passed. Manual verification of the issue description passed Issue: Format is valid Gate 1 Passed. Automatic verification of issue format passed Issue: Ready for Work Gate 4. Acknowledged. Issue is added to backlog and ready for development labels Oct 20, 2020
@sdzhepa sdzhepa added the Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it label Nov 6, 2020
@magento-engcom-team magento-engcom-team added Priority: P4 No current plan to fix. Fixing can be deferred as a logical part of more important work. Severity: S4 Affects aesthetics, professional look and feel, “quality” or “usability”. labels Nov 30, 2020
@stale
Copy link

stale bot commented Feb 15, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 14 days if no further activity occurs. Is this issue still relevant? If so, what is blocking it? Is there anything you can do to help move it forward? Thank you for your contributions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue: Confirmed Gate 3 Passed. Manual verification of the issue completed. Issue is confirmed Priority: P4 No current plan to fix. Fixing can be deferred as a logical part of more important work. Progress: done Reproduced on 2.1.x The issue has been reproduced on latest 2.1 release Reproduced on 2.2.x The issue has been reproduced on latest 2.2 release Reproduced on 2.3.x The issue has been reproduced on latest 2.3 release Severity: S4 Affects aesthetics, professional look and feel, “quality” or “usability”. stale issue Triage: Dev.Experience Issue related to Developer Experience and needs help with Triage to Confirm or Reject it
Projects
Development

No branches or pull requests

5 participants