カテゴリー : 2007年 4月

PHP 用デバッガ


PHP: Advanced PHP Debugger(PHP) (php.net)

(PHP 公式ドキュメントより引用)
APD は進化した PHP デバッガです。PHP コードのプロファイリングや デバッグの機能を提供すること、また完全なスタックトレースを出力する 機能を提供することを目的として作成されています。

 
インストール手順:

$ pecl install apd
 
$ cat >> /path/to/php.ini ← エディタ等で以下の内容を php.ini に追加
 
zend_extension = /path/to/apd.so ← APD モジュールのフルパス(zend_extention 指定の場合は相対パスは不可)
apd.dumpdir = /tmp/apd_log ← APD での解析結果の出力先フォルダ
apd.statement_tracing = 0 ← 行単位トレースをする場合 1 にする。これを有効にすると出力が冗長になります。

Windows 用のコンパイル済み PECL モジュールは http://snaps.php.net/win32/ からダウンロードできます(ファイル名 pecl からはじまるもの)。
 
 
利用方法:
デバッグをしたいスクリプトの一番始めに

apd_set_pprof_trace();

を呼ぶと PHP デバッガが利用できます。
 
PHP デバッガによる結果は php.ini の apd.dumpdir で指定したディレクトリに

pprof.<プロセスID>.<通し番号>

のような形式で保存されます。
 
そのまま読めなくはないですが、読みやすい形に整形するために pprofp コマンドを利用することができます(APD に付属しています)。
 
– かかった処理時間の多い関数順に並びかえて出力する

pprofp -R [ファイル名]

– コールツリー(関数の実行順序)を調べる

pprofp -t [ファイル名]

– (上の二つ)+(ビルトイン関数は含めない)+(コールツリーの横に経過時間を表示)

pprofp -Rtci [ファイル名]

 
その他のオプションについては「APD の pprofp 引数一覧(pprofp -h)」を参照してください。
  
Windows 用の pprofp についてはAPD のソースに含まれるものが使えるのですが、そのままの形では使いにくいため、実行用のバッチファイルを作成したものを置いておきます。
 
Windows 対応 pprofp をダウンロード
(APD 1.0.1 ソース付属のものから作成)
 
- pprofp
- pprofp.bat
- pprof2calltree
- pprof2calltree.bat
の 4 ファイルが入っています。
この4ファイルをそのまま php.exe と同じ場所など適当にパスが通る場所に置いてご利用ください。
 
参考:
- APD(公式 PHP マニュアル)
- cles::blog

PHP で OpenSSL


PHP で OpenSSL 関数を使ってみたので、定番のハローワールドを置いておきます。


OpenSSL 関数で暗号化、復号化を行うには、
暗号化、復号化に必要な鍵のペアを PEM 形式で生成する必要があります。
1. パスワード付き秘密鍵 private_password.pem を生成

$ openssl genrsa private_password.pem -des3 1024

2. (1)からパスワードのない秘密鍵 private.pem を生成

$ openssl rsa -in private_password.pem -out private.pem

3. (1)を削除

$ rm private_password.pem

4. (2)から公開鍵 public.pem を生成

$ openssl rsa -in private.pem -pubout -out public.pem

 


openssl_public_encrypt(), openssl_private
…公開鍵で暗号化し、秘密鍵で復号化します。
つまり、誰からでも暗号化して送れるが、内容を読めるのは秘密鍵を持っている人だけになるため、通信経路での傍受が不可能になり、内容が外部に洩れる心配がないので機密性が高くなります。
ただし誰でも暗号化できるので全く別の内容へのかいざんは可能であり、必ずしも信頼性はありません。
 

// 暗号化に使う公開鍵
$public_key = file_get_contents(“public.pem”);
 
// 暗号化する文字列
$message = “Hello, world!!”;
 
// 暗号化データの結果格納先
$encrypted = “”;
 
// 暗号化実行
if(!openssl_public_encrypt($message, $encrypted, $public_key)){
    echo “暗号化に失敗\n”;
    die;
}
 
