From 08fd1409752d4cc51d860ed0355f1a2825f91a46 Mon Sep 17 00:00:00 2001 From: jaswsinc Date: Thu, 10 Nov 2016 00:32:29 -0900 Subject: [PATCH] Completing work on ignored GET vars. See: websharks/comet-cache#639 --- src/includes/classes/AbsBase.php | 14 +- src/includes/classes/AbsBaseAp.php | 1 + src/includes/traits/Ac/ClientSideUtils.php | 29 ++- src/includes/traits/Ac/ObUtils.php | 14 +- src/includes/traits/Shared/CachePathUtils.php | 57 +++-- .../traits/Shared/ConditionalUtils.php | 210 ++++++++---------- 6 files changed, 153 insertions(+), 172 deletions(-) diff --git a/src/includes/classes/AbsBase.php b/src/includes/classes/AbsBase.php index 357bcf26..791b8ade 100644 --- a/src/includes/classes/AbsBase.php +++ b/src/includes/classes/AbsBase.php @@ -11,35 +11,35 @@ abstract class AbsBase implements Interfaces\Shared\NcDebugConsts, Interfaces\Shared\CachePathConsts { /** - * @type null|plugin Plugin reference. + * @var null|plugin Plugin reference. * * @since 150422 Rewrite. */ protected $plugin; /** - * @type array Instance cache. + * @var array Instance cache. * * @since 150422 Rewrite. */ protected $cache = []; /** - * @type array Global static cache ref. + * @var array Global static cache ref. * * @since 150422 Rewrite. */ protected $static = []; /** - * @type array Global static cache. + * @var array Global static cache. * * @since 150422 Rewrite. */ protected static $global_static = []; /** - * @type \stdClass Overload properties. + * @var \stdClass Overload properties. * * @since 150422 Rewrite. */ @@ -185,7 +185,7 @@ public function &cacheKey($function, $args = [], $___prop = 'cache') switch (gettype($_arg)) { case 'integer': - $_key = (integer) $_arg; + $_key = (int) $_arg; break; // Break switch handler. case 'double': @@ -194,7 +194,7 @@ public function &cacheKey($function, $args = [], $___prop = 'cache') break; // Break switch handler. case 'boolean': - $_key = (integer) $_arg; + $_key = (int) $_arg; break; // Break switch handler. case 'array': diff --git a/src/includes/classes/AbsBaseAp.php b/src/includes/classes/AbsBaseAp.php index 8739253c..46b6b093 100644 --- a/src/includes/classes/AbsBaseAp.php +++ b/src/includes/classes/AbsBaseAp.php @@ -11,6 +11,7 @@ abstract class AbsBaseAp extends AbsBase { /*[.build.php-auto-generate-use-Traits]*/ + use Traits\Shared\ArrayUtils; use Traits\Shared\BlogUtils; use Traits\Shared\CacheDirUtils; use Traits\Shared\CacheLockUtils; diff --git a/src/includes/traits/Ac/ClientSideUtils.php b/src/includes/traits/Ac/ClientSideUtils.php index 09242694..6d8a0066 100644 --- a/src/includes/traits/Ac/ClientSideUtils.php +++ b/src/includes/traits/Ac/ClientSideUtils.php @@ -6,25 +6,23 @@ trait ClientSideUtils { /** - * Sends no-cache headers (if applicable). + * Sends no-cache headers. * - * @since 150422 Rewrite. Enhanced/altered 151220. + * @since 150422 Rewrite. + * @since 151220 Enhancing. + * @since 16xxxx Enhancing. */ public function maybeStopBrowserCaching() { - if (!defined('COMET_CACHE_ALLOW_CLIENT_SIDE_CACHE')) { - return $this->sendNoCacheHeaders(); // Upgrading from <= v160521, before we renamed this constant. Return default. - } - - switch ((bool) COMET_CACHE_ALLOW_CLIENT_SIDE_CACHE) { + $short_name_lc = mb_strtolower(SHORT_NAME); // Needed below. - case true: // If global config allows, check exclusions. + switch (defined('COMET_CACHE_ALLOW_CLIENT_SIDE_CACHE') ? (bool) COMET_CACHE_ALLOW_CLIENT_SIDE_CACHE : false) { + case true: // If global config allows; check exclusions. - if (isset($_GET[mb_strtolower(SHORT_NAME).'ABC'])) { - if (!filter_var($_GET[mb_strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) { + if (isset($_GET[$short_name_lc.'ABC'])) { + if (!filter_var($_GET[$short_name_lc.'ABC'], FILTER_VALIDATE_BOOLEAN)) { return $this->sendNoCacheHeaders(); // Disallow. - } // Else, allow client-side caching; because `ABC` is a true-ish value. - // ↑ Note that exclusion patterns are ignored in this case, in favor of `ABC`. + } // Else, allow client-side caching because `ABC` is a true-ish value. } elseif (COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS && (empty($_SERVER['REQUEST_URI']) || preg_match(COMET_CACHE_EXCLUDE_CLIENT_SIDE_URIS, $_SERVER['REQUEST_URI']))) { return $this->sendNoCacheHeaders(); // Disallow. } @@ -32,11 +30,10 @@ public function maybeStopBrowserCaching() case false: // Global config disallows; check inclusions. - if (isset($_GET[mb_strtolower(SHORT_NAME).'ABC'])) { - if (filter_var($_GET[mb_strtolower(SHORT_NAME).'ABC'], FILTER_VALIDATE_BOOLEAN)) { + if (isset($_GET[$short_name_lc.'ABC'])) { + if (filter_var($_GET[$short_name_lc.'ABC'], FILTER_VALIDATE_BOOLEAN)) { return; // Allow, because `ABC` is a false-ish value. - } // Else, disallow client-side caching; because `ABC` is a true-ish value. - // ↑ Note that inclusion patterns are ignored in this case, in favor of `ABC`. + } // Else, disallow client-side caching because `ABC` is a true-ish value. } return $this->sendNoCacheHeaders(); // Disallow; default behavior in this mode. } diff --git a/src/includes/traits/Ac/ObUtils.php b/src/includes/traits/Ac/ObUtils.php index cebf52b3..aaf0b2a2 100644 --- a/src/includes/traits/Ac/ObUtils.php +++ b/src/includes/traits/Ac/ObUtils.php @@ -120,7 +120,7 @@ public function maybeStartOutputBuffering() if (isset($_SERVER['COMET_CACHE_ALLOWED']) && !$_SERVER['COMET_CACHE_ALLOWED']) { return $this->maybeSetDebugInfo($this::NC_DEBUG_COMET_CACHE_ALLOWED_SERVER_VAR); } - if (defined('DONOTCACHEPAGE')) { + if (defined('DONOTCACHEPAGE')) { // Common to most WP cache plugins. return $this->maybeSetDebugInfo($this::NC_DEBUG_DONOTCACHEPAGE_CONSTANT); } if (isset($_SERVER['DONOTCACHEPAGE'])) { @@ -135,7 +135,7 @@ public function maybeStartOutputBuffering() if (isset($_SERVER['SERVER_ADDR']) && $this->currentIp() === $_SERVER['SERVER_ADDR']) { if ((!IS_PRO || !$this->isAutoCacheEngine()) && !$this->isLocalhost()) { return $this->maybeSetDebugInfo($this::NC_DEBUG_SELF_SERVE_REQUEST); - } + } // Don't trip on requests by the auto-cache engine. } if (!COMET_CACHE_FEEDS_ENABLE && $this->isFeed()) { return $this->maybeSetDebugInfo($this::NC_DEBUG_FEED_REQUEST); @@ -155,7 +155,7 @@ public function maybeStartOutputBuffering() if (!COMET_CACHE_GET_REQUESTS && $this->requestContainsUncacheableQueryVars()) { return $this->maybeSetDebugInfo($this::NC_DEBUG_GET_REQUEST_QUERIES); } - if (!empty($_REQUEST['preview'])) { + if (!empty($_REQUEST['preview'])) { // Don't cache previews under any circumstance. return $this->maybeSetDebugInfo($this::NC_DEBUG_PREVIEW); } if (COMET_CACHE_EXCLUDE_HOSTS && preg_match(COMET_CACHE_EXCLUDE_HOSTS, $_SERVER['HTTP_HOST'])) { @@ -167,21 +167,21 @@ public function maybeStartOutputBuffering() if (COMET_CACHE_EXCLUDE_AGENTS && !empty($_SERVER['HTTP_USER_AGENT']) && (!IS_PRO || !$this->isAutoCacheEngine())) { if (preg_match(COMET_CACHE_EXCLUDE_AGENTS, $_SERVER['HTTP_USER_AGENT'])) { return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_AGENTS); - } + } // Don't trip on requests by the auto-cache engine. } if (COMET_CACHE_EXCLUDE_REFS && !empty($_REQUEST['_wp_http_referer'])) { if (preg_match(COMET_CACHE_EXCLUDE_REFS, stripslashes($_REQUEST['_wp_http_referer']))) { return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_REFS); - } + } // This variable is set by WordPress core in some cases. } if (COMET_CACHE_EXCLUDE_REFS && !empty($_SERVER['HTTP_REFERER'])) { if (preg_match(COMET_CACHE_EXCLUDE_REFS, $_SERVER['HTTP_REFERER'])) { return $this->maybeSetDebugInfo($this::NC_DEBUG_EXCLUDED_REFS); - } + } // Based on the HTTP referrer in this case. } - $this->protocol = $this->isSsl() ? 'https://' : 'http://'; $this->host_token = $this->hostToken(); $this->host_base_dir_tokens = $this->hostBaseDirTokens(); + $this->protocol = $this->isSsl() ? 'https://' : 'http://'; $this->version_salt = ''; // Initialize the version salt. /*[pro strip-from="lite"]*/ // Fill the version salt in pro version. diff --git a/src/includes/traits/Shared/CachePathUtils.php b/src/includes/traits/Shared/CachePathUtils.php index 4c83e5bc..d471bcc7 100644 --- a/src/includes/traits/Shared/CachePathUtils.php +++ b/src/includes/traits/Shared/CachePathUtils.php @@ -6,26 +6,38 @@ trait CachePathUtils { /** - * Filter GET vars; e.g., remove those we ignore. + * Filter query vars; e.g., remove those we ignore. * * @since 16xxxx Adding support for ignored GET vars. + * @since 16xxxx Adding support for sorted query vars. * - * @param array $get_vars GET vars to filter. + * @param array $_vars Query vars to filter. * - * @return array Filtered GET vars. + * @return array Filtered query vars. */ - public function filterGetVars($get_vars) + public function filterQueryVars($_vars) { - $get_vars = (array) $get_vars; + $_vars = (array) $_vars; // Force array. + $cache_key = $_vars === $_GET ? md5(serialize($_vars)) : ''; - if (COMET_CACHE_IGNORE_GET_REQUEST_VARS) { - foreach ($get_vars as $_key => $_value) { - if (is_string($_key) && preg_match(COMET_CACHE_IGNORE_GET_REQUEST_VARS, $_key)) { - unset($get_vars[$_key]); // Ignore. - } - } // unset($_key, $_value); // Housekeeping. + if ($cache_key && ($vars = &$this->staticKey(__FUNCTION__, $cache_key)) !== null) { + return $vars; // Already cached this. } - return $this->ksortDeep($get_vars); + $vars = $_vars; // Copy. + $short_name_lc = mb_strtolower(SHORT_NAME); + $ignore_get_request_vars_regex = defined('COMET_CACHE_IGNORE_GET_REQUEST_VARS') ? COMET_CACHE_IGNORE_GET_REQUEST_VARS : ''; + + foreach ($vars as $_key => $_value) { + if (!is_string($_key)) { + continue; // Not applicable. + } elseif ($_key === $short_name_lc.'AC' || $_key === $short_name_lc.'ABC') { + unset($vars[$_key]); + } elseif ($ignore_get_request_vars_regex && preg_match($ignore_get_request_vars_regex, $_key)) { + unset($vars[$_key]); + } + } // unset($_key, $_value); // Housekeeping. + + return $vars = $vars ? $this->ksortDeep($vars) : []; } /** @@ -116,11 +128,8 @@ public function buildCachePath($url, $with_user_token = '', $with_version_salt = $is_url_domain_mapped = $is_multisite && $can_consider_domain_mapping && $this->domainMappingBlogId($url); $host_base_dir_tokens = $this->hostBaseDirTokens(false, $is_url_domain_mapped, !empty($url_parts['path']) ? $url_parts['path'] : '/'); - $is_a_multisite_base_dir = $is_multisite && $host_base_dir_tokens && $host_base_dir_tokens !== '/' // Check? - && mb_stripos(!empty($url_parts['path']) ? rtrim($url_parts['path'], '/').'/' : '/', $host_base_dir_tokens) === 0; - - $is_a_multisite_base_dir_root = $is_multisite && $is_a_multisite_base_dir // Save time by using the previous check here. - && strcasecmp(trim($host_base_dir_tokens, '/'), trim(!empty($url_parts['path']) ? $url_parts['path'] : '/', '/')) === 0; + $is_a_multisite_base_dir = $is_multisite && $host_base_dir_tokens && $host_base_dir_tokens !== '/' && mb_stripos(!empty($url_parts['path']) ? rtrim($url_parts['path'], '/').'/' : '/', $host_base_dir_tokens) === 0; + $is_a_multisite_base_dir_root = $is_multisite && $is_a_multisite_base_dir && strcasecmp(trim($host_base_dir_tokens, '/'), trim(!empty($url_parts['path']) ? $url_parts['path'] : '/', '/')) === 0; # Build and return the cache path. @@ -129,7 +138,6 @@ public function buildCachePath($url, $with_user_token = '', $with_version_salt = } if (!($flags & $this::CACHE_PATH_NO_HOST)) { $cache_path .= $url_parts['host'].'/'; - // Put multisite sub-roots into a host directory of their own. // e.g., `example-com[[-base]-child1]` instead of `example-com`. if ($is_a_multisite_base_dir && $host_base_dir_tokens && $host_base_dir_tokens !== '/') { @@ -168,15 +176,20 @@ public function buildCachePath($url, $with_user_token = '', $with_version_salt = $cache_path .= 'index/'; } } - if ($this->isExtensionLoaded('mbstring') && mb_check_encoding($cache_path, 'UTF-8')) { - $cache_path = mb_strtolower($cache_path, 'UTF-8'); - } $cache_path = str_replace('.', '-', mb_strtolower($cache_path)); if (!($flags & $this::CACHE_PATH_NO_QUV)) { if (!($flags & $this::CACHE_PATH_NO_QUERY)) { if (isset($url_parts['query']) && $url_parts['query'] !== '') { - $cache_path = rtrim($cache_path, '/').'.q/'.md5($url_parts['query']).'/'; + + // Support for ignored GET vars. + parse_str($url_parts['query'], $_query_vars); + $_query_vars = $this->filterQueryVars($_query_vars); + // ↑ Also sorts query vars for smarter caching. + + if ($_query_vars) { // If we have cacheable query vars. + $cache_path = rtrim($cache_path, '/').'.q/'.md5(serialize($_query_vars)).'/'; + } // unset($_query_vars); // Housekeeping. } } if (!($flags & $this::CACHE_PATH_NO_USER)) { diff --git a/src/includes/traits/Shared/ConditionalUtils.php b/src/includes/traits/Shared/ConditionalUtils.php index 79203bcd..b6e9f044 100644 --- a/src/includes/traits/Shared/ConditionalUtils.php +++ b/src/includes/traits/Shared/ConditionalUtils.php @@ -9,7 +9,7 @@ trait ConditionalUtils * PHP's language constructs. * * @var array PHP's language constructs. - * Keys are currently unimportant. Subject to change. + * @note Keys unimportant; subject to change. * * @since 160222 First documented version. */ @@ -36,7 +36,7 @@ trait ConditionalUtils * * @since 150821 Improving multisite compat. * - * @return bool `TRUE` if this is the AdvancedCache class. + * @return bool True if this is the AdvancedCache class. */ public function isAdvancedCache() { @@ -48,7 +48,7 @@ public function isAdvancedCache() * * @since 150821 Improving multisite compat. * - * @return bool `TRUE` if this is the Plugin class. + * @return bool True if this is the Plugin class. */ public function isPlugin() { @@ -56,101 +56,88 @@ public function isPlugin() } /** - * Is the current request method `POST`, `PUT` or `DELETE`? + * `POST`, `PUT`, `DELETE`? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @return bool `TRUE` if current request method is `POST`, `PUT` or `DELETE`. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if `POST`, `PUT`, `DELETE`. */ public function isPostPutDeleteRequest() { - if (!is_null($is = &$this->staticKey('isPostPutDeleteRequest'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } if (!empty($_POST)) { return $is = true; - } - if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) { - if (in_array(mb_strtoupper($_SERVER['REQUEST_METHOD']), ['POST', 'PUT', 'DELETE'], true)) { - return $is = true; - } + } elseif (!empty($_SERVER['REQUEST_METHOD']) && in_array(mb_strtoupper($_SERVER['REQUEST_METHOD']), ['POST', 'PUT', 'DELETE'], true)) { + return $is = true; } return $is = false; } /** - * Does the current request include an uncacheable query string? - * - * @since 151002 Improving Nginx support. + * Is the current request method is uncacheable? * - * @return bool True if request includes an uncacheable query string. + * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if current request method is uncacheable. */ - public function requestContainsUncacheableQueryVars() + public function isUncacheableRequestMethod() { - if (!is_null($contains = &$this->staticKey('requestContainsUncacheableQueryVars'))) { - return $contains; // Already cached this. + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { + return $is; // Already cached this. } - if (!empty($_GET) || !empty($_SERVER['QUERY_STRING'])) { - $_get_count = !empty($_GET) ? count($_GET) : 0; - $is_abc_only = $_get_count === 1 && isset($_GET[mb_strtolower(SHORT_NAME).'ABC']); - $is_nginx_q_only = $_get_count === 1 && isset($_GET['q']) && $this->isNginx(); - $is_ac_get_var_true = isset($_GET[mb_strtolower(SHORT_NAME).'AC']) && filter_var($_GET[mb_strtolower(SHORT_NAME).'AC'], FILTER_VALIDATE_BOOLEAN); - - if (!$is_abc_only && !$is_nginx_q_only && !$is_ac_get_var_true) { - return $contains = true; - } + if (!empty($_POST)) { + return $is = true; + } elseif (!empty($_SERVER['REQUEST_METHOD']) && mb_strtoupper($_SERVER['REQUEST_METHOD']) !== 'GET') { + return $is = true; } - return $contains = false; + return $is = false; } /** - * Is the current request method is uncacheable? - * - * @since 150422 Rewrite. + * Does the current request include an uncacheable query string? * - * @return bool `TRUE` if current request method is uncacheable. + * @since 151002 Improving Nginx support. + * @since 16xxxx Adding support for ignored GET vars. * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if request includes an uncacheable query string. */ - public function isUncacheableRequestMethod() + public function requestContainsUncacheableQueryVars() { - if (!is_null($is = &$this->staticKey('isUncacheableRequestMethod'))) { - return $is; // Already cached this. + if (($contains = &$this->staticKey(__FUNCTION__)) !== null) { + return $contains; // Already cached this. } - if (!empty($_POST)) { - return $is = true; + if (!$_GET) { // No GET vars whatsoever? + return $contains = false; // Nothing to check. } - if (!empty($_SERVER['REQUEST_METHOD']) && is_string($_SERVER['REQUEST_METHOD'])) { - if (!in_array(mb_strtoupper($_SERVER['REQUEST_METHOD']), ['GET'], true)) { - return $is = true; - } + $short_name_lc = mb_strtolower(SHORT_NAME); // Needed below. + + if (isset($_GET[$short_name_lc.'AC']) && filter_var($_GET[$short_name_lc.'AC'], FILTER_VALIDATE_BOOLEAN)) { + return $contains = false; // `?ccAC` allows caching. } - return $is = false; + return $contains = $this->filterQueryVars($_GET) ? true : false; } /** * Should the current user should be considered a logged-in user? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @return bool `TRUE` if current user should be considered a logged-in user. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if current user should be considered a logged-in user. */ public function isLikeUserLoggedIn() { - if (!is_null($is = &$this->staticKey('isLikeUserLoggedIn'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } if (defined('SID') && SID) { - return $is = true; // Session ID. - } - if (empty($_COOKIE)) { - return $is = false; // No cookies. + return $is = true; + } elseif (empty($_COOKIE)) { + return $is = false; } $regex_logged_in_cookies = '/^'; // Initialize. @@ -165,11 +152,13 @@ public function isLikeUserLoggedIn() $regex_logged_in_cookies .= '/'; // Close regex. foreach ($_COOKIE as $_key => $_value) { - if ($_value && preg_match($regex_logged_in_cookies, $_key)) { + $_key = (string) $_key; + $_value = (string) $_value; + + if (isset($_value[0]) && preg_match($regex_logged_in_cookies, $_key)) { return $is = true; // Like a logged-in user. } - } - unset($_key, $_value); // Housekeeping. + } // unset($_key, $_value); // Housekeeping. return $is = false; } @@ -178,20 +167,18 @@ public function isLikeUserLoggedIn() * Are we in a LOCALHOST environment? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @return bool `TRUE` if we are in a LOCALHOST environment. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if we are in a LOCALHOST environment. */ public function isLocalhost() { - if (!is_null($is = &$this->staticKey('isLocalhost'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } if (defined('LOCALHOST')) { return $is = (bool) LOCALHOST; - } - if (preg_match('/\b(?:localhost|127\.0\.0\.1)\b/ui', $this->hostToken())) { + } elseif (preg_match('/\b(?:localhost|127\.0\.0\.1)\b/ui', $this->hostToken())) { return $is = true; } return $is = false; @@ -202,20 +189,17 @@ public function isLocalhost() * Is the current request for the Auto-Cache Engine? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @return bool `TRUE` if the current request is for the Auto-Cache Engine. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if the current request is for the Auto-Cache Engine. */ public function isAutoCacheEngine() { - if (!is_null($is = &$this->staticKey('isAutoCacheEngine'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } - if (!empty($_SERVER['HTTP_USER_AGENT']) && is_string($_SERVER['HTTP_USER_AGENT'])) { - if (mb_stripos($_SERVER['HTTP_USER_AGENT'], GLOBAL_NS) !== false) { - return $is = true; - } + if (!empty($_SERVER['HTTP_USER_AGENT']) && mb_stripos($_SERVER['HTTP_USER_AGENT'], GLOBAL_NS) !== false) { + return $is = true; } return $is = false; } @@ -225,23 +209,19 @@ public function isAutoCacheEngine() * Is the current request for a feed? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @return bool `TRUE` if the current request is for a feed. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if the current request is for a feed. */ public function isFeed() { - if (!is_null($is = &$this->staticKey('isFeed'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } if (isset($_REQUEST['feed'])) { return $is = true; - } - if (!empty($_SERVER['REQUEST_URI']) && is_string($_SERVER['REQUEST_URI'])) { - if (preg_match('/\/feed(?:[\/?]|$)/', $_SERVER['REQUEST_URI'])) { - return $is = true; - } + } elseif (!empty($_SERVER['REQUEST_URI']) && preg_match('/\/feed(?:[\/?]|$)/', $_SERVER['REQUEST_URI'])) { + return $is = true; } return $is = false; } @@ -250,6 +230,7 @@ public function isFeed() * Is a document/string an HTML/XML doc; or no? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * * @param string $doc Input string/document to check. * @@ -260,13 +241,10 @@ public function isHtmlXmlDoc($doc) $doc = trim((string) $doc); $doc_hash = sha1($doc); - if (!is_null($is = &$this->staticKey('isHtmlXmlDoc', $doc_hash))) { + if (($is = &$this->staticKey(__FUNCTION__, $doc_hash)) !== null) { return $is; // Already cached this. } - if (mb_stripos($doc, '') !== false) { - return $is = true; - } - if (mb_stripos($doc, '') !== false || mb_stripos($doc, 'staticKey('hasACacheableContentType'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } foreach ($this->headersList() as $_key => $_header) { - if (mb_stripos($_header, 'Content-Type:') === 0) { - $content_type = $_header; // Last one. - } - } - unset($_key, $_header); // Housekeeping. + if (mb_stripos($_header, 'content-type:') === 0) { + $content_type = $_header; + } // Use last content-type header. + } // unset($_key, $_header); // Housekeeping. if (isset($content_type[0]) && mb_stripos($content_type, 'html') === false - && mb_stripos($content_type, 'xml') === false && mb_stripos($content_type, GLOBAL_NS) === false - ) { + && mb_stripos($content_type, 'xml') === false && mb_stripos($content_type, GLOBAL_NS) === false) { return $is = false; // Do NOT cache data sent by scripts serving other MIME types. } return $is = true; @@ -307,31 +282,28 @@ public function hasACacheableContentType() * Does the current request have a cacheable HTTP status code? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * - * @return bool `TRUE` if the current request has a cacheable HTTP status code. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if the current request has a cacheable HTTP status code. * * @warning Do NOT call upon this method until the end of a script execution. */ public function hasACacheableStatus() { - if (!is_null($is = &$this->staticKey('hasACacheableStatus'))) { + if (($is = &$this->staticKey(__FUNCTION__)) !== null) { return $is; // Already cached this. } if (($http_status = (string) $this->httpStatus()) && $http_status[0] !== '2' && $http_status !== '404') { return $is = false; // A non-2xx & non-404 status code. } foreach ($this->headersList() as $_key => $_header) { - if (preg_match('/^(?:Retry\-After\:\s+(?P.+)|Status\:\s+(?P[0-9]+)|HTTP\/[0-9]+(?:\.[0-9]+)?\s+(?P[0-9]+))/ui', $_header, $_m)) { - if (!empty($_m['retry']) || (!empty($_m['status']) && $_m['status'][0] !== '2' && $_m['status'] !== '404') - || (!empty($_m['http_status']) && $_m['http_status'][0] !== '2' && $_m['http_status'] !== '404') - ) { - return $is = false; // Not a cacheable status. - } + if (!preg_match('/^(?:Retry\-After\:\s+(?P.+)|Status\:\s+(?P[0-9]+)|HTTP\/[0-9]+(?:\.[0-9]+)?\s+(?P[0-9]+))/ui', $_header, $_m)) { + continue; // Not applicable; i.e., Not a header that is worth checking here. + } elseif (!empty($_m['retry']) || (!empty($_m['status']) && $_m['status'][0] !== '2' && $_m['status'] !== '404') + || (!empty($_m['http_status']) && $_m['http_status'][0] !== '2' && $_m['http_status'] !== '404')) { + return $is = false; // Not a cacheable status. } - } - unset($_key, $_header); // Housekeeping. + } // unset($_key, $_header, $_m); // Housekeeping. return $is = true; } @@ -340,18 +312,17 @@ public function hasACacheableStatus() * Checks if a PHP extension is loaded up. * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * * @param string $extension A PHP extension slug (i.e. extension name). * - * @return bool `TRUE` if the extension is loaded. - * - * @note The return value of this function is cached to reduce overhead on repeat calls. + * @return bool True if the extension is loaded. */ public function isExtensionLoaded($extension) { $extension = (string) $extension; - if (!is_null($is = &$this->staticKey('isExtensionLoaded', $extension))) { + if (($is = &$this->staticKey(__FUNCTION__, $extension)) !== null) { return $is; // Already cached this. } return $is = (bool) extension_loaded($extension); @@ -361,22 +332,20 @@ public function isExtensionLoaded($extension) * Is a particular function possible in every way? * * @since 150422 Rewrite. + * @since 16xxxx Enhancements. * * @param string $function A PHP function (or user function) to check. * - * @return string `TRUE` if the function is possible. - * - * @note This checks (among other things) if the function exists and that it's callable. - * It also checks the currently configured `disable_functions` and `suhosin.executor.func.blacklist`. + * @return string True if the function is possible. */ public function functionIsPossible($function) { $function = (string) $function; - if (!is_null($is = &$this->staticKey('functionIsPossible', $function))) { + if (($is = &$this->staticKey(__FUNCTION__, $function)) !== null) { return $is; // Already cached this. } - if (is_null($disabled_functions = &$this->staticKey('functionIsPossible_disabled_functions'))) { + if (($disabled_functions = &$this->staticKey(__FUNCTION__.'_disabled_functions')) === null) { $disabled_functions = []; // Initialize disabled/blacklisted functions. if (($disable_functions = trim(ini_get('disable_functions')))) { @@ -390,10 +359,11 @@ public function functionIsPossible($function) } } if (!function_exists($function) || !is_callable($function)) { - if (!in_array($function, $this->php_constructs, true)) { // A language construct + if (!in_array($function, $this->php_constructs, true)) { return $is = false; // Not possible. } - } + } // In PHP, language constructs cannot be disabled/blacklisted whatsoever. + if ($disabled_functions && in_array(mb_strtolower($function), $disabled_functions, true)) { return $is = false; // Not possible. }