Allowed memory size в PHP 7.1

Статус
В этой теме нельзя размещать новые ответы.

Surprise

Мой дом здесь!
Регистрация
12 Мар 2012
Сообщения
430
Реакции
210
После перехода на новый хост, под PHP 7.1 с PHP 5.6 где все работало, столкнулся с рядом ошибок. Одна из них дикий жер памяти:
PHP:
Fatal error: Allowed memory size of 167772160 bytes exhausted (tried to allocate 9223372036854775840 bytes) in /***/rgen/system/rgen_assets.php on line 117
Да,да.
Если конкретно, содержимое файла:
PHP:
public function gfonts($val){
        if (factory::checkdata($val)) {
            $fonts = $val;
            $f = '';
            $s = '';
            foreach ($fonts as $key => $value) {
                if (isset($value['family']) && $value['family'] != '') {
                    $f[md5($value['family'])] = $value['family']; // 117 строка
                }
                if (isset($value['subset']) && $value['subset'] != '') {
                    $s[md5($value['subset'])] = $value['subset'];
                }
            }
            $f_url     = !factory::isEmpty($f) && $f != '' ? join($f, '|') : '';
            $s_url     = !factory::isEmpty($s) && $s != '' ? '&subset='.join($s, ',') : '';
            $gfont_url = $f_url != '' && $s_url != '' ? "@import url('//fonts.googleapis.com/css?family=".$f_url.$s_url."');" : '';
            return $gfont_url;
        } else {
            return false;
        }
    }
Увеличивать лимиты, собственно не поможет
CMS OpenCart 2.3, автор шаблона в котором возникла ошибка - продукт больше не поддерживает.
В какую сторону копать?
Спасибо за любые советы.
 
$s[md5($value['subset'])] = $value['subset'];
Сначала данные объявляются строкой, потом Это вдруг массив и в цикле?
Может надо так:
PHP:
$f = array();
$s = array();
Вообще мне не нравится то, что сначала присваивают массив в цикле, потом проверяют функцией на пустоту. Имхо в переборе можно было бы что-то там сравнивать и прервать по условию. Но это лишь догадки. Я бы эту задачу пытался решить, выведя содержимое массива с помощью var_dump или var_export

Вот здесь ещё непонятный код
&& $f != '' ? join($f, '|')
$f — выше был массивом, потом стал опять строкой? А потом — снова массивом, падла!
 
Последнее редактирование:
Hello,

have you tried to increase memory limit on PHP? Try to use "ini_set('memory_limit' '512M');" before running your script. Somethimes you can change this memory limit in your host panel.
 
Hello,

have you tried to increase memory limit on PHP? Try to use "ini_set('memory_limit' '512M');" before running your script. Somethimes you can change this memory limit in your host panel.
Thx, but the problem is solved by returning to php 5.6. Because software was not compatible with php 7.1
 
Thx, but the problem is solved by returning to php 5.6. Because software was not compatible with php 7.1
66b85dd47539828bf4ddc482a688f21b008d4a55_full.jpg

Там не так много несовместимостей обычно, а вот выгода от перехода существенна.

ЗЫ. Очевидно, что что-то не так происходит при работе с foreach, работа которого изменена в PHP7 Для просмотра ссылки Войди или Зарегистрируйся
Можно эмулируя работу предыдущей версии работать по ссылкам, а не по ключам, либо заменить foreach на for
 
Последнее редактирование:
Хотя вы решили это.

Для любого другого или для будущей ссылки просто введите cd в свое приложение или каталог веб-сайта и запустите команду php -ini, чтобы найти соответствующий файл php.ini, указанный для этого конкретного приложения или веб-сайта.
 
Быстро же ты сдался. 5.6 и 7 как небо и земля. Однозначно надо переходить. Если уверен, что именно тут у тебя память отъедает разбирай функцию на более мелкие детали и везде вставляй print memory_get_usage(true) и смотри где начинает расти.

