PHP Excel拡張モジュールをざっくり試してみた
『PHPでExcel』と来れば通常はPHPExcelなんですが、如何せん、『でかい・遅い・メモリ食う』と三拍子そろってしまってます。で、PHPのコア開発者のIlia氏が、LibXLを使った拡張モジュールを書いてしまったようです。
LibXLは、昔のExcelのデフォルト形式であるBIFF7/8形式で出力可能なライブラリです。ちなみに、無償でも使えますがトライアル扱いとなり、1行目に『フルバージョンのライセンスを買ってね』($199〜$2,199)というメッセージが入ってしまいます。
Since I broke my right hand 3 weeks ago while biking, I found myself with a lot of spare time :/. It is amazing just how limited your ability to do things becomes when you can only use one hand. So, to stave off the boredom, I've been slowly toiling away on a PHP Excel extension that I intend to use at work, which I've finally gotten ready for release today.
You can find it on github at: http://github.com/iliaal/php_excel.
ということで、ざっくり試してみました。
環境
- CF-R8+VMwarePlayer2.5.4+CentOS5.4
- PHP-5.2.13
インストール
まずはLibXLのインストール。基本的にバラすだけ。
$ wget http://www.libxl.com/download/libxl.tar.gz $ tar zxf libxl.tar.gz $
$ wget http://github.com/downloads/iliaal/php_excel/php-excel-0.8.tar.bz2 $ tar jxf php-excel-0.8.tar.bz2 $ cd php_excel/ $ phpize $ ./configure : checking for excel support... yes, shared checking for excel files in default path... not found configure: error: Please reinstall the excel distribution $
ということで、libxlの場所を指定して再度configure。
$ cd ../libxl-2.4.3 $ ln -s include_c include $ cd - $ ./configure --with-excel=shared,../libxl-2.4.3 $ make $ sudo make install $
動作確認
とりあえず、PHP Excel Extension - iBlog - Ilia Alshanetskyにあるサンプル(test.php)
<?php $sT = microtime(1); $x = new ExcelBook(); $s = $x->addSheet("Sheet 1"); for ($i = 0; $i < 1000; $i++) { for ($j = 0; $j < 10; $j++) { $s->write($i, $j, ($i * $j)); } } $x->save("bench.xls"); $eT = microtime(1); var_dump(($eT - $sT), memory_get_usage(1), memory_get_peak_usage(1));
を実行してみることに。
$ LD_LIBRARY_PATH=../libxl-2.4.3/lib php -dextension=excel.so test.php php: symbol lookup error: /path/to/php/extensions/no-debug-non-zts-20060613/excel.so: undefined symbol: xlCreateBookCA $
へっ。。。?ということで、lddで確認。
$ ldd /path/to/php/extensions/no-debug-non-zts-20060613/excel.so linux-gate.so.1 => (0x00599000) libc.so.6 => /lib/libc.so.6 (0x00753000) /lib/ld-linux.so.2 (0x00539000) $
あの。。。libxl.soがリンクされてませんが。。。
再インストール
Makefileを見てみると、やっぱりlibxlをリンクしてない感じ。で、
$ cp -p Makefile Makefile.org $ vi Makefile $ diff Makefile.org Makefile 40c40 < LDFLAGS = --- > LDFLAGS = -L../libxl-2.4.3/lib -lxl $ make clean $ make $ sudo make install $ LD_LIBRARY_PATH=../libxl-2.4.3/lib ldd /path/to/php/extensions/no-debug-non-zts-20060613/excel.so linux-gate.so.1 => (0x0091f000) libxl.so => ../libxl-2.4.3/lib/libxl.so (0x00110000) libc.so.6 => /lib/libc.so.6 (0x00bf9000) libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x003f4000) libm.so.6 => /lib/libm.so.6 (0x00d9f000) libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x009cb000) libpthread.so.0 => /lib/libpthread.so.0 (0x004df000) /lib/ld-linux.so.2 (0x00539000) $
今度はヨサゲ。
ホントは次のような感じでもOKだと思ったんですが、うまくconfigureできなかったので、configure後にMakefileを編集するといった方法を採りました。
$ LDFLAGS="-L../libxl-2.4.3/lib -lxl" ./configure --with-excel=shared,../libxl-2.4.3 : checking for C compiler default output file name... a.out checking whether the C compiler works... configure: error: cannot run C compiled programs. If you meant to cross compile, use `--host'. See `config.log' for more details. $
再度、動作確認
CF-R8+VMwarePlayer+CentOS5.4上で実行。ほぼ一瞬(0.1秒以下)で10桁x1000行のセルに値を突っ込んだxlsファイルが作成されました。
$ ll *.xls ls: *.xls: そのようなファイルやディレクトリはありません $ $ LD_LIBRARY_PATH=../libxl-2.4.3/lib php -dextension=excel.so test.php float(0.078369140625) int(262144) int(262144) $ $ ll *.xls -rw-rw-r-- 1 shimooka shimooka 147968 8月 2 19:12 bench.xls $
追加されるクラス/メソッド
LibXL documentationにある関数をOO的にしたものと考えてOKかと。とりあえず、定義されているものを抜き出すと以下のような感じ。定数は多すぎるので割愛(LibXL documentationを参照)。
- class ExcelBook
- public addFont(font)
- public addFormat(format)
- public getError()
- public loadFile(filename)
- public load(data)
- public save(filename)
- public getSheet(sheet)
- public addSheet(name)
- public copySheet(name, sheet_number)
- public deleteSheet(sheet)
- public sheetCount()
- public activeSheet(sheet)
- public getCustomFormat(id)
- public addCustomFormat(format)
- public packDate(timestamp)
- public unpackDate(date)
- public getActiveSheet()
- public setActiveSheet(sheet)
- public getDefaultFont()
- public setDefaultFont(font, font_size)
- public setLocale()
- public addPictureFromFile(filename)
- public addPictureFromString(data)
- public __construct(license_name, license_key)
- class ExcelSheet
- public cellType(row, column)
- public cellFormat(row, column)
- public read(row, column, format)
- public readRow(row, start_col, end_column)
- public readCol(column, start_row, end_row)
- public write(row, column, data, format, datatype)
- public writeRow(row, data, start_column, format)
- public writeCol(row, data, start_row, format)
- public isFormula(row, column)
- public isDate(row, column)
- public insertRow(row_first, row_last)
- public insertCol(col_first, col_last)
- public removeRow(row_first, row_last)
- public removeCol(col_first, col_last)
- public colWidth(column)
- public rowHeight(row)
- public readComment(row, column)
- public writeComment(row, column, value, author, width, height)
- public setColWidth(column_start, column_end, width, hidden, format)
- public setRowHeight(row, height, format, hidden)
- public getMerge(row, column)
- public setMerge(row_start, row_end, col_start, col_end)
- public deleteMerge(row, column)
- public addPictureScaled(row, column, pic_id, scale)
- public addPictureDim(row, column, pic_id, width, height)
- public horPageBreak(row, break)
- public verPageBreak(col, break)
- public splitSheet(row, column)
- public groupRows(start_row, end_row, collapse)
- public groupCols(start_column, end_column, collapse)
- public clear(row_s, row_e, col_s, col_s)
- public copy(row, col, to_row, to_col)
- public firstRow()
- public lastRow()
- public firstCol()
- public lastCol()
- public displayGridlines()
- public printGridlines()
- public setDisplayGridlines(value)
- public setPrintGridlines(value)
- public zoom()
- public zoomPrint()
- public setZoom(value)
- public setZoomPrint(value)
- public setLandscape(value)
- public landscape()
- public paper()
- public setPaper(value)
- public header()
- public footer()
- public setHeader()
- public setFooter()
- public headerMargin()
- public footerMargin()
- public hcenter()
- public vcenter()
- public setHCenter(value)
- public setVCenter(value)
- public marginLeft()
- public marginRight()
- public marginTop()
- public marginBottom()
- public setMarginLeft(value)
- public setMarginRight(value)
- public setMarginTop(value)
- public setMarginBottom(value)
- public printHeaders()
- public name()
- public setName()
- class ExcelFormat
- public getFont()
- public setFont(font)
- public numberFormat(format)
- public horizontalAlign(align_mode)
- public verticalAlign(align_mode)
- public wrap(wrap)
- public rotate(angle)
- public indent(indent)
- public shrinkToFit(shrink)
- public borderStyle(style)
- public borderColor(color)
- public borderLeftStyle(style)
- public borderLeftColor(color)
- public borderRightStyle(style)
- public borderRightColor(color)
- public borderTopStyle(style)
- public borderTopColor(color)
- public borderBottomStyle(style)
- public borderBottomColor(color)
- public borderDiagonalStyle(style)
- public borderDiagonalColor(color)
- public fillPattern(patern)
- public patternForegroundColor(color)
- public patternBackgroundColor(color)
- public locked(locked)
- public hidden(hidden)
- final public __construct(book)
- class ExcelFont
- public size(size)
- public name(name)
- public underline(underline_style)
- public mode(mode)
- public color(color)
- public bold(bold)
- public strike(strike)
- public italics(size)
- final public __construct(book)
日本語は?
次のようなコードで試してみましたが、EXCEL2000で問題なく表示できました。
<?php /** * 追加するシートの枚数 */ define('SHEETS', 10); /** * 新規Bookオブジェクトを作成 */ $book = new ExcelBook(); /** * シートを10枚追加 */ for ($i = 0; $i < SHEETS; $i++) { $book->addSheet("{$i}番目のシート"); } /** * 各シートにデータを書き込み */ for ($i = 0; $i < SHEETS; $i++) { $sheet = $book->getSheet($i); for ($col = 0; $col < 10; $col++) { for ($row = 0; $row < 10; $row++) { $sheet->write($row, $col, '日本語です' . ($row * $col + $i)); } } } /** * Bookを"multibytes.xls"として保存 */ $book->save("multibytes.xls");
ちなみに、mbstring系の設定は以下のように全てUTF-8で統一してあり、上記スクリプトのエンコーディングもUTF-8です。
mbstring.internal_encoding = utf-8 mbstring.http_input = utf-8 mbstring.http_output = utf-8 mbstring.encoding_translation = Off mbstring.detect_order = utf-8 mbstring.substitute_character = none; mbstring.func_overload = 0 mbstring.strict_detection = Off mbstring.script_encoding = utf-8
既存のxlsファイルに追記して別名で保存してみる
graph_org.xlsにはグラフだけ用意してあり、PHPスクリプトでデータを突っ込んで別名で保存する、というサンプルを作ってみました。これ、結構応用範囲が広いんじゃないかなぁ。
<?php /** * 雛形となるxlsファイルを読み込み */ $book = new ExcelBook(); $book->loadFile('graph_org.xls'); /** * 最左のシートを取得 */ $sheet = $book->getSheet(0); /** * データを書き込み */ $sheet->write(1, 0, '項目X'); // セルA2 $sheet->write(1, 1, '項目Y'); // セルB2 for ($col = 0; $col < 2; $col++) { for ($row = 2; $row < 50; $row++) { $sheet->write($row, $col, pow($col + $row, 2) + $row); } } /** * Bookを"graph.xls"として保存 */ $book->save("graph.xls");
まとめ
ざっくりとしか試してませんが、パフォーマンスも良く、結構使えるんじゃないかと思います。色やフォント、レイアウトなども指定できるようですが、まあ、1からゴリゴリxlsファイルを作るのは大変なので、既存ファイルを読み込んで、データを埋めたりシートをコピーしたりして結果を出力する、という使い方が良いんじゃないでしょうかね。
なお、各サンプルスクリプトと出力されたxlsファイルをDo You PHP?にUPしてありますので、興味がある方はどうぞ。
- http://www.doyouphp.jp/php_excel.zip (MD5:307d9b54db7e45154ec96da946cfe6a1)
追記(2010/08/12 22:12)
2010/08/11付けでバージョン0.8.5がリリースされました。
libxlがリンクされない件も修正されています:-)