// 暗号化結果を表示(そのままでは表示できないのでBASE64エンコードする)
echo “暗号化データ: \n “.base64_encode($encrypted) . “\n”;
 
$message = null;
// ———————
// 復号化
// ———————
// 暗号化に使った公開鍵に対応する秘密鍵
$private_key = file_get_contents(“private.pem”);
 
// 復号化結果格納先
$result = “”;
 
// 復号化実行
if(!openssl_private_decrypt($encrypted, $result, $private_key)){
    echo “復号化に失敗\n”;
    die;
}
 
// 結果出力
echo “復号化結果: \n ” . $result . “\n”
     ”BASE64:\n” . base64_encode($result).”\n”; // 比較用

結果:

暗号化データ:
FmPaJ9jOmjl/mK249NqUPzYjPUHsmFkSuNyp0SMlgW1c9RRzmKXBjcRhXH/5fwTxItAcE8/
AlZkjYo27XvGR7a3hdXxgaXbk4VORdRRk8LJXKB+xlVzF48OlHJ5lKUrXUdfr2BFtx53/IH
431mBlYWDhf+KyW608eKAA0T34qRY=
復号化結果:
Hello, world!!
BASE64:
SGVsbG8sIHdvcmxkZmc=

 


openssl_private_encrypt(), openssl_public_decrypt() の例:
…秘密鍵で暗号化し、公開鍵で復号化します。
つまり、鍵の所有者しか暗号化できないので中身のかいざんも、経路での差し替えも不可能になり、内容についての信頼性が高くなります。
ただし誰でも読めるため、内容の機密性はありません。
 
秘密鍵、公開鍵の使い方を逆にする以外は上の例と全く同じです。

// 暗号化に使う秘密鍵
$private_key = file_get_contents(“private.pem”);
 
// 暗号化する文字列
$message = “Hello, world!!”;
 
// 暗号化データの結果格納先
$encrypted = “”;
 
// 暗号化実行
if(!openssl_private_encrypt($message, $encrypted, $private_key)){
    echo “暗号化に失敗\n”;
    die;
}
 
// 暗号化結果を表示(そのままでは表示できないのでBASE64エンコードする)
echo “暗号化データ: \n “.base64_encode($encrypted) . “\n”;
 
$message = null;
// ———————
// 復号化
// ———————
// 暗号化に使った秘密鍵から作られた公開鍵
$public_key = file_get_contents(“public.pem”);
 
// 復号化結果格納先
$result = “”;
 
// 復号化実行
if(!openssl_public_decrypt($encrypted, $result, $public_key)){
    echo “復号化に失敗\n”;
    die;
}
 
// 結果出力
echo “復号化結果: \n ” . $result . “\n”
     ”BASE64:\n” . base64_encode($result).”\n”; // 比較用

結果:

暗号化データ:
CII4EW6LQBq0faVJxshUfk9VCdFsE66DnQPVdsLbvfq7tNCMzAIZbZA3GBxHu4cG2TqvOnn
xN9C9S9IgBIbulfj4c90IkrYtHQGt3RHAJBYiAgfmzCKbMr2cPJhZwkMUkjczXeaOD1htFJ
TL9e2RKsrWBVFIaiG1JgFk2Jyd928=
復号化結果:
Hello, world!!
BASE64:
SGVsbG8sIHdvcmxkZmc=


openssl_open(), openssl_seal() の例:
…同報メールのように、別々の公開鍵を持つ複数の相手に対して同じデータを暗号化して送る場合に使います
 
- 送信側(送信先1の公開鍵 user1_public.pem と送信先2の公開鍵 user2_public.pem を持っているとする. 自分の鍵は必要としない)

// 送り先の公開鍵一覧。
$pubkeys = array(
        file_get_contents(“user1_public.pem”),
        file_get_contents(“user2_public.pem”),
   );
 
// 送る内容
$real_message = “Hello, world!!”;
 
