├── LICENSE ├── README.rst └── disqusapi ├── cacert.pem ├── disqusapi.php ├── interfaces.json ├── json.php ├── tests ├── disqusapi.php └── json.php └── url.php /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2010 DISQUS 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | disqus-php 2 | ~~~~~~~~~~ 3 | 4 | Requires PHP 5.3.0 or newer! 5 | 6 | Use the API by instantiating it, and then calling the method through dotted notation chaining:: 7 | 8 | require('disqusapi/disqusapi.php'); 9 | $disqus = new DisqusAPI($secret_key); 10 | $disqus->trends->listThreads(); 11 | 12 | Parameters (including the ability to override version, api_secret, and format) are passed as keyword arguments to the resource call:: 13 | 14 | $disqus->posts->details(array('post'=>1, 'version'=>'3.0')); 15 | 16 | Documentation on all methods, as well as general API usage can be found at http://disqus.com/api/ 17 | -------------------------------------------------------------------------------- /disqusapi/disqusapi.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright 2007-2010 Big Head Labs 9 | * @link http://disqus.com/ 10 | * @package disqusapi 11 | * @version 0.1.1 12 | * 13 | * $disqus = new DisqusAPI($secret_key) 14 | * $disqus->trends->listThreads() 15 | * 16 | */ 17 | 18 | if (!defined('DISQUS_API_HOST')) { 19 | define('DISQUS_API_HOST', 'disqus.com'); 20 | } 21 | define('DISQUS_API_VERSION', '0.0.1'); 22 | 23 | require_once(dirname(__FILE__) . '/url.php'); 24 | 25 | if (!extension_loaded('json')) { 26 | require_once(dirname(__FILE__) . '/json.php'); 27 | function dsq_json_decode($data) { 28 | $json = new JSON; 29 | return $json->unserialize($data); 30 | } 31 | } else { 32 | function dsq_json_decode($data) { 33 | return json_decode($data); 34 | } 35 | } 36 | 37 | global $DISQUS_API_INTERFACES; 38 | 39 | $DISQUS_API_INTERFACES = dsq_json_decode(file_get_contents(dirname(__FILE__) . '/interfaces.json')); 40 | 41 | class DisqusInterfaceNotDefined extends Exception {} 42 | class DisqusAPIError extends Exception { 43 | public function __construct($code, $message) { 44 | $this->code = $code; 45 | $this->message = $message; 46 | } 47 | } 48 | 49 | class DisqusResource { 50 | public function __construct($api, $interface=null, $node=null, $tree=array()) { 51 | global $DISQUS_API_INTERFACES; 52 | 53 | if (!$interface) { 54 | $interface = $DISQUS_API_INTERFACES; 55 | } 56 | $this->api = $api; 57 | $this->interface = $interface; 58 | $this->node = $node; 59 | if ($node) { 60 | array_push($tree, $node); 61 | } 62 | $this->tree = $tree; 63 | } 64 | 65 | public function __get($attr) { 66 | $interface = $this->interface->$attr; 67 | if (!$interface) { 68 | throw new DisqusInterfaceNotDefined(); 69 | } 70 | return new DisqusResource($this->api, $interface, $attr, $this->tree); 71 | } 72 | 73 | public function __call($name, $args) { 74 | $resource = $this->interface->$name; 75 | if (!$resource) { 76 | throw new DisqusInterfaceNotDefined(); 77 | } 78 | $kwargs = (array)$args[0]; 79 | 80 | foreach ((array)$resource->required as $k) { 81 | if (empty($kwargs[$k])) { 82 | throw new Exception('Missing required argument: '.$k); 83 | } 84 | } 85 | 86 | $api = $this->api; 87 | 88 | if (empty($kwargs['api_secret'])) { 89 | $kwargs['api_secret'] = $api->key; 90 | } 91 | 92 | // emulate a named pop 93 | $version = (!empty($kwargs['version']) ? $kwargs['version'] : $api->version); 94 | $format = (!empty($kwargs['format']) ? $kwargs['format'] : $api->format); 95 | unset($kwargs['version'], $kwargs['format']); 96 | 97 | $url = 'https://'.DISQUS_API_HOST; 98 | $path = '/api/'.$version.'/'.implode('/', $this->tree).'/'.$name.'.'.$format; 99 | 100 | if (!empty($kwargs)) { 101 | if ($resource->method == 'POST') { 102 | $post_data = $kwargs; 103 | } else { 104 | $post_data = false; 105 | $path .= '?'.dsq_get_query_string($kwargs); 106 | } 107 | } 108 | 109 | 110 | $response = dsq_urlopen($url.$path, $post_data); 111 | 112 | $data = call_user_func($api->formats[$format], $response['data']); 113 | 114 | if ($response['code'] != 200) { 115 | throw new DisqusAPIError($data->code, $data->response); 116 | } 117 | 118 | return $data->response; 119 | } 120 | } 121 | 122 | 123 | class DisqusAPI extends DisqusResource { 124 | public $formats = array( 125 | 'json' => 'dsq_json_decode' 126 | ); 127 | 128 | public function __construct($key=null, $format='json', $version='3.0') { 129 | $this->key = $key; 130 | $this->format = $format; 131 | $this->version = $version; 132 | parent::__construct($this); 133 | } 134 | 135 | public function __invoke() { 136 | throw new Exception('You cannot call the API without a resource.'); 137 | } 138 | 139 | public function setKey($key) { 140 | $this->key = $key; 141 | } 142 | 143 | public function setFormat($format) { 144 | $this->format = $format; 145 | } 146 | 147 | public function setVersion($version) { 148 | $this->version = $version; 149 | } 150 | } -------------------------------------------------------------------------------- /disqusapi/interfaces.json: -------------------------------------------------------------------------------- 1 | { 2 | "reactions": { 3 | "restore": { 4 | "required": [ 5 | "reaction", 6 | "forum" 7 | ], 8 | "method": "POST", 9 | "formats": [ 10 | "json", 11 | "jsonp" 12 | ] 13 | }, 14 | "list": { 15 | "required": [ 16 | "forum" 17 | ], 18 | "method": "POST", 19 | "formats": [ 20 | "json", 21 | "jsonp" 22 | ] 23 | }, 24 | "details": { 25 | "required": [ 26 | "reaction", 27 | "forum" 28 | ], 29 | "method": "GET", 30 | "formats": [ 31 | "json", 32 | "jsonp" 33 | ] 34 | }, 35 | "remove": { 36 | "required": [ 37 | "reaction", 38 | "forum" 39 | ], 40 | "method": "POST", 41 | "formats": [ 42 | "json", 43 | "jsonp" 44 | ] 45 | } 46 | }, 47 | "exports": { 48 | "exportForum": { 49 | "required": [ 50 | "forum" 51 | ], 52 | "method": "POST", 53 | "formats": [ 54 | "json", 55 | "jsonp" 56 | ] 57 | } 58 | }, 59 | "users": { 60 | "listActiveForums": { 61 | "required": [], 62 | "method": "GET", 63 | "formats": [ 64 | "json", 65 | "jsonp" 66 | ] 67 | }, 68 | "listActiveThreads": { 69 | "required": [], 70 | "method": "GET", 71 | "formats": [ 72 | "json", 73 | "jsonp", 74 | "rss" 75 | ] 76 | }, 77 | "listFollowing": { 78 | "required": [], 79 | "method": "GET", 80 | "formats": [ 81 | "json", 82 | "jsonp" 83 | ] 84 | }, 85 | "listForums": { 86 | "required": [], 87 | "method": "GET", 88 | "formats": [ 89 | "json", 90 | "jsonp" 91 | ] 92 | }, 93 | "unfollow": { 94 | "required": [], 95 | "method": "POST", 96 | "formats": [ 97 | "json", 98 | "jsonp" 99 | ] 100 | }, 101 | "listPosts": { 102 | "required": [], 103 | "method": "GET", 104 | "formats": [ 105 | "json", 106 | "jsonp", 107 | "rss" 108 | ] 109 | }, 110 | "details": { 111 | "required": [], 112 | "method": "GET", 113 | "formats": [ 114 | "json", 115 | "jsonp" 116 | ] 117 | }, 118 | "listFollowers": { 119 | "required": [], 120 | "method": "GET", 121 | "formats": [ 122 | "json", 123 | "jsonp" 124 | ] 125 | }, 126 | "follow": { 127 | "required": [], 128 | "method": "POST", 129 | "formats": [ 130 | "json", 131 | "jsonp" 132 | ] 133 | }, 134 | "listActivity": { 135 | "required": [], 136 | "method": "GET", 137 | "formats": [ 138 | "json", 139 | "jsonp" 140 | ] 141 | }, 142 | "listMostActiveForums": { 143 | "required": [], 144 | "method": "GET", 145 | "formats": [ 146 | "json", 147 | "jsonp" 148 | ] 149 | } 150 | }, 151 | "imports": { 152 | "list": { 153 | "required": [ 154 | "forum" 155 | ], 156 | "method": "GET", 157 | "formats": [ 158 | "json", 159 | "jsonp" 160 | ] 161 | }, 162 | "details": { 163 | "required": [ 164 | "group" 165 | ], 166 | "method": "GET", 167 | "formats": [ 168 | "json", 169 | "jsonp" 170 | ] 171 | } 172 | }, 173 | "posts": { 174 | "restore": { 175 | "required": [ 176 | "post" 177 | ], 178 | "method": "POST", 179 | "formats": [ 180 | "json", 181 | "jsonp" 182 | ] 183 | }, 184 | "spam": { 185 | "required": [ 186 | "post" 187 | ], 188 | "method": "POST", 189 | "formats": [ 190 | "json", 191 | "jsonp" 192 | ] 193 | }, 194 | "unhighlight": { 195 | "required": [ 196 | "post" 197 | ], 198 | "method": "POST", 199 | "formats": [ 200 | "json", 201 | "jsonp" 202 | ] 203 | }, 204 | "listPopular": { 205 | "required": [], 206 | "method": "GET", 207 | "formats": [ 208 | "json", 209 | "jsonp", 210 | "rss" 211 | ] 212 | }, 213 | "create": { 214 | "required": [ 215 | "message" 216 | ], 217 | "method": "POST", 218 | "formats": [ 219 | "json", 220 | "jsonp" 221 | ] 222 | }, 223 | "update": { 224 | "required": [ 225 | "message", 226 | "post" 227 | ], 228 | "method": "POST", 229 | "formats": [ 230 | "json", 231 | "jsonp" 232 | ] 233 | }, 234 | "list": { 235 | "required": [], 236 | "method": "GET", 237 | "formats": [ 238 | "json", 239 | "jsonp", 240 | "rss" 241 | ] 242 | }, 243 | "remove": { 244 | "required": [ 245 | "post" 246 | ], 247 | "method": "POST", 248 | "formats": [ 249 | "json", 250 | "jsonp" 251 | ] 252 | }, 253 | "getContext": { 254 | "required": [ 255 | "post" 256 | ], 257 | "method": "GET", 258 | "formats": [ 259 | "json", 260 | "jsonp", 261 | "rss" 262 | ] 263 | }, 264 | "vote": { 265 | "required": [ 266 | "vote", 267 | "post" 268 | ], 269 | "method": "POST", 270 | "formats": [ 271 | "json", 272 | "jsonp" 273 | ] 274 | }, 275 | "details": { 276 | "required": [ 277 | "post" 278 | ], 279 | "method": "GET", 280 | "formats": [ 281 | "json", 282 | "jsonp" 283 | ] 284 | }, 285 | "report": { 286 | "required": [ 287 | "post" 288 | ], 289 | "method": "POST", 290 | "formats": [ 291 | "json", 292 | "jsonp" 293 | ] 294 | }, 295 | "highlight": { 296 | "required": [ 297 | "post" 298 | ], 299 | "method": "POST", 300 | "formats": [ 301 | "json", 302 | "jsonp" 303 | ] 304 | }, 305 | "approve": { 306 | "required": [ 307 | "post" 308 | ], 309 | "method": "POST", 310 | "formats": [ 311 | "json", 312 | "jsonp" 313 | ] 314 | } 315 | }, 316 | "blacklists": { 317 | "add": { 318 | "required": [ 319 | "forum" 320 | ], 321 | "method": "POST", 322 | "formats": [ 323 | "json", 324 | "jsonp" 325 | ] 326 | }, 327 | "list": { 328 | "required": [ 329 | "forum" 330 | ], 331 | "method": "GET", 332 | "formats": [ 333 | "json", 334 | "jsonp" 335 | ] 336 | }, 337 | "remove": { 338 | "required": [ 339 | "forum" 340 | ], 341 | "method": "POST", 342 | "formats": [ 343 | "json", 344 | "jsonp" 345 | ] 346 | } 347 | }, 348 | "reports": { 349 | "domains": { 350 | "required": [], 351 | "method": "GET", 352 | "formats": [ 353 | "json", 354 | "jsonp" 355 | ] 356 | }, 357 | "ips": { 358 | "required": [], 359 | "method": "GET", 360 | "formats": [ 361 | "json", 362 | "jsonp" 363 | ] 364 | }, 365 | "threads": { 366 | "required": [], 367 | "method": "GET", 368 | "formats": [ 369 | "json", 370 | "jsonp" 371 | ] 372 | }, 373 | "users": { 374 | "required": [], 375 | "method": "GET", 376 | "formats": [ 377 | "json", 378 | "jsonp" 379 | ] 380 | } 381 | }, 382 | "whitelists": { 383 | "add": { 384 | "required": [ 385 | "forum" 386 | ], 387 | "method": "POST", 388 | "formats": [ 389 | "json", 390 | "jsonp" 391 | ] 392 | }, 393 | "list": { 394 | "required": [ 395 | "forum" 396 | ], 397 | "method": "GET", 398 | "formats": [ 399 | "json", 400 | "jsonp" 401 | ] 402 | }, 403 | "remove": { 404 | "required": [ 405 | "forum" 406 | ], 407 | "method": "POST", 408 | "formats": [ 409 | "json", 410 | "jsonp" 411 | ] 412 | } 413 | }, 414 | "applications": { 415 | "listUsage": { 416 | "required": [], 417 | "method": "GET", 418 | "formats": [ 419 | "json", 420 | "jsonp" 421 | ] 422 | } 423 | }, 424 | "trends": { 425 | "listThreads": { 426 | "required": [], 427 | "method": "GET", 428 | "formats": [ 429 | "json", 430 | "jsonp", 431 | "rss" 432 | ] 433 | } 434 | }, 435 | "threads": { 436 | "restore": { 437 | "required": [ 438 | "thread" 439 | ], 440 | "method": "POST", 441 | "formats": [ 442 | "json", 443 | "jsonp" 444 | ] 445 | }, 446 | "vote": { 447 | "required": [ 448 | "vote", 449 | "thread" 450 | ], 451 | "method": "POST", 452 | "formats": [ 453 | "json", 454 | "jsonp" 455 | ] 456 | }, 457 | "open": { 458 | "required": [ 459 | "thread" 460 | ], 461 | "method": "POST", 462 | "formats": [ 463 | "json", 464 | "jsonp" 465 | ] 466 | }, 467 | "create": { 468 | "required": [ 469 | "forum", 470 | "title" 471 | ], 472 | "method": "POST", 473 | "formats": [ 474 | "json", 475 | "jsonp" 476 | ] 477 | }, 478 | "list": { 479 | "required": [], 480 | "method": "GET", 481 | "formats": [ 482 | "json", 483 | "jsonp", 484 | "rss" 485 | ] 486 | }, 487 | "listMostLiked": { 488 | "required": [], 489 | "method": "GET", 490 | "formats": [ 491 | "json", 492 | "jsonp", 493 | "rss" 494 | ] 495 | }, 496 | "listHot": { 497 | "required": [], 498 | "method": "GET", 499 | "formats": [ 500 | "json", 501 | "jsonp", 502 | "rss" 503 | ] 504 | }, 505 | "update": { 506 | "required": [ 507 | "thread" 508 | ], 509 | "method": "POST", 510 | "formats": [ 511 | "json", 512 | "jsonp" 513 | ] 514 | }, 515 | "listSimilar": { 516 | "required": [ 517 | "thread" 518 | ], 519 | "method": "GET", 520 | "formats": [ 521 | "json", 522 | "jsonp", 523 | "rss" 524 | ] 525 | }, 526 | "details": { 527 | "required": [ 528 | "thread" 529 | ], 530 | "method": "GET", 531 | "formats": [ 532 | "json", 533 | "jsonp" 534 | ] 535 | }, 536 | "listByDate": { 537 | "required": [], 538 | "method": "GET", 539 | "formats": [ 540 | "json", 541 | "jsonp", 542 | "rss" 543 | ] 544 | }, 545 | "listPosts": { 546 | "required": [ 547 | "thread" 548 | ], 549 | "method": "GET", 550 | "formats": [ 551 | "json", 552 | "jsonp", 553 | "rss" 554 | ] 555 | }, 556 | "close": { 557 | "required": [ 558 | "thread" 559 | ], 560 | "method": "POST", 561 | "formats": [ 562 | "json", 563 | "jsonp" 564 | ] 565 | }, 566 | "remove": { 567 | "required": [ 568 | "thread" 569 | ], 570 | "method": "POST", 571 | "formats": [ 572 | "json", 573 | "jsonp" 574 | ] 575 | }, 576 | "listPopular": { 577 | "required": [], 578 | "method": "GET", 579 | "formats": [ 580 | "json", 581 | "jsonp", 582 | "rss" 583 | ] 584 | } 585 | }, 586 | "forums": { 587 | "create": { 588 | "required": [ 589 | "website", 590 | "name", 591 | "short_name" 592 | ], 593 | "method": "POST", 594 | "formats": [ 595 | "json", 596 | "jsonp" 597 | ] 598 | }, 599 | "listCategories": { 600 | "required": [ 601 | "forum" 602 | ], 603 | "method": "GET", 604 | "formats": [ 605 | "json", 606 | "jsonp" 607 | ] 608 | }, 609 | "listThreads": { 610 | "required": [ 611 | "forum" 612 | ], 613 | "method": "GET", 614 | "formats": [ 615 | "json", 616 | "jsonp", 617 | "rss" 618 | ] 619 | }, 620 | "listUsers": { 621 | "required": [ 622 | "forum" 623 | ], 624 | "method": "GET", 625 | "formats": [ 626 | "json", 627 | "jsonp" 628 | ] 629 | }, 630 | "listMostLikedUsers": { 631 | "required": [ 632 | "forum" 633 | ], 634 | "method": "GET", 635 | "formats": [ 636 | "json", 637 | "jsonp" 638 | ] 639 | }, 640 | "details": { 641 | "required": [ 642 | "forum" 643 | ], 644 | "method": "GET", 645 | "formats": [ 646 | "json", 647 | "jsonp" 648 | ] 649 | }, 650 | "listPosts": { 651 | "required": [ 652 | "forum" 653 | ], 654 | "method": "GET", 655 | "formats": [ 656 | "json", 657 | "jsonp", 658 | "rss" 659 | ] 660 | }, 661 | "listModerators": { 662 | "required": [ 663 | "forum" 664 | ], 665 | "method": "GET", 666 | "formats": [ 667 | "json", 668 | "jsonp" 669 | ] 670 | } 671 | }, 672 | "categories": { 673 | "listPosts": { 674 | "required": [ 675 | "category" 676 | ], 677 | "method": "GET", 678 | "formats": [ 679 | "json", 680 | "jsonp", 681 | "rss" 682 | ] 683 | }, 684 | "listThreads": { 685 | "required": [ 686 | "category" 687 | ], 688 | "method": "GET", 689 | "formats": [ 690 | "json", 691 | "jsonp", 692 | "rss" 693 | ] 694 | }, 695 | "create": { 696 | "required": [ 697 | "forum", 698 | "title" 699 | ], 700 | "method": "POST", 701 | "formats": [ 702 | "json", 703 | "jsonp" 704 | ] 705 | }, 706 | "list": { 707 | "required": [], 708 | "method": "GET", 709 | "formats": [ 710 | "json", 711 | "jsonp" 712 | ] 713 | }, 714 | "details": { 715 | "required": [ 716 | "category" 717 | ], 718 | "method": "GET", 719 | "formats": [ 720 | "json", 721 | "jsonp" 722 | ] 723 | } 724 | } 725 | } 726 | -------------------------------------------------------------------------------- /disqusapi/json.php: -------------------------------------------------------------------------------- 1 | 35 | * @copyright 2007 Cesar D. Rodas 36 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License 37 | * @version 1.0 38 | * @link http://cesars.users.phpclasses.org/json 39 | */ 40 | 41 | define('IN_NOWHERE',0); 42 | define('IN_STRING',1); 43 | define('IN_OBJECT',2); 44 | define('IN_ATOMIC',3); 45 | define('IN_ASSIGN',4); 46 | define('IN_ENDSTMT',5); 47 | define('IN_ARRAY',6); 48 | 49 | /** 50 | * JSON 51 | * 52 | * This class serilize an PHP OBJECT or an ARRAY into JSON 53 | * notation. Also convert a JSON text into a PHP OBJECT or 54 | * array. 55 | * 56 | * @category Javascript 57 | * @package JSON 58 | * @author Cesar D. Rodas 59 | * @copyright 2007 Cesar D. Rodas 60 | * @license http://www.opensource.org/licenses/bsd-license.php BSD License 61 | * @version 1.0 62 | * @link http://cesars.users.phpclasses.org/json 63 | */ 64 | class JSON 65 | { 66 | /** 67 | * Was parsed with an error? 68 | * 69 | * var bool 70 | * @access private 71 | */ 72 | var $error; 73 | 74 | function Json() { 75 | $this->error = false; 76 | } 77 | 78 | /** 79 | * Serialize 80 | * 81 | * Serialize a PHP OBJECT or an ARRAY into 82 | * JSON notation. 83 | * 84 | * param mixed $obj Object or array to serialize 85 | * return string JSON. 86 | */ 87 | function serialize($obj) { 88 | if ( is_object($obj) ) { 89 | $e = get_object_vars($obj); 90 | /* bug reported by Ben Rowe */ 91 | /* Adding default empty array if the */ 92 | /* object doesn't have any property */ 93 | $properties = array(); 94 | foreach ($e as $k => $v) { 95 | $properties[] = $this->_serialize( $k,$v ); 96 | } 97 | return "{".implode(",",$properties)."}"; 98 | } else if ( is_array($obj) ) { 99 | return $this->_serialize('',$obj); 100 | } 101 | } 102 | 103 | /** 104 | * UnSerialize 105 | * 106 | * Transform an JSON text into a PHP object 107 | * and return it. 108 | * @access public 109 | * @param string $text JSON text 110 | * @return mixed PHP Object, array or false. 111 | */ 112 | function unserialize( $text ) { 113 | $this->error = false; 114 | 115 | return !$this->error ? $this->_unserialize($text) : false; 116 | } 117 | 118 | /** 119 | * UnSerialize 120 | * 121 | * Transform an JSON text into a PHP object 122 | * and return it. 123 | * @access private 124 | * @param string $text JSON text 125 | * @return mixed PHP Object, array or false. 126 | */ 127 | function _unserialize($text) { 128 | $ret = new stdClass; 129 | 130 | while ( $f = $this->getNextToken($text,$i,$type) ) { 131 | switch ( $type ) { 132 | case IN_ARRAY: 133 | $tmp = $this->_unserializeArray($text); 134 | $ret = $tmp[0]; 135 | break; 136 | case IN_OBJECT: 137 | $g=0; 138 | do { 139 | $varName = $this->getNextToken($f,$g,$xType); 140 | if ( $xType != IN_STRING ) { 141 | return false; /* error parsing */ 142 | } 143 | $this->getNextToken($f,$g,$xType); 144 | if ( $xType != IN_ASSIGN) return false; 145 | $value = $this->getNextToken($f,$g,$xType); 146 | 147 | if ( $xType == IN_OBJECT) { 148 | $ret->$varName = $this->unserialize( "{".$value."}" ); 149 | $g--; 150 | } else if ($xType == IN_ARRAY) { 151 | $ret->$varName = $this->_unserializeArray( $value); 152 | $g--; 153 | } else 154 | $ret->$varName = $value; 155 | 156 | $this->getNextToken($f,$g,$xType); 157 | } while ( $xType == IN_ENDSTMT); 158 | break; 159 | default: 160 | $this->error = true; 161 | break 2; 162 | } 163 | } 164 | return $ret; 165 | } 166 | 167 | /** 168 | * JSON Array Parser 169 | * 170 | * This method transform an json-array into a PHP 171 | * array 172 | * @access private 173 | * @param string $text String to parse 174 | * @return Array PHP Array 175 | */ 176 | function _unserializeArray($text) { 177 | $r = array(); 178 | do { 179 | $f = $this->getNextToken($text,$i,$type); 180 | switch ( $type ) { 181 | case IN_STRING: 182 | case IN_ATOMIC: 183 | $r[] = $f; 184 | break; 185 | case IN_OBJECT: 186 | $r[] = $this->unserialize("{".$f."}"); 187 | $i--; 188 | break; 189 | case IN_ARRAY: 190 | $r[] = $this->_unserializeArray($f); 191 | $i--; 192 | break; 193 | 194 | } 195 | $this->getNextToken($text,$i,$type); 196 | } while ( $type == IN_ENDSTMT); 197 | 198 | return $r; 199 | } 200 | 201 | /** 202 | * Tokenizer 203 | * 204 | * Return to the Parser the next valid token and the type 205 | * of the token. If the tokenizer fails it returns false. 206 | * 207 | * @access private 208 | * @param string $e Text to extract token 209 | * @param integer $i Start position to search next token 210 | * @param integer $state Variable to get the token type 211 | * @return string|bool Token in string or false on error. 212 | */ 213 | function getNextToken($e, &$i, &$state) { 214 | $state = IN_NOWHERE; 215 | $end = -1; 216 | $start = -1; 217 | while ( $i < strlen($e) && $end == -1 ) { 218 | switch( $e[$i] ) { 219 | /* objects */ 220 | case "{": 221 | case "[": 222 | $_tag = $e[$i]; 223 | $_endtag = $_tag == "{" ? "}" : "]"; 224 | if ( $state == IN_NOWHERE ) { 225 | $start = $i+1; 226 | switch ($state) { 227 | case IN_NOWHERE: 228 | $aux = 1; /* for loop objects */ 229 | $state = $_tag == "{" ? IN_OBJECT : IN_ARRAY; 230 | break; 231 | default: 232 | break 2; /* exit from switch and while */ 233 | } 234 | while ( ++$i && $i < strlen($e) && $aux != 0 ) { 235 | switch( $e[$i] ) { 236 | case $_tag: 237 | $aux++; 238 | break; 239 | case $_endtag: 240 | $aux--; 241 | break; 242 | } 243 | } 244 | $end = $i-1; 245 | } 246 | break; 247 | 248 | case '"': 249 | case "'": 250 | $state = IN_STRING; 251 | $buf = ""; 252 | while ( ++$i && $i < strlen($e) && $e[$i] != '"' ) { 253 | if ( $e[$i] == "\\") 254 | $i++; 255 | $buf .= $e[$i]; 256 | } 257 | $i++; 258 | return eval('return "'.str_replace('"','\"',$buf).'";'); 259 | break; 260 | case ":": 261 | $state = IN_ASSIGN; 262 | $end = 1; 263 | break; 264 | case "n": 265 | if ( substr($e,$i,4) == "null" ) { 266 | $i=$i+4; 267 | $state = IN_ATOMIC; 268 | return NULL; 269 | } 270 | else break 2; /* exit from switch and while */ 271 | case "t": 272 | if ( substr($e,$i,4) == "true") { 273 | $state = IN_ATOMIC; 274 | $i=$i+4; 275 | return true; 276 | } 277 | else break 2; /* exit from switch and while */ 278 | break; 279 | case "f": 280 | if ( substr($e,$i,5) == "false") { 281 | $state = IN_ATOMIC; 282 | $i=$i+5; 283 | return false; 284 | } 285 | else break 2; /* exit from switch and while */ 286 | break; 287 | case ",": 288 | $state = IN_ENDSTMT; 289 | $end = 1; 290 | break; 291 | case " ": 292 | case "\t": 293 | case "\r": 294 | case "\n": 295 | break; 296 | case "+": 297 | case "-": 298 | case 0: 299 | case 1: 300 | case 2: 301 | case 3: 302 | case 4: 303 | case 5: 304 | case 6: 305 | case 7: 306 | case 8: 307 | case 9: 308 | case '.': 309 | $state = IN_ATOMIC; 310 | $start = (int)$i; 311 | if ( $e[$i] == "-" || $e[$i] == "+") 312 | $i++; 313 | for ( ; $i < strlen($e) && (is_numeric($e[$i]) || $e[$i] == "." || strtolower($e[$i]) == "e") ;$i++){ 314 | $n = $i+1 < strlen($e) ? $e[$i+1] : ""; 315 | $a = strtolower($e[$i]); 316 | if ( $a == "e" && ($n == "+" || $n == "-")) 317 | $i++; 318 | else if ( $a == "e") 319 | $this->error=true; 320 | } 321 | 322 | $end = $i; 323 | break 2; /* break while too */ 324 | default: 325 | $this->error = true; 326 | 327 | } 328 | $i++; 329 | } 330 | 331 | return $start == -1 || $end == -1 ? false : substr($e, $start, $end - $start); 332 | } 333 | 334 | /** 335 | * Internal Serializer 336 | * 337 | * @param string $key Variable name 338 | * @param mixed $value Value of the variable 339 | * @access private 340 | * @return string Serialized variable 341 | */ 342 | function _serialize ( $key = '', &$value ) { 343 | $r = ''; 344 | if ( $key != '')$r .= "\"${key}\" : "; 345 | if ( is_numeric($value) ) { 346 | $r .= ''.$value.''; 347 | } else if ( is_string($value) ) { 348 | $r .= '"'.$this->toString($value).'"'; 349 | } else if ( is_object($value) ) { 350 | $r .= $this->serialize($value); 351 | } else if ( is_null($value) ) { 352 | $r .= "null"; 353 | } else if ( is_bool($value) ) { 354 | $r .= $value ? "true":"false"; 355 | } else if ( is_array($value) ) { 356 | foreach($value as $k => $v) 357 | $f[] = $this->_serialize('',$v); 358 | $r .= "[".implode(",",$f)."]"; 359 | unset($f); 360 | } 361 | return $r; 362 | } 363 | 364 | /** 365 | * Convert String variables 366 | * 367 | * @param string $e Variable with an string value 368 | * @access private 369 | * @return string Serialized variable 370 | */ 371 | function toString($e) { 372 | $rep = array("\\","\r","\n","\t","'",'"'); 373 | $val = array("\\\\",'\r','\n','\t','\'','\"'); 374 | $e = str_replace($rep, $val, $e); 375 | return $e; 376 | } 377 | } 378 | ?> -------------------------------------------------------------------------------- /disqusapi/tests/disqusapi.php: -------------------------------------------------------------------------------- 1 | assertEquals($api->key, 'a'); 16 | $api->setKey('b'); 17 | $this->assertEquals($api->key, 'b'); 18 | } 19 | 20 | function test_setFormat() { 21 | $api = new DisqusAPI(); 22 | $this->assertEquals($api->format, 'json'); 23 | $api->setFormat('jsonp'); 24 | $this->assertEquals($api->format, 'jsonp'); 25 | } 26 | 27 | function test_setVersion() { 28 | $api = new DisqusAPI(); 29 | $this->assertEquals($api->version, '3.0'); 30 | $api->setVersion('3.1'); 31 | $this->assertEquals($api->version, '3.1'); 32 | } 33 | 34 | /** 35 | * @expectedException DisqusInterfaceNotDefined 36 | */ 37 | function test_invalid_function() { 38 | $api = new DisqusAPI(); 39 | $api->users->foo(array('foo'=>'bar')); 40 | } 41 | 42 | /** 43 | * @expectedException DisqusAPIError 44 | */ 45 | function test_users_listActivity() { 46 | $api = new DisqusAPI($this->secret); 47 | $api->users->listActivity(array('foo'=>'bar')); 48 | } 49 | 50 | /** 51 | * @expectedException Exception 52 | */ 53 | function test_posts_create_missing_message() { 54 | $api = new DisqusAPI($this->secret); 55 | $api->posts->create(array('foo'=>'bar')); 56 | } 57 | 58 | /** 59 | * @expectedException DisqusAPIError 60 | */ 61 | function test_posts_create() { 62 | $api = new DisqusAPI($this->secret); 63 | $api->posts->create(array('message'=>'bar')); 64 | } 65 | } 66 | 67 | ?> -------------------------------------------------------------------------------- /disqusapi/tests/json.php: -------------------------------------------------------------------------------- 1 | json = new JSON; 9 | } 10 | 11 | public function test_decoding() { 12 | $data = '{ 13 | "a": false, 14 | "b": 1 15 | }'; 16 | $set1 = json_decode($data); 17 | $set2 = $this->json->unserialize($data); 18 | $this->assertEquals($set1->id, $set2->id); 19 | $this->assertEquals($set1, $set2); 20 | } 21 | } 22 | 23 | ?> -------------------------------------------------------------------------------- /disqusapi/url.php: -------------------------------------------------------------------------------- 1 | $value) { 10 | if (!is_array($value)) { 11 | $postdata_str .= urlencode($key) . '=' . urlencode($value) . '&'; 12 | } else { 13 | // if the item is an array, expands it so that the 'allows multiple' API option can work 14 | foreach($value as $multipleValue) { 15 | $postdata_str .= urlencode($key) . '=' . urlencode($multipleValue) . '&'; 16 | } 17 | } 18 | } 19 | } 20 | 21 | return $postdata_str; 22 | } 23 | 24 | 25 | function dsq_get_post_content($boundary, $postdata, $file_name, $file_field) { 26 | if(empty($file_name) || empty($file_field)) { 27 | return dsq_get_query_string($postdata); 28 | } 29 | 30 | $content = array(); 31 | $content[] = '--' . $boundary; 32 | foreach($postdata as $key=>$value) { 33 | $content[] = 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n" . $value; 34 | $content[] = '--' . $boundary; 35 | } 36 | $content[] = 'Content-Disposition: form-data; name="' . $file_field . '"; filename="' . $file_name . '"'; 37 | // HACK: We only need to handle text/plain files right now. 38 | $content[] = "Content-Type: text/plain\r\n"; 39 | $content[] = file_get_contents($file_name); 40 | $content[] = '--' . $boundary . '--'; 41 | $content = implode("\r\n", $content); 42 | return $content; 43 | } 44 | 45 | 46 | function dsq_get_http_headers_for_request($boundary, $content, $file_name, $file_field) { 47 | $headers = array(); 48 | $headers[] = 'User-Agent: ' . USER_AGENT; 49 | $headers[] = 'Connection: close'; 50 | if($content) { 51 | $headers[] = 'Content-Length: ' . strlen($content); 52 | if($file_name && $file_field) { 53 | $headers[] = 'Content-Type: multipart/form-data; boundary=' . $boundary; 54 | } else { 55 | $headers[] = 'Content-Type: application/x-www-form-urlencoded'; 56 | } 57 | } 58 | return implode("\r\n", $headers); 59 | } 60 | 61 | 62 | function _dsq_curl_urlopen($url, $postdata, &$response, $file_name, $file_field) { 63 | $c = curl_init($url); 64 | $postdata_str = dsq_get_query_string($postdata); 65 | 66 | $c_options = array( 67 | CURLOPT_USERAGENT => USER_AGENT, 68 | CURLOPT_RETURNTRANSFER => true, 69 | CURLOPT_POST => ($postdata_str ? 1 : 0), 70 | CURLOPT_HTTPHEADER => array('Expect:'), 71 | CURLOPT_TIMEOUT => SOCKET_TIMEOUT 72 | ); 73 | if($postdata) { 74 | $c_options[CURLOPT_POSTFIELDS] = $postdata_str; 75 | } 76 | if($file_name && $file_field) { 77 | $postdata[$file_field] = '@' . $file_name; 78 | $c_options[CURLOPT_POSTFIELDS] = $postdata; 79 | $c_options[CURLOPT_RETURNTRANSFER] = 1; 80 | } 81 | curl_setopt_array($c, $c_options); 82 | 83 | $response['data'] = curl_exec($c); 84 | 85 | $errno = curl_errno($c); 86 | 87 | // CURLE_SSL_CACERT || CURLE_SSL_CACERT_BADFILE 88 | if ($errno == 60 || $errno == 77) { 89 | curl_setopt($c, CURLOPT_CAINFO, dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cacert.pem'); 90 | $response['data'] = curl_exec($c); 91 | } 92 | 93 | $response['code'] = curl_getinfo($c, CURLINFO_HTTP_CODE); 94 | } 95 | 96 | 97 | function _dsq_fsockopen_urlopen($url, $postdata, &$response, $file_name, $file_field) { 98 | $buf = ''; 99 | $req = ''; 100 | $length = 0; 101 | $boundary = '----------' . md5(time()); 102 | $postdata_str = dsq_get_post_content($boundary, $postdata, $file_name, $file_field); 103 | $url_pieces = parse_url($url); 104 | 105 | // Set default port for supported schemes if none is provided. 106 | if(!isset($url_pieces['port'])) { 107 | switch($url_pieces['scheme']) { 108 | case 'http': 109 | $url_pieces['port'] = 80; 110 | break; 111 | case 'https': 112 | $url_pieces['port'] = 443; 113 | $url_pieces['host'] = 'ssl://' . $url_pieces['host']; 114 | break; 115 | } 116 | } 117 | 118 | // Set default path if trailing slash is not provided. 119 | if(!isset($url_pieces['path'])) { $url_pieces['path'] = '/'; } 120 | 121 | // Determine if we need to include the port in the Host header or not. 122 | if(($url_pieces['port'] == 80 && $url_pieces['scheme'] == 'http') || 123 | ($url_pieces['port'] == 443 && $url_pieces['scheme'] == 'https')) { 124 | $host = $url_pieces['host']; 125 | } else { 126 | $host = $url_pieces['host'] . ':' . $url_pieces['port']; 127 | } 128 | 129 | $fp = @fsockopen($url_pieces['host'], $url_pieces['port'], $errno, $errstr, SOCKET_TIMEOUT); 130 | if(!$fp) { return false; } 131 | 132 | $path = $url_pieces['path']; 133 | if ($url_pieces['query']) $path .= '?'.$url_pieces['query']; 134 | 135 | $req .= ($postdata_str ? 'POST' : 'GET') . ' ' . $path . " HTTP/1.1\r\n"; 136 | $req .= 'Host: ' . $host . "\r\n"; 137 | $req .= dsq_get_http_headers_for_request($boundary, $postdata_str, $file_name, $file_field); 138 | if($postdata_str) { 139 | $req .= "\r\n\r\n" . $postdata_str; 140 | } 141 | $req .= "\r\n\r\n"; 142 | 143 | fwrite($fp, $req); 144 | while(!feof($fp)) { 145 | $buf .= fgets($fp, 4096); 146 | } 147 | 148 | // Parse headers from the response buffers. 149 | list($headers, $response['data']) = explode("\r\n\r\n", $buf, 2); 150 | 151 | // Get status code from headers. 152 | $headers = explode("\r\n", $headers); 153 | list($unused, $response['code'], $unused) = explode(' ', $headers[0], 3); 154 | $headers = array_slice($headers, 1); 155 | 156 | // Convert headers into associative array. 157 | foreach($headers as $unused=>$header) { 158 | $header = explode(':', $header); 159 | $header[0] = trim($header[0]); 160 | $header[1] = trim($header[1]); 161 | $headers[strtolower($header[0])] = strtolower($header[1]); 162 | } 163 | 164 | // If transfer-coding is set to chunked, we need to join the message body 165 | // together. 166 | if(isset($headers['transfer-encoding']) && 'chunked' == $headers['transfer-encoding']) { 167 | $chunk_data = $response['data']; 168 | $joined_data = ''; 169 | while(true) { 170 | // Strip length from body. 171 | list($chunk_length, $chunk_data) = explode("\r\n", $chunk_data, 2); 172 | $chunk_length = hexdec($chunk_length); 173 | if(!$chunk_length || !strlen($chunk_data)) { break; } 174 | 175 | $joined_data .= substr($chunk_data, 0, $chunk_length); 176 | $chunk_data = substr($chunk_data, $chunk_length + 1); 177 | $length += $chunk_length; 178 | } 179 | $response['data'] = $joined_data; 180 | } else { 181 | $length = $headers['content-length']; 182 | } 183 | } 184 | 185 | 186 | function _dsq_fopen_urlopen($url, $postdata, &$response, $file_name, $file_field) { 187 | $params = array(); 188 | if($file_name && $file_field) { 189 | $boundary = '----------' . md5(time()); 190 | $content = dsq_get_post_content($boundary, $postdata, $file_name, $file_field); 191 | $header = dsq_get_http_headers_for_request($boundary, $content, $file_name, $file_field); 192 | 193 | $params = array('http' => array( 194 | 'method' => 'POST', 195 | 'header' => $header, 196 | 'content' => $content, 197 | 'timeout' => SOCKET_TIMEOUT 198 | )); 199 | } else { 200 | if($postdata) { 201 | $params = array('http' => array( 202 | 'method' => 'POST', 203 | 'header' => 'Content-Type: application/x-www-form-urlencoded', 204 | 'content' => dsq_get_query_string($postdata), 205 | 'timeout' => SOCKET_TIMEOUT 206 | )); 207 | } 208 | } 209 | 210 | 211 | ini_set('user_agent', USER_AGENT); 212 | $ctx = stream_context_create($params); 213 | $fp = fopen($url, 'rb', false, $ctx); 214 | if(!$fp) { 215 | return false; 216 | } 217 | 218 | // Get status code from headers. 219 | list($unused, $response['code'], $unused) = explode(' ', $http_response_header[0], 3); 220 | $headers = array_slice($http_response_header, 1); 221 | 222 | // Convert headers into associative array. 223 | foreach($headers as $unused=>$header) { 224 | $header = explode(':', $header); 225 | $header[0] = trim($header[0]); 226 | $header[1] = trim($header[1]); 227 | $headers[strtolower($header[0])] = strtolower($header[1]); 228 | } 229 | 230 | $response['data'] = stream_get_contents($fp); 231 | } 232 | 233 | 234 | /** 235 | * Wrapper to provide a single interface for making an HTTP request. 236 | * 237 | * Attempts to use cURL or fsockopen(), whichever is available 238 | * first. 239 | * 240 | * @param string $url URL to make request to. 241 | * @param array $postdata (optional) If postdata is provided, the request 242 | * method is POST with the key/value pairs as 243 | * the data. 244 | * @param array $file (optional) Should provide associative array 245 | * with two keys: name and field. Name should 246 | * be the name of the file and field is the name 247 | * of the field to POST. 248 | */ 249 | function dsq_urlopen($url, $postdata=false, $file=false) { 250 | $response = array( 251 | 'data' => '', 252 | 'code' => 0 253 | ); 254 | 255 | if($file) { 256 | extract($file, EXTR_PREFIX_ALL, 'file'); 257 | } 258 | if(empty($file_name) || empty($file_field)) { 259 | $file_name = false; 260 | $file_field = false; 261 | } 262 | 263 | // Try curl, fsockopen, fopen + stream (PHP5 only), exec wget 264 | if(function_exists('curl_init')) { 265 | if (!function_exists('curl_setopt_array')) { 266 | function curl_setopt_array(&$ch, $curl_options) 267 | { 268 | foreach ($curl_options as $option => $value) { 269 | if (!curl_setopt($ch, $option, $value)) { 270 | return false; 271 | } 272 | } 273 | return true; 274 | } 275 | } 276 | _dsq_curl_urlopen($url, $postdata, $response, $file_name, $file_field); 277 | // } else if(ini_get('allow_url_fopen') && function_exists('stream_get_contents')) { 278 | // _dsq_fopen_urlopen($url, $postdata, $response, $file_name, $file_field); 279 | } else { 280 | // TODO: Find the failure condition for fsockopen() (sockets?) 281 | _dsq_fsockopen_urlopen($url, $postdata, $response, $file_name, $file_field); 282 | } 283 | 284 | // returns array with keys data and code (from headers) 285 | 286 | return $response; 287 | } 288 | 289 | function dsq_url_method() { 290 | if(function_exists('curl_init')) { 291 | return 'curl'; 292 | } else if(ini_get('allow_url_fopen') && function_exists('stream_get_contents')) { 293 | return 'fopen'; 294 | } else { 295 | return 'fsockopen'; 296 | } 297 | } 298 | ?> 299 | --------------------------------------------------------------------------------