カテゴリー
PHP

Phpstorm6 リモートデバッグ

Vagrant + Chef で開発環境を簡単に立ち上げることができるようになったので、Phpstorm6を使ってリモートデバッグできる環境を整えてみたいと思います。

Vagrant+ChefでApache、PHP、MySQLがインストールされていることが前提となりますが、何らかの仮想サーバやリモートサーバでもほぼ同じ作業でOKだと思います。

1.はじめに

まずはサーバにxDebugをインストールしますが、Chefでやっちゃいます。

chef-repoのcookbooksにあるレシピに、definitionsディレクトリがあるので、そこにpeclのDSLを定義します。

上記の例では/chef-repo/cookbooks/myrecipe/definitionsにpecl.rbというファイル名で、下記内容のファイルを作成します。

define :pecl do
    bash "install-#{params[:name]}" do
        code <<-EOC
            pecl install #{params[:name]}
        EOC
        not_if 'pecl list xdebug'
    end
end

次に、レシピファイル(今回の例では/chef-repo/cookbooks/myrecipe/recipes/default.rb)に次の一文を追加します。

pecl 'xdebug'

これで vagrant up すると自動的にxdebugがインストールされます。

2.php.iniの編集

xdebugを有効化するため、php.iniを編集します。

ウチの環境では下記のような設定をphp.iniに追加しました。

[XDebug]
zend_extension = "/usr/lib/php/modules/xdebug.so"

xdebug.collect_params = 4
xdebug.default_enable = "On"
xdebug.profiler_append = 0
xdebug.profiler_enable = 1
xdebug.profiler_enable_trigger = 0
xdebug.profiler_output_dir = "/tmp"
xdebug.profiler_output_name = "xdebug_profile.%R::%u"
xdebug.remote_autostart = 1
xdebug.remote_enable = 1
xdebug.remote_handler = "dbgp"
xdebug.remote_host = 192.168.xxx.xxx
xdebug.remote_log = "/val/log/xdebug.log"
xdebug.remote_port = 9000

Chefを使っているなら、templateかcookbookfilesで処理するといいでしょう。

だがしかし!ここでいろいろハマりました。

「xdebugのマニュアル読みましょうw」というのはひとまず置いといて。

・xdebug.remote_autostart
これが0だと、スクリプトが走っても仮想サーバ側のデバッグ情報がホストOS側に投げられませんでした。

以前、xampp環境でxdebugを使った時はここが0でも上手く動いていたのですが...。

ちなみに、マニュアルにはここが0だと、GETかPOSTまたはCookieが投げられたらデバッグ情報投げるよ、1だと関係なく毎回デバッグ情報投げるよ、みたいに書かれているのですが、ウチの場合は期待通りに動作してくれませんでした。

・xdebug.remote_host
インターネット上の情報を拾ってきて設定を進めていて、最初は下記のような記述にしていたのですが...、

;xdebug.remote_host = "localhost"
xdebug.remote_connect_back = 1

これだとホストOS側にデバッグ情報が送信されてきませんでした。

ここも、以前xamppで環境を作ったときはremote_connect_back = 1で上手く動作していたのですが...。

あと、ウチの環境だけかどうかわかりませんが、xdebugが動作したとき(PHPスクリプトが実行されたとき)にデバッグクライアント(今回の場合はPhpstorm)がデバッグポートを待ち受けていない場合、PHPの実行が非常に遅くなりました。

いろいろ試して気が付いたのですが、どうやらxdebugがデバッグクライアントのポートに接続しにいっても接続先がないため、timeoutするまで延々スクリプトの実行が遅延しているようです。

3.Phpstorm側の設定

Ctrl+Alt+SでPhpstormの設定画面を開きます。

Project SettingsのPHP->Debugを開きます。

中央付近のXdebugのDebug portに9000を設定します。

環境によってはこれだけでOKなのですが、ウチの場合はサーバーと作業フォルダのマッピングが必要でした。

4.ファイルマッピング

完全にローカルPCだけで完結している開発作業ならともかく、Vagrantで立てた仮想サーバとホストOSといった具合にサーバマシンと開発マシンが分かれている場合、サーバ側のファイルが、ローカルのどのファイルに相当するのかをマッピングしてやる必要があります。

まずはCtrl+Alt+Sで設定画面を開きます。

Project SettingsのPHP->Serversを選択。

サーバー情報がまだ追加されてない場合は「+」をクリック、既に仮想サーバが追加されている場合はそれを選択します。

Name: 適当にサーバ名を入力
Host:仮想サーバのIPかアドレス
Port:80(デフォルト)
Debugger:Xdebug
Use path mappings:チェックを入れて、ディレクトリツリーを表示します

File/Directoryに現在のローカル(ホストOS)側のプロジェクトで使用しているディレクトリやファイルが表示されるので、それらがサーバ側のどれに該当するかをAbsolute path on the serverに指定します。

ファイルを1個づつ指定しなくても、ディレクトリ構造が同じであればトップディレクトリをマッピングすればOKですが、もし仮想サーバと作業フォルダで構造が異なっている場合はサブディレクトリ単位またはファイル単位で指定する必要があります。

Absolute path on the serverは、そう書かれているように仮想サーバ(Vagrant)側の絶対パスで指定します。

