make is.dev make it simple. development.
2024年4月18日

PhpSpreadsheet tips #1

PhpSpreadsheetを使ってEXCELファイルを操作してみる。その初手。

・ 新たにEXCELファイルを作る
・ 既存のファイルを土台にEXCELファイルを作る(ファイルを開く+保存)
・ 事後のメモリ解放について

使用環境

・ PHP 8.2.9
・ phpoffice/phpspreadsheet version 2.0.0 (Composerでインストール済み)

新たにEXCELファイルを作る

新しいスプレッドシートを作って、EXCEL形式で保存するサンプル。

<?php

require '../vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;


// スプレッドシートを新たに作る
$spreadsheet = new Spreadsheet();


// ここで設定や値を書き込む


// xlsx形式で保存する
$writer = new XlsxWriter( $spreadsheet );
$writer->save( './output.xlsx' );


// スプレッドシートを閉じてメモリを解放
$spreadsheet->disconnectWorksheets();
unset( $spreadsheet );
PHP

なお、個人的には予め用意したxlsxファイルを書き込んで別名保存することをお勧めする。
(やり方の詳細は後述)

理由は、手元でEXCELで作った新規ファイルと初期設定が色々と違うため。
例えばフォントなど。

必要な設定を1つずつ入れていっても良いが、予め設定が入っているxlsxファイルを土台にして作成した方が最低限必要なものだけ書き込めば良いので簡単。

既存ファイルに加筆して別名保存

既にあるファイルを土台として、新しいEXCELファイルを作るサンプル。

<?php

require '../vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;


// 元になるファイルをスプレッドシートに読み込む
$reader = new XlsxReader();
$spreadsheet = $reader->load( './template.xlsx' );


// ここで設定や値を書き込む


// xlsx形式で保存する
$writer = new XlsxWriter( $spreadsheet );
$writer->save( './output.xlsx' );


// スプレッドシートを閉じてメモリを解放
$spreadsheet->disconnectWorksheets();
unset( $spreadsheet );
PHP

書式や罫線などのフォーマット、計算式なども予めいれたファイルを土台にすれば、必要なセルに値を入れるだけで済む。

ただし、それでも消えるものがあるので注意が必要。
上記のように読み込んで別名保存するだけでもファイルの容量が減ったりする。

PhpSpreadsheet = EXCELではないので、当然EXCELが出来ることが全て出来るわけではない。
扱える機能や情報もサポートされている範囲に限られる。

そのため、既存ファイルの内容によっては、必要なものが消えてしまったり、出力したファイルをEXCELで開いても動かなくなってたりするものがある。

消える内容例は、別記事を参考。
https://makeis.dev/archives/206

ファイルの読み込みだけなら

<?php

require '../vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;


// 元になるファイルをスプレッドシートに読み込む
$reader = new XlsxReader();
$spreadsheet = $reader->load( './template.xlsx' );


// ここで値を読み込む


// スプレッドシートを閉じてメモリを解放
$spreadsheet->disconnectWorksheets();
unset( $spreadsheet );
PHP

必要な情報を読み込んだあと、保存せず閉じる。

読み込みたいデータ内容やシートによっては、以下のオプションを使って処理時間やメモリを抑える事も出来る。

$reader = new XlsxReader();

// 値だけ読み込むよう指定
$reader->setReadDataOnly( true );

// 読み込むシートを指定
$reader->setLoadSheetsOnly( 'Sheet1' );

$spreadsheet = $reader->load( './template.xlsx' );
PHP

メモリの解放について補足

必要がなくなったら早々にスプレッドシートを閉じてメモリを解放した方が良い。
理由は2つ。

1. メモリを大量に消費するので、続く後処理のためにもメモリを空けることが望ましい
2. 関数の終わりでスコープから外れてもメモリが解放されない

特に2.が問題で、明示的に解放しないとメモリが抑えられ続ける。
これは公式でも触れられている。

【参考】
https://phpspreadsheet.readthedocs.io/en/latest/topics/creating-spreadsheet/#clearing-a-workbook-from-memory

// スプレッドシートを閉じてメモリを解放
$spreadsheet->disconnectWorksheets();
unset( $spreadsheet );
PHP

なお、メモリ解放の情報をネットで探すと以下が紹介されていることが多い。

$spreadsheet->disconnectWorksheets();
$spreadsheet->garbageCollect();
PHP

ただ、この内容は望ましくないかも。

まず、garbageCollect()が公式のドキュメントでは検索にひっかかってこない関数で良く分からない。
公式が案内しているメモリ解放でも使われていないし。

関数の実装を眺めても、disconnectWorksheets()の後で使うものではない印象。

実際に試してみても、garbageCollect()の前後で使用メモリを確認しても差がなかった。
エラーが返ってこないから問題ないように見えるだけで、実際は意味ないのでは。
(データの内容による可能性は否定出来ないが)