This component changes the way Magento 2 generates Interceptor classes

Overview

ABOUT

This component changes the way Magento 2 generates Interceptor classes (a mechanism that allows plugins to work together).

Instead of generating boilerplate code it compiles the Interceptor using information from source code. This makes plugins slightly faster and as Magento uses a lot of plugins, even at its core, it lowers request time by ~10%. This is important in places where there is a lot of non-cached PHP logic going on (for example admin panel is noticeably faster).

The default method uses code that is called on nearly each method to see if there are any plugins connected, in generated code this is not required and call-stack is reduced.

Having plugins called directly also makes code easier to debug and bugs easier to find.

The Interceptors generated by this plugin are 100% compatible with the ones generated by Magento by default, so there is no need to change anything in your plugins.

CONFIGURATION

This module adds following preference to work in developer mode:

">

  

And following preferences to work in production or default modes:

">

  

  

Those are enabled by default in module di.xml.

However this module does not touch the default generation method so you can override above preferences to fallback to the default mechanism for all or selected modes.

TECHNICAL DETAILS

Instead of interceptors that read plugins config at runtime like this:

public function methodX($arg) {
    $pluginInfo = $this->pluginList->getNext($this->subjectType, 'methodX');
    if (!$pluginInfo) {
        return parent::methodX($arg);
    } else {
        return $this->___callPlugins('methodX', func_get_args(), $pluginInfo);
    }
}

This generator generates static interceptors like this:

public function methodX($arg) {
    switch(getCurrentScope()){
        case 'frontend':
            $this->_get_example_plugin()->beforeMethodX($this, $arg);
            $this->_get_another_plugin()->beforeMethodX($this, $arg);
            $result = $this->_get_around_plugin()->aroundMethodX($this, function($arg){
                return parent::methodX($arg);
            });
            return $this->_get_after_plugin()->afterMethodX($this, $result);
        case 'adminhtml':
            // ...
        default:
            return parent::methodX($arg);
    }
}

PROS

  • Easier debugging.

    • If you ever stumbled upon ___callPlugins when debugging you should know how painful it is to debug issues inside plugins.
    • Generated code is decorated with PHPDoc for easier debugging in IDE
  • Fastest response time (5%-15% faster in developer and production mode)

    • No redundant calls to ___callPlugins in call stack.
    • Methods with no plugins are not overridden in parent at all.
  • Implemented as a module and can be easily reverted to the default Generator\Interceptor

CONS

  • Each time after making change in etc plugins config, generated/code/* and var/cache/* needs to be purged
  • As this does not load plugins at runtime, might not work in an edge case of plugging into core Magento classes like PluginsList etc.
Comments
  • Package is missing on packagist.org

    Package is missing on packagist.org

    Hi

    Great work with the extension.

    We'd like to use it in our projects but currently, it is not listed on packagist.org.

    Can you submit it there?

    Also, can we get this merged: https://github.com/creatuity/magento2-interceptors/pull/8 ?

    opened by diwipl 11
  • Compatibility with Magento 2.4.1

    Compatibility with Magento 2.4.1

    Hi,

    Having tried out Magento 2.4.1-beta2, it appears that the module no longer generates interceptors for anything else besides "global" and "primary" scope, all other frontend, adminhtml etc receive the exact same content for interceptors configuration.

    The root cause of this is because the gathering plugin information by scope was moved out of the PluginList class https://github.com/magento/magento2/commit/7e4a72e70103a57e28c798f6b7e0e835d8a266da https://github.com/magento/magento2/commit/11c5a67c45e6ece91e477dd490feb641388decc4

    A new class called PluginListGenerator (https://github.com/magento/magento2/blob/2.4-develop/lib/internal/Magento/Framework/Interception/PluginListGenerator.php) was born, which has its own instance of ScopeConfig.

    The current compiled-interceptors flow is based on creating instances of PluginList and providing it with a custom ScopeConfig implementation -> https://github.com/creatuity/magento2-interceptors/blob/master/Generator/AreasPluginList.php#L51

    This worked very well before, as PluginList was responsible for gathering the data about interceptors in different scopes, but as of M 2.4.1, this functionality is outsourced to PluginListGenerator, which has its own understanding of scope.

    In native Magento there is only one instance of PluginList and then the scopeConfig is updated with scopes to generate the specific scope. Compiled interceptors module creates several instances of these with custom scope values and aggregates them later.

    Now because everything is declared private in PluginListGenerator, it is not a trivial task to fix it in a way that the generator is not completely overridden by custom implementation.

    As a solution, one possibility is to patch the core PluginList and PluginListGenerator to make it possible to switch the scope in run-time for the generator:

    +++ Interception/PluginListGenerator.php	2020-10-02 15:03:39.000000000 +0300
    @@ -267,6 +267,14 @@
         }
     
         /**
    +     * @param $scopeCode
    +     */
    +    public function setCurrentScope($scopeCode)
    +    {
    +        $this->scopeConfig->setCurrentScope($scopeCode);
    +    }
    +
    +    /**
          * Collect parent types configuration for requested type
          *
          * @param string $type
    --- Interception/PluginList/PluginList-o.php	2020-09-17 17:31:14.000000000 +0300
    +++ Interception/PluginList/PluginList.php	2020-10-02 15:05:55.000000000 +0300
    @@ -231,6 +231,7 @@
                             $this->_loadedScopes[$scopeCode] = true;
                         }
                     } else {
    +                    $this->pluginListGenerator->setCurrentScope($scope);
                         [
                             $virtualTypes,
                             $this->_scopePriorityScheme,
    

    However, taking possible future changes and modularity into account, this goes more into the "ugly hack" side of things. EDIT: now there is a bit more decent PR available: https://github.com/creatuity/magento2-interceptors/pull/4

    On each generation run, current scope is forced into the generator, so it will generate all scopes correctly. The hard bit here is that the PluginListGenerator has shared parts, but then again, the scope needs to change on each run, otherwise we could have multiple instances of PluginListGenerator as well (each PluginList would have its own).

    I was wondering, perhaps you have some ideas how to solve this in a more decent way. I am also happy to create a PR in case I figure it out meanwhile.

    P.S. This is a very good module that you have and I would love to see this as part of Magento at some point.

    opened by siimm 9
  • Productrepository interceptor bug

    Productrepository interceptor bug

    Installed this on Magento 2.4.0

    Encountered what seems to be a bug in the productRepository interceptor.

    /**
         * {@inheritdoc}
         */
        public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveOptions = false)
        {
            switch ($this->____scope->getCurrentScope()) {
            	case 'global':
            	case 'frontend':
            	case 'adminhtml':
            	case 'graphql':
            	case 'crontab':
            	case 'primary':
            		$beforeResult = $this->____plugin_configurableProductSaveOptions()->beforeSave($this, $product, $saveOptions);
            		if ($beforeResult !== null) list($product, $saveOptions) = (array)$beforeResult;
            		
            		$result = parent::save($product, $saveOptions);
            		
            		return (($tmp = $this->____plugin_configurableProductSaveOptions()->afterSave($this, $result, $product, $saveOptions)) !== null) ? $tmp : $result;
            	default:
            		$beforeResult = $this->____plugin_product_authorization()->beforeSave($this, $product, $saveOptions);
            		if ($beforeResult !== null) list($product, $saveOptions) = (array)$beforeResult;
            		
            		$beforeResult = $this->____plugin_configurableProductSaveOptions()->beforeSave($this, $product, $saveOptions);
            		if ($beforeResult !== null) list($product, $saveOptions) = (array)$beforeResult;
            		
            		$result = parent::save($product, $saveOptions);
            		
            		return (($tmp = $this->____plugin_configurableProductSaveOptions()->afterSave($this, $result, $product, $saveOptions)) !== null) ? $tmp : $result;
            }
        }
    

    When using productrepository to save a product, it will throw an undefined offset notice. That's because the configurableProductSaveOptions plugin only returns 1 item in its array, while the interceptor expects 2 at lines such as

    if ($beforeResult !== null) list($product, $saveOptions) = (array)$beforeResult;

    Or is this technically a bug in the magento 2 code itself? (plugin should return the same amount of parameters it gets fed)?

    opened by Quazz 3
  • Support for Magento 2.4.1

    Support for Magento 2.4.1

    Fix for Magento 2.4.1 discussed in #3

    This is based on a solution proposed by @siimm in #4 however it is wrapped in a check that will make this work also in previous Magento versions.

    opened by fsw 2
  • Add cache.xml file

    Add cache.xml file

    This makes Magento aware of the compiled_plugins cache definition and the filecache directory, meaning it clears it at appropriate times during development.

    opened by maaarghk 1
  • Prevent losing arguments during plugins execution

    Prevent losing arguments during plugins execution

    Motivation

    • Compiled interceptors respect variable amount of arguments (variadic).
    • Before plugin must have possibility to change a number of arguments passed to all other plugins and original methods in a chain without losing arguments.
    • Compiled interceptors respect OOP approach. It must be possible to extend public methods with additional arguments in children classes without any drawbacks.
    • There is no such kind of issue in M2 OOTB.

    Possible scenarios

    • Before plugin changes amount of result arguments, which is not equal to all arguments listed in an original method. For example original method has 5 arguments, but before plugin returns only 2, when 3 other are optional and could be omitted. There would be an issue with undefined offset when it comes to the list construction.
    • Before plugin returns more arguments compared to a list of arguments of an original method. Then all extra arguments would be lost as list construction expects certain number of arguments. All other further before / around / after plugins and original method would not receive correct list of arguments replaced by the first before plugin.
    • More arguments are passed to the original methods compared to the method's declaration. Then before / around / after plugins as well as original method would receive only list of arguments declared for the original method. All extra arguments would be lost.
    • Children class may extend any public method with extra arguments at the end of a declaration, then plugins, written for a parent class, would ignore all extra arguments declared in children class. Because of that original method may receive less amount of arguments compared to those, which reached interceptor.

    Possible drawbacks

    • With current approach (without requested changes) it is possible to design plugin with less strict definition compared to the original subject method. For example one can design code without respect to default values of original method. Compiled interceptors would pass all arguments to other plugins and original method without any error. So, basically it is possible to write plugin, which wouldn't work in clean M2, but it would work with compiled interceptors. But definition of the plugin wouldn't respect OOP principles. Plugin must respect number of arguments, their types and default values. After this PR merged such plugins would fail with error Too few arguments to function <class::method>, <number_of_args> passed in <path_to_class_file>, but same issue would happen with that class if we disable compiled interceptors and return back to native M2 interceptors.

    Examples

    Here we'd like to show example for plugin file with 2 plugins applied to \Magento\ImportExport\Model\Import\Entity\AbstractEntity class such as addRowError() and isNeedToLogInHistory(). Despite namespace is different plugins are still applied to \Magento\ImportExport\Model\Import\Entity\AbstractEntity.

    Plugin class generated with current (non-patched) version of compiled interceptors

    <?php
    namespace Vaimo\InventoryImport\Model\Import\Source\Satellite;
    
    use Magento\Framework\Config\ScopeInterface;
    use Magento\Framework\ObjectManagerInterface;
    
    /**
     * Interceptor class for @see \Vaimo\InventoryImport\Model\Import\Source\Satellite
     */
    class Interceptor extends \Vaimo\InventoryImport\Model\Import\Source\Satellite
    {
        /**
         * @var ScopeInterface
         */
        private $____scope = null;
    
        /**
         * @var ObjectManagerInterface
         */
        private $____om = null;
    
        /**
         * @var \Vaimo\DynamicScheduledImport\Plugin\Model\Import\Entity
         */
        private $____plugin_vaimo_dynamic_import_enable_logging = null;
    
        /**
         * {@inheritdoc}
         */
        public function __construct(\Magento\Framework\ObjectManagerInterface $____om, \Magento\Framework\Config\ScopeInterface $____scope, \Magento\InventoryImportExport\Model\Import\Serializer\Json $jsonHelper, \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface $errorAggregator, \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper, \Magento\ImportExport\Helper\Data $dataHelper, \Magento\ImportExport\Model\ResourceModel\Import\Data $importData, \Magento\InventoryImportExport\Model\Import\Validator\ValidatorInterface $validator, array $commands = [])
        {
            $this->____om = $____om;
            $this->____scope = $____scope;
            parent::__construct($jsonHelper, $errorAggregator, $resourceHelper, $dataHelper, $importData, $validator, $commands);
        }
    
        /**
         * {@inheritdoc}
         */
        public function addRowError($errorCode, $errorRowNum, $colName = null, $errorMessage = null, $errorLevel = 'critical', $errorDescription = null)
        {
            $beforeResult = $this->____plugin_vaimo_dynamic_import_enable_logging()->beforeAddRowError($this, $errorCode, $errorRowNum, $colName, $errorMessage, $errorLevel, $errorDescription);
            if ($beforeResult !== null) list($errorCode, $errorRowNum, $colName, $errorMessage, $errorLevel, $errorDescription) = (array)$beforeResult;
    
            return parent::addRowError($errorCode, $errorRowNum, $colName, $errorMessage, $errorLevel, $errorDescription);
        }
    
        /**
         * {@inheritdoc}
         */
        public function isNeedToLogInHistory()
        {
            $result = parent::isNeedToLogInHistory();
    
            return (($tmp = $this->____plugin_vaimo_dynamic_import_enable_logging()->afterIsNeedToLogInHistory($this, $result)) !== null) ? $tmp : $result;
        }
    
        /**
         * plugin "vaimo_dynamic_import_enable_logging"
         * @return \Vaimo\DynamicScheduledImport\Plugin\Model\Import\Entity
         */
        private function ____plugin_vaimo_dynamic_import_enable_logging()
        {
            if ($this->____plugin_vaimo_dynamic_import_enable_logging === null) {
            	$this->____plugin_vaimo_dynamic_import_enable_logging = $this->____om->get(\Vaimo\DynamicScheduledImport\Plugin\Model\Import\Entity::class);
            }
            return $this->____plugin_vaimo_dynamic_import_enable_logging;
        }
    }
    

    Plugin class generated with patched version of compiled interceptors (with changes proposed by this pull-request)

    <?php
    namespace Vaimo\InventoryImport\Model\Import\Source\Satellite;
    
    use Magento\Framework\Config\ScopeInterface;
    use Magento\Framework\ObjectManagerInterface;
    
    /**
     * Interceptor class for @see \Vaimo\InventoryImport\Model\Import\Source\Satellite
     */
    class Interceptor extends \Vaimo\InventoryImport\Model\Import\Source\Satellite
    {
        /**
         * @var ScopeInterface
         */
        private $____scope = null;
    
        /**
         * @var ObjectManagerInterface
         */
        private $____om = null;
    
        /**
         * @var \Vaimo\DynamicScheduledImport\Plugin\Model\Import\Entity
         */
        private $____plugin_vaimo_dynamic_import_enable_logging = null;
    
        /**
         * {@inheritdoc}
         */
        public function __construct(\Magento\Framework\ObjectManagerInterface $____om, \Magento\Framework\Config\ScopeInterface $____scope, \Magento\InventoryImportExport\Model\Import\Serializer\Json $jsonHelper, \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface $errorAggregator, \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper, \Magento\ImportExport\Helper\Data $dataHelper, \Magento\ImportExport\Model\ResourceModel\Import\Data $importData, \Magento\InventoryImportExport\Model\Import\Validator\ValidatorInterface $validator, array $commands = [])
        {
            $this->____om = $____om;
            $this->____scope = $____scope;
            parent::__construct($jsonHelper, $errorAggregator, $resourceHelper, $dataHelper, $importData, $validator, $commands);
        }
    
        /**
         * {@inheritdoc}
         */
        public function addRowError($errorCode, $errorRowNum, $colName = null, $errorMessage = null, $errorLevel = 'critical', $errorDescription = null)
        {
            $arguments = func_get_args();
            $beforeResult = $this->____plugin_vaimo_dynamic_import_enable_logging()->beforeAddRowError($this, ...array_values($arguments));
            if ($beforeResult !== null) $arguments = (array)$beforeResult;
    
            return parent::addRowError(...array_values($arguments));
        }
    
        /**
         * {@inheritdoc}
         */
        public function isNeedToLogInHistory()
        {
            $arguments = func_get_args();
            $result = parent::isNeedToLogInHistory(...array_values($arguments));
    
            return (($tmp = $this->____plugin_vaimo_dynamic_import_enable_logging()->afterIsNeedToLogInHistory($this, $result, ...array_values($arguments))) !== null) ? $tmp : $result;
        }
    
        /**
         * plugin "vaimo_dynamic_import_enable_logging"
         * @return \Vaimo\DynamicScheduledImport\Plugin\Model\Import\Entity
         */
        private function ____plugin_vaimo_dynamic_import_enable_logging()
        {
            if ($this->____plugin_vaimo_dynamic_import_enable_logging === null) {
            	$this->____plugin_vaimo_dynamic_import_enable_logging = $this->____om->get(\Vaimo\DynamicScheduledImport\Plugin\Model\Import\Entity::class);
            }
            return $this->____plugin_vaimo_dynamic_import_enable_logging;
        }
    }
    

    Plugin class generated by clean M2

    <?php
    namespace Vaimo\InventoryImport\Model\Import\Source\Satellite;
    
    /**
     * Interceptor class for @see \Vaimo\InventoryImport\Model\Import\Source\Satellite
     */
    class Interceptor extends \Vaimo\InventoryImport\Model\Import\Source\Satellite implements \Magento\Framework\Interception\InterceptorInterface
    {
        use \Magento\Framework\Interception\Interceptor;
    
        public function __construct(\Magento\InventoryImportExport\Model\Import\Serializer\Json $jsonHelper, \Magento\ImportExport\Model\Import\ErrorProcessing\ProcessingErrorAggregatorInterface $errorAggregator, \Magento\ImportExport\Model\ResourceModel\Helper $resourceHelper, \Magento\ImportExport\Helper\Data $dataHelper, \Magento\ImportExport\Model\ResourceModel\Import\Data $importData, \Magento\InventoryImportExport\Model\Import\Validator\ValidatorInterface $validator, array $commands = [])
        {
            $this->___init();
            parent::__construct($jsonHelper, $errorAggregator, $resourceHelper, $dataHelper, $importData, $validator, $commands);
        }
    
        /**
         * {@inheritdoc}
         */
        public function addRowError($errorCode, $errorRowNum, $colName = null, $errorMessage = null, $errorLevel = 'critical', $errorDescription = null)
        {
            $pluginInfo = $this->pluginList->getNext($this->subjectType, 'addRowError');
            return $pluginInfo ? $this->___callPlugins('addRowError', func_get_args(), $pluginInfo) : parent::addRowError($errorCode, $errorRowNum, $colName, $errorMessage, $errorLevel, $errorDescription);
        }
    
        /**
         * {@inheritdoc}
         */
        public function isNeedToLogInHistory()
        {
            $pluginInfo = $this->pluginList->getNext($this->subjectType, 'isNeedToLogInHistory');
            return $pluginInfo ? $this->___callPlugins('isNeedToLogInHistory', func_get_args(), $pluginInfo) : parent::isNeedToLogInHistory(...func_get_args());
        }
    }
    
    opened by meandor-ua 1
  • Support of __sleep and __wakeup magic methods

    Support of __sleep and __wakeup magic methods

    Motivation of this pull-request was: adding support of __sleep and __wakeup magic methods to compiled interceptors functionality to avoid fatal errors.

    When object is unserialized, then constructor is not used then. Because of that for all serialized objects in Magento 2 with compiled interceptors properties such as ____scope and ____om are not populated automatically. Unserialized objects will be still operational until plugin would be triggered. We'd face a fatal error PHP Fatal error: Uncaught Error: Call to a member function get() on null if any plugin from a compiled interceptor would be triggered from unserialized object.

    Test case here would be:

    1. Navigate to Admin Panel => System => Permissions => User Roles

    2. Select your current admin role

    3. Proceed to Role Resources => Roles Resources. Select Custom.

    4. Change some permissions and save admin user.

    5. Without the current fix it won't be possible to proceed because of fatal error PHP Fatal error: Uncaught Error: Call to a member function get() on null as current User model is serialized to session.

    The result produced by an interceptor code generator after this patch would be

    /**
     * {@inheritdoc}
     */
    public function __sleep()
    {
        $properties = parent::__sleep();
        return array_diff(
            $properties,
            [
                '____scope',
                '____om',
            ]
        );
    }
    
    /**
     * {@inheritdoc}
     */
    public function __wakeup()
    {
        parent::__wakeup();
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $this->____scope = $objectManager->get(\Magento\Framework\Config\ScopeInterface::class);
        $this->____om = $objectManager->get(\Magento\Framework\ObjectManagerInterface::class);
    }
    

    for classes where both __sleep and __wakeup methods are declared.

    opened by meandor-ua 1
  • Force scope change on shared PluginListGenerator to generate all scopes (M2.4.1 compatibility)

    Force scope change on shared PluginListGenerator to generate all scopes (M2.4.1 compatibility)

    Fixes the compatibility with latest Magento (dev-develop, 2.4.1.-beta2), enables gathering plugin data from all scopes. Fixes issue #3 Solution suggested by @meandor-ua

    opened by siimm 1
  • Compatibility with Magento Cloud

    Compatibility with Magento Cloud

    In the latest version (1.3.2), a cache.xml file has been added which adds cache and is to the problem on Magento Cloud. When setup:upgrade is executed, the files from generated/staticcache are deleted and generated. This is not executable on Magento Cloud, and we get an error during deployment.

    INFO: Running setup upgrade.
    INFO: Set flag: var/.deploy_is_failed
    ERROR: [126] The command "/bin/bash -c "set -o pipefail; php ./bin/magento setup:upgrade --keep-generated --ansi --no-interaction -vvv | tee -a /app/xxxxxxxx_stg/var/log/install_upgrade.log"" failed. Cache types config flushed successfully
      Warning: unlink(/app/xxxxxxxx_stg/generated/staticcache/global_primary_adminhtml_compiled_plugins.php): Read-only file system in /app/xxxxxxxx_stg/vendor/creatuity/magento2-interceptors/Generator/FileCache.php on line 121
    INFO: Set flag: var/.deploy_is_failed
      W: 
      W: In Processor.php line 129:
      W:                                                                                
      W:   The command "/bin/bash -c "set -o pipefail; php ./bin/magento setup:upgrade  
      W:    --keep-generated --ansi --no-interaction -vvv | tee -a /app/xxxxxxxx_  
      W:   stg/var/log/install_upgrade.log"" failed. Cache types config flushed s  
      W:   uccessfully                                                             
      W:   Warning: unlink(/app/xxxxxxxx_stg/generated/staticcache/global  
      W:   _primary_adminhtml_compiled_plugins.php): Read-only file system in /app/xxxxxxxx  
      W:   _stg/vendor/creatuity/magento2-interceptors/Generator/FileCache.p  
      W:   hp on line 121                                                       
      W:                                                                                
      W: 
      W: In InstallUpdate.php line 100:
      W:                                                                                
      W:   The command "/bin/bash -c "set -o pipefail; php ./bin/magento setup:upgrade  
      W:    --keep-generated --ansi --no-interaction -vvv | tee -a /app/xxxxxxxx_  
      W:   stg/var/log/install_upgrade.log"" failed. Cache types config flushed s  
      W:   uccessfully                                                             
      W:   Warning: unlink(/app/xxxxxxxx_stg/generated/staticcache/global  
      W:   _primary_adminhtml_compiled_plugins.php): Read-only file system in /app/xxxxxxxx  
      W:   _stg/vendor/creatuity/magento2-interceptors/Generator/FileCache.p  
      W:   hp on line 121                                                       
      W:                                                                                
      W: 
      W: In Setup.php line 58:
      W:                                                                                
      W:   The command "/bin/bash -c "set -o pipefail; php ./bin/magento setup:upgrade  
      W:    --keep-generated --ansi --no-interaction -vvv | tee -a /app/xxxxxxxx_  
      W:   stg/var/log/install_upgrade.log"" failed. Cache types config flushed s  
      W:   uccessfully                                                             
      W:   Warning: unlink(/app/xxxxxxxx_stg/generated/staticcache/global  
      W:   _primary_adminhtml_compiled_plugins.php): Read-only file system in /app/xxxxxxxx  
      W:   _stg/vendor/creatuity/magento2-interceptors/Generator/FileCache.p  
      W:   hp on line 121                                                       
      W:                                                                                
      W: 
      W: In Shell.php line 86:
      W:                                                                                
      W:   The command "/bin/bash -c "set -o pipefail; php ./bin/magento setup:upgrade  
      W:    --keep-generated --ansi --no-interaction -vvv | tee -a /app/xxxxxxxx_  
      W:   stg/var/log/install_upgrade.log"" failed. Cache types config flushed s  
      W:   uccessfully                                                             
      W:   Warning: unlink(/app/xxxxxxxx_stg/generated/staticcache/global  
      W:   _primary_adminhtml_compiled_plugins.php): Read-only file system in /app/xxxxxxxx  
      W:   _stg/vendor/creatuity/magento2-interceptors/Generator/FileCache.p  
      W:   hp on line 121                                                       
      W:                                                                                
      W: 
      W: deploy
      W: 
      E: Failed to run deploy hook
      E: Command '['sudo', '-u', 'xxxxxxxx_stg', 'bash', '-c', '/etc/platform/xxxxxxxx_stg/post_deploy.sh']' returned non-zero exit status 126
      Exiting maintenance mode
    

    On cloud when running setup:upgrade, the generated directory is only available in read-only and there is no way to change this. The application when deploying performs compilation every time, and with older versions it worked without an issue.

    opened by kbalazy-creatuity 3
