Smartyでpreg_matchエラー

Smartyを使っていてこんなエラーが出た。

PHP Warning: preg_match() [<a href=’function.preg-match’>function.preg-match</a>]: Compilation failed: repeated subpattern is too long at offset 18454 in /library/smarty/Smarty_Compiler.class.php on line 454

たしか以前もこんなエラーに出くわしたことがある。確か、そのときは「PHPのバージョンが古いとpcre関連でバグがある」というような理解で終わったと記憶していた。そのときの解決策までは思い出せないが、エラーメッセージを見る限りでは正規表現のパターンの長すぎが原因のようだ。

しかし困ったのは、このエラーがSmarty内部で発生しているということ。ライブラリの中身なんて修正したくない(だって他のところでは動作しているのだから)。よくよく調べたらこのエラー、Smartyのバージョンを落とすことによって回避できるらしい。きっとバージョンが新しくなり、正規表現で記述していたパターンが長くなってエラーが出たのだろう。

エラーが出たバージョンは2.6.21だった。調べた結果によると、2.6.20と2.6.21がちょうど天と地の境目のようだ。早速2.6.20をダウンロードしてみたら見事解決した。よかった。ていうか、レンタルサーバ会社のPHPのバージョンが低いせいだよね、きっと。

CGI版PHP用の設定

CGIとしてPHP4をインストールし、PHPスクリプトの1行目にパスを記述して、ウェブからアクセスしてみた。しかし500エラーが返ってきた。Apacheのログを見ると「Security Alert!」「The PHP CGI cannot be accessed directly.」とある。でも、これ意味がよくわからない。さらにメッセージが並んでいるのだが、自分の英語の読解力が低いせいか、全部読んでも意味がわからない。

調べてみると、設定ファイルを変更すればよさそうだ。

cgi.force_redirect=0

やっぱり意味がわからない。設定変更して、Apacheを再起動し、スクリプトがPHP4のCGIとして動作することを確認できた。セキュリティの問題らしいので、詳しくドキュメントを読み込む必要がありそうだ。ま、とりあえず、これでよし。

解せない日付処理

海外サーバを利用する上で欠かせないのが日付処理での一工夫だ。例えばアメリカのレンタルサーバを利用した場合、当然サーバはアメリカの現地時間に設定されている。だから、例えば掲示板プログラムを設置した場合、例えば日本でお昼の14時に投稿していたとしても、プログラムで何の工夫もされていなければ、深夜の1時に投稿した、というような具合で記録、表示されることになるだろう。海外のサーバを使う際は時差の処理が大切になってくる。

PHP5の場合はデフォルトのタイムゾーンを設定する「date_default_timezone_set」など有用な関数が装備されたので、最初に宣言すれば気にせずdate関数などを使用することができそうだ。しかしPHP4の場合はそうもいかない。やはり一工夫しなければいけないのだ。

実はこのようなことを考えていて、PHP標準で備わっている関数をいくつか試したのだが、解せない結果となって驚いている。実行したのは以下のようなプログラムだ。

(1) print(date(“Y-m-d H:i:s”,gmmktime()));
(2) print(date(“Y-m-d H:i:s”,mktime()));
(3) print(date(“Y-m-d H:i:s”,gmdate(‘U’)));
(4) print(date(“Y-m-d H:i:s”,date(‘U’)));
(5) print(gmdate(‘Y-m-d H:i:s’));
(6) print(date(‘Y-m-d H:i:s’));

GMTで処理して、逐一タイムゾーンにあわせて処理すればよいはず、と考えて上記プログラムがどのような結果になるのか試してみたのだが、意外な結果となった。

まずこれをアメリカのサーバで試すと以下の結果となった。

(1) 2008-04-08 20:28:11
(2) 2008-04-09 00:28:11
(3) 2008-04-09 00:28:11
(4) 2008-04-09 00:28:11
(5) 2008-04-09 04:28:11
(6) 2008-04-09 00:28:11

