2010年10月20日水曜日

PHPExcel1.7.4の不具合?について

以前PHPExcelのメモリ馬鹿食いについてで触れていたコードがPHPExcel1.7.4では正常に動作しないことが判明しました。

ワークシートオブジェクトのclone処理で、セルへの参照が1.7.4では上手くハンドリングされていないようで、書式が設定されたセルへ値を書き込むと、別のシートの同じセルにも値が書き込まれることがあります。

再現する条件を完全には洗い出せていませんが、書式が何も設定されていないプレーンなセルに対する書き込みは問題ありませんが、以下のような条件で別のシートの同じセルにも値が書き込まれることがあります。

・書き込むセルに既に値が入力されている
・書き込むセルがセル結合されている

他にもワークシート名の変更もちょっと動作が怪しいことがあります。

PHPExcelの1.7.4には不具合があるようです。
というよりも根本的な不具合があるのは1.7.3系で、PHPExcelのメモリ馬鹿食いについてで触れていたメモリリークっぽい現象を修正しようとして失敗しているようです。

セルのデータをキャッシュ(デフォルトではメモリ上に展開?)し、それを利用してセルのコピーやclone処理を行っているようですが、1.7.4系ではデータキャッシュのライブラリが格納されているClasses/PHPExcel/CachedObjectStorage配下が大幅に修正されています。

1.7.4は使用せずに1.7.3cに巻き戻した方が良さそうです。

1.7.3cではPHPExcel_CachedObjectStorage_Memcache.phpに明らかなバグ(単純ミス)があって気持ち悪いので、その箇所のみパッチ当てて使ってます。
ただし、このクラスはPHPExcel_Settingsを通して明示的にmemcachedをキャッシュオブジェクトとして指定しないと使われないようなので、デフォルト設定では実害はないようです。

以下、パッチ。