Releases(1.2.1)
Owner
Creatuity Corp.
Creatuity is a Magento Enterprise Solutions Partner and frequent contributor to the Magento community.
Creatuity Corp.
This component provides a collection of functions/classes using the symfony/intl package when the Intl extension is not installed.

Symfony Polyfill / Intl: ICU This package provides fallback implementations when the Intl extension is not installed. It is limited to the "en" locale

Symfony 2.4k Jan 6, 2023
More options when uploading files such as name changes, resizing or compression through TinyPNG.

Kirby Upload Extended More options when uploading files like name changes, resizing via Kirby or compression and optional resizing via TinyPNG. Thanks

Oli 24 Nov 12, 2022
Joole Reflector - used to work with the properties of objects, their changes and merges

Joole Reflector allows you to change protected, as well as private properties of an object.

Ravil Sitdikov 1 May 7, 2022
🔍 Generates database queries based on one unique string

?? Laravel Search String Generates database queries based on one unique string using a simple and customizable syntax. Introduction Laravel Search Str

Loris Leiva 735 Dec 30, 2022
Generates a static website of metal music events in Leipzig

About EN: This projects generates a static website of metal music events in Leipzig (Ger). DE: Dieses Projekt erstellt einen Webkalender zu diversen M

null 3 Dec 15, 2022
The plugin generates posts/pages. Useful to generate millions of records in wp_posts table

