PHP脚本内存不足

时间:2014-08-26 16:06:44

标签: php memory-management symfony1 out-of-memory

我编写了以下PHP脚本(symphony 1.4 Task),它循环遍历CSV文件,并通过Doctrine将数据导入MySQL。

不幸的是,脚本被中止,因为它分配了很多内存。使用的内存随着每个foreach循环而增长。我尝试了很多东西,但是我无法使用过的内存更加稳定。

有人能给我一个提示吗?

<?php 
class importERPTask extends sfBaseTask
{
protected function configure()
{
    $this->addOptions(array(
        new sfCommandOption('application', null, sfCommandOption::PARAMETER_REQUIRED, 'The application name', 'frontend'),
        new sfCommandOption('env', null, sfCommandOption::PARAMETER_REQUIRED, 'The environment', 'dev'),
        new sfCommandOption('connection', null, sfCommandOption::PARAMETER_REQUIRED, 'The connection name', 'doctrine'),
        // add your own options here
    ));

    $this->namespace                = 'w2';
    $this->name                         = 'importERP';
    $this->briefDescription = 'Imports product data .';
}

public function execute($arguments = array(), $options = array())
{
    // initialize the database connection
    $databaseManager = new sfDatabaseManager($this->configuration);
    $connection = $databaseManager->getDatabase($options['connection'])->getConnection();

    $this->logSection('importERP', 'Start');
    if(!(file_exists(sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'VK.csv') && file_exists(sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'Artikel.csv')))
    {
        $this->logSection('importERP', 'No import CSV');
        $this->logSection('importERP', 'Done.');
        return;
    }

    $this->importPrices();

    //import products
    $this->logSection('importERP', 'Import products');

    Doctrine::getTable('CatalogueProduct')->setAllImportFalse();

    $file_handle = fopen(sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'Artikel.csv', 'r');
    if($file_handle)
    {
        $i = 0;
        while(!feof($file_handle))
        {
            //skip first line
            if(++$i == 1)
            {
                continue;
            }
            $this->importProduct($file_handle);
        }
        fclose($file_handle);
    }

    $this->deleteProducts();

    unlink(sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'VK.csv');
    unlink(sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'Artikel.csv');
    $this->logSection('importERP', 'Done.');
}

private function importPrices()
{
    //import price helper table
    $this->logSection('importERP', 'Import price helper table');

    Doctrine::getTable('ImportHelperPrice')->clearAllData();
    $file_handle = fopen(sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'VK.csv', 'r');
    if($file_handle)
    {
        $i = 0;
        while(!feof($file_handle))
        {
            $line_of_text = fgetcsv($file_handle, 0, ';', '"');

            //skip first line
            if(++$i == 1)
            {
                continue;
            }

            $price = new ImportHelperPrice();
            $price->setImportId($line_of_text[0]);
            $price->setStartAmount($line_of_text[1]);
            $price->setPrice(str_replace(',', '.', $line_of_text[2]));
            $price->save();
        }
    }
}

private function importProduct($file_handle)
{
    $line_of_text = fgetcsv($file_handle, 0, ';', '"');

    $this->logSection('importERP', 'Import product '.$line_of_text[1]);
    //no empty article number
    if($line_of_text[0] == '')
    {
        $this->logSection('importERP', '... skipped');
        return;
    }

    if($line_of_text[4] == '')
    {
        $this->logSection('importERP', '... has no category');
        return;
    }
    $my_product = Doctrine::getTable('CatalogueProduct')->findOneByCode($line_of_text[0]);
    $my_cat = Doctrine::getTable('CatalogueCategory')->findOneByImportCode($line_of_text[4]);
    if(!$my_cat)
    {
        $this->logSection('importERP', '... has no category');
        return;
    }
    if(!$my_product)
    {
        $this->logSection('importERP', '... is new');
        $my_product = new CatalogueProduct();
        $my_product->setCode($line_of_text[0]);

        // do not overwrite handmade configurations from backend
        $my_product->setVatId(1);
        $my_product->setTemplateId(4);
    }
    else
    {
        $this->logSection('importERP', '... is updated');
    }

    //get prices
    $price = Doctrine::getTable('ImportHelperPrice')->getPriceForImportId($line_of_text[0]);
    if(!$price)
    {
        return;
    }

    $my_product->setPriceGrossEur($price->getPrice());
    $my_product->Translation['de']->title = $line_of_text[2];
    $my_product->Translation['de']->shortdescription = $line_of_text[3];
    $my_product->Translation['de']->description =$line_of_text[3];
    $my_product->setCatalogueCategory($my_cat);
    $my_product->setHidden(false);
    $my_product->setImportFlag(true);

    $config_prices = Doctrine::getTable('ImportHelperPrice')->getPriceConfigForImportId($line_of_text[0]);
    if($config_prices)
    {
        $price_config = '';
        foreach($config_prices as $cp)
        {
            $discount = 100 - ($cp->getPrice() / ($price->getPrice() / 100));
            if($discount == 0)
            {
                continue;
            }
            if($price_config != '')
            {
                $price_config .= ';';
            }
            $price_config .= $cp->getStartAmount() . ':' . number_format($discount, 2, ',', '');
        }
        $my_product->setPriceConfig($price_config);
    }


    //move images
    $img_default = sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$line_of_text[1].'_m.jpg';
    if(file_exists($img_default))
    {
        rename($img_default, sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'products'.DIRECTORY_SEPARATOR.$line_of_text[1].'_m.jpg');
        $my_product->setImageDefault($line_of_text[1].'_m.jpg');
        $this->logSection('importERP', '... '.$my_product->getImageDefault().' saved');
    }
    else
    {
        $this->logSection('importERP', '... '.$img_default.' not found');
    }
    $img_zoom = sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$line_of_text[1].'_gr.jpg';
    if(file_exists($img_zoom))
    {
        rename($img_zoom, sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'products'.DIRECTORY_SEPARATOR.$line_of_text[1].'_zoom.jpg');
        $my_product->setImageZoom($line_of_text[1].'_zoom.jpg');
        $this->logSection('importERP', '... '.$my_product->getImageZoom().' saved');
    }
    else
    {
        $this->logSection('importERP', '... '.$img_zoom.' not found');
    }
    $img_icon = sfConfig::get('sf_root_dir'). DIRECTORY_SEPARATOR .'import_data'.DIRECTORY_SEPARATOR.'images'.DIRECTORY_SEPARATOR.$line_of_text[1].'_kl.jpg';
    if(file_exists($img_icon))
    {
        rename($img_icon, sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'products'.DIRECTORY_SEPARATOR.$line_of_text[1].'_icon.jpg');
        $my_product->setImageIcon($line_of_text[1].'_icon.jpg');
        $this->logSection('importERP', '... '.$my_product->getImageIcon().' saved');
    }
    else
    {
        $this->logSection('importERP', '... '.$img_icon.' not found');
    }

    $my_product->save();

    $this->logSection('importERP', 'Memory usage '.memory_get_peak_usage() / 1048576,2 .' MB');
}

private function deleteProducts()
{
    //delete not mentioned products
    $this->logSection('importERP', 'Delete not mentioned products');
    $del_products = Doctrine::getTable('CatalogueProduct')->getAllImportFalse();
    foreach($del_products as $dp)
    {
        $this->logSection('importERP', 'Delete '.$dp->getCode());

        //delete images
        $img_default = sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'products'.DIRECTORY_SEPARATOR.$dp->getImageDefault();
        $img_zoom = sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'products'.DIRECTORY_SEPARATOR.$dp->getImageZoom();
        $img_icon = sfConfig::get('sf_web_dir').DIRECTORY_SEPARATOR.'products'.DIRECTORY_SEPARATOR.$dp->getImageIcon();
        if($dp->getImageDefault() != NULL && $dp->getImageDefault() != '' && file_exists($img_default))
        {
            unlink($img_default);
        }
        if($dp->getImageZoom() != NULL && $dp->getImageZoom() != '' && file_exists($img_zoom))
        {
            unlink($img_zoom);
        }
        if($dp->getImageIcon() != NULL && $dp->getImageIcon() != '' && file_exists($img_icon))
        {
            unlink($img_icon);
        }

        //delete product
        $dp->delete();
    }
}
}

4 个答案:

答案 0 :(得分:0)

最好的解决方案是编辑php.ini文件。分配:

memory_limit = 16M

不确定16M是否为默认值但应该关闭。无论如何,这是允许PHP脚本使用的内存量。您可以将此数字设置得更高并解决问题。

或者,您可以在PHP脚本的顶部写下这个:

ini_set('memory_limit', '16M');

其中16M可以更改为您希望脚本允许的任何内存量。

答案 1 :(得分:0)

注意ini_sets,其值以“M”结尾 - 它们无法按预期工作。速记符号仅适用于php.ini,相应的信息包含在文档中:http://php.net/manual/en/faq.using.php#faq.using.shorthandbytes

答案 2 :(得分:0)

你应该这样做:

...
$my_product->save();
$my_product->free();
unset($my_product);
...

答案 3 :(得分:0)

假设您的CSV太大,服务器无法一次性处理内存,我会考虑将CSV拆分为较小的文件,这对于Unix命令(拆分等)来说非常简单。

然后,您将使用PHP脚本异步执行每个bitesize CSV。