// 暗号化データの結果格納先
$encrypted = “”;
// 復号化に必要なエンベロープキーの格納先。$pubkeys の数だけ格納される。
$ekeys = array();
 
if(!openssl_seal($real_message, $encrypted, $ekeys, $pubkeys)){
    echo “暗号化に失敗しました\n”;
    die;
}
 
// 出力. エンベロープキーと暗号化データについてはバイトコードなので、表示のために BASE64 エンコードしています。
for($i = 0; $i < count($pubkeys); $i++){
    echo “送り先番号: $i\n” .
         ”使った公開鍵: \n” .
         $pubkeys[$i] .”\n” .
         ”対応するエンベロープキー: \n” .
         base64_encode($ekeys[$i]) .”\n” .
         ”暗号化されたデータ: \n” .
         base64_decode($encrypted);
}

 
受信側(送信側からエンベロープキー $ekey と暗号化データ $encrypted を受け取っているとする):

// 送信側が暗号化に使った公開鍵(user1_public.pem)に対応する秘密鍵
$private_key = file_get_contents(“user1_private.pem”);
 
// 結果格納先。復号化した文字列が入る
$message = “”;
 
if(!openssl_open($encrypted, $message, $ekey, $private_key)){
    echo “復号化に失敗しました\n”;
    die;
}
 
// 結果表示
echo $message . “\n”;

 
送信側結果は次のようになります。


送り先番号: 0
使った公開鍵:


-BEGIN PUBLIC KEY—–
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzZFcKoXbuWzGvlv++7Y05/jBh
nYC+PM67v3H2I5pYV8tUz/6GqsG5afFgLJYoWPEd3KYXHb46B0GxEOydOTuTLEcv
+gVr6v4khgOgjxOcM1hlv+EvAThm1lIY1Y5lbTv+MEUyrGm7SDk8iUaHF6OBcQp3
NzkO6j3+YrsC8znXPwIDAQAB


-END PUBLIC KEY—–
対応するエンベロープキー:
lu4ON1xW2hrrS1jXfav6mjWD32+Iuz1Jk20vhDRmSsfEp3vXuij7Z0R/ClhpgE9MxDJADiU
LS/CCUPuiZ3wUDErccfUFGGgcBKFsoXOgcX/vSfhi8yCYu3BOD+h5fOMmg/ZVe6NMjCVq50
D8yce5gPoypg2LHijse+0R1bPil8o=
暗号化されたデータ:
0qf1eWjjaHkiqRFKijam


送り先番号: 1
使った公開鍵:


-BEGIN PUBLIC KEY—–
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCzZFcKoXbuWzGvlv++7Y05/jBh
nYC+PM67v3H2I5pYV8tUz/6GqsG5afFgLJYoWPEd3KYXHb46B0GxEOydOTuTLEcv
+gVr6v4khgOgjxOcM1hlv+EvAThm1lIY1Y5lbTv+MEUyrGm7SDk8iUaHF6OBcQp3
NzkO6j3+YrsC8znXPwIDAQAB


-END PUBLIC KEY—–
対応するエンベロープキー:
Hpv4sE4M4RGnbEAPMqFKxOOPDJG0hpWBoIXCe8xUaIzx/dskUQ/a+T4jYyw+RRGh11wlzZt
CJOjzkP3pbafc0ymElAzS8RFAne7bVuCJh44UF55thAUo0nZVo1/2Wz9HuTDL23AYwc+jyh
U/k24bpGh81VJl5I7IkItoDeiS70Y=
暗号化されたデータ:
0qf1eWjjaHkiqRFKijam

受信側結果:

Hello, world!!

 
参考:
- OpenSSL 関数(php.net)
 PHP マニュアルの OpenSSL 関数についての項目。
- OpenSSL 公式サイトの日本語訳サイト
更新は不定期のようですが、openssl のマニュアルが参考になります。
公式はこちらです。

PHP の OpenSSL 関数で DSA(DSS) を使うとエラー


PHP で OpenSSL 関数を使って DSA 公開鍵での暗号化を行おうとしたところ