KAGG Generator The plugin generates posts/pages. Useful to generate millions of records in wp_posts table. In WordPress development, sometimes it is n

Igor at KAGG Design 16 Jan 1, 2023
QR Tips is a Wordpress plugin that generates a QR code for sending a tip via MultiSafepay

QR Tips is a Wordpress plugin that generates a QR code for sending a tip via MultiSafepay

Robin de Laater 1 Mar 25, 2022
bin/magento command to display configured preferences for classes or interfaces

bin/magento command to display configured preferences for classes or interfaces A bin/magento command that will show you the configured preferences fo

David Manners 14 Jul 18, 2022
A tool that allows to quickly export data from Magento 1 and Magento 2 store and import it back into Magento 2

Simple Import / Export tool A tool that allows to quickly export data from Magento 1 and Magento 2 store and import it back into Magento 2. Table data

EcomDev B.V. 51 Dec 5, 2022
MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query and get result in a fastest way

Mysql Optimizer mysql optimizer also known as MOP is a php query handling and manipulation library providing easy and reliable way to manipulate query

null 2 Nov 20, 2021
Magento 2 Megamenu extension is an indispensable component, and plays the role of website navigation to help customers easily categorize and find information

Mageno 2 Mega Menu (Magicmenu) helps you create neat and smart navigation menus to display the main categories on your website.

https://magepow.com 35 Dec 1, 2022
[READ-ONLY] CakePHP Utility classes such as Inflector, Text, Hash, Security and Xml. This repo is a split of the main code that can be found in https://github.com/cakephp/cakephp

CakePHP Utility Classes This library provides a range of utility classes that are used throughout the CakePHP framework What's in the toolbox? Hash A

CakePHP 112 Feb 15, 2022
A set of classes to create and manipulate HTML objects abstractions

HTMLObject HTMLObject is a set of classes to create and manipulate HTML objects abstractions. Static calls to the classes echo Element::p('text')->cla

Emma Fabre 128 Dec 22, 2022
Enforce that your classes get only instantiated by the factories you define!

Enforce that your classes get only instantiated by the factories you define!

null 3 Nov 15, 2021
Set of classes and tools to communicate with a Noso wallet using NosoP

NosoPHP Set of classes and tools to communicate with a Noso wallet using NosoP(Noso Protocol) Examples Node Info include __DIR__ . '/vendor/autoload.p

Noso Project 1 Jan 10, 2022
A set of PHP classes to let you manage your Dynamic Widget UI programmatically.

Dynamic Widget for Flutter A set of PHP classes to let you manage your Dynamic Widget UI programmatically. Why? Why not? Developing my application I f

Agostino Fiscale 3 Mar 22, 2022
Collection of useful PHP functions, mini-classes, and snippets for every day.

JBZoo / Utils Collection of PHP functions, mini classes and snippets for everyday developer's routine life. Install composer require jbzoo/utils Usage

JBZoo Toolbox 786 Dec 30, 2022