1<?php 2/** 3 * CodeIgniter 4 * 5 * An open source application development framework for PHP 6 * 7 * This content is released under the MIT License (MIT) 8 * 9 * Copyright (c) 2014 - 2019, British Columbia Institute of Technology 10 * 11 * Permission is hereby granted, free of charge, to any person obtaining a copy 12 * of this software and associated documentation files (the "Software"), to deal 13 * in the Software without restriction, including without limitation the rights 14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 * copies of the Software, and to permit persons to whom the Software is 16 * furnished to do so, subject to the following conditions: 17 * 18 * The above copyright notice and this permission notice shall be included in 19 * all copies or substantial portions of the Software. 20 * 21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 * THE SOFTWARE. 28 * 29 * @package CodeIgniter 30 * @author EllisLab Dev Team 31 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/) 32 * @copyright Copyright (c) 2014 - 2019, British Columbia Institute of Technology (https://bcit.ca/) 33 * @license https://opensource.org/licenses/MIT MIT License 34 * @link https://codeigniter.com 35 * @since Version 1.0.0 36 * @filesource 37 */ 38defined('BASEPATH') OR exit('No direct script access allowed'); 39 40/** 41 * Input Class 42 * 43 * Pre-processes global input data for security 44 * 45 * @package CodeIgniter 46 * @subpackage Libraries 47 * @category Input 48 * @author EllisLab Dev Team 49 * @link https://codeigniter.com/user_guide/libraries/input.html 50 */ 51class CI_Input { 52 53 /** 54 * IP address of the current user 55 * 56 * @var string 57 */ 58 protected $ip_address = FALSE; 59 60 /** 61 * Allow GET array flag 62 * 63 * If set to FALSE, then $_GET will be set to an empty array. 64 * 65 * @var bool 66 */ 67 protected $_allow_get_array = TRUE; 68 69 /** 70 * Standardize new lines flag 71 * 72 * If set to TRUE, then newlines are standardized. 73 * 74 * @var bool 75 */ 76 protected $_standardize_newlines; 77 78 /** 79 * Enable XSS flag 80 * 81 * Determines whether the XSS filter is always active when 82 * GET, POST or COOKIE data is encountered. 83 * Set automatically based on config setting. 84 * 85 * @var bool 86 */ 87 protected $_enable_xss = FALSE; 88 89 /** 90 * Enable CSRF flag 91 * 92 * Enables a CSRF cookie token to be set. 93 * Set automatically based on config setting. 94 * 95 * @var bool 96 */ 97 protected $_enable_csrf = FALSE; 98 99 /** 100 * List of all HTTP request headers 101 * 102 * @var array 103 */ 104 protected $headers = array(); 105 106 /** 107 * Raw input stream data 108 * 109 * Holds a cache of php://input contents 110 * 111 * @var string 112 */ 113 protected $_raw_input_stream; 114 115 /** 116 * Parsed input stream data 117 * 118 * Parsed from php://input at runtime 119 * 120 * @see CI_Input::input_stream() 121 * @var array 122 */ 123 protected $_input_stream; 124 125 protected $security; 126 protected $uni; 127 128 // -------------------------------------------------------------------- 129 130 /** 131 * Class constructor 132 * 133 * Determines whether to globally enable the XSS processing 134 * and whether to allow the $_GET array. 135 * 136 * @return void 137 */ 138 public function __construct() 139 { 140 $this->_allow_get_array = (config_item('allow_get_array') !== FALSE); 141 $this->_enable_xss = (config_item('global_xss_filtering') === TRUE); 142 $this->_enable_csrf = (config_item('csrf_protection') === TRUE); 143 $this->_standardize_newlines = (bool) config_item('standardize_newlines'); 144 145 $this->security =& load_class('Security', 'core'); 146 147 // Do we need the UTF-8 class? 148 if (UTF8_ENABLED === TRUE) 149 { 150 $this->uni =& load_class('Utf8', 'core'); 151 } 152 153 // Sanitize global arrays 154 $this->_sanitize_globals(); 155 156 // CSRF Protection check 157 if ($this->_enable_csrf === TRUE && ! is_cli()) 158 { 159 $this->security->csrf_verify(); 160 } 161 162 log_message('info', 'Input Class Initialized'); 163 } 164 165 // -------------------------------------------------------------------- 166 167 /** 168 * Fetch from array 169 * 170 * Internal method used to retrieve values from global arrays. 171 * 172 * @param array &$array $_GET, $_POST, $_COOKIE, $_SERVER, etc. 173 * @param mixed $index Index for item to be fetched from $array 174 * @param bool $xss_clean Whether to apply XSS filtering 175 * @return mixed 176 */ 177 protected function _fetch_from_array(&$array, $index = NULL, $xss_clean = NULL) 178 { 179 is_bool($xss_clean) OR $xss_clean = $this->_enable_xss; 180 181 // If $index is NULL, it means that the whole $array is requested 182 isset($index) OR $index = array_keys($array); 183 184 // allow fetching multiple keys at once 185 if (is_array($index)) 186 { 187 $output = array(); 188 foreach ($index as $key) 189 { 190 $output[$key] = $this->_fetch_from_array($array, $key, $xss_clean); 191 } 192 193 return $output; 194 } 195 196 if (isset($array[$index])) 197 { 198 $value = $array[$index]; 199 } 200 elseif (($count = preg_match_all('/(?:^[^\[]+)|\[[^]]*\]/', $index, $matches)) > 1) // Does the index contain array notation 201 { 202 $value = $array; 203 for ($i = 0; $i < $count; $i++) 204 { 205 $key = trim($matches[0][$i], '[]'); 206 if ($key === '') // Empty notation will return the value as array 207 { 208 break; 209 } 210 211 if (isset($value[$key])) 212 { 213 $value = $value[$key]; 214 } 215 else 216 { 217 return NULL; 218 } 219 } 220 } 221 else 222 { 223 return NULL; 224 } 225 226 return ($xss_clean === TRUE) 227 ? $this->security->xss_clean($value) 228 : $value; 229 } 230 231 // -------------------------------------------------------------------- 232 233 /** 234 * Fetch an item from the GET array 235 * 236 * @param mixed $index Index for item to be fetched from $_GET 237 * @param bool $xss_clean Whether to apply XSS filtering 238 * @return mixed 239 */ 240 public function get($index = NULL, $xss_clean = NULL) 241 { 242 return $this->_fetch_from_array($_GET, $index, $xss_clean); 243 } 244 245 // -------------------------------------------------------------------- 246 247 /** 248 * Fetch an item from the POST array 249 * 250 * @param mixed $index Index for item to be fetched from $_POST 251 * @param bool $xss_clean Whether to apply XSS filtering 252 * @return mixed 253 */ 254 public function post($index = NULL, $xss_clean = NULL) 255 { 256 return $this->_fetch_from_array($_POST, $index, $xss_clean); 257 } 258 259 // -------------------------------------------------------------------- 260 261 /** 262 * Fetch an item from POST data with fallback to GET 263 * 264 * @param string $index Index for item to be fetched from $_POST or $_GET 265 * @param bool $xss_clean Whether to apply XSS filtering 266 * @return mixed 267 */ 268 public function post_get($index, $xss_clean = NULL) 269 { 270 return isset($_POST[$index]) 271 ? $this->post($index, $xss_clean) 272 : $this->get($index, $xss_clean); 273 } 274 275 // -------------------------------------------------------------------- 276 277 /** 278 * Fetch an item from GET data with fallback to POST 279 * 280 * @param string $index Index for item to be fetched from $_GET or $_POST 281 * @param bool $xss_clean Whether to apply XSS filtering 282 * @return mixed 283 */ 284 public function get_post($index, $xss_clean = NULL) 285 { 286 return isset($_GET[$index]) 287 ? $this->get($index, $xss_clean) 288 : $this->post($index, $xss_clean); 289 } 290 291 // -------------------------------------------------------------------- 292 293 /** 294 * Fetch an item from the COOKIE array 295 * 296 * @param mixed $index Index for item to be fetched from $_COOKIE 297 * @param bool $xss_clean Whether to apply XSS filtering 298 * @return mixed 299 */ 300 public function cookie($index = NULL, $xss_clean = NULL) 301 { 302 return $this->_fetch_from_array($_COOKIE, $index, $xss_clean); 303 } 304 305 // -------------------------------------------------------------------- 306 307 /** 308 * Fetch an item from the SERVER array 309 * 310 * @param mixed $index Index for item to be fetched from $_SERVER 311 * @param bool $xss_clean Whether to apply XSS filtering 312 * @return mixed 313 */ 314 public function server($index, $xss_clean = NULL) 315 { 316 return $this->_fetch_from_array($_SERVER, $index, $xss_clean); 317 } 318 319 // ------------------------------------------------------------------------ 320 321 /** 322 * Fetch an item from the php://input stream 323 * 324 * Useful when you need to access PUT, DELETE or PATCH request data. 325 * 326 * @param string $index Index for item to be fetched 327 * @param bool $xss_clean Whether to apply XSS filtering 328 * @return mixed 329 */ 330 public function input_stream($index = NULL, $xss_clean = NULL) 331 { 332 // Prior to PHP 5.6, the input stream can only be read once, 333 // so we'll need to check if we have already done that first. 334 if ( ! is_array($this->_input_stream)) 335 { 336 // $this->raw_input_stream will trigger __get(). 337 parse_str($this->raw_input_stream, $this->_input_stream); 338 is_array($this->_input_stream) OR $this->_input_stream = array(); 339 } 340 341 return $this->_fetch_from_array($this->_input_stream, $index, $xss_clean); 342 } 343 344 // ------------------------------------------------------------------------ 345 346 /** 347 * Set cookie 348 * 349 * Accepts an arbitrary number of parameters (up to 7) or an associative 350 * array in the first parameter containing all the values. 351 * 352 * @param string|mixed[] $name Cookie name or an array containing parameters 353 * @param string $value Cookie value 354 * @param int $expire Cookie expiration time in seconds 355 * @param string $domain Cookie domain (e.g.: '.yourdomain.com') 356 * @param string $path Cookie path (default: '/') 357 * @param string $prefix Cookie name prefix 358 * @param bool $secure Whether to only transfer cookies via SSL 359 * @param bool $httponly Whether to only makes the cookie accessible via HTTP (no javascript) 360 * @return void 361 */ 362 public function set_cookie($name, $value = '', $expire = '', $domain = '', $path = '/', $prefix = '', $secure = NULL, $httponly = NULL) 363 { 364 if (is_array($name)) 365 { 366 // always leave 'name' in last place, as the loop will break otherwise, due to $$item 367 foreach (array('value', 'expire', 'domain', 'path', 'prefix', 'secure', 'httponly', 'name') as $item) 368 { 369 if (isset($name[$item])) 370 { 371 $$item = $name[$item]; 372 } 373 } 374 } 375 376 if ($prefix === '' && config_item('cookie_prefix') !== '') 377 { 378 $prefix = config_item('cookie_prefix'); 379 } 380 381 if ($domain == '' && config_item('cookie_domain') != '') 382 { 383 $domain = config_item('cookie_domain'); 384 } 385 386 if ($path === '/' && config_item('cookie_path') !== '/') 387 { 388 $path = config_item('cookie_path'); 389 } 390 391 $secure = ($secure === NULL && config_item('cookie_secure') !== NULL) 392 ? (bool) config_item('cookie_secure') 393 : (bool) $secure; 394 395 $httponly = ($httponly === NULL && config_item('cookie_httponly') !== NULL) 396 ? (bool) config_item('cookie_httponly') 397 : (bool) $httponly; 398 399 if ( ! is_numeric($expire)) 400 { 401 $expire = time() - 86500; 402 } 403 else 404 { 405 $expire = ($expire > 0) ? time() + $expire : 0; 406 } 407 408 setcookie($prefix.$name, $value, $expire, $path, $domain, $secure, $httponly); 409 } 410 411 // -------------------------------------------------------------------- 412 413 /** 414 * Fetch the IP Address 415 * 416 * Determines and validates the visitor's IP address. 417 * 418 * @return string IP address 419 */ 420 public function ip_address() 421 { 422 if ($this->ip_address !== FALSE) 423 { 424 return $this->ip_address; 425 } 426 427 $proxy_ips = config_item('proxy_ips'); 428 if ( ! empty($proxy_ips) && ! is_array($proxy_ips)) 429 { 430 $proxy_ips = explode(',', str_replace(' ', '', $proxy_ips)); 431 } 432 433 $this->ip_address = $this->server('REMOTE_ADDR'); 434 435 if ($proxy_ips) 436 { 437 foreach (array('HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP') as $header) 438 { 439 if (($spoof = $this->server($header)) !== NULL) 440 { 441 // Some proxies typically list the whole chain of IP 442 // addresses through which the client has reached us. 443 // e.g. client_ip, proxy_ip1, proxy_ip2, etc. 444 sscanf($spoof, '%[^,]', $spoof); 445 446 if ( ! $this->valid_ip($spoof)) 447 { 448 $spoof = NULL; 449 } 450 else 451 { 452 break; 453 } 454 } 455 } 456 457 if ($spoof) 458 { 459 for ($i = 0, $c = count($proxy_ips); $i < $c; $i++) 460 { 461 // Check if we have an IP address or a subnet 462 if (strpos($proxy_ips[$i], '/') === FALSE) 463 { 464 // An IP address (and not a subnet) is specified. 465 // We can compare right away. 466 if ($proxy_ips[$i] === $this->ip_address) 467 { 468 $this->ip_address = $spoof; 469 break; 470 } 471 472 continue; 473 } 474 475 // We have a subnet ... now the heavy lifting begins 476 isset($separator) OR $separator = $this->valid_ip($this->ip_address, 'ipv6') ? ':' : '.'; 477 478 // If the proxy entry doesn't match the IP protocol - skip it 479 if (strpos($proxy_ips[$i], $separator) === FALSE) 480 { 481 continue; 482 } 483 484 // Convert the REMOTE_ADDR IP address to binary, if needed 485 if ( ! isset($ip, $sprintf)) 486 { 487 if ($separator === ':') 488 { 489 // Make sure we're have the "full" IPv6 format 490 $ip = explode(':', 491 str_replace('::', 492 str_repeat(':', 9 - substr_count($this->ip_address, ':')), 493 $this->ip_address 494 ) 495 ); 496 497 for ($j = 0; $j < 8; $j++) 498 { 499 $ip[$j] = intval($ip[$j], 16); 500 } 501 502 $sprintf = '%016b%016b%016b%016b%016b%016b%016b%016b'; 503 } 504 else 505 { 506 $ip = explode('.', $this->ip_address); 507 $sprintf = '%08b%08b%08b%08b'; 508 } 509 510 $ip = vsprintf($sprintf, $ip); 511 } 512 513 // Split the netmask length off the network address 514 sscanf($proxy_ips[$i], '%[^/]/%d', $netaddr, $masklen); 515 516 // Again, an IPv6 address is most likely in a compressed form 517 if ($separator === ':') 518 { 519 $netaddr = explode(':', str_replace('::', str_repeat(':', 9 - substr_count($netaddr, ':')), $netaddr)); 520 for ($j = 0; $j < 8; $j++) 521 { 522 $netaddr[$j] = intval($netaddr[$j], 16); 523 } 524 } 525 else 526 { 527 $netaddr = explode('.', $netaddr); 528 } 529 530 // Convert to binary and finally compare 531 if (strncmp($ip, vsprintf($sprintf, $netaddr), $masklen) === 0) 532 { 533 $this->ip_address = $spoof; 534 break; 535 } 536 } 537 } 538 } 539 540 if ( ! $this->valid_ip($this->ip_address)) 541 { 542 return $this->ip_address = '0.0.0.0'; 543 } 544 545 return $this->ip_address; 546 } 547 548 // -------------------------------------------------------------------- 549 550 /** 551 * Validate IP Address 552 * 553 * @param string $ip IP address 554 * @param string $which IP protocol: 'ipv4' or 'ipv6' 555 * @return bool 556 */ 557 public function valid_ip($ip, $which = '') 558 { 559 switch (strtolower($which)) 560 { 561 case 'ipv4': 562 $which = FILTER_FLAG_IPV4; 563 break; 564 case 'ipv6': 565 $which = FILTER_FLAG_IPV6; 566 break; 567 default: 568 $which = NULL; 569 break; 570 } 571 572 return (bool) filter_var($ip, FILTER_VALIDATE_IP, $which); 573 } 574 575 // -------------------------------------------------------------------- 576 577 /** 578 * Fetch User Agent string 579 * 580 * @return string|null User Agent string or NULL if it doesn't exist 581 */ 582 public function user_agent($xss_clean = NULL) 583 { 584 return $this->_fetch_from_array($_SERVER, 'HTTP_USER_AGENT', $xss_clean); 585 } 586 587 // -------------------------------------------------------------------- 588 589 /** 590 * Sanitize Globals 591 * 592 * Internal method serving for the following purposes: 593 * 594 * - Unsets $_GET data, if query strings are not enabled 595 * - Cleans POST, COOKIE and SERVER data 596 * - Standardizes newline characters to PHP_EOL 597 * 598 * @return void 599 */ 600 protected function _sanitize_globals() 601 { 602 // Is $_GET data allowed? If not we'll set the $_GET to an empty array 603 if ($this->_allow_get_array === FALSE) 604 { 605 $_GET = array(); 606 } 607 elseif (is_array($_GET)) 608 { 609 foreach ($_GET as $key => $val) 610 { 611 $_GET[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); 612 } 613 } 614 615 // Clean $_POST Data 616 if (is_array($_POST)) 617 { 618 foreach ($_POST as $key => $val) 619 { 620 $_POST[$this->_clean_input_keys($key)] = $this->_clean_input_data($val); 621 } 622 } 623 624 // Clean $_COOKIE Data 625 if (is_array($_COOKIE)) 626 { 627 // Also get rid of specially treated cookies that might be set by a server 628 // or silly application, that are of no use to a CI application anyway 629 // but that when present will trip our 'Disallowed Key Characters' alarm 630 // http://www.ietf.org/rfc/rfc2109.txt 631 // note that the key names below are single quoted strings, and are not PHP variables 632 unset( 633 $_COOKIE['$Version'], 634 $_COOKIE['$Path'], 635 $_COOKIE['$Domain'] 636 ); 637 638 foreach ($_COOKIE as $key => $val) 639 { 640 if (($cookie_key = $this->_clean_input_keys($key)) !== FALSE) 641 { 642 $_COOKIE[$cookie_key] = $this->_clean_input_data($val); 643 } 644 else 645 { 646 unset($_COOKIE[$key]); 647 } 648 } 649 } 650 651 // Sanitize PHP_SELF 652 $_SERVER['PHP_SELF'] = strip_tags($_SERVER['PHP_SELF']); 653 654 log_message('debug', 'Global POST, GET and COOKIE data sanitized'); 655 } 656 657 // -------------------------------------------------------------------- 658 659 /** 660 * Clean Input Data 661 * 662 * Internal method that aids in escaping data and 663 * standardizing newline characters to PHP_EOL. 664 * 665 * @param string|string[] $str Input string(s) 666 * @return string 667 */ 668 protected function _clean_input_data($str) 669 { 670 if (is_array($str)) 671 { 672 $new_array = array(); 673 foreach (array_keys($str) as $key) 674 { 675 $new_array[$this->_clean_input_keys($key)] = $this->_clean_input_data($str[$key]); 676 } 677 return $new_array; 678 } 679 680 /* We strip slashes if magic quotes is on to keep things consistent 681 682 NOTE: In PHP 5.4 get_magic_quotes_gpc() will always return 0 and 683 it will probably not exist in future versions at all. 684 */ 685 if ( ! is_php('5.4') && get_magic_quotes_gpc()) 686 { 687 $str = stripslashes($str); 688 } 689 690 // Clean UTF-8 if supported 691 if (UTF8_ENABLED === TRUE) 692 { 693 $str = $this->uni->clean_string($str); 694 } 695 696 // Remove control characters 697 $str = remove_invisible_characters($str, FALSE); 698 699 // Standardize newlines if needed 700 if ($this->_standardize_newlines === TRUE) 701 { 702 return preg_replace('/(?:\r\n|[\r\n])/', PHP_EOL, $str); 703 } 704 705 return $str; 706 } 707 708 // -------------------------------------------------------------------- 709 710 /** 711 * Clean Keys 712 * 713 * Internal method that helps to prevent malicious users 714 * from trying to exploit keys we make sure that keys are 715 * only named with alpha-numeric text and a few other items. 716 * 717 * @param string $str Input string 718 * @param bool $fatal Whether to terminate script exection 719 * or to return FALSE if an invalid 720 * key is encountered 721 * @return string|bool 722 */ 723 protected function _clean_input_keys($str, $fatal = TRUE) 724 { 725 if ( ! preg_match('/^[a-z0-9:_\/|-]+$/i', $str)) 726 { 727 if ($fatal === TRUE) 728 { 729 return FALSE; 730 } 731 else 732 { 733 set_status_header(503); 734 echo 'Disallowed Key Characters.'; 735 exit(7); // EXIT_USER_INPUT 736 } 737 } 738 739 // Clean UTF-8 if supported 740 if (UTF8_ENABLED === TRUE) 741 { 742 return $this->uni->clean_string($str); 743 } 744 745 return $str; 746 } 747 748 // -------------------------------------------------------------------- 749 750 /** 751 * Request Headers 752 * 753 * @param bool $xss_clean Whether to apply XSS filtering 754 * @return array 755 */ 756 public function request_headers($xss_clean = FALSE) 757 { 758 // If header is already defined, return it immediately 759 if ( ! empty($this->headers)) 760 { 761 return $this->_fetch_from_array($this->headers, NULL, $xss_clean); 762 } 763 764 // In Apache, you can simply call apache_request_headers() 765 if (function_exists('apache_request_headers')) 766 { 767 $this->headers = apache_request_headers(); 768 } 769 else 770 { 771 isset($_SERVER['CONTENT_TYPE']) && $this->headers['Content-Type'] = $_SERVER['CONTENT_TYPE']; 772 773 foreach ($_SERVER as $key => $val) 774 { 775 if (sscanf($key, 'HTTP_%s', $header) === 1) 776 { 777 // take SOME_HEADER and turn it into Some-Header 778 $header = str_replace('_', ' ', strtolower($header)); 779 $header = str_replace(' ', '-', ucwords($header)); 780 781 $this->headers[$header] = $_SERVER[$key]; 782 } 783 } 784 } 785 786 return $this->_fetch_from_array($this->headers, NULL, $xss_clean); 787 } 788 789 // -------------------------------------------------------------------- 790 791 /** 792 * Get Request Header 793 * 794 * Returns the value of a single member of the headers class member 795 * 796 * @param string $index Header name 797 * @param bool $xss_clean Whether to apply XSS filtering 798 * @return string|null The requested header on success or NULL on failure 799 */ 800 public function get_request_header($index, $xss_clean = FALSE) 801 { 802 static $headers; 803 804 if ( ! isset($headers)) 805 { 806 empty($this->headers) && $this->request_headers(); 807 foreach ($this->headers as $key => $value) 808 { 809 $headers[strtolower($key)] = $value; 810 } 811 } 812 813 $index = strtolower($index); 814 815 if ( ! isset($headers[$index])) 816 { 817 return NULL; 818 } 819 820 return ($xss_clean === TRUE) 821 ? $this->security->xss_clean($headers[$index]) 822 : $headers[$index]; 823 } 824 825 // -------------------------------------------------------------------- 826 827 /** 828 * Is AJAX request? 829 * 830 * Test to see if a request contains the HTTP_X_REQUESTED_WITH header. 831 * 832 * @return bool 833 */ 834 public function is_ajax_request() 835 { 836 return ( ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest'); 837 } 838 839 // -------------------------------------------------------------------- 840 841 /** 842 * Is CLI request? 843 * 844 * Test to see if a request was made from the command line. 845 * 846 * @deprecated 3.0.0 Use is_cli() instead 847 * @return bool 848 */ 849 public function is_cli_request() 850 { 851 return is_cli(); 852 } 853 854 // -------------------------------------------------------------------- 855 856 /** 857 * Get Request Method 858 * 859 * Return the request method 860 * 861 * @param bool $upper Whether to return in upper or lower case 862 * (default: FALSE) 863 * @return string 864 */ 865 public function method($upper = FALSE) 866 { 867 return ($upper) 868 ? strtoupper($this->server('REQUEST_METHOD')) 869 : strtolower($this->server('REQUEST_METHOD')); 870 } 871 872 // ------------------------------------------------------------------------ 873 874 /** 875 * Magic __get() 876 * 877 * Allows read access to protected properties 878 * 879 * @param string $name 880 * @return mixed 881 */ 882 public function __get($name) 883 { 884 if ($name === 'raw_input_stream') 885 { 886 isset($this->_raw_input_stream) OR $this->_raw_input_stream = file_get_contents('php://input'); 887 return $this->_raw_input_stream; 888 } 889 elseif ($name === 'ip_address') 890 { 891 return $this->ip_address; 892 } 893 } 894 895} 896