From 0146ecd95db482a04918777e1df5a3f06448ad74 Mon Sep 17 00:00:00 2001 From: Hannes Papenberg Date: Mon, 30 Mar 2026 13:47:56 +0200 Subject: [PATCH 1/3] Removing deprecated Joomla CMS HTTP package --- libraries/src/Http/Http.php | 61 ---- libraries/src/Http/HttpFactory.php | 139 -------- libraries/src/Http/Response.php | 69 ---- .../src/Http/Transport/CurlTransport.php | 310 ------------------ .../src/Http/Transport/SocketTransport.php | 294 ----------------- .../src/Http/Transport/StreamTransport.php | 241 -------------- libraries/src/Http/TransportInterface.php | 28 -- 7 files changed, 1142 deletions(-) delete mode 100644 libraries/src/Http/Http.php delete mode 100644 libraries/src/Http/HttpFactory.php delete mode 100644 libraries/src/Http/Response.php delete mode 100644 libraries/src/Http/Transport/CurlTransport.php delete mode 100644 libraries/src/Http/Transport/SocketTransport.php delete mode 100644 libraries/src/Http/Transport/StreamTransport.php delete mode 100644 libraries/src/Http/TransportInterface.php diff --git a/libraries/src/Http/Http.php b/libraries/src/Http/Http.php deleted file mode 100644 index 2e402f5704e3c..0000000000000 --- a/libraries/src/Http/Http.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http; - -use Joomla\Http\Http as FrameworkHttp; -use Joomla\Http\TransportInterface as FrameworkTransportInterface; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP client class. - * - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Http instead - */ -class Http extends FrameworkHttp -{ - /** - * Constructor. - * - * @param array|\ArrayAccess $options Client options array. If the registry contains any headers.* elements, - * these will be added to the request headers. - * @param ?FrameworkTransportInterface $transport The HTTP transport object. - * - * @throws \InvalidArgumentException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Http::__construct() instead - */ - public function __construct($options = [], ?FrameworkTransportInterface $transport = null) - { - if (!\is_array($options) && !($options instanceof \ArrayAccess)) { - throw new \InvalidArgumentException( - 'The options param must be an array or implement the ArrayAccess interface.' - ); - } - - $this->options = $options; - - if (!isset($transport)) { - $transport = HttpFactory::getAvailableDriver($this->options); - } - - // Ensure the transport is a framework TransportInterface instance or bail out - if (!($transport instanceof FrameworkTransportInterface)) { - throw new \InvalidArgumentException('A valid TransportInterface object was not set.'); - } - - $this->transport = $transport; - } -} diff --git a/libraries/src/Http/HttpFactory.php b/libraries/src/Http/HttpFactory.php deleted file mode 100644 index 948af9662b6d3..0000000000000 --- a/libraries/src/Http/HttpFactory.php +++ /dev/null @@ -1,139 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http; - -use Joomla\CMS\Version; -use Joomla\Http\TransportInterface; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP factory class. - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\HttpFactory instead - */ -class HttpFactory -{ - /** - * Method to create a JHttp instance. - * - * @param array|\ArrayAccess $options Client options array. - * @param array|string $adapters Adapter (string) or queue of adapters (array) to use for communication. - * - * @return Http - * - * @throws \RuntimeException - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\HttpFactory::getHttp() instead - */ - public static function getHttp($options = [], $adapters = null) - { - if (!\is_array($options) && !($options instanceof \ArrayAccess)) { - throw new \InvalidArgumentException( - 'The options param must be an array or implement the ArrayAccess interface.' - ); - } - - // Set default userAgent if nothing else is set - if (!isset($options['userAgent'])) { - $version = new Version(); - $options['userAgent'] = $version->getUserAgent('Joomla', true, false); - } - - if (!$driver = static::getAvailableDriver($options, $adapters)) { - throw new \RuntimeException('No transport driver available.'); - } - - return new Http($options, $driver); - } - - /** - * Finds an available http transport object for communication - * - * @param array|\ArrayAccess $options Options for creating TransportInterface object - * @param array|string $default Adapter (string) or queue of adapters (array) to use - * - * @return TransportInterface|boolean Interface sub-class or boolean false if no adapters are available - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\HttpFactory::getAvailableDriver() instead - */ - public static function getAvailableDriver($options = [], $default = null) - { - if (\is_null($default)) { - $availableAdapters = static::getHttpTransports(); - } else { - settype($default, 'array'); - $availableAdapters = $default; - } - - // Check if there is at least one available http transport adapter - if (!\count($availableAdapters)) { - return false; - } - - foreach ($availableAdapters as $adapter) { - /** @var $class TransportInterface */ - $class = __NAMESPACE__ . '\\Transport\\' . ucfirst($adapter) . 'Transport'; - - if (!class_exists($class)) { - $class = 'JHttpTransport' . ucfirst($adapter); - } - - if (class_exists($class) && $class::isSupported()) { - return new $class($options); - } - } - - return false; - } - - /** - * Get the http transport handlers - * - * @return array An array of available transport handlers - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\HttpFactory::getHttpTransports() instead - */ - public static function getHttpTransports() - { - $names = []; - $iterator = new \DirectoryIterator(__DIR__ . '/Transport'); - - /** @type $file \DirectoryIterator */ - foreach ($iterator as $file) { - $fileName = $file->getFilename(); - - // Only load for php files. - if ($file->isFile() && $file->getExtension() === 'php') { - $names[] = substr($fileName, 0, strrpos($fileName, 'Transport.')); - } - } - - // Keep alphabetical order across all environments - sort($names); - - // If curl is available set it to the first position - if ($key = array_search('Curl', $names)) { - unset($names[$key]); - array_unshift($names, 'Curl'); - } - - return $names; - } -} diff --git a/libraries/src/Http/Response.php b/libraries/src/Http/Response.php deleted file mode 100644 index 002c4531b865c..0000000000000 --- a/libraries/src/Http/Response.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http; - -use Joomla\Http\Response as FrameworkResponse; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP response data object class. - * - * @since 1.7.3 - * - * @deprecated 4.0 will be removed in 7.0 - * Use Joomla\Http\Response instead - */ -class Response extends FrameworkResponse -{ - /** - * Magic getter for backward compatibility with the 1.x Joomla Framework API - * - * @param string $name The variable to return - * - * @return mixed - * - * @since 6.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Access data via the PSR-7 ResponseInterface instead - */ - public function __get($name) - { - switch (strtolower($name)) { - case 'body': - $stream = $this->getBody(); - $stream->rewind(); - return $stream->getContents(); - - case 'code': - return $this->getStatusCode(); - - case 'headers': - return $this->getHeaders(); - - default: - $trace = debug_backtrace(); - - trigger_error( - \sprintf( - 'Undefined property via __get(): %s in %s on line %s', - $name, - $trace[0]['file'], - $trace[0]['line'] - ), - E_USER_NOTICE - ); - - break; - } - } -} diff --git a/libraries/src/Http/Transport/CurlTransport.php b/libraries/src/Http/Transport/CurlTransport.php deleted file mode 100644 index 742fb295b8dd3..0000000000000 --- a/libraries/src/Http/Transport/CurlTransport.php +++ /dev/null @@ -1,310 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http\Transport; - -use Composer\CaBundle\CaBundle; -use Joomla\CMS\Factory; -use Joomla\CMS\Http\Response; -use Joomla\CMS\Http\TransportInterface; -use Joomla\CMS\Uri\Uri; -use Joomla\Http\AbstractTransport; -use Joomla\Http\Exception\InvalidResponseCodeException; -use Joomla\Uri\UriInterface; -use Laminas\Diactoros\Stream as StreamResponse; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP transport class for using cURL. - * - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Curl instead - */ -class CurlTransport extends AbstractTransport implements TransportInterface -{ - /** - * Send a request to the server and return a Response object with the response. - * - * @param string $method The HTTP method for sending the request. - * @param UriInterface $uri The URI to the resource to request. - * @param mixed $data Either an associative array or a string to be sent with the request. - * @param array $headers An array of request headers to send with the request. - * @param integer $timeout Read timeout in seconds. - * @param string $userAgent The optional user agent string to send with the request. - * - * @return Response - * - * @throws \RuntimeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Curl::request() instead - */ - public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) - { - // Setup the cURL handle. - $ch = curl_init(); - - $options = []; - - // Set the request method. - switch (strtoupper($method)) { - case 'GET': - $options[CURLOPT_HTTPGET] = true; - break; - - case 'POST': - $options[CURLOPT_POST] = true; - break; - - case 'PUT': - default: - $options[CURLOPT_CUSTOMREQUEST] = strtoupper($method); - break; - } - - // Don't wait for body when $method is HEAD - $options[CURLOPT_NOBODY] = ($method === 'HEAD'); - - // Initialize the certificate store - $options[CURLOPT_CAINFO] = $this->getOption('curl.certpath', CaBundle::getBundledCaBundlePath()); - - // If data exists let's encode it and make sure our Content-type header is set. - if (isset($data)) { - // If the data is a scalar value simply add it to the cURL post fields. - if (\is_scalar($data) || (isset($headers['Content-Type']) && str_starts_with($headers['Content-Type'], 'multipart/form-data'))) { - $options[CURLOPT_POSTFIELDS] = $data; - } else { - // Otherwise we need to encode the value first. - $options[CURLOPT_POSTFIELDS] = http_build_query($data); - } - - if (!isset($headers['Content-Type'])) { - $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; - } - - // Add the relevant headers. - if (\is_scalar($options[CURLOPT_POSTFIELDS])) { - $headers['Content-Length'] = \strlen($options[CURLOPT_POSTFIELDS]); - } - } - - // Build the headers string for the request. - $headerArray = []; - - if (isset($headers)) { - foreach ($headers as $key => $value) { - if (\is_array($value)) { - foreach ($value as $header) { - $headerArray[] = "$key: $header"; - } - } else { - $headerArray[] = "$key: $value"; - } - } - - // Add the headers string into the stream context options array. - $options[CURLOPT_HTTPHEADER] = $headerArray; - } - - // Curl needs the accepted encoding header as option - if (isset($headers['Accept-Encoding'])) { - $options[CURLOPT_ENCODING] = $headers['Accept-Encoding']; - } - - // If an explicit timeout is given user it. - if (isset($timeout)) { - $options[CURLOPT_TIMEOUT] = (int) $timeout; - $options[CURLOPT_CONNECTTIMEOUT] = (int) $timeout; - } - - // If an explicit user agent is given use it. - if (isset($userAgent)) { - $options[CURLOPT_USERAGENT] = $userAgent; - } - - // Set the request URL. - $options[CURLOPT_URL] = (string) $uri; - - // We want our headers. :-) - $options[CURLOPT_HEADER] = true; - - // Return it... echoing it would be tacky. - $options[CURLOPT_RETURNTRANSFER] = true; - - // Override the Expect header to prevent cURL from confusing itself in its own stupidity. - // Link: http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/ - $options[CURLOPT_HTTPHEADER][] = 'Expect:'; - - // Follow redirects if server config allows - if ($this->redirectsAllowed()) { - $options[CURLOPT_FOLLOWLOCATION] = (bool) $this->getOption('follow_location', true); - } - - // Proxy configuration - $app = Factory::getApplication(); - - if ($app->get('proxy_enable')) { - $options[CURLOPT_PROXY] = $app->get('proxy_host') . ':' . $app->get('proxy_port'); - - if ($user = $app->get('proxy_user')) { - $options[CURLOPT_PROXYUSERPWD] = $user . ':' . $app->get('proxy_pass'); - } - } - - // Set any custom transport options - foreach ($this->getOption('transport.curl', []) as $key => $value) { - $options[$key] = $value; - } - - // Authentication, if needed - if ($this->getOption('userauth') && $this->getOption('passwordauth')) { - $options[CURLOPT_USERPWD] = $this->getOption('userauth') . ':' . $this->getOption('passwordauth'); - $options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC; - } - - // Set the cURL options. - curl_setopt_array($ch, $options); - - // Execute the request and close the connection. - $content = curl_exec($ch); - - // Check if the content is a string. If it is not, it must be an error. - if (!\is_string($content)) { - $message = curl_error($ch); - - if (empty($message)) { - // Error but nothing from cURL? Create our own - $message = 'No HTTP response received'; - } - - throw new \RuntimeException($message); - } - - // Get the request information. - $info = curl_getinfo($ch); - - $response = $this->getResponse($content, $info); - - // Manually follow redirects if server doesn't allow to follow location using curl - if ($response->getStatusCode() >= 301 && $response->getStatusCode() < 400 && isset($response->getHeaders()['Location']) && (bool) $this->getOption('follow_location', true)) { - $redirect_uri = new Uri($response->getHeaders()['Location'][0]); - - if (\in_array($redirect_uri->getScheme(), ['file', 'scp'])) { - throw new \RuntimeException('Curl redirect cannot be used in file or scp requests.'); - } - - $response = $this->request($method, $redirect_uri, $data, $headers, $timeout, $userAgent); - } - - return $response; - } - - /** - * Method to get a response object from a server response. - * - * @param string $content The complete server response, including headers - * as a string if the response has no errors. - * @param array $info The cURL request information. - * - * @return Response - * - * @throws InvalidResponseCodeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Curl::getResponse() instead - */ - protected function getResponse($content, $info) - { - // Try to get header size - if (isset($info['header_size'])) { - $headerString = trim(substr($content, 0, $info['header_size'])); - $headerArray = explode("\r\n\r\n", $headerString); - - // Get the last set of response headers as an array. - $headers = explode("\r\n", array_pop($headerArray)); - - // Set the body for the response. - $body = substr($content, $info['header_size']); - } else { - // Fallback and try to guess header count by redirect count - // Get the number of redirects that occurred. - $redirects = $info['redirect_count'] ?? 0; - - /* - * Split the response into headers and body. If cURL encountered redirects, the headers for the redirected requests will - * also be included. So we split the response into header + body + the number of redirects and only use the last two - * sections which should be the last set of headers and the actual body. - */ - $response = explode("\r\n\r\n", $content, 2 + $redirects); - - // Set the body for the response. - $body = array_pop($response); - - // Get the last set of response headers as an array. - $headers = explode("\r\n", array_pop($response)); - } - - // Get the response code from the first offset of the response headers. - preg_match('/[0-9]{3}/', array_shift($headers), $matches); - - $code = \count($matches) ? $matches[0] : null; - - if (!is_numeric($code)) { - // No valid response code was detected. - throw new InvalidResponseCodeException('No HTTP response code found.'); - } - - $statusCode = (int) $code; - $verifiedHeaders = $this->processHeaders($headers); - - $streamInterface = new StreamResponse('php://memory', 'rw'); - $streamInterface->write($body); - - return new Response($streamInterface, $statusCode, $verifiedHeaders); - } - - /** - * Method to check if HTTP transport cURL is available for use - * - * @return boolean true if available, else false - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Curl::isSupported() instead - */ - public static function isSupported() - { - return \function_exists('curl_version') && curl_version(); - } - - /** - * Check if redirects are allowed - * - * @return boolean - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Curl::redirectsAllowed() instead - */ - private function redirectsAllowed() - { - $curlVersion = curl_version(); - - // If open_basedir is enabled we also need to check if libcurl version is 7.19.4 or higher - if (!\ini_get('open_basedir') || version_compare($curlVersion['version'], '7.19.4', '>=')) { - return true; - } - - return false; - } -} diff --git a/libraries/src/Http/Transport/SocketTransport.php b/libraries/src/Http/Transport/SocketTransport.php deleted file mode 100644 index 35000cd03419c..0000000000000 --- a/libraries/src/Http/Transport/SocketTransport.php +++ /dev/null @@ -1,294 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http\Transport; - -use Joomla\CMS\Factory; -use Joomla\CMS\Http\Response; -use Joomla\CMS\Http\TransportInterface; -use Joomla\CMS\Uri\Uri; -use Joomla\Http\AbstractTransport; -use Joomla\Http\Exception\InvalidResponseCodeException; -use Joomla\Uri\UriInterface; -use Laminas\Diactoros\Stream as StreamResponse; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP transport class for using sockets directly. - * - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Socket instead - */ -class SocketTransport extends AbstractTransport implements TransportInterface -{ - /** - * @var array Reusable socket connections. - * @since 1.7.3 - */ - protected $connections; - - /** - * Send a request to the server and return a Response object with the response. - * - * @param string $method The HTTP method for sending the request. - * @param UriInterface $uri The URI to the resource to request. - * @param mixed $data Either an associative array or a string to be sent with the request. - * @param array $headers An array of request headers to send with the request. - * @param integer $timeout Read timeout in seconds. - * @param string $userAgent The optional user agent string to send with the request. - * - * @return Response - * - * @throws \RuntimeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Socket::request() instead - */ - public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) - { - $connection = $this->connect($uri, $timeout); - - // Make sure the connection is alive and valid. - if (\is_resource($connection)) { - // Make sure the connection has not timed out. - $meta = stream_get_meta_data($connection); - - if ($meta['timed_out']) { - throw new \RuntimeException('Server connection timed out.'); - } - } else { - throw new \RuntimeException('Not connected to server.'); - } - - // Get the request path from the URI object. - $path = $uri->toString(['path', 'query']); - - // If we have data to send make sure our request is setup for it. - if (!empty($data)) { - // If the data is not a scalar value encode it to be sent with the request. - if (!\is_scalar($data)) { - $data = http_build_query($data); - } - - if (!isset($headers['Content-Type'])) { - $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; - } - - // Add the relevant headers. - $headers['Content-Length'] = \strlen($data); - } - - // Build the request payload. - $request = []; - $request[] = strtoupper($method) . ' ' . ((empty($path)) ? '/' : $path) . ' HTTP/1.0'; - $request[] = 'Host: ' . $uri->getHost(); - - // If an explicit user agent is given use it. - if (isset($userAgent)) { - $headers['User-Agent'] = $userAgent; - } - - // If there are custom headers to send add them to the request payload. - if (\is_array($headers)) { - foreach ($headers as $k => $v) { - if (\is_array($v)) { - foreach ($v as $value) { - $request[] = "$k: $value"; - } - } else { - $request[] = "$k: $v"; - } - } - } - - // Set any custom transport options - foreach ($this->getOption('transport.socket', []) as $value) { - $request[] = $value; - } - - // If we have data to send add it to the request payload. - if (!empty($data)) { - $request[] = null; - $request[] = $data; - } - - // Authentication, if needed - if ($this->getOption('userauth') && $this->getOption('passwordauth')) { - $request[] = 'Authorization: Basic ' . base64_encode($this->getOption('userauth') . ':' . $this->getOption('passwordauth')); - } - - // Send the request to the server. - fwrite($connection, implode("\r\n", $request) . "\r\n\r\n"); - - // Get the response data from the server. - $content = ''; - - while (!feof($connection)) { - $content .= fgets($connection, 4096); - } - - $content = $this->getResponse($content); - - // Follow Http redirects - if ($content->getStatusCode() >= 301 && $content->getStatusCode() < 400 && isset($content->getHeaders()['Location'][0])) { - return $this->request($method, new Uri($content->getHeaders()['Location'][0]), $data, $headers, $timeout, $userAgent); - } - - return $content; - } - - /** - * Method to get a response object from a server response. - * - * @param string $content The complete server response, including headers. - * - * @return Response - * - * @throws InvalidResponseCodeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Socket::getResponse() instead - */ - protected function getResponse($content) - { - if (empty($content)) { - throw new \UnexpectedValueException('No content in response.'); - } - - // Split the response into headers and body. - $response = explode("\r\n\r\n", $content, 2); - - // Get the response headers as an array. - $headers = explode("\r\n", $response[0]); - - // Set the body for the response. - $body = empty($response[1]) ? '' : $response[1]; - - // Get the response code from the first offset of the response headers. - preg_match('/[0-9]{3}/', array_shift($headers), $matches); - $code = $matches[0]; - - if (!is_numeric($code)) { - // No valid response code was detected. - throw new InvalidResponseCodeException('No HTTP response code found.'); - } - - $statusCode = (int) $code; - $verifiedHeaders = $this->processHeaders($headers); - - $streamInterface = new StreamResponse('php://memory', 'rw'); - $streamInterface->write($body); - - return new Response($streamInterface, $statusCode, $verifiedHeaders); - } - - /** - * Method to connect to a server and get the resource. - * - * @param UriInterface $uri The URI to connect with. - * @param integer $timeout Read timeout in seconds. - * - * @return resource Socket connection resource. - * - * @throws \RuntimeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Socket::connect() instead - */ - protected function connect(UriInterface $uri, $timeout = null) - { - $errno = null; - $err = null; - - // Get the host from the uri. - $host = ($uri->isSsl()) ? 'ssl://' . $uri->getHost() : $uri->getHost(); - - // If the port is not explicitly set in the URI detect it. - if (!$uri->getPort()) { - $port = ($uri->getScheme() === 'https') ? 443 : 80; - } else { - // Use the set port. - $port = $uri->getPort(); - } - - // Build the connection key for resource memory caching. - $key = md5($host . $port); - - // If the connection already exists, use it. - if (!empty($this->connections[$key]) && \is_resource($this->connections[$key])) { - // Connection reached EOF, cannot be used anymore - $meta = stream_get_meta_data($this->connections[$key]); - - if ($meta['eof']) { - if (!fclose($this->connections[$key])) { - throw new \RuntimeException('Cannot close connection'); - } - } elseif (!$meta['timed_out']) { - // Make sure the connection has not timed out. - return $this->connections[$key]; - } - } - - if (!is_numeric($timeout)) { - $timeout = \ini_get('default_socket_timeout'); - } - - // Capture PHP errors - // PHP sends a warning if the uri does not exist; we silence it and throw an exception instead. - set_error_handler(static function ($errno, $err) { - throw new \Exception($err); - }, \E_WARNING); - - try { - // Attempt to connect to the server - $connection = fsockopen($host, $port, $errno, $err, $timeout); - - if (!$connection) { - // Error but nothing from php? Create our own - if (!$err) { - $err = \sprintf('Could not connect to host: %s:%s', $host, $port); - } - - throw new \Exception($err); - } - } catch (\Exception $e) { - throw new \RuntimeException($e->getMessage()); - } finally { - restore_error_handler(); - } - - // Since the connection was successful let's store it in case we need to use it later. - $this->connections[$key] = $connection; - - // If an explicit timeout is set, set it. - if (isset($timeout)) { - stream_set_timeout($this->connections[$key], (int) $timeout); - } - - return $this->connections[$key]; - } - - /** - * Method to check if http transport socket available for use - * - * @return boolean True if available else false - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Socket::isSupported() instead - */ - public static function isSupported() - { - return \function_exists('fsockopen') && \is_callable('fsockopen') && !Factory::getApplication()->get('proxy_enable'); - } -} diff --git a/libraries/src/Http/Transport/StreamTransport.php b/libraries/src/Http/Transport/StreamTransport.php deleted file mode 100644 index 3289de98e5c6b..0000000000000 --- a/libraries/src/Http/Transport/StreamTransport.php +++ /dev/null @@ -1,241 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http\Transport; - -use Composer\CaBundle\CaBundle; -use Joomla\CMS\Factory; -use Joomla\CMS\Http\Response; -use Joomla\CMS\Http\TransportInterface; -use Joomla\Http\AbstractTransport; -use Joomla\Http\Exception\InvalidResponseCodeException; -use Joomla\Uri\Uri; -use Joomla\Uri\UriInterface; -use Laminas\Diactoros\Stream as StreamResponse; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP transport class for using PHP streams. - * - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Stream instead - */ -class StreamTransport extends AbstractTransport implements TransportInterface -{ - /** - * Send a request to the server and return a Response object with the response. - * - * @param string $method The HTTP method for sending the request. - * @param UriInterface $uri The URI to the resource to request. - * @param mixed $data Either an associative array or a string to be sent with the request. - * @param array $headers An array of request headers to send with the request. - * @param integer $timeout Read timeout in seconds. - * @param string $userAgent The optional user agent string to send with the request. - * - * @return Response - * - * @throws \RuntimeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Stream::request() instead - */ - public function request($method, UriInterface $uri, $data = null, array $headers = [], $timeout = null, $userAgent = null) - { - // Create the stream context options array with the required method offset. - $options = ['method' => strtoupper($method)]; - - // If data exists let's encode it and make sure our Content-Type header is set. - if (isset($data)) { - // If the data is a scalar value simply add it to the stream context options. - if (\is_scalar($data)) { - $options['content'] = $data; - } else { - // Otherwise we need to encode the value first. - $options['content'] = http_build_query($data); - } - - if (!isset($headers['Content-Type'])) { - $headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; - } - - // Add the relevant headers. - $headers['Content-Length'] = \strlen($options['content']); - } - - // If an explicit timeout is given user it. - if (isset($timeout)) { - $options['timeout'] = (int) $timeout; - } - - // If an explicit user agent is given use it. - if (isset($userAgent)) { - $options['user_agent'] = $userAgent; - } - - // Ignore HTTP errors so that we can capture them. - $options['ignore_errors'] = 1; - - // Follow redirects. - $options['follow_location'] = (int) $this->getOption('follow_location', 1); - - // Set any custom transport options - foreach ($this->getOption('transport.stream', []) as $key => $value) { - $options[$key] = $value; - } - - // Add the proxy configuration, if any. - $app = Factory::getApplication(); - - if ($app->get('proxy_enable')) { - $options['proxy'] = $app->get('proxy_host') . ':' . $app->get('proxy_port'); - $options['request_fulluri'] = true; - - // Put any required authorization into the headers array to be handled later - // @todo: do we need to support any auth type other than Basic? - if ($user = $app->get('proxy_user')) { - $auth = base64_encode($app->get('proxy_user') . ':' . $app->get('proxy_pass')); - - $headers['Proxy-Authorization'] = 'Basic ' . $auth; - } - } - - // Build the headers string for the request. - $headerEntries = []; - - if (isset($headers)) { - foreach ($headers as $key => $value) { - if (\is_array($value)) { - foreach ($value as $header) { - $headerEntries[] = "$key: $header"; - } - } else { - $headerEntries[] = "$key: $value"; - } - } - - // Add the headers string into the stream context options array. - $options['header'] = implode("\r\n", $headerEntries); - } - - // Get the current context options. - $contextOptions = stream_context_get_options(stream_context_get_default()); - - // Add our options to the current ones, if any. - $contextOptions['http'] = isset($contextOptions['http']) ? array_merge($contextOptions['http'], $options) : $options; - - // Create the stream context for the request. - $context = stream_context_create( - [ - 'http' => $options, - 'ssl' => [ - 'verify_peer' => true, - 'cafile' => $this->getOption('stream.certpath', CaBundle::getBundledCaBundlePath()), - 'verify_depth' => 5, - 'verify_peer_name' => true, - ], - ] - ); - - // Authentication, if needed - if ($uri instanceof Uri && $this->getOption('userauth') && $this->getOption('passwordauth')) { - $uri->setUser($this->getOption('userauth')); - $uri->setPass($this->getOption('passwordauth')); - } - - // Capture PHP errors - // PHP sends a warning if the uri does not exist; we silence it and throw an exception instead. - set_error_handler(static function ($errno, $err) { - throw new \Exception($err); - }, \E_WARNING); - - try { - // Open the stream for reading. - $stream = fopen((string) $uri, 'r', false, $context); - - if (!$stream) { - // Error but nothing from php? Create our own - throw new \Exception(\sprintf('Could not connect to resource: %s', $uri)); - } - } catch (\Exception $e) { - throw new \RuntimeException($e->getMessage()); - } finally { - restore_error_handler(); - } - - // Get the metadata for the stream, including response headers. - $metadata = stream_get_meta_data($stream); - - // Get the contents from the stream. - $content = stream_get_contents($stream); - - // Close the stream. - fclose($stream); - - if (isset($metadata['wrapper_data']['headers'])) { - $headers = $metadata['wrapper_data']['headers']; - } elseif (isset($metadata['wrapper_data'])) { - $headers = $metadata['wrapper_data']; - } else { - $headers = []; - } - - return $this->getResponse($headers, $content); - } - - /** - * Method to get a response object from a server response. - * - * @param array $headers The response headers as an array. - * @param string $body The response body as a string. - * - * @return Response - * - * @throws InvalidResponseCodeException - * @since 1.7.3 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Stream::getResponse() instead - */ - protected function getResponse(array $headers, $body) - { - // Get the response code from the first offset of the response headers. - preg_match('/[0-9]{3}/', array_shift($headers), $matches); - $code = $matches[0]; - - if (!is_numeric($code)) { - // No valid response code was detected. - throw new InvalidResponseCodeException('No HTTP response code found.'); - } - - $statusCode = (int) $code; - $verifiedHeaders = $this->processHeaders($headers); - - $streamInterface = new StreamResponse('php://memory', 'rw'); - $streamInterface->write($body); - - return new Response($streamInterface, $statusCode, $verifiedHeaders); - } - - /** - * Method to check if http transport stream available for use - * - * @return boolean true if available else false - * - * @since 3.0.0 - * @deprecated 6.0.0 will be removed in 7.0 - * Use Joomla\Http\Transport\Stream::isSupported() instead - */ - public static function isSupported() - { - return \function_exists('fopen') && \is_callable('fopen') && \ini_get('allow_url_fopen'); - } -} diff --git a/libraries/src/Http/TransportInterface.php b/libraries/src/Http/TransportInterface.php deleted file mode 100644 index c9246ecabb629..0000000000000 --- a/libraries/src/Http/TransportInterface.php +++ /dev/null @@ -1,28 +0,0 @@ - - * @license GNU General Public License version 2 or later; see LICENSE.txt - */ - -namespace Joomla\CMS\Http; - -use Joomla\Http\TransportInterface as FrameworkTransportInterface; - -// phpcs:disable PSR1.Files.SideEffects -\defined('_JEXEC') or die; -// phpcs:enable PSR1.Files.SideEffects - -/** - * HTTP transport class interface. - * - * @since 1.7.3 - * - * @deprecated 4.0 will be removed in 7.0 - * Implement Joomla\Http\TransportInterface instead - */ -interface TransportInterface extends FrameworkTransportInterface -{ -} From ba4cb37ae9cd502e5d1d4dad07627ef69f5f684a Mon Sep 17 00:00:00 2001 From: Hannes Papenberg Date: Fri, 17 Apr 2026 21:36:38 +0200 Subject: [PATCH 2/3] Fix class name in tests --- tests/Unit/Libraries/Tuf/HttpLoaderTest.php | 2 +- .../Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Libraries/Tuf/HttpLoaderTest.php b/tests/Unit/Libraries/Tuf/HttpLoaderTest.php index 06ebd9dda56da..4414862ea8da0 100644 --- a/tests/Unit/Libraries/Tuf/HttpLoaderTest.php +++ b/tests/Unit/Libraries/Tuf/HttpLoaderTest.php @@ -10,7 +10,7 @@ namespace Joomla\Tests\Unit\Libraries\Tuf; -use Joomla\CMS\Http\Http; +use Joomla\Http\Http; use Joomla\CMS\TUF\HttpLoader; use Joomla\Http\Response; use Joomla\Tests\Unit\UnitTestCase; diff --git a/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php b/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php index 614c326930d56..f557b87b9e0ae 100644 --- a/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php +++ b/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php @@ -11,7 +11,7 @@ namespace Joomla\Tests\Unit\Plugin\Task\Requests\Extension; use Joomla\CMS\Application\CMSApplicationInterface; -use Joomla\CMS\Http\Http; +use Joomla\Http\Http; use Joomla\CMS\Language\Language; use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; use Joomla\Component\Scheduler\Administrator\Task\Status; From 7de342fd9455cbfddc6eb0b29e320f36f1339b09 Mon Sep 17 00:00:00 2001 From: Hannes Papenberg Date: Fri, 17 Apr 2026 21:45:50 +0200 Subject: [PATCH 3/3] Codestyle --- tests/Unit/Libraries/Tuf/HttpLoaderTest.php | 2 +- .../Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Unit/Libraries/Tuf/HttpLoaderTest.php b/tests/Unit/Libraries/Tuf/HttpLoaderTest.php index 4414862ea8da0..6663eef1de695 100644 --- a/tests/Unit/Libraries/Tuf/HttpLoaderTest.php +++ b/tests/Unit/Libraries/Tuf/HttpLoaderTest.php @@ -10,8 +10,8 @@ namespace Joomla\Tests\Unit\Libraries\Tuf; -use Joomla\Http\Http; use Joomla\CMS\TUF\HttpLoader; +use Joomla\Http\Http; use Joomla\Http\Response; use Joomla\Tests\Unit\UnitTestCase; use Laminas\Diactoros\Stream; diff --git a/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php b/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php index f557b87b9e0ae..6123475e2fc11 100644 --- a/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php +++ b/tests/Unit/Plugin/Task/Requests/Extension/RequestsPluginTest.php @@ -11,12 +11,12 @@ namespace Joomla\Tests\Unit\Plugin\Task\Requests\Extension; use Joomla\CMS\Application\CMSApplicationInterface; -use Joomla\Http\Http; use Joomla\CMS\Language\Language; use Joomla\Component\Scheduler\Administrator\Event\ExecuteTaskEvent; use Joomla\Component\Scheduler\Administrator\Task\Status; use Joomla\Component\Scheduler\Administrator\Task\Task; use Joomla\Filesystem\Folder; +use Joomla\Http\Http; use Joomla\Http\HttpFactory; use Joomla\Http\Response; use Joomla\Http\TransportInterface;