5

I upgraded to magento 2.3.3 but didn't noticed that they changed email classes and zend framework.

I am having a custom module for editing emails in magento admin.

But I am having error:

FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught Error: Class 'Zend_Reflection_Class' not found in myfile.php

My code is:

 {
        /**
         * @var VariableResolverInterface
         */
        private $variableResolver;

        public function __construct(
            VariableResolverInterface $variableResolver
        ) {
            $this->variableResolver = $variableResolver;
        }

        /**
         * {@inheritdoc}
         */
        public function getVariables()
        {
            $variables = $this->variableResolver->getOrigVariables();
            $result = [
                [
                    'label' => __('General Variables'),
                    'value' => [
                        [
                            'label' => __('Customer Name'),
                            'value' => __('{{ customer_name }}')
                        ],
                        [
                            'label' => __('Customer Email'),
                            'value' => __('{{ customer_email }}')
                        ],
                        [
                            'label' => __('Customer First Name'),
                            'value' => __('{{ customer_name | split: " " | first }}')
                        ],
                    ]
                ]
            ];

            foreach ($variables as $variable) {
                $variableReflection = new \Zend_Reflection_Class($variable);
                /** @var \Zend_Reflection_Method[] $reflectionMethods */
                $reflectionMethods = $variableReflection->getMethods(\ReflectionMethod::IS_PUBLIC);

                if ($methods = $this->collectMethods($variable, $reflectionMethods)) {
                    $result[] = [
                        'label' => __('%1 Variables', ucfirst($variable->getVariableName())),
                        'value' => $methods
                    ];
                }
            }

            return $result;
        }

        /**
         * Collect all available methods for user.
         *
         * @param VariableInterface         $variable
         * @param \Zend_Reflection_Method[] $reflectionMethods
         *
         * @return array
         * @throws \Zend_Reflection_Exception
         */
        private function collectMethods(VariableInterface $variable, array $reflectionMethods = [])
        {
            $methods = [];
            foreach ($reflectionMethods as $method) {
                if ($this->canUseMethod($method, $variable)) {
                    $docblock = $method->getDocblock();
                    $methodName = $this->convertToLiquid($method->getName());

                    // add methods available in the model object
                    if ($this->isObjectReturned($docblock) && $variable->getNamespace() == $methodName) {
                        $returnType = $docblock->getTag('return')->getType();
                        $variableReflection = new \Zend_Reflection_Class($returnType);
                        $reflectionMethods = $variableReflection->getMethods(\ReflectionMethod::IS_PUBLIC);

                        $methods = array_merge(
                            $methods,
                            $this->collectMethods($variable, $reflectionMethods)
                        );

                        continue;
                    }

                    if ($definition = $this->getMethodDefinition($variable, $method)) {
                        array_unshift($methods, $definition);
                    }
                }
            }

            return $methods;
        }

        /**
         * Determine whether the method can be used or not.
         *
         * @param \Zend_Reflection_Method $method
         * @param VariableInterface       $variable
         *
         * @return bool
         * @throws \Zend_Reflection_Exception
         */
        private function canUseMethod(\Zend_Reflection_Method $method, VariableInterface $variable)
        {
            $canUse = false;
            if (strpos($method->getName(), 'get') === 0 // only getters
                && strpos($method->getDeclaringClass()->getName(), 'AbstractVariable') === false // ignore AbstractClass
                && strpos($method->getDocComment(), '@Suppress') === false // tags without space throw an error
                && $method->getNumberOfRequiredParameters() === 0 // only methods without parameters
                && stripos($method->getDocComment(), '@inheritdoc') === false // ignore methods with "inheritdoc"
                && $method->getDocComment() // skip methods without docblock
            ) {
                $canUse = true;
            }

            return $canUse;
        }

        /**
         * Convert method name to liquid compatible syntax.
         *
         * e.g., "getCouponCode" => coupon_code
         *
         * @param string $methodName
         *
         * @return string
         */
        private function convertToLiquid($methodName)
        {
            $methodWords = preg_split('/(?=[A-Z])/', $methodName); // split name by uppercase letters
            $methodWords = array_filter($methodWords, function($name) { // remove keyword "get" from words
                return $name !== 'get';
            });

            // join remained words with underscore
            return implode('_', array_map('strtolower', $methodWords));
        }

        /**
         * Whether the method return type is object or not.
         *
         * @param \Zend_Reflection_Docblock $docblock
         *
         * @return bool
         *
         */
        private function isObjectReturned(\Zend_Reflection_Docblock $docblock)
        {
            if ($docblock->hasTag('return')) {
                return strpos($docblock->getTag('return')->getType(), '\\') !== false;
            }

            return false;
        }

        /**
         * Get liquid variable definition.
         *
         * @param VariableInterface $variable
         * @param \Zend_Reflection_Method $method
         *
         * @return array
         * @throws \Zend_Reflection_Exception
         * @SuppressWarnings(PHPMD.CyclomaticComplexity)
         */
        private function getMethodDefinition(VariableInterface $variable, \Zend_Reflection_Method $method)
        {
            $definition = [];
            $docblock   = $method->getDocblock();
            $methodName = $this->convertToLiquid($method->getName());
            $namespace  = $docblock->getTag(VariableInterface::DOCBLOCK_TAG_NAMESPACE)
                ? $docblock->getTag(VariableInterface::DOCBLOCK_TAG_NAMESPACE)->getDescription()
                : $variable->getNamespace();

            if ((method_exists($variable, $method->getName())
                    && $method->getDeclaringClass()->getName() === get_class($variable)
                ) || in_array($method->getName(), $variable->getWhitelist())
            ) {
                $value = '';
                if ($docblock->getTag('return')->getType() == 'array'
                    || strpos($docblock->getTag('return')->getType(), '[]') !== false
                ) {
                    $value .= "{% for item in {$namespace}.{$methodName} %}";
                    $value .= "\\n\\n";
                    $value .= "{% endfor %}";
                } else {
                    $filter = $docblock->getTag(VariableInterface::DOCBLOCK_TAG_FILTER)
                        ? $docblock->getTag(VariableInterface::DOCBLOCK_TAG_FILTER)->getDescription()
                        : null;

                    $value = "{{ {$namespace}.{$methodName} ";

                    if ($filter !== null) {
                        $value .= $filter; // add filter to variable
                    }

                    $value .= "}}"; // close variable
                }

                $definition = [
                    'value' => $value,
                    'label' => $docblock->hasTag(VariableInterface::DOCBLOCK_TAG_DESCRIPTION)
                        ? $docblock->getTag(VariableInterface::DOCBLOCK_TAG_DESCRIPTION)->getDescription()
                        : explode("\n", $docblock->getShortDescription())[0]
                ];
            }

            return $definition;
        }
    }