Warning: openssl_public_encrypt(): key type not supported in this PHP build!

のようにエラーがでてしまった。
 
PHP のソースを追ってみたところ、どうやら PHP では現在のところ DSA の秘密鍵/公開鍵についてはサポートされていないようです。
 
php-5.2.1/ext/openssl/openssl.c:

PHP_FUNCTION(openssl_public_encrypt)
{
// ** 省略 **
        switch (pkey->type) {
                case EVP_PKEY_RSA:
                case EVP_PKEY_RSA2:
                        successful = (RSA_public_encrypt(data_len,
                                                (unsigned char *)data,
                                                cryptedbuf,
                                                pkey->pkey.rsa,
                                                padding) == cryptedlen);
                        break;
                default:
                        php_error_docref(NULL TSRMLS_CC, E_WARNING, “key type no
t supported in this PHP build!”);

        }
// ** 以下略 **

のように、
openssl_open(), openssl_seal(), openssl_private_decrypt(), openssl_private_encrypt(), openssl_public_encrypt(), openssl_public_decrypt()
など、暗号化/復号化を行う関数では RSA か RSA2 の時の処理しか実装されていないようです。
 
参考:
- OpenSSL 関数(php.net)

他のアプリケーションのキーイベント等メッセージ監視


SetWindowsHookEx() という API を使うと、他のウィンドウに対するさまざまなイベントを監視することができます。
 
デフォルトの動作であるローカルフックだとそのアプリケーションに対するイベントしかとれません。他のウィンドウのイベントをとるキーロガー的な動作を実現するにはグローバルフックにする必要があります。
グローバルフックを行うには、DLL のハンドルを引数として渡さなければならないため、DLL で実装しなければなりません。
 
参考:
- SetWindowsHookEx()(MSDN)
- 他のアプリケーションのメッセージを監視(doumo.jp)

オープンソースのアンケートシステム


- opensurveypilot
PHP+MySQL で動作するオープンソースのアンケートCGI。日本語にも対応しているようです。
動作するには、–with-mysql, –with-bz2, –with-zlib, –with-mhash, –with-mcrypt オプションをつけてコンパイルした PHP プログラム(PHP5にも対応)が必要です。
 
- UCCASS
こちらも同様に、PHP+MySQL なアンケートCGIです。
 
参考:
- MOONGIFT オープンソース opensurveypilot 高性能アンケートソフトウェア
- オープンソースメモ

3Dでのカメラ設定


デバイスのプロパティ Device.Transform.View に、Matrix インスタンスを指定することで設定可能。

// 左手座標の視点マトリクスを構築する
public static Matrix Matrix.LookAtLH(
    Vector3 cameraPosition, // カメラの位置。
    Vector3 cameraTarget, // カメラの見ている位置。
    Vector3 cameraUpVector // カメラの頭の方向(例: 正立は new Vector3(0f,1f,0f), 倒立は new Vector3(0f,-1f,0f)). 方向を指定する目的なので、ベクトルの大きさは問わない
)

例:

Device device;
 
// ..中略..
 
// 座標 (0,0,-10) から正立状態で 座標 (0,0,0) を見る
device.Transform.View = Matrix.LookAtLH(
    new Vector3(0f, 0f, -10.0f),
    new Vector3(0f,0f,0f),
    new Vector3(0f,1f,0f) );
    
);

実際に表示するには、さらに 3D から 2D への投影の設定を行う必要があります。
この設定を行うには Device.Transform.Projection プロパティに Matrix インスタンスを指定します。

// 左手座標のパースペクティブマトリクスを構築する
public Matrix Matrix.PerspectiveFovLH(
    float fieldOfViewY, // (カメラから見て) Y軸方向の視野。ラジアンで指定する。(1/4 π == 45度 くらいが適切)
    float aspectRatio, // 2Dに変換後の画面のアスペクト比(横幅 ÷ 縦幅) (例: 高さも幅も同じなら 1.0f, 汎用:(float)device.Viewport.Width / (float)device.Viewport.Height )
    float znearPlane, // (カメラから見た)可視範囲のうち、最も近い距離(1.0f)
    float zfarPlane // (カメラから見た)可視範囲のうち、最も遠い距離 (100.0f)
);

