phpfatal-errormemory-limit

Allowed Memory Size Fatal Error; Improve Script


I have the following function which returns a formatted table to me showing me the list of browsers that viewed the website. It is retrieved from the MySQL Database and the User Agent (I know not totally reliable but it's good enough for this at this point in time).

However; this function (and another that does operating system from the same user agent maxes our the Allowed Memory Size and throws the PHP Fatal Error. I increased my Memory Limit to 100 MB and then checked the memory_get_peak_usage() and the two are running around 56 MB.

Is there anything I can do to this function (and the os one not included but very similar to this one) to improve the memory usage?

function getBrowsers() {
    global $stats,$db;
    $overall = $db->select("SELECT * FROM `".$db->prefix."web_stats`");
    /*while($row = $db->get_row($overall)) {
        $table[] = $row["agent"];
    }
    echo "<pre>";
    print_r($table);
    echo "</pre>";*/
    $overall = $db->num_rows($overall);

    $browser_array = array(
                        'msie'      => 'Internet Explorer',
                        'firefox'   => 'Firefox',
                        'safari'    => 'Safari',
                        'chrome'    => 'Chrome',
                        'edge'      => 'Edge',
                        'opera'     => 'Opera',
                        'netscape'  => 'Netscape',
                        'maxthon'   => 'Maxthon',
                        'konqueror' => 'Konqueror',
                        'mobile'    => 'Handheld Browser',
                        'spider'    => 'Search Spider',
                        'AdsBot'    => 'Google AdsBot',
                        'bot'       => 'Bot'
                 );

    

    $data["total"] = 0;
    foreach($browser_array as $value => $key) {
        $count = $this->get_os($value);
        $data[$key] = $count;
        $data["total"] += $count;
    }
    
    //$data["Unkown"] = $overall - $data["total"];
    
    arsort($data, SORT_NUMERIC);
    $table = "
        <table  class='table table-no-more table-bordered table-striped mb-none'>
            <thead>
                <tr>
                    <th>OS</th>
                    <th>Hits</th>
                    <th>Percent</th>
                </tr>
            </thead>
            <tbody>";
    foreach($data as $key => $value) {
        if($key!="total" && $value > 0) {
            $percent = $this->calculateper($value, $overall);
            if(preg_match('/(Chrome)/i', $key) === 1) {
                $icon = '<i class="fab fa-chrome"></i>';                    
            }
            elseif(preg_match('/(FireFox)/i', $key) === 1) {
                $icon = '<i class="fab fa-firefox"></i>';                   
            }
            elseif(preg_match('/(Edge)/i', $key) === 1) {
                $icon = '<i class="fab fa-edge"></i>';                  
            }
            elseif(preg_match('/(Google)/i', $key) === 1) {
                $icon = '<i class="fab fa-google"></i>';                    
            }
            elseif(preg_match('/(Google)/i', $key) === 1) {
                $icon = '<i class="fab fa-google"></i>';                    
            }
            elseif(preg_match('/(bot)/i', $key) === 1) {
                $icon = '<i class="fas fa-robot"></i>';                 
            }
            
            elseif(preg_match('/(Internet Explorer)/i', $key) === 1) {
                $icon = '<i class="fab fa-internet-explorer"></i>';                 
            }
            
            elseif(preg_match('/(Spider)/i', $key) === 1) {
                $icon = '<i class="fas fa-spider"></i>';                    
            }
            
            elseif(preg_match('/(Opera)/i', $key) === 1) {
                $icon = '<i class="fab fa-opera"></i>';                 
            }
            elseif(preg_match('/(Safari)/i', $key) === 1) {
                $icon = '<i class="fab fa-safari"></i>';                    
            }
            else {
                $icon = '<i class="far fa-question-circle"></i>';
            }
            $table .= "<tr>
                <td data-title='OS'>
                    ".$icon." ".$key."
                </td>
                <td data-title='Hits'>
                ".$value."
                </td>
                <td data-title='Percent'>
                    <div class='progress dark m-md'>
                        <div class='progress-bar progress-bar-success' role='progressbar' aria-valuenow='".$percent."' aria-valuemin='0' aria-valuemax='100' style='width: ".$percent."%;'>
                            ".$percent."%
                        </div>
                    </div>
                </td>
                </tr>";
        }
    }
    $table .= '</tbody>
            </table>';

    return $table;
}
//Get counts from the user agent
function get_os($opers) {
    global $stats,$db;
    $sql = "SELECT count(agent) as cnt FROM `".$db->prefix."web_stats` WHERE `agent` LIKE '%$opers%'"; 
    $result = $db->select($sql); 
     while ($row = $db->get_row($result)) { 
        $os1 = $row["cnt"]; 
        return $os1;
     } 
} 

Solution

  • At first glance, it seems that the most memory is used in the first query you do, as you load all data in that table just to count the results later on.

    So, the first improvement would be to let the database do the count, as you already did for the OSes:

    $stmt = $db->select("SELECT COUNT(*) AS total FROM `".$db->prefix."web_stats`");
    $result = $db->get_row($stmt);
    $overall = $result['total'];
    

    The second thing you should consider would be to format a bit the code since it looks a bit messy to me.