This is happening also with another class:

Zend_Reflection_Method

Anyone can help how can I declare these classes now please?

10
  • 1
    Perhaps \Zend\Code\Reflection\ClassReflection and \Zend\Code\Reflection\MethodReflection
    – danronmoon
    Commented Oct 18, 2019 at 17:47
  • Regarding mail changes: community.magento.com/t5/Magento-DevBlog/…
    – danronmoon
    Commented Oct 18, 2019 at 17:48
  • @danronmoon Thank you...Checking your first comment....As for mails, magento team has made huge mistake putting this as a patch....I reverted back some folders because I couldn't send any mail with my third party extensions
    – G. G.
    Commented Oct 18, 2019 at 17:50
  • You're absolutely correct about this being a mistake. I wish I could offer more comfort about future "patch" releases.
    – danronmoon
    Commented Oct 18, 2019 at 17:57
  • @danronmoon your fix is not working
    – G. G.
    Commented Oct 18, 2019 at 17:58

2 Answers 2

8

Please run composer require zf1/zend-exception composer require zf1/zend-reflection I had the same issue and that solved.

3
  • It worked!! Thank you!
    – G. G.
    Commented Jan 24, 2020 at 23:12
  • Thank you. Worked!!!
    – panosdotk
    Commented Sep 4, 2020 at 13:23
  • I guess the extensions we are using do not have the current depandancies setup or they assume these packagages are already there or perhaps they used to be part of the core Magento and have since been removed? Commented May 23 at 21:50
1

For anyone having a similar problem with 2.3.5+ you need to install 2 laminas packages. Firstly

 composer require laminas/laminas-dependency-plugin

This intercepts requests to install packages from the Zend Framework and will replace them with the equivalents from the Laminas Project. Then you want to install this:

composer require laminas/laminas-zendframework-bridge

This provides a custom autoloader that aliases legacy Zend Framework, Apigility, and Expressive classes to their replacements under the Laminas Project.

You can see the full post here. I hope this helps save you some time!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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