cloudflare/Cloudflare-WordPress
Publicmirrored fromhttps://github.com/cloudflare/Cloudflare-WordPress
src/WordPress/Hooks.php
577lines · modecode
5 months ago
| 1 | <?php |
| 2 | |
| 3 | namespace Cloudflare\APO\WordPress; |
| 4 | |
| 5 | use Cloudflare\APO\API\APIInterface; |
| 6 | use Cloudflare\APO\Integration; |
| 7 | use Psr\Log\LoggerInterface; |
| 8 | use WP_Taxonomy; |
| 9 | |
| 10 | class Hooks |
| 11 | { |
| 12 | protected $api; |
| 13 | protected $config; |
| 14 | protected $dataStore; |
| 15 | protected $integrationContext; |
| 16 | protected $integrationAPI; |
| 17 | protected $logger; |
| 18 | protected $proxy; |
| 19 | |
| 20 | const CLOUDFLARE_JSON = 'CLOUDFLARE_JSON'; |
| 21 | const WP_AJAX_ACTION = 'cloudflare_proxy'; |
| 22 | |
| 23 | // See https://developers.cloudflare.com/cache/about/default-cache-behavior/ |
| 24 | const CLOUDFLARE_CACHABLE_EXTENSIONS = [ |
| 25 | "7z", "csv", "gif", "midi", "png", "tif", "zip", "avi", "doc", "gz", |
| 26 | "mkv", "ppt", "tiff", "zst", "avif", "docx", "ico", "mp3", "pptx", |
| 27 | "ttf", "apk", "dmg", "iso", "mp4", "ps", "webm", "bin", "ejs", "jar", |
| 28 | "ogg", "rar", "webp", "bmp", "eot", "jpg", "otf", "svg", "woff", "bz2", |
| 29 | "eps", "jpeg", "pdf", "svgz", "woff2", "class", "exe", "js", "pict", |
| 30 | "swf", "xls", "css", "flac", "mid", "pls", "tar", "xlsx" |
| 31 | ]; |
| 32 | |
| 33 | public function __construct() |
| 34 | { |
| 35 | $this->config = new Integration\DefaultConfig(file_get_contents(CLOUDFLARE_PLUGIN_DIR . 'config.json', true)); |
| 36 | $this->logger = new Integration\DefaultLogger($this->config->getValue('debug')); |
| 37 | $this->dataStore = new DataStore($this->logger); |
| 38 | $this->integrationAPI = new WordPressAPI($this->dataStore); |
| 39 | $this->integrationContext = new Integration\DefaultIntegration($this->config, $this->integrationAPI, $this->dataStore, $this->logger); |
| 40 | $this->api = new WordPressClientAPI($this->integrationContext); |
| 41 | $this->proxy = new Proxy($this->integrationContext); |
| 42 | } |
| 43 | |
| 44 | /** |
| 45 | * @param \Cloudflare\APO\API\APIInterface $api |
| 46 | */ |
| 47 | public function setAPI(APIInterface $api) |
| 48 | { |
| 49 | $this->api = $api; |
| 50 | } |
| 51 | |
| 52 | public function setConfig(Integration\ConfigInterface $config) |
| 53 | { |
| 54 | $this->config = $config; |
| 55 | } |
| 56 | |
| 57 | public function setDataStore(Integration\DataStoreInterface $dataStore) |
| 58 | { |
| 59 | $this->dataStore = $dataStore; |
| 60 | } |
| 61 | |
| 62 | public function setIntegrationContext(Integration\IntegrationInterface $integrationContext) |
| 63 | { |
| 64 | $this->integrationContext = $integrationContext; |
| 65 | } |
| 66 | |
| 67 | public function setIntegrationAPI(Integration\IntegrationAPIInterface $integrationAPI) |
| 68 | { |
| 69 | $this->integrationAPI = $integrationAPI; |
| 70 | } |
| 71 | |
| 72 | public function setLogger(LoggerInterface $logger) |
| 73 | { |
| 74 | $this->logger = $logger; |
| 75 | } |
| 76 | |
| 77 | public function setProxy(Proxy $proxy) |
| 78 | { |
| 79 | $this->proxy = $proxy; |
| 80 | } |
| 81 | |
| 82 | public function cloudflareConfigPage() |
| 83 | { |
| 84 | if (function_exists('add_options_page')) { |
| 85 | add_options_page(__('Cloudflare Configuration'), __('Cloudflare'), 'manage_options', 'cloudflare', array($this, 'cloudflareIndexPage')); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | public function cloudflareIndexPage() |
| 90 | { |
| 91 | include CLOUDFLARE_PLUGIN_DIR . 'index.php'; |
| 92 | } |
| 93 | |
| 94 | public function pluginActionLinks($links) |
| 95 | { |
| 96 | $links[] = '<a href="' . get_admin_url(null, 'options-general.php?page=cloudflare') . '">Settings</a>'; |
| 97 | |
| 98 | return $links; |
| 99 | } |
| 100 | |
| 101 | public function initProxy() |
| 102 | { |
| 103 | $this->proxy->run(); |
| 104 | } |
| 105 | |
| 106 | public function activate() |
| 107 | { |
| 108 | if (version_compare($GLOBALS['wp_version'], CLOUDFLARE_MIN_WP_VERSION, '<')) { |
| 109 | deactivate_plugins(basename(CLOUDFLARE_PLUGIN_DIR)); |
| 110 | wp_die('<p><strong>Cloudflare</strong> plugin requires WordPress version ' . CLOUDFLARE_MIN_WP_VERSION . ' or greater.</p>', 'Plugin Activation Error', array('response' => 200, 'back_link' => true)); |
| 111 | } |
| 112 | |
| 113 | return true; |
| 114 | } |
| 115 | |
| 116 | public function deactivate() |
| 117 | { |
| 118 | $this->dataStore->clearDataStore(); |
| 119 | } |
| 120 | |
| 121 | public function purgeCacheEverything() |
| 122 | { |
| 123 | if ($this->isPluginSpecificCacheEnabled() || $this->isAutomaticPlatformOptimizationEnabled()) { |
| 124 | $wpDomainList = $this->integrationAPI->getDomainList(); |
| 125 | if (count($wpDomainList) > 0) { |
| 126 | $wpDomain = $wpDomainList[0]; |
| 127 | |
| 128 | $zoneTag = $this->api->getZoneTag($wpDomain); |
| 129 | |
| 130 | if (isset($zoneTag)) { |
| 131 | $isOK = $this->api->zonePurgeCache($zoneTag); |
| 132 | |
| 133 | $isOK = ($isOK) ? 'succeeded' : 'failed'; |
| 134 | $this->logger->debug("purgeCacheEverything " . $isOK); |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | public function purgeCacheByRelevantURLs($postIds) |
| 141 | { |
| 142 | if ($this->isPluginSpecificCacheEnabled() || $this->isAutomaticPlatformOptimizationEnabled()) { |
| 143 | $wpDomainList = $this->integrationAPI->getDomainList(); |
| 144 | if (!count($wpDomainList)) { |
| 145 | return; |
| 146 | } |
| 147 | $wpDomain = $wpDomainList[0]; |
| 148 | $zoneTag = $this->api->getZoneTag($wpDomain); |
| 149 | if (!isset($zoneTag)) { |
| 150 | return; |
| 151 | } |
| 152 | |
| 153 | $postIds = (array) $postIds; |
| 154 | $urls = []; |
| 155 | foreach ($postIds as $postId) { |
| 156 | // Do not purge for autosaves or updates to post revisions. |
| 157 | if (wp_is_post_autosave($postId) || wp_is_post_revision($postId)) { |
| 158 | continue; |
| 159 | } |
| 160 | |
| 161 | $postType = get_post_type_object(get_post_type($postId)); |
| 162 | if (!is_post_type_viewable($postType)) { |
| 163 | continue; |
| 164 | } |
| 165 | |
| 166 | $savedPost = get_post($postId); |
| 167 | if (!is_a($savedPost, 'WP_Post')) { |
| 168 | continue; |
| 169 | } |
| 170 | |
| 171 | $relatedUrls = apply_filters('cloudflare_purge_by_url', $this->getPostRelatedLinks($postId), $postId); |
| 172 | $urls = array_merge($urls, $relatedUrls); |
| 173 | } |
| 174 | |
| 175 | // Don't attempt to purge anything outside of the provided zone. |
| 176 | foreach ($urls as $key => $url) { |
| 177 | $url_to_test = $url; |
| 178 | if (is_array($url) && !!$url['url']) { |
| 179 | $url_to_test = $url['url']; |
| 180 | } |
| 181 | |
| 182 | if (!Utils::strEndsWith(parse_url($url_to_test, PHP_URL_HOST), $wpDomain)) { |
| 183 | unset($urls[$key]); |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | if (empty($urls)) { |
| 188 | return; |
| 189 | } |
| 190 | |
| 191 | // Filter by unique urls |
| 192 | $urls = array_values(array_filter(array_unique($urls))); |
| 193 | |
| 194 | $activePageRules = $this->api->getPageRules($zoneTag, "active"); |
| 195 | $hasCacheOverride = $this->pageRuleContains($activePageRules, "cache_level", "cache_everything"); |
| 196 | |
| 197 | // Should we not have a 'cache_everything' page rule override, feeds |
| 198 | // shouldn't be attempted to be purged as they are not cachable by |
| 199 | // default. |
| 200 | if (!$hasCacheOverride) { |
| 201 | $this->logger->debug("cache everything behaviour found, filtering out feeds URLs"); |
| 202 | $urls = array_filter($urls, array($this, "pathIsNotForFeeds")); |
| 203 | } |
| 204 | |
| 205 | // Fetch the page rules and should we not have any hints of cache |
| 206 | // all behaviour or APO, filter out the non-cacheable URLs. |
| 207 | if (!$hasCacheOverride && !$this->isAutomaticPlatformOptimizationEnabled()) { |
| 208 | $this->logger->debug("cache everything behaviour and APO not found, filtering URLs to only be those that are cacheable by default"); |
| 209 | $urls = array_filter($urls, array($this, "pathHasCachableFileExtension")); |
| 210 | } |
| 211 | |
| 212 | if ($this->zoneSettingAlwaysUseHTTPSEnabled($zoneTag)) { |
| 213 | $this->logger->debug("zone level always_use_https is enabled, removing HTTP based URLs"); |
| 214 | $urls = array_filter($urls, array($this, "urlIsHTTPS")); |
| 215 | } |
| 216 | |
| 217 | if (!empty($urls)) { |
| 218 | do_action('cloudflare_purged_urls', $urls, $postIds); |
| 219 | $chunks = array_chunk($urls, 30); |
| 220 | |
| 221 | foreach ($chunks as $chunk) { |
| 222 | $isOK = $this->api->zonePurgeFiles($zoneTag, $chunk); |
| 223 | |
| 224 | $isOK = ($isOK) ? 'succeeded' : 'failed'; |
| 225 | $this->logger->debug("List of URLs purged are: " . print_r($chunk, true)); |
| 226 | $this->logger->debug("purgeCacheByRelevantURLs " . $isOK); |
| 227 | } |
| 228 | |
| 229 | // Purge cache on mobile if APO Cache By Device Type |
| 230 | if ($this->isAutomaticPlatformOptimizationCacheByDeviceTypeEnabled()) { |
| 231 | foreach ($chunks as $chunk) { |
| 232 | $isOK = $this->api->zonePurgeFiles($zoneTag, array_map(array($this, 'toPurgeCacheOnMobile'), $chunk)); |
| 233 | |
| 234 | $isOK = ($isOK) ? 'succeeded' : 'failed'; |
| 235 | $this->logger->debug("List of URLs purged on mobile are: " . print_r($chunk, true)); |
| 236 | $this->logger->debug("purgeCacheByRelevantURLs " . $isOK); |
| 237 | } |
| 238 | } |
| 239 | } |
| 240 | } |
| 241 | } |
| 242 | |
| 243 | protected function toPurgeCacheOnMobile($url) |
| 244 | { |
| 245 | //Purge cache on mobile |
| 246 | $headers = array("CF-Device-Type" => "mobile"); |
| 247 | $purge_object = array("url" => $url, "headers" => $headers); |
| 248 | $json = json_decode(json_encode($purge_object, JSON_FORCE_OBJECT)); |
| 249 | return $json; |
| 250 | } |
| 251 | |
| 252 | public function getPostRelatedLinks($postId) |
| 253 | { |
| 254 | $listofurls = array(); |
| 255 | $postType = get_post_type($postId); |
| 256 | |
| 257 | //Purge taxonomies terms and feeds URLs |
| 258 | $postTypeTaxonomies = get_object_taxonomies($postType); |
| 259 | |
| 260 | foreach ($postTypeTaxonomies as $taxonomy) { |
| 261 | // Only if taxonomy is public |
| 262 | $taxonomy_data = get_taxonomy($taxonomy); |
| 263 | if ($taxonomy_data instanceof WP_Taxonomy && false === $taxonomy_data->public) { |
| 264 | continue; |
| 265 | } |
| 266 | |
| 267 | $terms = get_the_terms($postId, $taxonomy); |
| 268 | |
| 269 | if (empty($terms) || is_wp_error($terms)) { |
| 270 | continue; |
| 271 | } |
| 272 | |
| 273 | foreach ($terms as $term) { |
| 274 | $termLink = get_term_link($term); |
| 275 | $termFeedLink = get_term_feed_link($term->term_id, $term->taxonomy); |
| 276 | if (!is_wp_error($termLink) && !is_wp_error($termFeedLink)) { |
| 277 | array_push($listofurls, $termLink); |
| 278 | array_push($listofurls, $termFeedLink); |
| 279 | } |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | // Author URL |
| 284 | array_push( |
| 285 | $listofurls, |
| 286 | get_author_posts_url(get_post_field('post_author', $postId)), |
| 287 | get_author_feed_link(get_post_field('post_author', $postId)) |
| 288 | ); |
| 289 | |
| 290 | // Archives and their feeds |
| 291 | if (get_post_type_archive_link($postType) == true) { |
| 292 | array_push( |
| 293 | $listofurls, |
| 294 | get_post_type_archive_link($postType), |
| 295 | get_post_type_archive_feed_link($postType) |
| 296 | ); |
| 297 | } |
| 298 | |
| 299 | // Post URL |
| 300 | array_push($listofurls, get_permalink($postId)); |
| 301 | |
| 302 | // Also clean URL for trashed post. |
| 303 | if (get_post_status($postId) == 'trash') { |
| 304 | $trashPost = get_permalink($postId); |
| 305 | $trashPost = str_replace('__trashed', '', $trashPost); |
| 306 | array_push($listofurls, $trashPost, $trashPost . 'feed/'); |
| 307 | } |
| 308 | |
| 309 | // Feeds |
| 310 | array_push( |
| 311 | $listofurls, |
| 312 | get_bloginfo_rss('rdf_url'), |
| 313 | get_bloginfo_rss('rss_url'), |
| 314 | get_bloginfo_rss('rss2_url'), |
| 315 | get_bloginfo_rss('atom_url'), |
| 316 | get_bloginfo_rss('comments_rss2_url'), |
| 317 | get_post_comments_feed_link($postId) |
| 318 | ); |
| 319 | |
| 320 | // Home Page and (if used) posts page |
| 321 | array_push($listofurls, home_url('/')); |
| 322 | $pageLink = get_permalink(get_option('page_for_posts')); |
| 323 | if (is_string($pageLink) && !empty($pageLink) && get_option('show_on_front') == 'page') { |
| 324 | array_push($listofurls, $pageLink); |
| 325 | } |
| 326 | |
| 327 | // Refresh pagination |
| 328 | $total_posts_count = wp_count_posts()->publish; |
| 329 | $posts_per_page = get_option('posts_per_page'); |
| 330 | // Limit to up to 3 pages |
| 331 | $page_number_max = min(3, ceil($total_posts_count / $posts_per_page)); |
| 332 | |
| 333 | $this->logger->debug("total_posts_count $total_posts_count"); |
| 334 | $this->logger->debug("posts_per_page $posts_per_page"); |
| 335 | $this->logger->debug("page_number_max $page_number_max"); |
| 336 | |
| 337 | foreach (range(1, $page_number_max) as $page_number) { |
| 338 | array_push($listofurls, home_url(sprintf('/page/%s/', $page_number))); |
| 339 | } |
| 340 | |
| 341 | // Attachments |
| 342 | if ('attachment' == $postType) { |
| 343 | $attachmentUrls = array(); |
| 344 | foreach (get_intermediate_image_sizes() as $size) { |
| 345 | $attachmentSrc = wp_get_attachment_image_src($postId, $size); |
| 346 | if (is_array($attachmentSrc) && !empty($attachmentSrc)) { |
| 347 | $attachmentUrls[] = $attachmentSrc[0]; |
| 348 | } |
| 349 | } |
| 350 | $listofurls = array_merge( |
| 351 | $listofurls, |
| 352 | $attachmentUrls |
| 353 | ); |
| 354 | } |
| 355 | |
| 356 | // Clean array and get unique values |
| 357 | $listofurls = array_values(array_filter(array_unique($listofurls))); |
| 358 | |
| 359 | // Purge https and http URLs |
| 360 | if (function_exists('force_ssl_admin') && force_ssl_admin()) { |
| 361 | $listofurls = array_merge($listofurls, str_replace('https://', 'http://', $listofurls)); |
| 362 | } elseif (!is_ssl() && function_exists('force_ssl_content') && force_ssl_content()) { |
| 363 | $listofurls = array_merge($listofurls, str_replace('http://', 'https://', $listofurls)); |
| 364 | } |
| 365 | |
| 366 | return $listofurls; |
| 367 | } |
| 368 | |
| 369 | protected function isPluginSpecificCacheEnabled() |
| 370 | { |
| 371 | $cacheSettingObject = $this->dataStore->getPluginSetting(\Cloudflare\APO\API\Plugin::SETTING_PLUGIN_SPECIFIC_CACHE); |
| 372 | |
| 373 | if (!$cacheSettingObject) { |
| 374 | return false; |
| 375 | } |
| 376 | |
| 377 | $cacheSettingValue = $cacheSettingObject[\Cloudflare\APO\API\Plugin::SETTING_VALUE_KEY]; |
| 378 | |
| 379 | return $cacheSettingValue !== false |
| 380 | && $cacheSettingValue !== 'off'; |
| 381 | } |
| 382 | |
| 383 | protected function isAutomaticPlatformOptimizationEnabled() |
| 384 | { |
| 385 | $cacheSettingObject = $this->dataStore->getPluginSetting(\Cloudflare\APO\API\Plugin::SETTING_AUTOMATIC_PLATFORM_OPTIMIZATION); |
| 386 | |
| 387 | if (!$cacheSettingObject) { |
| 388 | return false; |
| 389 | } |
| 390 | |
| 391 | $cacheSettingValue = $cacheSettingObject[\Cloudflare\APO\API\Plugin::SETTING_VALUE_KEY]; |
| 392 | |
| 393 | return $cacheSettingValue !== false |
| 394 | && $cacheSettingValue !== 'off'; |
| 395 | } |
| 396 | |
| 397 | protected function isAutomaticPlatformOptimizationCacheByDeviceTypeEnabled() |
| 398 | { |
| 399 | $cacheSettingObject = $this->dataStore->getPluginSetting(\Cloudflare\APO\API\Plugin::SETTING_AUTOMATIC_PLATFORM_OPTIMIZATION_CACHE_BY_DEVICE_TYPE); |
| 400 | |
| 401 | if (!$cacheSettingObject) { |
| 402 | return false; |
| 403 | } |
| 404 | |
| 405 | $cacheSettingValue = $cacheSettingObject[\Cloudflare\APO\API\Plugin::SETTING_VALUE_KEY]; |
| 406 | |
| 407 | return $cacheSettingValue !== false |
| 408 | && $cacheSettingValue !== 'off'; |
| 409 | } |
| 410 | |
| 411 | public function http2ServerPushInit() |
| 412 | { |
| 413 | HTTP2ServerPush::init(); |
| 414 | } |
| 415 | |
| 416 | /* |
| 417 | * php://input can only be read once before PHP 5.6, try to grab it ONLY if the request |
| 418 | * is coming from the cloudflare proxy. We store it in a global so \Cloudflare\APO\WordPress\Proxy |
| 419 | * can act on the request body later on in the script execution. |
| 420 | */ |
| 421 | public function getCloudflareRequestJSON() |
| 422 | { |
| 423 | if (isset($_GET['action']) && $_GET['action'] === self::WP_AJAX_ACTION) { |
| 424 | $GLOBALS[self::CLOUDFLARE_JSON] = file_get_contents('php://input'); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | public function initAutomaticPlatformOptimization() |
| 429 | { |
| 430 | // it could be too late to set the headers, |
| 431 | // return early without triggering a warning in logs |
| 432 | if (headers_sent()) { |
| 433 | return; |
| 434 | } |
| 435 | |
| 436 | // add header unconditionally so we can detect plugin is activated |
| 437 | $cache = apply_filters('cloudflare_use_cache', !is_user_logged_in()); |
| 438 | if ($cache) { |
| 439 | header('cf-edge-cache: cache,platform=wordpress'); |
| 440 | } else { |
| 441 | header('cf-edge-cache: no-cache'); |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | public function purgeCacheOnPostStatusChange($new_status, $old_status, $post) |
| 446 | { |
| 447 | if ('publish' === $new_status || 'publish' === $old_status) { |
| 448 | $this->purgeCacheByRelevantURLs($post->ID); |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | public function purgeCacheOnCommentStatusChange($new_status, $old_status, $comment) |
| 453 | { |
| 454 | if (!isset($comment->comment_post_ID) || empty($comment->comment_post_ID)) { |
| 455 | return; // nothing to do |
| 456 | } |
| 457 | |
| 458 | // in case the comment status changed, and either old or new status is "approved", we need to purge cache for the corresponding post |
| 459 | if (($old_status != $new_status) && (($old_status === 'approved') || ($new_status === 'approved'))) { |
| 460 | $this->purgeCacheByRelevantURLs($comment->comment_post_ID); |
| 461 | return; |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | public function purgeCacheOnNewComment($comment_id, $comment_status, $comment_data) |
| 466 | { |
| 467 | if ($comment_status != 1) { |
| 468 | return; // if comment is not approved, stop |
| 469 | } |
| 470 | if (!is_array($comment_data)) { |
| 471 | return; // nothing to do |
| 472 | } |
| 473 | if (!array_key_exists('comment_post_ID', $comment_data)) { |
| 474 | return; // nothing to do |
| 475 | } |
| 476 | |
| 477 | // all clear, we ne need to purge cache related to this post id |
| 478 | $this->purgeCacheByRelevantURLs($comment_data['comment_post_ID']); |
| 479 | } |
| 480 | |
| 481 | /** |
| 482 | * Accepts a page rule key and value to check if it exists in the page rules |
| 483 | * provided. |
| 484 | * |
| 485 | * @param mixed $pagerules |
| 486 | * @param mixed $key |
| 487 | * @param mixed $value |
| 488 | * @return bool |
| 489 | */ |
| 490 | private function pageRuleContains($pagerules, $key, $value) |
| 491 | { |
| 492 | if (!is_array($pagerules)) { |
| 493 | return false; |
| 494 | } |
| 495 | |
| 496 | foreach ($pagerules as $pagerule) { |
| 497 | foreach ($pagerule["actions"] as $action) { |
| 498 | // always_use_https can only be toggled on for a URL but doesn't |
| 499 | // have a value so we merely check the presence of the key |
| 500 | // instead. |
| 501 | if ($action["id"] == "always_use_https" && $key == "always_use_https") { |
| 502 | return true; |
| 503 | } |
| 504 | |
| 505 | if (!array_key_exists("value", $action)) { |
| 506 | continue; |
| 507 | } |
| 508 | |
| 509 | if ($action["id"] == $key && $action["value"] == $value) { |
| 510 | return true; |
| 511 | } |
| 512 | } |
| 513 | } |
| 514 | |
| 515 | |
| 516 | return false; |
| 517 | } |
| 518 | |
| 519 | private function zoneSettingAlwaysUseHTTPSEnabled($zoneTag) |
| 520 | { |
| 521 | $settings = $this->api->getZoneSetting($zoneTag, "always_use_https"); |
| 522 | return !empty($settings["value"]) && $settings["value"] == "on"; |
| 523 | } |
| 524 | |
| 525 | |
| 526 | /** |
| 527 | * pathHasCachableFileExtension takes a string of a URL and evaluates if it |
| 528 | * has a file extension that Cloudflare caches by default. |
| 529 | * |
| 530 | * @param mixed $value |
| 531 | * @return bool |
| 532 | */ |
| 533 | private function pathHasCachableFileExtension($value) |
| 534 | { |
| 535 | $parsed_url = parse_url($value, PHP_URL_PATH); |
| 536 | |
| 537 | foreach (self::CLOUDFLARE_CACHABLE_EXTENSIONS as $ext) { |
| 538 | if (Utils::strEndsWith($parsed_url, "." . $ext)) { |
| 539 | return true; |
| 540 | } |
| 541 | } |
| 542 | |
| 543 | return false; |
| 544 | } |
| 545 | |
| 546 | /** |
| 547 | * pathIsNotForFeeds accepts a string URL and checks if the path doesn't matches any |
| 548 | * known feed paths such as "/feed", "/feed/", "/feed/rdf/", "/feed/rss/", |
| 549 | * "/feed/atom/", "/author/foo/feed", "/comments/feed", "/shop/feed", |
| 550 | * "/tag/.../feed/", etc. |
| 551 | * |
| 552 | * @param mixed $value |
| 553 | * @return bool |
| 554 | */ |
| 555 | private function pathIsNotForFeeds($value) |
| 556 | { |
| 557 | $parsed_url = parse_url($value, PHP_URL_PATH); |
| 558 | return (bool) !preg_match('/\/feed(?:\/(?:atom\/?|r(?:df|ss)\/?)?)?$/', $parsed_url); |
| 559 | } |
| 560 | |
| 561 | /** |
| 562 | * urlIsHTTPS determines if a scheme used for a URL is HTTPS. |
| 563 | * |
| 564 | * @param mixed $value |
| 565 | * @return bool |
| 566 | */ |
| 567 | private function urlIsHTTPS($value) |
| 568 | { |
| 569 | $parsed_scheme = parse_url($value, PHP_URL_SCHEME); |
| 570 | |
| 571 | if ($parsed_scheme == "https") { |
| 572 | return true; |
| 573 | } |
| 574 | |
| 575 | return false; |
| 576 | } |
| 577 | } |