PageRenderTime 57ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/sapphire/core/HTTP.php

https://github.com/benbruscella/vpcounselling.com
PHP | 403 lines | 323 code | 31 blank | 49 comment | 42 complexity | 2da7e1bc5db7bc509e691059bde63bc0 MD5 | raw file
  1. <?php
  2. /**
  3. * A class with HTTP-related helpers.
  4. * Like Debug, this is more a bundle of methods than a class ;-)
  5. *
  6. * @package sapphire
  7. * @subpackage misc
  8. */
  9. class HTTP {
  10. static $userName;
  11. static $password;
  12. /**
  13. * Turns a local system filename into a URL by comparing it to the script filename
  14. */
  15. static function filename2url($filename) {
  16. $slashPos = -1;
  17. while(($slashPos = strpos($filename, "/", $slashPos+1)) !== false) {
  18. if(substr($filename, 0, $slashPos) == substr($_SERVER['SCRIPT_FILENAME'],0,$slashPos)) {
  19. $commonLength = $slashPos;
  20. } else {
  21. break;
  22. }
  23. }
  24. $urlBase = substr($_SERVER['PHP_SELF'], 0, -(strlen($_SERVER['SCRIPT_FILENAME']) - $commonLength));
  25. $url = $urlBase . substr($filename, $commonLength);
  26. $protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') ? "https" : "http";
  27. return "$protocol://". $_SERVER['HTTP_HOST'] . $url;
  28. // Count the number of extra folders the script is in.
  29. // $prefix = str_repeat("../", substr_count(substr($_SERVER[SCRIPT_FILENAME],$commonBaseLength)));
  30. }
  31. /**
  32. * Turn all relative URLs in the content to absolute URLs
  33. */
  34. static function absoluteURLs($html) {
  35. $html = str_replace('$CurrentPageURL', $_SERVER['REQUEST_URI'], $html);
  36. return HTTP::urlRewriter($html, '(substr($URL,0,1) == "/") ? ( Director::protocolAndHost() . $URL ) : ( (ereg("^[A-Za-z]+:", $URL)) ? $URL : Director::absoluteBaseURL() . $URL )' );
  37. }
  38. /*
  39. * Rewrite all the URLs in the given content, evaluating the given string as PHP code
  40. *
  41. * Put $URL where you want the URL to appear, however, you can't embed $URL in strings
  42. * Some example code:
  43. * '"../../" . $URL'
  44. * 'myRewriter($URL)'
  45. * '(substr($URL,0,1)=="/") ? "../" . substr($URL,1) : $URL'
  46. */
  47. static function urlRewriter($content, $code) {
  48. $attribs = array("src","background","a" => "href","link" => "href", "base" => "href");
  49. foreach($attribs as $tag => $attrib) {
  50. if(!is_numeric($tag)) $tagPrefix = "$tag ";
  51. else $tagPrefix = "";
  52. $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *\")([^\"]*)(\")/ie";
  53. $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *')([^']*)(')/ie";
  54. $regExps[] = "/(<{$tagPrefix}[^>]*$attrib *= *)([^\"' ]*)( )/ie";
  55. }
  56. $regExps[] = '/(background-image:[^;]*url *\()([^)]+)(\))/ie';
  57. $regExps[] = '/(background:[^;]*url *\()([^)]+)(\))/ie';
  58. // Make
  59. $code = 'stripslashes("$1") . (' . str_replace('$URL', 'stripslashes("$2")', $code) . ') . stripslashes("$3")';
  60. foreach($regExps as $regExp) {
  61. $content = preg_replace($regExp, $code, $content);
  62. }
  63. return $content;
  64. }
  65. /**
  66. * Will try to include a GET parameter for an existing URL,
  67. * preserving existing parameters and fragments.
  68. * If no URL is given, falls back to $_SERVER['REQUEST_URI'].
  69. * Uses parse_url() to dissect the URL, and http_build_query() to reconstruct it
  70. * with the additional parameter. Converts any '&' (ampersand)
  71. * URL parameter separators to the more XHTML compliant '&amp;'.
  72. *
  73. * CAUTION: If the URL is determined to be relative,
  74. * it is prepended with Director::absoluteBaseURL().
  75. * This method will always return an absolute URL because
  76. * Director::makeRelative() can lead to inconsistent results.
  77. *
  78. * @param String $varname
  79. * @param String $varvalue
  80. * @param String $currentURL Relative or absolute URL (Optional).
  81. * @param String $separator Separator for http_build_query(). (Optional).
  82. * @return String Absolute URL
  83. */
  84. public static function setGetVar($varname, $varvalue, $currentURL = null, $separator = '&amp;') {
  85. $uri = $currentURL ? $currentURL : Director::makeRelative($_SERVER['REQUEST_URI']);
  86. $isRelative = false;
  87. // We need absolute URLs for parse_url()
  88. if(Director::is_relative_url($uri)) {
  89. $uri = Director::absoluteBaseURL() . $uri;
  90. $isRelative = true;
  91. }
  92. // try to parse uri
  93. $parts = parse_url($uri);
  94. if(!$parts) {
  95. throw new InvalidArgumentException("Can't parse URL: " . $uri);
  96. }
  97. // Parse params and add new variable
  98. $params = array();
  99. if(isset($parts['query'])) parse_str($parts['query'], $params);
  100. $params[$varname] = $varvalue;
  101. // Generate URI segments and formatting
  102. $scheme = (isset($parts['scheme'])) ? $parts['scheme'] : 'http';
  103. $user = (isset($parts['user']) && $parts['user'] != '') ? $parts['user'] : '';
  104. if($user != '') {
  105. // format in either user:pass@host.com or user@host.com
  106. $user .= (isset($parts['pass']) && $parts['pass'] != '') ? ':' . $parts['pass'] . '@' : '@';
  107. }
  108. $host = (isset($parts['host'])) ? $parts['host'] : '';
  109. $port = (isset($parts['port']) && $parts['port'] != '') ? ':'.$parts['port'] : '';
  110. $path = (isset($parts['path']) && $parts['path'] != '') ? $parts['path'] : '';
  111. // handle URL params which are existing / new
  112. $params = ($params) ? '?' . http_build_query($params, null, $separator) : '';
  113. // keep fragments (anchors) intact.
  114. $fragment = (isset($parts['fragment']) && $parts['fragment'] != '') ? '#'.$parts['fragment'] : '';
  115. // Recompile URI segments
  116. $newUri = $scheme . '://' . $user . $host . $port . $path . $params . $fragment;
  117. if($isRelative) return Director::makeRelative($newUri);
  118. return $newUri;
  119. }
  120. static function RAW_setGetVar($varname, $varvalue, $currentURL = null) {
  121. $url = self::setGetVar($varname, $varvalue, $currentURL);
  122. return Convert::xml2raw($url);
  123. }
  124. /**
  125. * Search for all tags with a specific attribute, then return the value of that attribute in a flat array.
  126. *
  127. * @param string $content
  128. * @param array $attributes an array of tags to attributes, for example "[a] => 'href', [div] => 'id'"
  129. * @return array
  130. */
  131. public static function findByTagAndAttribute($content, $attributes) {
  132. $regexes = array();
  133. foreach($attributes as $tag => $attribute) {
  134. $regexes[] = "/<{$tag} [^>]*$attribute *= *([\"'])(.*?)\\1[^>]*>/i";
  135. $regexes[] = "/<{$tag} [^>]*$attribute *= *([^ \"'>]+)/i";
  136. }
  137. $result = array();
  138. if($regexes) foreach($regexes as $regex) {
  139. if(preg_match_all($regex, $content, $matches)) {
  140. $result = array_merge_recursive($result, (isset($matches[2]) ? $matches[2] : $matches[1]));
  141. }
  142. }
  143. return count($result) ? $result : null;
  144. }
  145. static function getLinksIn($content) {
  146. return self::findByTagAndAttribute($content, array("a" => "href"));
  147. }
  148. static function getImagesIn($content) {
  149. return self::findByTagAndAttribute($content, array("img" => "src"));
  150. }
  151. /*
  152. * Get mime type based on extension
  153. */
  154. static function getMimeType($filename) {
  155. global $global_mimetypes;
  156. if(!$global_mimetypes) self::loadMimeTypes();
  157. $ext = strtolower(substr($filename,strrpos($filename,'.')+1));
  158. if(isset($global_mimetypes[$ext])) return $global_mimetypes[$ext];
  159. }
  160. /*
  161. * Load the mime-type data from the system file
  162. */
  163. static function loadMimeTypes() {
  164. if(@file_exists('/etc/mime.types')) {
  165. $mimeTypes = file('/etc/mime.types');
  166. foreach($mimeTypes as $typeSpec) {
  167. if(($typeSpec = trim($typeSpec)) && substr($typeSpec,0,1) != "#") {
  168. $parts = preg_split("/[ \t\r\n]+/", $typeSpec);
  169. if(sizeof($parts) > 1) {
  170. $mimeType = array_shift($parts);
  171. foreach($parts as $ext) {
  172. $ext = strtolower($ext);
  173. $mimeData[$ext] = $mimeType;
  174. }
  175. }
  176. }
  177. }
  178. // Fail-over for if people don't have /etc/mime.types on their server. it's unclear how important this actually is
  179. } else {
  180. $mimeData = array(
  181. "doc" => "application/msword",
  182. "xls" => "application/vnd.ms-excel",
  183. "rtf" => "application/rtf",
  184. );
  185. }
  186. global $global_mimetypes;
  187. $global_mimetypes = $mimeData;
  188. return $mimeData;
  189. }
  190. /**
  191. * Send an HTTP request to the host.
  192. *
  193. * @return String Response text
  194. */
  195. static function sendRequest( $host, $path, $query, $port = 80 ) {
  196. $socket = fsockopen( $host, $port, $errno, $error );
  197. if( !$socket )
  198. return $error;
  199. if( $query )
  200. $query = '?' . $query;
  201. if( self::$userName && self::$password ) {
  202. $auth = "Authorization: Basic " . base64_encode( self::$userName . ':' . self::$password ) . "\r\n";
  203. } else {
  204. $auth = '';
  205. }
  206. $request = "GET {$path}{$query} HTTP/1.1\r\nHost: $host\r\nConnection: Close\r\n{$auth}\r\n";
  207. fwrite( $socket, $request );
  208. $response = stream_get_contents( $socket );
  209. return $response;
  210. }
  211. /**
  212. * Send a HTTP POST request through fsockopen().
  213. *
  214. * @param string $host Absolute URI without path, e.g. http://silverstripe.com
  215. * @param string $path Path with leading slash
  216. * @param array|string $data Payload for the request
  217. * @param string $name Parametername for the payload (only if passed as a string)
  218. * @param string $query
  219. * @param string $port
  220. * @return string Raw HTTP-result including headers
  221. */
  222. static function sendPostRequest($host, $path, $data, $name = null, $query = '', $port = 80, $getResponse = true) {
  223. $socket = fsockopen($host, $port, $errno, $error);
  224. if(!$socket)
  225. return $error;
  226. if(self::$userName && self::$password)
  227. $auth = "Authorization: Basic " . base64_encode(self::$userName . ':' . self::$password) . "\r\n";
  228. else
  229. $auth = '';
  230. if($query)
  231. $query = '?' . $query;
  232. $dataStr = (is_array($data)) ? http_build_query($data) : $name . '=' . urlencode($data);
  233. $request = "POST {$path}{$query} HTTP/1.1\r\nHost: $host\r\n{$auth}Content-Type: application/x-www-form-urlencoded\r\nContent-Length: " . strlen($dataStr) . "\r\n\r\n";
  234. $request .= $dataStr . "\r\n\r\n";
  235. fwrite($socket, $request);
  236. if($getResponse){
  237. $response = stream_get_contents($socket);
  238. return $response;
  239. }
  240. }
  241. protected static $cache_age = 0, $modification_date = null;
  242. protected static $etag = null;
  243. /**
  244. * Set the maximum age of this page in web caches, in seconds
  245. */
  246. static function set_cache_age($age) {
  247. self::$cache_age = $age;
  248. }
  249. static function register_modification_date($dateString) {
  250. $timestamp = strtotime($dateString);
  251. if($timestamp > self::$modification_date)
  252. self::$modification_date = $timestamp;
  253. }
  254. static function register_modification_timestamp($timestamp) {
  255. if($timestamp > self::$modification_date)
  256. self::$modification_date = $timestamp;
  257. }
  258. static function register_etag($etag) {
  259. self::$etag = $etag;
  260. }
  261. /**
  262. * Add the appropriate caching headers to the response, including If-Modified-Since / 304 handling.
  263. *
  264. * @param SS_HTTPResponse The SS_HTTPResponse object to augment. Omitted the argument or passing a string is deprecated; in these
  265. * cases, the headers are output directly.
  266. */
  267. static function add_cache_headers($body = null) {
  268. // Validate argument
  269. if($body && !($body instanceof SS_HTTPResponse)) {
  270. user_error("HTTP::add_cache_headers() must be passed an SS_HTTPResponse object", E_USER_WARNING);
  271. $body = null;
  272. }
  273. // Development sites have frequently changing templates; this can get stuffed up by the code
  274. // below.
  275. if(Director::isDev()) return;
  276. // The headers have been sent and we don't have an SS_HTTPResponse object to attach things to; no point in us trying.
  277. if(headers_sent() && !$body) return;
  278. // Popuplate $responseHeaders with all the headers that we want to build
  279. $responseHeaders = array();
  280. if(function_exists('apache_request_headers')) {
  281. $requestHeaders = apache_request_headers();
  282. if(isset($requestHeaders['X-Requested-With']) && $requestHeaders['X-Requested-With'] == 'XMLHttpRequest') self::$cache_age = 0;
  283. // bdc: now we must check for DUMB IE6:
  284. if(isset($requestHeaders['x-requested-with']) && $requestHeaders['x-requested-with'] == 'XMLHttpRequest') self::$cache_age = 0;
  285. }
  286. if(self::$cache_age > 0) {
  287. $responseHeaders["Cache-Control"] = "max-age=" . self::$cache_age . ", must-revalidate";
  288. $responseHeaders["Pragma"] = "";
  289. } else {
  290. $responseHeaders["Cache-Control"] = "no-cache, max-age=0, must-revalidate";
  291. }
  292. if(self::$modification_date && self::$cache_age > 0) {
  293. $responseHeaders["Last-Modified"] =self::gmt_date(self::$modification_date);
  294. // 304 response detection
  295. if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  296. $ifModifiedSince = strtotime(stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']));
  297. if($ifModifiedSince >= self::$modification_date) {
  298. if($body) {
  299. $body->setStatusCode(304);
  300. $body->setBody('');
  301. } else {
  302. header('HTTP/1.0 304 Not Modified');
  303. die();
  304. }
  305. }
  306. }
  307. $expires = time() + self::$cache_age;
  308. $responseHeaders["Expires"] = self::gmt_date($expires);
  309. }
  310. if(self::$etag) {
  311. $responseHeaders['ETag'] = self::$etag;
  312. }
  313. // Now that we've generated them, either output them or attach them to the SS_HTTPResponse as appropriate
  314. foreach($responseHeaders as $k => $v) {
  315. if($body) $body->addHeader($k, $v);
  316. else if(!headers_sent()) header("$k: $v");
  317. }
  318. }
  319. /**
  320. * Return an {@link http://www.faqs.org/rfcs/rfc2822 RFC 2822} date in the
  321. * GMT timezone (a timestamp is always in GMT: the number of seconds
  322. * since January 1 1970 00:00:00 GMT)
  323. */
  324. static function gmt_date($timestamp) {
  325. return gmdate('D, d M Y H:i:s', $timestamp) . ' GMT';
  326. }
  327. /*
  328. * Return static variable cache_age in second
  329. */
  330. static function get_cache_age() {
  331. return self::$cache_age;
  332. }
  333. }
  334. ?>