7

I'm using Magento 2.2 in development mode.

I have a controller class

class MyController
{
   public function __construct(
      \Magento\Framework\App\Action\Context $context,
      \Magento\Framework\Message\ManagerInterface $messageManager)
   {
      // stuff
   }
}

That works correctly. However, if I add an additional constructor parameter like this:

class MyController
{
   public function __construct(
      \Magento\Framework\App\Action\Context $context,
      \Magento\Framework\View\Result\PageFactory $resultPageFactory,
      \Magento\Framework\Message\ManagerInterface $messageManager)
   {
      // stuff
   }
}

I get the error

PHP Fatal error: Uncaught TypeError: Argument 2 passed to
Ben\Testing\Controller\Test::__construct() must be an instance of 
Magento\Framework\View\Result\PageFactory, instance of 
Magento\Framework\Message\ManagerInterface given, called in 
/magento-dev/magento2/generated/code/Ben/Testing/Controller/Test/Interceptor.php
on line 7 and defined in /magento-dev/magento2/app/code/Ben/Testing/Controller/Test.php:7
...

If I clear the /generated folder and refresh the page, it works.

My question is about how Magento handles caching and generated code. I can add additional code to non-constructor functions without needing to clear /generated, and it works. Why does that work, but adding additional constructor parameters requires me to clear the /generated folder?

7

Short answer: Because Magento does not use the class you think it uses.
Long answer:
It is because of plugins/interceptors.
So, for every class that has methods that has plugins, magento generates a class with the same name as the original class and adds \Interceptor at the end.
You can see that in your error message.
This interceptors extend the original classes

use \Magento\Framework\Interception\Interceptor;

public function __construct(list of main  class parameters in here)
{
    $this->___init();
    parent::__construct(list of main class parameters in here); //THIS LINE IS IMPORTANT FOR YOUR PROBLEM.  
}

/**
 * {@inheritdoc}
 * This is added for every interceptable method in the main class and it's named exactly like the method
 */
public function interceptableMethod(parameters of interceptable method)
{
    $pluginInfo = $this->pluginList->getNext($this->subjectType, 'interceptableMethod');
    if (!$pluginInfo) {
        return parent::interceptableMethod(parameters of interceptable method);
    } else {
        return $this->___callPlugins('interceptableMethod', func_get_args(), $pluginInfo);
    }
}

The second thing to take into account is that such an interceptor class is generated only if it does not exist already.

So when you change the constructor signature of your class and the interceptor class will be generated this happens.

Your new class constructor will require 3 parameters.
The generated interceptor still has the old constructor with 2 parameters.
This is not a problem for the interceptor class itself as it will be instantiated with 2 dependencies.
But then it calls parent::__construct (the line I marked in the example above) with the same 2 parameters. and parent is your own class that now requires 3 parameters.
So this will trigger an error.
Removing the interceptor class from the generated folder makes magento regenerate it on the next call and this time it will have the proper constructor signature.

  • Great explanation. Thank you for your help. – Ben Rubin Jan 18 at 17:43

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.