Создание HTML-файлов из огромных табличных данных - PullRequest
0 голосов
/ 23 августа 2011

Моя конечная цель - создать счета в формате PDF для проекта биллинга, над которым я сейчас работаю - создание PDF - это то, что я уже сделал - сначала я создаю файл HTML для каждого счета, а затем пакетную обработку HTML-файлов в PDF в отдельном процессе. .

Тогда проблема в том, что данные, которые я пытаюсь получить в файл HTML, очень велики ... они хранятся в MySQL (я использую Symfony 1.4 и Doctrine). В настоящее время php.ini настроен на 500М памяти для php. Мне нужно иметь все invoiceLines для одного счета в одном файле HTML / PDF.

В настоящее время я зацикливаю таблицу счетов-фактур (1 строка на счет-фактуру), а затем получаю фактические строки счета-фактуры из другой таблицы (много строк на счет-фактуру) - проблема, с которой я сталкиваюсь, связана с огромным использованием памяти - может кто-нибудь предложить альтернативный способ сделать это.

foreach ($invoices as $inv)
{
    $outfile = '/ivoice' . $inv[0] . '.html';

    $tempinvfile = fopen($outfile, 'w'); //open file and replace current it exists

    echo '1 - Used memory : ' . number_format(memory_get_usage() - $mem) . PHP_EOL;

    $invoiceLines = Doctrine::getTable('InvoiceLine')
            ->createQuery()
            ->where('invoiceid = ?', $inv[0])
            ->setHydrationMode(Doctrine::HYDRATE_NONE)
            ->execute();

    echo "Count = " . count($invoiceLines) . PHP_EOL;

    echo '2 - Used memory : ' . number_format(memory_get_usage() - $mem) . PHP_EOL;

    $bodyhtml = '';
    foreach ($invoiceLines as $invline)
    {
//        $bodyhtml .= '<tr>';
//        $bodyhtml .= '    <td><b>' . $invline[2] . '</b></td>';
//        $bodyhtml .= '    <td>&pound;' . $invline[4] . '</td>';
//        $bodyhtml .= '    <td>&pound;' . $invline[5]. '</td>';
//        $bodyhtml .= '    <td>&pound;' . $invline[8] . '</td>';
//        $bodyhtml .= '    <td>&pound;' . $invline[9] . '</td>';
//        $bodyhtml .= '    <td>&pound;' . $invline[10] . '</td>';
//        $bodyhtml .= '    <td>&pound;' . $invline[12] . '</td>';
//        $bodyhtml .= '</tr>';
    }
    echo '3 - Used memory : ' . number_format(memory_get_usage() - $mem) . PHP_EOL;
//          fwrite($tempinvfile, $bodyhtml);
//          fclose($tempinvfile);
}

echo 'Used memory : ' . number_format(memory_get_usage() - $mem) . PHP_EOL;

echo "Done processing, time = " . (time() - $start) . PHP_EOL;

Выходные данные выглядят следующим образом:

Starting Processing
1 - Used memory : 615,736
Count = 39
2 - Used memory : 1,033,264
3 - Used memory : 1,033,344
after end loop - Used memory : 1,033,344
1 - Used memory : 1,055,448
Count = 11
2 - Used memory : 1,118,200
3 - Used memory : 1,118,200
after end loop - Used memory : 1,118,200
1 - Used memory : 1,140,304
Count = 30340
2 - Used memory : 89,061,552
3 - Used memory : 88,977,472
after end loop - Used memory : 88,977,472
1 - Used memory : 88,999,576
Count = 156
2 - Used memory : 89,482,752
3 - Used memory : 89,482,752
after end loop - Used memory : 89,482,752
1 - Used memory : 89,505,368
Count = 3867
2 - Used memory : 100,737,248
3 - Used memory : 100,737,248
after end loop - Used memory : 100,737,248
Used memory : 100,737,248
Done processing, time = 0

Итак, я ясно вижу, что основное использование памяти происходит, когда строки invoiceLines извлекаются из таблицы. Но я не знаю, как лучше подойти к этому ..... Как я могу восстановить память в конце каждого цикла?

Я пытался использовать DoctrinePager и зацикливать по одной странице за раз - работал, но использовал больше памяти, чем описанный выше подход.

Предложения очень приветствуются .....

РЕДАКТИРОВАТЬ:

Вывод памяти со всеми закомментированными файловыми операциями и добавлением unset($invoiceLines) сразу после эха 3:

Starting Processing
1 - Used memory : 615,712
Count = 39
2 - Used memory : 1,033,248
3 - Used memory : 1,033,328
after end loop - Used memory : 1,033,328
1 - Used memory : 1,055,432
Count = 11
2 - Used memory : 1,118,176
3 - Used memory : 1,118,176
after end loop - Used memory : 1,118,176
1 - Used memory : 1,140,280
Count = 30340
2 - Used memory : 89,061,760
3 - Used memory : 88,977,680
after end loop - Used memory : 88,977,680
1 - Used memory : 88,999,784
Count = 156
2 - Used memory : 89,482,968
3 - Used memory : 89,482,968
after end loop - Used memory : 89,482,968
1 - Used memory : 89,505,584
Count = 3867
2 - Used memory : 100,737,464
3 - Used memory : 100,737,464
after end loop - Used memory : 100,737,464
Used memory : 100,737,464
Done processing, time = 0

РЕДАКТИРОВАТЬ 2: Предложение @Crack -> добавлен профилировщик: false для файла database.yml .. вот результаты памяти:

Starting Processing
1 - Used memory : 600,392
Count = 39
2 - Used memory : 1,006,768
3 - Used memory : 1,006,848
after end loop - Used memory : 896,352
1 - Used memory : 903,192
Count = 11
2 - Used memory : 954,624
3 - Used memory : 951,824
after end loop - Used memory : 922,576
1 - Used memory : 929,416
Count = 30340
2 - Used memory : 88,840,104
3 - Used memory : 88,751,672
after end loop - Used memory : 863,168
1 - Used memory : 870,008
Count = 156
2 - Used memory : 1,342,816
3 - Used memory : 1,340,016
after end loop - Used memory : 889,392
1 - Used memory : 896,232
Count = 3867
2 - Used memory : 12,117,080
3 - Used memory : 12,114,280
after end loop - Used memory : 915,616
Used memory : 915,616
Done processing, time = 0

1 Ответ

0 голосов
/ 23 августа 2011

Используйте fwrite() после прочтения 1000 строк, затем сбросьте $ bodyhtml ($bodyhtml = '';). Таким образом, вы не будете хранить большую строку в памяти.

EDIT

Упс, Doctrine должна хранить все ранее прочитанные строки в некотором кэше в $invoiceLines ... Попробуйте поискать решения для php / symfony / doctrine утечки памяти? .

...