PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/libraries/koowa/request/request.php

https://bitbucket.org/organicdevelopment/joomla-2.5
PHP | 712 lines | 402 code | 100 blank | 210 comment | 81 complexity | f80fe5079b35ce06f8864d54f526ca60 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-2.0, MIT, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * @version $Id: request.php 4477 2012-02-10 01:06:38Z johanjanssens $
  4. * @category Koowa
  5. * @package Koowa_Request
  6. * @copyright Copyright (C) 2007 - 2012 Johan Janssens. All rights reserved.
  7. * @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
  8. * @link http://www.nooku.org
  9. */
  10. //Instantiate the request singleton
  11. KRequest::getInstance();
  12. /**
  13. * Request class
  14. *
  15. * @author Johan Janssens <johan@nooku.org>
  16. * @category Koowa
  17. * @package Koowa_Request
  18. * @uses KFilter
  19. * @uses KInflector
  20. * @uses KService
  21. * @static
  22. */
  23. class KRequest
  24. {
  25. /**
  26. * URL of the request regardless of the server
  27. *
  28. * @var KHttpUrl
  29. */
  30. protected static $_url = null;
  31. /**
  32. * Base path of the request.
  33. *
  34. * @var KHttpUrl
  35. */
  36. protected static $_base = null;
  37. /**
  38. * Root path of the request.
  39. *
  40. * @var KHttpUrl
  41. */
  42. protected static $_root = null;
  43. /**
  44. * Referrer of the request
  45. *
  46. * @var KHttpUrl
  47. */
  48. protected static $_referrer = null;
  49. /**
  50. * The raw post or put content information
  51. *
  52. * @var array
  53. */
  54. protected static $_content = null;
  55. /**
  56. * The request accepts information
  57. *
  58. * @var array
  59. */
  60. protected static $_accept = null;
  61. /**
  62. * Constructor
  63. *
  64. * Prevent creating instances of this class by making the contructor private
  65. */
  66. final private function __construct(KConfig $config)
  67. {
  68. $content = self::content();
  69. if(self::type() == 'HTTP')
  70. {
  71. if(strpos(PHP_SAPI, 'cgi') !== false) {
  72. $authorization = KRequest::get('server.REDIRECT_HTTP_AUTHORIZATION', 'string');
  73. } else {
  74. $authorization = KRequest::get('server.HTTP_AUTHORIZATION', 'url');
  75. }
  76. if (strstr($authorization,"Basic"))
  77. {
  78. $parts = explode(':',base64_decode(substr($authorization, 6)));
  79. if (count($parts) == 2)
  80. {
  81. KRequest::set('server.PHP_AUTH_USER', $parts[0]);
  82. KRequest::set('server.PHP_AUTH_PW' , $parts[1]);
  83. }
  84. }
  85. }
  86. if(!empty($content['data']))
  87. {
  88. if($content['type'] == 'application/x-www-form-urlencoded')
  89. {
  90. if (in_array(self::method(), array('PUT', 'DELETE')))
  91. {
  92. parse_str($content['data'], $GLOBALS['_'.self::method()]);
  93. $GLOBALS['_REQUEST'] = array_merge($GLOBALS['_REQUEST'], $GLOBALS['_'.self::method()]);
  94. }
  95. }
  96. if($content['type'] == 'application/json')
  97. {
  98. if(in_array(self::method(), array('POST', 'PUT', 'DELETE')))
  99. {
  100. $GLOBALS['_'.self::method()] = json_decode($content['data'], true);
  101. $GLOBALS['_REQUEST'] = array_merge($GLOBALS['_REQUEST'], $GLOBALS['_'.self::method()]);
  102. }
  103. }
  104. }
  105. }
  106. /**
  107. * Clone
  108. *
  109. * Prevent creating clones of this class
  110. */
  111. final private function __clone() { }
  112. /**
  113. * Force creation of a singleton
  114. *
  115. * @return void
  116. */
  117. public static function getInstance($config = array())
  118. {
  119. static $instance;
  120. if ($instance === NULL)
  121. {
  122. if(!$config instanceof KConfig) {
  123. $config = new KConfig($config);
  124. }
  125. $instance = new self($config);
  126. }
  127. return $instance;
  128. }
  129. /**
  130. * Get sanitized data from the request.
  131. *
  132. * @param string Variable identifier, prefixed by hash name eg post.foo.bar
  133. * @param mixed Filter(s), can be a KFilter object, a filter name, an array of filter names or a filter identifier
  134. * @param mixed Default value when the variable doesn't exist
  135. * @throws KRequestException When an invalid filter was passed
  136. * @return mixed The sanitized data
  137. */
  138. public static function get($identifier, $filter, $default = null)
  139. {
  140. list($hash, $keys) = self::_parseIdentifier($identifier);
  141. $result = null;
  142. if(isset($GLOBALS['_'.$hash]))
  143. {
  144. $result = $GLOBALS['_'.$hash];
  145. foreach($keys as $key)
  146. {
  147. if(array_key_exists($key, $result)) {
  148. $result = $result[$key];
  149. } else {
  150. $result = null;
  151. break;
  152. }
  153. }
  154. }
  155. // If the value is null return the default
  156. if(is_null($result)) {
  157. return $default;
  158. }
  159. // Handle magic quotes compatability
  160. if (get_magic_quotes_gpc() && !in_array($hash, array('FILES', 'SESSION'))) {
  161. $result = self::_stripSlashes( $result );
  162. }
  163. if(!($filter instanceof KFilterInterface)) {
  164. $filter = KService::get('koowa:filter.factory')->instantiate($filter);
  165. }
  166. return $filter->sanitize($result);
  167. }
  168. /**
  169. * Set a variable in the request. Cookies and session data are stored persistently.
  170. *
  171. * @param mixed Variable identifier, prefixed by hash name eg post.foo.bar
  172. * @param mixed Variable value
  173. */
  174. public static function set($identifier, $value)
  175. {
  176. list($hash, $keys) = self::_parseIdentifier($identifier);
  177. // Add to _REQUEST hash if original hash is get, post, or cookies
  178. if(in_array($hash, array('GET', 'POST', 'COOKIE'))) {
  179. self::set('request.'.implode('.', $keys), $value);
  180. }
  181. // Store cookies persistently
  182. if($hash == 'COOKIE' && strpos(KRequest::protocol(), 'http') !== false)
  183. {
  184. // rewrite the $keys as foo[bar][bar]
  185. $ckeys = $keys; // get a copy
  186. $name = array_shift($ckeys);
  187. foreach($ckeys as $ckey) {
  188. $name .= '['.$ckey.']';
  189. }
  190. if(!setcookie($name, $value)) {
  191. throw new KRequestException("Couldn't set cookie, headers already sent.");
  192. }
  193. }
  194. // Store in $GLOBALS
  195. foreach(array_reverse($keys, true) as $key) {
  196. $value = array($key => $value);
  197. }
  198. // Add the global if it's doesn't exist
  199. if(!isset($GLOBALS['_'.$hash])) {
  200. $GLOBALS['_'.$hash] = array();
  201. }
  202. $GLOBALS['_'.$hash] = KHelperArray::merge($GLOBALS['_'.$hash], $value);
  203. }
  204. /**
  205. * Check if a variable exists based on an identifier
  206. *
  207. * @param string Variable identifier, prefixed by hash name eg post.foo.bar
  208. * @return boolean
  209. */
  210. public static function has($identifier)
  211. {
  212. list($hash, $keys) = self::_parseIdentifier($identifier);
  213. foreach($keys as $key)
  214. {
  215. if(isset($GLOBALS['_'.$hash]) && array_key_exists($key, $GLOBALS['_'.$hash])) {
  216. return true;
  217. }
  218. }
  219. return false;
  220. }
  221. /**
  222. * Get the POST or PUT raw content information
  223. *
  224. * The raw post data is not available with enctype="multipart/form-data".
  225. *
  226. * @param string The content data to return. Can be 'type' or 'data'.
  227. * If not set, all the data will be returned.
  228. * @return array An associative array with the content data. Valid keys are
  229. * 'type' and 'data'
  230. */
  231. public static function content($key = null)
  232. {
  233. $result = '';
  234. if (!isset(self::$_content) && isset($_SERVER['CONTENT_TYPE']))
  235. {
  236. $type = $_SERVER['CONTENT_TYPE'];
  237. // strip parameters from content-type like "; charset=UTF-8"
  238. if (is_string($type))
  239. {
  240. if (preg_match('/^([^,\;]*)/', $type, $matches)) {
  241. $type = $matches[1];
  242. }
  243. }
  244. self::$_content['type'] = $type;
  245. $data = '';
  246. if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0)
  247. {
  248. $input = fopen('php://input', 'r');
  249. while ($chunk = fread($input, 1024)) {
  250. $data .= $chunk;
  251. }
  252. fclose($input);
  253. }
  254. self::$_content['data'] = $data;
  255. }
  256. return isset($key) ? self::$_content[$key] : self::$_content;
  257. }
  258. /**
  259. * Get the accept request information
  260. *
  261. * @param string The accept data to return. Can be 'format', 'encoding' or 'language'.
  262. * If not set, all the accept data will be returned.
  263. * @return array An associative array with the content data. Valid keys are
  264. * 'format', 'encoding' and 'language'
  265. */
  266. public static function accept($type = null)
  267. {
  268. if (!isset(self::$_accept) && isset($_SERVER['HTTP_ACCEPT']))
  269. {
  270. $accept = KRequest::get('server.HTTP_ACCEPT', 'string');
  271. self::$_accept['format'] = self::_parseAccept($accept);
  272. if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
  273. {
  274. $accept = KRequest::get('server.HTTP_ACCEPT_ENCODING', 'string');
  275. self::$_accept['encoding'] = self::_parseAccept($accept);
  276. }
  277. if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  278. {
  279. $accept = KRequest::get('server.HTTP_ACCEPT_LANGUAGE', 'string');
  280. self::$_accept['language'] = self::_parseAccept($accept);
  281. }
  282. }
  283. return $type ? self::$_accept[$type] : self::$_accept;
  284. }
  285. /**
  286. * Returns the client information doing the request
  287. *
  288. * @return string $_SERVER['HTTP_USER_AGENT'] or an empty string if it's not supplied in the request
  289. */
  290. public static function client()
  291. {
  292. return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
  293. }
  294. /**
  295. * Returns the HTTP referrer.
  296. *
  297. * 'referer' a commonly used misspelling word for 'referrer'
  298. * @see http://en.wikipedia.org/wiki/HTTP_referrer
  299. *
  300. * @param boolean Only allow internal url's
  301. * @return KHttpUrl A KHttpUrl object
  302. */
  303. public static function referrer($isInternal = true)
  304. {
  305. if(!isset(self::$_referrer))
  306. {
  307. if($referrer = KRequest::get('server.HTTP_REFERER', 'url'))
  308. {
  309. self::$_referrer = KService::get('koowa:http.url', array('url' => $referrer));
  310. if($isInternal)
  311. {
  312. if(!KService::get('koowa:filter.internalurl')->validate((string)self::$_referrer)) {
  313. return null;
  314. }
  315. }
  316. }
  317. }
  318. return self::$_referrer;
  319. }
  320. /**
  321. * Return the URI of the request regardless of the server
  322. *
  323. * @return KHttpUrl A KHttpUri object
  324. */
  325. public static function url()
  326. {
  327. if(!isset(self::$_url))
  328. {
  329. $url = self::protocol().'://';
  330. if (PHP_SAPI !== 'cli')
  331. {
  332. /*
  333. * Since we are assigning the URI from the server variables, we first need
  334. * to determine if we are running on apache or IIS. If PHP_SELF and REQUEST_URI
  335. * are present, we will assume we are running on apache.
  336. */
  337. if (!empty ($_SERVER['PHP_SELF']) && !empty ($_SERVER['REQUEST_URI']))
  338. {
  339. /*
  340. * To build the entire URI we need to prepend the protocol, and the http host
  341. * to the URI string.
  342. */
  343. $url .= $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  344. /*
  345. * Since we do not have REQUEST_URI to work with, we will assume we are
  346. * running on IIS and will therefore need to work some magic with the SCRIPT_NAME and
  347. * QUERY_STRING environment variables.
  348. */
  349. }
  350. else
  351. {
  352. // IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable
  353. $url .= $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'];
  354. // If the query string exists append it to the URI string
  355. if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
  356. $url .= '?' . $_SERVER['QUERY_STRING'];
  357. }
  358. }
  359. }
  360. else $url .= 'koowa';
  361. // Sanitize the url since we can't trust the server var
  362. $url = KService::get('koowa:filter.url')->sanitize($url);
  363. // Create the URI object
  364. self::$_url = KService::get('koowa:http.url', array('url' => $url));
  365. }
  366. return self::$_url;
  367. }
  368. /**
  369. * Returns the base path of the request.
  370. *
  371. * @return object A KHttpUrl object
  372. */
  373. public static function base()
  374. {
  375. if(!isset(self::$_base))
  376. {
  377. // Get the base request path
  378. if (strpos(PHP_SAPI, 'cgi') !== false && !ini_get('cgi.fix_pathinfo') && !empty($_SERVER['REQUEST_URI']))
  379. {
  380. // PHP-CGI on Apache with "cgi.fix_pathinfo = 0"
  381. // We don't have user-supplied PATH_INFO in PHP_SELF
  382. $path = $_SERVER['PHP_SELF'];
  383. }
  384. else $path = $_SERVER['SCRIPT_NAME'];
  385. $path = rtrim(dirname($path), '/\\');
  386. // Sanitize the url since we can't trust the server var
  387. $path = KService::get('koowa:filter.url')->sanitize($path);
  388. self::$_base = KService::get('koowa:http.url', array('url' => $path));
  389. }
  390. return self::$_base;
  391. }
  392. /**
  393. * Returns the root path of the request.
  394. *
  395. * In most case this value will be the same as KRequest::base however it can be
  396. * changed by pushing in a different value
  397. *
  398. * @return object A KHttpUrl object
  399. */
  400. public static function root($path = null)
  401. {
  402. if(!is_null($path))
  403. {
  404. if(!$path instanceof KhttpUrl) {
  405. $path = KService::get('koowa:http.url', array('url' => $path));
  406. }
  407. self::$_root = $path;
  408. }
  409. if(is_null(self::$_root)) {
  410. self::$_root = self::$_base;
  411. }
  412. return self::$_root;
  413. }
  414. /**
  415. * Returns the current request protocol, based on $_SERVER['https']. In CLI
  416. * mode, 'cli' will be returned.
  417. *
  418. * @return string
  419. */
  420. public static function protocol()
  421. {
  422. $protocol = 'cli';
  423. if (PHP_SAPI !== 'cli')
  424. {
  425. $protocol = 'http';
  426. if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off')) {
  427. $protocol = 'https';
  428. }
  429. }
  430. return $protocol;
  431. }
  432. /**
  433. * Returns current request method.
  434. *
  435. * @return string
  436. */
  437. public static function method()
  438. {
  439. $method = '';
  440. if(PHP_SAPI != 'cli')
  441. {
  442. $method = strtoupper($_SERVER['REQUEST_METHOD']);
  443. if($method == 'POST')
  444. {
  445. if(isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
  446. $method = strtoupper($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']);
  447. }
  448. if(self::has('post._method')) {
  449. $method = strtoupper(self::get('post._method', 'cmd'));
  450. }
  451. }
  452. }
  453. return $method;
  454. }
  455. /**
  456. * Return the current request transport type.
  457. *
  458. * @return string
  459. */
  460. public static function type()
  461. {
  462. $type = 'HTTP';
  463. if(isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
  464. $type = 'AJAX';
  465. }
  466. if( isset($_SERVER['HTTP_X_FLASH_VERSION'])) {
  467. $type = 'FLASH';
  468. }
  469. if(preg_match('/^(Shockwave|Adobe) Flash/', KRequest::client()) == 1) {
  470. $type = 'FLASH';
  471. }
  472. return $type;
  473. }
  474. /**
  475. * Return the request token
  476. *
  477. * @return string The request token or NULL if no token could be found
  478. */
  479. public static function token()
  480. {
  481. $token = null;
  482. if(self::has('server.HTTP_X_TOKEN')) {
  483. $token = self::get('server.HTTP_X_TOKEN', 'md5');
  484. }
  485. if(self::has('request._token')) {
  486. $token = self::get('request._token', 'md5');
  487. }
  488. return $token;
  489. }
  490. /**
  491. * Return the request format
  492. *
  493. * This function tries to find the format by inspecting the accept header,
  494. * only if one accept type is specified the format will be parsed from it,
  495. * otherwise the path extension or the 'format' request variable is used.
  496. *
  497. * @return string The request format or NULL if no format could be found
  498. */
  499. public static function format()
  500. {
  501. $format = null;
  502. if(count(self::accept('format')) == 1)
  503. {
  504. $mime = explode('/', key(self::accept('format')));
  505. $format = $mime[1];
  506. if($pos = strpos($format, '+')) {
  507. $format = substr($format, 0, $pos);
  508. }
  509. //Format cannot be *
  510. if($format == '*') {
  511. $format = null;
  512. }
  513. }
  514. if(self::has('request.format')) {
  515. $format = self::get('request.format', 'word');
  516. }
  517. return $format;
  518. }
  519. /**
  520. * Parse the variable identifier
  521. *
  522. * @param string Variable identifier
  523. * @return array 0 => hash, 1 => parts
  524. */
  525. protected static function _parseIdentifier($identifier)
  526. {
  527. $parts = array();
  528. $hash = $identifier;
  529. // Validate the variable format
  530. if(strpos($identifier, '.') !== false)
  531. {
  532. // Split the variable name into it's parts
  533. $parts = explode('.', $identifier);
  534. // Validate the hash name
  535. $hash = array_shift($parts);
  536. }
  537. $hash = strtoupper($hash);
  538. return array($hash, $parts);
  539. }
  540. /**
  541. * Parses an accept header and returns an array (type => quality) of the
  542. * accepted types, ordered by quality.
  543. *
  544. * @param string header to parse
  545. * @param array default values
  546. * @return array
  547. */
  548. protected static function _parseAccept( $accept, array $defaults = NULL)
  549. {
  550. if (!empty($accept))
  551. {
  552. // Get all of the types
  553. $types = explode(',', $accept);
  554. foreach ($types as $type)
  555. {
  556. // Split the type into parts
  557. $parts = explode(';', $type);
  558. // Make the type only the MIME
  559. $type = trim(array_shift($parts));
  560. // Default quality is 1.0
  561. $quality = 1.0;
  562. foreach ($parts as $part)
  563. {
  564. // Prevent undefined $value notice below
  565. if (strpos($part, '=') === FALSE) {
  566. continue;
  567. }
  568. // Separate the key and value
  569. list ($key, $value) = explode('=', trim($part));
  570. if ($key === 'q')
  571. {
  572. // There is a quality for this type
  573. $quality = (float) trim($value);
  574. }
  575. }
  576. // Add the accept type and quality
  577. $defaults[$type] = $quality;
  578. }
  579. }
  580. // Make sure that accepts is an array
  581. $accepts = (array) $defaults;
  582. // Order by quality
  583. arsort($accepts);
  584. return $accepts;
  585. }
  586. /**
  587. * Strips slashes recursively on an array
  588. *
  589. * @param array Array of (nested arrays of) strings
  590. * @return array The input array with stripshlashes applied to it
  591. */
  592. protected static function _stripSlashes( $value )
  593. {
  594. if(!is_object($value)) {
  595. $value = is_array( $value ) ? array_map( array( 'KRequest', '_stripSlashes' ), $value ) : stripslashes( $value );
  596. }
  597. return $value;
  598. }
  599. }