Allowed memory size в PHP 7.1

Тема в разделе "PHP", создана пользователем Surprise, 15 мар 2018.

Статус темы:
Закрыта.
Модераторы: latteo
  1. Surprise

    Surprise

    Регистр.:
    13 мар 2012
    Сообщения:
    392
    Симпатии:
    199
    После перехода на новый хост, под PHP 7.1 с PHP 5.6 где все работало, столкнулся с рядом ошибок. Одна из них дикий жер памяти:
    PHP:
    Fatal errorAllowed memory size of 167772160 bytes exhausted (tried to allocate 9223372036854775840 bytesin /***/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, автор шаблона в котором возникла ошибка - продукт больше не поддерживает.
    В какую сторону копать?
    Спасибо за любые советы.
     
  2. Den1xxx

    Den1xxx

    Moderator
    Регистр.:
    15 янв 2014
    Сообщения:
    279
    Симпатии:
    152
    Сначала данные объявляются строкой, потом Это вдруг массив и в цикле?
    Может надо так:
    PHP:
    $f = array();
    $s = array();
    Вообще мне не нравится то, что сначала присваивают массив в цикле, потом проверяют функцией на пустоту. Имхо в переборе можно было бы что-то там сравнивать и прервать по условию. Но это лишь догадки. Я бы эту задачу пытался решить, выведя содержимое массива с помощью var_dump или var_export

    Вот здесь ещё непонятный код
    $f — выше был массивом, потом стал опять строкой? А потом — снова массивом, падла!
     
    Последнее редактирование: 16 мар 2018
    Meow12, pautina и Surprise нравится это.
  3. Chubazaa

    Chubazaa Писатель

    Регистр.:
    17 фев 2015
    Сообщения:
    6
    Симпатии:
    3
    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.
     
  4. Surprise

    Surprise

    Регистр.:
    13 мар 2012
    Сообщения:
    392
    Симпатии:
    199
    Thx, but the problem is solved by returning to php 5.6. Because software was not compatible with php 7.1
     
    Meow12 нравится это.
  5. Den1xxx

    Den1xxx

    Moderator
    Регистр.:
    15 янв 2014
    Сообщения:
    279
    Симпатии:
    152
    [​IMG]
    Там не так много несовместимостей обычно, а вот выгода от перехода существенна.

    ЗЫ. Очевидно, что что-то не так происходит при работе с foreach, работа которого изменена в PHP7 https://jeka.by/ask/216/php-7-foreach
    Можно эмулируя работу предыдущей версии работать по ссылкам, а не по ключам, либо заменить foreach на for
     
    Последнее редактирование: 25 мар 2018
    pautina нравится это.
  6. tonytone

    tonytone

    Регистр.:
    30 ноя 2014
    Сообщения:
    162
    Симпатии:
    457
    Хотя вы решили это.

    Для любого другого или для будущей ссылки просто введите cd в свое приложение или каталог веб-сайта и запустите команду php -ini, чтобы найти соответствующий файл php.ini, указанный для этого конкретного приложения или веб-сайта.
     
  7. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Быстро же ты сдался. 5.6 и 7 как небо и земля. Однозначно надо переходить. Если уверен, что именно тут у тебя память отъедает разбирай функцию на более мелкие детали и везде вставляй print memory_get_usage(true) и смотри где начинает расти.

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

    Сужай круг поиска и в итоге найдешь виновника.
     
    Surprise нравится это.
  8. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Могу подкинуть тебе свой отладчик. Я его где-то на хабре увидел, лень было разбираться со всякими 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($size1024));
            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(-=> 'ps', -=> 'ns', -=> 'mcs', -=> 'ms'=> 's');
            
    //логарифм времени в сек по основанию 1000
            //берем значение не больше 0, т.к. секунды у нас последняя изменяемая по тысяче величина, дальше по 60
            
    $i min(0floor(log($time1000)));

            
    //тут делим наше время на число соответствующее единицам измерения т.е. на миллион для секунд,
            //на тысячу для миллисекунд
            
    $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(=> '#f00'=> '#ff0'=> '#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 503);
                    
    $perc round($perc 1001);
                } 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 503);
                    
    $perc round($perc 1001);
                } 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();
        }

    }

     
    Surprise нравится это.
  9. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Это класс для псевдографических таблиц. Он используется журналом.
    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_lentrue);
            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(2floor($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) ? 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 && $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 === 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 === 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 === 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 $len2) : 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($ccount($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
     
  10. babahalki

    babahalki

    Регистр.:
    6 май 2016
    Сообщения:
    246
    Симпатии:
    98
    Мякотка в том, что класс не интегрируется с самим скриптом, а все строки dtimer::log(); можно потом быстро убрать регуляркой.
     
Статус темы:
Закрыта.