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 +469 -147 2.1.23.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,97 +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 - 'critical_css' => false,
70 - 'critical_css_additional_files' => '',
71 - 'critical_css_excluded_files' => '',
72 - 'critical_css_appended_content' => '',
73 - 'critical_css_fallback' => '',
74 - 'excluded_css_files' => '',
75 - 'minify_js' => false,
76 - 'combine_js' => false,
77 - 'excluded_js_files' => '',
78 - 'js_execution_method' => 'blocking',
79 - '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,
80 97 // media optimization
98 + 'enable_image_optimization' => false,
99 + 'image_optimizer_preferred_format' => '',
100 + 'add_missing_image_dimensions' => false,
81 101 // lazyload
82 - 'enable_lazy_load' => false,
83 - 'lazy_load_post_content' => true,
84 - 'lazy_load_images' => true,
85 - 'lazy_load_iframes' => true,
86 - 'lazy_load_widgets' => true,
87 - 'lazy_load_post_thumbnail' => true,
88 - 'lazy_load_avatars' => true,
89 - 'disable_wp_lazy_load' => false,
90 - 'disable_wp_embeds' => false,
91 - '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,
92 115 // cdn
93 - 'enable_cdn' => false,
94 - 'cdn_hostname' => array( '' ),
95 - 'cdn_zone' => array( '' ),
96 - 'cdn_rejected_files' => '',
116 + 'enable_cdn' => false,
117 + 'cdn_hostname' => array( '' ),
118 + 'cdn_zone' => array( '' ),
119 + 'cdn_rejected_files' => '',
97 120 // preload
98 - 'enable_cache_preload' => false,
99 - 'preload_homepage' => true,
100 - 'preload_public_posts' => true,
101 - 'preload_public_tax' => true,
102 - 'enable_sitemap_preload' => false,
103 - 'preload_sitemap' => '',
104 - '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,
105 131 // db options
106 - 'db_cleanup_post_revisions' => false,
107 - 'db_cleanup_auto_drafts' => false,
108 - 'db_cleanup_trashed_posts' => false,
109 - 'db_cleanup_spam_comments' => false,
110 - 'db_cleanup_trashed_comments' => false,
111 - 'db_cleanup_expired_transients' => false,
112 - 'db_cleanup_all_transients' => false,
113 - 'db_cleanup_optimize_tables' => false,
114 - 'enable_scheduled_db_cleanup' => false,
115 - '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',
116 142 // add-ons
117 - 'enable_cloudflare' => false,
118 - 'cloudflare_email' => '',
119 - 'cloudflare_api_key' => '',
120 - 'cloudflare_zone' => '',
121 - 'enable_heartbeat' => false, // extention status
122 - 'heartbeat_dashboard_status' => 'enable', // enable,disable,modify
123 - 'heartbeat_dashboard_interval' => 60, // default interval in seconds
124 - 'heartbeat_editor_status' => 'enable', // enable,disable,modify
125 - 'heartbeat_editor_interval' => 15, // default interval in seconds
126 - 'heartbeat_frontend_status' => 'enable', // enable,disable,modify
127 - 'heartbeat_frontend_interval' => 60, // default interval in seconds
128 - 'enable_varnish' => false,
129 - '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' => '',
130 157 // misc
131 - 'cache_footprint' => true,
158 + 'cache_footprint' => true,
159 + 'async_cache_cleaning' => false,
160 + 'dev_mode' => false,
132 161 // new options needs to migrate from extensions
133 - 'enable_google_tracking' => false,
134 - 'enable_fb_tracking' => false,
162 + 'enable_google_tracking' => false,
163 + 'enable_fb_tracking' => false,
135 164 ];
136 165
137 166 /**
138 167 * Filter default settings.
@@ -178,9 +207,8 @@
178 207 * Object cache methods keys will use as option
179 208 *
180 209 * @return array $object_caches
181 210 * @since 1.2 apcu added
182 - *
183 211 * @since 1.0
184 212 */
185 213 function get_object_cache_dropins() {
186 214
@@ -340,8 +368,10 @@
340 368
341 369 /**
342 370 * Supported js execution methods
343 371 *
372 + * @depreacated since 3.2
373 + *
344 374 * @return mixed|void
345 375 */
346 376 function js_execution_methods() {
347 377 $methods = [
@@ -347,8 +377,9 @@
347 377 $methods = [
348 378 'blocking' => esc_html__( 'Blocking – (default)', 'powered-cache' ),
349 379 'async' => esc_html__( 'Non-blocking using async', 'powered-cache' ),
350 380 'defer' => esc_html__( 'Non-blocking using defer', 'powered-cache' ),
381 + 'delayed' => esc_html__( 'Delayed for user interaction', 'powered-cache' ),
351 382 ];
352 383
353 384 /**
354 385 * Filter supported JS execution methods.
@@ -482,9 +513,8 @@
482 513 }
483 514
484 515 /**
485 516 * remove directories recursively
486 - *
487 517 * Adopted from W3TC Utility
488 518 *
489 519 * @param string $path The target path
490 520 * @param array $exclude list of the files that will excluded
@@ -688,80 +718,172 @@
688 718 * @since 1.0
689 719 * @since 1.1 powered_cache_post_related_urls filter added
690 720 */
691 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 );
692 726
693 - $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';
694 731
695 - // array to collect all our URLs
696 - $related_urls = array();
732 + $related_urls = [];
697 733
698 - if ( get_permalink( $post_id ) ) {
699 - // 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 );
700 737
701 - // related category urls
702 - $categories = get_the_category( $post_id );
703 - if ( $categories ) {
704 - foreach ( $categories as $cat ) {
705 - 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 . '/';
706 746 }
707 747 }
708 748
709 - // related tags url
710 - $tags = get_the_tags( $post_id );
711 - if ( $tags ) {
712 - foreach ( $tags as $tag ) {
713 - 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;
714 774 }
715 - }
716 775
717 - // Author URL
718 - 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 );
719 777
720 - // Archives and their feeds
721 - if ( get_post_type_archive_link( get_post_type( $post_id ) ) === true ) {
722 - 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 + }
723 813 }
724 814
725 - // Post URL
726 - 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 + }
727 823
728 - // Also clean URL for trashed post.
729 - if ( 'trash' === $current_post_status ) {
730 - $trashpost = get_permalink( $post_id );
731 - $trashpost = str_replace( '__trashed', '', $trashpost );
732 - 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 );
733 834 }
734 835
735 - // Add in AMP permalink if Automattic's AMP is installed
736 - if ( function_exists( 'amp_get_permalink' ) ) {
737 - 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 );
738 840 }
739 841
740 - // Regular AMP url for posts
741 - array_push( $related_urls, get_permalink( $post_id ) . 'amp/' );
842 + $post_date = strtotime( $post->post_date );
742 843
743 - // Feeds
744 - 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 );
745 849
746 - // Home Page and (if used) posts page
747 - array_push( $related_urls, trailingslashit( home_url() ) );
748 - if ( get_option( 'show_on_front' ) === 'page' ) {
749 - // Ensure we have a page_for_posts setting to avoid empty URL
750 - if ( get_option( 'page_for_posts' ) ) {
751 - 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 );
752 863 }
753 864 }
754 865 }
755 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 +
756 877 /**
757 878 * Filters post related urls.
758 879 *
759 - * @hook powered_cache_post_related_urls
880 + * @hook powered_cache_post_related_urls
760 881 *
761 - * @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.
762 883 *
763 - * @since 1.0
884 + * @return {array} New value.
885 + * @since 1.0
764 886 */
765 887 $related_urls = apply_filters( 'powered_cache_post_related_urls', $related_urls );
766 888
767 889 return $related_urls;
@@ -770,17 +892,17 @@
770 892
771 893 /**
772 894 * Delete cache file
773 895 *
774 - * @param string $url Target URL
896 + * @param string $url Target URL
897 + * @param bool $delete_subdirectories Whether delete subdirectories or not
775 898 *
776 899 * @return bool true when found cache dir, otherwise false
777 900 * @since 1.0
778 901 */
779 -function delete_page_cache( $url ) {
902 +function delete_page_cache( $url, $delete_subdirectories = false ) {
903 + $dir = get_url_dir( trim( $url ) );
780 904
781 - $dir = trailingslashit( get_url_dir( trim( $url ) ) );
782 -
783 905 if ( is_dir( $dir ) ) {
784 906 $files = scandir( $dir );
785 907 foreach ( $files as $file ) {
786 908 /**
@@ -786,15 +908,15 @@
786 908 /**
787 909 * Don't need to lookup for index-https, index-https-mobile etc..
788 910 * Just clean that directory's files only.
789 911 */
790 - 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 ) ) {
791 913 unlink( $dir . $file );
792 914 }
915 + }
793 916
794 - if ( file_exists( $dir ) && is_dir_empty( $dir ) ) {
795 - remove_dir( $dir );
796 - }
917 + if ( file_exists( $dir ) && ( $delete_subdirectories || is_dir_empty( $dir ) ) ) {
918 + remove_dir( $dir );
797 919 }
798 920
799 921 return true;
800 922 }
@@ -812,9 +934,9 @@
812 934 * @since 1.1
813 935 */
814 936 function get_url_dir( $url ) {
815 937 $url_info = wp_parse_url( $url );
816 - $sub_dir = $url_info['host'];
938 + $sub_dir = isset( $url_info['host'] ) ? $url_info['host'] : '';
817 939
818 940 if ( ! empty( $url_info['path'] ) ) {
819 941 $sub_dir .= $url_info['path'];
820 942 }
@@ -819,8 +941,9 @@
819 941 $sub_dir .= $url_info['path'];
820 942 }
821 943
822 944 $path = trailingslashit( get_page_cache_dir() ) . ltrim( $sub_dir, '/' );
945 + $path = trailingslashit( $path );
823 946
824 947 /**
825 948 * Filters the path of the given url in the cache directory.
826 949 *
@@ -846,8 +969,12 @@
846 969 $zones = $settings['cdn_zone'];
847 970
848 971 $cdn_addresses = array();
849 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 +
850 977 if ( ! empty( $host ) ) {
851 978 $cdn_addresses[ $zones[ $host_key ] ][] = $host;
852 979 }
853 980 }
@@ -856,12 +983,11 @@
856 983 * Filters CDN Addresses.
857 984 *
858 985 * @hook powered_cache_cdn_addresses
859 986 *
860 - * @param {array} $cdn_addresses CDN Adresses.
987 + * @param {array} $cdn_addresses CDN Addresses.
861 988 *
862 989 * @return {array} New value.
863 - *
864 990 * @since 1.0
865 991 */
866 992 return apply_filters( 'powered_cache_cdn_addresses', $cdn_addresses );
867 993 }
@@ -921,9 +1047,8 @@
921 1047 /**
922 1048 * Fires after cache flush.
923 1049 *
924 1050 * @hook powered_cache_flushed
925 - *
926 1051 * @since 1.0
927 1052 */
928 1053 do_action( 'powered_cache_flushed' );
929 1054 }
@@ -953,9 +1078,8 @@
953 1078 *
954 1079 * @param {string} $log_message The log message.
955 1080 *
956 1081 * @return {string} New value.
957 - *
958 1082 * @since 2.0
959 1083 */
960 1084 $log_message = apply_filters( 'powered_cache_log_message', $log_message );
961 1085
@@ -963,15 +1087,14 @@
963 1087 * Filters log message type.
964 1088 *
965 1089 * @hook powered_cache_log_message_type
966 1090 *
967 - * @param {null|int} null default message type
1091 + * @param {int} 0 default message type since 3.6
968 1092 *
969 - * @return {null|int} New value.
970 - *
1093 + * @return {int} New value.
971 1094 * @since 2.0
972 1095 */
973 - $message_type = apply_filters( 'powered_cache_log_message_type', null );
1096 + $message_type = apply_filters( 'powered_cache_log_message_type', 0 );
974 1097 $destination = null;
975 1098
976 1099 if ( defined( 'POWERED_CACHE_LOG_FILE' ) ) {
977 1100 $destination = POWERED_CACHE_LOG_FILE;
@@ -985,9 +1108,8 @@
985 1108 *
986 1109 * @param {null|string} $destination The destination of the log.
987 1110 *
988 1111 * @return {null|string} New value.
989 - *
990 1112 * @since 2.0
991 1113 */
992 1114 $log_destination = apply_filters( 'powered_cache_log_destination', $destination );
993 1115
@@ -995,8 +1117,10 @@
995 1117 if ( defined( 'POWERED_CACHE_LOG_IP' ) && POWERED_CACHE_LOG_IP !== get_client_ip() ) {
996 1118 return false;
997 1119 }
998 1120
1121 + $message_type = absint( $message_type );
1122 +
999 1123 return error_log( $log_message, $message_type, $log_destination ); // phpcs:ignore
1000 1124 }
1001 1125
1002 1126 /**
@@ -1005,12 +1129,14 @@
1005 1129 * @return mixed
1006 1130 */
1007 1131 function get_client_ip() {
1008 1132 if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1009 - return $_SERVER['HTTP_X_FORWARDED_FOR'];
1133 + return wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1010 1134 }
1011 1135
1012 - return $_SERVER['REMOTE_ADDR'];
1136 + if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
1137 + return wp_unslash( $_SERVER['REMOTE_ADDR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1138 + }
1013 1139 }
1014 1140
1015 1141 /**
1016 1142 * Fragment caching
@@ -1102,9 +1228,8 @@
1102 1228 *
1103 1229 * @param {array} $known_headers The list of known HTTP headers.
1104 1230 *
1105 1231 * @return {array} New value.
1106 - *
1107 1232 * @since 1.2
1108 1233 */
1109 1234 $known_headers = apply_filters( 'powered_cache_known_headers', $known_headers );
1110 1235
@@ -1258,13 +1383,210 @@
1258 1383 function powered_cache_is_mobile() {
1259 1384
1260 1385 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
1261 1386
1262 - $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_browsers ) ), ' ' );
1263 - $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_prefixes ) ), ' ' );
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 ) ), ' ' );
1264 1389
1265 - if ( ( preg_match( '#^.*(' . $mobile_browsers . ').*#i', $_SERVER['HTTP_USER_AGENT'] ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $_SERVER['HTTP_USER_AGENT'], 0, 4 ) ) ) ) {
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 ) ) ) ) {
1266 1397 return true;
1267 1398 }
1268 1399
1269 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'] );
1270 1592 }