New onsite patient portal, take 4.
[openemr.git] / portal / patient / fwk / libs / verysimple / HTTP / RequestUtil.php
blob2b4b60e4fb3d6c48cc662068b97d44571a6004db
1 <?php
2 /** @package verysimple::HTTP */
4 /**
5 * import supporting libraries
6 */
8 /**
9 * Static utility class for processing form post/request data
11 * Contains various methods for retrieving user input from forms
13 * @package verysimple::HTTP
14 * @author VerySimple Inc.
15 * @copyright 1997-2011 VerySimple, Inc. http://www.verysimple.com
16 * @license http://www.gnu.org/licenses/lgpl.html LGPL
17 * @version 1.4
19 class RequestUtil {
21 /** @var bool set to true and all non-ascii characters in request variables will be html encoded */
22 static $ENCODE_NON_ASCII = false;
24 /** @var bool set to false to skip is_uploaded_file. This allows for simulated file uploads during unit testing */
25 static $VALIDATE_FILE_UPLOAD = true;
27 /** @var body contents, only read once in case GetBody is called multiple times */
28 private static $bodyCache = '';
30 /** @var true if the request body has already been read */
31 private static $bodyCacheIsReady = false;
33 /**
35 * @var bool
36 * @deprecated use $VALIDATE_FILE_UPLOAD instead
38 static $TestMode = false;
40 /**
41 * Returns the remote host IP address, attempting to locate originating
42 * IP of the requester in the case of proxy/load balanced requests.
44 * @see http://en.wikipedia.org/wiki/X-Forwarded-For
45 * @return string
47 static function GetRemoteHost() {
48 if (array_key_exists ( 'HTTP_X_CLUSTER_CLIENT_IP', $_SERVER ))
49 return $_SERVER ['HTTP_X_CLUSTER_CLIENT_IP'];
50 if (array_key_exists ( 'HTTP_X_FORWARDED_FOR', $_SERVER ))
51 return $_SERVER ['HTTP_X_FORWARDED_FOR'];
52 if (array_key_exists ( 'X_FORWARDED_FOR', $_SERVER ))
53 return $_SERVER ['X_FORWARDED_FOR'];
54 if (array_key_exists ( 'REMOTE_ADDR', $_SERVER ))
55 return $_SERVER ['REMOTE_ADDR'];
56 return "0.0.0.0";
59 /**
60 * Returns true if the current session is running in SSL
62 static function IsSSL() {
63 return isset ( $_SERVER ['HTTPS'] ) && $_SERVER ['HTTPS'] != "" && $_SERVER ['HTTPS'] != "off";
66 /**
67 * In the case of URL re-writing, sometimes querystrings appended to a URL can get
68 * lost.
69 * This function examines the original request URI and updates $_REQUEST
70 * superglobal to ensure that it contains all of values in the qeurtystring
72 public static function NormalizeUrlRewrite() {
73 $uri = array ();
74 if (isset ( $_SERVER ["REQUEST_URI"] )) {
75 $uri = parse_url ( $_SERVER ["REQUEST_URI"] );
76 } elseif (isset ( $_SERVER ["QUERY_STRING"] )) {
77 $uri ['query'] = $_SERVER ["QUERY_STRING"];
80 if (isset ( $uri ['query'] )) {
81 $parts = explode ( "&", $uri ['query'] );
82 foreach ( $parts as $part ) {
83 $keyval = explode ( "=", $part, 2 );
84 $_REQUEST [$keyval [0]] = isset ( $keyval [1] ) ? urldecode ( $keyval [1] ) : "";
89 /**
90 * Returns the root url of the server without any subdirectories
92 * @return string URL path with trailing slash
94 public static function GetServerRootUrl() {
95 $url = self::GetCurrentURL ( false );
96 $parts = explode ( '/', $url );
97 if (count ( $parts ) < 2)
98 throw new Exception ( 'RequestUtil is unable to determine the server root' );
99 return $parts [0] . '//' . $parts [2] . '/';
103 * Returns the base url of the currently executing script.
104 * For example
105 * the script http://localhost/myapp/index.php would return http://localhost/myapp/
107 * @return string URL path with trailing slash
109 public static function GetBaseURL() {
110 $url = self::GetCurrentURL ( false );
111 $slash = strripos ( $url, "/" );
112 return substr ( $url, 0, $slash + 1 );
116 * Returns the parts of the url as deliminated by forward slashes for example /this/that/other
117 * will be returned as an array [this,that,other]
119 * @param
120 * string root folder for the app (ex. 'myapp' or 'myapp/subdir1')
121 * @return array
123 public static function GetUrlParts($appRoot = '') {
124 $urlqs = explode ( "?", self::GetCurrentURL (), 2 );
125 $url = $urlqs [0];
127 // if a root folder was provided, then we need to strip that out as well
128 if ($appRoot)
129 $url = str_replace ( $appRoot . '/', '', $url );
131 $parts = explode ( "/", $url );
132 // we only want the parts starting with #3 (after http://server/)
134 array_shift ( $parts );
135 array_shift ( $parts );
136 array_shift ( $parts );
138 // if there is no action specified then we don't want to return an array with an empty string
139 while ( count ( $parts ) && $parts [0] == '' ) {
140 array_shift ( $parts );
143 return $parts;
147 * Returns the request method (GET, POST, PUT, DELETE).
148 * This is detected based
149 * on the HTTP request method, a special URL parameter, or a request header
150 * with the name 'X-HTTP-Method-Override'
152 * For clients or servers that don't support PUT/DELETE requests, the emulated
153 * param can be used or the override header
155 * @param
156 * string name of the querystring parameter that has the overridden request method
158 public static function GetMethod($emulateHttpParamName = '_method') {
159 if (array_key_exists ( $emulateHttpParamName, $_REQUEST ))
160 return $_REQUEST [$emulateHttpParamName];
162 $headers = self::GetRequestHeaders ();
164 // this is used by backbone
165 if (array_key_exists ( 'X-HTTP-Method-Override', $headers )) {
166 return $headers ['X-HTTP-Method-Override'];
169 return array_key_exists ( 'REQUEST_METHOD', $_SERVER ) ? $_SERVER ['REQUEST_METHOD'] : '';
173 * Return all request headers using the best method available for the server environment
175 * @return array
177 public static function GetRequestHeaders() {
178 if (function_exists ( 'getallheaders' ))
179 return getallheaders ();
181 $headers = array ();
182 foreach ( $_SERVER as $k => $v ) {
183 if (substr ( $k, 0, 5 ) == "HTTP_") {
184 $k = str_replace ( '_', ' ', substr ( $k, 5 ) );
185 $k = str_replace ( ' ', '-', ucwords ( strtolower ( $k ) ) );
186 $headers [$k] = $v;
189 return $headers;
193 * Returns the body/payload of a request.
194 * this is cached so that this method
195 * may be called multiple times.
197 * Note: if this is a PUT request and the body is not returning data, then
198 * you must look in other code an libraries that may read from php://input,
199 * which can only be read one time for PUT requests
201 * @return string
203 public static function GetBody() {
204 if (! self::$bodyCacheIsReady) {
205 self::$bodyCache = @file_get_contents ( 'php://input' );
206 self::$bodyCacheIsReady = true;
209 return self::$bodyCache;
213 * Used primarily for unit testing.
214 * Set the contents of the request body
216 * @param string $contents
218 public static function SetBody($contents) {
219 self::$bodyCache = $contents;
220 self::$bodyCacheIsReady = true;
224 * Return the HTTP headers sent along with the request.
225 * This will attempt
226 * to use apache_request_headers if available in the environment, otherwise
227 * will manually build the headers using $_SERVER superglobal
229 * @return array
231 public static function GetHeaders() {
232 $headers = false;
234 if (function_exists ( 'apache_request_headers' ))
235 $headers = apache_request_headers ();
237 if ($headers === false) {
238 // apache_request_headers is not supported in this environment
240 $headers = array ();
241 foreach ( $_SERVER as $key => $value ) {
242 if (substr ( $key, 0, 5 ) != 'HTTP_') {
243 continue;
245 $header = str_replace ( ' ', '-', ucwords ( str_replace ( '_', ' ', strtolower ( substr ( $key, 5 ) ) ) ) );
246 $headers [$header] = $value;
250 return $headers;
254 * Returns the full URL of the PHP page that is currently executing
256 * @param bool $include_querystring
257 * (optional) Specify true/false to include querystring. Default is true.
258 * @param bool $append_post_vars
259 * true to append post variables to the querystring as GET parameters Default is false
260 * @return string URL
262 public static function GetCurrentURL($include_querystring = true, $append_post_vars = false) {
263 $server_protocol = isset ( $_SERVER ["SERVER_PROTOCOL"] ) ? $_SERVER ["SERVER_PROTOCOL"] : "";
264 $http_host = isset ( $_SERVER ["HTTP_HOST"] ) ? $_SERVER ["HTTP_HOST"] : "";
265 $server_port = isset ( $_SERVER ["SERVER_PORT"] ) ? $_SERVER ["SERVER_PORT"] : "";
267 $protocol = substr ( $server_protocol, 0, strpos ( $server_protocol, "/" ) ) . (isset ( $_SERVER ["HTTPS"] ) && $_SERVER ["HTTPS"] == "on" ? "S" : "");
268 $port = "";
270 $domainport = explode ( ":", $http_host );
271 $domain = $domainport [0];
273 $port = (isset ( $domainport [1] )) ? $domainport [1] : $server_port;
275 // ports 80 and 443 are generally not included in the url
276 $port = ($port == "" || $port == "80" || $port == "443") ? "" : (":" . $port);
278 if (isset ( $_SERVER ['REQUEST_URI'] )) {
279 // REQUEST_URI is more accurate but isn't always defined on windows
280 // in particular for the format http://www.domain.com/?var=val
281 $pq = explode ( "?", $_SERVER ['REQUEST_URI'], 2 );
282 $path = $pq [0];
283 $qs = isset ( $pq [1] ) ? "?" . $pq [1] : "";
284 } else {
285 // otherwise use SCRIPT_NAME & QUERY_STRING
286 $path = isset ( $_SERVER ['SCRIPT_NAME'] ) ? $_SERVER ['SCRIPT_NAME'] : "";
287 $qs = isset ( $_SERVER ['QUERY_STRING'] ) ? "?" . $_SERVER ['QUERY_STRING'] : "";
290 // if we also want the post variables appended we can get them as a querystring from php://input
291 if ($append_post_vars && isset ( $_POST )) {
292 $post = self::GetBody ();
293 $qs .= $qs ? "&$post" : "?$post";
296 $url = strtolower ( $protocol ) . "://" . $domain . $port . $path . ($include_querystring ? $qs : "");
298 return $url;
302 * Returns a form upload as a FileUpload object.
303 * This function throws an exeption on fail
304 * with details, so it is recommended to use try/catch when calling this function
306 * @param string $fieldname
307 * name of the html form field
308 * @param bool $b64encode
309 * true to base64encode file data (default false)
310 * @param bool $ignore_empty
311 * true to not throw exception if form fields doesn't contain a file (default false)
312 * @param int $max_kb
313 * maximum size allowed for upload (default unlimited)
314 * @param array $ok_types
315 * if array is provided, only files with those Extensions will be allowed (default all)
316 * @return FileUpload object (or null if $ignore_empty = true and there is no file data)
318 public static function GetFileUpload($fieldname, $ignore_empty = false, $max_kb = 0, $ok_types = null) {
319 // make sure there is actually a file upload
320 if (! isset ( $_FILES [$fieldname] )) {
321 // this means the form field wasn't present which is generally an error
322 // however if ignore is specified, then return empty string
323 if ($ignore_empty) {
324 return null;
326 throw new Exception ( "\$_FILES['" . $fieldname . "'] is empty. Did you forget to add enctype='multipart/form-data' to your form code?" );
329 // make sure a file was actually uploaded, otherwise return null
330 if ($_FILES [$fieldname] ['error'] == 4) {
331 return;
334 // get the upload ref
335 $upload = $_FILES [$fieldname];
337 // make sure there were no errors during upload, but ignore case where
338 if ($upload ['error']) {
339 $error_codes [0] = "The file uploaded with success.";
340 $error_codes [1] = "The uploaded file exceeds the upload_max_filesize directive in php.ini.";
341 $error_codes [2] = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the html form.";
342 $error_codes [3] = "The uploaded file was only partially uploaded.";
343 $error_codes [4] = "No file was uploaded.";
344 throw new Exception ( "Error uploading file: " . $error_codes [$upload ['error']] );
347 // backwards compatibility
348 if (self::$TestMode)
349 self::$VALIDATE_FILE_UPLOAD = false;
351 // make sure this is a legit file request
352 if (self::$VALIDATE_FILE_UPLOAD && is_uploaded_file ( $upload ['tmp_name'] ) == false) {
353 throw new Exception ( "Unable to access this upload: " . $fieldname );
356 // get the filename and Extension
357 $tmp_path = $upload ['tmp_name'];
358 $info = pathinfo ( $upload ['name'] );
360 require_once ("FileUpload.php");
361 $fupload = new FileUpload ();
362 $fupload->Name = $info ['basename'];
363 $fupload->Size = $upload ['size'];
364 $fupload->Type = $upload ['type'];
365 $fupload->Extension = strtolower ( $info ['extension'] );
367 if ($ok_types && ! in_array ( $fupload->Extension, $ok_types )) {
368 throw new Exception ( "The file '" . htmlentities ( $fupload->Name ) . "' is not a type that is allowed. Allowed file types are: " . (implode ( ", ", $ok_types )) . "." );
371 if ($max_kb && ($fupload->Size / 1024) > $max_kb) {
372 throw new Exception ( "The file '" . htmlentities ( $fupload->Name ) . "' is to large. Maximum allowed size is " . number_format ( $max_kb / 1024, 2 ) . "Mb" );
375 // open the file and read the entire contents
376 $fh = fopen ( $tmp_path, "r" );
377 $fupload->Data = fread ( $fh, filesize ( $tmp_path ) );
378 fclose ( $fh );
380 return $fupload;
384 * Returns a form upload as an xml document with the file data base64 encoded.
385 * suitable for storing in a clob or blob
387 * @param string $fieldname
388 * name of the html form field
389 * @param bool $b64encode
390 * true to base64encode file data (default true)
391 * @param bool $ignore_empty
392 * true to not throw exception if form fields doesn't contain a file (default false)
393 * @param int $max_kb
394 * maximum size allowed for upload (default unlimited)
395 * @param array $ok_types
396 * if array is provided, only files with those Extensions will be allowed (default all)
397 * @return string or null
399 public static function GetFile($fieldname, $b64encode = true, $ignore_empty = false, $max_kb = 0, $ok_types = null) {
400 $fupload = self::GetFileUpload ( $fieldname, $ignore_empty, $max_kb, $ok_types );
401 return ($fupload) ? $fupload->ToXML ( $b64encode ) : null;
405 * Sets a value as if it was sent from the browser - primarily used for unit testing
407 * @param string $key
408 * @param variant $val
410 public static function Set($key, $val) {
411 $_REQUEST [$key] = $val;
415 * Clears all browser input - primarily used for unit testing
417 public static function ClearAll() {
418 $_REQUEST = array ();
419 $_FILES = array ();
421 self::$bodyCache = "";
422 self::$bodyCacheIsReady = false;
426 * Returns a form parameter as a string, handles null values.
427 * Note that if
428 * $ENCODE_NON_ASCII = true then the value will be passed through VerySimpleStringUtil::EncodeToHTML
429 * before being returned.
431 * If the form field is a multi-value type (checkbox, etc) then an array may be returned
433 * @param string $fieldname
434 * @param string $default
435 * value returned if $_REQUEST[$fieldname] is blank or null (default = empty string)
436 * @param bool $escape
437 * if true htmlspecialchars($val) is returned (default = false)
438 * @param bool $ignorecase
439 * if true then request fieldname will not be case sensitive (default = false)
440 * @return string | array
442 public static function Get($fieldname, $default = "", $escape = false, $ignorecase = false) {
443 $val = null;
445 if ($ignorecase) {
446 $_REQUEST_LOWER = array_change_key_case ( $_REQUEST, CASE_LOWER );
447 $val = (isset ( $_REQUEST_LOWER [strtolower ( $fieldname )] ) && $_REQUEST_LOWER [strtolower ( $fieldname )] != "") ? $_REQUEST_LOWER [strtolower ( $fieldname )] : $default;
448 } else {
449 $val = (isset ( $_REQUEST [$fieldname] ) && $_REQUEST [$fieldname] != "") ? $_REQUEST [$fieldname] : $default;
452 if ($escape) {
453 $val = htmlspecialchars ( $val, ENT_COMPAT, null, false );
456 if (self::$ENCODE_NON_ASCII) {
457 require_once ("verysimple/String/VerySimpleStringUtil.php");
459 if (is_array ( $val )) {
460 foreach ( $val as $k => $v ) {
461 $val [$k] = VerySimpleStringUtil::EncodeToHTML ( $v );
463 } else {
464 $val = VerySimpleStringUtil::EncodeToHTML ( $val );
468 return $val;
472 * Returns true if the given form field has non-ascii characters
474 * @param string $fieldname
475 * @return bool
477 public static function HasNonAsciiChars($fieldname) {
478 require_once ("verysimple/String/VerySimpleStringUtil.php");
480 $val = isset ( $_REQUEST [$fieldname] ) ? $_REQUEST [$fieldname] : '';
481 return VerySimpleStringUtil::EncodeToHTML ( $val ) != $val;
485 * Returns a form parameter and persists it in the session.
486 * If the form parameter was not passed
487 * again, then it returns the session value. if the session value doesn't exist, then it returns
488 * the default setting
490 * @param string $fieldname
491 * @param string $default
492 * @return string
494 public static function GetPersisted($fieldname, $default = "", $escape = false) {
495 if (isset ( $_REQUEST [$fieldname] )) {
496 $_SESSION ["_PERSISTED_" . $fieldname] = self::Get ( $fieldname, $default, $escape );
499 if (! isset ( $_SESSION ["_PERSISTED_" . $fieldname] )) {
500 $_SESSION ["_PERSISTED_" . $fieldname] = $default;
503 return $_SESSION ["_PERSISTED_" . $fieldname];
507 * Returns a form parameter as a date formatted for mysql YYYY-MM-DD,
508 * expects some type of date format.
509 * if default value is not provided,
510 * will return today. if default value is empty string "" will return
511 * empty string.
513 * @param string $fieldname
514 * @param string $default
515 * default value = today
516 * @param bool $includetime
517 * whether to include the time in addition to date
518 * @return string
520 public static function GetAsDate($fieldname, $default = "date('Y-m-d')", $includetime = false) {
521 $returnVal = self::Get ( $fieldname, $default );
523 if ($returnVal == "date('Y-m-d')") {
524 return date ( 'Y-m-d' );
525 } elseif ($returnVal == "date('Y-m-d H:i:s')") {
526 return date ( 'Y-m-d H:i:s' );
527 } elseif ($returnVal == "") {
528 return "";
529 } else {
530 if ($includetime) {
531 if (self::Get ( $fieldname . "Hour" )) {
532 $hour = self::Get ( $fieldname . "Hour", date ( "H" ) );
533 $minute = self::Get ( $fieldname . "Minute", date ( "i" ) );
534 $ampm = self::Get ( $fieldname . "AMPM", "AM" );
536 if ($ampm == "PM") {
537 $hour = ($hour * 1) + 12;
539 $returnVal .= " " . $hour . ":" . $minute . ":" . "00";
542 return date ( "Y-m-d H:i:s", strtotime ( $returnVal ) );
543 } else {
544 return date ( "Y-m-d", strtotime ( $returnVal ) );
550 * Returns a form parameter as a date formatted for mysql YYYY-MM-DD HH:MM:SS,
551 * expects some type of date format.
552 * if default value is not provided,
553 * will return now. if default value is empty string "" will return
554 * empty string.
556 * @param string $fieldname
557 * @param string $default
558 * default value = today
559 * @return string
561 public static function GetAsDateTime($fieldname, $default = "date('Y-m-d H:i:s')") {
562 return self::GetAsDate ( $fieldname, $default, true );
566 * Returns a form parameter minus currency symbols
568 * @param string $fieldname
569 * @return string
571 public static function GetCurrency($fieldname) {
572 return str_replace ( array (
573 ',',
574 '$'
575 ), '', self::Get ( $fieldname ) );