NXS_tmhOAuth

TmhOAuth.

Defined (1)

The class is defined in the following location(s).

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