例えばプロジェクトの作業フォルダが c:\project で、そこにindex.phpを作成したとします。

CentOSなどでyumからApacheをインストールした場合のデフォルトは/var/www/htmlがWebサーバのドキュメントルートになるはずなので

c:\project/var/www/html にディレクトリ単位でマッピングするか、

c:\project\index.php/var/www/html/index.php のようにファイル単位で個別にマッピングするかのどちらかになりますが、多くの場合はディレクトリ単位で指定する方がお手軽に済むでしょう。

以上で設定は完了になります。

あとは、Phpstorm側のリモートデバッグをOn(リモートデバッグスイッチをクリック)にしてブラウザから仮想環境のPHPスクリプトを実行すればブレークポイントで停止するはず!

カテゴリー
cakephp cakephp2 PHP

Cakephp2.xでのBlackHoleまとめ

CakePHPでSecurityコンポーネントを使っていると、時折発生するBlackHole。
発生時はエラーログを確認するしか詳細な原因がわからないのですが、多くの場合は以下のどれかに該当するのではないでしょうか。

  • CSRFチェックのトークンがUseOnceなのにフォーム送信後にブラウザの「戻る」とか押してからフォームを再送した。
  • フォームを生成してから30分過ぎた。(トークンのデフォルト有効期限が30分)
  • JavaScriptでHidden項目を書き換えた。
  • JavaScriptで入力項目を追加したり取り去ったりした。

ブラウザの戻るを想定する場合はトークンを複数回使えるように設定しなければいけません。(ただし、セキュリティー的には若干低下します)

複数回使いまわせるようにするにはControllerのbeforeFilterなどで以下のようにcsrfUseOnceをfalseにします。

$this->Security->csrfUseOnce = false;

また、トークンのデフォルト有効期限が短すぎると判断される場合はbeforFilterで

$this->Security->csrfExpires = '+1 hour';

とするか、または$componentsの設定で以下のように設定すると良いでしょう。

public $components = array(
    'Security' => array(
        'csrfExpires' => '+1 hour'
    )
);

そもそもCSRF対策不要だよ!ってアプリケーションでは、

public $components = array(
    'Security' => array(
        'csrfCheck' => false
    )
);

として、CSRF対策機能をばっさり無くすことも可能ですが、セキュリティー的に問題が残るため、多くの場合はおすすめできません。

あと、JavaScriptでフォームの中身を変更すると、ことごとくBlackHoleに吸い込まれる事がありますが、Securityコンポーネントを有効にするとデフォルトでついてくる機能「FormTamperingPrevention」のおかげです。

Cakeがフォームを生成した時にフォームの内容をまるごとハッシュ化しており、フォーム送信時にフォームの内容に差異が生じていると、フォームを不正に改変されたとみなしてBlackHoleに吸い込んでくれる機能です。

ただし、JavaScriptでHiddenに値をセットしたり、Ajaxを使う場合に項目を動的に追加したりなんかしてもBlackHoleに吸い込んでくれるので、そのような場合は以下の何れかの対策が必要です。

FormTamperingPreventionをまるっと無効にする場合はbeforeFilterで以下のように指定します。

$this->Security->validatePost = false;

また、特定のフィールドだけFormTamperingPreventionの対象外としたい場合(JavaScriptで値をセットするhidden項目など)は、ViewのFormHelperを用いて以下のように指定することで回避できます。

$this->Form->unlockFields('Model.field_name');

 

BlackHoleに吸い込まれた時の処理

BlackHoleに吸い込まれた場合、デフォルトではDebugモードにもよりますが、プロダクションモードでは真っ白なページが表示され、404エラーが返されてしまいます。
では実際に何らかの問題が生じてBlackHoleに吸い込まれるべくして吸い込まれた場合、どのようにハンドリングすればスマートでしょうか。

ここはひとまずCakePHPのCookBookにのっとってblackholeメソッドを用意し、そちらに処理を任せるようにしましょう。

public function beforeFilter() {
    $this->Security->blackHoleCallback = 'blackhole';
}

public function blackhole($type) {
    // handle errors.
}

上記はCookBookからの引用になります。またblackholeメソッドのパラメータ$typeは以下の値をとるようですが、内容はCookBookのほぼ直訳になります。

  • ‘auth’ フォームバリデーションエラーまたはコントローラ/アクションの不一致によるエラー。
  • ‘csrf’ CSRFエラー
  • ‘get’ HTTPメソッド制限違反
  • ‘post’ HTTPメソッド制限違反
  • ‘put’ HTTPメソッド制限違反
  • ‘delete’ HTTPメソッド制限違反
  • ‘secure’ SSLメソッド制限違反

なので、blackholeメソッド側で$typeを参照し、エラー内容に沿ったメッセージを表示してあげると良さそうです。

カテゴリー
PHP

PHPで形態素解析

サイト内検索を実装する際、サーバの都合でMecabをインストールできなかったりする場合もある。
そんな時に重宝するのがPHPでできた形態素解析器「Igo-php」です。

元々はJavaで作られていたMecab互換を目指した形態素解析器で、それがさらにPHPに移植されたというもの。

PHP版には辞書ファイルを作成する機能が無いようなので、Mecabの辞書をダウンロードしてJava版のIgoで辞書ファイルをコンパイルする必要があります。

参考にしたのはこちらのサイト