例:

Device device;
// …中略…
 
device.Transform.View = … // 略
 
// 上下角度45度で奥行き 1.0f 前方から 100.0f 前方までの範囲を表示する
device.Transform.Projection = Matrix.PerspectiveFovLH(
    (float) Math.PI / 4,
    (float) device.Viewport.Width / device.Viewport.Height,
    1.0f, 100.0f );

ちなみに、Matrix.PerspectiveForLH ではなくて Matrix.PerspectiveFovLH なので注意してください。for でなく fov です。
 
参考:
- チュートリアル3 行列の使い方(MSDN Direct3D)
- Matrix.LookAtLH メソッド(MSDN)
- ビュー行列を設定する(MSDN)
- Matrix.PerspectiveFovLH メソッド

左手座標系での X, Y, Z 軸それぞれの正の方向


左手のそれぞれの指を直角にして、
Y 軸(人差指): 上向きが正の方向
X 軸(親指): 右向きが正の方向
Z 軸(中指): 奥が正の方向
となる。

UDP 通信を簡単に行うには


Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SendTo(…);
socket.ReceiveFrom(…);

のように System.Net.Sockets.Socket を使う方法もありますが、System.Net.Sockets.UdpClient を使うと簡単に UDP 通信を行うことができます。

// 必要な宣言
using System.Net;
using System.Net.Sockets;

private void DoSend(){
// 送信元とするポート
int localPort = 1234;
 
UdpClient client = new UdpClient(localPort);
 
// UDP パケットの送信先
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(“192.168.2.1″), 5678);
// 送信する内容(バイト配列で指定)
byte[] msg = Encoding.ASCII.GetBytes(“Hello, world\n”);
 
// 送信する
client.Send(msg, msg.Length, remoteEP);
// // 同じ場所に送信し続ける場合は次のように接続と送信をわけてもよい
// client.Connect(remoteEP);
// client.Send(msg, msg.Length);
}

以下のようにすれば、受信ができます。

private void DoReceive(){
int localPort = 5678;
UdpClient client = new UdpClient(localPort);
 
// 送信元。任意のIPアドレス、任意のポートから許可
IPEndPoint remoteEP = IPEndPoint(IPAddress.Any, 0);
 
// 受信するまで待ち続ける
byte[] res = client.Receive(ref remoteEP);
 
// バイト配列から ASCII 文字列に変換して表示
System.Console.Write(
   ”送信元:” + remoteEP + “\n” + // 実際に送信が行われた IPアドレス, ポートが格納されているので一緒に表示
   ”受信内容:”+Encoding.ASCII.GetString(res)
  );
}

TCP 版の TcpClient もあります。
 
参考:
- UdpClient クラス (System.Net.Sockets)
- TcpClient クラス
- Socket クラス(System.Net.Sockets)

enum 型の一覧を得る


列挙型のすべての要素を得るには、Enum.GetValues() や Enum.GetNames() を使います。

private enum UserType {
    Guest = 1,
    Member,
    VIP,
    Refused = 99
}
 
private void SomeMethod(){
    foreach(UserType type in Enum.GetValues(typeof(UserType))){
        System.Console.Write(type + ” = ” + ((int) type)+ “\n”);
    }
}

結果:

Guest = 1
Member = 2
VIP = 3
Refused = 99

C# で DirectX


.NET Framework で DirectX を利用するには、Microsoft DirectX SDK を利用するのが一般的です。
 
- マネージコードの使用に関するヒント(MSDN)
- Direct3D のチュートリアル(MSDN)
参考になりますが、とくにチュートリアル3以降、部分解説になっているので完全に動作させるにはSDK 付属のチュートリアルのソースの参照が必須です。
サンプルソースは

(SDK ルート)\Samples\Managed\Direct3D\Tutorials

にあります。