Completed
Push — master ( 86e979...7efc92 )
by Jaisen
9s
created

MultiCurl::reset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 4
nc 1
nop 0
1
<?php namespace JMathai\PhpMultiCurl;
2
/*if(!class_exists('MultiCurlManager'))
0 ignored issues
show
Unused Code Comprehensibility introduced by
74% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
3
  include 'MultiCurlManager.php';
4
if(!class_exists('MultiCurlSequence'))
5
  include 'MultiCurlSequence.php';
6
if(!class_exists('MultiCurlException'))
7
  include 'MultiCurlException.php';*/
8
9
/**
10
 * MultiCurl multicurl http client
11
 *
12
 * @author Jaisen Mathai <[email protected]>
13
 */
14
class MultiCurl
15
{
16
  const timeout = 3;
17
  private static $inst = null;
18
  /* @TODO make this private and add a method to set it to 0 */
19
  public static $singleton = 0;
20
21
  private $mc;
22
  private $running;
23
  private $execStatus;
24
  private $sleepIncrement = 1.1;
25
  private $requests = array();
26
  private $responses = array();
27
  private $properties = array();
28
  private static $timers = array();
29
30
  public function __construct()
31
  {
32
    if(self::$singleton === 0)
33
    {
34
      throw new MultiCurlException('This class cannot be instantiated by the new keyword.  You must instantiate it using: $obj = MultiCurl::getInstance();');
35
    }
36
37
    $this->mc = curl_multi_init();
38
    $this->properties = array(
39
      'code'  => CURLINFO_HTTP_CODE,
40
      'time'  => CURLINFO_TOTAL_TIME,
41
      'length'=> CURLINFO_CONTENT_LENGTH_DOWNLOAD,
42
      'type'  => CURLINFO_CONTENT_TYPE,
43
      'url'   => CURLINFO_EFFECTIVE_URL
44
      );
45
  }
46
  
47
  public function reset(){
48
      $this->requests = array();
49
      $this->responses = array();
50
      self::$timers = array();
51
  }
52
53
  public function addUrl($url, $options = array())
54
  {
55
    $ch = curl_init($url);
56
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
57
    foreach($options as $option=>$value)
58
    {
59
        curl_setopt($ch, $option, $value);
60
    }
61
    return $this->addCurl($ch);
62
  }
63
64
  public function addCurl($ch)
65
  {
66
    if(gettype($ch) !== 'resource')
67
    {
68
      throw new MultiCurlInvalidParameterException('Parameter must be a valid curl handle');
69
    }
70
71
    $key = $this->getKey($ch);
72
    $this->requests[$key] = $ch;
73
    curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, 'headerCallback'));
74
75
    $code = curl_multi_add_handle($this->mc, $ch);
76
    $this->startTimer($key);
77
    
78
    // (1)
79
    if($code === CURLM_OK || $code === CURLM_CALL_MULTI_PERFORM)
80
    {
81
      do
82
      {
83
        $this->execStatus = curl_multi_exec($this->mc, $this->running);
84
      } while ($this->execStatus === CURLM_CALL_MULTI_PERFORM);
85
86
      return new MultiCurlManager($key);
87
    }
88
    else
89
    {
90
      return $code;
91
    }
92
  }
93
94
  public function getResult($key = null)
