Search code examples
phpmysqlmultidimensional-arrayout-of-memorynested-loops

Memory exhausted using an array in a nested loop


This is the error I'm getting.

Fatal Error: Allowed memory size exhausted.

I'm fetching arrays containing a date from and a date till. I'm trying to get all the dates in between and add them to a new array. Apparently nested loops and multi-arrays are exhausting.

I need a less exhausting way of getting all the dates.

This is my code:

$query = "SELECT *
          FROM reservate
          ORDER BY from";
$result = $connect->query($query);

$reservations = array();
if ($result->num_rows > 0) {
    while($row = $result->fetch_assoc()) {
        $reservations[] = $row;
    }
}

$connect->close();

//AVAILABILITY CHECK
$nonAvailable = array();
foreach($reservations as $reservation){
    $from = $reservation['from'];
    $till = $reservation['till'];

    while($from <= $till){
        $nonAvailable[] = $from;
        $from = date('Y-m-d', strtotime("+1 days"));
    }
}

Solution

  • It looks like you made an infinite loop1.

    // if $till is in the future, this is an infinite loop
    while($from <= $till){
        // appending the same value of $from on each iteration
        $nonAvailable[] = $from;
        // as $from never changes value
        $from = date('Y-m-d', strtotime("+1 days"));
    }
    

    Add 1 day to the current value of $from

    while ($from <= $till){
         $nonAvailable[] = $from;
         // add 1 day to $from
         $from = date('Y-m-d', strtotime($from, "+1 days"));
    }
    

    There are still a few improvements that can be made:

    • In the following example the entire table is not copied to the reservation array and we are only retrieving the columns we need from the table.
    • Comparing integers instead of strings.
    • Adding 1 day to $from manually rather then relying on strtotime to do it.
    $query = "SELECT from, till
              FROM reservate
              ORDER BY from";
    $result = $connect->query($query);
    
    $nonAvailable = array();
    
    if ($result->num_rows > 0) {
        while ($row = $result->fetch_assoc()) {
            $from = strtotime($row['from']);
            $till = strtotime($row['till']);
    
            while ($from <= $till) {
                $nonAvailable[] = date('Y-m-d', $from);
                $from += 60*60*24;
            }
        }
    }
    
    $connect->close();
    

    It is likely that this algorithm can be made much more efficient. For example do you need an exhaustive list of each day something is reserved? If not then just the start date and end date should suffice.


    1 Given enough time and memory the loop would exit as strtotime("+1 days") would eventually return a value greater than $till.