PHP's archives

FlashPlayer10で外部swfのロードに失敗する件

by ginriki | 11 月 5th, 2008 

2008/10にFlashPlayer10が正式リリースされました。

Flashの動作に若干変更が加えられたようで、バイト先のWebサイトで今まで正常動作していたFlashがFlashPlayer10では動かないという問題が起きました。

挙動を調べてみると、MovieClipLoader.loadClipで外部swfのロードに失敗してます。

外部swfとして指定するURLは実際にはPHPファイルを指しており、GETパラメータを変えることで
PHPファイルが出力するswfデータが変わるようになってます。

どうも、PHPファイルがswfデータを出力する際に指定してるHTTPレスポンスヘッダが良くないようなので、調べてみるとContent-disposition 指定がswfロードに失敗する原因でした。

そこで、下記のようにContent-dispositionの指定をコメントアウトしたら、FlashPlayer10でも
外部swfのロードに成功しました。

PHP:
  1. <?php
  2.   //...略
  3.   // header("Content-disposition: attachment;filename=" . $filename);
  4.   header("Content-type: application/x-shockwave-flash");
  5.   header("Content-length: " . filesize($filepath));
  6.   readfile($filepath);
  7. ?>

Content-dispositionの仕様はRFCで規定されています。その内容については、こちらによくまとめられています。
以下、一部引用

Content-Disposition レスポンスヘッダフィールドは、ユーザがその内容を
ファイルに保存したい場合にオリジンサーバが既定のファイル名を提案する事を
意味するように勧告されている。

Adobe的には、Flashでロードするswfコンテンツに、ファイルを保存する場合に指定するフィールドを指定するなってことなんでしょうかねえ。

その他FlashPlayer10の問題

FlashPlayer10になって、バージョン番号が1桁から2桁に増えたため、
バージョンチェックを適当に行うJavascriptを書いているサイトなどでは、
FlashPlayer10をFlashPlayer1と勘違いして、Flashが表示されない問題が起きてるようです。
http://bakera.jp/ebi/topic/3302

・・・なんとなく2000年問題を思い出しました。

追記

Adobe公式のFlashPlayer10におけるセキュリティ上の変更点解説の中で、
Content-Disposition: attachment 指定されたswfに対する挙動を変えた理由が書いてありました。

不特定の人がファイルをアップロード/ダウンロードできるアップローダサイト等で、
信頼できないユーザによってアップロードされたswfが、そのサイトドメイン上で実行される危険性を
なくすためだそうです。

画像やxmlファイルなどは、Content-Disposition: attachmentがHTMLレスポンスヘッダに
ついていても、今までと同様に動作するそうです。

PhpDocumentorによるドキュメント自動生成

by ginriki | 10 月 29th, 2008 

バイト先のWebアプリケーション用ソースコードの量が増えてきたので、
そろそろドキュメント生成ツールでも使おうということで、そのメモ。

バイト先では、PHP & symfonyフレームワークで書いてるんで、
ドキュメント生成ツールにはPhpDocumentor1を使います。

インストールはPEARを使えば楽勝。

 $ pear install PhpDocumentor

で、ドキュンメントの生成にはphpdocコマンドを使います。

 $ phpdoc -d apps,lib -t doc/phpdoc

これで、appsとlibディレクトリ以下すべてのphpファイルをHTMLドキュメント化して、
doc/phpdoc以下に吐き出してくれます。

ただ、デフォルトで生成するHTMLには、charset=iso-8859-1が指定されているため、
日本語コメントは文字化けしてしまいます。

対処法としては、HTML内にcharset指定のないHTMLドキュメントを生成するのが楽です2

 $ phpdoc -d apps,lib -t doc/phpdoc -o HTML:Smarty:PHP

この場合、デフォルトで生成したHTMLとはレイアウトが変わってしまいます。
デフォルトレイアウトが好きな方は、下記サイトを参考にテンプレートを書き換えてください。
http://je-pu-pu.jp/blog/archives/2006/04/phpdocumentor.html

参考文献

  1. 現バージョンは1.4.2です []
  2. とはいえ、webサーバのDefaultCharsetが間違っていたり、ブラウザの文字コード自動判定が失敗した場合は、文字化けが起きるので注意 []