Помню у меня был абсолютно простой механизм с циклом через foreach, там тоже память текла, поменял на for и все решилось. Если у тебя на этом месте срабатывает, не значит, что именно тут течет.

Сужай круг поиска и в итоге найдешь виновника.
 
Могу подкинуть тебе свой отладчик. Я его где-то на хабре увидел, лень было разбираться со всякими xdebug. Уже полтора года пользуюсь, усовершенствовал даже немного. Очень им доволен. Утечку памяти им можно отыскать без труда.

вот демо скрипт:
PHP:
<?php
//подключаем нашего летописца
require_once(dirname(__FILE__).'/api/Dtimer.php');


//эта функция для создания массивов
function create($keys) {
    for ($i = 1; $i <= $keys; $i++) {
        $arr['key-' . $i] = $i;
    }
    return $arr;
}

function demo(){
$i = 0;
$cnt = 10;
while($i < $cnt){
    $i++;
    //тут будем создавать большие массивы, чтобы увидеть, как на глазах растет потребление памяти
    $garbage[] = create(500000);
    dtimer::log('hello, baby!');
}
}

//этим методом мы пишем запись в журнал. Есть 2 возможных аргумента 1 (обязательный) - сообщение в журнал, 
//2 (необязательный) - тип сообщения. Тип (1, 2 или 3) влияет на цвет строчки записи при отображении журнала
dtimer::log(__FILE__ .' ' . __LINE__ .'first message default type is 3');
dtimer::log(__FILE__ .' ' . __LINE__ .'second message type is 2', 2);
demo();
dtimer::log(__FILE__ .' ' . __LINE__ .'third and last message type is 1', 1);

//Этим методом мы показываем наш журнал
print "Так я могу показывать таблицы для работы из браузера!\n";
dtimer::show();

print "<PRE>";
//в качестве аргумента можно задать ширину таблицы в символах
print "Так я могу показывать псевдографические таблицы для работы из консоли!\n";
dtimer::show_console(180);
print "</PRE>";

Это класс для самого журнала:
PHP:
<?php

if (!function_exists('convert')) {

//функция для конвертации величин измерения информации
    function convert($size)
    {
        if ($size == 0) {
            return 0;
        }
        $unit = array('b', 'kb', 'mb', 'gb', 'tb', 'pb');
        $i = floor(log($size, 1024));
        return @round($size / pow(1024, $i), 1) . $unit[$i];
    }
}

if (!function_exists('convert_time')) {
//функция для конвертации времени, принимает значения в секундах
    function convert_time($time)
    {
        if ($time == 0) {
            return 0;
        }
        //допустимые единицы измерения
        $unit = array(-4 => 'ps', -3 => 'ns', -2 => 'mcs', -1 => 'ms', 0 => 's');
        //логарифм времени в сек по основанию 1000
        //берем значение не больше 0, т.к. секунды у нас последняя изменяемая по тысяче величина, дальше по 60
        $i = min(0, floor(log($time, 1000)));

        //тут делим наше время на число соответствующее единицам измерения т.е. на миллион для секунд,
        //на тысячу для миллисекунд
        $t = @round($time / pow(1000, $i), 1);
        return $t . $unit[$i];
    }
}


class dtimer
{

    public static $enabled = true;
    protected static $startTime;
    protected static $points = array();
    private static $color_array = array(1 => '#f00', 2 => '#ff0', 3 => '#fff');

    public static function reset()
    {
        self::$points = null;
        self::$startTime = null;
    }

    public static function log($message = '', $type = null)
    {
        //останавливаемся, если отключено
        if (self::$enabled !== true) {
            return false;
        }

        //тут будем заводить тип сообщения, если он не задан
        // 3 - информация, 2 - предупреждение, 1 - ошибка
        //по умолчанию сообщение будет информационным
        if (!isset($type)) {
            $type = '3';
        }

        if (self::$startTime === null)
            self::run();

        self::$points[] = array('message' => $message,
            'type' => $type, 'ram' => convert(memory_get_usage(true)), 'time' => microtime(true) - self::$startTime);
    }

