PhpSpreadsheet 計算エンジンの高度な実装手法

式の評価処理

PhpSpreadsheetはメモリ上のスプレッドシートを操作するため、式の計算機能を内蔵しています。セルには数値や文字列を格納する「値セル」と、評価可能な数式を含む「式セル」があります。例として=SUM(B2:B11)はB2からB11の合計値を算出します。

式の評価はcalculateCellValue()メソッドで実行可能です:

$book = new Spreadsheet();
$sheet = $book->getActiveSheet();
$result = $sheet->calculateCellValue('F12');

このメソッドは式を評価し、結果を数値または文字列で返します。Invoiceデモで上記コードを実行すると72が得られます。

特筆すべき点は、行・列の挿入時に式が自動調整される点です。例えばF12セルにSUM(F3:F10)が設定されている場合、

$sheet->insertNewRowBefore(5, 3);

を実行すると、式は自動的にSUM(F3:F13)へと更新されます。挿入されたセルの書式設定も元のセルを継承するため、Excelと同様の動作を実現しています。

計算キャッシュの管理

式の評価結果はキャッシュに保存されるため、同一セルの再評価時は計算をスキップします。この仕組みにより、計算負荷の高い式でもパフォーマンスを維持できます。

ただし、基となるデータが変更された場合はキャッシュを無効化する必要があります:

// キャッシュを完全無効化
Calculation::getInstance($book)->setCacheEnabled(false);

// キャッシュをクリア(有効状態を維持)
Calculation::getInstance($book)->clearCacheData();

デフォルトではキャッシュが有効ですが、頻繁にデータを更新するシナリオではsetCacheEnabled(false)を事前に設定することを推奨します。

日付・時刻の取り扱い

日付関数の出力形式はFunctions::setReturnDateType()で制御可能です:

use PhpOffice\PhpSpreadsheet\Calculation\Functions;

// Excel互換形式に設定
Functions::setReturnDateType(Functions::RETURN_DATE_EXCEL);

// PHP DateTimeオブジェクト形式
Functions::setReturnDateType(Functions::RETURN_DATE_OBJECT);
定数 データ形式 有効範囲
RETURN_DATE_NUMERIC PHPタイムスタンプ(整数) 1901-2038年
RETURN_DATE_OBJECT DateTimeオブジェクト 全期間対応
RETURN_DATE_EXCEL Excelシリアル値(浮動小数) 1900-9999年

カレンダーベースの設定はDate::setExcelCalendar()で変更可能です:

use PhpOffice\PhpSpreadsheet\Shared\Date;

// Mac用1904基準カレンダに切り替え
Date::setExcelCalendar(Date::CALENDAR_MAC_1904);

日付関数のサンプル

DATE関数の利用例:

// 2023年12月25日をExcelシリアル値で取得
Functions::setReturnDateType(Functions::RETURN_DATE_EXCEL);
$excelDate = Functions::DATE(2023, 12, 25); // 45285.0

// 同日をDateTimeオブジェクトで取得
Functions::setReturnDateType(Functions::RETURN_DATE_OBJECT);
$dateTime = Functions::DATE(2023, 12, 25); // DateTimeインスタンス

DATEDIF関数は日付間の差分を計算します:

$start = '2020-01-01';
$end = '2023-03-15';

// 3年2ヶ月14日を日数換算
$days = Functions::DATEDIF($start, $end, 'd'); // 1169

// 月単位の差分
$months = Functions::DATEDIF($start, $end, 'm'); // 38

データベース関数の実装例

DAVERAGE関数は条件に合致するデータの平均値を算出します。実装例:

// 商品データセット
$productData = [
    ['商品', '高さ', '年齢', '収量', '利益'],
    ['りんご', 15, 18, 12, 90.50],
    ['みかん', 10, 10, 8, 85.00],
    ['さくらんぼ', 12, 13, 7, 95.00],
    ['りんご', 13, 14, 9, 70.00]
];

// 条件設定
$criteria = [
    ['商品', '高さ'],
    ['りんご', '>12']
];

// ワークシートに反映
$sheet->fromArray($criteria, null, 'A1');
$sheet->fromArray($productData, null, 'A4');

// 平均収量を計算
$sheet->setCellValue('A10', '=DAVERAGE(A4:E7, "収量", A1:B2)');
$avgYield = $sheet->getCell('A10')->getCalculatedValue(); // 10.5

DCOUNT関数は数値を含むセルのカウントに使用します:

$sheet->setCellValue('A11', '=DCOUNT(A4:E7, "高さ", A1:A2)');
$count = $sheet->getCell('A11')->getCalculatedValue(); // 2

DMAX/DMINは最大値・最小値を取得します:

$sheet->setCellValue('A12', '=DMAX(A4:E7, "利益", A1:B2)');
$maxProfit = $sheet->getCell('A12')->getCalculatedValue(); // 90.50

制限事項と対処法

計算エンジンには以下の制限があります:

  • Excelの&演算子の優先順位がPHPと異なる
  • 数値と文字列の演算でエラーが発生しない場合がある
  • Xlsフォーマットでは一部関数がサポートされない

特に日付計算では、タイムゾーン設定が重要です:

// タイムゾーンを東京標準時に設定
Date::setDefaultTimezone('Asia/Tokyo');

// Excelシリアル値をDateTimeに変換
$dateTime = Date::excelToDateTimeObject(45285.0); // 2023-12-25

タグ: PhpSpreadsheet excel-formulas php-date-handling spreadsheet-engine

6月29日 19:10 投稿