Interactive ShellによるPHP対話入力補完

by ginriki | 9 月 24th, 2008 

この前の記事の続き。

rubyには、irbっていうinteractive modeあります。
irb実行中に、 require 'irb/completion' すると、TABでメソッド名等の補完ができるようになって大変便利です。

PHPのinteractive modeでも同じことができないかなと思い、
探してみたところ、PHP5.1 から標準で導入されてることがわかりました1

ただし、--with-readline付きでPHPをコンパイルした場合に限ります。2

yumでインストールしたPHPは、--with-readlineが付いてないので、3
PHP-cliだけソースコードからもう一度コンパイルしてみました。

環境は、CentOS 5.1です。

$ wget http://jp2.php.net/get/php-5.2.6.tar.gz/from/jp.php.net/mirror
$ tar xzvf php-5.2.6.tar.gz
$ cd php-5.2.6/
$ yum install libxml2-devel
$ yum install readline-devel
$ ./configure --prefix=~/local --disable-cgi --with-readline
$ make
$ make install
$ ~/local/bin/php -a
Interactive shell

php > get_class  [TAB]
get_class          get_class_methods  get_class_vars
php > get_class

うむ、便利だ。

  1. 補完機能付きのinteractive modeは、Interactive Shellっていう名称が付いてます。 []
  2. なんで、readlineを動的リンクするだけで使えるようにしてないんだー []
  3. 少なくとも、CentOS 5.1のyumでインストールしたPHPは--with-readlineはついてませんでした。 []

PHPインタラクティブモードの便利な使い方

by ginriki | 9 月 8th, 2008 

ブログに書くネタは結構あるんだけど、整理してから書こうとするとなかなか書けない・・・。

しょうがないんで、ネタは小出しに書くことにしよう。

PHPで何かのライブラリのオブジェクトを使う際、どんなメソッドがあるのかよくわからにことが多々あります。リファレンスを見りゃいいんですが、それが面倒な時は、PHP (CLI)のインタラクティブモードを使うと便利です。

インタラクティブモードの場合、statementが完成するたびに(セミコロンで区切られるたびに)処理が実行されるようです。

PHP:
  1. $ php -a
  2.  
  3. <?php
  4. $methods = get_class_methods(new XMLReader());
  5. print_r($methods);
  6. (
  7. [0] => close
  8. [1] => getAttribute
  9. ...
  10. [24] => expand
  11. )
  12. (Windowsだと Ctrl-Zで終了)
  13.  
  14. $

インタラクティブモードで、よく利用する関数は以下 (全部、リフレクション系関数)、

  • get_class_methods( mixed $class_name )  [クラスのメソッドを配列として返す]
  • get_object_vars ( object $object ) [オブジェクトのプロパティを配列として返す]
  • get_declared_classes(void) [定義済のクラスの名前を配列として返す]
  • get_defined_functions(void) [定義済の関数の名前を配列として返す]
  • get_defined_vars(void) [定義済の変数の名前を配列として返す]
  • get_loaded_extensions(void) [コンパイル/ロードされている全てのモジュールの名前を配列として返す]
  • get_extension_funcs ( string $module_name ) [あるモジュールの関数名を配列として返す]

ブラウザキャッシュの無効/有効

in PHP, cache, http
by ginriki | 7 月 14th, 2008 

ブラウザキャッシュは意識して設定しないと簡単に無効になったり有効になったりしてしまいます。
必ず無効にしたい or 必ず有効にしたいというポリシーがあるなら、きちんとした設定が必要です。

・ブラウザキャッシュを無効する方法
http://blog.knockoutmarch.com/2008/02/06/2323.html
より引用

HTML:
  1. <!-- メタタグに以下を記述。 -->
  2. <meta http-equiv="Pragma" content="no-cache">
  3. <meta http-equiv="Cache-Control" content="no-store">
  4. <meta http-equiv="Cache-Control" content="no-cache">
  5. <meta http-equiv="Expires" content="-1">
  6.  
  7. <!-- また、body内に以下を記述。 (for safari) -->
  8. <iframe style="height:0px;width:0px;visibility:hidden" src="about:blank">
  9.     this frame prevents back forward cache
  10. </iframe>