    public static function run()
    {
        self::$startTime = microtime(true);
    }

    public static function show()
    {
        if (self::$enabled !== true) {
            return false;
        }

        $oldtime = 0;

        echo '
            <table style="table-layout: fixed; overflow-wrap: break-word; width: calc(100% - 20px); margin: 10px; !important; box-sizing: border-box; right:0; top:0; z-index:200; background:#fff !important">
            <tr>
                <th style="width:2%; box-sizing: border-box; border: 1px dotted;">T.</th>
                <th style="width:75%; box-sizing: border-box; border: 1px dotted;">Message</th>
                <th style="width: 5.5%; box-sizing: border-box; border: 1px dotted;">RAM</th>
                <th style="width: 6.5%; box-sizing: border-box; border: 1px dotted;">Diff</th>
                <th style="width: 4.5%; box-sizing: border-box; border: 1px dotted;">Perc</th>
                <th style="width: 6.5%; box-sizing: border-box; border: 1px dotted;">Time</th>
            </tr>
        ';
        $last = end(self::$points);
        //reset(self::$points);

        foreach (self::$points as $item) {

            $type = $item['type'];
            $color_type = self::$color_array[$item['type']];
            $message = $item['message'];
            $ram = $item['ram'];
            //время из последней записи
            $total = $last['time'];
            //разница во времени
            $diff = $item['time'] - $oldtime;
            //время из записи пишем для сравнения на следующем цикле
            $oldtime = $item['time'];

            if ($total != 0) {
                $perc = $diff / $total;
                $color = round(99 - $perc * 50, 3);
                $perc = round($perc * 100, 1);
            } else {
                $color = 255;
                $perc = 0;
            }
            //тут сконвертируем все величины времени
            //время из записи
            $time = convert_time($item['time']);
            $diff = convert_time($diff);
            echo "
                <tr>
                    <td style='padding: 3px; box-sizing: border-box; border: 1px dotted;background: $color_type;'>{$type}</td>
                    <td style='padding: 3px; box-sizing: border-box; border: 1px dotted;background: hsl( 0, 100%, $color% );'>$message</td>
                    <td style='padding: 3px; box-sizing: border-box; border: 1px dotted;'>{$ram}</td>
                    <td style='padding: 3px; box-sizing: border-box; border: 1px dotted;'>{$diff}</td>
                    <td style='padding: 3px; box-sizing: border-box; border: 1px dotted;'>{$perc}</td>
                    <td style='padding: 3px; box-sizing: border-box; border: 1px dotted;'>{$time}</td>
                </tr>
            ";

        };
        echo "</table>\n";
        //self::$points = array();
    }

    public static function show_console($width = 100)
    {
        require_once(dirname(__FILE__) . '/Table2ascii.php');
        $table = new Table2ascii($width);
        if (self::$enabled !== true) {
            return false;
        }

        $oldtime = 0;


        $last = end(self::$points);
        //reset(self::$points);
        $res = array();
        foreach (self::$points as $item) {

            $type = $item['type'];
            $color_type = self::$color_array[$item['type']];
            $message = $item['message'];
            $ram = $item['ram'];
            //время из последней записи
            $total = $last['time'];
            //разница во времени
            $diff = $item['time'] - $oldtime;
            //время из записи пишем для сравнения на следующем цикле
            $oldtime = $item['time'];

            if ($total != 0) {
                $perc = $diff / $total;
                $color = round(99 - $perc * 50, 3);
                $perc = round($perc * 100, 1);
            } else {
                $color = 255;
                $perc = 0;
            }
            //тут сконвертируем все величины времени
            //время из записи
            $time = convert_time($item['time']);
            $diff = convert_time($diff);
//           echo "****    $type    ram:{$ram}    diff:{$diff}    perc:{$perc}   time:{$time}    ***********\n";
//           echo "$message\n";
//           echo "******************************************************\n";
            $res[] = array(
                'type' => $type,
                'ram' => $ram,
                'diff' => $diff,
                'perc' => $perc,
                'time' => $time,
                'message' => $message,
            );

        };
        //print_r($res);
        print $table->draw($res);
        self::$points = array();
    }

}
 
