W3TC_POWERED_BY ), $args ); return wp_remote_request( $w3tc_url, $args ); } /** * Sends HTTP GET request * * @param string $w3tc_url URL to send the GET request to. * @param array $args Arguments for the GET request. * * @return array|\WP_Error Returns the response data or a \WP_Error object on failure. */ public static function get( $w3tc_url, $args = array() ) { // Merge the provided arguments with the GET method. $args = array_merge( $args, array( 'method' => 'GET' ) ); // Use the request method to send the GET request. return self::request( $w3tc_url, $args ); } /** * Downloads URL into a file * * @param string $w3tc_url URL to download. * @param string $w3tc_file Path to the file where the content will be saved. * @param array $args Optional. Arguments for the download request. * * @return bool Returns true on success, or false on failure. */ public static function download( $w3tc_url, $w3tc_file, $args = array() ) { $w3tc_url = Util_Url::normalize_protocol_relative_url( $w3tc_url ); if ( '' === $w3tc_url ) { return false; } $public_only = ! empty( $args['w3tc_public_only'] ); $https_only = ! empty( $args['w3tc_https_only'] ); $same_host = ! empty( $args['w3tc_same_host'] ); $max_size = isset( $args['w3tc_max_response_size'] ) ? (int) $args['w3tc_max_response_size'] : 0; $validator = isset( $args['w3tc_response_validator'] ) && \is_callable( $args['w3tc_response_validator'] ) ? $args['w3tc_response_validator'] : null; unset( $args['w3tc_public_only'], $args['w3tc_https_only'], $args['w3tc_same_host'], $args['w3tc_max_response_size'], $args['w3tc_response_validator'] ); $original_host = self::normalized_url_host( $w3tc_url ); if ( $same_host && '' === $original_host ) { return false; } /** * Follow Location responses manually so each hop is evaluated * against the requested policy before the next request. */ $max_redirects = 5; for ( $hop = 0; $hop <= $max_redirects; $hop++ ) { if ( $https_only && 'https' !== \wp_parse_url( $w3tc_url, PHP_URL_SCHEME ) ) { return false; } if ( $same_host && self::normalized_url_host( $w3tc_url ) !== $original_host ) { return false; } $allowed = $public_only ? Util_Url::is_public_host( $w3tc_url ) : Util_Url::is_allowed_outbound_url( $w3tc_url ); if ( ! $allowed ) { return false; } $request_args = \array_merge( $args, array( 'redirection' => 0, ) ); if ( $max_size > 0 ) { $request_args['limit_response_size'] = $max_size + 1; } $response = self::get( $w3tc_url, $request_args ); if ( \is_wp_error( $response ) ) { return false; } $code = isset( $response['response']['code'] ) ? (int) $response['response']['code'] : 0; if ( $code >= 300 && $code < 400 ) { $location = self::response_location_header( $response ); if ( '' === $location ) { return false; } $w3tc_url = self::resolve_redirect_url( $w3tc_url, $location ); if ( '' === $w3tc_url ) { return false; } continue; } if ( 200 !== $code ) { return false; } $body = isset( $response['body'] ) && \is_string( $response['body'] ) ? $response['body'] : ''; if ( ( $max_size > 0 && \strlen( $body ) > $max_size ) || ( $validator && ! \call_user_func( $validator, $response ) ) ) { return false; } return (bool) @file_put_contents( $w3tc_file, $body ); } return false; } /** * Returns a normalized host for redirect comparisons. * * @since 2.10.7 * * @param string $url URL to inspect. * * @return string */ private static function normalized_url_host( $url ) { $host = \wp_parse_url( $url, PHP_URL_HOST ); return \is_string( $host ) ? \strtolower( \rtrim( $host, '.' ) ) : ''; } /** * Reads the Location header from a wp_remote_* response. * * @since 2.10.6 * * @param array $response HTTP API response. * * @return string */ private static function response_location_header( $response ) { if ( empty( $response['headers'] ) ) { return ''; } $headers = $response['headers']; if ( \is_array( $headers ) && isset( $headers['location'] ) ) { $location = $headers['location']; } elseif ( \is_object( $headers ) && isset( $headers['location'] ) ) { $location = $headers['location']; } else { return ''; } if ( \is_array( $location ) ) { $location = \reset( $location ); } return \is_string( $location ) ? \trim( $location ) : ''; } /** * Resolves a redirect Location against the current request URL. * * @since 2.10.6 * * @param string $current Absolute URL of the current hop. * @param string $location Location header value. * * @return string Absolute URL, or empty string when resolution fails. */ private static function resolve_redirect_url( $current, $location ) { if ( ! \is_string( $location ) || '' === $location ) { return ''; } $parts = \wp_parse_url( $current ); if ( ! \is_array( $parts ) || empty( $parts['scheme'] ) || empty( $parts['host'] ) ) { return ''; } // A protocol-relative Location keeps the scheme of the hop that returned it. if ( 0 === \strpos( $location, '//' ) ) { $location = $parts['scheme'] . ':' . $location; } if ( ! \class_exists( '\WP_Http' ) ) { return ''; } $resolved = \WP_Http::make_absolute_url( $location, $current ); return Util_Url::is_valid_http_scheme( $resolved ) ? $resolved : ''; } /** * Returns upload info * * @return array|false Returns an array containing upload directory information or false on error. */ public static function upload_info() { // Use a static variable to cache the upload info, avoiding repeated calls to wp_upload_dir(). static $upload_info = null; // If the upload info has not been cached yet, retrieve it. if ( null === $upload_info ) { // Get the WordPress upload directory information. $upload_info = Util_Environment::wp_upload_dir(); // Check if there is no error in the upload directory information. if ( empty( $upload_info['error'] ) ) { // Parse the base URL of the upload directory to extract its components. $parse_url = @parse_url( $upload_info['baseurl'] ); // If parsing the URL was successful, extract the path component. if ( $parse_url ) { // Trim any leading or trailing slashes from the path component. $baseurlpath = ( ! empty( $parse_url['path'] ) ? trim( $parse_url['path'], '/' ) : '' ); } else { // If parsing failed, default to 'wp-content/uploads' as the base URL path. $baseurlpath = 'wp-content/uploads'; } // Add the base URL path to the upload info array, prefixed and suffixed with slashes. $upload_info['baseurlpath'] = '/' . $baseurlpath . '/'; } else { // If there was an error, set the upload info to false. $upload_info = false; } } // Return the cached upload info or false if there was an error. return $upload_info; } /** * Test the time to first byte (TTFB). * * @param string $w3tc_url URL to test. * @param bool $nocache Whether or not to request no cache response, by sending a Cache-Control header. * * @return float|false Time in seconds until the first byte is about to be transferred or false on error. */ public static function ttfb( $w3tc_url, $nocache = false ) { $ch = curl_init( esc_url( $w3tc_url ) ); $pass = (bool) $ch; $ttfb = false; $opts = array( CURLOPT_FORBID_REUSE => 1, CURLOPT_FRESH_CONNECT => 1, CURLOPT_HEADER => 0, CURLOPT_RETURNTRANSFER => 1, CURLOPT_FOLLOWLOCATION => 1, /** * SSL peer verification is the cURL default; the legacy * `CURLOPT_SSL_VERIFYPEER => false` was removed (// TTFB leg). Note: this function calls raw cURL directly * (not the WordPress HTTP API), so the WP `http_request_args` * / `https_ssl_verify` filters do NOT apply here. Operators * with broken CA bundles need to fix the bundle itself; a * TTFB probe MUST NOT silently accept a forged cert. */ CURLOPT_USERAGENT => 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), ); if ( $nocache ) { $opts[ CURLOPT_HTTPHEADER ] = array( 'Cache-Control: no-cache', 'Pragma: no-cache', ); $opts[ CURLOPT_URL ] = add_query_arg( 'time', microtime( true ), $w3tc_url ); } if ( $ch ) { $pass = curl_setopt_array( $ch, $opts ); // phpcs:ignore WordPress.WP.AlternativeFunctions } if ( $pass ) { $pass = (bool) curl_exec( $ch ); // phpcs:ignore WordPress.WP.AlternativeFunctions } if ( $pass ) { $ttfb = curl_getinfo( $ch, CURLINFO_STARTTRANSFER_TIME ); // phpcs:ignore WordPress.WP.AlternativeFunctions } if ( $ch ) { curl_close( $ch ); // phpcs:ignore WordPress.WP.AlternativeFunctions } return $ttfb; } /** * Generate unique md5 value based on domain. * * @return string Returns an MD5 hash generated from the site's network home URL. */ public static function generate_site_id() { // Generate an MD5 hash of the network home URL to create a unique site identifier. return md5( network_home_url() ); } }