してログ

単純なバイナリファイルの読み込みだけど、fopen、feof、fread、fclose を使った至ってシンプルな下記のようなコードは危険だ。

$fp = fopen('binary/file/path' ,'rb');
while (!feof($fp)) {
	echo fread($fp,8192);
}
fclose($fp);

これはだいたい上手くいくが、無限ループの危険をはらんでいる。 そのシナリオは、まず、fopen が失敗して $fp に false が入る。 次に、ループ条件が評価され、feof は無効なファイルポインタに対して false を返してしまう。 feof はファイルの終端に達しているかを論理値で返すため、終端でないの意の false と区別がつかない。 従って、無効なファイルポインタを渡されたループは、ファイルをまだ読み終わってないとして、延々と空回りしてしまうことになる。

これを避けるには、ファイルポインタが有効かどうかの判断を加えれば良く、単純に下記のようなコードで良い。 変わったのは、while のループ条件にファイルポインタ自体を加えただけで、ファイルポインタが無効なときは無限ループを回避する。

$fp = fopen('binary/file/path' ,'rb');
while ($fp && !feof($fp)) {
	echo fread($fp,8192);
}
fclose($fp);

なお、ファイルポインタがデータベースの問い合わせで得たバイナリ型のものであってもこれで良さそうだ。 比較関係では書き方や関数があるので、一度ここらへんを見て勉強しておこうと思う。 今更だけれど。

HTML5 で作られたページを IE8 で表示させたときに、互換モードで表示されてしまい、レイアウトが崩れるという事態に遭遇しました。 調べてみると、META タグで互換モードへ落ちるのを防げるらしいので、試してみました。 下記のような記述を、<head> の直後に書いたところ、アドレスバーの脇に出ていた互換モードのアイコンが消えました。

<meta http-equiv="X-UA-Compatible" content="IE=edge" />

PHPExcel はとにかく遅いし、バグも多いのでできれば使いたくないですよね。 でも、xlsx に出すなら使うしかなくて、そんなときはおっかなびっくり使うしか無い、です。 さて、今日はこのエラー「Maximum 31 characters allowed in sheet title」が唐突に出るようになりました。

メッセージからすると、31文字を超えるシート名は使えない、ということになろうかと思います。 しかし、正しく処理できていたエクセルファイルを保存しなおしただけで、このエラーが出るようになったので、訳が分かりません。 念のため、すべてのシートの文字数をチェックしましたが、31文字を超えるものはありませんでした。 Google 先生に聞いてみると、ライブラリに応急手当てして無理やり出ないようにしたという記事が幾つかみつかります。 原因はともかく、私もそれに習って修正してみました。

