【PHP】PHPExcelがループ内でメモリを使いすぎる

PHPExcelを使って、ループしながら帳票をシートに書き出しているときにあまりにメモリを使うのでいろいろ調査したメモ。

処理の流れとしては

  1. 元になるエクセルのテンプレートファイルを読み込み
  2. 最初のシートを非表示にしておく
  3. 最初のシートをコピーして帳票を描画
  4. 2ページ以降も最初のシートをコピーして追加
  5. ファイルとして書き出し
  6. ここまでを必要な分だけループして、出来たエクセルをzip形式で圧縮してダウンロード。

「シートをコピーして追加する」の処理がメモリを大量に消費する。コピーするごとにメモリ消費量が倍になっていくみたい。

なので、PHP-Excelで「シートをコピーして追加する」はやらない方がいい追加したいタイミングでテンプレートファイルを読み込んで追加すると余計なメモリを食わない。

require_once(PW_VENDORS_DIR ."/PHPExcel/Classes/PHPExcel/IOFactory.php"); //PHPExcel 読み込み
$objReader   = PHPExcel_IOFactory::createReader('Excel5');
$objExcel = $objReader->load($sTemplateFile);
$objSheet = $objExcel->getSheet(0)->copy();
$objExcel->disconnectWorksheets();
unset($objExcel);

$objSheet->setTitle('page' . ($this->excel->getSheetCount() + 1));
$this->excel->addExternalSheet($objSheet);

さらに注意点としては、1.7.3cはaddExternalSheetにバグがあるので、最新のソースをダウンロードした方がいい。ちなみにaddSheetを使う場合はrebindParentしなければならない。

動作確認したのはビルドNo.53908

あと、saveで書き出したあとは次のようなメソッドを実行すれば、ある程度メモリを開放してくれる。ドキュメントに書いてあるけどね。

/**
 * メモリの開放
 */
public function destroy()
{
    $this->excel->disconnectWorksheets();
    unset($this->excel);
}

メモリがどれくらい使っているかは次のスクリプトを埋め込んで調査すると分かる。

echo date('H:i:s') . " Peak memory usage: " . (memory_get_peak_usage(true) / 1024 / 1024) . " MB<br/>";

PHPExcelで困ったら本家の掲示板が一番詳しい(英語)

<関連記事>

PHPからエクセルを操作するPHP-Excelを使ってみた