95
  {
96
    if($key != null)
97
    {
98
      if(isset($this->responses[$key]['code']))
99
      {
100
        return $this->responses[$key];
101
      }
102
103
      $innerSleepInt = $outerSleepInt = 1;
104
      while($this->running && ($this->execStatus == CURLM_OK || $this->execStatus == CURLM_CALL_MULTI_PERFORM))
105
      {
106
        usleep(intval($outerSleepInt));
107
        $outerSleepInt = intval(max(1, ($outerSleepInt*$this->sleepIncrement)));
108
        $ms=curl_multi_select($this->mc, 0);
109
110
        // bug in PHP 5.3.18+ where curl_multi_select can return -1
111
        // https://bugs.php.net/bug.php?id=63411
112
        if($ms === -1)
113
          usleep(100000);
114
115
        // see pull request https://github.com/jmathai/php-multi-curl/pull/17
116
        // details here http://curl.haxx.se/libcurl/c/libcurl-errors.html
117
        if($ms >= CURLM_CALL_MULTI_PERFORM)
118
        {
119
          do{
120
            $this->execStatus = curl_multi_exec($this->mc, $this->running);
121
            usleep(intval($innerSleepInt));
122
            $innerSleepInt = intval(max(1, ($innerSleepInt*$this->sleepIncrement)));
123
          }while($this->execStatus==CURLM_CALL_MULTI_PERFORM);
124
          $innerSleepInt = 1;
125
        }
126
        $this->storeResponses();
127
        if(isset($this->responses[$key]['data']))
128
        {
129
          return $this->responses[$key];
130
        }
131
      }
132
      return null;
133
    }
134
    return false;
135
  }
136
137
  public static function getSequence()
138
  {
139
    return new MultiCurlSequence(self::$timers);
140
  }
141
142
  public static function getTimers()
143
  {
144
    return self::$timers;
145
  }
146
147
  public function inject($key, $value)
148
  {
149
    $this->$key = $value;
150
  }
151
152
  private function getKey($ch)
153
  {
154
    return (string)$ch;
155
  }
156
157
  private function headerCallback($ch, $header)
158
  {
159
    $_header = trim($header);
160
    $colonPos= strpos($_header, ':');
161
    if($colonPos > 0)
162
    {
163
      $key = substr($_header, 0, $colonPos);
164
      $val = preg_replace('/^\W+/','',substr($_header, $colonPos));
165
      $this->responses[$this->getKey($ch)]['headers'][$key] = $val;
166
    }
167
    return strlen($header);
168
  }
169
170
  private function storeResponses()
171
  {
172
    while($done = curl_multi_info_read($this->mc))
173
    {
174
      $this->storeResponse($done);
175
    }
176
  }
177
178
  private function storeResponse($done, $isAsynchronous = true)
179
  {
180
    $key = $this->getKey($done['handle']);
181
    $this->stopTimer($key, $done);
182
    if($isAsynchronous)
183
      $this->responses[$key]['data'] = curl_multi_getcontent($done['handle']);
184
    else
185
      $this->responses[$key]['data'] = curl_exec($done['handle']);
186
187
    $this->responses[$key]['response'] = $this->responses[$key]['data'];
188
189
    foreach($this->properties as $name => $const)
190
    {
191
      $this->responses[$key][$name] = curl_getinfo($done['handle'], $const);
192
    }
193
    if($isAsynchronous)
194
      curl_multi_remove_handle($this->mc, $done['handle']);
195
    curl_close($done['handle']);
196
  }
197
198
  private function startTimer($key)
199
  {
200
    self::$timers[$key]['start'] = microtime(true);
201
  }
202
203
  private function stopTimer($key, $done)
204
  {
205
      self::$timers[$key]['end'] = microtime(true);
206
      self::$timers[$key]['api'] = curl_getinfo($done['handle'], CURLINFO_EFFECTIVE_URL);
207
      self::$timers[$key]['time'] = curl_getinfo($done['handle'], CURLINFO_TOTAL_TIME);
208
      self::$timers[$key]['code'] = curl_getinfo($done['handle'], CURLINFO_HTTP_CODE);
209
  }
210
211
  public static function getInstance()
212
  {
213
    if(self::$inst == null)
214
    {
215
      self::$singleton = 1;
216
      self::$inst = new MultiCurl();
217
    }
218
219
    return self::$inst;
220
  }
221
}
222
223
/*
224
 * Credits:
225
 *  - (1) Alistair pointed out that curl_multi_add_handle can return CURLM_CALL_MULTI_PERFORM on success.
226
 */
227