Categories


Archives


Recent Posts


Categories


Magento 2: More Plugin Edge Cases

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

This one’s more interesting than it is useful (or its main use is as a debugging exercise), but while I was working on a UI Component tutorial I wanted to turn off the XSD validation for ui_component files.

It turns out there’s a dedicated object that can control whether the XSD validations are applied ($this->validationState->isValidationRequired() below.

#File: vendor/magento/framework/View/Element/UiComponent/Config/DomMerger.php
public function createDomDocument($xml)
{
    $domDocument = new DOMDocument();
    $domDocument->loadXML($xml);
    if ($this->validationState->isValidationRequired() && $this->schema) {
        $errors = $this->validateDomDocument($domDocument);
        if (count($errors)) {
            throw new MagentoFrameworkExceptionLocalizedException(
                new MagentoFrameworkPhrase(implode("n", $errors))
            );
        }
    }

    return $domDocument;
}

Jumping up to the constructor we see that validationState is

#File: vendor/magento/framework/View/Element/UiComponent/Config/DomMerger.php
//...
use MagentoFrameworkConfigValidationStateInterface;
//...
public function __construct(
    ValidationStateInterface $validationState,
    $schema,
    $isMergeSimpleXMLElement = false,
    array $contextXPath = [],
    array $idAttributes = []
) {
    $this->validationState = $validationState;
    $this->schema = $schema;
    $this->isMergeSimpleXMLElement = $isMergeSimpleXMLElement;
    $this->contextXPath = $contextXPath;
    $this->idAttributes = $idAttributes;
}

a MagentoFrameworkConfigValidationStateInterface type/object/interface. Searching though the di.xml files we see the MagentoFrameworkConfigValidationStateInterface has

$ find vendor/magento/ -name di.xml | xargs grep ValidationStateInterface
vendor/magento//magento2-base/app/etc/di.xml:    <preference for="MagentoFrameworkConfigValidationStateInterface" type="MagentoFrameworkAppArgumentsValidationState"></preference>

a concrete class of MagentoFrameworkAppArgumentsValidationState. Peeking at this class’s source file

#File: vendor/magento/framework/App/Arguments/ValidationState.php
public function isValidationRequired()
{
    return $this->_appMode == MagentoFrameworkAppState::MODE_DEVELOPER;
}    

we see that the isValidationRequired only checks to see if the mode is development. The interesting bit for me is this means production and default modes don’t apply the XSD validations.

As for skipping this in development mode, a little bit of pestle let me configure a plugin for the MagentoFrameworkAppArgumentsValidationState class.

$ pestle.phar generate_plugin_xml
Create in which module? (Pulsestorm_Helloworld)] Pulsestorm_SimpleUiComponent
Which class are you plugging into? (MagentoFrameworkLoggerMonolog)] MagentoFrameworkAppArgumentsValidationState
What's your plugin class name? (PulsestormSimpleUiComponentPluginMagentoFrameworkAppArgumentsValidationState)] 
Added nodes to /Users/alanstorm/Sites/magento-2-1-1.dev/project-community-edition/app/code/;/SimpleUiComponent/etc/di.xml
Created file /Users/alanstorm/Sites/magento-2-1-1.dev/project-community-edition/app/code/Pulsestorm/SimpleUiComponent/Plugin/Magento/Framework/App/Arguments/ValidationState.php

and then drop a hard coded return false into my plugin class.

<?php namespace PulsestormSimpleUiComponentPluginMagentoFrameworkAppArguments;
class ValidationState
{
    public function afterIsValidationRequired($subject, $result)
    {
        return false;
    }    
}

Or – that’s what I thought anyways. Unfortunately, this ended up giving me the following error.

Fatal error: Maximum function nesting level of ‘300’ reached, aborting! in /Users/alanstorm/Sites/magento-2-1-1.dev/project-community-edition/vendor/colinmollenhour/cache-backend-file/File.php on line 404

The problem? Jumping into the call stack

118 2.8599  28646104    MagentoFrameworkAppArgumentsValidationStateInterceptor->isValidationRequired( )    .../Dom.php:387
119 2.8599  28646104    MagentoFrameworkInterceptionPluginListPluginList->getNext( )    .../Interceptor.php:22
120 2.8599  28646104    MagentoFrameworkInterceptionPluginListPluginList->_loadScopedData( )    .../PluginList.php:248
121 2.8599  28646216    MagentoFrameworkObjectManagerConfigReaderDomProxy->read( )    .../PluginList.php:280
122 2.8599  28646216    MagentoFrameworkConfigReaderFilesystem->read( ) .../Proxy.php:95
123 2.8633  28668096    MagentoFrameworkConfigReaderFilesystem->_readFiles( )   .../Filesystem.php:127
124 2.8633  28688712    MagentoFrameworkObjectManagerConfigReaderDom->_createConfigMerger( )   .../Filesystem.php:146
125 2.8633  28688872    MagentoFrameworkConfigDom->__construct( )    .../Dom.php:70

In order for plugins to work, Magento needs to instantiate a MagentoFrameworkConfigDom object. This object has MagentoFrameworkConfigValidationStateInterface as an injected dependency, and we end up in an endless loop (yay for xDebug saving us!).

So, in the end? I had to settle for a di.xml <preference></preference> configuration. Not ideal, but when the (benign?) neglect of a system’s owner leads to situations like the above, developers are going to do the less-right thing that gets their jobs done.

Copyright © Alan Storm 1975 – 2017 All Rights Reserved

Originally Posted: 3rd September 2016