免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2369 | 回复: 3
打印 上一主题 下一主题

php-curl封装[避免上传文件二义性Bug] [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2012-09-21 11:35 |只看该作者 |倒序浏览
由于php的curl在curl_setopt($curl, CURLOPT_POSTFIELDS, xxx)时, 当xxx为数组时, 如果值的第一个字符是@, 则认为是文件上传, 当同时需要上传文件, 也需要提交可能首字符为@的其他普通数据时, 存在冲突. 因此, 在api_common.php中的post数据的设置进行了封装
  1. <?php
  2. /**
  3. * php-curl库封装
  4. * author: selfimpr
  5. * blog: http://blog.csdn.net/lgg201
  6. * mail: lgg860911@yahoo.com.cn
  7. */

  8. define('API_CURL_UPLOAD_FILE',                                                        '__file');

  9. #支持的请求方法
  10. define('REQUEST_METHOD_GET',                                                        'GET');
  11. define('REQUEST_METHOD_POST',                                                        'POST');
  12. define('REQUEST_METHOD_HEAD',                                                        'HEAD');

  13. #请求行为
  14. define('REQUEST_BEHAVIOR_ALLOW_REDIRECT',                                'allow_redirect');
  15. define('REQUEST_BEHAVIOR_MAX_REDIRECT',                                        'max_redirect');
  16. define('REQUEST_BEHAVIOR_USER_AGENT',                                        'user_agent');
  17. define('REQUEST_BEHAVIOR_AUTOREFERER',                                        'autoreferer');
  18. define('REQUEST_BEHAVIOR_UPLOAD',                                                'upload');
  19. define('REQUEST_BEHAVIOR_CONNECTTIMEOUT',                                'connecttimeout');
  20. define('REQUEST_BEHAVIOR_DNS_CACHE_TIMEOUT',                        'dns_cache_timeout');
  21. define('REQUEST_BEHAVIOR_TIMEOUT',                                                'timeout');
  22. define('REQUEST_BEHAVIOR_ENCODING',                                                'encoding');
  23. define('REQUEST_BEHAVIOR_ERROR_HANDLER',                                'error_handler');
  24. define('REQUEST_BEHAVIORS',                                                                'behaviors');
  25. $GLOBALS[REQUEST_BEHAVIORS]        = array(
  26.         REQUEST_BEHAVIOR_ALLOW_REDIRECT                                => TRUE,
  27.         REQUEST_BEHAVIOR_MAX_REDIRECT                                => 5,
  28.         REQUEST_BEHAVIOR_USER_AGENT                                        => 'curl-lib',
  29.         REQUEST_BEHAVIOR_AUTOREFERER                                => TRUE,
  30.         REQUEST_BEHAVIOR_UPLOAD                                                => FALSE,
  31.         REQUEST_BEHAVIOR_CONNECTTIMEOUT                                => 3,
  32.         REQUEST_BEHAVIOR_DNS_CACHE_TIMEOUT                        => 3600,
  33.         REQUEST_BEHAVIOR_TIMEOUT                                        => 3,
  34.         REQUEST_BEHAVIOR_ENCODING                                        => 'gzip',
  35.         REQUEST_BEHAVIOR_ERROR_HANDLER                                => '__default_curl_error_handler',
  36. );

  37. define('MULTIPART_FORM_DATA_HEAD_FMT',                                'Content-Type: multipart/form-data; boundary=----------------------------%s');
  38. define('MULTIPART_FORM_DATA_BODY_STRING',                        "------------------------------%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n");
  39. define('MULTIPART_FORM_DATA_BODY_FILE',                                "------------------------------%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: application/octet-stream\r\n\r\n%s\r\n");
  40. define('MULTIPART_FORM_DATA_BODY_END',                                "------------------------------%s--\r\n\r\n");

  41. #响应键值
  42. define('RESP_CODE',                                                                        'resp_code');
  43. define('RESP_BODY',                                                                        'resp_body');
  44. define('RESP_HEADER',                                                                'resp_header');

  45. #HTTP 1xx状态验证
  46. define('HTTP_1XX_RESP',                                                                '/^HTTP\/1.[01] 1\d{2} \w+/');

  47. #默认错误处理的错误消息
  48. define('E_CURL_ERROR_FMT',                                                                'curl "%s" error[%d]: %s');

  49. #默认的curl错误处理
  50. function __default_curl_error_handler($curl, $url, $errno, $errstr) {
  51.         trigger_error(sprintf(E_CURL_ERROR_FMT, $url, $errno, $errstr), E_USER_ERROR);
  52. }
  53. #切换CURL请求方法
  54. function __method_switch($curl, $method) {
  55.         switch ( $method) {
  56.                 case REQUEST_METHOD_POST:
  57.                         __curl_setopt($curl, CURLOPT_POST, TRUE);
  58.                         break;
  59.                 case REQUEST_METHOD_HEAD:
  60.                         __curl_setopt($curl, CURLOPT_NOBODY, TRUE);
  61.                         break;
  62.                 case REQUEST_METHOD_GET:
  63.                         __curl_setopt($curl, CURLOPT_HTTPGET, TRUE);
  64.                         break;
  65.                 default:
  66.                         break;
  67.         }
  68. }
  69. #设置默认头信息
  70. function __default_header_set($curl) {
  71.         __curl_setopt($curl, CURLOPT_RETURNTRANSFER,                        TRUE);
  72.         __curl_setopt($curl, CURLOPT_HEADER,                                        TRUE);
  73.         __curl_setopt($curl, CURLOPT_FOLLOWLOCATION,                        (bool)curl_behavior(REQUEST_BEHAVIOR_ALLOW_REDIRECT));
  74.         __curl_setopt($curl, CURLOPT_MAXREDIRS,                                        (int)curl_behavior(REQUEST_BEHAVIOR_MAX_REDIRECT));
  75.         __curl_setopt($curl, CURLOPT_USERAGENT,                                        (string)curl_behavior(REQUEST_BEHAVIOR_USER_AGENT));
  76.         __curl_setopt($curl, CURLOPT_AUTOREFERER,                                (bool)curl_behavior(REQUEST_BEHAVIOR_AUTOREFERER));
  77.         __curl_setopt($curl, CURLOPT_UPLOAD,                                        (bool)curl_behavior(REQUEST_BEHAVIOR_UPLOAD));
  78.         __curl_setopt($curl, CURLOPT_CONNECTTIMEOUT,                        (int)curl_behavior(REQUEST_BEHAVIOR_CONNECTTIMEOUT));
  79.         __curl_setopt($curl, CURLOPT_DNS_CACHE_TIMEOUT,                        (int)curl_behavior(REQUEST_BEHAVIOR_DNS_CACHE_TIMEOUT));
  80.         __curl_setopt($curl, CURLOPT_TIMEOUT,                                        (int)curl_behavior(REQUEST_BEHAVIOR_TIMEOUT));
  81.         __curl_setopt($curl, CURLOPT_ENCODING,                                        (string)curl_behavior(REQUEST_BEHAVIOR_ENCODING));
  82. }
  83. #设置用户自定义头信息
  84. function __custom_header_set($curl, $headers = NULL) {
  85.         if ( empty($headers) ) return ;
  86.         if ( is_string($headers) )
  87.                 $headers        = explode("\r\n", $headers);
  88.         #类型修复
  89.         foreach ( $headers as &$header )
  90.                 if ( is_array($header) )
  91.                         $header        = sprintf('%s: %s', $header[0], $header[1]);
  92.         __curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  93. }
  94. #设置请求body
  95. function __datas_set($curl, $datas = NULL) {
  96.         if ( empty($datas) ) return ;
  97.         if ( is_array($datas) ) {
  98.                 $custom_body                = FALSE;
  99.                 $uniqid                                = uniqid();
  100.                 $custom_body_str        = '';
  101.                 foreach ( $datas as $name => $data ) {
  102.                         if ( is_array($data) && array_key_exists(API_CURL_UPLOAD_FILE, $data) ) {
  103.                                 $file        = $data[API_CURL_UPLOAD_FILE];
  104.                                 if ( file_exists($file) ) {
  105.                                         $custom_body                = TRUE;
  106.                                         $custom_body_str        .= sprintf(MULTIPART_FORM_DATA_BODY_FILE,
  107.                                                                                 $uniqid, $name,
  108.                                                                                 $file, file_get_contents($file));
  109.                                 }
  110.                         } else {
  111.                                 $custom_body_str                .= sprintf(MULTIPART_FORM_DATA_BODY_STRING,
  112.                                                                                 $uniqid, $name, $data);
  113.                         }
  114.                 }
  115.                 if ( $custom_body ) {
  116.                         curl_setopt($curl, CURLOPT_HTTPHEADER, array(sprintf(MULTIPART_FORM_DATA_HEAD_FMT, $uniqid)));
  117.                         $datas                                = $custom_body_str . sprintf(MULTIPART_FORM_DATA_BODY_END, $uniqid);
  118.                 }
  119.         }
  120.         __curl_setopt($curl, CURLOPT_POSTFIELDS, $datas);
  121. }
  122. #对curl_setopt的封装
  123. function __curl_setopt($curl, $optname, $optval) {
  124.         curl_setopt($curl, $optname, $optval);
  125.         __curl_error($curl);
  126. }
  127. #curl错误检查处理
  128. function __curl_error($curl) {
  129.         if ( curl_errno($curl) ) {
  130.                 $url        = curl_getinfo($curl, CURLINFO_EFFECTIVE_URL);
  131.                 $errno        = curl_errno($curl);
  132.                 $errstr        = curl_error($curl);
  133.                 $errh        = curl_behavior(REQUEST_BEHAVIOR_ERROR_HANDLER);
  134.                 if ( function_exists($errh) )
  135.                         $errh($curl, $url, $errno, $errstr);
  136.         }
  137. }

  138. #api默认行为切换
  139. function curl_behavior($names, $values = NULL) {
  140.         if ( !is_string($names) && !is_array($names) ) return ;
  141.         if ( !is_null($values) ) {
  142.                 if ( is_string($names) )
  143.                         $GLOBALS[REQUEST_BEHAVIORS][$names]        = $values;
  144.                 else if ( is_array($names) && !is_array($values) )
  145.                         foreach ( $names as $name )
  146.                                 $GLOBALS[REQUEST_BEHAVIORS][$name]        = $values;
  147.                 else if ( is_array($names) && is_array($values) )
  148.                         foreach ( $names as $k => $name )
  149.                                 $GLOBALS[REQUEST_BEHAVIORS][$name]        = $values[$k];
  150.         }
  151.         if ( is_string($names) ) {
  152.                 $return        = $GLOBALS[REQUEST_BEHAVIORS][$names];
  153.         } else if ( is_array($names) ) {
  154.                 $return        = array();
  155.                 foreach ( $names as $name )
  156.                         $return[$name]        = array_key_exists($name, $GLOBALS[REQUEST_BEHAVIORS])
  157.                                                         ? $GLOBALS[REQUEST_BEHAVIORS][$name]
  158.                                                         : NULL;
  159.         }
  160.         return $return;
  161. }
  162. #请求入口
  163. function curl_request($url, $method, $datas = NULL, $headers = NULL) {
  164.         $curl        = curl_init($url);
  165.         __method_switch($curl, $method);
  166.         __default_header_set($curl);
  167.         __custom_header_set($curl, $headers);
  168.         __datas_set($curl, $datas);
  169.         $response        = curl_exec($curl);
  170.         __curl_error($curl);
  171.         $status_code        = curl_getinfo($curl, CURLINFO_HTTP_CODE);
  172.         $components                = explode("\r\n\r\n", $response);
  173.         $i                                = -1;
  174.         while ( ++ $i < count($components) )
  175.                 if ( !preg_match(HTTP_1XX_RESP, $components[$i]) ) break;
  176.         $headers                = $components[$i];
  177.         $body                        = implode("\r\n\r\n", array_slice($components, $i + 1));
  178.         return array(
  179.                 RESP_CODE        => $status_code,
  180.                 RESP_HEADER        => $headers,
  181.                 RESP_BODY        => $body,
  182.         );
  183. }
  184. #GET请求
  185. function curl_get($url, $headers = NULL) {
  186.         return curl_request($url, REQUEST_METHOD_GET, NULL, $headers);
  187. }
  188. #POST请求
  189. function curl_post($url, $datas = NULL, $headers = NULL) {
  190.         return curl_request($url, REQUEST_METHOD_POST, $datas, $headers);
  191. }
  192. #HEAD请求
  193. function curl_head($url, $headers = NULL) {
  194.         return curl_request($url, REQUEST_METHOD_HEAD, NULL, $headers);
  195. }
  196. #构造上传文件字段
  197. function curl_post_file($file) {
  198.         return array(
  199.                 API_CURL_UPLOAD_FILE        => $file,
  200.         );
  201. }
  202. #读取响应码
  203. function curl_resp_code($resp) {
  204.         return $resp[RESP_CODE];
  205. }
  206. #读取响应头
  207. function curl_resp_header($resp) {
  208.         return $resp[RESP_HEADER];
  209. }
  210. #读取响应体
  211. function curl_resp_body($resp) {
  212.         return $resp[RESP_BODY];
  213. }
复制代码

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
2 [报告]
发表于 2012-09-21 14:20 |只看该作者
本帖最后由 linux_c_py_php 于 2012-09-21 14:37 编辑

1, 这种情况用stream_context_create吧, 自己看php.net该函数下面的example, 有完整示例.

2, 我自己也试了一下, 发现@的确没有转义的方法:
  1. [liangdong@bb-browser-test00.vm.baidu.com php_project]$ php main.php
  2. Array
  3. (
  4.     [data] => Array
  5.         (
  6.             [name] => data
  7.             [type] => application/octet-stream
  8.             [tmp_name] => /tmp/php5i2paG
  9.             [error] => 0
  10.             [size] => 12
  11.         )

  12. )
  13. Array
  14. (
  15.     [name] =>  @liangdong
  16. )
  17. [liangdong@bb-browser-test00.vm.baidu.com php_project]$ cat main.php
  18. <?php
  19. $curl = curl_init();
  20. curl_setopt($curl, CURLOPT_POST, True);
  21. curl_setopt($curl, CURLOPT_POSTFIELDS, array(
  22.                                 "name" => " @liangdong", //留了个空格, 在test_post.php里做trim吧.
  23.                                 "data" => "@/home/liangdong/php_project/data"
  24.                         ));
  25. curl_setopt($curl, CURLOPT_URL, "http://localhost:8500/test_post.php");
  26. curl_setopt($curl, CURLOPT_RETURNTRANSFER, True);
  27. echo curl_exec($curl);
  28. //echo "%" . dechex(ord('@'));
  29. ?>
复制代码
  1. [liangdong@bb-browser-test00.vm.baidu.com htdocs]$ cat test_post.php
  2. <?php
  3. print_r($_FILES);
  4. print_r($_POST);
  5. ?>
复制代码

论坛徽章:
0
3 [报告]
发表于 2012-09-21 16:03 |只看该作者
linux_c_py_php 发表于 2012-09-21 14:20
1, 这种情况用stream_context_create吧, 自己看php.net该函数下面的example, 有完整示例.

2, 我自己也试 ...


1. 以前原始的都用fsock处理, 没有用过stream_context_create, 刚看了一下, 的确挺方便.

2. 这种方案的缺点在于对服务端需要产生影响, 而服务端如果支持多种客户端, 其中只有php-curl的客户端传递空格, 其他不传就也存在冲突.

3. 不使用stream_context_create或fsock的处理, 原因在于curl本身封装的其实挺好, 整个系统多处需要http-client功能时, 用这样的封装还是要省事不少的, 因此, 选择了对php-curl的这个缺陷进行修补.

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
4 [报告]
发表于 2012-09-21 17:41 |只看该作者
补的有道理, 我支持你.

我一开始试图给@转hex, 但发现php没有urldecode我POST的字段,  应该是curl这个POST接口实现的不够灵活, 没法指定dispostion=form-data, 导致没有php没有decode.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP