<?php
/**
 * -----------------------------------------------------------------------------
 *
 * SyL - Web Application Framework for PHP
 *
 * PHP version 4 (>= 4.3.x) or 5
 *
 * Copyright (C) 2006 k.watanabe
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * -----------------------------------------------------------------------------
 * @package   SyL
 * @author    k.watanabe <k.watanabe@syl.jp>
 * @copyright 2006 k.watanabe
 * @license   http://www.opensource.org/licenses/lgpl-license.php
 * @version   CVS: $Id: SyL_Socket.php,v 1.7 2006/12/23 11:13:09 seasonstream Exp $
 * @link      http://www.syl.jp/
 * -----------------------------------------------------------------------------
 */

define('SYL_DEBUG', false);

/**
 *  ソケットクラス
 *
 * @package   SyL
 * @author    k.watanabe <k.watanabe@syl.jp>
 * @copyright 2006 k.watanabe
 * @license   http://www.opensource.org/licenses/lgpl-license.php
 * @version   CVS: $Id: SyL_Socket.php,v 1.7 2006/12/23 11:13:09 seasonstream Exp $
 * @link      http://www.syl.jp/
 */
class SyL_Socket
{
    /**
     * 接続対象サーバー
     * 
     * @access private
     * @var string
     */
    var $host;
    /**
     * サーバーのポート
     * 
     * @access private
     * @var int
     */
    var $port;
    /**
     * サーバーレスポンスデータ
     * 
     * @access private
     * @var string
     */
    var $response = '';
  
    /**
     * エラーNo
     * 
     * @access private
     * @var int
     */
    var $error_no = 0;
    /**
     * エラーメッセージ
     * 
     * @access private
     * @var int
     */
    var $error_string = '';
    /**
     * ソケットコネクション
     * 
     * @access private
     * @var resource
     */
    var $conn = null;
    /**
     * デバック文字列取得メソッド
     * 
     * @access private
     * @var mixed
     */
    var $debug_callback_func = null;

    /**
     * コンストラクタ
     * 
     * @access public
     * @param string 接続対象サーバー
     * @param int 接続対象サーバーのポート
     */
    function SyL_Socket($host, $port)
    {
        $this->host = $host;
        $this->port = $port;
    }

    /**
     * デバック文字列取得メソッドをセット
     * 
     * @access public
     * @param mixed デバック文字列取得メソッド
     */
    function setDebugCallBackFunc($debug_callback_func)
    {
        $this->debug_callback_func =& $debug_callback_func;
    }

    /**
     * デバック文字列取得をメソッド実行
     * 
     * @access public
     * @param string send or receive
     * @param string ソケット送受信文字列
     */
    function trace($type, $message)
    {
        if ($this->debug_callback_func != null) {
            call_user_func($this->debug_callback_func, $type, $message);
        }
    }

    /**
     * エラーを判定する
     * 
     * @access public
     * @return bool true: 正常、false: エラー
     */
    function isError()
    {
        return ($this->error_no != 0);
    }

    /**
     * エラーNoとエラーメッセージを取得
     * 
     * @access public
     * @return array エラーNoとエラーメッセージを含んだ配列
     */
    function getLastError()
    {
        return (($this->error_no != 0) ? "[SyL error] Socket error: No.{$this->error_no} {$this->error_string}" : null);
    }

    /**
     * ソケットを開く
     * 
     * @access public
     * @param int タイムアウト
     * @return true: 接続OK, false: 接続NG
     */
    function open($timeout=3)
    {
        $this->conn = fsockopen($this->host, $this->port, $this->error_no, $this->error_string, $timeout);
        if (!$this->isSocket()) {
            return false;
        }
        // タイムアウト
        if (!stream_set_timeout($this->conn, $timeout)) {
            $this->error_no = 1;
            $this->error_string = "stream_set_timeout setting error";
            return false;
        }
        // ブロッキングモード
        if (!stream_set_blocking($this->conn, 1)) {
            $this->error_no = 1;
            $this->error_string = "stream_set_blocking setting error";
            return false;
        }

        return true;
    }

    /**
     * ソケット接続確認
     * 
     * @access public
     * @return true: OK, false: NG
     */
    function isSocket()
    {
        return is_resource($this->conn);
    }

    /**
     * ソケットを閉じる
     * 
     * @access public
     * @return true: クローズOK, false: クローズNG
     */
    function close()
    {
        if ($this->isSocket()) {
            if (fclose($this->conn)) {
              return true;
            } else {
                $this->error_no = 1;
                $this->error_string = "fclose error";
                return false;
            }
        } else {
            $this->error_no = 1;
            $this->error_string = "close failed for socket is not resource";
            return false;
        }
    }

    /**
     * コマンドをサーバーする
     * 
     * @access public
     *
     * @param string 送信コマンド
     * @return boolean OK: true, NG: false
     */
    function send($request)
    {
        // リソースの確認
        if (!$this->isSocket()) return false;
        // コマンドを送信
        fwrite($this->conn, $request . "\r\n");
        // ログ出力
        $this->trace('send', $request);
        return true;
    }

    /**
     * 結果を個別に取得
     * 
     * @access public
     * @param string 取得データ
     * @return bool true: 取得OK, false: 取得エラー
     */
    function receive()
    {
        // リソースの確認
        if (!$this->isSocket()) return false;
        // 結果を受信
        $receive = fgets($this->conn, 4096);
        // ログ出力
        $this->trace('receive', $receive);
        // タイムアウト判定
        if (!$this->isTimeout()) {
            return false;
        }
        return $receive;
    }

    /**
     * 結果を全て取得
     * 
     * @access public
     * @return string 取得データ
     * @return bool true: 取得OK, false: 取得エラー
     */
    function receiveAll($eof=false)
    {
        // 結果を受信
        $receive = '';
        if ($eof) {
            while (!feof($this->conn)) {
                $receive .= $this->receive();
            }
        } else {
            while (true) {
                $tmp = $this->receive();
                if ($tmp === false) {
                    break;
                }
                $receive .= $tmp;
            }
        }
        return $receive;
    }

    /**
     * タイムアウト判定
     * 
     * @access public
     * @return bool true: タイムアウト無し, false: タイムアウトエラー
     */
    function isTimeout()
    {
        // タイムアウト判定
        $stream = stream_get_meta_data($this->conn);
        if (isset($stream['timeout']) && $stream['timeout']) {
            $this->error_no = 1;
            $this->error_string = "send timeout";
            return false;
        } else {
            return true;
        }
    }
}

?>