189行目
- if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback')) {
+ if (!$this->_memcache->addServer($memcacheServer, $memcachePort, false, 50, 5, 5, true, array($this, 'failureCallback'))) {

200行目
- throw new Exception('memcache '.$host.':'.$port' failed');
+ throw new Exception('memcache '.$host.':'.$port.' failed');

それから、PHPExcelのメモリリークを調べていた際に参考にさせて頂いたサイト【PHP】PHPExcelがループ内でメモリを使いすぎるで「さらに注意点としては、1.7.3cはaddExternalSheetにバグがあるので、最新のソースをダウンロードした方がいい。」との記述がありますが、これもPHPExcel.phpに以下のパッチを当てれば良さそう。

437行目
- $cell = $sheet->getCell($cellID);
+ $cell = $pSheet->getCell($cellID);

パッチを当てた1.7.3cで当分しのいで、1.7.4の修正版が出るのを待つ作戦をおすすめ。

2010年10月10日日曜日

PHPExcelでセルの背景色指定

背景色の付け方自体は以下の参考リンクから。

PHPExcel-セルのスタイル
PHPExcel(リファレンス1)
PHPExcel セル背景色 & 行と列からアドレスに変換

上記、すごく助かったんですが、実際にどんな値設定すれば何色になるのかリファレンスは見つからず。
PHPExcel_Style_Colorにいくつか有用な定義がされていますが、今一つ何色になるのか分かりにくい。

    const COLOR_BLACK                        = 'FF000000';
    const COLOR_WHITE                        = 'FFFFFFFF';
    const COLOR_RED                            = 'FFFF0000';
    const COLOR_DARKRED                        = 'FF800000';
    const COLOR_BLUE                        = 'FF0000FF';
    const COLOR_DARKBLUE                    = 'FF000080';
    const COLOR_GREEN                        = 'FF00FF00';
    const COLOR_DARKGREEN                    = 'FF008000';
    const COLOR_YELLOW                        = 'FFFFFF00';
    const COLOR_DARKYELLOW                    = 'FF808000';

    public static function indexedColor($pIndex) {
        // Clean parameter
        $pIndex = intval($pIndex);

        // Indexed colors
        if (is_null(self::$_indexedColors)) {
            self::$_indexedColors = array();
            self::$_indexedColors[] = '00000000';
            self::$_indexedColors[] = '00FFFFFF';
            self::$_indexedColors[] = '00FF0000';
            self::$_indexedColors[] = '0000FF00';
            self::$_indexedColors[] = '000000FF';
            self::$_indexedColors[] = '00FFFF00';
            self::$_indexedColors[] = '00FF00FF';
            self::$_indexedColors[] = '0000FFFF';
            self::$_indexedColors[] = '00000000';
            self::$_indexedColors[] = '00FFFFFF';
            self::$_indexedColors[] = '00FF0000';
            self::$_indexedColors[] = '0000FF00';
            self::$_indexedColors[] = '000000FF';
            self::$_indexedColors[] = '00FFFF00';
            self::$_indexedColors[] = '00FF00FF';
            self::$_indexedColors[] = '0000FFFF';
            self::$_indexedColors[] = '00800000';
            self::$_indexedColors[] = '00008000';
            self::$_indexedColors[] = '00000080';
            self::$_indexedColors[] = '00808000';
            self::$_indexedColors[] = '00800080';
            self::$_indexedColors[] = '00008080';
            self::$_indexedColors[] = '00C0C0C0';
            self::$_indexedColors[] = '00808080';
            self::$_indexedColors[] = '009999FF';
            self::$_indexedColors[] = '00993366';
            self::$_indexedColors[] = '00FFFFCC';
            self::$_indexedColors[] = '00CCFFFF';
            self::$_indexedColors[] = '00660066';
            self::$_indexedColors[] = '00FF8080';
            self::$_indexedColors[] = '000066CC';
            self::$_indexedColors[] = '00CCCCFF';
            self::$_indexedColors[] = '00000080';
            self::$_indexedColors[] = '00FF00FF';
            self::$_indexedColors[] = '00FFFF00';
            self::$_indexedColors[] = '0000FFFF';
            self::$_indexedColors[] = '00800080';
            self::$_indexedColors[] = '00800000';
            self::$_indexedColors[] = '00008080';
            self::$_indexedColors[] = '000000FF';
            self::$_indexedColors[] = '0000CCFF';
            self::$_indexedColors[] = '00CCFFFF';
            self::$_indexedColors[] = '00CCFFCC';
            self::$_indexedColors[] = '00FFFF99';
            self::$_indexedColors[] = '0099CCFF';
            self::$_indexedColors[] = '00FF99CC';
            self::$_indexedColors[] = '00CC99FF';
            self::$_indexedColors[] = '00FFCC99';
            self::$_indexedColors[] = '003366FF';
            self::$_indexedColors[] = '0033CCCC';
            self::$_indexedColors[] = '0099CC00';
            self::$_indexedColors[] = '00FFCC00';
            self::$_indexedColors[] = '00FF9900';
            self::$_indexedColors[] = '00FF6600';
            self::$_indexedColors[] = '00666699';
            self::$_indexedColors[] = '00969696';
            self::$_indexedColors[] = '00003366';
            self::$_indexedColors[] = '00339966';
            self::$_indexedColors[] = '00003300';
            self::$_indexedColors[] = '00333300';
            self::$_indexedColors[] = '00993300';
            self::$_indexedColors[] = '00993366';
            self::$_indexedColors[] = '00333399';
            self::$_indexedColors[] = '00333333';
        }

        if (array_key_exists($pIndex, self::$_indexedColors)) {
            return new PHPExcel_Style_Color(self::$_indexedColors[$pIndex]);
        }

        return new PHPExcel_Style_Color();
    }

ということで、Excel標準のカラーパレットに定義されている色のARGBを調べました。
カラーパレット左上から右順で、

FF000000, FF993300, FF333300, FF003300, FF003366, FF000080, FF333399, FF333333,
FF800000, FFFF6600, FF808000, FF008000, FF008080, FF0000FF, FF666699, FF808080,
FFFF0000, FFFF9900, FF99CC00, FF339966, FF33CCCC, FF3366FF, FF800080, FF969696,
FFFF00FF, FFFFCC00, FFFFFF00, FF00FF00, FF00FFFF, FF00CCFF, FF993366, FFC0C0C0,
FFFF99CC, FFFFCC99, FFFFFF99, FFCCFFCC, FFCCFFFF, FF99CCFF, FFCC99FF, FFFFFFFF,

控えておくと地味に役に立つこともあるかも。