PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 3.6.3
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v3.6.3
trunk 1.0 1.0.1 1.1 1.1.1 1.1.2 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.1 2.1.1 2.1.2 2.2 2.2.1 All 69 releases
← All changes | includes/utils.php +503 -157 2.0.33.6.3 View file →
@@ -6,8 +6,9 @@
6 6 */
7 7
8 8 namespace PoweredCache\Utils;
9 9
10 +use PoweredCache\Encryption;
10 11 use const PoweredCache\Constants\SETTING_OPTION;
11 12 use RecursiveDirectoryIterator;
12 13 use RecursiveIteratorIterator;
13 14
@@ -42,92 +43,125 @@
42 43 * @return array
43 44 * @since 2.0
44 45 */
45 46 function get_settings( $force_network_wide = false ) {
47 + global $is_apache;
48 +
46 49 $settings = [
47 50 // basic options
48 - 'enable_page_cache' => true,
49 - 'object_cache' => 'off',
50 - 'cache_mobile' => true,
51 - 'cache_mobile_separate_file' => false,
52 - 'loggedin_user_cache' => false,
53 - 'ssl_cache' => true, // deprecated
54 - 'gzip_compression' => false,
55 - 'cache_timeout' => 1440,
51 + 'enable_page_cache' => true,
52 + 'object_cache' => 'off',
53 + 'cache_mobile' => true,
54 + 'cache_mobile_separate_file' => false,
55 + 'loggedin_user_cache' => false,
56 + 'ssl_cache' => true, // deprecated
57 + 'gzip_compression' => false,
58 + 'cache_timeout' => 1440,
56 59 // advanced options
57 - 'auto_configure_htaccess' => true,
58 - 'rejected_user_agents' => '',
59 - 'rejected_cookies' => '',
60 - 'vary_cookies' => '',
61 - 'rejected_uri' => '',
62 - 'accepted_query_strings' => '',
63 - 'purge_additional_pages' => '',
60 + 'auto_configure_htaccess' => $is_apache,
61 + 'rejected_user_agents' => '',
62 + 'rejected_cookies' => '',
63 + 'rejected_referrers' => '',
64 + 'vary_cookies' => '',
65 + 'rejected_uri' => '',
66 + 'ignored_query_strings' => '',
67 + 'cache_query_strings' => '',
68 + 'purge_additional_pages' => '',
64 69 // file optimization
65 - 'minify_html' => false,
66 - 'combine_google_fonts' => false,
67 - 'minify_css' => false,
68 - 'combine_css' => false,
69 - 'excluded_css_files' => '',
70 - 'minify_js' => false,
71 - 'combine_js' => false,
72 - 'excluded_js_files' => '',
73 - 'js_execution_method' => 'blocking',
74 - 'js_execution_optimized_only' => true,
70 + 'minify_html' => false,
71 + 'minify_html_dom_optimization' => false,
72 + 'combine_google_fonts' => false,
73 + 'swap_google_fonts_display' => true,
74 + 'use_bunny_fonts' => false,
75 + 'minify_css' => false,
76 + 'combine_css' => false,
77 + 'critical_css' => false,
78 + 'critical_css_additional_files' => '',
79 + 'critical_css_excluded_files' => '',
80 + 'critical_css_appended_content' => '',
81 + 'critical_css_fallback' => '',
82 + 'excluded_css_files' => '',
83 + 'remove_unused_css' => false,
84 + 'ucss_safelist' => '',
85 + 'ucss_excluded_files' => '',
86 + 'minify_js' => false,
87 + 'combine_js' => false,
88 + 'excluded_js_files' => '',
89 + 'js_execution_method' => 'blocking', // deprecated @since 3.2
90 + 'js_defer' => false,
91 + 'js_defer_exclusions' => '',
92 + 'js_delay' => false,
93 + 'js_delay_exclusions' => '',
94 + 'js_delay_timeout' => 0,
95 + 'js_execution_optimized_only' => true, // deprecated @since 3.2
96 + 'rewrite_file_optimizer' => $is_apache,
75 97 // media optimization
98 + 'enable_image_optimization' => false,
99 + 'image_optimizer_preferred_format' => '',
100 + 'add_missing_image_dimensions' => false,
76 101 // lazyload
77 - 'enable_lazy_load' => false,
78 - 'lazy_load_post_content' => true,
79 - 'lazy_load_images' => true,
80 - 'lazy_load_iframes' => true,
81 - 'lazy_load_widgets' => true,
82 - 'lazy_load_post_thumbnail' => true,
83 - 'lazy_load_avatars' => true,
84 - 'disable_wp_lazy_load' => false,
85 - 'disable_wp_embeds' => false,
86 - 'disable_emoji_scripts' => false,
102 + 'enable_lazy_load' => false,
103 + 'lazy_load_post_content' => true,
104 + 'lazy_load_images' => true,
105 + 'lazy_load_iframes' => true,
106 + 'lazy_load_widgets' => true,
107 + 'lazy_load_post_thumbnail' => true,
108 + 'lazy_load_avatars' => true,
109 + 'lazy_load_youtube' => false,
110 + 'lazy_load_skip_first_nth_img' => 3,
111 + 'lazy_load_exclusions' => '',
112 + 'disable_wp_lazy_load' => false,
113 + 'disable_wp_embeds' => false,
114 + 'disable_emoji_scripts' => false,
87 115 // cdn
88 - 'enable_cdn' => false,
89 - 'cdn_hostname' => array( '' ),
90 - 'cdn_zone' => array( '' ),
91 - 'cdn_rejected_files' => '',
116 + 'enable_cdn' => false,
117 + 'cdn_hostname' => array( '' ),
118 + 'cdn_zone' => array( '' ),
119 + 'cdn_rejected_files' => '',
92 120 // preload
93 - 'enable_cache_preload' => false,
94 - 'preload_homepage' => true,
95 - 'preload_public_posts' => true,
96 - 'preload_public_tax' => true,
97 - 'enable_sitemap_preload' => false,
98 - 'preload_sitemap' => '',
99 - 'prefetch_dns' => '',
121 + 'enable_cache_preload' => false,
122 + 'preload_homepage' => true,
123 + 'preload_public_posts' => true,
124 + 'preload_public_tax' => true,
125 + 'enable_sitemap_preload' => false,
126 + 'preload_request_interval' => 2, // in seconds
127 + 'preload_sitemap' => '',
128 + 'prefetch_dns' => '',
129 + 'preconnect_resource' => '',
130 + 'prefetch_links' => true,
100 131 // db options
101 - 'db_cleanup_post_revisions' => false,
102 - 'db_cleanup_auto_drafts' => false,
103 - 'db_cleanup_trashed_posts' => false,
104 - 'db_cleanup_spam_comments' => false,
105 - 'db_cleanup_trashed_comments' => false,
106 - 'db_cleanup_expired_transients' => false,
107 - 'db_cleanup_all_transients' => false,
108 - 'db_cleanup_optimize_tables' => false,
109 - 'enable_scheduled_db_cleanup' => false,
110 - 'scheduled_db_cleanup_frequency' => 'daily',
132 + 'db_cleanup_post_revisions' => false,
133 + 'db_cleanup_auto_drafts' => false,
134 + 'db_cleanup_trashed_posts' => false,
135 + 'db_cleanup_spam_comments' => false,
136 + 'db_cleanup_trashed_comments' => false,
137 + 'db_cleanup_expired_transients' => false,
138 + 'db_cleanup_all_transients' => false,
139 + 'db_cleanup_optimize_tables' => false,
140 + 'enable_scheduled_db_cleanup' => false,
141 + 'scheduled_db_cleanup_frequency' => 'daily',
111 142 // add-ons
112 - 'enable_cloudflare' => false,
113 - 'cloudflare_email' => '',
114 - 'cloudflare_api_key' => '',
115 - 'cloudflare_zone' => '',
116 - 'enable_heartbeat' => false, // extention status
117 - 'heartbeat_dashboard_status' => 'enable', // enable,disable,modify
118 - 'heartbeat_dashboard_interval' => 60, // default interval in seconds
119 - 'heartbeat_editor_status' => 'enable', // enable,disable,modify
120 - 'heartbeat_editor_interval' => 15, // default interval in seconds
121 - 'heartbeat_frontend_status' => 'enable', // enable,disable,modify
122 - 'heartbeat_frontend_interval' => 60, // default interval in seconds
123 - 'enable_varnish' => false,
124 - 'varnish_ip' => '',
143 + 'enable_cloudflare' => false,
144 + 'cloudflare_api_token' => '',
145 + 'cloudflare_email' => '',
146 + 'cloudflare_api_key' => '',
147 + 'cloudflare_zone' => '',
148 + 'enable_heartbeat' => false, // extension status
149 + 'heartbeat_dashboard_status' => 'enable', // enable,disable,modify
150 + 'heartbeat_dashboard_interval' => 60, // default interval in seconds
151 + 'heartbeat_editor_status' => 'enable', // enable,disable,modify
152 + 'heartbeat_editor_interval' => 15, // default interval in seconds
153 + 'heartbeat_frontend_status' => 'enable', // enable,disable,modify
154 + 'heartbeat_frontend_interval' => 60, // default interval in seconds
155 + 'enable_varnish' => false,
156 + 'varnish_ip' => '',
125 157 // misc
126 - 'cache_footprint' => true,
158 + 'cache_footprint' => true,
159 + 'async_cache_cleaning' => false,
160 + 'dev_mode' => false,
127 161 // new options needs to migrate from extensions
128 - 'enable_google_tracking' => false,
129 - 'enable_fb_tracking' => false,
162 + 'enable_google_tracking' => false,
163 + 'enable_fb_tracking' => false,
130 164 ];
131 165
132 166 /**
133 167 * Filter default settings.
@@ -173,9 +207,8 @@
173 207 * Object cache methods keys will use as option
174 208 *
175 209 * @return array $object_caches
176 210 * @since 1.2 apcu added
177 - *
178 211 * @since 1.0
179 212 */
180 213 function get_object_cache_dropins() {
181 214
@@ -335,8 +368,10 @@
335 368
336 369 /**
337 370 * Supported js execution methods
338 371 *
372 + * @depreacated since 3.2
373 + *
339 374 * @return mixed|void
340 375 */
341 376 function js_execution_methods() {
342 377 $methods = [
@@ -342,8 +377,9 @@
342 377 $methods = [
343 378 'blocking' => esc_html__( 'Blocking – (default)', 'powered-cache' ),
344 379 'async' => esc_html__( 'Non-blocking using async', 'powered-cache' ),
345 380 'defer' => esc_html__( 'Non-blocking using defer', 'powered-cache' ),
381 + 'delayed' => esc_html__( 'Delayed for user interaction', 'powered-cache' ),
346 382 ];
347 383
348 384 /**
349 385 * Filter supported JS execution methods.
@@ -386,26 +422,8 @@
386 422 return apply_filters( 'powered_cache_cdn_zones', $zones );
387 423 }
388 424
389 425 /**
390 - * Get counts for db entities
391 - *
392 - * @return array
393 - */
394 -function get_db_cleanup_counts() {
395 - return [
396 - 'post_revision' => 0,
397 - 'post_auto_draft' => 1,
398 - 'post_trashed' => 4,
399 - 'comment_spam' => 4,
400 - 'comment_trashed' => 4,
401 - 'transient_expired' => 4,
402 - 'transient_all' => 4,
403 - 'table_count' => 0,
404 - ];
405 -}
406 -
407 -/**
408 426 * Which version of plugin running
409 427 *
410 428 * @return bool
411 429 */
@@ -495,9 +513,8 @@
495 513 }
496 514
497 515 /**
498 516 * remove directories recursively
499 - *
500 517 * Adopted from W3TC Utility
501 518 *
502 519 * @param string $path The target path
503 520 * @param array $exclude list of the files that will excluded
@@ -701,80 +718,172 @@
701 718 * @since 1.0
702 719 * @since 1.1 powered_cache_post_related_urls filter added
703 720 */
704 721 function get_post_related_urls( $post_id ) {
722 + // Valid post statuses that require cache purging.
723 + $valid_post_statuses = [ 'publish', 'private', 'trash', 'pending', 'draft' ];
724 + $post_status = get_post_status( $post_id );
725 + $post = get_post( $post_id );
705 726
706 - $current_post_status = get_post_status( $post_id );
727 + // Post types that should not have their cache purged.
728 + $excluded_post_types = [ 'nav_menu_item', 'revision' ];
729 + $post_type = get_post_type( $post_id );
730 + $rest_api_route = 'wp/v2';
707 731
708 - // array to collect all our URLs
709 - $related_urls = array();
732 + $related_urls = [];
710 733
711 - if ( get_permalink( $post_id ) ) {
712 - // we're going to add a ton of things to flush.
734 + if ( false !== get_permalink( $post_id ) && in_array( $post_status, $valid_post_statuses, true ) && ! in_array( $post_type, $excluded_post_types, true ) ) {
735 + // Add the post URL.
736 + $related_urls[] = get_permalink( $post_id );
713 737
714 - // related category urls
715 - $categories = get_the_category( $post_id );
716 - if ( $categories ) {
717 - foreach ( $categories as $cat ) {
718 - array_push( $related_urls, get_category_link( $cat->term_id ) );
738 + // Add REST API URL if applicable.
739 + if ( $rest_api_route ) {
740 + $post_type_object = get_post_type_object( $post_type );
741 + if ( ! empty( $post_type_object->show_in_rest ) ) {
742 + $post_type_base = $post_type_object->rest_base ? $post_type_object->rest_base : $post_type_object->name;
743 + $related_urls[] = get_rest_url() . $rest_api_route . '/' . $post_type_base . '/' . $post_id . '/';
744 + } elseif ( in_array( $post_type, [ 'post', 'page' ], true ) ) {
745 + $related_urls[] = get_rest_url() . $rest_api_route . '/' . $post_type . 's/' . $post_id . '/';
719 746 }
720 747 }
721 748
722 - // related tags url
723 - $tags = get_the_tags( $post_id );
724 - if ( $tags ) {
725 - foreach ( $tags as $tag ) {
726 - array_push( $related_urls, get_tag_link( $tag->term_id ) );
749 + // Add AMP URL if AMP plugin is active.
750 + if ( function_exists( 'amp_get_permalink' ) ) {
751 + $related_urls[] = amp_get_permalink( $post_id );
752 + }
753 +
754 + // Regular AMP url for posts if ant of the following are active:
755 + // https://wordpress.org/plugins/accelerated-mobile-pages/
756 + if ( defined( 'AMPFORWP_AMP_QUERY_VAR' ) ) {
757 + $related_urls[] = get_permalink( $post_id ) . 'amp/';
758 + }
759 +
760 + // Handle trashed post URLs.
761 + if ( 'trash' === $post_status ) {
762 + $trash_permalink = str_replace( '__trashed', '', get_permalink( $post_id ) );
763 + $related_urls[] = $trash_permalink;
764 + $related_urls[] = $trash_permalink . 'feed/';
765 + }
766 +
767 + $taxonomies = get_object_taxonomies( get_post_type( $post_id ), 'objects' );
768 +
769 + // Purge terms associated with the post.
770 + foreach ( $taxonomies as $taxonomy ) {
771 + // Skip non-public taxonomies.
772 + if ( ! $taxonomy->public ) {
773 + continue;
727 774 }
728 - }
729 775
730 - // Author URL
731 - array_push( $related_urls, get_author_posts_url( get_post_field( 'post_author', $post_id ) ), get_author_feed_link( get_post_field( 'post_author', $post_id ) ) );
776 + $terms = get_the_terms( $post_id, $taxonomy->name );
732 777
733 - // Archives and their feeds
734 - if ( get_post_type_archive_link( get_post_type( $post_id ) ) === true ) {
735 - array_push( $related_urls, get_post_type_archive_link( get_post_type( $post_id ) ), get_post_type_archive_feed_link( get_post_type( $post_id ) ) );
778 + if ( empty( $terms ) || is_wp_error( $terms ) ) {
779 + continue;
780 + }
781 +
782 + foreach ( $terms as $term ) {
783 + $term_url = get_term_link( $term->slug, $taxonomy->name );
784 + if ( ! is_wp_error( $term_url ) ) {
785 + $related_urls[] = $term_url;
786 + if ( $taxonomy->show_in_rest ) {
787 + $taxonomy_base = $taxonomy->rest_base ? $taxonomy->rest_base : $taxonomy->name;
788 + $related_urls[] = rest_url( "{$taxonomy->rest_namespace}/{$taxonomy_base}/{$term->term_id}/" ); // REST API URL for the term
789 + }
790 + }
791 +
792 + if ( ! is_taxonomy_hierarchical( $taxonomy->name ) ) {
793 + continue;
794 + }
795 +
796 + $ancestors = (array) get_ancestors( $term->term_id, $taxonomy->name );
797 + foreach ( $ancestors as $ancestor ) {
798 + $ancestor_object = get_term( $ancestor, $taxonomy->name );
799 + if ( ! is_a( $ancestor, '\WP_Term' ) ) {
800 + continue;
801 + }
802 +
803 + $ancestor_term_url = get_term_link( $ancestor_object->slug, $taxonomy->name );
804 + if ( ! is_wp_error( $ancestor_term_url ) ) {
805 + $related_urls[] = $ancestor_term_url;
806 + if ( $taxonomy->show_in_rest ) {
807 + $taxonomy_base = $taxonomy->rest_base ? $taxonomy->rest_base : $taxonomy->name;
808 + $related_urls[] = rest_url( "{$taxonomy->rest_namespace}/{$taxonomy_base}/{$ancestor_object->term_id}/" ); // REST API URL for the ancestor term
809 + }
810 + }
811 + }
812 + }
736 813 }
737 814
738 - // Post URL
739 - array_push( $related_urls, get_permalink( $post_id ) );
815 + // Purge author and feed URLs for posts.
816 + if ( 'post' === $post_type ) {
817 + $author_id = get_post_field( 'post_author', $post_id );
818 + $related_urls[] = get_author_posts_url( $author_id );
819 + $related_urls[] = get_author_feed_link( $author_id );
820 + if ( $rest_api_route ) {
821 + $related_urls[] = get_rest_url() . $rest_api_route . '/users/' . $author_id . '/';
822 + }
740 823
741 - // Also clean URL for trashed post.
742 - if ( 'trash' === $current_post_status ) {
743 - $trashpost = get_permalink( $post_id );
744 - $trashpost = str_replace( '__trashed', '', $trashpost );
745 - array_push( $related_urls, $trashpost, $trashpost . 'feed/' );
824 + // Include various feed URLs.
825 + $feed_urls = [
826 + get_bloginfo_rss( 'rdf_url' ),
827 + get_bloginfo_rss( 'rss_url' ),
828 + get_bloginfo_rss( 'rss2_url' ),
829 + get_bloginfo_rss( 'atom_url' ),
830 + get_bloginfo_rss( 'comments_rss2_url' ),
831 + get_post_comments_feed_link( $post_id ),
832 + ];
833 + $related_urls = array_merge( $related_urls, $feed_urls );
746 834 }
747 835
748 - // Add in AMP permalink if Automattic's AMP is installed
749 - if ( function_exists( 'amp_get_permalink' ) ) {
750 - array_push( $related_urls, amp_get_permalink( $post_id ) );
836 + // Purge archive pages if not excluded.
837 + if ( ! in_array( $post_type, [ 'post', 'page' ], true ) ) {
838 + $related_urls[] = get_post_type_archive_link( $post_type );
839 + $related_urls[] = get_post_type_archive_feed_link( $post_type );
751 840 }
752 841
753 - // Regular AMP url for posts
754 - array_push( $related_urls, get_permalink( $post_id ) . 'amp/' );
842 + $post_date = strtotime( $post->post_date );
755 843
756 - // Feeds
757 - array_push( $related_urls, get_bloginfo_rss( 'rdf_url' ), get_bloginfo_rss( 'rss_url' ), get_bloginfo_rss( 'rss2_url' ), get_bloginfo_rss( 'atom_url' ), get_bloginfo_rss( 'comments_rss2_url' ), get_post_comments_feed_link( $post_id ) );
844 + if ( $post_date ) {
845 + // Generate the date archive URLs
846 + $year = gmdate( 'Y', $post_date );
847 + $month = gmdate( 'm', $post_date );
848 + $day = gmdate( 'd', $post_date );
758 849
759 - // Home Page and (if used) posts page
760 - array_push( $related_urls, trailingslashit( home_url() ) );
761 - if ( get_option( 'show_on_front' ) === 'page' ) {
762 - // Ensure we have a page_for_posts setting to avoid empty URL
763 - if ( get_option( 'page_for_posts' ) ) {
764 - array_push( $related_urls, get_permalink( get_option( 'page_for_posts' ) ) );
850 + $related_urls[] = get_year_link( $year );
851 + $related_urls[] = get_month_link( $year, $month );
852 + $related_urls[] = get_day_link( $year, $month, $day );
853 + }
854 +
855 + // Always purge the home page.
856 + $related_urls[] = home_url( '/' );
857 +
858 + // Purge the posts page if it's set to a static page.
859 + if ( 'page' === get_option( 'show_on_front' ) ) {
860 + $posts_page_id = get_option( 'page_for_posts' );
861 + if ( $posts_page_id ) {
862 + $related_urls[] = get_permalink( $posts_page_id );
765 863 }
766 864 }
767 865 }
768 866
867 + // Remove query strings and ensure unique URLs before purging.
868 + $related_urls = array_unique(
869 + array_map(
870 + function ( $url ) {
871 + return strtok( $url, '?' );
872 + },
873 + $related_urls
874 + )
875 + );
876 +
769 877 /**
770 878 * Filters post related urls.
771 879 *
772 - * @hook powered_cache_post_related_urls
880 + * @hook powered_cache_post_related_urls
773 881 *
774 - * @param {array} $related_urls The list of the URLs that related with the post.
882 + * @param {array} $related_urls The list of the URLs that related with the post.
775 883 *
776 - * @since 1.0
884 + * @return {array} New value.
885 + * @since 1.0
777 886 */
778 887 $related_urls = apply_filters( 'powered_cache_post_related_urls', $related_urls );
779 888
780 889 return $related_urls;
@@ -783,17 +892,17 @@
783 892
784 893 /**
785 894 * Delete cache file
786 895 *
787 - * @param string $url Target URL
896 + * @param string $url Target URL
897 + * @param bool $delete_subdirectories Whether delete subdirectories or not
788 898 *
789 899 * @return bool true when found cache dir, otherwise false
790 900 * @since 1.0
791 901 */
792 -function delete_page_cache( $url ) {
902 +function delete_page_cache( $url, $delete_subdirectories = false ) {
903 + $dir = get_url_dir( trim( $url ) );
793 904
794 - $dir = trailingslashit( get_url_dir( trim( $url ) ) );
795 -
796 905 if ( is_dir( $dir ) ) {
797 906 $files = scandir( $dir );
798 907 foreach ( $files as $file ) {
799 908 /**
@@ -799,15 +908,15 @@
799 908 /**
800 909 * Don't need to lookup for index-https, index-https-mobile etc..
801 910 * Just clean that directory's files only.
802 911 */
803 - if ( ! is_dir( $dir . $file ) && ! in_array( $file, array( '.', '..' ), true ) ) {
912 + if ( ! is_dir( $dir . $file ) && file_exists( $dir . $file ) && ! in_array( $file, array( '.', '..' ), true ) ) {
804 913 unlink( $dir . $file );
805 914 }
915 + }
806 916
807 - if ( file_exists( $dir ) && is_dir_empty( $dir ) ) {
808 - remove_dir( $dir );
809 - }
917 + if ( file_exists( $dir ) && ( $delete_subdirectories || is_dir_empty( $dir ) ) ) {
918 + remove_dir( $dir );
810 919 }
811 920
812 921 return true;
813 922 }
@@ -825,9 +934,9 @@
825 934 * @since 1.1
826 935 */
827 936 function get_url_dir( $url ) {
828 937 $url_info = wp_parse_url( $url );
829 - $sub_dir = $url_info['host'];
938 + $sub_dir = isset( $url_info['host'] ) ? $url_info['host'] : '';
830 939
831 940 if ( ! empty( $url_info['path'] ) ) {
832 941 $sub_dir .= $url_info['path'];
833 942 }
@@ -832,8 +941,9 @@
832 941 $sub_dir .= $url_info['path'];
833 942 }
834 943
835 944 $path = trailingslashit( get_page_cache_dir() ) . ltrim( $sub_dir, '/' );
945 + $path = trailingslashit( $path );
836 946
837 947 /**
838 948 * Filters the path of the given url in the cache directory.
839 949 *
@@ -859,8 +969,12 @@
859 969 $zones = $settings['cdn_zone'];
860 970
861 971 $cdn_addresses = array();
862 972 foreach ( $hostnames as $host_key => $host ) {
973 + if ( filter_var( $host, FILTER_VALIDATE_URL ) ) {
974 + $host = wp_parse_url( $host, PHP_URL_HOST );
975 + }
976 +
863 977 if ( ! empty( $host ) ) {
864 978 $cdn_addresses[ $zones[ $host_key ] ][] = $host;
865 979 }
866 980 }
@@ -869,12 +983,11 @@
869 983 * Filters CDN Addresses.
870 984 *
871 985 * @hook powered_cache_cdn_addresses
872 986 *
873 - * @param {array} $cdn_addresses CDN Adresses.
987 + * @param {array} $cdn_addresses CDN Addresses.
874 988 *
875 989 * @return {array} New value.
876 - *
877 990 * @since 1.0
878 991 */
879 992 return apply_filters( 'powered_cache_cdn_addresses', $cdn_addresses );
880 993 }
@@ -934,9 +1047,8 @@
934 1047 /**
935 1048 * Fires after cache flush.
936 1049 *
937 1050 * @hook powered_cache_flushed
938 - *
939 1051 * @since 1.0
940 1052 */
941 1053 do_action( 'powered_cache_flushed' );
942 1054 }
@@ -966,9 +1078,8 @@
966 1078 *
967 1079 * @param {string} $log_message The log message.
968 1080 *
969 1081 * @return {string} New value.
970 - *
971 1082 * @since 2.0
972 1083 */
973 1084 $log_message = apply_filters( 'powered_cache_log_message', $log_message );
974 1085
@@ -976,15 +1087,14 @@
976 1087 * Filters log message type.
977 1088 *
978 1089 * @hook powered_cache_log_message_type
979 1090 *
980 - * @param {null|int} null default message type
1091 + * @param {int} 0 default message type since 3.6
981 1092 *
982 - * @return {null|int} New value.
983 - *
1093 + * @return {int} New value.
984 1094 * @since 2.0
985 1095 */
986 - $message_type = apply_filters( 'powered_cache_log_message_type', null );
1096 + $message_type = apply_filters( 'powered_cache_log_message_type', 0 );
987 1097 $destination = null;
988 1098
989 1099 if ( defined( 'POWERED_CACHE_LOG_FILE' ) ) {
990 1100 $destination = POWERED_CACHE_LOG_FILE;
@@ -998,9 +1108,8 @@
998 1108 *
999 1109 * @param {null|string} $destination The destination of the log.
1000 1110 *
1001 1111 * @return {null|string} New value.
1002 - *
1003 1112 * @since 2.0
1004 1113 */
1005 1114 $log_destination = apply_filters( 'powered_cache_log_destination', $destination );
1006 1115
@@ -1008,8 +1117,10 @@
1008 1117 if ( defined( 'POWERED_CACHE_LOG_IP' ) && POWERED_CACHE_LOG_IP !== get_client_ip() ) {
1009 1118 return false;
1010 1119 }
1011 1120
1121 + $message_type = absint( $message_type );
1122 +
1012 1123 return error_log( $log_message, $message_type, $log_destination ); // phpcs:ignore
1013 1124 }
1014 1125
1015 1126 /**
@@ -1018,12 +1129,14 @@
1018 1129 * @return mixed
1019 1130 */
1020 1131 function get_client_ip() {
1021 1132 if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1022 - return $_SERVER['HTTP_X_FORWARDED_FOR'];
1133 + return wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1023 1134 }
1024 1135
1025 - return $_SERVER['REMOTE_ADDR'];
1136 + if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
1137 + return wp_unslash( $_SERVER['REMOTE_ADDR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1138 + }
1026 1139 }
1027 1140
1028 1141 /**
1029 1142 * Fragment caching
@@ -1115,9 +1228,8 @@
1115 1228 *
1116 1229 * @param {array} $known_headers The list of known HTTP headers.
1117 1230 *
1118 1231 * @return {array} New value.
1119 - *
1120 1232 * @since 1.2
1121 1233 */
1122 1234 $known_headers = apply_filters( 'powered_cache_known_headers', $known_headers );
1123 1235
@@ -1242,5 +1354,239 @@
1242 1354 $doc_url .= '#' . $fragment;
1243 1355 }
1244 1356
1245 1357 return $doc_url;
1358 +}
1359 +
1360 +/**
1361 + * Sanitize CSS
1362 + *
1363 + * @param string $css Input
1364 + *
1365 + * @return string|string[] $css
1366 + * @since 2.1
1367 + */
1368 +function sanitize_css( $css ) {
1369 + $css = wp_strip_all_tags( $css );
1370 +
1371 + if ( false !== strpos( $css, '<' ) ) {
1372 + $css = preg_replace( '#<(\/?\w+)#', '\00003C$1', $css );
1373 + }
1374 +
1375 + return $css;
1376 +}
1377 +
1378 +
1379 +/**
1380 + * Test if the current browser runs on a mobile device (smart phone, tablet, etc.)
1381 + * Sort of custom version of wp_is_mobile
1382 + */
1383 +function powered_cache_is_mobile() {
1384 +
1385 + global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
1386 +
1387 + $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', (string) $powered_cache_mobile_browsers ) ), ' ' );
1388 + $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', (string) $powered_cache_mobile_prefixes ) ), ' ' );
1389 +
1390 + if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
1391 + return false;
1392 + }
1393 +
1394 + $user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1395 +
1396 + if ( ( preg_match( '#^.*(' . $mobile_browsers . ').*#i', $user_agent ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $user_agent, 0, 4 ) ) ) ) {
1397 + return true;
1398 + }
1399 +
1400 + return false;
1401 +}
1402 +
1403 +/**
1404 + * If the site is a local site.
1405 + *
1406 + * @return bool
1407 + * @since 2.2
1408 + */
1409 +function is_local_site() {
1410 + $site_url = site_url();
1411 +
1412 + // Check for localhost and sites using an IP only first.
1413 + $is_local = $site_url && false === strpos( $site_url, '.' );
1414 +
1415 + // Use Core's environment check, if available. Added in 5.5.0 / 5.5.1 (for `local` return value).
1416 + if ( function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type() ) {
1417 + $is_local = true;
1418 + }
1419 +
1420 + // Then check for usual usual domains used by local dev tools.
1421 + $known_local = array(
1422 + '#\.local$#i',
1423 + '#\.localhost$#i',
1424 + '#\.test$#i',
1425 + '#\.docksal$#i', // Docksal.
1426 + '#\.docksal\.site$#i', // Docksal.
1427 + '#\.dev\.cc$#i', // ServerPress.
1428 + '#\.lndo\.site$#i', // Lando.
1429 + );
1430 +
1431 + if ( ! $is_local ) {
1432 + foreach ( $known_local as $url ) {
1433 + if ( preg_match( $url, $site_url ) ) {
1434 + $is_local = true;
1435 + break;
1436 + }
1437 + }
1438 + }
1439 +
1440 + /**
1441 + * Filters is_local_site check.
1442 + *
1443 + * @param bool $is_local If the current site is a local site.
1444 + *
1445 + * @since 2.2
1446 + */
1447 + $is_local = apply_filters( 'powered_cache_is_local_site', $is_local );
1448 +
1449 + return $is_local;
1450 +}
1451 +
1452 +/**
1453 + * Check whether request for bypass or process normally
1454 + *
1455 + * @return bool
1456 + * @since 3.0
1457 + */
1458 +function bypass_request() {
1459 + if ( isset( $_GET['nopoweredcache'] ) && $_GET['nopoweredcache'] ) { // phpcs:ignore
1460 + return true;
1461 + }
1462 +
1463 + return false;
1464 +}
1465 +
1466 +
1467 +/**
1468 + * Calculate total amount of autoloaded data.
1469 + *
1470 + * @return int autoloaded data in bytes.
1471 + * @global wpdb $wpdb WordPress database abstraction object.
1472 + * @since 3.4
1473 + */
1474 +function autoloaded_options_size() {
1475 + global $wpdb;
1476 +
1477 + return (int) $wpdb->get_var( 'SELECT SUM(LENGTH(option_value)) FROM ' . $wpdb->prefix . 'options WHERE autoload = \'yes\'' ); // phpcs:ignore
1478 +}
1479 +
1480 +/**
1481 + * Mask the string with asterisk
1482 + *
1483 + * @param string $input_string String
1484 + * @param int $unmask_length The length of the string that will not be masked
1485 + *
1486 + * @return string
1487 + * @since 3.4
1488 + */
1489 +function mask_string( $input_string, $unmask_length ) {
1490 + $output_string = substr( $input_string, 0, $unmask_length );
1491 +
1492 + if ( strlen( $input_string ) > $unmask_length ) {
1493 + $output_string .= str_repeat( '*', strlen( $input_string ) - $unmask_length );
1494 + }
1495 +
1496 + return $output_string;
1497 +}
1498 +
1499 +/**
1500 + * Get sensitive data in decrypted form
1501 + *
1502 + * @param string $field field name
1503 + *
1504 + * @return bool|mixed|string
1505 + */
1506 +function get_decrypted_setting( $field ) {
1507 + $settings = \PoweredCache\Utils\get_settings();
1508 + $value = isset( $settings[ $field ] ) ? $settings[ $field ] : '';
1509 +
1510 + // decrypt the value
1511 + $encryption = new Encryption();
1512 + $decrypted_value = $encryption->decrypt( $value );
1513 + if ( false !== $decrypted_value ) {
1514 + return $decrypted_value;
1515 + }
1516 +
1517 + return $value;
1518 +}
1519 +
1520 +
1521 +/**
1522 + * Check if a given IP is within a specific range.
1523 + * Supports both IPv4 and IPv6 addresses.
1524 + *
1525 + * @param string $ip The IP address to check.
1526 + * @param string $range The IP range in CIDR notation.
1527 + *
1528 + * @return bool True if the IP is in the range, false otherwise.
1529 + */
1530 +function is_ip_in_range( $ip, $range ) {
1531 + if ( false !== strpos( $range, '/' ) ) {
1532 + list( $subnet, $bits ) = explode( '/', $range, 2 );
1533 + } else {
1534 + $subnet = $range;
1535 + $bits = ( false === filter_var( $subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) ? 32 : 128;
1536 + }
1537 +
1538 + $bits = intval( $bits );
1539 +
1540 + if ( false !== filter_var( $subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
1541 + $subnet_bin = inet_pton( $subnet );
1542 + $ip_bin = inet_pton( $ip );
1543 +
1544 + if ( false === $subnet_bin || false === $ip_bin ) {
1545 + return false;
1546 + }
1547 +
1548 + $subnet_bin = str_pad( $subnet_bin, 16, "\0" );
1549 + $ip_bin = str_pad( $ip_bin, 16, "\0" );
1550 +
1551 + for ( $i = 0; $i * 8 < $bits; $i ++ ) {
1552 + if ( $bits >= ( $i + 1 ) * 8 && $subnet_bin[ $i ] !== $ip_bin[ $i ] ) {
1553 + return false;
1554 + } elseif ( $bits > $i * 8 ) {
1555 + $bitmask = 0xff00 >> ( $bits % 8 );
1556 + if ( ( ord( $subnet_bin[ $i ] ) & $bitmask ) !== ( ord( $ip_bin[ $i ] ) & $bitmask ) ) {
1557 + return false;
1558 + }
1559 + }
1560 + }
1561 +
1562 + return true;
1563 + }
1564 +
1565 + if ( false === filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
1566 + return false;
1567 + }
1568 +
1569 + $subnet_decimal = ip2long( $subnet );
1570 + $ip_decimal = ip2long( $ip );
1571 + $mask_decimal = - 1 << ( 32 - $bits );
1572 +
1573 + return ( $subnet_decimal & $mask_decimal ) === ( $ip_decimal & $mask_decimal );
1574 +}
1575 +
1576 +/**
1577 + * Check if the dev mode is active
1578 + *
1579 + * @return bool
1580 + * @since 3.6
1581 + */
1582 +function is_dev_mode_active() {
1583 + // Check global config first (fast)
1584 + if ( ! empty( $GLOBALS['powered_cache_options']['dev_mode'] ) ) {
1585 + return true;
1586 + }
1587 +
1588 + // Fallback to database option
1589 + $settings = \PoweredCache\Utils\get_settings();
1590 +
1591 + return ! empty( $settings['dev_mode'] );
1246 1592 }