<?php

/** 输入数据管理类
 * 使用方法
 *  $Input = Input::getInstance();
 *  $Input->get('name','md5','0');
 *  $Input->session('memberId','','0');
 *
 * 下面总结了一些常用的数据处理方法。以下方法无需考虑magic_quotes_gpc的设置。
 *
 * 获取数据：
 *    如果从$_POST或者$_GET中获取，使用Input::getVar($_POST['field']);，从数据库或者文件就不需要了。
 *    或者直接使用 Input::magicQuotes来消除所有的magic_quotes_gpc转义。
 *
 * 存储过程：
 *    经过Input::getVar($_POST['field'])获得的数据，就是干净的数据，可以直接保存。
 *    如果要过滤危险的html，可以使用 $html = Input::safeHtml($data);
 *
 * 页面显示：
 *    纯文本显示在网页中，如文章标题<title>$data</title>： $data = Input::forShow($field);
 *    HTML 在网页中显示，如文章内容：无需处理。
 *    在网页中以源代码方式显示html：$vo = Input::forShow($html);
 *    纯文本或者HTML在textarea中进行编辑: $vo = Input::forTarea($value);
 *    html在标签中使用，如<input value="数据" /> ，使用 $vo = Input::forTag($value); 或者 $vo = Input::hsc($value);
 *
 * 特殊使用情况：
 *    字符串要在数据库进行搜索： $data = Input::forSearch($field);
 */
class Input {

    private $filter = null;   // 输入过滤
    private static $_input = array('get', 'post', 'request', 'env', 'server', 'cookie', 'session', 'globals', 'config', 'lang', 'call');
    //html标签设置
    public static $htmlTags = array(
        'allow' => 'table|td|th|tr|i|b|u|strong|img|p|br|div|strong|em|ul|ol|li|dl|dd|dt|a',
        'ban' => 'html|head|meta|link|base|basefont|body|bgsound|title|style|script|form|iframe|frame|frameset|applet|id|ilayer|layer|name|script|style|xml',
    );

    static public function getInstance() {
        return get_instance_of(__CLASS__);
    }

    /**
      +----------------------------------------------------------
     * 魔术方法 有不存在的操作的时候执行
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $type 输入数据类型
     * @param array $args 参数 array(key,filter,default)
      +----------------------------------------------------------
     * @return mixed
      +----------------------------------------------------------
     */
    public function __call($type, $args = array()) {
        $type = strtolower(trim($type));
        if (in_array($type, self::$_input, true)) {
            switch ($type) {
                case 'get': $input = & $_GET;
                    break;
                case 'post': $input = & $_POST;
                    break;
                case 'request': $input = & $_REQUEST;
                    break;
                case 'env': $input = & $_ENV;
                    break;
                case 'server': $input = & $_SERVER;
                    break;
                case 'cookie': $input = & $_COOKIE;
                    break;
                case 'session': $input = & $_SESSION;
                    break;
                case 'globals': $input = & $GLOBALS;
                    break;
                case 'files': $input = & $_FILES;
                    break;
                case 'call': $input = 'call';
                    break;
                case 'config': $input = C();
                    break;
                case 'lang': $input = L();
                    break;
                default:return NULL;
            }
            if ('call' === $input) {
                // 呼叫其他方式的输入数据
                $callback = array_shift($args);
                $params = array_shift($args);
                $data = call_user_func_array($callback, $params);
                if (count($args) === 0) {
                    return $data;
                }
                $filter = isset($args[0]) ? $args[0] : $this->filter;
                if (!empty($filter)) {
                    $data = call_user_func_array($filter, $data);
                }
            } else {
                if (0 == count($args) || empty($args[0])) {
                    return $input;
                } elseif (array_key_exists($args[0], $input)) {
                    // 系统变量
                    $data = $input[$args[0]];
                    $filter = isset($args[1]) ? $args[1] : $this->filter;
                    if (!empty($filter)) {
                        $data = call_user_func_array($filter, $data);
                    }
                } else {
                    // 不存在指定输入
                    $data = isset($args[2]) ? $args[2] : NULL;
                }
            }
            return $data;
        }
    }

    /**
      +----------------------------------------------------------
     * 设置数据过滤方法
      +----------------------------------------------------------
     * @access private
      +----------------------------------------------------------
     * @param mixed $filter 过滤方法
      +----------------------------------------------------------
     * @return void
      +----------------------------------------------------------
     */
    public function filter($filter) {
        $this->filter = $filter;
        return $this;
    }

