<?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_Console.php,v 1.3 2006/12/23 11:13:09 seasonstream Exp $
 * @link      http://www.syl.jp/
 * -----------------------------------------------------------------------------
 */

/**
 *  コンソール表示クラス
 *
 * @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_Console.php,v 1.3 2006/12/23 11:13:09 seasonstream Exp $
 * @link      http://www.syl.jp/
 */
class SyL_Console
{
    /**
     * 標準入力
     *
     * @access private
     * @var resource
     */
    var $stdin;
    /**
     * 標準出力
     *
     * @access private
     * @var resource
     */
    var $stdout;
    /**
     * 標準エラー出力
     *
     * @access private
     * @var resource
     */
    var $stderr;
    /**
     * 引数配列
     *
     * @access private
     * @var array
     */
    var $args = array();
    /**
     * シェルとして実行するときの引数
     * ※falseの場合はコマンド実行不可
     *
     * @access private
     * @var array
     */
    var $shell_string = '\!';
    /**
     * 一括実行時にコマンドラインを終了する文字列
     *
     * @access private
     * @var array
     */
    var $exit_strings = array(
      'quit',
      'exit',
      '\q'
    );
    /**
     * コマンドラインに表示するメッセージ
     *
     * [message]
     * [default_message] > 
     *
     * array(
     *   [0] => array( [message], [default_message] ),
     *   ...
     * );
     *
     * @access private
     * @var array
     */
    var $command_messages = array();
    /**
     * コマンドラインから取得した入力値を取得するコールバック関数
     *
     * @access private
     * @var array
     */
    var $callback_func = array();

    /**
     * コンストラクタ
     *
     * @access public
     */
    function SyL_Console()
    {
        // とりあえず破棄
        while (@ob_end_clean());

        ob_start();
        $this->stdin  = fopen('php://stdin',  'r');
        $this->stdout = fopen('php://stdout', 'w');
        $this->stderr = fopen('php://stderr', 'w');

        $this->callback_func = array(&$this, 'doCallback');
        $this->args =  $_SERVER['argv'];
    }

    /**
     * 引数が存在するか判定する
     *
     * @access public
     * @param string 引数配列要素
     * @return mixed 引数値
     */
    function isArg($arg)
    {
        return in_array($arg, $this->args);
    }

    /**
     * 引数を取得する
     * 文字列や、接頭辞に「-」が付いている場合は、オプションから引数を取得する
     *
     * @access public
     * @param string 引数配列要素
     * @return mixed 引数値
     */
    function getArg($arg=null)
    {
        $return = '';
        if ($arg === null) {
            // オプション無し引数
            $return = array();
            $match = 1;
            for ($i=1; $i<$this->getArgCount(); $i++) {
                if ((substr($this->args[$i], 0, 1) != '-') && ($match >= 1)) {
                    $return[] = $this->args[$i];
                }
                $match = (substr($this->args[$i], 0, 1) == '-') ? 0 : $match + 1;
            }

        } else if ((substr($arg, 0, 1) == '-') || !is_numeric($arg)) {
            // オプションあり引数
            $return = array();
            if (substr($arg, 0, 1) != '-') {
                $arg = '-' . $arg;
            }

            $match = false;
            for ($i=1; $i<$this->getArgCount(); $i++) {
                if ($this->args[$i] == $arg) {
                    $match = false;
                }
                if ($match) {
                    $return[] = $this->args[$i];
                }
                $match = !$match && ($this->args[$i] == $arg);
            }

        } else {
            // 数値指定
            if (isset($this->args[$arg])) {
                $return = $this->args[$arg];
            }
        }

        return $return;
    }

    /**
     * 全引数を取得する
     *
     * @access public
     * @return array 全引数
     */
    function getArgs()
    {
        return $this->args;
    }

    /**
     * 引数の数を取得する
     *
     * @access public
     * @return int 引数の数
     */
    function getArgCount()
    {
        return count($this->args);
    }

    /**
     * 終了文字を追加する
     *
     * @access public
     * @param string 終了文字
     */
    function addExitString($exit_string)
    {
        $this->exit_strings[] = $exit_string;
    }

    /**
     * シェル文字を変更する
     * ※falseの場合はコマンド実行不可
     *
     * @access public
     * @param string シェル文字
     */
    function setShellString($shell_string)
    {
        $this->shell_string = $shell_string;
    }

    /**
     * 一括実行用、表示するメッセージをセット
     *
     * @access public
     * @param string 表示メッセージ
     * @param string コマンド補足メッセージ
     */
    function setMessage($message, $message_default='')
    {
        $this->command_messages[] = array($message, $message_default);
    }

    /**
     * 一括実行用、コマンドラインからの入力値を取得するコールバック関数をセット
     *
     * @access public
     * @param mixed コールバック関数
     */
    function setCallbackFunc($callback_func)
    {
        if (is_callable($callback_func)) {
            $this->callback_func = $callback_func;
        } else {
            trigger_error("[SyL error] invalid callable format", E_USER_ERROR);
        }
    }

    /**
     * デフォルトコールバック関数
     *
     * @access public
     * @param string 入力値
     */
    function doCallback(&$console, $return, $shell)
    {
    }

    /**
     * 一括実行スタート
     *
     * @access public
     * @param bool コマンドループフラグ
     */
    function start($loop=false)
    {
        while (true) {
            $shell = false;
            if ($this->command_messages) {
                $command_message = array_shift($this->command_messages);
                $return = $this->getInput($command_message[0], $command_message[1]);
            } else {
                if (!$loop) {
                    break;
                }
                $return = $this->getInput('');
            }
            if (in_array($return, $this->exit_strings)) {
                break;
            }
            if ($this->shell_string && (substr($return, 0, strlen($this->shell_string)) == $this->shell_string)) {
                $shell = true;
                $return = shell_exec(substr($return, strlen($this->shell_string)));
            }
            // コールバック関数実行
            call_user_func_array($this->callback_func, array(&$this, $return, $shell));
        }
    }

    /**
     * 表示を出力する
     *
     * @access public
     * @param string 表示メッセージ
     * @param bool 改行フラグ
     */
    function stdout($message, $newline=true)
    {
       if ($newline) {
           $message .= "\n";
       }
       fwrite($this->stdout, $message);
    }

    /**
     * エラー表示を出力する
     *
     * @access public
     * @param string エラー表示メッセージ
     * @param bool 改行フラグ
     */
    function stderr($message, $newline=true)
    {
       if ($newline) {
           $message .= "\n";
       }
       fwrite($this->stderr, $message);
    }

    /**
     * 入力値を取得する
     *
     * @access public
     * @param string 表示メッセージ
     * @param string コマンド補足メッセージ
     */
    function getInput($message, $message_default='')
    {
        if ($message_default != '') {
            $message_default .= ' ';
        }
        $message_default .= '> ';
        if ($message) {
            $this->stdout($message);
        }
        $this->stdout($message_default, false);

        ob_flush();
        return trim(fgets($this->stdin,256));
    }
    
}

?>