・ブラウザキャッシュを有効にする方法

そもそも、あるリンクをクリックした後のブラウザキャッシュ挙動には2パターンあることに注意
1. キャッシュしてあるコンテンツが新しくなっていないかwebサーバに問い合わせる。新しくなってなければキャッシュを使う。 (Last-Modified & If-Modified-Since & 304 status)

2. webサーバに問い合わせず、キャッシュをそのまま使う (Expires or Cache-Control: max-age)

しかも、ブラウザによってhttpレスポンス/リクエストヘッダのLast-Modified, Expiresなどの解釈や優先順位が違うことに注意。
http://labs.cybozu.co.jp/blog/kazuho/archives/2006/02/utilizing_cache.php
http://fdays.blogspot.com/2007/11/ie6cache-controlcache.html

以上を踏まえると、PHPコンテンツに対して、ブラウザキャッシュを上記2番の方法で一定時間有効にしたいときは下記のようにしとけば良さそう ( $interval 秒だけキャッシュを有効にしたいとする)

PHP:
  1. header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $interval ) . " GMT" );
  2. header( "Cache-Control: max-age=" . $interval);
  3. header( "Pragma: cache"); // no-cache以外にすればOK

キャッシュを無効にしたい場合も、有効にしたい場合も、同じようなhttpリクエスト項目を複数指定する必要があるのがだるい・・・

レンタルサーバsakuraにsymfonyをインストール

in PHP, symfony
by ginriki | 7 月 13th, 2008 

私はレンタルサーバとしてsakuraを使ってます。

PHPフレームワークのsymfonyをsakura上で動かしたかったので、インストール方法を探ってみたところ、すでにやってる方がいらっしゃったのでサクッとインストールできました。

http://ueblog.natural-wave.com/2008/06/15/sakura-symfony/

PHPはリファレンスカウントGCを使っているというお話

in GC, PHP
by ginriki | 3 月 20th, 2008 

バイト先でHyper Estraierを使った全文検索機能を実装する機会がありました。

そこで、EstraierPureライブラリを使って、PHPスクリプトを通じてHyper Estraierにテキストのインデックスを作成させるようにしたのですが、複数のテキストファイルをインデックス化する途中で下記PHPエラーが出てPHPプロセスが終了しました。

PHP Fatal error: Allowed memory size of 8388608 bytes exhausted (tried to allocate 22423 bytes) in /var/lib/Services/HyperEstraier/Utility.php on line 278

なんかメモリが足りなくなっとるらしい。

また、インデックス化に失敗したテキストファイルから実行再開すると、そのテキストのインデックス化に成功した後、別のテキストファイルをインデックス化する時に同様のエラーが出てプロセスが終了します。

以上の結果からメモリリークの疑いが強いのですが、EstraierPureは100%PHPコードなので、PHPのメモリ管理に原因がありそうです。

ということで、PHPのメモリ管理方法を調べてみたところ、リファレンスカウント方式のGCだと判明しました*1。今までTracing GC系だと思ってたよ・・・

現在、PHPのリファレンスカウントGCは循環参照しているオブジェクトは解放できません。おそらく、EstraierPureの中で循環参照があって、そのオブジェクトが解放されずにリークし、最終的にメモリが足りなくなるのでしょう。

PHP6からは、Pythonでも使われているCycle Collector*2が実装されるそうなので*3、今回の問題もPHP6を使えば解消します。

ってまだPHP6は正式リリースされてないし、今更PHP5から変えられるかーっていう状況。PHP5.3でもCycle Collectorが導入されるといいなあ。・・・結局リリース待ちなわけだけど。

*1:PHPカンファレンス2007 「PHPの今とこれから 2007」:http://www.php.gr.jp/seminar/20070901/data/phpcon070901a.ppt

*2:Cycle Collectorアルゴリズムの論文: http://www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf

*3:PHPカンファレンス2007 「PHPの今とこれから 2007」:http://www.php.gr.jp/seminar/20070901/data/phpcon070901a.ppt

ブログで紹介した商品

  • Image of デバッガの理論と実装 (ASCII SOFTWARE SCIENCE Language)