<?php

namespace loyalsoft;

/**
 * Http辅助类,可以发送请求/接收请求(提取参数)
 * @author gwang (email: mail@wanggangzero.cn)
 * @copyright 2016-3-12 ©SJZ LoyalSoft Corporation & gwang. All rights reserved.
 */
class HttpUtil {

    /**
     * 从请求字符串中解析请求参数
     * @param type $queryStr
     * @return type 注意:解析过程未加入url_decode, 请调用方根据情况自行处理.
     */
    public static function parseQueryParas($queryStr) {
//        $queryStr = urldecode($queryStr);
        $arr = explode('&', $queryStr);
        $queryParas = array();
        foreach ($arr as $value) {
            $paras = explode('=', $value);
            $queryParas[$paras[0]] = isset($paras[1]) ? $paras[1] : "";
        }
        return $queryParas;
    }

    /**
     * 执行一个 HTTP 请求
     *
     * @param string 	$url 	执行请求的URL
     * @param mixed	$params 表单参数, 可以是array, 也可以是经过url编码之后的string
     * @param mixed	$cookie cookie参数, 可以是array, 也可以是经过拼接的string, 默认array()
     * @param array     $header http请求头, 默认array()
     * @param string	$method 请求方法 post / get, 默认='post'
     * @param string	$protocol http协议类型 http / https, 默认='http'
     * @return array 结果数组 {"result":bool,"msg":返回值/curlErrMsg,"errno":curl错误码,"ifno":curlInfo}
     */
    public static function makeRequest($url, $params, $cookie = array(), #
            $header = array(), $method = 'post', $protocol = 'https') {
        $query_string = self::makeQueryString($params);
        $cookie_string = self::makeCookieString($cookie);

        $ch = curl_init();

        if ('GET' == strtoupper($method)) {                                     # Get
            curl_setopt($ch, CURLOPT_URL, "$url?$query_string");
        } else {                                                                # POST
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $query_string);                # postdata
        }

        curl_setopt($ch, CURLOPT_HEADER, false);                                # 控制是否返回响应头信息
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3);                            # 超时时间
        // 如果客户端要发送一段很大的数据, 可以先向服务器发送一个expect: 100-continue,
        // 等待服务器响应 100以后, 继续发送数据体. 比如nginx默认接收客户端数据的大小为1M,PHP2M.
        if (strlen($query_string) > 1024 * 1024) {
            if (is_array($header)) {                                            # http header是个数组,
                $header[] = 'Expect: 100-continue';                             # 补充一个'Expect:', 避免出现 100-continue.
            } else if (is_string($header)) {                                    # 如果header传过来了一个字符串
                $header = array($header, 'Expect: 100-continue');               # 拼接一个数组
            } else {                                                            # 其他情况,直接忽略, 覆盖掉
                $header = array('Expect: 100-continue');
            }
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $header);

        if (!empty($cookie_string)) {
            curl_setopt($ch, CURLOPT_COOKIE, $cookie_string);
        }

        if ('https' == $protocol or substr($url, 0, 5) == 'https') {            # 若为https协议
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);                    # 若是出错,检查php.ini中curl.cainfo配置
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        }

//        curl_setopt($ch, CURLOPT_PROXYPORT, 8888);                            # 代理,调试用的
//        curl_setopt($ch, CURLOPT_PROXY, '127.0.0.1');

        $ret = curl_exec($ch);
        $err = curl_error($ch);

        if (false === $ret || !empty($err)) {
            $errno = curl_errno($ch);
            $info = curl_getinfo($ch);
            curl_close($ch);

            return array(
                'result' => false,
                'msg' => $err,
                'errno' => $errno,
                'info' => $info,
            );
        }

        curl_close($ch);

        return array(
            'result' => true,
            'msg' => $ret,
        );
    }

    /**
     * 从参数构造请求串
     * @param type $params  如果是字符串直接返回,(关联)数组或者object(仅取public字段)
     * @return string 编码规范-RFC3986
     */
    public static function makeQueryString($params) {
        if (is_string($params)) {
            return $params;
        }
        return http_build_query($params, "", '&', PHP_QUERY_RFC3986);           # 3986
    }

    /**
     * 从参数构造Cookie字符串
     * @param type $params 如果是字符串直接返回,(关联)数组或者object(仅取public字段)
     * @return string 编码规范-RFC3986
     */
    public static function makeCookieString($params) {
        if (is_string($params)) {
            return $params;
        }
        return http_build_query($params, "", "; ", PHP_QUERY_RFC3986);          # 3986
    }

    /**
     * 取Request过来的Data转为关联数组
     * @return array
     */
    public static function getQueryParas() {
        $param = $_REQUEST;
        if (isset($_SERVER['REQUEST_METHOD'])) {
            switch ($_SERVER['REQUEST_METHOD']) {
                case 'GET':
                    $param = $_REQUEST;
                    break;
                case 'POST':
                    $str = file_get_contents('php://input');                    # 注意: 若是multipart/form-data,则PHP不会填充input.
                    $param = array_merge($param, ( strlen($str) > 0 ? self::parseQueryParas($str) : array()));
                    break;
                default:
            }
        }
        return $param;
    }

    /**
     * 取Request过来的数据流转为字符串
     * @return string
     */
    public static function getQueryString() {
        $queryStr = null;
        if (isset($_SERVER['REQUEST_METHOD'])) {
            switch ($_SERVER['REQUEST_METHOD']) {
                case 'GET':
                    $queryStr = $_SERVER['QUERY_STRING'];
                    break;
                case 'POST':
                    $queryStr = file_get_contents('php://input');
                    break;
                default:
            }
        }
        return $queryStr;
    }

    /**
     * 用于确保服务器只响应POST方法.
     */
    public static function PostOnly() {
        if ($_SERVER['REQUEST_METHOD'] != 'POST') {
            header('Allow: POST ', false, 405);
            exit();
        }
    }

    /**
     * 测试是否支持tls1.2
     * @return bool true:支持, false:不支持
     */
    public static function CanSurpotTls_1_2() {
        $target_url = 'https://www.baidu.com/';                                 # 测试地址

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $target_url);
        curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        return curl_exec($ch) !== false;                                        # 响应结果不为false则支持
    }

    /**
     * 获取客户端ip地址及端口
     * @return ip:port
     */
    public static function getClientEP() {
        return $_SERVER['REMOTE_ADDR'] . ":" . $_SERVER['REMOTE_PORT'];
    }

    /**
     * 获取客户端IP地址（摘自discuz）
     * @return string
     */
    public static function clientIP() {
        $ip = '未知IP';
        if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
            return self::is_ip($_SERVER['HTTP_CLIENT_IP']) ? $_SERVER['HTTP_CLIENT_IP'] : $ip;
        } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
            return self::is_ip($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $ip;
        } elseif (!empty($_SERVER['REMOTE_ADDR'])) {
            return self::is_ip($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : $ip;
        } else {
            return $ip;
        }
    }

    private static function is_ip($str) {
        $ip = explode('.', $str);
        for ($i = 0; $i < count($ip); $i++) {
            if ($ip[$i] > 255) {
                return false;
            }
        }
        return preg_match('/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $str);
    }

}

/**
 * 取Request过来的Data转为关联数组
 * @return array
 */
function query_paras() {
    return HttpUtil::getQueryParas();
}

/**
 * 取Request过来的数据流转为字符串
 * @return string
 */
function query_string() {
    return HttpUtil::getQueryString();
}