Это класс для псевдографических таблиц. Он используется журналом.
PHP:
<?php

if(!function_exists('mb_str_split')) {
    /**
     * @param $string
     * @param int $split_length
     * @return array
     */
    function mb_str_split($string, $split_length = 1)
    {
        preg_match_all('`.`u', $string, $arr);
        $arr = array_chunk($arr[0], $split_length);
        $arr = array_map('implode', $arr);
        return $arr;
    }
}

/**
* This library can generate ASCII pseudographic table from an array
* One public method draw()
*
* @author Legale <legale.legale@gmail.com>
* @email legale.legale@gmail.com
* @license GPL v3
*/
class Table2ascii
{
    const UPLEFT = '┌';
    const UPRIGHT = '┐';
    const DOWNLEFT = '└';
    const DOWNRIGHT = '┘';

    const SPACE = ' ';
    const CROSS = '┼';
    const UPCROSS = '┬';
    const DOWNCROSS = '┴';
    //const HORIZ = '─'; //another horiz. line symbol, better but not universal like dash -
    const HORIZ = '-';
    const VERT = '│';
    private static $MAX_WIDTH;

   public function __construct($MAX_WIDTH = 100){
      self::$MAX_WIDTH = $MAX_WIDTH;
   }


    /**
     * @param $table
     * @return string
     */
    public function draw($table)
    {
        if (!is_array($table) || !is_array(reset($table))) {
            return 'wrong table';
        }

        $headers = $this->columns_headers($table);
        $col_len = $this->columns_lengths($table, $headers);

        $res = array();
        $res[] = "\n";
        $res[] = $this->draw_separator($col_len, 'top');
        $res[] = $this->draw_row($headers, $col_len, true);
        foreach ($table as $row) {
            $res[] = $this->draw_separator($col_len);
            $res[] = $this->draw_row($row, $col_len);
        }
        $res[] = $this->draw_separator($col_len, 'bottom');
        return implode('', $res);

    }

    /**
     * @param $table
     * @return array
     */
    private function columns_headers($table)
    {
        $k = array_keys(reset($table));
        return array_combine($k, $k);
    }

    /**
     * @param $table
     * @param $headers
     * @return array
     */
    private function columns_lengths($table, $headers)
    {
        $headers = array_map('mb_strlen', $headers);
      $headers = array('len' => $headers,'size' => $headers);
    
        $cnt = count($headers['len']);
        $max_width = self::$MAX_WIDTH  - $cnt - 1;
        $width = 0;
        $total_size = 0;
        foreach ($table as $num => $row) {
            foreach ($row as $col => $cell) {
            $cell_len = mb_strlen($cell);
                $headers['len'][$col] = max($headers['len'][$col], $cell_len);
                $headers['size'][$col] += $cell_len;
                $total_size += $cell_len;
                $width += $headers['len'][$col];
            }
        }

        //average column length
        $col_len = max(2, floor($max_width / $cnt));

        //computed max. table width
        $max_width_comp = $col_len * $cnt;
        $max_width = ($max_width_comp > $max_width) ? $max_width_comp : $max_width;

        $chars_left = $max_width;
        $headers_orig = $headers;

        //setting initial lenght for each column
        foreach ($headers['len'] as $k=>&$len) {
         $size = $headers['size'][$k];
         $perc = $size / $total_size;
         $increment = ($perc < 0.01) ? 2 : 6;
         $len = $increment;
            $chars_left -= $increment;
        }  
        foreach ($headers['len'] as $k=>&$len) {
         $size = $headers['size'][$k];
         $perc = $size / $total_size;
         $increment = ceil($chars_left * $perc);
         $len += $increment;
            $chars_left -= $increment;
        }
        //print_r($headers);  
        unset($len);

        //free chars to spread
        $free_chars = $chars_left;
        //cycle counter
        $cycle = 1;
        //~ print_r($headers);

        //cycle while we have got free chars and cycle counter less than initial value of free chars
        while ($chars_left > 0 && $cycle < $free_chars) {
            foreach ($headers_orig['len'] as $k => $len) {
                if ($len > $headers['len'][$k]) {
                    $headers['len'][$k]++;
                    $chars_left--;
                }
            }
            $cycle++;
        }


        return $headers['len'];
    }

