PHP Script always exceeds memory limit when adding to an array?

6.6k views Asked by At

I've been going mental over this one. I'm sure it never used to do this, however I must've changed something so it did.

This is the php script in question:

<?php

echo <<<START
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Spotify Community Staff Tracker</title>
<link rel="stylesheet" type="text/css" href="styles.css" />
<meta http-equiv="Content-Type" content="text/xhtml; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type='text/javascript' src='scripts/respond.min.js'></script>
</head>
<body>
START;

error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING);

require_once('config.php');

$posts = array();

$db = new mysqli($config['host'], $config['user'], $config['password'], $config['database']);

if ( ($result = $db->query('SELECT * FROM `posts`')) != false)
{
    while ( ($obj = mysqli_fetch_object($result)) !== false)
    {
        if (@$_GET['idfilter'] && @$_GET['idfilter'] != $obj->board)
        {
            continue;
        }

        $posts[] = array('datetime' => $obj->datetime, 'subject' => $obj->subject, 'post_url' => $obj->post_link, 'user_url' => $obj->author_link, 'user' => $obj->author_name);
    }

    if (sizeof($posts) == 0)
    {
        if ($_GET['idfilter'])
            die("Filter caused zero result, or cron hasn't run.");
        die("Cron hasn't been run.");
    }

}
else
{
    die("An error occured.");
}

$lupdate = mysqli_fetch_object($db->query("SELECT * FROM `last_update`"));

echo <<<BOTTOM
<div id="right" class="fixed">
    <p id="lastupdate">Last Updated: {$lupdate->timestamp}</p>
    <p><form id="filter" action="" method="get">
            <input type="text" placeholder="Enter a forum id to filter..." name="idfilter" />
            <input type="submit" value="Filter" id="submit" />
    </form>
            </p>
</div>
BOTTOM;

echo("\n<div id=\"posts\">");

foreach (array_reverse($posts) as $post)
{
    echo("\n<p><a class=\"postlink\" target=\"_blank\" href=\"{$post['post_url']}\">{$post['subject']}</a> - by <a class=\"suser\" target=\"_blank\" href=\"{$post['user_url']}\"><img src=\"http://spotify.i.lithium.com/html/rank_icons/spotify_icon_new.png\" alt=\"Spotify Staff\" />{$post['user']}</a> <span class=\"datetime\">on {$post['datetime']}</span>\n</p>");
}

echo("\n</div>");

echo <<<END
\n</body>
</html>
END;

?>

Whenever I run it I get the following error:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 44 bytes) in <filepath>\index.php on line 36

It's always the same error, - same allocation, same line. I've tried moving things around, I've tried using an array instead of an object, no use.

Any ideas as to why such a large amount of memory is trying to be used?

(There are around 400 rows in the database)

3

There are 3 answers

1
andrewsi On BEST ANSWER

It's this line that's causing you the problem:

while ( ($obj = mysqli_fetch_object($result)) !== false)

If you look at the documentation: http://www.php.net/manual/en/mysqli-result.fetch-object.php

mysqli_fetch_object returns the next row if there is one; or null if there isn't. So because you're doing a strict comparison, your loop will never end. To fix this, you can simple do:

while ($obj = mysqli_fetch_object($result))

And let PHP's type juggling convert the null at the end of the recordset to a boolean false.

4
Bryan Elliott On

Try Increasing your memory-limit in php.ini or in this script use:

ini_set('memory_limit','256M');
0
Ignacio Ocampo On

You are duplicating work to PHP.

After your query, you are doing while iterations through your results, and storing rows in a PHP array. And, at the end of your script, you are iterating your array and put this into HTML output.

I think, that you cand do all with a single iteration, replacing your foreach posts by:

while ( ($obj = mysqli_fetch_object($result)) !== false)
{
    if (@$_GET['idfilter'] && @$_GET['idfilter'] != $obj->board)
    {
        continue;
    }

    echo("\n<p><a class=\"postlink\" target=\"_blank\" href=\"{$post['post_url']}\">{$post['subject']}</a> - by <a class=\"suser\" target=\"_blank\" href=\"{$post['user_url']}\"><img src=\"http://spotify.i.lithium.com/html/rank_icons/spotify_icon_new.png\" alt=\"Spotify Staff\" />{$post['user']}</a> <span class=\"datetime\">on {$post['datetime']}</span>\n</p>");
}