Classes/PHPExcel/Worksheet.php
	public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
	{
		$pValue = substr($pValue, 0, 31); // この行を追加

(追記)この対応をすると、iconv でマルチバイト文字が正しくないというエラーが出ますので、やめた方が良いです。 詳しくは分かりませんが、元のエラーはメモリ不足で出ていたように思います。

jQuery UI Dialog で modal: true としたダイアログで、再表示したときに操作不能になるという現象に遭遇しました。 操作不能というのは、ダイアログに配置したセレクトボックスは選択リストが表示されなくなり、テキストボックスはクリックしてもキャレットすら表示されないという状態です。 ただし、フォーカスは入っているらしく Chrome などで動かすと、枠の色が変わるのが分かります。 ちなみに、モーダルでなければそういうことにはなりません。

色々と調べた結果、作りおきのダイアログがあるとダメなことが分かりました。 問題のあったページでも、そのダイアログとは別に、autoOpen: false として作っておいて、必要なときに dialog('open') しているものがありました。 これを、ボタンやリンクがクリックされた際に、都度作るようにしてみたところ、問題が解決しました。 この現象でお悩みの方は、作りおきのダイアログが他に無いか調べてみてください。

単純なワークシートのコピーで、下記のようなエラーが出ました。 コードを最小化しても出続けるみたいなので、試しに 1.7.8 にバージョンを下げてみたところ解決しました。 とりあえず、PHPExcel 1.7.9 は封印したほうが良いかも知れません。

Catchable fatal error: Argument 1 passed to PHPExcel_Cell::attach() must be an instance of PHPExcel_CachedObjectStorage_CacheBase, instance of PHPExcel_Worksheet given, called in C:\wk\bousai3\_cmn\PHPExcel_1.7.9\Classes\PHPExcel\CachedObjectStorage\Memory.php on line 99 and defined in C:\wk\bousai3\_cmn\PHPExcel_1.7.9\Classes\PHPExcel\Cell.php on line 115

エクセルのセル番号「A1」とか、アルファベットが列、数字が行というやつ。 アルファベットの部分をプログラムで扱う場合は、数字で管理しないとやりにくい。 そこで今日、その変換ルーチンを書いていたんですが、どうもうまくいかない。 二桁程度だとロジックも簡単なのだが、一般化しようと思って任意の桁に拡大した途端、難易度が段違いに上がる。 というか、断念してしまいました。

基本は、アルファベット26文字による26進数への変換となるはずなのですが、そうすると A~Z までは OK、その次が BA となる。 なぜかというと、ゼロが無いから。 よく考えると、ゼロのない26進数となっているのだが、アルファベット表記なため、それに気づきにくい。 話を簡単にするために、1~3 のゼロなし3進数とすると、1、2、3、11、12、13、21... というような並びになる。 従って、ゼロのある頭でロジックを考えた結果、Z の次が BA になる訳だ。

というところまで分かって、ゼロなしの26進数への変換ロジックを考えてみる。 これが、予想以上に難解で3桁の直前の YZ までできる関数しか出来ませんでした。 ので、ソースはありません。

代わりに、PHPExcel のスタティック関数(PHPExcel_Cell::stringFromColumnIndex)を使うことにしました。 PHPExcel のライブラリをインクルードすれば、静的関数なのですぐに使えます。 今度、ソースコードを覗いて、どんなロジックで変換しているのか勉強してみようと思います。

Windows7 の上位エディションには、仮想マシンで Windows XP を実行できるライセンスが付いています。 一般的な仮想マシンのようにウィンドウ内で Windows XP を実行する OS モードと、Windows7 のデスクトップ上で Windows XP のアプリケーションを実行できる仮想アプリケーションモードがあります。 で、その仮想アプリケーションモードでは、Windows XP に後からインストールしたアプリケーションしかメニューに出てこないため、最初から入っている Internet Explorer は利用できないのかと思っていました。 しかし、次のように手動で登録することにより、実行できることが分かりましたので紹介します。

  1. XP mode を OS モードで起動します
  2. 次のフォルダを開きます「C:\Documents and Settings\All Users\スタート メニュー\プログラム」
  3. Internet Explorer のショートカットを先のフォルダに作ります
  4. Windows7 のプログラムメニューに自動的に公開されます
  5. XP mode を終了します(仮想アプリケーションモードと共存できないため)

InternetExplorer 8 は、まだ要求仕様になることがあり、これならデバッグを効率的に行うことができます。 もちろん、自動更新を停止して IE6 や IE7 を Windows7 上で表示させることも可能です。

OpenLayers をバージョンアップしたところ、jQuery の draggable を適用したフローティング・パネルの動きが変になってしまいました。ゆっくりと動かして、ドラッグハンドルをマウスが外れなければ正しく動きますが、速く(といっても普通に)動かして、ドラッグハンドルをマウスが少しでも外れると、ドラッグが解除されたり、おかしな動作が始まります。マウスダウンからアップまで、ハンドリングできない状態になり、低スペックマシンではかなり操作しづらくなります。

なかなか原因が分からなくて、旧バージョンにロールバックしようかと思いましたが、分かってしまえば至って単純な原因でした。OpenLayers の新しいバージョンは、fallThrouth というオプションがデフォルトで false になっています。これを true にするだけでした。

	map = new OpenLayers.Map({
		fallThrough:       true
	});

FormData があると Ajax でファイルアップロードできるようになって大変便利なのですが、困るのが IE8 と IE9 の対応です。 IE10 からは FormData が使えますが、未だに IE8 の要求は来ます。 かつての IE6 のポジションを受け継いでいる臭が漂っています。

諦めて POST で画面リロードするところでしたが、ちゃんと調べて見ると代替策がありました。 使うのは POST ですが、見せ方としては非同期通信っぽく動きます。 サンプルコードは載せませんが、要点を箇条書きにしますので、参考にしてください。

  • 親ページに iframe を配置して見えないようにします(display:none だと Safari でまずいみたいなので、横幅&縦幅をゼロにします)
  • フォームのターゲット要素(target="<iframe名>")で、配置した iframe を指定します
  • サーバー側で POST を受け取ったら、JavaScript で親ページの関数を呼んで通知するようにします
  • 親ページでは関数が呼ばれたら、アップロード完了などのメッセージを提供できます