[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/application/src/ -> AbstractWebApplication.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Application Package
   4   *
   5   * @copyright  Copyright (C) 2005 - 2021 Open Source Matters, Inc. All rights reserved.
   6   * @license    GNU General Public License version 2 or later; see LICENSE
   7   */
   8  
   9  namespace Joomla\Application;
  10  
  11  use Joomla\Application\Exception\UnableToWriteBody;
  12  use Joomla\Input\Input;
  13  use Joomla\Registry\Registry;
  14  use Joomla\Uri\Uri;
  15  use Laminas\Diactoros\Response;
  16  use Laminas\Diactoros\Stream;
  17  use Psr\Http\Message\ResponseInterface;
  18  
  19  /**
  20   * Base class for a Joomla! Web application.
  21   *
  22   * @since  1.0
  23   *
  24   * @property-read  Input  $input  The application input object
  25   */
  26  abstract class AbstractWebApplication extends AbstractApplication implements WebApplicationInterface
  27  {
  28      /**
  29       * The application input object.
  30       *
  31       * @var    Input
  32       * @since  1.0
  33       */
  34      protected $input;
  35  
  36      /**
  37       * Character encoding string.
  38       *
  39       * @var    string
  40       * @since  1.0
  41       */
  42      public $charSet = 'utf-8';
  43  
  44      /**
  45       * Response mime type.
  46       *
  47       * @var    string
  48       * @since  1.0
  49       */
  50      public $mimeType = 'text/html';
  51  
  52      /**
  53       * HTTP protocol version.
  54       *
  55       * @var    string
  56       * @since  1.9.0
  57       */
  58      public $httpVersion = '1.1';
  59  
  60      /**
  61       * The body modified date for response headers.
  62       *
  63       * @var    \DateTime
  64       * @since  1.0
  65       */
  66      public $modifiedDate;
  67  
  68      /**
  69       * The application client object.
  70       *
  71       * @var    Web\WebClient
  72       * @since  1.0
  73       */
  74      public $client;
  75  
  76      /**
  77       * The application response object.
  78       *
  79       * @var    ResponseInterface
  80       * @since  1.0
  81       */
  82      protected $response;
  83  
  84      /**
  85       * Is caching enabled?
  86       *
  87       * @var    boolean
  88       * @since  2.0.0
  89       */
  90      private $cacheable = false;
  91  
  92      /**
  93       * A map of integer HTTP response codes to the full HTTP Status for the headers.
  94       *
  95       * @var    array
  96       * @since  1.6.0
  97       * @link   https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
  98       */
  99      private $responseMap = [
 100          100 => 'HTTP/{version} 100 Continue',
 101          101 => 'HTTP/{version} 101 Switching Protocols',
 102          102 => 'HTTP/{version} 102 Processing',
 103          200 => 'HTTP/{version} 200 OK',
 104          201 => 'HTTP/{version} 201 Created',
 105          202 => 'HTTP/{version} 202 Accepted',
 106          203 => 'HTTP/{version} 203 Non-Authoritative Information',
 107          204 => 'HTTP/{version} 204 No Content',
 108          205 => 'HTTP/{version} 205 Reset Content',
 109          206 => 'HTTP/{version} 206 Partial Content',
 110          207 => 'HTTP/{version} 207 Multi-Status',
 111          208 => 'HTTP/{version} 208 Already Reported',
 112          226 => 'HTTP/{version} 226 IM Used',
 113          300 => 'HTTP/{version} 300 Multiple Choices',
 114          301 => 'HTTP/{version} 301 Moved Permanently',
 115          302 => 'HTTP/{version} 302 Found',
 116          303 => 'HTTP/{version} 303 See other',
 117          304 => 'HTTP/{version} 304 Not Modified',
 118          305 => 'HTTP/{version} 305 Use Proxy',
 119          306 => 'HTTP/{version} 306 (Unused)',
 120          307 => 'HTTP/{version} 307 Temporary Redirect',
 121          308 => 'HTTP/{version} 308 Permanent Redirect',
 122          400 => 'HTTP/{version} 400 Bad Request',
 123          401 => 'HTTP/{version} 401 Unauthorized',
 124          402 => 'HTTP/{version} 402 Payment Required',
 125          403 => 'HTTP/{version} 403 Forbidden',
 126          404 => 'HTTP/{version} 404 Not Found',
 127          405 => 'HTTP/{version} 405 Method Not Allowed',
 128          406 => 'HTTP/{version} 406 Not Acceptable',
 129          407 => 'HTTP/{version} 407 Proxy Authentication Required',
 130          408 => 'HTTP/{version} 408 Request Timeout',
 131          409 => 'HTTP/{version} 409 Conflict',
 132          410 => 'HTTP/{version} 410 Gone',
 133          411 => 'HTTP/{version} 411 Length Required',
 134          412 => 'HTTP/{version} 412 Precondition Failed',
 135          413 => 'HTTP/{version} 413 Payload Too Large',
 136          414 => 'HTTP/{version} 414 URI Too Long',
 137          415 => 'HTTP/{version} 415 Unsupported Media Type',
 138          416 => 'HTTP/{version} 416 Range Not Satisfiable',
 139          417 => 'HTTP/{version} 417 Expectation Failed',
 140          418 => 'HTTP/{version} 418 I\'m a teapot',
 141          421 => 'HTTP/{version} 421 Misdirected Request',
 142          422 => 'HTTP/{version} 422 Unprocessable Entity',
 143          423 => 'HTTP/{version} 423 Locked',
 144          424 => 'HTTP/{version} 424 Failed Dependency',
 145          426 => 'HTTP/{version} 426 Upgrade Required',
 146          428 => 'HTTP/{version} 428 Precondition Required',
 147          429 => 'HTTP/{version} 429 Too Many Requests',
 148          431 => 'HTTP/{version} 431 Request Header Fields Too Large',
 149          451 => 'HTTP/{version} 451 Unavailable For Legal Reasons',
 150          500 => 'HTTP/{version} 500 Internal Server Error',
 151          501 => 'HTTP/{version} 501 Not Implemented',
 152          502 => 'HTTP/{version} 502 Bad Gateway',
 153          503 => 'HTTP/{version} 503 Service Unavailable',
 154          504 => 'HTTP/{version} 504 Gateway Timeout',
 155          505 => 'HTTP/{version} 505 HTTP Version Not Supported',
 156          506 => 'HTTP/{version} 506 Variant Also Negotiates',
 157          507 => 'HTTP/{version} 507 Insufficient Storage',
 158          508 => 'HTTP/{version} 508 Loop Detected',
 159          510 => 'HTTP/{version} 510 Not Extended',
 160          511 => 'HTTP/{version} 511 Network Authentication Required',
 161      ];
 162  
 163      /**
 164       * Class constructor.
 165       *
 166       * @param   Input              $input     An optional argument to provide dependency injection for the application's
 167       *                                        input object.  If the argument is an Input object that object will become
 168       *                                        the application's input object, otherwise a default input object is
 169       *                                        created.
 170       * @param   Registry           $config    An optional argument to provide dependency injection for the application's
 171       *                                        config object.  If the argument is a Registry object that object will
 172       *                                        become the application's config object, otherwise a default config object
 173       *                                        is created.
 174       * @param   Web\WebClient      $client    An optional argument to provide dependency injection for the application's
 175       *                                        client object.  If the argument is a Web\WebClient object that object will
 176       *                                        become the application's client object, otherwise a default client object
 177       *                                        is created.
 178       * @param   ResponseInterface  $response  An optional argument to provide dependency injection for the application's
 179       *                                        response object.  If the argument is a ResponseInterface object that object
 180       *                                        will become the application's response object, otherwise a default response
 181       *                                        object is created.
 182       *
 183       * @since   1.0
 184       */
 185  	public function __construct(Input $input = null, Registry $config = null, Web\WebClient $client = null, ResponseInterface $response = null)
 186      {
 187          $this->input  = $input ?: new Input;
 188          $this->client = $client ?: new Web\WebClient;
 189  
 190          // Setup the response object.
 191          if (!$response)
 192          {
 193              $response = new Response;
 194          }
 195  
 196          $this->setResponse($response);
 197  
 198          // Call the constructor as late as possible (it runs `initialise`).
 199          parent::__construct($config);
 200  
 201          // Set the system URIs.
 202          $this->loadSystemUris();
 203      }
 204  
 205      /**
 206       * Magic method to access properties of the application.
 207       *
 208       * @param   string  $name  The name of the property.
 209       *
 210       * @return  mixed   A value if the property name is valid, null otherwise.
 211       *
 212       * @since       2.0.0
 213       * @deprecated  3.0  This is a B/C proxy for deprecated read accesses
 214       */
 215  	public function __get($name)
 216      {
 217          switch ($name)
 218          {
 219              case 'input':
 220                  trigger_deprecation(
 221                      'joomla/application',
 222                      '2.0.0',
 223                      'Accessing the input property of %s is deprecated, use the %s::getInput() method instead.',
 224                      self::class,
 225                      self::class
 226                  );
 227  
 228                  return $this->getInput();
 229  
 230              default:
 231                  $trace = debug_backtrace();
 232                  trigger_error(
 233                      sprintf(
 234                          'Undefined property via __get(): %1$s in %2$s on line %3$s',
 235                          $name,
 236                          $trace[0]['file'],
 237                          $trace[0]['line']
 238                      ),
 239                      E_USER_NOTICE
 240                  );
 241          }
 242      }
 243  
 244      /**
 245       * Execute the application.
 246       *
 247       * @return  void
 248       *
 249       * @since   1.0
 250       */
 251  	public function execute()
 252      {
 253          try
 254          {
 255              $this->dispatchEvent(ApplicationEvents::BEFORE_EXECUTE);
 256  
 257              // Perform application routines.
 258              $this->doExecute();
 259  
 260              $this->dispatchEvent(ApplicationEvents::AFTER_EXECUTE);
 261  
 262              // If gzip compression is enabled in configuration and the server is compliant, compress the output.
 263              if ($this->get('gzip') && !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler'))
 264              {
 265                  $this->compress();
 266              }
 267          }
 268          catch (\Throwable $throwable)
 269          {
 270              $this->dispatchEvent(ApplicationEvents::ERROR, new Event\ApplicationErrorEvent($throwable, $this));
 271          }
 272  
 273          $this->dispatchEvent(ApplicationEvents::BEFORE_RESPOND);
 274  
 275          // Send the application response.
 276          $this->respond();
 277  
 278          $this->dispatchEvent(ApplicationEvents::AFTER_RESPOND);
 279      }
 280  
 281      /**
 282       * Checks the accept encoding of the browser and compresses the data before sending it to the client if possible.
 283       *
 284       * @return  void
 285       *
 286       * @since   1.0
 287       */
 288  	protected function compress()
 289      {
 290          // Supported compression encodings.
 291          $supported = [
 292              'x-gzip'  => 'gz',
 293              'gzip'    => 'gz',
 294              'deflate' => 'deflate',
 295          ];
 296  
 297          // Get the supported encoding.
 298          $encodings = array_intersect($this->client->encodings, array_keys($supported));
 299  
 300          // If no supported encoding is detected do nothing and return.
 301          if (empty($encodings))
 302          {
 303              return;
 304          }
 305  
 306          // Verify that headers have not yet been sent, and that our connection is still alive.
 307          if ($this->checkHeadersSent() || !$this->checkConnectionAlive())
 308          {
 309              return;
 310          }
 311  
 312          // Iterate through the encodings and attempt to compress the data using any found supported encodings.
 313          foreach ($encodings as $encoding)
 314          {
 315              if (($supported[$encoding] == 'gz') || ($supported[$encoding] == 'deflate'))
 316              {
 317                  // Verify that the server supports gzip compression before we attempt to gzip encode the data.
 318                  // @codeCoverageIgnoreStart
 319                  if (!\extension_loaded('zlib') || ini_get('zlib.output_compression'))
 320                  {
 321                      continue;
 322                  }
 323  
 324                  // @codeCoverageIgnoreEnd
 325  
 326                  // Attempt to gzip encode the data with an optimal level 4.
 327                  $data   = $this->getBody();
 328                  $gzdata = gzencode($data, 4, ($supported[$encoding] == 'gz') ? FORCE_GZIP : FORCE_DEFLATE);
 329  
 330                  // If there was a problem encoding the data just try the next encoding scheme.
 331                  // @codeCoverageIgnoreStart
 332                  if ($gzdata === false)
 333                  {
 334                      continue;
 335                  }
 336  
 337                  // @codeCoverageIgnoreEnd
 338  
 339                  // Set the encoding headers.
 340                  $this->setHeader('Content-Encoding', $encoding);
 341                  $this->setHeader('Vary', 'Accept-Encoding');
 342                  $this->setHeader('X-Content-Encoded-By', 'Joomla');
 343  
 344                  // Replace the output with the encoded data.
 345                  $this->setBody($gzdata);
 346  
 347                  // Compression complete, let's break out of the loop.
 348                  break;
 349              }
 350          }
 351      }
 352  
 353      /**
 354       * Method to send the application response to the client.  All headers will be sent prior to the main application output data.
 355       *
 356       * @return  void
 357       *
 358       * @since   1.0
 359       */
 360  	protected function respond()
 361      {
 362          // Send the content-type header.
 363          if (!$this->getResponse()->hasHeader('Content-Type'))
 364          {
 365              $this->setHeader('Content-Type', $this->mimeType . '; charset=' . $this->charSet);
 366          }
 367  
 368          // If the response is set to uncachable, we need to set some appropriate headers so browsers don't cache the response.
 369          if (!$this->allowCache())
 370          {
 371              // Expires in the past.
 372              $this->setHeader('Expires', 'Wed, 17 Aug 2005 00:00:00 GMT', true);
 373  
 374              // Always modified.
 375              $this->setHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT', true);
 376              $this->setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', false);
 377  
 378              // HTTP 1.0
 379              $this->setHeader('Pragma', 'no-cache');
 380          }
 381          else
 382          {
 383              // Expires.
 384              if (!$this->getResponse()->hasHeader('Expires'))
 385              {
 386                  $this->setHeader('Expires', gmdate('D, d M Y H:i:s', time() + 900) . ' GMT');
 387              }
 388  
 389              // Last modified.
 390              if (!$this->getResponse()->hasHeader('Last-Modified') && $this->modifiedDate instanceof \DateTime)
 391              {
 392                  $this->modifiedDate->setTimezone(new \DateTimeZone('UTC'));
 393                  $this->setHeader('Last-Modified', $this->modifiedDate->format('D, d M Y H:i:s') . ' GMT');
 394              }
 395          }
 396  
 397          // Make sure there is a status header already otherwise generate it from the response
 398          if (!$this->getResponse()->hasHeader('Status'))
 399          {
 400              $this->setHeader('Status', (string) $this->getResponse()->getStatusCode());
 401          }
 402  
 403          $this->sendHeaders();
 404  
 405          echo $this->getBody();
 406      }
 407  
 408      /**
 409       * Method to get the application input object.
 410       *
 411       * @return  Input
 412       *
 413       * @since   2.0.0
 414       */
 415  	public function getInput(): Input
 416      {
 417          return $this->input;
 418      }
 419  
 420      /**
 421       * Redirect to another URL.
 422       *
 423       * If the headers have not been sent the redirect will be accomplished using a "301 Moved Permanently" or "303 See Other" code in the header
 424       * pointing to the new location. If the headers have already been sent this will be accomplished using a JavaScript statement.
 425       *
 426       * @param   string           $url     The URL to redirect to. Can only be http/https URL
 427       * @param   integer|boolean  $status  The HTTP status code to be provided. 303 is assumed by default.
 428       *
 429       * @return  void
 430       *
 431       * @since   1.0
 432       * @throws  \InvalidArgumentException
 433       */
 434  	public function redirect($url, $status = 303)
 435      {
 436          // Check for relative internal links.
 437          if (preg_match('#^index\.php#', $url))
 438          {
 439              $url = $this->get('uri.base.full') . $url;
 440          }
 441  
 442          // Perform a basic sanity check to make sure we don't have any CRLF garbage.
 443          $url = preg_split("/[\r\n]/", $url);
 444          $url = $url[0];
 445  
 446          /*
 447           * Here we need to check and see if the URL is relative or absolute.  Essentially, do we need to
 448           * prepend the URL with our base URL for a proper redirect.  The rudimentary way we are looking
 449           * at this is to simply check whether or not the URL string has a valid scheme or not.
 450           */
 451          if (!preg_match('#^[a-z]+\://#i', $url))
 452          {
 453              // Get a Uri instance for the requested URI.
 454              $uri = new Uri($this->get('uri.request'));
 455  
 456              // Get a base URL to prepend from the requested URI.
 457              $prefix = $uri->toString(['scheme', 'user', 'pass', 'host', 'port']);
 458  
 459              // We just need the prefix since we have a path relative to the root.
 460              if ($url[0] == '/')
 461              {
 462                  $url = $prefix . $url;
 463              }
 464              else
 465              {
 466                  // It's relative to where we are now, so lets add that.
 467                  $parts = explode('/', $uri->toString(['path']));
 468                  array_pop($parts);
 469                  $path = implode('/', $parts) . '/';
 470                  $url  = $prefix . $path . $url;
 471              }
 472          }
 473  
 474          // If the headers have already been sent we need to send the redirect statement via JavaScript.
 475          if ($this->checkHeadersSent())
 476          {
 477              echo '<script>document.location.href=' . json_encode($url) . ";</script>\n";
 478          }
 479          // We have to use a JavaScript redirect here because MSIE doesn't play nice with UTF-8 URLs.
 480          elseif (($this->client->engine == Web\WebClient::TRIDENT) && !static::isAscii($url))
 481          {
 482              $html = '<html><head>';
 483              $html .= '<meta http-equiv="content-type" content="text/html; charset=' . $this->charSet . '" />';
 484              $html .= '<script>document.location.href=' . json_encode($url) . ';</script>';
 485              $html .= '</head><body></body></html>';
 486  
 487              echo $html;
 488          }
 489          else
 490          {
 491              // Check if we have a boolean for the status variable for compatability with v1 of the framework
 492              // @deprecated 3.0
 493              if (\is_bool($status))
 494              {
 495                  trigger_deprecation(
 496                      'joomla/application',
 497                      '2.0.0',
 498                      'Passing a boolean value for the $status argument in %s() is deprecated, an integer should be passed instead.',
 499                      __METHOD__
 500                  );
 501  
 502                  $status = $status ? 301 : 303;
 503              }
 504  
 505              if (!\is_int($status) && !$this->isRedirectState($status))
 506              {
 507                  throw new \InvalidArgumentException('You have not supplied a valid HTTP status code');
 508              }
 509  
 510              // All other cases use the more efficient HTTP header for redirection.
 511              $this->setHeader('Status', (string) $status, true);
 512              $this->setHeader('Location', $url, true);
 513          }
 514  
 515          $this->dispatchEvent(ApplicationEvents::BEFORE_RESPOND);
 516  
 517          // Set appropriate headers
 518          $this->respond();
 519  
 520          $this->dispatchEvent(ApplicationEvents::AFTER_RESPOND);
 521  
 522          // Close the application after the redirect.
 523          $this->close();
 524      }
 525  
 526      /**
 527       * Set/get cachable state for the response.
 528       *
 529       * If $allow is set, sets the cachable state of the response.  Always returns the current state.
 530       *
 531       * @param   boolean  $allow  True to allow browser caching.
 532       *
 533       * @return  boolean
 534       *
 535       * @since   1.0
 536       */
 537  	public function allowCache($allow = null)
 538      {
 539          if ($allow !== null)
 540          {
 541              $this->cacheable = (bool) $allow;
 542          }
 543  
 544          return $this->cacheable;
 545      }
 546  
 547      /**
 548       * Method to set a response header.
 549       *
 550       * If the replace flag is set then all headers with the given name will be replaced by the new one.
 551       * The headers are stored in an internal array to be sent when the site is sent to the browser.
 552       *
 553       * @param   string   $name     The name of the header to set.
 554       * @param   string   $value    The value of the header to set.
 555       * @param   boolean  $replace  True to replace any headers with the same name.
 556       *
 557       * @return  $this
 558       *
 559       * @since   1.0
 560       */
 561  	public function setHeader($name, $value, $replace = false)
 562      {
 563          // Sanitize the input values.
 564          $name     = (string) $name;
 565          $value    = (string) $value;
 566          $response = $this->getResponse();
 567  
 568          // If the replace flag is set, unset all known headers with the given name.
 569          if ($replace && $response->hasHeader($name))
 570          {
 571              $response = $response->withoutHeader($name);
 572          }
 573  
 574          // Add the header to the internal array.
 575          $this->setResponse($response->withAddedHeader($name, $value));
 576  
 577          return $this;
 578      }
 579  
 580      /**
 581       * Method to get the array of response headers to be sent when the response is sent to the client.
 582       *
 583       * @return  array
 584       *
 585       * @since   1.0
 586       */
 587  	public function getHeaders()
 588      {
 589          $return = [];
 590  
 591          foreach ($this->getResponse()->getHeaders() as $name => $values)
 592          {
 593              foreach ($values as $value)
 594              {
 595                  $return[] = ['name' => $name, 'value' => $value];
 596              }
 597          }
 598  
 599          return $return;
 600      }
 601  
 602      /**
 603       * Method to clear any set response headers.
 604       *
 605       * @return  $this
 606       *
 607       * @since   1.0
 608       */
 609  	public function clearHeaders()
 610      {
 611          $response = $this->getResponse();
 612  
 613          foreach ($response->getHeaders() as $name => $values)
 614          {
 615              $response = $response->withoutHeader($name);
 616          }
 617  
 618          $this->setResponse($response);
 619  
 620          return $this;
 621      }
 622  
 623      /**
 624       * Send the response headers.
 625       *
 626       * @return  $this
 627       *
 628       * @since   1.0
 629       */
 630  	public function sendHeaders()
 631      {
 632          if (!$this->checkHeadersSent())
 633          {
 634              foreach ($this->getHeaders() as $header)
 635              {
 636                  if (strtolower($header['name']) == 'status')
 637                  {
 638                      // 'status' headers indicate an HTTP status, and need to be handled slightly differently
 639                      $status = $this->getHttpStatusValue($header['value']);
 640  
 641                      $this->header($status, true, (int) $header['value']);
 642                  }
 643                  else
 644                  {
 645                      $this->header($header['name'] . ': ' . $header['value']);
 646                  }
 647              }
 648          }
 649  
 650          return $this;
 651      }
 652  
 653      /**
 654       * Set body content.  If body content already defined, this will replace it.
 655       *
 656       * @param   string  $content  The content to set as the response body.
 657       *
 658       * @return  $this
 659       *
 660       * @since   1.0
 661       */
 662  	public function setBody($content)
 663      {
 664          $stream = new Stream('php://memory', 'rw');
 665          $stream->write((string) $content);
 666          $this->setResponse($this->getResponse()->withBody($stream));
 667  
 668          return $this;
 669      }
 670  
 671      /**
 672       * Prepend content to the body content
 673       *
 674       * @param   string  $content  The content to prepend to the response body.
 675       *
 676       * @return  $this
 677       *
 678       * @since   1.0
 679       */
 680  	public function prependBody($content)
 681      {
 682          $currentBody = $this->getResponse()->getBody();
 683  
 684          if (!$currentBody->isReadable())
 685          {
 686              throw new UnableToWriteBody;
 687          }
 688  
 689          $stream = new Stream('php://memory', 'rw');
 690          $stream->write((string) $content . (string) $currentBody);
 691          $this->setResponse($this->getResponse()->withBody($stream));
 692  
 693          return $this;
 694      }
 695  
 696      /**
 697       * Append content to the body content
 698       *
 699       * @param   string  $content  The content to append to the response body.
 700       *
 701       * @return  $this
 702       *
 703       * @since   1.0
 704       */
 705  	public function appendBody($content)
 706      {
 707          $currentStream = $this->getResponse()->getBody();
 708  
 709          if ($currentStream->isWritable())
 710          {
 711              $currentStream->write((string) $content);
 712              $this->setResponse($this->getResponse()->withBody($currentStream));
 713          }
 714          elseif ($currentStream->isReadable())
 715          {
 716              $stream = new Stream('php://memory', 'rw');
 717              $stream->write((string) $currentStream . (string) $content);
 718              $this->setResponse($this->getResponse()->withBody($stream));
 719          }
 720          else
 721          {
 722              throw new UnableToWriteBody;
 723          }
 724  
 725          return $this;
 726      }
 727  
 728      /**
 729       * Return the body content
 730       *
 731       * @return  string  The response body as a string.
 732       *
 733       * @since   1.0
 734       */
 735  	public function getBody()
 736      {
 737          return (string) $this->getResponse()->getBody();
 738      }
 739  
 740      /**
 741       * Get the PSR-7 Response Object.
 742       *
 743       * @return  ResponseInterface
 744       *
 745       * @since   2.0.0
 746       */
 747  	public function getResponse(): ResponseInterface
 748      {
 749          return $this->response;
 750      }
 751  
 752      /**
 753       * Check if a given value can be successfully mapped to a valid http status value
 754       *
 755       * @param   string|int  $value  The given status as int or string
 756       *
 757       * @return  string
 758       *
 759       * @since   1.8.0
 760       */
 761  	protected function getHttpStatusValue($value)
 762      {
 763          $code = (int) $value;
 764  
 765          if (array_key_exists($code, $this->responseMap))
 766          {
 767              $value = $this->responseMap[$code];
 768          }
 769          else
 770          {
 771              $value = 'HTTP/{version} ' . $code;
 772          }
 773  
 774          return str_replace('{version}', $this->httpVersion, $value);
 775      }
 776  
 777      /**
 778       * Check if the value is a valid HTTP status code
 779       *
 780       * @param   integer  $code  The potential status code
 781       *
 782       * @return  boolean
 783       *
 784       * @since   1.8.1
 785       */
 786  	public function isValidHttpStatus($code)
 787      {
 788          return array_key_exists($code, $this->responseMap);
 789      }
 790  
 791      /**
 792       * Method to check the current client connection status to ensure that it is alive.  We are
 793       * wrapping this to isolate the connection_status() function from our code base for testing reasons.
 794       *
 795       * @return  boolean  True if the connection is valid and normal.
 796       *
 797       * @codeCoverageIgnore
 798       * @see     connection_status()
 799       * @since   1.0
 800       */
 801  	protected function checkConnectionAlive()
 802      {
 803          return connection_status() === CONNECTION_NORMAL;
 804      }
 805  
 806      /**
 807       * Method to check to see if headers have already been sent.
 808       *
 809       * @return  boolean  True if the headers have already been sent.
 810       *
 811       * @codeCoverageIgnore
 812       * @see     headers_sent()
 813       * @since   1.0
 814       */
 815  	protected function checkHeadersSent()
 816      {
 817          return headers_sent();
 818      }
 819  
 820      /**
 821       * Method to detect the requested URI from server environment variables.
 822       *
 823       * @return  string  The requested URI
 824       *
 825       * @since   1.0
 826       */
 827  	protected function detectRequestUri()
 828      {
 829          // First we need to detect the URI scheme.
 830          $scheme = $this->isSslConnection() ? 'https://' : 'http://';
 831  
 832          /*
 833           * There are some differences in the way that Apache and IIS populate server environment variables.  To
 834           * properly detect the requested URI we need to adjust our algorithm based on whether or not we are getting
 835           * information from Apache or IIS.
 836           */
 837  
 838          $phpSelf    = $this->input->server->getString('PHP_SELF', '');
 839          $requestUri = $this->input->server->getString('REQUEST_URI', '');
 840  
 841          // If PHP_SELF and REQUEST_URI are both populated then we will assume "Apache Mode".
 842          if (!empty($phpSelf) && !empty($requestUri))
 843          {
 844              // The URI is built from the HTTP_HOST and REQUEST_URI environment variables in an Apache environment.
 845              $uri = $scheme . $this->input->server->getString('HTTP_HOST') . $requestUri;
 846          }
 847          else
 848          {
 849              // If not in "Apache Mode" we will assume that we are in an IIS environment and proceed.
 850              // IIS uses the SCRIPT_NAME variable instead of a REQUEST_URI variable... thanks, MS
 851              $uri       = $scheme . $this->input->server->getString('HTTP_HOST') . $this->input->server->getString('SCRIPT_NAME');
 852              $queryHost = $this->input->server->getString('QUERY_STRING', '');
 853  
 854              // If the QUERY_STRING variable exists append it to the URI string.
 855              if (!empty($queryHost))
 856              {
 857                  $uri .= '?' . $queryHost;
 858              }
 859          }
 860  
 861          return trim($uri);
 862      }
 863  
 864      /**
 865       * Method to send a header to the client.
 866       *
 867       * @param   string   $string   The header string.
 868       * @param   boolean  $replace  The optional replace parameter indicates whether the header should replace a previous similar header, or add
 869       *                             a second header of the same type.
 870       * @param   integer  $code     Forces the HTTP response code to the specified value. Note that this parameter only has an effect if the string
 871       *                             is not empty.
 872       *
 873       * @return  void
 874       *
 875       * @codeCoverageIgnore
 876       * @see     header()
 877       * @since   1.0
 878       */
 879  	protected function header($string, $replace = true, $code = null)
 880      {
 881          if ($code === null)
 882          {
 883              $code = 0;
 884          }
 885  
 886          header(str_replace(\chr(0), '', $string), $replace, $code);
 887      }
 888  
 889      /**
 890       * Set the PSR-7 Response Object.
 891       *
 892       * @param   ResponseInterface  $response  The response object
 893       *
 894       * @return  void
 895       *
 896       * @since   2.0.0
 897       */
 898  	public function setResponse(ResponseInterface $response): void
 899      {
 900          $this->response = $response;
 901      }
 902  
 903      /**
 904       * Checks if a state is a redirect state
 905       *
 906       * @param   integer  $state  The HTTP status code.
 907       *
 908       * @return  boolean
 909       *
 910       * @since   1.8.0
 911       */
 912  	protected function isRedirectState($state)
 913      {
 914          $state = (int) $state;
 915  
 916          return $state > 299 && $state < 400 && array_key_exists($state, $this->responseMap);
 917      }
 918  
 919      /**
 920       * Determine if we are using a secure (SSL) connection.
 921       *
 922       * @return  boolean  True if using SSL, false if not.
 923       *
 924       * @since   1.0
 925       */
 926  	public function isSslConnection()
 927      {
 928          $serverSSLVar = $this->input->server->getString('HTTPS', '');
 929  
 930          if (!empty($serverSSLVar) && strtolower($serverSSLVar) !== 'off')
 931          {
 932              return true;
 933          }
 934  
 935          $serverForwarderProtoVar = $this->input->server->getString('HTTP_X_FORWARDED_PROTO', '');
 936  
 937          return !empty($serverForwarderProtoVar) && strtolower($serverForwarderProtoVar) === 'https';
 938      }
 939  
 940      /**
 941       * Method to load the system URI strings for the application.
 942       *
 943       * @param   string  $requestUri  An optional request URI to use instead of detecting one from the server environment variables.
 944       *
 945       * @return  void
 946       *
 947       * @since   1.0
 948       */
 949  	protected function loadSystemUris($requestUri = null)
 950      {
 951          // Set the request URI.
 952          if (!empty($requestUri))
 953          {
 954              $this->set('uri.request', $requestUri);
 955          }
 956          else
 957          {
 958              $this->set('uri.request', $this->detectRequestUri());
 959          }
 960  
 961          // Check to see if an explicit base URI has been set.
 962          $siteUri = trim($this->get('site_uri'));
 963  
 964          if ($siteUri != '')
 965          {
 966              $uri  = new Uri($siteUri);
 967              $path = $uri->toString(['path']);
 968          }
 969          else
 970          {
 971              // No explicit base URI was set so we need to detect it. Start with the requested URI.
 972              $uri = new Uri($this->get('uri.request'));
 973  
 974              $requestUri = $this->input->server->getString('REQUEST_URI', '');
 975  
 976              // If we are working from a CGI SAPI with the 'cgi.fix_pathinfo' directive disabled we use PHP_SELF.
 977              if (strpos(PHP_SAPI, 'cgi') !== false && !ini_get('cgi.fix_pathinfo') && !empty($requestUri))
 978              {
 979                  // We aren't expecting PATH_INFO within PHP_SELF so this should work.
 980                  $path = \dirname($this->input->server->getString('PHP_SELF', ''));
 981              }
 982              else
 983              {
 984                  // Pretty much everything else should be handled with SCRIPT_NAME.
 985                  $path = \dirname($this->input->server->getString('SCRIPT_NAME', ''));
 986              }
 987          }
 988  
 989          // Get the host from the URI.
 990          $host = $uri->toString(['scheme', 'user', 'pass', 'host', 'port']);
 991  
 992          // Check if the path includes "index.php".
 993          if (strpos($path, 'index.php') !== false)
 994          {
 995              // Remove the index.php portion of the path.
 996              $path = substr_replace($path, '', strpos($path, 'index.php'), 9);
 997          }
 998  
 999          $path = rtrim($path, '/\\');
1000  
1001          // Set the base URI both as just a path and as the full URI.
1002          $this->set('uri.base.full', $host . $path . '/');
1003          $this->set('uri.base.host', $host);
1004          $this->set('uri.base.path', $path . '/');
1005  
1006          // Set the extended (non-base) part of the request URI as the route.
1007          if (stripos($this->get('uri.request'), $this->get('uri.base.full')) === 0)
1008          {
1009              $this->set('uri.route', substr_replace($this->get('uri.request'), '', 0, \strlen($this->get('uri.base.full'))));
1010          }
1011  
1012          // Get an explicitly set media URI is present.
1013          $mediaURI = trim($this->get('media_uri'));
1014  
1015          if ($mediaURI)
1016          {
1017              if (strpos($mediaURI, '://') !== false)
1018              {
1019                  $this->set('uri.media.full', $mediaURI);
1020                  $this->set('uri.media.path', $mediaURI);
1021              }
1022              else
1023              {
1024                  // Normalise slashes.
1025                  $mediaURI = trim($mediaURI, '/\\');
1026                  $mediaURI = !empty($mediaURI) ? '/' . $mediaURI . '/' : '/';
1027                  $this->set('uri.media.full', $this->get('uri.base.host') . $mediaURI);
1028                  $this->set('uri.media.path', $mediaURI);
1029              }
1030          }
1031          else
1032          {
1033              // No explicit media URI was set, build it dynamically from the base uri.
1034              $this->set('uri.media.full', $this->get('uri.base.full') . 'media/');
1035              $this->set('uri.media.path', $this->get('uri.base.path') . 'media/');
1036          }
1037      }
1038  
1039      /**
1040       * Tests whether a string contains only 7bit ASCII bytes.
1041       *
1042       * You might use this to conditionally check whether a string
1043       * needs handling as UTF-8 or not, potentially offering performance
1044       * benefits by using the native PHP equivalent if it's just ASCII e.g.;
1045       *
1046       * @param   string  $str  The string to test.
1047       *
1048       * @return  boolean  True if the string is all ASCII
1049       *
1050       * @since   1.4.0
1051       */
1052  	public static function isAscii($str)
1053      {
1054          // Search for any bytes which are outside the ASCII range...
1055          return preg_match('/(?:[^\x00-\x7F])/', $str) !== 1;
1056      }
1057  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer