简体   繁体   English

PHP脚本内存不足

[英]PHP script is running out of memory

I wrote the following PHP script (symphony 1.4 Task), which loops over a CSV file an d imports the data via Doctrine into MySQL. 我写了下面的PHP脚本(语法1.4任务),该脚本遍历CSV文件,然后通过Doctrine将数据导入MySQL。

Unfortunately the script is aborted because it allocates to much memory. 不幸的是,脚本被中止,因为它分配了很多内存。 The used memory grows with each foreach cycle. 使用的内存随着每个foreach周期的增长而增加。 I tried a lot of things, but i am not able to keep the used memory more stable. 我尝试了很多事情,但是无法使使用的内存更稳定。

Could anyone give me a hint? 有人可以给我一个提示吗?

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

The best solution would be to edit your php.ini file. 最好的解决方案是编辑您的php.ini文件。 Allocate: 分配:

memory_limit = 16M

Not sure if 16M is default but it should be close. 不确定16M是否为默认值,但应该将其关闭。 Anyway, that's the amount of memory a PHP script is allowed to use. 无论如何,这就是允许PHP脚本使用的内存量。 You can just set this number higher and solve the problem. 您可以将此数字设置得更高一些并解决问题。

Alternatively you can write this on the top of your PHP script: 或者,您可以在PHP脚本的顶部编写此代码:

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

Where 16M can be changed to whatever amount of memory you wish the script to allow. 可以将16M更改为您希望脚本允许的任何内存量。

Watch out for ini_sets with values ending with 'M' - they don't work as expected. 注意ini_sets的值以'M'结尾-它们不能按预期工作。 Shorthand notation works only in php.ini, appropriate info is included in the docs: http://php.net/manual/en/faq.using.php#faq.using.shorthandbytes 速记符号仅在php.ini中有效,适当的信息包含在文档中: http : //php.net/manual/zh/faq.using.php#faq.using.shorthandbytes

You should do: 你应该做:

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

Assuming your CSV is just too big for your server to handle in memory in one swoop, I would consider splitting the CSV into smaller files, which is quite a simple task with Unix commands (split, etc). 假设您的CSV太大了,服务器无法一sw而就地处理内存,我会考虑将CSV拆分成较小的文件,这对于Unix命令(拆分等)来说是非常简单的任务。

You would then execute each bitesize CSV asynchronously with your PHP script. 然后,您将使用PHP脚本异步执行每个bitesize CSV。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM