PHP实现的带超时功能get_headers函数

这篇文章主要介绍了PHP实现的带超时功能的get_headers函数,本文直接给出实现代码,需要的朋友可以参考下

代码比较多,但是比较简单,一眼就看穿的,so,文字尽量少写了。

因为众所周知的网络原因,gavatar也开始越来越慢,写了一个小东西来解决这个问题,过程中遇到了get_headers这个函数,甚是忧伤,记录下来,以免后来人踩坑。

更新记录,函数稍微改了一下,返回值基本和之前序列化后的结果一致,暂时没考虑支持子项也支持数组等(考虑细节性能,还想把没用的http头砍掉….)

需求很简单:获取图片的head信息。

调试程序的时候发现这个函数的调用很缓慢,即使绑定ip,有时候都能蹦到20多秒。

寻思这个事情还是该加个超时吧,但是看官方文档,给出的导出函数接口如下:

array get_headers(string$url[,int$format=0])

你没有看错,这个东西没有超时接口…

上github翻看源码,期望可以用他的底层实现来重新实现一套:

地址 https://github.com/php/php-src/blob/88ca46d92bc1c426e7c7f7313f0fd2b7dcc33cf6/ext/standard/url.c#L710

代码如下:

  1. /* {{{ proto array get_headers(string url[, int format])
  2. fetches all the headers sent by the server in response to a HTTP request */
  3. PHP_FUNCTION(get_headers)
  4. {
  5. char*url;
  6. size_t url_len;
  7. php_stream_context*context;
  8. php_stream*stream;
  9. zval*prev_val,*hdr=NULL,*h;
  10. HashTable*hashT;
  11. zend_long format=0;
  12. if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,"s|l",&url,&url_len,&format)==FAILURE){
  13. return;
  14. }
  15. /** 省略其他一堆... **/
  16. }
  17. /* }}} */

但是很不幸的是,zend_parse_parameters 和 ZEND_NUM_ARGS也都没有PHP版的导出函数。

于是造轮子开始:

  1. functionget_url_headers($url,$timeout=10)
  2. {
  3. $ch=curl_init();
  4. curl_setopt($ch,CURLOPT_URL,$url);
  5. curl_setopt($ch,CURLOPT_HEADER,true);
  6. curl_setopt($ch,CURLOPT_NOBODY,true);
  7. curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
  8. curl_setopt($ch,CURLOPT_TIMEOUT,$timeout);
  9. $data=curl_exec($ch);
  10. $data=preg_split('/\n/',$data);
  11. $data=array_filter(array_map(function($data){
  12. $data=trim($data);
  13. if($data){
  14. $data=preg_split('/:\s/',trim($data),2);
  15. $length=count($data);
  16. switch($length){
  17. case2:
  18. returnarray($data[0]=>$data[1]);
  19. break;
  20. case1:
  21. return$data;
  22. break;
  23. default:
  24. break;
  25. }
  26. }
  27. },$data));
  28. sort($data);
  29. foreach($dataas$key=>$value){
  30. $itemKey=array_keys($value)[0];
  31. if(is_int($itemKey)){
  32. $data[$key]=$value[$itemKey];
  33. }elseif(is_string($itemKey)){
  34. $data[$itemKey]=$value[$itemKey];
  35. unset($data[$key]);
  36. }
  37. }
  38. return$data;
  39. }

对比最后结果:

原版又是蛮长的等待,不知道校验啥去了(没继续追代码了,有兴趣的童鞋可以去跟下玩):

  1. Array
  2. (
  3. [0]=>HTTP/1.0302Found
  4. [Accept-Ranges]=>bytes
  5. [Cache-Control]=>max-age=300
  6. [Content-Type]=>Array
  7. (
  8. [0]=>text/html;charset=utf-8
  9. [1]=>text/html;charset=utf-8
  10. )
  11. [Date]=>Array
  12. (
  13. [0]=>Fri,12Dec201415:35:40GMT
  14. [1]=>Fri,12Dec201415:35:43GMT
  15. )
  16. [Expires]=>Fri,12Dec201415:40:40GMT
  17. [Last-Modified]=>Wed,11Jan198408:00:00GMT
  18. [Link]=><http://www.gravatar.com/avatar/[省略...]?s=42&d=http%3A%2F%2F[省略...]&r=G>; rel="canonical"
  19. [Location]=>http://i2.wp.com/[省略...]
  20. [Server]=>Array
  21. (
  22. [0]=>ECS(oxr/838B)
  23. [1]=>nginx
  24. )
  25. [Source-Age]=>85
  26. [Via]=>1.1varnish
  27. [X-Cache]=>302-HIT
  28. [X-Varnish]=>14702550881470006304
  29. [Content-Length]=>0
  30. [Connection]=>Array
  31. (
  32. [0]=>close
  33. [1]=>close
  34. )
  35. [1]=>HTTP/1.1504Gateway Timeout
  36. )

轮子版返回(瞬间返回,两者内容略有不同,你仔细看就能发现一些有趣的地方了):

  1. Array
  2. (
  3. [0]=>HTTP/1.1302Found
  4. [Accept-Ranges]=>bytes
  5. [Via]=>1.1varnish
  6. [Cache-Control]=>max-age=300
  7. [Server]=>ECS(oxr/838B)
  8. [Content-Type]=>text/html;charset=utf-8
  9. [X-Varnish]=>14702550881470006304
  10. [Date]=>Fri,12Dec201420:31:02GMT
  11. [Location]=>http://i2.wp.com/[省略...]
  12. [Expires]=>Fri,12Dec201420:36:02GMT
  13. [Source-Age]=>85
  14. [Last-Modified]=>Wed,11Jan198408:00:00GMT
  15. [X-Cache]=>302-HIT
  16. [Link]=><http://www.gravatar.com/avatar/[省略...]?s=42&d=http%3A%2F%2F[省略...]&r=G>; rel="canonical"
  17. [Content-Length]=>0
  18. )