r107660 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r107659‎ | r107660 | r107661 >
Date:21:58, 30 December 2011
Author:reedy
Status:ok
Tags:
Comment:
Revert r107654, whitespace conversion wasn't intentional
Modified paths:
  • /trunk/extensions/TweetANew/lib/tmhOAuth.php (modified) (history)

Diff [purge]

Index: trunk/extensions/TweetANew/lib/tmhOAuth.php
@@ -7,649 +7,604 @@
88 * REST requests. OAuth authentication is sent using the an Authorization Header.
99 *
1010 * @author themattharris
11 - * @version 0.60
 11+ * @version 0.57
1212 *
13 - * 29 December 2011
 13+ * 11 December 2011
1414 */
1515 class tmhOAuth {
16 - const VERSION = 0.60;
 16+ const VERSION = 0.57;
1717
18 - /**
19 - * Creates a new tmhOAuth object
20 - *
21 - * @param string $config, the configuration to use for this request
22 - */
23 - function __construct($config) {
24 - $this->params = array();
25 - $this->headers = array();
26 - $this->auto_fixed_time = false;
27 - $this->buffer = null;
 18+ /**
 19+ * Creates a new tmhOAuth object
 20+ *
 21+ * @param string $config, the configuration to use for this request
 22+ */
 23+ function __construct($config) {
 24+ $this->params = array();
 25+ $this->headers = array();
 26+ $this->auto_fixed_time = false;
 27+ $this->buffer = null;
2828
29 - // default configuration options
30 - $this->config = array_merge(
31 - array(
32 - // leave 'user_agent' blank for default, otherwise set this to
33 - // something that clearly identifies your app
34 - 'user_agent' => '',
 29+ // default configuration options
 30+ $this->config = array_merge(
 31+ array(
 32+ 'user_agent' => 'tmhOAuth ' . self::VERSION . ' - //github.com/themattharris/tmhOAuth',
 33+ 'consumer_key' => '',
 34+ 'consumer_secret' => '',
 35+ 'user_token' => '',
 36+ 'user_secret' => '',
 37+ 'use_ssl' => true,
 38+ 'host' => 'api.twitter.com',
 39+ 'debug' => false,
 40+ 'force_nonce' => false,
 41+ 'nonce' => false, // used for checking signatures. leave as false for auto
 42+ 'force_timestamp' => false,
 43+ 'timestamp' => false, // used for checking signatures. leave as false for auto
 44+ 'oauth_version' => '1.0',
3545
36 - 'use_ssl' => true,
37 - 'host' => 'api.twitter.com',
 46+ // you probably don't want to change any of these curl values
 47+ 'curl_connecttimeout' => 30,
 48+ 'curl_timeout' => 10,
 49+ // for security you may want to set this to TRUE. If you do you need
 50+ // to install the servers certificate in your local certificate store.
 51+ 'curl_ssl_verifypeer' => false,
 52+ 'curl_followlocation' => false, // whether to follow redirects or not
 53+ // support for proxy servers
 54+ 'curl_proxy' => false, // really you don't want to use this if you are using streaming
 55+ 'curl_proxyuserpwd' => false, // format username:password for proxy, if required
 56+ 'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity
3857
39 - 'consumer_key' => '',
40 - 'consumer_secret' => '',
41 - 'user_token' => '',
42 - 'user_secret' => '',
43 - 'force_nonce' => false,
44 - 'nonce' => false, // used for checking signatures. leave as false for auto
45 - 'force_timestamp' => false,
46 - 'timestamp' => false, // used for checking signatures. leave as false for auto
 58+ // streaming API
 59+ 'is_streaming' => false,
 60+ 'streaming_eol' => "\r\n",
 61+ 'streaming_metrics_interval' => 60,
4762
48 - // oauth signing variables that are not dynamic
49 - 'oauth_version' => '1.0',
50 - 'oauth_signature_method' => 'HMAC-SHA1',
 63+ // header or querystring. You should always use header
 64+ // this is just to help me debug other developers
 65+ // implementations
 66+ 'as_header' => true,
 67+ ),
 68+ $config
 69+ );
 70+ }
5171
52 - // you probably don't want to change any of these curl values
53 - 'curl_connecttimeout' => 30,
54 - 'curl_timeout' => 10,
 72+ /**
 73+ * Generates a random OAuth nonce.
 74+ * If 'force_nonce' is true a nonce is not generated and the value in the configuration will be retained.
 75+ *
 76+ * @param string $length how many characters the nonce should be before MD5 hashing. default 12
 77+ * @param string $include_time whether to include time at the beginning of the nonce. default true
 78+ * @return void
 79+ */
 80+ private function create_nonce($length=12, $include_time=true) {
 81+ if ($this->config['force_nonce'] == false) {
 82+ $sequence = array_merge(range(0,9), range('A','Z'), range('a','z'));
 83+ $length = $length > count($sequence) ? count($sequence) : $length;
 84+ shuffle($sequence);
5585
56 - // for security these should always be set to true.
57 - 'curl_ssl_verifyhost' => true,
58 - 'curl_ssl_verifypeer' => true,
 86+ $prefix = $include_time ? microtime() : '';
 87+ $this->config['nonce'] = md5(substr($prefix . implode($sequence), 0, $length));
 88+ }
 89+ }
5990
60 - // you can get the latest cacert.pem from here http://curl.haxx.se/ca/cacert.pem
61 - 'curl_cainfo' => dirname(__FILE__) . '/cacert.pem',
62 - 'curl_capath' => dirname(__FILE__),
 91+ /**
 92+ * Generates a timestamp.
 93+ * If 'force_timestamp' is true a nonce is not generated and the value in the configuration will be retained.
 94+ *
 95+ * @return void
 96+ */
 97+ private function create_timestamp() {
 98+ $this->config['timestamp'] = ($this->config['force_timestamp'] == false ? time() : $this->config['timestamp']);
 99+ }
63100
64 - 'curl_followlocation' => false, // whether to follow redirects or not
 101+ /**
 102+ * Encodes the string or array passed in a way compatible with OAuth.
 103+ * If an array is passed each array value will will be encoded.
 104+ *
 105+ * @param mixed $data the scalar or array to encode
 106+ * @return $data encoded in a way compatible with OAuth
 107+ */
 108+ private function safe_encode($data) {
 109+ if (is_array($data)) {
 110+ return array_map(array($this, 'safe_encode'), $data);
 111+ } else if (is_scalar($data)) {
 112+ return str_ireplace(
 113+ array('+', '%7E'),
 114+ array(' ', '~'),
 115+ rawurlencode($data)
 116+ );
 117+ } else {
 118+ return '';
 119+ }
 120+ }
65121
66 - // support for proxy servers
67 - 'curl_proxy' => false, // really you don't want to use this if you are using streaming
68 - 'curl_proxyuserpwd' => false, // format username:password for proxy, if required
69 - 'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity
 122+ /**
 123+ * Decodes the string or array from it's URL encoded form
 124+ * If an array is passed each array value will will be decoded.
 125+ *
 126+ * @param mixed $data the scalar or array to decode
 127+ * @return $data decoded from the URL encoded form
 128+ */
 129+ private function safe_decode($data) {
 130+ if (is_array($data)) {
 131+ return array_map(array($this, 'safe_decode'), $data);
 132+ } else if (is_scalar($data)) {
 133+ return rawurldecode($data);
 134+ } else {
 135+ return '';
 136+ }
 137+ }
70138
71 - // streaming API
72 - 'is_streaming' => false,
73 - 'streaming_eol' => "\r\n",
74 - 'streaming_metrics_interval' => 60,
 139+ /**
 140+ * Returns an array of the standard OAuth parameters.
 141+ *
 142+ * @return array all required OAuth parameters, safely encoded
 143+ */
 144+ private function get_defaults() {
 145+ $defaults = array(
 146+ 'oauth_version' => $this->config['oauth_version'],
 147+ 'oauth_nonce' => $this->config['nonce'],
 148+ 'oauth_timestamp' => $this->config['timestamp'],
 149+ 'oauth_consumer_key' => $this->config['consumer_key'],
 150+ 'oauth_signature_method' => 'HMAC-SHA1',
 151+ );
75152
76 - // header or querystring. You should always use header!
77 - // this is just to help me debug other developers implementations
78 - 'as_header' => true,
79 - 'debug' => false,
80 - ),
81 - $config
82 - );
83 - $this->set_user_agent();
84 - }
 153+ // include the user token if it exists
 154+ if ( $this->config['user_token'] )
 155+ $defaults['oauth_token'] = $this->config['user_token'];
85156
86 - function set_user_agent() {
87 - if (!empty($this->config['user_agent']))
88 - return;
 157+ // safely encode
 158+ foreach ($defaults as $k => $v) {
 159+ $_defaults[$this->safe_encode($k)] = $this->safe_encode($v);
 160+ }
89161
90 - if ($this->config['curl_ssl_verifyhost'] && $this->config['curl_ssl_verifypeer']) {
91 - $ssl = '+SSL';
92 - } else {
93 - $ssl = '-SSL';
94 - }
 162+ return $_defaults;
 163+ }
95164
96 - $ua = 'tmhOAuth ' . self::VERSION . $ssl . ' - //github.com/themattharris/tmhOAuth';
97 - $this->config['user_agent'] = $ua;
98 - }
 165+ /**
 166+ * Extracts and decodes OAuth parameters from the passed string
 167+ *
 168+ * @param string $body the response body from an OAuth flow method
 169+ * @return array the response body safely decoded to an array of key => values
 170+ */
 171+ function extract_params($body) {
 172+ $kvs = explode('&', $body);
 173+ $decoded = array();
 174+ foreach ($kvs as $kv) {
 175+ $kv = explode('=', $kv, 2);
 176+ $kv[0] = $this->safe_decode($kv[0]);
 177+ $kv[1] = $this->safe_decode($kv[1]);
 178+ $decoded[$kv[0]] = $kv[1];
 179+ }
 180+ return $decoded;
 181+ }
99182
100 - /**
101 - * Generates a random OAuth nonce.
102 - * If 'force_nonce' is true a nonce is not generated and the value in the configuration will be retained.
103 - *
104 - * @param string $length how many characters the nonce should be before MD5 hashing. default 12
105 - * @param string $include_time whether to include time at the beginning of the nonce. default true
106 - * @return void
107 - */
108 - private function create_nonce($length=12, $include_time=true) {
109 - if ($this->config['force_nonce'] == false) {
110 - $sequence = array_merge(range(0,9), range('A','Z'), range('a','z'));
111 - $length = $length > count($sequence) ? count($sequence) : $length;
112 - shuffle($sequence);
 183+ /**
 184+ * Prepares the HTTP method for use in the base string by converting it to
 185+ * uppercase.
 186+ *
 187+ * @param string $method an HTTP method such as GET or POST
 188+ * @return void value is stored to a class variable
 189+ * @author themattharris
 190+ */
 191+ private function prepare_method($method) {
 192+ $this->method = strtoupper($method);
 193+ }
113194
114 - $prefix = $include_time ? microtime() : '';
115 - $this->config['nonce'] = md5(substr($prefix . implode('', $sequence), 0, $length));
116 - }
117 - }
 195+ /**
 196+ * Prepares the URL for use in the base string by ripping it apart and
 197+ * reconstructing it.
 198+ *
 199+ * Ref: 3.4.1.2
 200+ *
 201+ * @param string $url the request URL
 202+ * @return void value is stored to a class variable
 203+ * @author themattharris
 204+ */
 205+ private function prepare_url($url) {
 206+ $parts = parse_url($url);
118207
119 - /**
120 - * Generates a timestamp.
121 - * If 'force_timestamp' is true a nonce is not generated and the value in the configuration will be retained.
122 - *
123 - * @return void
124 - */
125 - private function create_timestamp() {
126 - $this->config['timestamp'] = ($this->config['force_timestamp'] == false ? time() : $this->config['timestamp']);
127 - }
 208+ $port = isset($parts['port']) ? $parts['port'] : false;
 209+ $scheme = $parts['scheme'];
 210+ $host = $parts['host'];
 211+ $path = isset($parts['path']) ? $parts['path'] : false;
128212
129 - /**
130 - * Encodes the string or array passed in a way compatible with OAuth.
131 - * If an array is passed each array value will will be encoded.
132 - *
133 - * @param mixed $data the scalar or array to encode
134 - * @return $data encoded in a way compatible with OAuth
135 - */
136 - private function safe_encode($data) {
137 - if (is_array($data)) {
138 - return array_map(array($this, 'safe_encode'), $data);
139 - } else if (is_scalar($data)) {
140 - return str_ireplace(
141 - array('+', '%7E'),
142 - array(' ', '~'),
143 - rawurlencode($data)
144 - );
145 - } else {
146 - return '';
147 - }
148 - }
 213+ $port or $port = ($scheme == 'https') ? '443' : '80';
149214
150 - /**
151 - * Decodes the string or array from it's URL encoded form
152 - * If an array is passed each array value will will be decoded.
153 - *
154 - * @param mixed $data the scalar or array to decode
155 - * @return $data decoded from the URL encoded form
156 - */
157 - private function safe_decode($data) {
158 - if (is_array($data)) {
159 - return array_map(array($this, 'safe_decode'), $data);
160 - } else if (is_scalar($data)) {
161 - return rawurldecode($data);
162 - } else {
163 - return '';
164 - }
165 - }
 215+ if (($scheme == 'https' && $port != '443')
 216+ || ($scheme == 'http' && $port != '80')) {
 217+ $host = "$host:$port";
 218+ }
 219+ $this->url = strtolower("$scheme://$host$path");
 220+ }
166221
167 - /**
168 - * Returns an array of the standard OAuth parameters.
169 - *
170 - * @return array all required OAuth parameters, safely encoded
171 - */
172 - private function get_defaults() {
173 - $defaults = array(
174 - 'oauth_version' => $this->config['oauth_version'],
175 - 'oauth_nonce' => $this->config['nonce'],
176 - 'oauth_timestamp' => $this->config['timestamp'],
177 - 'oauth_consumer_key' => $this->config['consumer_key'],
178 - 'oauth_signature_method' => $this->config['oauth_signature_method'],
179 - );
 222+ /**
 223+ * Prepares all parameters for the base string and request.
 224+ * Multipart parameters are ignored as they are not defined in the specification,
 225+ * all other types of parameter are encoded for compatibility with OAuth.
 226+ *
 227+ * @param array $params the parameters for the request
 228+ * @return void prepared values are stored in class variables
 229+ */
 230+ private function prepare_params($params) {
 231+ // do not encode multipart parameters, leave them alone
 232+ if ($this->config['multipart']) {
 233+ $this->request_params = $params;
 234+ $params = array();
 235+ }
180236
181 - // include the user token if it exists
182 - if ( $this->config['user_token'] )
183 - $defaults['oauth_token'] = $this->config['user_token'];
 237+ // signing parameters are request parameters + OAuth default parameters
 238+ $this->signing_params = array_merge($this->get_defaults(), (array)$params);
184239
185 - // safely encode
186 - foreach ($defaults as $k => $v) {
187 - $_defaults[$this->safe_encode($k)] = $this->safe_encode($v);
188 - }
 240+ // Remove oauth_signature if present
 241+ // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
 242+ if (isset($this->signing_params['oauth_signature'])) {
 243+ unset($this->signing_params['oauth_signature']);
 244+ }
189245
190 - return $_defaults;
191 - }
 246+ // Parameters are sorted by name, using lexicographical byte value ordering.
 247+ // Ref: Spec: 9.1.1 (1)
 248+ uksort($this->signing_params, 'strcmp');
192249
193 - /**
194 - * Extracts and decodes OAuth parameters from the passed string
195 - *
196 - * @param string $body the response body from an OAuth flow method
197 - * @return array the response body safely decoded to an array of key => values
198 - */
199 - function extract_params($body) {
200 - $kvs = explode('&', $body);
201 - $decoded = array();
202 - foreach ($kvs as $kv) {
203 - $kv = explode('=', $kv, 2);
204 - $kv[0] = $this->safe_decode($kv[0]);
205 - $kv[1] = $this->safe_decode($kv[1]);
206 - $decoded[$kv[0]] = $kv[1];
207 - }
208 - return $decoded;
209 - }
 250+ // encode. Also sort the signed parameters from the POST parameters
 251+ foreach ($this->signing_params as $k => $v) {
 252+ $k = $this->safe_encode($k);
 253+ $v = $this->safe_encode($v);
 254+ $_signing_params[$k] = $v;
 255+ $kv[] = "{$k}={$v}";
 256+ }
210257
211 - /**
212 - * Prepares the HTTP method for use in the base string by converting it to
213 - * uppercase.
214 - *
215 - * @param string $method an HTTP method such as GET or POST
216 - * @return void value is stored to a class variable
217 - * @author themattharris
218 - */
219 - private function prepare_method($method) {
220 - $this->method = strtoupper($method);
221 - }
 258+ // auth params = the default oauth params which are present in our collection of signing params
 259+ $this->auth_params = array_intersect_key($this->get_defaults(), $_signing_params);
 260+ if (isset($_signing_params['oauth_callback'])) {
 261+ $this->auth_params['oauth_callback'] = $_signing_params['oauth_callback'];
 262+ unset($_signing_params['oauth_callback']);
 263+ }
222264
223 - /**
224 - * Prepares the URL for use in the base string by ripping it apart and
225 - * reconstructing it.
226 - *
227 - * Ref: 3.4.1.2
228 - *
229 - * @param string $url the request URL
230 - * @return void value is stored to a class variable
231 - * @author themattharris
232 - */
233 - private function prepare_url($url) {
234 - $parts = parse_url($url);
 265+ // request_params is already set if we're doing multipart, if not we need to set them now
 266+ if ( ! $this->config['multipart'])
 267+ $this->request_params = array_diff_key($_signing_params, $this->get_defaults());
235268
236 - $port = isset($parts['port']) ? $parts['port'] : false;
237 - $scheme = $parts['scheme'];
238 - $host = $parts['host'];
239 - $path = isset($parts['path']) ? $parts['path'] : false;
 269+ // create the parameter part of the base string
 270+ $this->signing_params = implode('&', $kv);
 271+ }
240272
241 - $port or $port = ($scheme == 'https') ? '443' : '80';
 273+ /**
 274+ * Prepares the OAuth signing key
 275+ *
 276+ * @return void prepared signing key is stored in a class variables
 277+ */
 278+ private function prepare_signing_key() {
 279+ $this->signing_key = $this->safe_encode($this->config['consumer_secret']) . '&' . $this->safe_encode($this->config['user_secret']);
 280+ }
242281
243 - if (($scheme == 'https' && $port != '443')
244 - || ($scheme == 'http' && $port != '80')) {
245 - $host = "$host:$port";
246 - }
247 - $this->url = strtolower("$scheme://$host$path");
248 - }
 282+ /**
 283+ * Prepare the base string.
 284+ * Ref: Spec: 9.1.3 ("Concatenate Request Elements")
 285+ *
 286+ * @return void prepared base string is stored in a class variables
 287+ */
 288+ private function prepare_base_string() {
 289+ $base = array(
 290+ $this->method,
 291+ $this->url,
 292+ $this->signing_params
 293+ );
 294+ $this->base_string = implode('&', $this->safe_encode($base));
 295+ }
249296
250 - /**
251 - * Prepares all parameters for the base string and request.
252 - * Multipart parameters are ignored as they are not defined in the specification,
253 - * all other types of parameter are encoded for compatibility with OAuth.
254 - *
255 - * @param array $params the parameters for the request
256 - * @return void prepared values are stored in class variables
257 - */
258 - private function prepare_params($params) {
259 - // do not encode multipart parameters, leave them alone
260 - if ($this->config['multipart']) {
261 - $this->request_params = $params;
262 - $params = array();
263 - }
 297+ /**
 298+ * Prepares the Authorization header
 299+ *
 300+ * @return void prepared authorization header is stored in a class variables
 301+ */
 302+ private function prepare_auth_header() {
 303+ $this->headers = array();
 304+ uksort($this->auth_params, 'strcmp');
 305+ if (!$this->config['as_header']) :
 306+ $this->request_params = array_merge($this->request_params, $this->auth_params);
 307+ return;
 308+ endif;
264309
265 - // signing parameters are request parameters + OAuth default parameters
266 - $this->signing_params = array_merge($this->get_defaults(), (array)$params);
 310+ foreach ($this->auth_params as $k => $v) {
 311+ $kv[] = "{$k}=\"{$v}\"";
 312+ }
 313+ $this->auth_header = 'OAuth ' . implode(', ', $kv);
 314+ $this->headers['Authorization'] = $this->auth_header;
 315+ }
267316
268 - // Remove oauth_signature if present
269 - // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
270 - if (isset($this->signing_params['oauth_signature'])) {
271 - unset($this->signing_params['oauth_signature']);
272 - }
 317+ /**
 318+ * Signs the request and adds the OAuth signature. This runs all the request
 319+ * parameter preparation methods.
 320+ *
 321+ * @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
 322+ * @param string $url the request URL without query string parameters
 323+ * @param array $params the request parameters as an array of key=value pairs
 324+ * @param string $useauth whether to use authentication when making the request.
 325+ */
 326+ private function sign($method, $url, $params, $useauth) {
 327+ $this->prepare_method($method);
 328+ $this->prepare_url($url);
 329+ $this->prepare_params($params);
273330
274 - // Parameters are sorted by name, using lexicographical byte value ordering.
275 - // Ref: Spec: 9.1.1 (1)
276 - uksort($this->signing_params, 'strcmp');
 331+ // we don't sign anything is we're not using auth
 332+ if ($useauth) {
 333+ $this->prepare_base_string();
 334+ $this->prepare_signing_key();
277335
278 - // encode. Also sort the signed parameters from the POST parameters
279 - foreach ($this->signing_params as $k => $v) {
280 - $k = $this->safe_encode($k);
281 - $v = $this->safe_encode($v);
282 - $_signing_params[$k] = $v;
283 - $kv[] = "{$k}={$v}";
284 - }
 336+ $this->auth_params['oauth_signature'] = $this->safe_encode(
 337+ base64_encode(
 338+ hash_hmac(
 339+ 'sha1', $this->base_string, $this->signing_key, true
 340+ )));
285341
286 - // auth params = the default oauth params which are present in our collection of signing params
287 - $this->auth_params = array_intersect_key($this->get_defaults(), $_signing_params);
288 - if (isset($_signing_params['oauth_callback'])) {
289 - $this->auth_params['oauth_callback'] = $_signing_params['oauth_callback'];
290 - unset($_signing_params['oauth_callback']);
291 - }
 342+ $this->prepare_auth_header();
 343+ }
 344+ }
292345
293 - if (isset($_signing_params['oauth_verifier'])) {
294 - $this->auth_params['oauth_verifier'] = $_signing_params['oauth_verifier'];
295 - unset($_signing_params['oauth_verifier']);
296 - }
 346+ /**
 347+ * Make an HTTP request using this library. This method doesn't return anything.
 348+ * Instead the response should be inspected directly.
 349+ *
 350+ * @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
 351+ * @param string $url the request URL without query string parameters
 352+ * @param array $params the request parameters as an array of key=value pairs
 353+ * @param string $useauth whether to use authentication when making the request. Default true.
 354+ * @param string $multipart whether this request contains multipart data. Default false
 355+ */
 356+ function request($method, $url, $params=array(), $useauth=true, $multipart=false) {
 357+ $this->config['multipart'] = $multipart;
297358
298 - // request_params is already set if we're doing multipart, if not we need to set them now
299 - if ( ! $this->config['multipart'])
300 - $this->request_params = array_diff_key($_signing_params, $this->get_defaults());
 359+ $this->create_nonce();
 360+ $this->create_timestamp();
301361
302 - // create the parameter part of the base string
303 - $this->signing_params = implode('&', $kv);
304 - }
 362+ $this->sign($method, $url, $params, $useauth);
 363+ return $this->curlit();
 364+ }
305365
306 - /**
307 - * Prepares the OAuth signing key
308 - *
309 - * @return void prepared signing key is stored in a class variables
310 - */
311 - private function prepare_signing_key() {
312 - $this->signing_key = $this->safe_encode($this->config['consumer_secret']) . '&' . $this->safe_encode($this->config['user_secret']);
313 - }
 366+ /**
 367+ * Make a long poll HTTP request using this library. This method is
 368+ * different to the other request methods as it isn't supposed to disconnect
 369+ *
 370+ * Using this method expects a callback which will receive the streaming
 371+ * responses.
 372+ *
 373+ * @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
 374+ * @param string $url the request URL without query string parameters
 375+ * @param array $params the request parameters as an array of key=value pairs
 376+ * @param string $callback the callback function to stream the buffer to.
 377+ */
 378+ function streaming_request($method, $url, $params=array(), $callback='') {
 379+ if ( ! empty($callback) ) {
 380+ if ( ! function_exists($callback) ) {
 381+ return false;
 382+ }
 383+ $this->config['streaming_callback'] = $callback;
 384+ }
 385+ $this->metrics['start'] = time();
 386+ $this->metrics['interval_start'] = $this->metrics['start'];
 387+ $this->metrics['tweets'] = 0;
 388+ $this->metrics['last_tweets'] = 0;
 389+ $this->metrics['bytes'] = 0;
 390+ $this->metrics['last_bytes'] = 0;
 391+ $this->config['is_streaming'] = true;
 392+ $this->request($method, $url, $params);
 393+ }
314394
315 - /**
316 - * Prepare the base string.
317 - * Ref: Spec: 9.1.3 ("Concatenate Request Elements")
318 - *
319 - * @return void prepared base string is stored in a class variables
320 - */
321 - private function prepare_base_string() {
322 - $base = array(
323 - $this->method,
324 - $this->url,
325 - $this->signing_params
326 - );
327 - $this->base_string = implode('&', $this->safe_encode($base));
328 - }
 395+ /**
 396+ * Handles the updating of the current Streaming API metrics.
 397+ */
 398+ function update_metrics() {
 399+ $now = time();
 400+ if (($this->metrics['interval_start'] + $this->config['streaming_metrics_interval']) > $now)
 401+ return false;
329402
330 - /**
331 - * Prepares the Authorization header
332 - *
333 - * @return void prepared authorization header is stored in a class variables
334 - */
335 - private function prepare_auth_header() {
336 - $this->headers = array();
337 - uksort($this->auth_params, 'strcmp');
338 - if (!$this->config['as_header']) :
339 - $this->request_params = array_merge($this->request_params, $this->auth_params);
340 - return;
341 - endif;
 403+ $this->metrics['tps'] = round( ($this->metrics['tweets'] - $this->metrics['last_tweets']) / $this->config['streaming_metrics_interval'], 2);
 404+ $this->metrics['bps'] = round( ($this->metrics['bytes'] - $this->metrics['last_bytes']) / $this->config['streaming_metrics_interval'], 2);
342405
343 - foreach ($this->auth_params as $k => $v) {
344 - $kv[] = "{$k}=\"{$v}\"";
345 - }
346 - $this->auth_header = 'OAuth ' . implode(', ', $kv);
347 - $this->headers['Authorization'] = $this->auth_header;
348 - }
 406+ $this->metrics['last_bytes'] = $this->metrics['bytes'];
 407+ $this->metrics['last_tweets'] = $this->metrics['tweets'];
 408+ $this->metrics['interval_start'] = $now;
 409+ return $this->metrics;
 410+ }
349411
350 - /**
351 - * Signs the request and adds the OAuth signature. This runs all the request
352 - * parameter preparation methods.
353 - *
354 - * @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
355 - * @param string $url the request URL without query string parameters
356 - * @param array $params the request parameters as an array of key=value pairs
357 - * @param string $useauth whether to use authentication when making the request.
358 - */
359 - private function sign($method, $url, $params, $useauth) {
360 - $this->prepare_method($method);
361 - $this->prepare_url($url);
362 - $this->prepare_params($params);
 412+ /**
 413+ * Utility function to create the request URL in the requested format
 414+ *
 415+ * @param string $request the API method without extension
 416+ * @param string $format the format of the response. Default json. Set to an empty string to exclude the format
 417+ * @return string the concatenation of the host, API version, API method and format
 418+ */
 419+ function url($request, $format='json') {
 420+ $format = strlen($format) > 0 ? ".$format" : '';
 421+ $proto = $this->config['use_ssl'] ? 'https:/' : 'http:/';
363422
364 - // we don't sign anything is we're not using auth
365 - if ($useauth) {
366 - $this->prepare_base_string();
367 - $this->prepare_signing_key();
 423+ // backwards compatibility with v0.1
 424+ if (isset($this->config['v']))
 425+ $this->config['host'] = $this->config['host'] . '/' . $this->config['v'];
368426
369 - $this->auth_params['oauth_signature'] = $this->safe_encode(
370 - base64_encode(
371 - hash_hmac(
372 - 'sha1', $this->base_string, $this->signing_key, true
373 - )));
 427+ return implode('/', array(
 428+ $proto,
 429+ $this->config['host'],
 430+ $request . $format
 431+ ));
 432+ }
374433
375 - $this->prepare_auth_header();
376 - }
377 - }
 434+ /**
 435+ * Public access to the private safe decode/encode methods
 436+ *
 437+ * @param string $text the text to transform
 438+ * @param string $mode the transformation mode. either encode or decode
 439+ * @return the string as transformed by the given mode
 440+ */
 441+ function transformText($text, $mode='encode') {
 442+ return $this->{"safe_$mode"}($text);
 443+ }
378444
379 - /**
380 - * Make an HTTP request using this library. This method doesn't return anything.
381 - * Instead the response should be inspected directly.
382 - *
383 - * @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
384 - * @param string $url the request URL without query string parameters
385 - * @param array $params the request parameters as an array of key=value pairs
386 - * @param string $useauth whether to use authentication when making the request. Default true.
387 - * @param string $multipart whether this request contains multipart data. Default false
388 - */
389 - function request($method, $url, $params=array(), $useauth=true, $multipart=false) {
390 - $this->config['multipart'] = $multipart;
 445+ /**
 446+ * Utility function to parse the returned curl headers and store them in the
 447+ * class array variable.
 448+ *
 449+ * @param object $ch curl handle
 450+ * @param string $header the response headers
 451+ * @return the string length of the header
 452+ */
 453+ private function curlHeader($ch, $header) {
 454+ $i = strpos($header, ':');
 455+ if ( ! empty($i) ) {
 456+ $key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
 457+ $value = trim(substr($header, $i + 2));
 458+ $this->response['headers'][$key] = $value;
 459+ }
 460+ return strlen($header);
 461+ }
391462
392 - $this->create_nonce();
393 - $this->create_timestamp();
 463+ /**
 464+ * Utility function to parse the returned curl buffer and store them until
 465+ * an EOL is found. The buffer for curl is an undefined size so we need
 466+ * to collect the content until an EOL is found.
 467+ *
 468+ * This function calls the previously defined streaming callback method.
 469+ *
 470+ * @param object $ch curl handle
 471+ * @param string $data the current curl buffer
 472+ */
 473+ private function curlWrite($ch, $data) {
 474+ $l = strlen($data);
 475+ if (strpos($data, $this->config['streaming_eol']) === false) {
 476+ $this->buffer .= $data;
 477+ return $l;
 478+ }
394479
395 - $this->sign($method, $url, $params, $useauth);
396 - return $this->curlit();
397 - }
 480+ $buffered = explode($this->config['streaming_eol'], $data);
 481+ $content = $this->buffer . $buffered[0];
398482
399 - /**
400 - * Make a long poll HTTP request using this library. This method is
401 - * different to the other request methods as it isn't supposed to disconnect
402 - *
403 - * Using this method expects a callback which will receive the streaming
404 - * responses.
405 - *
406 - * @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
407 - * @param string $url the request URL without query string parameters
408 - * @param array $params the request parameters as an array of key=value pairs
409 - * @param string $callback the callback function to stream the buffer to.
410 - */
411 - function streaming_request($method, $url, $params=array(), $callback='') {
412 - if ( ! empty($callback) ) {
413 - if ( ! function_exists($callback) ) {
414 - return false;
415 - }
416 - $this->config['streaming_callback'] = $callback;
417 - }
418 - $this->metrics['start'] = time();
419 - $this->metrics['interval_start'] = $this->metrics['start'];
420 - $this->metrics['tweets'] = 0;
421 - $this->metrics['last_tweets'] = 0;
422 - $this->metrics['bytes'] = 0;
423 - $this->metrics['last_bytes'] = 0;
424 - $this->config['is_streaming'] = true;
425 - $this->request($method, $url, $params);
426 - }
 483+ $this->metrics['tweets']++;
 484+ $this->metrics['bytes'] += strlen($content);
427485
428 - /**
429 - * Handles the updating of the current Streaming API metrics.
430 - */
431 - function update_metrics() {
432 - $now = time();
433 - if (($this->metrics['interval_start'] + $this->config['streaming_metrics_interval']) > $now)
434 - return false;
 486+ if ( ! function_exists($this->config['streaming_callback']))
 487+ return 0;
435488
436 - $this->metrics['tps'] = round( ($this->metrics['tweets'] - $this->metrics['last_tweets']) / $this->config['streaming_metrics_interval'], 2);
437 - $this->metrics['bps'] = round( ($this->metrics['bytes'] - $this->metrics['last_bytes']) / $this->config['streaming_metrics_interval'], 2);
 489+ $metrics = $this->update_metrics();
 490+ $stop = call_user_func(
 491+ $this->config['streaming_callback'],
 492+ $content,
 493+ strlen($content),
 494+ $metrics
 495+ );
 496+ $this->buffer = $buffered[1];
 497+ if ($stop)
 498+ return 0;
438499
439 - $this->metrics['last_bytes'] = $this->metrics['bytes'];
440 - $this->metrics['last_tweets'] = $this->metrics['tweets'];
441 - $this->metrics['interval_start'] = $now;
442 - return $this->metrics;
443 - }
 500+ return $l;
 501+ }
444502
445 - /**
446 - * Utility function to create the request URL in the requested format
447 - *
448 - * @param string $request the API method without extension
449 - * @param string $format the format of the response. Default json. Set to an empty string to exclude the format
450 - * @return string the concatenation of the host, API version, API method and format
451 - */
452 - function url($request, $format='json') {
453 - $format = strlen($format) > 0 ? ".$format" : '';
454 - $proto = $this->config['use_ssl'] ? 'https:/' : 'http:/';
 503+ /**
 504+ * Makes a curl request. Takes no parameters as all should have been prepared
 505+ * by the request method
 506+ *
 507+ * @return void response data is stored in the class variable 'response'
 508+ */
 509+ private function curlit() {
 510+ // method handling
 511+ switch ($this->method) {
 512+ case 'POST':
 513+ break;
 514+ default:
 515+ // GET, DELETE request so convert the parameters to a querystring
 516+ if ( ! empty($this->request_params)) {
 517+ foreach ($this->request_params as $k => $v) {
 518+ // Multipart params haven't been encoded yet.
 519+ // Not sure why you would do a multipart GET but anyway, here's the support for it
 520+ if ($this->config['multipart']) {
 521+ $params[] = $this->safe_encode($k) . '=' . $this->safe_encode($v);
 522+ } else {
 523+ $params[] = $k . '=' . $v;
 524+ }
 525+ }
 526+ $qs = implode('&', $params);
 527+ $this->url = strlen($qs) > 0 ? $this->url . '?' . $qs : $this->url;
 528+ $this->request_params = array();
 529+ }
 530+ break;
 531+ }
455532
456 - // backwards compatibility with v0.1
457 - if (isset($this->config['v']))
458 - $this->config['host'] = $this->config['host'] . '/' . $this->config['v'];
 533+ // configure curl
 534+ $c = curl_init();
 535+ curl_setopt_array($c, array(
 536+ CURLOPT_USERAGENT => $this->config['user_agent'],
 537+ CURLOPT_CONNECTTIMEOUT => $this->config['curl_connecttimeout'],
 538+ CURLOPT_TIMEOUT => $this->config['curl_timeout'],
 539+ CURLOPT_RETURNTRANSFER => TRUE,
 540+ CURLOPT_SSL_VERIFYPEER => $this->config['curl_ssl_verifypeer'],
 541+ CURLOPT_FOLLOWLOCATION => $this->config['curl_followlocation'],
 542+ CURLOPT_PROXY => $this->config['curl_proxy'],
 543+ CURLOPT_ENCODING => $this->config['curl_encoding'],
 544+ CURLOPT_URL => $this->url,
 545+ // process the headers
 546+ CURLOPT_HEADERFUNCTION => array($this, 'curlHeader'),
 547+ CURLOPT_HEADER => FALSE,
 548+ CURLINFO_HEADER_OUT => true,
 549+ ));
459550
460 - return implode('/', array(
461 - $proto,
462 - $this->config['host'],
463 - $request . $format
464 - ));
465 - }
 551+ if ($this->config['curl_proxyuserpwd'] !== false)
 552+ curl_setopt($c, CURLOPT_PROXYUSERPWD, $this->config['curl_proxyuserpwd']);
466553
467 - /**
468 - * Public access to the private safe decode/encode methods
469 - *
470 - * @param string $text the text to transform
471 - * @param string $mode the transformation mode. either encode or decode
472 - * @return the string as transformed by the given mode
473 - */
474 - function transformText($text, $mode='encode') {
475 - return $this->{"safe_$mode"}($text);
476 - }
 554+ if ($this->config['is_streaming']) {
 555+ // process the body
 556+ $this->response['content-length'] = 0;
 557+ curl_setopt($c, CURLOPT_TIMEOUT, 0);
 558+ curl_setopt($c, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
 559+ }
477560
478 - /**
479 - * Utility function to parse the returned curl headers and store them in the
480 - * class array variable.
481 - *
482 - * @param object $ch curl handle
483 - * @param string $header the response headers
484 - * @return the string length of the header
485 - */
486 - private function curlHeader($ch, $header) {
487 - $i = strpos($header, ':');
488 - if ( ! empty($i) ) {
489 - $key = str_replace('-', '_', strtolower(substr($header, 0, $i)));
490 - $value = trim(substr($header, $i + 2));
491 - $this->response['headers'][$key] = $value;
492 - }
493 - return strlen($header);
494 - }
 561+ switch ($this->method) {
 562+ case 'GET':
 563+ break;
 564+ case 'POST':
 565+ curl_setopt($c, CURLOPT_POST, TRUE);
 566+ break;
 567+ default:
 568+ curl_setopt($c, CURLOPT_CUSTOMREQUEST, $this->method);
 569+ }
495570
496 - /**
497 - * Utility function to parse the returned curl buffer and store them until
498 - * an EOL is found. The buffer for curl is an undefined size so we need
499 - * to collect the content until an EOL is found.
500 - *
501 - * This function calls the previously defined streaming callback method.
502 - *
503 - * @param object $ch curl handle
504 - * @param string $data the current curl buffer
505 - */
506 - private function curlWrite($ch, $data) {
507 - $l = strlen($data);
508 - if (strpos($data, $this->config['streaming_eol']) === false) {
509 - $this->buffer .= $data;
510 - return $l;
511 - }
 571+ if ( ! empty($this->request_params) ) {
 572+ // if not doing multipart we need to implode the parameters
 573+ if ( ! $this->config['multipart'] ) {
 574+ foreach ($this->request_params as $k => $v) {
 575+ $ps[] = "{$k}={$v}";
 576+ }
 577+ $this->request_params = implode('&', $ps);
 578+ }
 579+ curl_setopt($c, CURLOPT_POSTFIELDS, $this->request_params);
 580+ } else {
 581+ // CURL will set length to -1 when there is no data, which breaks Twitter
 582+ $this->headers['Content-Type'] = '';
 583+ $this->headers['Content-Length'] = '';
 584+ }
512585
513 - $buffered = explode($this->config['streaming_eol'], $data);
514 - $content = $this->buffer . $buffered[0];
 586+ // CURL defaults to setting this to Expect: 100-Continue which Twitter rejects
 587+ $this->headers['Expect'] = '';
515588
516 - $this->metrics['tweets']++;
517 - $this->metrics['bytes'] += strlen($content);
 589+ if ( ! empty($this->headers)) {
 590+ foreach ($this->headers as $k => $v) {
 591+ $headers[] = trim($k . ': ' . $v);
 592+ }
 593+ curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
 594+ }
518595
519 - if ( ! function_exists($this->config['streaming_callback']))
520 - return 0;
 596+ if (isset($this->config['prevent_request']) && true == $this->config['prevent_request'])
 597+ return;
521598
522 - $metrics = $this->update_metrics();
523 - $stop = call_user_func(
524 - $this->config['streaming_callback'],
525 - $content,
526 - strlen($content),
527 - $metrics
528 - );
529 - $this->buffer = $buffered[1];
530 - if ($stop)
531 - return 0;
 599+ // do it!
 600+ $response = curl_exec($c);
 601+ $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
 602+ $info = curl_getinfo($c);
 603+ curl_close($c);
532604
533 - return $l;
534 - }
535 -
536 - /**
537 - * Makes a curl request. Takes no parameters as all should have been prepared
538 - * by the request method
539 - *
540 - * @return void response data is stored in the class variable 'response'
541 - */
542 - private function curlit() {
543 - // method handling
544 - switch ($this->method) {
545 - case 'POST':
546 - break;
547 - default:
548 - // GET, DELETE request so convert the parameters to a querystring
549 - if ( ! empty($this->request_params)) {
550 - foreach ($this->request_params as $k => $v) {
551 - // Multipart params haven't been encoded yet.
552 - // Not sure why you would do a multipart GET but anyway, here's the support for it
553 - if ($this->config['multipart']) {
554 - $params[] = $this->safe_encode($k) . '=' . $this->safe_encode($v);
555 - } else {
556 - $params[] = $k . '=' . $v;
557 - }
558 - }
559 - $qs = implode('&', $params);
560 - $this->url = strlen($qs) > 0 ? $this->url . '?' . $qs : $this->url;
561 - $this->request_params = array();
562 - }
563 - break;
564 - }
565 -
566 - // configure curl
567 - $c = curl_init();
568 - curl_setopt_array($c, array(
569 - CURLOPT_USERAGENT => $this->config['user_agent'],
570 - CURLOPT_CONNECTTIMEOUT => $this->config['curl_connecttimeout'],
571 - CURLOPT_TIMEOUT => $this->config['curl_timeout'],
572 - CURLOPT_RETURNTRANSFER => true,
573 - CURLOPT_SSL_VERIFYPEER => $this->config['curl_ssl_verifypeer'],
574 - CURLOPT_SSL_VERIFYHOST => $this->config['curl_ssl_verifyhost'],
575 -
576 - CURLOPT_FOLLOWLOCATION => $this->config['curl_followlocation'],
577 - CURLOPT_PROXY => $this->config['curl_proxy'],
578 - CURLOPT_ENCODING => $this->config['curl_encoding'],
579 - CURLOPT_URL => $this->url,
580 - // process the headers
581 - CURLOPT_HEADERFUNCTION => array($this, 'curlHeader'),
582 - CURLOPT_HEADER => false,
583 - CURLINFO_HEADER_OUT => true,
584 - ));
585 -
586 - if ($this->config['curl_cainfo'] !== false)
587 - curl_setopt($c, CURLOPT_CAINFO, $this->config['curl_cainfo']);
588 -
589 - if ($this->config['curl_capath'] !== false)
590 - curl_setopt($c, CURLOPT_CAPATH, $this->config['curl_capath']);
591 -
592 - if ($this->config['curl_proxyuserpwd'] !== false)
593 - curl_setopt($c, CURLOPT_PROXYUSERPWD, $this->config['curl_proxyuserpwd']);
594 -
595 - if ($this->config['is_streaming']) {
596 - // process the body
597 - $this->response['content-length'] = 0;
598 - curl_setopt($c, CURLOPT_TIMEOUT, 0);
599 - curl_setopt($c, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
600 - }
601 -
602 - switch ($this->method) {
603 - case 'GET':
604 - break;
605 - case 'POST':
606 - curl_setopt($c, CURLOPT_POST, true);
607 - break;
608 - default:
609 - curl_setopt($c, CURLOPT_CUSTOMREQUEST, $this->method);
610 - }
611 -
612 - if ( ! empty($this->request_params) ) {
613 - // if not doing multipart we need to implode the parameters
614 - if ( ! $this->config['multipart'] ) {
615 - foreach ($this->request_params as $k => $v) {
616 - $ps[] = "{$k}={$v}";
617 - }
618 - $this->request_params = implode('&', $ps);
619 - }
620 - curl_setopt($c, CURLOPT_POSTFIELDS, $this->request_params);
621 - } else {
622 - // CURL will set length to -1 when there is no data, which breaks Twitter
623 - $this->headers['Content-Type'] = '';
624 - $this->headers['Content-Length'] = '';
625 - }
626 -
627 - // CURL defaults to setting this to Expect: 100-Continue which Twitter rejects
628 - $this->headers['Expect'] = '';
629 -
630 - if ( ! empty($this->headers)) {
631 - foreach ($this->headers as $k => $v) {
632 - $headers[] = trim($k . ': ' . $v);
633 - }
634 - curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
635 - }
636 -
637 - if (isset($this->config['prevent_request']) && true == $this->config['prevent_request'])
638 - return;
639 -
640 - // do it!
641 - $response = curl_exec($c);
642 - $code = curl_getinfo($c, CURLINFO_HTTP_CODE);
643 - $info = curl_getinfo($c);
644 - $error = curl_error($c);
645 - $errno = curl_errno($c);
646 - curl_close($c);
647 -
648 - // store the response
649 - $this->response['code'] = $code;
650 - $this->response['response'] = $response;
651 - $this->response['info'] = $info;
652 - $this->response['error'] = $error;
653 - $this->response['errno'] = $errno;
654 - return $code;
655 - }
 605+ // store the response
 606+ $this->response['code'] = $code;
 607+ $this->response['response'] = $response;
 608+ $this->response['info'] = $info;
 609+ return $code;
 610+ }
656611 }

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r107654Bump to 0.60 from https://raw.github.com/themattharris/tmhOAuth/master/tmhOAu...reedy21:05, 30 December 2011

Status & tagging log