日本のPHP5サーバで試すとこんな結果になった。
(1) 2008-04-09 13:28:11
(2) 2008-04-09 13:28:11
(3) 2008-04-09 13:28:11
(4) 2008-04-09 13:28:11
(5) 2008-04-09 04:28:11
(6) 2008-04-09 13:28:11

日本のPHP4サーバで試すとこんな結果になった。
(1) 2008-04-09 22:28:11
(2) 2008-04-09 13:28:11
(3) 2008-04-09 13:28:11
(4) 2008-04-09 13:28:11
(5) 2008-04-09 04:28:11
(6) 2008-04-09 13:28:11

個人的には(1)(3)(5)は全部同じ値になって、(2)(4)(6)は時差の分だけ考えれば同じ値となる、と思っていたのだが(1)(3)(5)が期待と大きく異なる結果となった。

サーバの設定とかいろいろあるとは思うが原因がわからない(ひょっとしたら大きな勘違いをしているのかもしれないが)。とりあえず全部で同じ値を返した(5)を使うのがよさそうだ。今後しばらくはこの関数をベースに時差調整のプログラムを書くことにしよう。

セッション絡みのエラー

とある既存システムの改修作業をしていて、こんなエラーメッセージに出会った。

Warning: Unknown(): Your script possibly relies on a session side-effect which existed until PHP 4.2.3. Please be advised that the session extension does not consider global variables as a source of data, unless register_globals is enabled. You can disable this functionality and this warning by setting session.bug_compat_42 or session.bug_compat_warn to off, respectively. in Unknown on line 0

これはどうもセッションへ変数とグローバル変数絡みのエラーのように見受けられる。「セッション機能はregister_globalsをonにしない限り、グローバル変数をデータのソースとして認識しません」。意味がよくわかんない。

まぁ、文章から判断して「register_globals」をonに設定するか、「session.bug_compat_42」か「session.bug_compat_warn」をoffにするか、で対応できる模様。

(1)「register_globals」をon
 動作した
(2)「session.bug_compat_42」をoff
 動作した
(3)「session.bug_compat_warn」をoff
 動作しなかった

それぞれをhtaccessファイルで設定してみたら、上記のような結果になった。ということで(2)を採用した。
これって結局はPHPのバグということなのかな。

どこでブラウザへの出力をしてしまったのかチェック

PHPの超便利関数を見つけたのでメモ。
headers_sent関数がそれ。

header関数を使おうと思ったら「headers already sent」なんていわれることがあるわけだが、この関数を使うと「もう何か出力したかどうか」をチェックすることができる。しかしこの関数のもっとすごいところは・・・どのファイルの何行目で出力があったのか教えてくれる点だ。
通常スクリプトを書くときは共通の処理を外部ファイルとするわけで、ある程度の規模になってくると当然ひとつの処理を実行するのにいくつものファイルをincludeすることが多々ある。こんなときに「headers already sent」なんていわれた日には、それこそ手当たり次第にrequireしているファイルを開いてチェックしなければならない。
しかしこの関数を使うと、どこで出力があったかわかってしまうのだ。実際には以下のように使用する。

bool headers_sent([string &file [, int &line]])

引数なしで使うと出力があったかどうかを判別するだけ。
第一引数を渡すと、すでに出力があった場合に、その第一引数の変数に出力があったファイル名を代入してくれる。さらに第二引数を渡すと、出力のあった行番号も代入してくれる。header関数で悩んでしまったら以下のようなスクリプトをheader関数の直前にでも入れてチェックすると、手っ取り早く出力箇所を特定できる。

if(headers_sent($fileName,$lineNumber)){
  exit(‘File Name: ‘.$filename.'(‘.strval($lineNumber).’)’);
}

まだまだ知らない関数がたくさんありそうだ。


守谷市(まちの情報ポータル) 無料アンケートレンタルjpForm.net