|
- <?php
- /**
- * *************************************************************************
- *
- * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
- *
- * ************************************************************************
- */
- /**
- *
- * Packing the cUrl and make it easy
- *
- * @file HttpRequest.php
- * @encoding UTF-8
- *
- *
- * @date 2014年12月25日
- *
- */
-
- require_once(PUSH_SDK_HOME.'/lib/PushSimpleLog.php');
- require_once(PUSH_SDK_HOME.'/lib/PushException.php');
-
- class HttpRequest {
- const HTTP_GET = 'GET';
- const HTTP_POST = 'POST';
- /**
- * @var PushSimpleLog
- */
- private $log = null;
- private $urlBase;
- private static $defaultOpts = array (
-
- // default use the http version 1.0 , To prevent on the MAC platform using post speed too slow
- CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0,
- CURLOPT_FAILONERROR => false,
- CURLOPT_HEADER => true,
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_TIMEOUT => 10,
- CURLOPT_HTTPHEADER => array (
- // server端 ,目前唯一支持编码格式,即为utf-8;
- 'Content-type: application/x-www-form-urlencoded;charset=utf-8',
- ),
- );
-
- private $currentOpts = array();
- /**
- * Constructor
- *
- * @param string $urlBase
- * url基础路径
- * @param array $opts
- * cUrl options
- * @param PushSimpleLog $log
- * @throws Exception
- */
- function __construct($urlBase = null, $opts = array(), $log = null) {
-
- // 如果未指定, 则创建一个被disable掉的log对象;
- $this -> log = $log === null ? new PushSimpleLog() : $log;
-
- if (! is_callable('curl_version')) {
- throw new Exception("php extension [cUrl] is not exists!!");
- }
-
- if ($urlBase === null) {
- throw new HttpException('URL must be a string');
- }
-
- if (preg_match('/^http.*/i', $urlBase) === 0) {
- throw new HttpException('URL is not correct');
- }
-
- if (strrpos($urlBase, '/') != strlen($urlBase) - 1) {
- $urlBase .= '/';
- }
-
- $this -> urlBase = $urlBase;
-
- if(defined(CURLOPT_IPRESOLVE)){
- self::$defaultOpts[CURLOPT_IPRESOLVE] = CURL_IPRESOLVE_V4;
- }
-
- $this -> currentOpts = self::$defaultOpts;
-
- if (is_array($opts)) {
- foreach ( $opts as $k => $v ) {
- $this -> currentOpts [$k] = $v;
- }
- }
- }
-
- /**
- * 判断一个字符串是否为以http://开头
- *
- * @param string $url
- * @return boolean
- */
- public function isUrlFormat($url){
- return $this->isMatchReg('/^http:\/\/.*/i',$url);
- }
-
- /**
- * 判断一个字符串,是否符合给写的格式
- *
- * @param string $match 正则表达式格式字符串 PREG库风格
- * @param string $str 将验证的字符串
- * @return boolean
- */
- function isMatchReg($match,$str){
- return preg_match($match,$str) > 0;
- }
- /**
- * 以标准uri风格编码一个array<K,V>
- *
- * @param array<K,V> $body
- * @return string
- */
- private function encodePostBody($body) {
- if ($body === null) {
- return '';
- }
-
- if (! is_array($body)) {
- return urlencode(strval($body));
- }
-
- $result = array ();
- foreach ( $body as $k => $v ) {
- $result [ ] = urlencode($k) . '=' . urlencode($v);
- }
-
- return join('&', $result);
- }
- /**
- * 解析curl的response,并折分其中的header与body部份.
- * @param string $content
- * @return array
- */
- private function parseResponse($content) {
-
- list ( $headerStr, $body ) = explode("\r\n\r\n", $content, 2);
-
- $header = array ();
-
- $headerPart = explode("\r\n", $headerStr);
-
- list ( $httpProtocol, $statusCode, $statStr ) = explode(' ', array_shift($headerPart), 3);
-
- foreach ( $headerPart as $item ) {
- list ( $key, $value ) = explode(':', $item);
- $header [$key] = $value;
- }
-
- $result = array (
- 'protocol' => $httpProtocol,
- 'status' => intval($statusCode),
- 'statusMsg' => $statStr,
- 'header' => $header,
- 'content' => $body,
- );
-
- return $result;
- }
- /**
- * 发起http请求, 并返回响应内容
- *
- * @param [string] $path
- * url的拼接部份,将拼接到urlBase部份
- * @param [string] $method
- * 使用的http请求方法, 目前只支持GET和POST
- * @param [mixed] $payload
- * 请求时的附加的数据.
- * @param [array] $reqHeaders
- * 请求时附加的header信息
- * @param [array] $curlOpts
- * 每次请求时, 对curl的配置信息.
- * @throws HttpException 请求异常时, 抛出异常
- * @return array http response信息 , 具有如下结构 array(
- * httpProtocol:'',
- * status:'',
- * statusMsg:'',
- * header:array(),
- * content:""
- * )
- */
- function request($path = '', $method = "GET", $payload = "", $reqHeaders = null, $curlOpts = null) {
- if ($path === null) {
- throw new HttpException('path must be string');
- }
-
- // 决定访问位置
- $url = $this->resolve($path);
-
- $curlOptsFinal = $this -> currentOpts;
-
- if (is_array($curlOpts)) {
- foreach ( $curlOpts as $k => $v ) {
- $curlOptsFinal [$k] = $v;
- }
- }
-
- if (is_array($reqHeaders)) {
- if(!is_array($curlOptsFinal [CURLOPT_HTTPHEADER])){
- $curlOptsFinal [CURLOPT_HTTPHEADER] = array(); // 这玩意必须是个数组.不是就直接覆盖.
- }
- $curlOptsFinal [CURLOPT_HTTPHEADER] = array_merge($curlOptsFinal [CURLOPT_HTTPHEADER], $reqHeaders);
-
- //print_r($curlOptsFinal);
- }
-
- $port = parse_url($url, PHP_URL_PORT);
-
- $curlOptsFinal [CURLOPT_URL] = $url;
-
- $curlOptsFinal [CURLOPT_PORT] = empty($port) ? 80 : $port;
-
- $req = curl_init();
-
- curl_setopt_array($req, $curlOptsFinal);
-
- switch ($method) {
- case self::HTTP_GET :
- curl_setopt($req, CURLOPT_CUSTOMREQUEST, 'GET');
- break;
- case self::HTTP_POST :
- curl_setopt($req, CURLOPT_POST, true);
- curl_setopt($req, CURLOPT_POSTFIELDS, $payload);
- break;
- default :
- throw new HttpException("method not be support");
- }
-
-
- $response = curl_exec($req);
- $errCode = curl_errno($req);
-
-
- if ($errCode === 0) {
- curl_close($req);
- // log http access
- $response = $this -> parseResponse($response);
- $status = $response['status'];
- $this -> log -> log("HttpRequest: $status $method $url");
-
- return $response;
- } else {
- // get message and close curl resource;
-
- $errMsg = curl_error($req);
- curl_close($req);
-
- throw new HttpException( "curl_error($errMsg); with url($url)", $errCode);
- }
- }
- /**
- * 快速发起get请求
- *
- * @param string $path
- * 请求资源路径
- * @param array $query
- * 请求参数
- * @param array $headers
- * 附带header
- * @param array $curlOpts
- * 附加curlOpts
- *
- * @return array http response信息 , 具有如下结构 array(
- * httpProtocol:'',
- * status:'',
- * statusMsg:'',
- * header:array(),
- * content:""
- * )
- */
- function get($path, $query = null, $headers = null, $curlOpts = null) {
- $payload = $this -> encodePostBody($query);
-
- if ($payload !== "") {
- if (strpos($path, '?')) {
- $url = $path . "&" . $payload;
- } else {
- $url = $path . "?" . $payload;
- }
- }else{
- $url = $path;
- }
-
- return $this -> request($url, self::HTTP_GET, '', $headers, $curlOpts);
- }
- /**
- * 快速发起post请求
- *
- * @param string $path
- * 请求资源路径
- * @param array $postBody
- * 请求参数
- * @param array $headers
- * 附带header
- * @param array $curlOpts
- * 附加curlOpts
- *
- * @return array http response信息 , 具有如下结构 array(
- * httpProtocol:'',
- * status:'',
- * statusMsg:'',
- * header:array(),
- * content:""
- * )
- */
- function post($path = null, $postBody = null, $headers = null, $curlOpts = null) {
- $payload = $this -> encodePostBody($postBody);
- $this -> log -> debug("\n\n ====== Dump the payload before Http POST send! ====== \n\n$payload\n\n====== End Dump! ======\n");
- return $this -> request($path, self::HTTP_POST, $payload, $headers, $curlOpts);
- }
-
- /**
- * 根据传入内容,决定具体请求地址;
- * @param string $path
- * @return Ambigous <string, unknown>
- */
- public function resolve($path){
- // 如果不指定完整的url, 则认为是访问$urlBase内资源
- if ($this -> isUrlFormat($path)) {
- $url = $path;
- }
- // 忽略重复的路径分隔符
- else if (strpos($path, '/') === 0) {
- $url = $this -> urlBase . substr($path, 1);
- } else {
- $url = $this -> urlBase . $path;
- }
-
- return $url;
- }
-
- /**
- * 取得url中表示资源位置的部份,即去掉query_string及其后的部份
- *
- * @param string $url
- * @return string
- */
- public function getResourceAddress($url){
- $url = $this->resolve($url);
-
- $p = strpos($url,'?');
- $p = $p === false ? strpos($url,'#') : $p;
-
- return $p === false ? $url : substr($url, 0, $p);
- }
- }
|