Guest User

OAuth.php

a guest
May 24th, 2014
271
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.58 KB | None | 0 0
  1. <?php
  2.  
  3. class OAuthException extends Exception {
  4. // pass
  5. }
  6.  
  7. class OAuthConsumer {
  8. public $key;
  9. public $secret;
  10.  
  11. function __construct($key, $secret, $callback_url=NULL) { $this->key = $key; $this->secret = $secret; $this->callback_url = $callback_url;
  12. }
  13.  
  14. function __toString() {
  15. return "OAuthConsumer[key=$this->key,secret=$this->secret]";
  16. }
  17. }
  18.  
  19. class OAuthToken {
  20. // access tokens and request tokens
  21. public $key;
  22. public $secret;
  23.  
  24. /**
  25. * key = the token
  26. * secret = the token secret
  27. */
  28. function __construct($key, $secret) {
  29. $this->key = $key;
  30. $this->secret = $secret;
  31. }
  32.  
  33. /**
  34. * generates the basic string serialization of a token that a server
  35. * would respond to request_token and access_token calls with
  36. */
  37. function to_string() {
  38. return "oauth_token=" .
  39. OAuthUtil::urlencode_rfc3986($this->key) .
  40. "&oauth_token_secret=" .
  41. OAuthUtil::urlencode_rfc3986($this->secret);
  42. }
  43.  
  44. function __toString() {
  45. return $this->to_string();
  46. }
  47. }
  48.  
  49.  
  50. abstract class OAuthSignatureMethod {
  51.  
  52. abstract public function get_name();
  53.  
  54. abstract public function build_signature($request, $consumer, $token);
  55.  
  56. public function check_signature($request, $consumer, $token, $signature) { $built = $this->build_signature($request, $consumer, $token); return $built == $signature;
  57. }
  58. }
  59.  
  60.  
  61. class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { function get_name() { return "HMAC-SHA1";
  62. }
  63.  
  64. public function build_signature($request, $consumer, $token) { $base_string = $request->get_signature_base_string(); $request->base_string = $base_string; $key_parts = array(
  65. $consumer->secret,
  66. ($token) ? $token->secret : ""
  67. );
  68.  
  69. $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); $key = implode('&', $key_parts); return base64_encode(hash_hmac('sha1', $base_string, $key, true));
  70. }
  71. }
  72.  
  73.  
  74. class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { public function get_name() { return "PLAINTEXT";
  75. }
  76.  
  77.  
  78. public function build_signature($request, $consumer, $token) { $key_parts = array( $consumer->secret, ($token) ? $token->secret : ""
  79. );
  80.  
  81. $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
  82. $key = implode('&', $key_parts);
  83. $request->base_string = $key;
  84.  
  85. return $key;
  86. }
  87. }
  88.  
  89.  
  90. abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { public function get_name() { return "RSA-SHA1"; }
  91.  
  92.  
  93. protected abstract function fetch_public_cert(&$request); protected abstract function fetch_private_cert(&$request); public function build_signature($request, $consumer, $token) { $base_string = $request->get_signature_base_string(); $request->base_string = $base_string;
  94. // Fetch the private key cert based on the request
  95. $cert = $this->fetch_private_cert($request);
  96.  
  97. // Pull the private key ID from the certificate
  98. $privatekeyid = openssl_get_privatekey($cert);
  99.  
  100. // Sign using the key
  101. $ok = openssl_sign($base_string, $signature, $privatekeyid);
  102.  
  103. // Release the key resource
  104. openssl_free_key($privatekeyid);
  105.  
  106. return base64_encode($signature);
  107. }
  108.  
  109. public function check_signature($request, $consumer, $token, $signature) {
  110. $decoded_sig = base64_decode($signature);
  111.  
  112. $base_string = $request->get_signature_base_string();
  113.  
  114. // Fetch the public key cert based on the request
  115. $cert = $this->fetch_public_cert($request);
  116.  
  117. // Pull the public key ID from the certificate
  118. $publickeyid = openssl_get_publickey($cert);
  119.  
  120. // Check the computed signature against the one passed in the query
  121. $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
  122.  
  123. // Release the key resource
  124. openssl_free_key($publickeyid);
  125.  
  126. return $ok == 1;
  127. }
  128. }
  129.  
  130. class OAuthRequest {
  131. private $parameters;
  132. private $http_method;
  133. private $http_url;
  134. // for debug purposes
  135. public $base_string;
  136. public static $version = '1.0';
  137. public static $POST_INPUT = 'php://input';
  138.  
  139. function __construct($http_method, $http_url, $parameters=NULL) {
  140. @$parameters or $parameters = array();
  141. $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters);
  142. $this->parameters = $parameters;
  143. $this->http_method = $http_method;
  144. $this->http_url = $http_url;
  145. }
  146.  
  147.  
  148. /**
  149. * attempt to build up a request from what was passed to the server
  150. */
  151. public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) {
  152. $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on")
  153. ? 'http'
  154. : 'https';
  155. @$http_url or $http_url = $scheme .
  156. '://' . $_SERVER['HTTP_HOST'] .
  157. ':' .
  158. $_SERVER['SERVER_PORT'] .
  159. $_SERVER['REQUEST_URI'];
  160. @$http_method or $http_method = $_SERVER['REQUEST_METHOD'];
  161.  
  162.  
  163. if (!$parameters) {
  164. // Find request headers
  165. $request_headers = OAuthUtil::get_headers();
  166.  
  167. // Parse the query-string to find GET parameters
  168. $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']);
  169.  
  170. // It's a POST request of the proper content-type, so parse POST
  171. // parameters and add those overriding any duplicates from GET
  172. if ($http_method == "POST"
  173. && @strstr($request_headers["Content-Type"],
  174. "application/x-www-form-urlencoded")
  175. ) {
  176. $post_data = OAuthUtil::parse_parameters(
  177. file_get_contents(self::$POST_INPUT)
  178. );
  179. $parameters = array_merge($parameters, $post_data);
  180. }
  181.  
  182.  
  183.  
  184. if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") {
  185. $header_parameters = OAuthUtil::split_header(
  186. $request_headers['Authorization']
  187. );
  188. $parameters = array_merge($parameters, $header_parameters);
  189. }
  190.  
  191. }
  192.  
  193. return new OAuthRequest($http_method, $http_url, $parameters);
  194. }
  195.  
  196. /**
  197. * pretty much a helper function to set up the request
  198. */
  199. public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
  200. @$parameters or $parameters = array();
  201. $defaults = array("oauth_version" => OAuthRequest::$version,
  202. "oauth_nonce" => OAuthRequest::generate_nonce(),
  203. "oauth_timestamp" => OAuthRequest::generate_timestamp(),
  204. "oauth_consumer_key" => $consumer->key);
  205. if ($token)
  206. $defaults['oauth_token'] = $token->key;
  207.  
  208. $parameters = array_merge($defaults, $parameters);
  209.  
  210. return new OAuthRequest($http_method, $http_url, $parameters);
  211. }
  212.  
  213. public function set_parameter($name, $value, $allow_duplicates = true) {
  214. if ($allow_duplicates && isset($this->parameters[$name])) {
  215. // We have already added parameter(s) with this name, so add to the list
  216. if (is_scalar($this->parameters[$name])) {
  217. // This is the first duplicate, so transform scalar (string)
  218. // into an array so we can add the duplicates
  219. $this->parameters[$name] = array($this->parameters[$name]);
  220. }
  221.  
  222. $this->parameters[$name][] = $value;
  223. } else {
  224. $this->parameters[$name] = $value;
  225. }
  226. }
  227.  
  228. public function get_parameter($name) {
  229. return isset($this->parameters[$name]) ? $this->parameters[$name] : null;
  230. }
  231.  
  232. public function get_parameters() {
  233. return $this->parameters;
  234. }
  235.  
  236. public function unset_parameter($name) {
  237. unset($this->parameters[$name]);
  238. }
  239.  
  240. /**
  241. * The request parameters, sorted and concatenated into a normalized string.
  242. * @return string
  243. */
  244. public function get_signable_parameters() {
  245. // Grab all parameters
  246. $params = $this->parameters;
  247.  
  248. // Remove oauth_signature if present
  249. // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
  250. if (isset($params['oauth_signature'])) {
  251. unset($params['oauth_signature']);
  252. }
  253.  
  254. return OAuthUtil::build_http_query($params);
  255. }
  256.  
  257. /**
  258. * Returns the base string of this request
  259. *
  260. * The base string defined as the method, the url
  261. * and the parameters (normalized), each urlencoded
  262. * and the concated with &.
  263. */
  264. public function get_signature_base_string() {
  265. $parts = array(
  266. $this->get_normalized_http_method(),
  267. $this->get_normalized_http_url(),
  268. $this->get_signable_parameters()
  269. );
  270.  
  271. $parts = OAuthUtil::urlencode_rfc3986($parts);
  272.  
  273. return implode('&', $parts);
  274. }
  275.  
  276. /**
  277. * just uppercases the http method
  278. */
  279. public function get_normalized_http_method() {
  280. return strtoupper($this->http_method);
  281. }
  282.  
  283. /**
  284. * parses the url and rebuilds it to be
  285. * scheme://host/path
  286. */
  287. public function get_normalized_http_url() {
  288. $parts = parse_url($this->http_url);
  289.  
  290. $port = @$parts['port'];
  291. $scheme = $parts['scheme'];
  292. $host = $parts['host'];
  293. $path = @$parts['path'];
  294.  
  295. $port or $port = ($scheme == 'https') ? '443' : '80';
  296.  
  297. if (($scheme == 'https' && $port != '443')
  298. || ($scheme == 'http' && $port != '80')) {
  299. $host = "$host:$port";
  300. }
  301. return "$scheme://$host$path";
  302. }
  303.  
  304. /**
  305. * builds a url usable for a GET request
  306. */
  307. public function to_url() {
  308. $post_data = $this->to_postdata();
  309. $out = $this->get_normalized_http_url();
  310. if ($post_data) {
  311. $out .= '?'.$post_data;
  312. }
  313. return $out;
  314. }
  315.  
  316. /**
  317. * builds the data one would send in a POST request
  318. */
  319. public function to_postdata() {
  320. return OAuthUtil::build_http_query($this->parameters);
  321. }
  322.  
  323. /**
  324. * builds the Authorization: header
  325. */
  326. public function to_header($realm=null) {
  327. $first = true;
  328. if($realm) {
  329. $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"';
  330. $first = false;
  331. } else
  332. $out = 'Authorization: OAuth';
  333.  
  334. $total = array();
  335. foreach ($this->parameters as $k => $v) {
  336. if (substr($k, 0, 5) != "oauth") continue;
  337. if (is_array($v)) {
  338. throw new OAuthException('Arrays not supported in headers');
  339. }
  340. $out .= ($first) ? ' ' : ',';
  341. $out .= OAuthUtil::urlencode_rfc3986($k) .
  342. '="' .
  343. OAuthUtil::urlencode_rfc3986($v) .
  344. '"';
  345. $first = false;
  346. }
  347. return $out;
  348. }
  349.  
  350. public function __toString() {
  351. return $this->to_url();
  352. }
  353.  
  354.  
  355. public function sign_request($signature_method, $consumer, $token) {
  356. $this->set_parameter(
  357. "oauth_signature_method",
  358. $signature_method->get_name(),
  359. false
  360. );
  361. $signature = $this->build_signature($signature_method, $consumer, $token);
  362. $this->set_parameter("oauth_signature", $signature, false);
  363. }
  364.  
  365. public function build_signature($signature_method, $consumer, $token) {
  366. $signature = $signature_method->build_signature($this, $consumer, $token);
  367. return $signature;
  368. }
  369.  
  370. /**
  371. * util function: current timestamp
  372. */
  373. private static function generate_timestamp() {
  374. return time();
  375. }
  376.  
  377. /**
  378. * util function: current nonce
  379. */
  380. private static function generate_nonce() {
  381. $mt = microtime();
  382. $rand = mt_rand();
  383.  
  384. return md5($mt . $rand); // md5s look nicer than numbers
  385. }
  386. }
  387.  
  388. class OAuthServer {
  389. protected $timestamp_threshold = 300; // in seconds, five minutes
  390. protected $version = '1.0'; // hi blaine
  391. protected $signature_methods = array();
  392.  
  393. protected $data_store;
  394.  
  395. function __construct($data_store) {
  396. $this->data_store = $data_store;
  397. }
  398.  
  399. public function add_signature_method($signature_method) {
  400. $this->signature_methods[$signature_method->get_name()] =
  401. $signature_method;
  402. }
  403.  
  404. // high level functions
  405.  
  406. /**
  407. * process a request_token request
  408. * returns the request token on success
  409. */
  410. public function fetch_request_token(&$request) {
  411. $this->get_version($request);
  412.  
  413. $consumer = $this->get_consumer($request);
  414.  
  415. // no token required for the initial token request
  416. $token = NULL;
  417.  
  418. $this->check_signature($request, $consumer, $token);
  419.  
  420. // Rev A change
  421. $callback = $request->get_parameter('oauth_callback');
  422. $new_token = $this->data_store->new_request_token($consumer, $callback);
  423.  
  424. return $new_token;
  425. }
  426.  
  427. /**
  428. * process an access_token request
  429. * returns the access token on success
  430. */
  431. public function fetch_access_token(&$request) {
  432. $this->get_version($request);
  433.  
  434. $consumer = $this->get_consumer($request);
  435.  
  436. // requires authorized request token
  437. $token = $this->get_token($request, $consumer, "request");
  438.  
  439. $this->check_signature($request, $consumer, $token);
  440.  
  441. // Rev A change
  442. $verifier = $request->get_parameter('oauth_verifier');
  443. $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
  444.  
  445. return $new_token;
  446. }
  447.  
  448. /**
  449. * verify an api call, checks all the parameters
  450. */
  451. public function verify_request(&$request) {
  452. $this->get_version($request);
  453. $consumer = $this->get_consumer($request);
  454. $token = $this->get_token($request, $consumer, "access");
  455. $this->check_signature($request, $consumer, $token);
  456. return array($consumer, $token);
  457. }
  458.  
  459. // Internals from here
  460. /**
  461. * version 1
  462. */
  463. private function get_version(&$request) {
  464. $version = $request->get_parameter("oauth_version");
  465. if (!$version) {
  466. // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
  467. // Chapter 7.0 ("Accessing Protected Ressources")
  468. $version = '1.0';
  469. }
  470. if ($version !== $this->version) {
  471. throw new OAuthException("OAuth version '$version' not supported");
  472. }
  473. return $version;
  474. }
  475.  
  476. /**
  477. * figure out the signature with some defaults
  478. */
  479. private function get_signature_method(&$request) {
  480. $signature_method =
  481. @$request->get_parameter("oauth_signature_method");
  482.  
  483. if (!$signature_method) {
  484. // According to chapter 7 ("Accessing Protected Ressources") the signature-method
  485. // parameter is required, and we can't just fallback to PLAINTEXT
  486. throw new OAuthException('No signature method parameter. This parameter is required');
  487. }
  488.  
  489. if (!in_array($signature_method,
  490. array_keys($this->signature_methods))) {
  491. throw new OAuthException(
  492. "Signature method '$signature_method' not supported " .
  493. "try one of the following: " .
  494. implode(", ", array_keys($this->signature_methods))
  495. );
  496. }
  497. return $this->signature_methods[$signature_method];
  498. }
  499.  
  500. /**
  501. * try to find the consumer for the provided request's consumer key
  502. */
  503. private function get_consumer(&$request) {
  504. $consumer_key = @$request->get_parameter("oauth_consumer_key");
  505. if (!$consumer_key) {
  506. throw new OAuthException("Invalid consumer key");
  507. }
  508.  
  509. $consumer = $this->data_store->lookup_consumer($consumer_key);
  510. if (!$consumer) {
  511. throw new OAuthException("Invalid consumer");
  512. }
  513.  
  514. return $consumer;
  515. }
  516.  
  517. /**
  518. * try to find the token for the provided request's token key
  519. */
  520. private function get_token(&$request, $consumer, $token_type="access") {
  521. $token_field = @$request->get_parameter('oauth_token');
  522. $token = $this->data_store->lookup_token(
  523. $consumer, $token_type, $token_field
  524. );
  525. if (!$token) {
  526. throw new OAuthException("Invalid $token_type token: $token_field");
  527. }
  528. return $token;
  529. }
  530.  
  531. /**
  532. * all-in-one function to check the signature on a request
  533. * should guess the signature method appropriately
  534. */
  535. private function check_signature(&$request, $consumer, $token) {
  536. // this should probably be in a different method
  537. $timestamp = @$request->get_parameter('oauth_timestamp');
  538. $nonce = @$request->get_parameter('oauth_nonce');
  539.  
  540. $this->check_timestamp($timestamp);
  541. $this->check_nonce($consumer, $token, $nonce, $timestamp);
  542.  
  543. $signature_method = $this->get_signature_method($request);
  544.  
  545. $signature = $request->get_parameter('oauth_signature');
  546. $valid_sig = $signature_method->check_signature(
  547. $request,
  548. $consumer,
  549. $token,
  550. $signature
  551. );
  552.  
  553. if (!$valid_sig) {
  554. throw new OAuthException("Invalid signature");
  555. }
  556. }
  557.  
  558. /**
  559. * check that the timestamp is new enough
  560. */
  561. private function check_timestamp($timestamp) {
  562. if( ! $timestamp )
  563. throw new OAuthException(
  564. 'Missing timestamp parameter. The parameter is required'
  565. );
  566.  
  567. // verify that timestamp is recentish
  568. $now = time();
  569. if (abs($now - $timestamp) > $this->timestamp_threshold) {
  570. throw new OAuthException(
  571. "Expired timestamp, yours $timestamp, ours $now"
  572. );
  573. }
  574. }
  575.  
  576. /**
  577. * check that the nonce is not repeated
  578. */
  579. private function check_nonce($consumer, $token, $nonce, $timestamp) {
  580. if( ! $nonce )
  581. throw new OAuthException(
  582. 'Missing nonce parameter. The parameter is required'
  583. );
  584.  
  585. // verify that the nonce is uniqueish
  586. $found = $this->data_store->lookup_nonce(
  587. $consumer,
  588. $token,
  589. $nonce,
  590. $timestamp
  591. );
  592. if ($found) {
  593. throw new OAuthException("Nonce already used: $nonce");
  594. }
  595. }
  596.  
  597. }
  598.  
  599. class OAuthDataStore {
  600. function lookup_consumer($consumer_key) {
  601. // implement me
  602. }
  603.  
  604. function lookup_token($consumer, $token_type, $token) {
  605. // implement me
  606. }
  607.  
  608. function lookup_nonce($consumer, $token, $nonce, $timestamp) {
  609. // implement me
  610. }
  611.  
  612. function new_request_token($consumer, $callback = null) {
  613. // return a new token attached to this consumer
  614. }
  615.  
  616. function new_access_token($token, $consumer, $verifier = null) {
  617. // return a new access token attached to this consumer
  618. // for the user associated with this token if the request token
  619. // is authorized
  620. // should also invalidate the request token
  621. }
  622.  
  623. }
  624.  
  625. class OAuthUtil {
  626. public static function urlencode_rfc3986($input) {
  627. if (is_array($input)) {
  628. return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input);
  629. } else if (is_scalar($input)) {
  630. return str_replace(
  631. '+',
  632. ' ',
  633. str_replace('%7E', '~', rawurlencode($input))
  634. );
  635. } else {
  636. return '';
  637. }
  638. }
  639.  
  640.  
  641. // This decode function isn't taking into consideration the above
  642. // modifications to the encoding process. However, this method doesn't
  643. // seem to be used anywhere so leaving it as is.
  644. public static function urldecode_rfc3986($string) {
  645. return urldecode($string);
  646. }
  647.  
  648. // Utility function for turning the Authorization: header into
  649. // parameters, has to do some unescaping
  650. // Can filter out any non-oauth parameters if needed (default behaviour)
  651. public static function split_header($header, $only_allow_oauth_parameters = true) {
  652. $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/';
  653. $offset = 0;
  654. $params = array();
  655. while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) {
  656. $match = $matches[0];
  657. $header_name = $matches[2][0];
  658. $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0];
  659. if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) {
  660. $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content);
  661. }
  662. $offset = $match[1] + strlen($match[0]);
  663. }
  664.  
  665. if (isset($params['realm'])) {
  666. unset($params['realm']);
  667. }
  668.  
  669. return $params;
  670. }
  671.  
  672. // helper to try to sort out headers for people who aren't running apache
  673. public static function get_headers() {
  674. if (function_exists('apache_request_headers')) {
  675. // we need this to get the actual Authorization: header
  676. // because apache tends to tell us it doesn't exist
  677. $headers = apache_request_headers();
  678.  
  679. // sanitize the output of apache_request_headers because
  680. // we always want the keys to be Cased-Like-This and arh()
  681. // returns the headers in the same case as they are in the
  682. // request
  683. $out = array();
  684. foreach( $headers AS $key => $value ) {
  685. $key = str_replace(
  686. " ",
  687. "-",
  688. ucwords(strtolower(str_replace("-", " ", $key)))
  689. );
  690. $out[$key] = $value;
  691. }
  692. } else {
  693. // otherwise we don't have apache and are just going to have to hope
  694. // that $_SERVER actually contains what we need
  695. $out = array();
  696. if( isset($_SERVER['CONTENT_TYPE']) )
  697. $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
  698. if( isset($_ENV['CONTENT_TYPE']) )
  699. $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
  700.  
  701. foreach ($_SERVER as $key => $value) {
  702. if (substr($key, 0, 5) == "HTTP_") {
  703. // this is chaos, basically it is just there to capitalize the first
  704. // letter of every word that is not an initial HTTP and strip HTTP
  705. // code from przemek
  706. $key = str_replace(
  707. " ",
  708. "-",
  709. ucwords(strtolower(str_replace("_", " ", substr($key, 5))))
  710. );
  711. $out[$key] = $value;
  712. }
  713. }
  714. }
  715. return $out;
  716. }
  717.  
  718. // This function takes a input like a=b&a=c&d=e and returns the parsed
  719. // parameters like this
  720. // array('a' => array('b','c'), 'd' => 'e')
  721. public static function parse_parameters( $input ) {
  722. if (!isset($input) || !$input) return array();
  723.  
  724. $pairs = explode('&', $input);
  725.  
  726. $parsed_parameters = array();
  727. foreach ($pairs as $pair) {
  728. $split = explode('=', $pair, 2);
  729. $parameter = OAuthUtil::urldecode_rfc3986($split[0]);
  730. $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : '';
  731.  
  732. if (isset($parsed_parameters[$parameter])) {
  733. // We have already recieved parameter(s) with this name, so add to the list
  734. // of parameters with this name
  735.  
  736. if (is_scalar($parsed_parameters[$parameter])) {
  737. // This is the first duplicate, so transform scalar (string) into an array
  738. // so we can add the duplicates
  739. $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]);
  740. }
  741.  
  742. $parsed_parameters[$parameter][] = $value;
  743. } else {
  744. $parsed_parameters[$parameter] = $value;
  745. }
  746. }
  747. return $parsed_parameters;
  748. }
  749.  
  750. public static function build_http_query($params) {
  751. if (!$params) return '';
  752.  
  753. // Urlencode both keys and values
  754. $keys = OAuthUtil::urlencode_rfc3986(array_keys($params));
  755. $values = OAuthUtil::urlencode_rfc3986(array_values($params));
  756. $params = array_combine($keys, $values);
  757.  
  758. // Parameters are sorted by name, using lexicographical byte value ordering.
  759. // Ref: Spec: 9.1.1 (1)
  760. uksort($params, 'strcmp');
  761.  
  762. $pairs = array();
  763. foreach ($params as $parameter => $value) {
  764. if (is_array($value)) {
  765. // If two or more parameters share the same name, they are sorted by their value
  766. // Ref: Spec: 9.1.1 (1)
  767. natsort($value);
  768. foreach ($value as $duplicate_value) {
  769. $pairs[] = $parameter . '=' . $duplicate_value;
  770. }
  771. } else {
  772. $pairs[] = $parameter . '=' . $value;
  773. }
  774. }
  775. // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
  776. // Each name-value pair is separated by an '&' character (ASCII code 38)
  777. return implode('&', $pairs);
  778. }
  779. }
  780.  
  781. ?>
Advertisement
Add Comment
Please, Sign In to add comment