Проблема ImportExport с новым деструктором Varien_Image_Adapter_Gd2 в 1.9.2.0

23

Может кто-нибудь объяснить, для чего используется следующий код, введенный между Magento CE 1.9.1.0 и 1.9.2.0?

class Varien_Image_Adapter_Gd2:

public function __construct()
{
    // Initialize shutdown function
    register_shutdown_function(array($this, 'destruct'));
}

/**
 * Destroy object image on shutdown
 */
public function destruct()
{
    @imagedestroy($this->_imageHandler);
}

После того, как эти две функции были добавлены, наш импорт изображений галереи продуктов с интерфейсом ImportExport перестал работать. Ошибка возникает из-за ограничения памяти (которое соответствует максимальному размеру открытого файла).

Моя идея заключается в том, что файлы, открытые при импорте, не будут закрыты правильно.

Я также увидел, что были destruct()введены некоторые пустые функции ( Mage_ImportExport_Model_Import_Adapter_Abstract), но расширение их для соответствия родительской логике не помогает.

Ахим Розенхаген
источник

Ответы:

14

Похоже, что они пытались уничтожить ресурс изображения, но вместо этого внесли утечку памяти. Если честно, я не могу придумать вескую причину для этого кода, но могу объяснить, что было изменено:

Первоначально, imagedestroy()был бы вызван в desctructor__destruct()

function __destruct()
{
    @imagedestroy($this->_imageHandler);
}

Деструктор вызывается всякий раз, когда сборщик мусора PHP уничтожает неиспользуемые объекты (то есть объекты в памяти, на которые больше нет ссылок).

Теперь imagedestroy()вместо этого вызывается в функции завершения работы, и, поскольку это обратный вызов метода Varien_Image_Adapter_Gd2объекта, он не может быть даже собран мусором до самого конца. Таким образом, все ресурсы изображения остаются открытыми до завершения выполнения скрипта.

Фабиан Шменглер
источник
Спасибо за объяснение - это то, о чем я подумала. Таким образом, в целом, этот введенный код делает большую часть импорта бесполезной на 1.9.2. в моих глазах. Надеюсь, это будет исправлено в ближайшее время. Любой совет, где открыть сообщение об ошибке?
Ахим Rosenhagen
6

Имея те же проблемы с моим Magento 1.9.2.0 ...

Я получаю только это работать, изменяя Varien_Image_Adapter_Gd2 в /lib/Varien/Image/Adapter/Gd2.phpследующим образом :

public function __construct()
{
    // Initialize shutdown function
    // register_shutdown_function(array($this, 'destruct'));
}

/**
 * Destroy object image on shutdown
 */
public function __destruct()
{
    @imagedestroy($this->_imageHandler);
}
  • удалить строку с помощью register_shutdown_function (или закомментировать)
  • изменить имя функции destruct на __destruct

Я установил memory_limit обратно в 1G (ранее я поднял до 32 ГБ), и теперь это работает ...

Этот проект реализует указанную процедуру в удобной форме. Просто установите его с композитором, и все готово.

ДКР
источник
Это на самом деле не отвечает на вопрос. Если у вас есть другой вопрос, вы можете задать его, нажав Задать вопрос . Вы также можете добавить награду, чтобы привлечь больше внимания к этому вопросу, когда у вас будет достаточно репутации .
Раджив К Томи
Да, это не дает ответа на вопрос , но будет помогать людям , кто нуждается временное решение и без обсуждения
Dkr
исправлена ​​проблема с потреблением памяти при импорте. Интересно, Magento как-то тестирует то, что выпускают?
клипач
Это решает не только проблему импорта. Это решает проблему использования большого объема памяти процессом создания / воссоздания кэша и версий с измененным размером для изображений каждого продукта. Если я загружаю PNG-изображения в мои продукты, без этого «хака» я не могу работать, и я получаю много ошибок исчерпания памяти.
Simbus82
Сегодня я нашел это предложение. Я реализовал это, и утечка памяти исчезла. Затем я создал этот github.com/borasocom-team/magento-gd2-memoryleak, чтобы установить его чистым способом.
Доктор Джанлуиджи Зане Занеттини
5

Это было частью решения проблем безопасности с помощью unserialize. У таких магических методов, как __destruct, есть проблемы с сериализацией.

Мы видели предложенные эксплойты, которые использовали сериализацию и __destruct для создания файлов в файловой системе - и это изменение (вы увидите больше подобных изменений в других местах) было сделано, чтобы избежать этого.

Это вызывает утечку памяти или просто использует больше памяти, пока сценарий не завершится?

/security/77549/is-php-unserialize-exploitable-without-any-interesting-methods

Петр Каминский
источник
Спасибо за контекст. Это конкретное изменение было сделано, чтобы предотвратить конкретную эксплойт или просто чтобы быть уверенным?
Фабиан Шменглер
И нет, это, вероятно, просто заставляет скрипт потреблять больше памяти, а не настоящую утечку памяти
Фабиан Шменглер,
Это вызывает огромную утечку памяти, особенно при импорте изображений, так как все файлы изображений будут оставаться открытыми до конца обработки импорта. Таким образом, мы можем импортировать только около 50 продуктов (тогда, прежде чем мы сможем сделать импорт более 2 тыс.). Я запустил свой тест на локальной виртуальной машине с 8 ГБ ОЗУ, и все исходные файлы имеют размер около 300 КБ. Перед изменением используемой памяти PHP остается 1 КБ в течение всего импорта.
Ахим Розенхаген
fschmengler прав - это может быть не «утечка памяти», но потребление возрастает с каждым днем ​​;-)
Ахим Розенхаген
1
@ Алекс спасибо за совет. Я реверс залатал. Теперь утечка памяти ушла, но нет решения на будущее.
Арне
4

Поэтому я поднял ошибку в Magento, включая «решение», которое должно было решить проблемы с использованием памяти в процессе импорта изображений.

Решение можно найти на github по адресу https://github.com/sitewards/import_image_memory_leak_fix, но основная идея такова.

Исправление Mage_Catalog_Helper_Image::validateUploadFileдля фактического вызова destructметода на процессоре изображений. К сожалению, кажется, что по умолчанию Varien_Imageне работает, destructпоэтому нам пришлось добавить свой собственный класс, который делает.

<?php
/**
 * @category    Sitewards
 * @package     Sitewards_ImportImageMemoryLeakFix
 * @copyright   Copyright (c) Sitewards GmbH (http://www.sitewards.com/)
 */
class Sitewards_ImportImageMemoryLeakFix_Model_Destructable_Image extends Varien_Image
{
    /**
     * Constructor,
     * difference from original constructor - we register a destructor here.
     *
     * @param string $sFileName
     * @param Varien_Image_Adapter $oAdapter Default value is GD2
     */
    public function __construct($sFileName = null, $oAdapter = Varien_Image_Adapter::ADAPTER_GD2)
    {
        parent::__construct($sFileName, $oAdapter);

        // Initialize shutdown function
        register_shutdown_function(array($this, 'destruct'));
    }

    /**
     * Destroy object image on shutdown
     */
    public function destruct()
    {
        $oAdapter = $this->_getAdapter();
        if (method_exists($oAdapter, 'destruct')) {
            $oAdapter->destruct();
        } else {
            Mage::log('Image can not be destructed properly, adapter doesn\'t support the method.');
        }
    }
}

А потом переписать помощника.

<?xml version="1.0"?>
<config>
    <modules>
        <Sitewards_ImportImageMemoryLeakFix>
            <version>0.1.0</version>
        </Sitewards_ImportImageMemoryLeakFix>
    </modules>
    <global>
        <models>
            <sitewards_importimagememoryleakfix>
                <class>Sitewards_ImportImageMemoryLeakFix_Model</class>
            </sitewards_importimagememoryleakfix>
        </models>
        <helpers>
            <catalog>
                <rewrite>
                    <image>Sitewards_ImportImageMemoryLeakFix_Helper_Catalog_Helper_Image</image>
                </rewrite>
            </catalog>
        </helpers>
    </global>
</config>

И новая функция вызывает новый разрушаемый класс изображений.

<?php
/**
 * @category    Sitewards
 * @package     Sitewards_ImportImageMemoryLeakFix
 * @copyright   Copyright (c) Sitewards GmbH (http://www.sitewards.com/)
 */
class Sitewards_ImportImageMemoryLeakFix_Helper_Catalog_Helper_Image extends Mage_Catalog_Helper_Image
{
    /**
     * Check - is this file an image
     *
     * Difference from original method - we destroy the image object here,
     * i.e. we are not wasting memory, without that fix product import with images
     * easily goes over 4Gb on memory with just couple hundreds of products.
     *
     * @param string $sFilePath
     *
     * @return bool
     * @throws Mage_Core_Exception
     */
    public function validateUploadFile($sFilePath) {
        if (!getimagesize($sFilePath)) {
            Mage::throwException($this->__('Disallowed file type.'));
        }

        /** @var Sitewards_ImportImageMemoryLeakFix_Model_Destructable_Image $oImageProcessor */
        $oImageProcessor = Mage::getModel('sitewards_importimagememoryleakfix/destructable_image', $sFilePath);
        $sMimeType       = $oImageProcessor->getMimeType();
        $oImageProcessor->destruct();

        return $sMimeType !== null;
    }
}
Дэвид Мэннерс
источник