cakeの便利なライブラリを使ってみよう(Socket編)

環境はCakePHP 1.2RC3です。

結構前にcakeの便利なライブラリを使ってみよう(xml編)というエントリーを書いたのですが、今回はソケット編です。
Cakephpには便利なライブラリがcake/libs以下にあります。


例えば、http_socketであれば、簡単にhttp通信が行えます。http_socketに関する詳しい記事がありました。
http://www.syuhari.jp/blog/archives/825


http通信ではなく、他のプロトコルの通信が行いたい場合(レアケースだと思うけど)、socketライブラリを使うと簡単にできます。
今回はcakephpコマンドラインからシェルで動かして、IRCサーバと通信する例です。
Cakephpのシェルに関する記事は下記に書いたので参照下さい。
cakePHPのコマンドラインプログラムでバッチ処理


まずは単純にサーバにConnect, Disconnectする例です。
app/venders/shells/cakeircbot.php

<?php
class CakeircbotShell extends Shell {


    var $uses = null;

    function main() {

		$ircserver = 'irc.freenode.net';
		$port = 6667;
		$channel = '#cakephp-jp';
		$nick = 'ichibot';
		$username = 'ichibot';

		$this->config = array(
			'persistent'	=> false,
			'host'			=> $ircserver,
			'protocol'		=> 'tcp',
			'port'			=> $port,
			'timeout'		=> 3000
		);

		uses('Socket');

		$sock = new CakeSocket($this->config);

		$sock->connect();

		$sock->disconnect();

    }

}

?>

接続するサーバのアドレスやポート番号をconfigにセットします。
uses('Socket')でcake/libs/socket.phpを呼び出しています。socket.phpを見ると、クラス名はCakeSocketになっているため、new CakeSocketでインスタンスを生成します。
メソッドconnectで接続、disconnectで切断です。これ以外にサーバへデータを送信するwrite, データを受信するreadメソッドがあります。


実行は、下記をコマンドラインから実行します。-app /home/hoge/www/appは付けなくても動くかもしれません。

/home/hoge/www/cake/console/cake -app /home/hoge/www/app cakeircbot

さて、socket通信でIRCサーバに接続するところまでできました。次は、IRCサーバのチャンネルにJoinして、IRCでのすべての発言を取得し、その内容に応じて応答を返すプログラムです。

<?php
class CakeircbotShell extends Shell {


    var $uses = null;

    function main() {


		$ircserver = 'irc.freenode.net';
		$port = 6667;
		$channel = '#cakephp-jp';
		$nick = 'ichibot';
		$username = 'ichibot';

		$this->config = array(
			'persistent'	=> false,
			'host'			=> $ircserver,
			'protocol'		=> 'tcp',
			'port'			=> $port,
			'timeout'		=> 3000
		);

		uses('Socket');

		$sock = new CakeSocket($this->config);


		$sock->connect();

		/* サーバへのメッセージは必ず\r\nで終わらせる */
		$sock->write("NICK " . $nick . "\r\n");
		$sock->write("USER $username $username $username $username" . "\r\n");
		$sock->write("JOIN " . $channel . "\r\n");



		while( 1 ){
			$value = $sock->read();
			if( $value == false ){

				if( $sock->connected != 1){
					exit;
					break;

				}else{
					continue;
				}

			}


			if( preg_match("/:.*hoge/i", $value) ){
				$sock->write('PRIVMSG ' . $channel . ' : '  . 'hogeって言ったよね?' . "\r\n");

			}elseif( preg_match("/:.*> $nick/i", $value) ){
				$sock->write('PRIVMSG ' . $channel . ' : '  . '呼んだ?' . "\r\n");


			
			}


			echo $value;
		}


		$sock->disconnect();

    }

}

?>

まず、connectした後に、writeでIRCサーバにChannelやNickの情報を送ります。送信後は、readでサーバからの応答を受け取ります。ここは永久ループでreadし続けます(終了は Ctl + c)

readした内容がfalseの場合はコネクションが切れている場合があるので、

$sock->connected

を使ってコネクションが切断されているかチェックします。

誰かがIRCで発言すると、$valueにその内容がセットされます。
下記の箇所では、誰かの発言の中にhogeという文字が含まれていたら、このボットプログラムが「hogeって言ったよね?」という応答を該当チャンネルで発言します。
それ以外に、ボットの名前を誰かの発言の中で見つけると、「呼んだ?」と返します。

if( preg_match("/:.*hoge/i", $value) ){
	$sock->write('PRIVMSG ' . $channel . ' : '  . 'hogeって言ったよね?' . "\r\n");

}elseif( preg_match("/:.*> $nick/i", $value) ){
	$sock->write('PRIVMSG ' . $channel . ' : '  . '呼んだ?' . "\r\n");


こんな感じで、お手軽にsocket通信できます。
今回はIRCでしたが、そのプロトコルに従えば何でも通信できますし、サーバ側のプログラムを自分で書けばオリジナルプロトコルも実現可能です。