    /**
      +----------------------------------------------------------
     * 字符MagicQuote转义过滤
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @return void
      +----------------------------------------------------------
     */
    static public function noGPC() {
        if (get_magic_quotes_gpc()) {
            $_POST = stripslashes_deep($_POST);
            $_GET = stripslashes_deep($_GET);
            $_COOKIE = stripslashes_deep($_COOKIE);
            $_REQUEST = stripslashes_deep($_REQUEST);
        }
    }

    /**
      +----------------------------------------------------------
     * 处理字符串，以便可以正常进行搜索
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function forSearch($string) {
        return str_replace(array('%', '_'), array('\%', '\_'), $string);
    }

    /**
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function forShow($string) {
        return self::nl2Br(self::hsc($string));
    }

    /**
      +----------------------------------------------------------
     * 处理纯文本数据，以便在textarea标签中显示
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function forTarea($string) {
        return str_ireplace(array('<textarea>', '</textarea>'), array('&lt;textarea>', '&lt;/textarea>'), $string);
    }

    /**
      +----------------------------------------------------------
     * 将数据中的单引号和双引号进行转义
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $text 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function forTag($string) {
        return str_replace(array('"', "'"), array('&quot;', '&#039;'), $string);
    }

    /**
      +----------------------------------------------------------
     * 转换文字中的超链接为可点击连接
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function makeLink($string) {
        $validChars = "a-z0-9\/\-_+=.~!%@?#&;:$\|";
        $patterns = array(
            "/(^|[^]_a-z0-9-=\"'\/])([a-z]+?):\/\/([{$validChars}]+)/ei",
            "/(^|[^]_a-z0-9-=\"'\/])www\.([a-z0-9\-]+)\.([{$validChars}]+)/ei",
            "/(^|[^]_a-z0-9-=\"'\/])ftp\.([a-z0-9\-]+)\.([{$validChars}]+)/ei",
            "/(^|[^]_a-z0-9-=\"'\/:\.])([a-z0-9\-_\.]+?)@([{$validChars}]+)/ei");
        $replacements = array(
            "'\\1<a href=\"\\2://\\3\" title=\"\\2://\\3\" rel=\"external\">\\2://'.Input::truncate( '\\3' ).'</a>'",
            "'\\1<a href=\"http://www.\\2.\\3\" title=\"www.\\2.\\3\" rel=\"external\">'.Input::truncate( 'www.\\2.\\3' ).'</a>'",
            "'\\1<a href=\"ftp://ftp.\\2.\\3\" title=\"ftp.\\2.\\3\" rel=\"external\">'.Input::truncate( 'ftp.\\2.\\3' ).'</a>'",
            "'\\1<a href=\"mailto:\\2@\\3\" title=\"\\2@\\3\">'.Input::truncate( '\\2@\\3' ).'</a>'");
        return preg_replace($patterns, $replacements, $string);
    }

    /**
      +----------------------------------------------------------
     * 缩略显示字符串
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
     * @param int $length 缩略之后的长度
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function truncate($string, $length = '50') {
        if (empty($string) || empty($length) || strlen($string) < $length)
            return $string;
        $len = floor($length / 2);
        $ret = substr($string, 0, $len) . " ... " . substr($string, 5 - $len);
        return $ret;
    }

    /**
      +----------------------------------------------------------
     * 把换行转换为<br />标签
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function nl2Br($string) {
        return preg_replace("/(\015\012)|(\015)|(\012)/", "<br />", $string);
    }

    /**
      +----------------------------------------------------------
     * 如果 magic_quotes_gpc 为关闭状态，这个函数可以转义字符串
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function addSlashes($string) {
        if (!get_magic_quotes_gpc()) {
            $string = addslashes($string);
        }
        return $string;
    }

    /**
      +----------------------------------------------------------
     * 从$_POST，$_GET，$_COOKIE，$_REQUEST等数组中获得数据
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function getVar($string) {
        return Input::stripSlashes($string);
    }

    /**
      +----------------------------------------------------------
     * 如果 magic_quotes_gpc 为开启状态，这个函数可以反转义字符串
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function stripSlashes($string) {
        if (get_magic_quotes_gpc()) {
            $string = stripslashes($string);
        }
        return $string;
    }

    /**
      +----------------------------------------------------------
     * 用于在textbox表单中显示html代码
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static function hsc($string) {
        return preg_replace(array("/&amp;/i", "/&nbsp;/i"), array('&', '&amp;nbsp;'), htmlspecialchars($string, ENT_QUOTES));
    }

    /**
      +----------------------------------------------------------
     * 是hsc()方法的逆操作
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $text 要处理的字符串
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static function undoHsc($text) {
        return preg_replace(array("/&gt;/i", "/&lt;/i", "/&quot;/i", "/&#039;/i", '/&amp;nbsp;/i'), array(">", "<", "\"", "'", "&nbsp;"), $text);
    }

    /**
      +----------------------------------------------------------
     * 输出安全的html，用于过滤危险代码
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $text 要处理的字符串
     * @param mixed $allowTags 允许的标签列表，如 table|td|th|td
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function safeHtml($text, $allowTags = null) {
        $text = trim($text);
        //完全过滤注释
        $text = preg_replace('/<!--?.*-->/', '', $text);
        //完全过滤动态代码
        $text = preg_replace('/<\?|\?' . '>/', '', $text);
        //完全过滤js
        $text = preg_replace('/<script?.*\/script>/', '', $text);

        $text = str_replace('[', '&#091;', $text);
        $text = str_replace(']', '&#093;', $text);
        $text = str_replace('|', '&#124;', $text);
        //过滤换行符
        $text = preg_replace('/\r?\n/', '', $text);
        //br
        $text = preg_replace('/<br(\s\/)?' . '>/i', '[br]', $text);
        $text = preg_replace('/(\[br\]\s*){10,}/i', '[br]', $text);
        //过滤危险的属性，如：过滤on事件lang js
        while (preg_match('/(<[^><]+)(lang|on|action|background|codebase|dynsrc|lowsrc)[^><]+/i', $text, $mat)) {
            $text = str_replace($mat[0], $mat[1], $text);
        }
        while (preg_match('/(<[^><]+)(window\.|javascript:|js:|about:|file:|document\.|vbs:|cookie)([^><]*)/i', $text, $mat)) {
            $text = str_replace($mat[0], $mat[1] . $mat[3], $text);
        }
        if (empty($allowTags)) {
            $allowTags = self::$htmlTags['allow'];
        }
        //允许的HTML标签
        $text = preg_replace('/<(' . $allowTags . ')( [^><\[\]]*)>/i', '[\1\2]', $text);
        //过滤多余html
        if (empty($banTag)) {
            $banTag = self::$htmlTags['ban'];
        }
        $text = preg_replace('/<\/?(' . $banTag . ')[^><]*>/i', '', $text);
        //过滤合法的html标签
        while (preg_match('/<([a-z]+)[^><\[\]]*>[^><]*<\/\1>/i', $text, $mat)) {
            $text = str_replace($mat[0], str_replace('>', ']', str_replace('<', '[', $mat[0])), $text);
        }
        //转换引号
        while (preg_match('/(\[[^\[\]]*=\s*)(\"|\')([^\2=\[\]]+)\2([^\[\]]*\])/i', $text, $mat)) {
            $text = str_replace($mat[0], $mat[1] . '|' . $mat[3] . '|' . $mat[4], $text);
        }
        //空属性转换
        $text = str_replace('\'\'', '||', $text);
        $text = str_replace('""', '||', $text);
        //过滤错误的单个引号
        while (preg_match('/\[[^\[\]]*(\"|\')[^\[\]]*\]/i', $text, $mat)) {
            $text = str_replace($mat[0], str_replace($mat[1], '', $mat[0]), $text);
        }
        //转换其它所有不合法的 < >
        $text = str_replace('<', '&lt;', $text);
        $text = str_replace('>', '&gt;', $text);
        $text = str_replace('"', '&quot;', $text);
        //反转换
        $text = str_replace('[', '<', $text);
        $text = str_replace(']', '>', $text);
        $text = str_replace('|', '"', $text);
        //过滤多余空格
        $text = str_replace('  ', ' ', $text);
        return $text;
    }

    /**
      +----------------------------------------------------------
     * 删除html标签，得到纯文本。可以处理嵌套的标签
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的html
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function deleteHtmlTags($string) {
        while (strstr($string, '>')) {
            $currentBeg = strpos($string, '<');
            $currentEnd = strpos($string, '>');
            $tmpStringBeg = @substr($string, 0, $currentBeg);
            $tmpStringEnd = @substr($string, $currentEnd + 1, strlen($string));
            $string = $tmpStringBeg . $tmpStringEnd;
        }
        return $string;
    }

    /**
      +----------------------------------------------------------
     * 处理文本中的换行
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param string $string 要处理的字符串
     * @param mixed $br 对换行的处理，
     *        false：去除换行；true：保留原样；string：替换成string
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    static public function nl2($string, $br = '<br />') {
        if ($br == false) {
            $string = preg_replace("/(\015\012)|(\015)|(\012)/", '', $string);
        } elseif ($br != true) {
            $string = preg_replace("/(\015\012)|(\015)|(\012)/", $br, $string);
        }
        return $string;
    }

}