    /**
     * @param $col_len
     * @return string
     */
    private function draw_separator($col_len, $type = 'middle')
    {
        $str = '';
        $first = true;
        $i = 1;
        $cnt = count($col_len);

        switch ($type) {
            case 'top':
                foreach ($col_len as $len) {
                    $str .= $i === 1 ? self::UPLEFT : self::UPCROSS;
                    $str .= str_repeat(self::HORIZ, $len);
                    $str .= $i === $cnt ? self::UPRIGHT . "\n" : '';
                    $i++;
                }
                break;

            case 'middle':
                foreach ($col_len as $len) {
                    $str .= $i === 1 ? self::VERT : self::CROSS;
                    $str .= str_repeat(self::HORIZ, $len);
                    $str .= $i === $cnt ? self::VERT . "\n" : '';
                    $i++;
                }
                break;

            case 'bottom':
                foreach ($col_len as $len) {
                    $str .= $i === 1 ? self::DOWNLEFT : self::DOWNCROSS;
                    $str .= str_repeat(self::HORIZ, $len);
                    $str .= $i === $cnt ? self::DOWNRIGHT . "\n" : '';
                    $i++;
                }
                break;
        }

        return $str;
    }

    /**
     * @param $row
     * @param $col_len
     * @param bool $align
     * @return string
     */
    private function draw_row($row, $col_len, $align = false)
    {
        $str = '';
        $multirow = $this->make_row($row, $col_len);
        foreach ($multirow as $line => $cell_array) {
            $st = array();
            foreach ($cell_array as $col => $cell) {
                $max = $col_len[$col];
                $len = mb_strlen($cell);
                $start = $align ? intdiv($max - $len, 2) : 0;

                $end = $max - $start - $len;
                preg_match_all('`.`u', $cell, $arr);
                $str .= self::VERT;
                $str .= str_repeat(self::SPACE, $start);
                $str .= $cell;
                $str .= str_repeat(self::SPACE, $end);
            }
            $str .= self::VERT . "\n";
        }
        return $str;
    }

    /**
     * @param $row
     * @param $col_len
     * @return array
     */
    private function make_row($row, $col_len)
    {
        $res = array();
        foreach ($row as $col => $cell) {
            $max_len = $col_len[$col];
            $res[$col] = mb_str_split($cell, $max_len);
        }
        //max. lines per one row
        $max_lines = array_reduce($res, function ($c, $i) {
            return max($c, count($i));
        });

        foreach ($res as &$el) {
            $el = array_pad($el, $max_lines, '');
        }
        $lines = array_keys($el);
        $cols = array_keys($col_len);
        unset($el);

        $final = array();
        foreach ($cols as $col) {
            foreach ($lines as $line) {
                $final[$line][$col] = strtr(html_entity_decode($res[$col][$line]), array("\n" =>" ", "\r" => " ", "\t"=> " "));
            }
        }

        return $final;
    }

    /**
     * @param $cell
     * @param $max_len
     */
    public function make_cell($cell, $max_len)
    {
        return;
    }


}

?>

Вот как это будет выглядеть при запуске из браузера.
upload_2018-3-30_15-22-41.png
 
Мякотка в том, что класс не интегрируется с самим скриптом, а все строки dtimer::log(); можно потом быстро убрать регуляркой.
 
Статус
В этой теме нельзя размещать новые ответы.
Назад
Сверху