Problema de ImportExport con el nuevo destructor de Varien_Image_Adapter_Gd2 en 1.9.2.0

23

¿Alguien puede explicar para qué se utiliza el siguiente Código introducido entre Magento CE 1.9.1.0 y 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);
}

Después de agregar esas dos funciones, nuestra importación de imágenes de la galería de productos con la interfaz ImportExport dejó de funcionar. El error se debe a un límite de memoria (que resulta ser el límite máximo de tamaño de archivo abierto).

Mi idea es que los archivos abiertos por la importación no se cerrarán correctamente.

También vi que se destruct()introdujeron algunas funciones vacías ( Mage_ImportExport_Model_Import_Adapter_Abstract), pero extenderlas para que coincidan con la lógica principal no ayuda.

Achim Rosenhagen
fuente

Respuestas:

14

Parece que intentaron asegurarse de destruir el recurso de imagen, pero en su lugar introdujeron una pérdida de memoria. No puedo pensar en una razón válida para este código, para ser honesto, pero puedo explicar lo que ha cambiado:

Originalmente, imagedestroy()habría sido llamado en el desctructor__destruct()

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

Se llama al destructor cada vez que el recolector de basura PHP destruye los objetos no utilizados (es decir, objetos en la memoria que ya no están referenciados).

Ahora, en imagedestroy()su lugar , se llama en una función de apagado y, dado que se trata de una devolución de llamada a un método del Varien_Image_Adapter_Gd2objeto, ni siquiera se puede recolectar basura hasta el final. De esta forma, todos los recursos de imagen permanecen abiertos hasta que finaliza la ejecución del script.

Fabian Schmengler
fuente
Gracias por la explicación, esto es lo que pensé. En general, este código introducido hace que la mayoría de las importaciones sean inútiles en 1.9.2. en mis ojos. Espero que esto se arregle pronto. ¿Algún consejo sobre dónde abrir un informe de error?
Achim Rosenhagen
6

Tener los mismos problemas con mi Magento 1.9.2.0 ...

Solo hago que esto funcione cambiando Varien_Image_Adapter_Gd2 de la /lib/Varien/Image/Adapter/Gd2.phpsiguiente manera:

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

/**
 * Destroy object image on shutdown
 */
public function __destruct()
{
    @imagedestroy($this->_imageHandler);
}
  • eliminar línea con register_shutdown_function (o comentar)
  • cambiar el nombre de la función destruir a __destruct

He configurado memory_limit de nuevo a 1G (anteriormente subí hasta 32GB) y ahora funciona ...

Este proyecto implementa dicho procedimiento de manera amigable con el modman. Simplemente instálalo con el compositor y listo.

dkr
fuente
Esto realmente no responde la pregunta. Si tiene una pregunta diferente, puede hacerla haciendo clic en Hacer pregunta . Tú también puedes agregar una recompensa para llamar más la atención sobre esta pregunta una vez que tenga suficiente reputación .
Rajeev K Tomy
Sí, esto no responde la pregunta, pero ayudará a las personas que necesitan una solución temporal y sin discusión
dkr
Se solucionó un problema con el consumo de memoria durante la importación. Interesante, ¿Magento prueba de alguna manera lo que están lanzando?
klipach
Esto resuelve no solo el problema de importación. Esto resuelve una gran memoria comiendo el proceso que crea / recrea el caché y las versiones redimensionadas para cada imagen de productos. Si subo imágenes png en mis productos, sin este "hack" no puedo trabajar y obtengo muchos errores de memoria agotada.
Simbus82
Hoy encontré esta sugerencia. Lo implementé y la pérdida de memoria desapareció. Entonces creé este github.com/borasocom-team/magento-gd2-memoryleak para instalarlo de una manera limpia.
Dr. Gianluigi Zane Zanettini
5

Fue parte de la solución de problemas de seguridad con unserialize. Los métodos mágicos como __destruct tienen problemas inherentes con la serialización.

Hemos visto exploits propuestos que usaban serialización y __destruct para crear archivos en el sistema de archivos, y este cambio (verá más cambios similares en otros lugares) se realizó para evitar esto.

¿Causa pérdida de memoria o simplemente usa más memoria hasta que finaliza el script?

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

Piotr Kaminski
fuente
Gracias por el contexto. ¿Se ha realizado este cambio en particular para evitar un exploit específico o simplemente para estar seguro?
Fabian Schmengler
Y no, probablemente solo haga que el script consuma más memoria, no una pérdida de memoria real
Fabian Schmengler
Causa una gran pérdida de memoria, especialmente al importar imágenes, ya que mantendrá todos los archivos de imagen abiertos hasta el final del proceso de importación. De esta manera solo podemos importar aproximadamente 50 productos (antes de que podamos hacer una importación de> 2k aparentemente). Ejecuté mi prueba en una máquina virtual local con 8G de RAM y los archivos de origen son aproximadamente 300 KB. Antes del cambio, la memoria utilizada por PHP se arma a 1k durante toda la importación.
Achim Rosenhagen
fschmengler tiene razón: puede que no sea una "pérdida de memoria", pero el consumo aumenta en la colina ;-)
Achim Rosenhagen
1
@ Alex gracias por el consejo. Invierto parcheado. Ahora, la pérdida de memoria se ha ido, pero no hay solución para el futuro.
Arne
4

Así que planteé un error con Magento, incluida una "solución" que debería tratar los problemas de uso de memoria en el proceso de importación de imágenes.

La solución se puede encontrar en github en https://github.com/sitewards/import_image_memory_leak_fix pero la idea básica es.

Arreglando el Mage_Catalog_Helper_Image::validateUploadFilepara llamar realmente al destructmétodo en el procesador de imágenes. Lamentablemente, parece que el valor predeterminado Varien_Imageno trata con undestruct por lo que hemos tenido que agregar nuestra propia clase que sí lo hace.

<?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.');
        }
    }
}

Y luego una reescritura del ayudante.

<?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>

Y la nueva función llama a la nueva clase de imagen destruible.

<?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;
    }
}
David modales
fuente