/**
 * 関数の再帰処理クラス
 * $typeがstring以外の場合はあまり使うことがないかもしれない
 */
class FuncRecursive {
    var $type = 'string';
    var $func = '';
    var $data = array();
    var $args = array();
    var $map = array();

    function __construct() {}

    /**
     * 再帰処理実行
     */
    function execute() {
        //データのセット
        $args = func_get_args();
        $this->_setParams($args);

        //配列じゃない場合
        if(!is_array($this->data)) {
            $params = array_merge(array($this->data), $this->args);
            return call_user_func_array($this->func, $params);
        }

        // switch($this->type) {
        //     case 'string':
        //         return $this->_exeString($this->data);
        //     case 'array':
        //         return $this->_exeArray($this->data);
        //     case 'map':
        //         return $this->_exeMap($this->data);
        // }    

        //実行
        $execute = '_exe'.ucfirst($this->type);
        return $this->{$execute}($this->data);
    }

    /**
     * 処理対象が文字列の場合
     * mb_convert_encodingなど
     */
    function _exeString($data) {
        foreach($data as &$val) {
            if(is_array($val)) {
                $val = $this->_exeString($val);
            } else {
                $params = array_merge(array($val), $this->args);
                $val = call_user_func_array($this->func, $params);
            }
        }

        return $data;
    }

    /**
     * 処理対象が配列の場合
     * implodeなど
     */
    function _exeArray($data) {
        foreach($data as &$val) {
            if(is_array($val)) {
                $val = $this->_exeArray($val);
            }
        }

        $params = array_merge(array($data), $this->args);
        return call_user_func_array($this->func, $params);                
    }

    /**
     * array_mapっぽい処理を行なう場合
     */
    function _exeMap($data) {
        foreach($data as &$val) {
            //対応するパラメータを取得
            $array = !empty($this->map) ? 'map' : 'args';
            $map = array();

            foreach($this->{$array} as &$a) {
                $map[] = is_array($a) ? array_shift($a) : $a;
            }

            if(is_array($val)) {
                //使用するパラメータを変数に保持しておく
                $this->map = $map;
                $val = $this->_exeMap($val);
            } else {
                array_unshift($map, $val);
                $val = call_user_func_array($this->func, $map);
            }
        }

        $this->map = array();
        return $data;
    }

    /**
     * 引数を実行可能な形で取得
     */
    function _setParams($args) {
        //使用する関数と参照するデータ
        $this->func = array_shift($args);
        $this->data = array_shift($args);

        //残りの引数
        $this->args = $args;
        return;        
    }
}