[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/joomla/http/src/Transport/ -> Curl.php (source)

   1  <?php
   2  /**
   3   * Part of the Joomla Framework Http 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\Http\Transport;
  10  
  11  use Composer\CaBundle\CaBundle;
  12  use Joomla\Http\AbstractTransport;
  13  use Joomla\Http\Exception\InvalidResponseCodeException;
  14  use Joomla\Http\Response;
  15  use Joomla\Uri\UriInterface;
  16  use Laminas\Diactoros\Stream as StreamResponse;
  17  
  18  /**
  19   * HTTP transport class for using cURL.
  20   *
  21   * @since  1.0
  22   */
  23  class Curl extends AbstractTransport
  24  {
  25      /**
  26       * Send a request to the server and return a Response object with the response.
  27       *
  28       * @param   string        $method     The HTTP method for sending the request.
  29       * @param   UriInterface  $uri        The URI to the resource to request.
  30       * @param   mixed         $data       Either an associative array or a string to be sent with the request.
  31       * @param   array         $headers    An array of request headers to send with the request.
  32       * @param   integer       $timeout    Read timeout in seconds.
  33       * @param   string        $userAgent  The optional user agent string to send with the request.
  34       *
  35       * @return  Response
  36       *
  37       * @since   1.0
  38       * @throws  \RuntimeException
  39       */
  40  	public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null)
  41      {
  42          // Setup the cURL handle.
  43          $ch = curl_init();
  44  
  45          // Initialize the certificate store
  46          $this->setCAOptionAndValue($ch);
  47  
  48          $options = [];
  49  
  50          // Set the request method.
  51          switch (strtoupper($method))
  52          {
  53              case 'GET':
  54                  $options[\CURLOPT_HTTPGET] = true;
  55  
  56                  break;
  57  
  58              case 'POST':
  59                  $options[\CURLOPT_POST] = true;
  60  
  61                  break;
  62  
  63              default:
  64                  $options[\CURLOPT_CUSTOMREQUEST] = strtoupper($method);
  65  
  66                  break;
  67          }
  68  
  69          // Don't wait for body when $method is HEAD
  70          $options[\CURLOPT_NOBODY] = ($method === 'HEAD');
  71  
  72          // Initialize the certificate store
  73          $options[CURLOPT_CAINFO] = $this->getOption('curl.certpath', CaBundle::getSystemCaRootBundlePath());
  74  
  75          // If data exists let's encode it and make sure our Content-type header is set.
  76          if (isset($data))
  77          {
  78              // If the data is a scalar value simply add it to the cURL post fields.
  79              if (is_scalar($data) || (isset($headers['Content-Type']) && strpos($headers['Content-Type'], 'multipart/form-data') === 0))
  80              {
  81                  $options[\CURLOPT_POSTFIELDS] = $data;
  82              }
  83              else
  84              {
  85                  // Otherwise we need to encode the value first.
  86                  $options[\CURLOPT_POSTFIELDS] = http_build_query($data);
  87              }
  88  
  89              if (!isset($headers['Content-Type']))
  90              {
  91                  $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
  92              }
  93  
  94              // Add the relevant headers.
  95              if (is_scalar($options[\CURLOPT_POSTFIELDS]))
  96              {
  97                  $headers['Content-Length'] = \strlen($options[\CURLOPT_POSTFIELDS]);
  98              }
  99          }
 100  
 101          // Build the headers string for the request.
 102          $headerArray = [];
 103  
 104          if (!empty($headers))
 105          {
 106              foreach ($headers as $key => $value)
 107              {
 108                  if (\is_array($value))
 109                  {
 110                      foreach ($value as $header)
 111                      {
 112                          $headerArray[] = "$key: $header";
 113                      }
 114                  }
 115                  else
 116                  {
 117                      $headerArray[] = "$key: $value";
 118                  }
 119              }
 120  
 121              // Add the headers string into the stream context options array.
 122              $options[\CURLOPT_HTTPHEADER] = $headerArray;
 123          }
 124  
 125          // Curl needs the accepted encoding header as option
 126          if (isset($headers['Accept-Encoding']))
 127          {
 128              $options[\CURLOPT_ENCODING] = $headers['Accept-Encoding'];
 129          }
 130  
 131          // If an explicit timeout is given user it.
 132          if (isset($timeout))
 133          {
 134              $options[\CURLOPT_TIMEOUT]        = (int) $timeout;
 135              $options[\CURLOPT_CONNECTTIMEOUT] = (int) $timeout;
 136          }
 137  
 138          // If an explicit user agent is given use it.
 139          if (isset($userAgent))
 140          {
 141              $options[\CURLOPT_USERAGENT] = $userAgent;
 142          }
 143  
 144          // Set the request URL.
 145          $options[\CURLOPT_URL] = (string) $uri;
 146  
 147          // We want our headers. :-)
 148          $options[\CURLOPT_HEADER] = true;
 149  
 150          // Return it... echoing it would be tacky.
 151          $options[\CURLOPT_RETURNTRANSFER] = true;
 152  
 153          // Override the Expect header to prevent cURL from confusing itself in its own stupidity.
 154          // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/
 155          $options[\CURLOPT_HTTPHEADER][] = 'Expect:';
 156  
 157          // Follow redirects if server config allows
 158          if ($this->redirectsAllowed())
 159          {
 160              $options[\CURLOPT_FOLLOWLOCATION] = (bool) $this->getOption('follow_location', true);
 161          }
 162  
 163          // Authentication, if needed
 164          if ($this->getOption('userauth') && $this->getOption('passwordauth'))
 165          {
 166              $options[\CURLOPT_USERPWD]  = $this->getOption('userauth') . ':' . $this->getOption('passwordauth');
 167              $options[\CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
 168          }
 169  
 170          // Configure protocol version
 171          if ($protocolVersion = $this->getOption('protocolVersion'))
 172          {
 173              $options[\CURLOPT_HTTP_VERSION] = $this->mapProtocolVersion($protocolVersion);
 174          }
 175  
 176          // Set any custom transport options
 177          foreach ($this->getOption('transport.curl', []) as $key => $value)
 178          {
 179              $options[$key] = $value;
 180          }
 181  
 182          // Set the cURL options.
 183          curl_setopt_array($ch, $options);
 184  
 185          // Execute the request and close the connection.
 186          $content = curl_exec($ch);
 187  
 188          // Check if the content is a string. If it is not, it must be an error.
 189          if (!\is_string($content))
 190          {
 191              $message = curl_error($ch);
 192  
 193              if (empty($message))
 194              {
 195                  // Error but nothing from cURL? Create our own
 196                  $message = 'No HTTP response received';
 197              }
 198  
 199              throw new \RuntimeException($message);
 200          }
 201  
 202          // Get the request information.
 203          $info = curl_getinfo($ch);
 204  
 205          // Close the connection.
 206          curl_close($ch);
 207  
 208          return $this->getResponse($content, $info);
 209      }
 210  
 211      /**
 212       * Configure the cURL resources with the appropriate root certificates.
 213       *
 214       * @param   resource  $ch  The cURL resource you want to configure the certificates on.
 215       *
 216       * @return  void
 217       *
 218       * @since   1.3.2
 219       */
 220  	protected function setCAOptionAndValue($ch)
 221      {
 222          if ($certpath = $this->getOption('curl.certpath'))
 223          {
 224              // Option is passed to a .PEM file.
 225              curl_setopt($ch, \CURLOPT_CAINFO, $certpath);
 226  
 227              return;
 228          }
 229  
 230          $caPathOrFile = CaBundle::getSystemCaRootBundlePath();
 231  
 232          if (is_dir($caPathOrFile) || (is_link($caPathOrFile) && is_dir(readlink($caPathOrFile))))
 233          {
 234              curl_setopt($ch, \CURLOPT_CAPATH, $caPathOrFile);
 235  
 236              return;
 237          }
 238  
 239          curl_setopt($ch, \CURLOPT_CAINFO, $caPathOrFile);
 240      }
 241  
 242      /**
 243       * Method to get a response object from a server response.
 244       *
 245       * @param   string  $content  The complete server response, including headers
 246       *                            as a string if the response has no errors.
 247       * @param   array   $info     The cURL request information.
 248       *
 249       * @return  Response
 250       *
 251       * @since   1.0
 252       * @throws  InvalidResponseCodeException
 253       */
 254  	protected function getResponse($content, $info)
 255      {
 256          // Try to get header size
 257          if (isset($info['header_size']))
 258          {
 259              $headerString = trim(substr($content, 0, $info['header_size']));
 260              $headerArray  = explode("\r\n\r\n", $headerString);
 261  
 262              // Get the last set of response headers as an array.
 263              $headers = explode("\r\n", array_pop($headerArray));
 264  
 265              // Set the body for the response.
 266              $body = substr($content, $info['header_size']);
 267          }
 268          // Fallback and try to guess header count by redirect count
 269          else
 270          {
 271              // Get the number of redirects that occurred.
 272              $redirects = $info['redirect_count'] ?? 0;
 273  
 274              /*
 275               * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will
 276               * also be included. So we split the response into header + body + the number of redirects and only use the last two
 277               * sections which should be the last set of headers and the actual body.
 278               */
 279              $response = explode("\r\n\r\n", $content, 2 + $redirects);
 280  
 281              // Set the body for the response.
 282              $body = array_pop($response);
 283  
 284              // Get the last set of response headers as an array.
 285              $headers = explode("\r\n", array_pop($response));
 286          }
 287  
 288          // Get the response code from the first offset of the response headers.
 289          preg_match('/[0-9]{3}/', array_shift($headers), $matches);
 290  
 291          $code = \count($matches) ? $matches[0] : null;
 292  
 293          if (!is_numeric($code))
 294          {
 295              // No valid response code was detected.
 296              throw new InvalidResponseCodeException('No HTTP response code found.');
 297          }
 298  
 299          $statusCode      = (int) $code;
 300          $verifiedHeaders = $this->processHeaders($headers);
 301  
 302          $streamInterface = new StreamResponse('php://memory', 'rw');
 303          $streamInterface->write($body);
 304  
 305          return new Response($streamInterface, $statusCode, $verifiedHeaders);
 306      }
 307  
 308      /**
 309       * Method to check if HTTP transport cURL is available for use
 310       *
 311       * @return  boolean  True if available, else false
 312       *
 313       * @since   1.0
 314       */
 315  	public static function isSupported()
 316      {
 317          return \function_exists('curl_version') && curl_version();
 318      }
 319  
 320      /**
 321       * Get the cURL constant for a HTTP protocol version
 322       *
 323       * @param   string  $version  The HTTP protocol version to use
 324       *
 325       * @return  integer
 326       *
 327       * @since   1.3.1
 328       */
 329  	private function mapProtocolVersion(string $version): int
 330      {
 331          switch ($version)
 332          {
 333              case '1.0':
 334                  return \CURL_HTTP_VERSION_1_0;
 335  
 336              case '1.1':
 337                  return \CURL_HTTP_VERSION_1_1;
 338  
 339              case '2.0':
 340              case '2':
 341                  if (\defined('CURL_HTTP_VERSION_2'))
 342                  {
 343                      return \CURL_HTTP_VERSION_2;
 344                  }
 345          }
 346  
 347          return \CURL_HTTP_VERSION_NONE;
 348      }
 349  
 350      /**
 351       * Check if redirects are allowed
 352       *
 353       * @return  boolean
 354       *
 355       * @since   1.2.1
 356       */
 357  	private function redirectsAllowed(): bool
 358      {
 359          return true;
 360      }
 361  }


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