PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / trunk
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score vtrunk
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 +420 -150 2.2.1trunk 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,100 +43,126 @@
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 - 'swap_google_fonts_display' => true,
68 - 'minify_css' => false,
69 - 'combine_css' => false,
70 - 'critical_css' => false,
71 - 'critical_css_additional_files' => '',
72 - 'critical_css_excluded_files' => '',
73 - 'critical_css_appended_content' => '',
74 - 'critical_css_fallback' => '',
75 - 'excluded_css_files' => '',
76 - 'minify_js' => false,
77 - 'combine_js' => false,
78 - 'excluded_js_files' => '',
79 - 'js_execution_method' => 'blocking',
80 - '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,
81 97 // media optimization
82 - 'enable_image_optimization' => false,
98 + 'enable_image_optimization' => false,
99 + 'image_optimizer_preferred_format' => '',
100 + 'add_missing_image_dimensions' => false,
83 101 // lazyload
84 - 'enable_lazy_load' => false,
85 - 'lazy_load_post_content' => true,
86 - 'lazy_load_images' => true,
87 - 'lazy_load_iframes' => true,
88 - 'lazy_load_widgets' => true,
89 - 'lazy_load_post_thumbnail' => true,
90 - 'lazy_load_avatars' => true,
91 - 'disable_wp_lazy_load' => false,
92 - 'disable_wp_embeds' => false,
93 - '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,
94 115 // cdn
95 - 'enable_cdn' => false,
96 - 'cdn_hostname' => array( '' ),
97 - 'cdn_zone' => array( '' ),
98 - 'cdn_rejected_files' => '',
116 + 'enable_cdn' => false,
117 + 'cdn_hostname' => array( '' ),
118 + 'cdn_zone' => array( '' ),
119 + 'cdn_rejected_files' => '',
99 120 // preload
100 - 'enable_cache_preload' => false,
101 - 'preload_homepage' => true,
102 - 'preload_public_posts' => true,
103 - 'preload_public_tax' => true,
104 - 'enable_sitemap_preload' => false,
105 - 'preload_sitemap' => '',
106 - 'prefetch_dns' => '',
107 - 'preconnect_resource' => '',
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,
131 + 'enable_lcp_optimization' => false,
108 132 // db options
109 - 'db_cleanup_post_revisions' => false,
110 - 'db_cleanup_auto_drafts' => false,
111 - 'db_cleanup_trashed_posts' => false,
112 - 'db_cleanup_spam_comments' => false,
113 - 'db_cleanup_trashed_comments' => false,
114 - 'db_cleanup_expired_transients' => false,
115 - 'db_cleanup_all_transients' => false,
116 - 'db_cleanup_optimize_tables' => false,
117 - 'enable_scheduled_db_cleanup' => false,
118 - 'scheduled_db_cleanup_frequency' => 'daily',
133 + 'db_cleanup_post_revisions' => false,
134 + 'db_cleanup_auto_drafts' => false,
135 + 'db_cleanup_trashed_posts' => false,
136 + 'db_cleanup_spam_comments' => false,
137 + 'db_cleanup_trashed_comments' => false,
138 + 'db_cleanup_expired_transients' => false,
139 + 'db_cleanup_all_transients' => false,
140 + 'db_cleanup_optimize_tables' => false,
141 + 'enable_scheduled_db_cleanup' => false,
142 + 'scheduled_db_cleanup_frequency' => 'daily',
119 143 // add-ons
120 - 'enable_cloudflare' => false,
121 - 'cloudflare_email' => '',
122 - 'cloudflare_api_key' => '',
123 - 'cloudflare_zone' => '',
124 - 'enable_heartbeat' => false, // extention status
125 - 'heartbeat_dashboard_status' => 'enable', // enable,disable,modify
126 - 'heartbeat_dashboard_interval' => 60, // default interval in seconds
127 - 'heartbeat_editor_status' => 'enable', // enable,disable,modify
128 - 'heartbeat_editor_interval' => 15, // default interval in seconds
129 - 'heartbeat_frontend_status' => 'enable', // enable,disable,modify
130 - 'heartbeat_frontend_interval' => 60, // default interval in seconds
131 - 'enable_varnish' => false,
132 - 'varnish_ip' => '',
144 + 'enable_cloudflare' => false,
145 + 'cloudflare_api_token' => '',
146 + 'cloudflare_email' => '',
147 + 'cloudflare_api_key' => '',
148 + 'cloudflare_zone' => '',
149 + 'enable_heartbeat' => false, // extension status
150 + 'heartbeat_dashboard_status' => 'enable', // enable,disable,modify
151 + 'heartbeat_dashboard_interval' => 60, // default interval in seconds
152 + 'heartbeat_editor_status' => 'enable', // enable,disable,modify
153 + 'heartbeat_editor_interval' => 15, // default interval in seconds
154 + 'heartbeat_frontend_status' => 'enable', // enable,disable,modify
155 + 'heartbeat_frontend_interval' => 60, // default interval in seconds
156 + 'enable_varnish' => false,
157 + 'varnish_ip' => '',
133 158 // misc
134 - 'cache_footprint' => true,
159 + 'cache_footprint' => true,
160 + 'async_cache_cleaning' => false,
161 + 'dev_mode' => false,
135 162 // new options needs to migrate from extensions
136 - 'enable_google_tracking' => false,
137 - 'enable_fb_tracking' => false,
163 + 'enable_google_tracking' => false,
164 + 'enable_fb_tracking' => false,
138 165 ];
139 166
140 167 /**
141 168 * Filter default settings.
@@ -181,9 +208,8 @@
181 208 * Object cache methods keys will use as option
182 209 *
183 210 * @return array $object_caches
184 211 * @since 1.2 apcu added
185 - *
186 212 * @since 1.0
187 213 */
188 214 function get_object_cache_dropins() {
189 215
@@ -343,8 +369,10 @@
343 369
344 370 /**
345 371 * Supported js execution methods
346 372 *
373 + * @depreacated since 3.2
374 + *
347 375 * @return mixed|void
348 376 */
349 377 function js_execution_methods() {
350 378 $methods = [
@@ -350,8 +378,9 @@
350 378 $methods = [
351 379 'blocking' => esc_html__( 'Blocking – (default)', 'powered-cache' ),
352 380 'async' => esc_html__( 'Non-blocking using async', 'powered-cache' ),
353 381 'defer' => esc_html__( 'Non-blocking using defer', 'powered-cache' ),
382 + 'delayed' => esc_html__( 'Delayed for user interaction', 'powered-cache' ),
354 383 ];
355 384
356 385 /**
357 386 * Filter supported JS execution methods.
@@ -485,9 +514,8 @@
485 514 }
486 515
487 516 /**
488 517 * remove directories recursively
489 - *
490 518 * Adopted from W3TC Utility
491 519 *
492 520 * @param string $path The target path
493 521 * @param array $exclude list of the files that will excluded
@@ -691,80 +719,172 @@
691 719 * @since 1.0
692 720 * @since 1.1 powered_cache_post_related_urls filter added
693 721 */
694 722 function get_post_related_urls( $post_id ) {
723 + // Valid post statuses that require cache purging.
724 + $valid_post_statuses = [ 'publish', 'private', 'trash', 'pending', 'draft' ];
725 + $post_status = get_post_status( $post_id );
726 + $post = get_post( $post_id );
695 727
696 - $current_post_status = get_post_status( $post_id );
728 + // Post types that should not have their cache purged.
729 + $excluded_post_types = [ 'nav_menu_item', 'revision' ];
730 + $post_type = get_post_type( $post_id );
731 + $rest_api_route = 'wp/v2';
697 732
698 - // array to collect all our URLs
699 - $related_urls = array();
733 + $related_urls = [];
700 734
701 - if ( get_permalink( $post_id ) ) {
702 - // we're going to add a ton of things to flush.
735 + if ( false !== get_permalink( $post_id ) && in_array( $post_status, $valid_post_statuses, true ) && ! in_array( $post_type, $excluded_post_types, true ) ) {
736 + // Add the post URL.
737 + $related_urls[] = get_permalink( $post_id );
703 738
704 - // related category urls
705 - $categories = get_the_category( $post_id );
706 - if ( $categories ) {
707 - foreach ( $categories as $cat ) {
708 - array_push( $related_urls, get_category_link( $cat->term_id ) );
739 + // Add REST API URL if applicable.
740 + if ( $rest_api_route ) {
741 + $post_type_object = get_post_type_object( $post_type );
742 + if ( ! empty( $post_type_object->show_in_rest ) ) {
743 + $post_type_base = $post_type_object->rest_base ? $post_type_object->rest_base : $post_type_object->name;
744 + $related_urls[] = get_rest_url() . $rest_api_route . '/' . $post_type_base . '/' . $post_id . '/';
745 + } elseif ( in_array( $post_type, [ 'post', 'page' ], true ) ) {
746 + $related_urls[] = get_rest_url() . $rest_api_route . '/' . $post_type . 's/' . $post_id . '/';
709 747 }
710 748 }
711 749
712 - // related tags url
713 - $tags = get_the_tags( $post_id );
714 - if ( $tags ) {
715 - foreach ( $tags as $tag ) {
716 - array_push( $related_urls, get_tag_link( $tag->term_id ) );
750 + // Add AMP URL if AMP plugin is active.
751 + if ( function_exists( 'amp_get_permalink' ) ) {
752 + $related_urls[] = amp_get_permalink( $post_id );
753 + }
754 +
755 + // Regular AMP url for posts if ant of the following are active:
756 + // https://wordpress.org/plugins/accelerated-mobile-pages/
757 + if ( defined( 'AMPFORWP_AMP_QUERY_VAR' ) ) {
758 + $related_urls[] = get_permalink( $post_id ) . 'amp/';
759 + }
760 +
761 + // Handle trashed post URLs.
762 + if ( 'trash' === $post_status ) {
763 + $trash_permalink = str_replace( '__trashed', '', get_permalink( $post_id ) );
764 + $related_urls[] = $trash_permalink;
765 + $related_urls[] = $trash_permalink . 'feed/';
766 + }
767 +
768 + $taxonomies = get_object_taxonomies( get_post_type( $post_id ), 'objects' );
769 +
770 + // Purge terms associated with the post.
771 + foreach ( $taxonomies as $taxonomy ) {
772 + // Skip non-public taxonomies.
773 + if ( ! $taxonomy->public ) {
774 + continue;
717 775 }
718 - }
719 776
720 - // Author URL
721 - 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 ) ) );
777 + $terms = get_the_terms( $post_id, $taxonomy->name );
722 778
723 - // Archives and their feeds
724 - if ( get_post_type_archive_link( get_post_type( $post_id ) ) === true ) {
725 - 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 ) ) );
779 + if ( empty( $terms ) || is_wp_error( $terms ) ) {
780 + continue;
781 + }
782 +
783 + foreach ( $terms as $term ) {
784 + $term_url = get_term_link( $term->slug, $taxonomy->name );
785 + if ( ! is_wp_error( $term_url ) ) {
786 + $related_urls[] = $term_url;
787 + if ( $taxonomy->show_in_rest ) {
788 + $taxonomy_base = $taxonomy->rest_base ? $taxonomy->rest_base : $taxonomy->name;
789 + $related_urls[] = rest_url( "{$taxonomy->rest_namespace}/{$taxonomy_base}/{$term->term_id}/" ); // REST API URL for the term
790 + }
791 + }
792 +
793 + if ( ! is_taxonomy_hierarchical( $taxonomy->name ) ) {
794 + continue;
795 + }
796 +
797 + $ancestors = (array) get_ancestors( $term->term_id, $taxonomy->name );
798 + foreach ( $ancestors as $ancestor ) {
799 + $ancestor_object = get_term( $ancestor, $taxonomy->name );
800 + if ( ! is_a( $ancestor, '\WP_Term' ) ) {
801 + continue;
802 + }
803 +
804 + $ancestor_term_url = get_term_link( $ancestor_object->slug, $taxonomy->name );
805 + if ( ! is_wp_error( $ancestor_term_url ) ) {
806 + $related_urls[] = $ancestor_term_url;
807 + if ( $taxonomy->show_in_rest ) {
808 + $taxonomy_base = $taxonomy->rest_base ? $taxonomy->rest_base : $taxonomy->name;
809 + $related_urls[] = rest_url( "{$taxonomy->rest_namespace}/{$taxonomy_base}/{$ancestor_object->term_id}/" ); // REST API URL for the ancestor term
810 + }
811 + }
812 + }
813 + }
726 814 }
727 815
728 - // Post URL
729 - array_push( $related_urls, get_permalink( $post_id ) );
816 + // Purge author and feed URLs for posts.
817 + if ( 'post' === $post_type ) {
818 + $author_id = get_post_field( 'post_author', $post_id );
819 + $related_urls[] = get_author_posts_url( $author_id );
820 + $related_urls[] = get_author_feed_link( $author_id );
821 + if ( $rest_api_route ) {
822 + $related_urls[] = get_rest_url() . $rest_api_route . '/users/' . $author_id . '/';
823 + }
730 824
731 - // Also clean URL for trashed post.
732 - if ( 'trash' === $current_post_status ) {
733 - $trashpost = get_permalink( $post_id );
734 - $trashpost = str_replace( '__trashed', '', $trashpost );
735 - array_push( $related_urls, $trashpost, $trashpost . 'feed/' );
825 + // Include various feed URLs.
826 + $feed_urls = [
827 + get_bloginfo_rss( 'rdf_url' ),
828 + get_bloginfo_rss( 'rss_url' ),
829 + get_bloginfo_rss( 'rss2_url' ),
830 + get_bloginfo_rss( 'atom_url' ),
831 + get_bloginfo_rss( 'comments_rss2_url' ),
832 + get_post_comments_feed_link( $post_id ),
833 + ];
834 + $related_urls = array_merge( $related_urls, $feed_urls );
736 835 }
737 836
738 - // Add in AMP permalink if Automattic's AMP is installed
739 - if ( function_exists( 'amp_get_permalink' ) ) {
740 - array_push( $related_urls, amp_get_permalink( $post_id ) );
837 + // Purge archive pages if not excluded.
838 + if ( ! in_array( $post_type, [ 'post', 'page' ], true ) ) {
839 + $related_urls[] = get_post_type_archive_link( $post_type );
840 + $related_urls[] = get_post_type_archive_feed_link( $post_type );
741 841 }
742 842
743 - // Regular AMP url for posts
744 - array_push( $related_urls, get_permalink( $post_id ) . 'amp/' );
843 + $post_date = strtotime( $post->post_date );
745 844
746 - // Feeds
747 - 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 ) );
845 + if ( $post_date ) {
846 + // Generate the date archive URLs
847 + $year = gmdate( 'Y', $post_date );
848 + $month = gmdate( 'm', $post_date );
849 + $day = gmdate( 'd', $post_date );
748 850
749 - // Home Page and (if used) posts page
750 - array_push( $related_urls, trailingslashit( home_url() ) );
751 - if ( get_option( 'show_on_front' ) === 'page' ) {
752 - // Ensure we have a page_for_posts setting to avoid empty URL
753 - if ( get_option( 'page_for_posts' ) ) {
754 - array_push( $related_urls, get_permalink( get_option( 'page_for_posts' ) ) );
851 + $related_urls[] = get_year_link( $year );
852 + $related_urls[] = get_month_link( $year, $month );
853 + $related_urls[] = get_day_link( $year, $month, $day );
854 + }
855 +
856 + // Always purge the home page.
857 + $related_urls[] = home_url( '/' );
858 +
859 + // Purge the posts page if it's set to a static page.
860 + if ( 'page' === get_option( 'show_on_front' ) ) {
861 + $posts_page_id = get_option( 'page_for_posts' );
862 + if ( $posts_page_id ) {
863 + $related_urls[] = get_permalink( $posts_page_id );
755 864 }
756 865 }
757 866 }
758 867
868 + // Remove query strings and ensure unique URLs before purging.
869 + $related_urls = array_unique(
870 + array_map(
871 + function ( $url ) {
872 + return strtok( $url, '?' );
873 + },
874 + $related_urls
875 + )
876 + );
877 +
759 878 /**
760 879 * Filters post related urls.
761 880 *
762 - * @hook powered_cache_post_related_urls
881 + * @hook powered_cache_post_related_urls
763 882 *
764 - * @param {array} $related_urls The list of the URLs that related with the post.
883 + * @param {array} $related_urls The list of the URLs that related with the post.
765 884 *
766 - * @since 1.0
885 + * @return {array} New value.
886 + * @since 1.0
767 887 */
768 888 $related_urls = apply_filters( 'powered_cache_post_related_urls', $related_urls );
769 889
770 890 return $related_urls;
@@ -773,17 +893,17 @@
773 893
774 894 /**
775 895 * Delete cache file
776 896 *
777 - * @param string $url Target URL
897 + * @param string $url Target URL
898 + * @param bool $delete_subdirectories Whether delete subdirectories or not
778 899 *
779 900 * @return bool true when found cache dir, otherwise false
780 901 * @since 1.0
781 902 */
782 -function delete_page_cache( $url ) {
903 +function delete_page_cache( $url, $delete_subdirectories = false ) {
904 + $dir = get_url_dir( trim( $url ) );
783 905
784 - $dir = trailingslashit( get_url_dir( trim( $url ) ) );
785 -
786 906 if ( is_dir( $dir ) ) {
787 907 $files = scandir( $dir );
788 908 foreach ( $files as $file ) {
789 909 /**
@@ -789,15 +909,15 @@
789 909 /**
790 910 * Don't need to lookup for index-https, index-https-mobile etc..
791 911 * Just clean that directory's files only.
792 912 */
793 - if ( ! is_dir( $dir . $file ) && ! in_array( $file, array( '.', '..' ), true ) ) {
913 + if ( ! is_dir( $dir . $file ) && file_exists( $dir . $file ) && ! in_array( $file, array( '.', '..' ), true ) ) {
794 914 unlink( $dir . $file );
795 915 }
916 + }
796 917
797 - if ( file_exists( $dir ) && is_dir_empty( $dir ) ) {
798 - remove_dir( $dir );
799 - }
918 + if ( file_exists( $dir ) && ( $delete_subdirectories || is_dir_empty( $dir ) ) ) {
919 + remove_dir( $dir );
800 920 }
801 921
802 922 return true;
803 923 }
@@ -815,9 +935,9 @@
815 935 * @since 1.1
816 936 */
817 937 function get_url_dir( $url ) {
818 938 $url_info = wp_parse_url( $url );
819 - $sub_dir = $url_info['host'];
939 + $sub_dir = isset( $url_info['host'] ) ? $url_info['host'] : '';
820 940
821 941 if ( ! empty( $url_info['path'] ) ) {
822 942 $sub_dir .= $url_info['path'];
823 943 }
@@ -822,8 +942,9 @@
822 942 $sub_dir .= $url_info['path'];
823 943 }
824 944
825 945 $path = trailingslashit( get_page_cache_dir() ) . ltrim( $sub_dir, '/' );
946 + $path = trailingslashit( $path );
826 947
827 948 /**
828 949 * Filters the path of the given url in the cache directory.
829 950 *
@@ -863,12 +984,11 @@
863 984 * Filters CDN Addresses.
864 985 *
865 986 * @hook powered_cache_cdn_addresses
866 987 *
867 - * @param {array} $cdn_addresses CDN Adresses.
988 + * @param {array} $cdn_addresses CDN Addresses.
868 989 *
869 990 * @return {array} New value.
870 - *
871 991 * @since 1.0
872 992 */
873 993 return apply_filters( 'powered_cache_cdn_addresses', $cdn_addresses );
874 994 }
@@ -928,9 +1048,8 @@
928 1048 /**
929 1049 * Fires after cache flush.
930 1050 *
931 1051 * @hook powered_cache_flushed
932 - *
933 1052 * @since 1.0
934 1053 */
935 1054 do_action( 'powered_cache_flushed' );
936 1055 }
@@ -960,9 +1079,8 @@
960 1079 *
961 1080 * @param {string} $log_message The log message.
962 1081 *
963 1082 * @return {string} New value.
964 - *
965 1083 * @since 2.0
966 1084 */
967 1085 $log_message = apply_filters( 'powered_cache_log_message', $log_message );
968 1086
@@ -970,15 +1088,14 @@
970 1088 * Filters log message type.
971 1089 *
972 1090 * @hook powered_cache_log_message_type
973 1091 *
974 - * @param {null|int} null default message type
1092 + * @param {int} 0 default message type since 3.6
975 1093 *
976 - * @return {null|int} New value.
977 - *
1094 + * @return {int} New value.
978 1095 * @since 2.0
979 1096 */
980 - $message_type = apply_filters( 'powered_cache_log_message_type', null );
1097 + $message_type = apply_filters( 'powered_cache_log_message_type', 0 );
981 1098 $destination = null;
982 1099
983 1100 if ( defined( 'POWERED_CACHE_LOG_FILE' ) ) {
984 1101 $destination = POWERED_CACHE_LOG_FILE;
@@ -992,9 +1109,8 @@
992 1109 *
993 1110 * @param {null|string} $destination The destination of the log.
994 1111 *
995 1112 * @return {null|string} New value.
996 - *
997 1113 * @since 2.0
998 1114 */
999 1115 $log_destination = apply_filters( 'powered_cache_log_destination', $destination );
1000 1116
@@ -1002,8 +1118,10 @@
1002 1118 if ( defined( 'POWERED_CACHE_LOG_IP' ) && POWERED_CACHE_LOG_IP !== get_client_ip() ) {
1003 1119 return false;
1004 1120 }
1005 1121
1122 + $message_type = absint( $message_type );
1123 +
1006 1124 return error_log( $log_message, $message_type, $log_destination ); // phpcs:ignore
1007 1125 }
1008 1126
1009 1127 /**
@@ -1012,12 +1130,14 @@
1012 1130 * @return mixed
1013 1131 */
1014 1132 function get_client_ip() {
1015 1133 if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1016 - return $_SERVER['HTTP_X_FORWARDED_FOR'];
1134 + return wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1017 1135 }
1018 1136
1019 - return $_SERVER['REMOTE_ADDR'];
1137 + if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
1138 + return wp_unslash( $_SERVER['REMOTE_ADDR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1139 + }
1020 1140 }
1021 1141
1022 1142 /**
1023 1143 * Fragment caching
@@ -1109,9 +1229,8 @@
1109 1229 *
1110 1230 * @param {array} $known_headers The list of known HTTP headers.
1111 1231 *
1112 1232 * @return {array} New value.
1113 - *
1114 1233 * @since 1.2
1115 1234 */
1116 1235 $known_headers = apply_filters( 'powered_cache_known_headers', $known_headers );
1117 1236
@@ -1265,12 +1384,18 @@
1265 1384 function powered_cache_is_mobile() {
1266 1385
1267 1386 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
1268 1387
1269 - $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_browsers ) ), ' ' );
1270 - $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', $powered_cache_mobile_prefixes ) ), ' ' );
1388 + $mobile_browsers = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', (string) $powered_cache_mobile_browsers ) ), ' ' );
1389 + $mobile_prefixes = addcslashes( implode( '|', preg_split( '/[\s*,\s*]*,+[\s*,\s*]*/', (string) $powered_cache_mobile_prefixes ) ), ' ' );
1271 1390
1272 - if ( ( preg_match( '#^.*(' . $mobile_browsers . ').*#i', $_SERVER['HTTP_USER_AGENT'] ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $_SERVER['HTTP_USER_AGENT'], 0, 4 ) ) ) ) {
1391 + if ( ! isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
1392 + return false;
1393 + }
1394 +
1395 + $user_agent = wp_unslash( $_SERVER['HTTP_USER_AGENT'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1396 +
1397 + if ( ( preg_match( '#^.*(' . $mobile_browsers . ').*#i', $user_agent ) || preg_match( '#^(' . $mobile_prefixes . ').*#i', substr( $user_agent, 0, 4 ) ) ) ) {
1273 1398 return true;
1274 1399 }
1275 1400
1276 1401 return false;
@@ -1283,8 +1408,11 @@
1283 1408 * @since 2.2
1284 1409 */
1285 1410 function is_local_site() {
1286 1411 $site_url = site_url();
1412 + if ( ! is_string( $site_url ) ) {
1413 + $site_url = '';
1414 + }
1287 1415
1288 1416 // Check for localhost and sites using an IP only first.
1289 1417 $is_local = $site_url && false === strpos( $site_url, '.' );
1290 1418
@@ -1322,5 +1450,147 @@
1322 1450 */
1323 1451 $is_local = apply_filters( 'powered_cache_is_local_site', $is_local );
1324 1452
1325 1453 return $is_local;
1454 +}
1455 +
1456 +/**
1457 + * Check whether request for bypass or process normally
1458 + *
1459 + * @return bool
1460 + * @since 3.0
1461 + */
1462 +function bypass_request() {
1463 + if ( isset( $_GET['nopoweredcache'] ) && $_GET['nopoweredcache'] ) { // phpcs:ignore
1464 + return true;
1465 + }
1466 +
1467 + return false;
1468 +}
1469 +
1470 +
1471 +/**
1472 + * Calculate total amount of autoloaded data.
1473 + *
1474 + * @return int autoloaded data in bytes.
1475 + * @global wpdb $wpdb WordPress database abstraction object.
1476 + * @since 3.4
1477 + */
1478 +function autoloaded_options_size() {
1479 + global $wpdb;
1480 +
1481 + return (int) $wpdb->get_var( 'SELECT SUM(LENGTH(option_value)) FROM ' . $wpdb->prefix . 'options WHERE autoload = \'yes\'' ); // phpcs:ignore
1482 +}
1483 +
1484 +/**
1485 + * Mask the string with asterisk
1486 + *
1487 + * @param string $input_string String
1488 + * @param int $unmask_length The length of the string that will not be masked
1489 + *
1490 + * @return string
1491 + * @since 3.4
1492 + */
1493 +function mask_string( $input_string, $unmask_length ) {
1494 + $output_string = substr( $input_string, 0, $unmask_length );
1495 +
1496 + if ( strlen( $input_string ) > $unmask_length ) {
1497 + $output_string .= str_repeat( '*', strlen( $input_string ) - $unmask_length );
1498 + }
1499 +
1500 + return $output_string;
1501 +}
1502 +
1503 +/**
1504 + * Get sensitive data in decrypted form
1505 + *
1506 + * @param string $field field name
1507 + *
1508 + * @return bool|mixed|string
1509 + */
1510 +function get_decrypted_setting( $field ) {
1511 + $settings = \PoweredCache\Utils\get_settings();
1512 + $value = isset( $settings[ $field ] ) ? $settings[ $field ] : '';
1513 +
1514 + // decrypt the value
1515 + $encryption = new Encryption();
1516 + $decrypted_value = $encryption->decrypt( $value );
1517 + if ( false !== $decrypted_value ) {
1518 + return $decrypted_value;
1519 + }
1520 +
1521 + return $value;
1522 +}
1523 +
1524 +
1525 +/**
1526 + * Check if a given IP is within a specific range.
1527 + * Supports both IPv4 and IPv6 addresses.
1528 + *
1529 + * @param string $ip The IP address to check.
1530 + * @param string $range The IP range in CIDR notation.
1531 + *
1532 + * @return bool True if the IP is in the range, false otherwise.
1533 + */
1534 +function is_ip_in_range( $ip, $range ) {
1535 + if ( false !== strpos( $range, '/' ) ) {
1536 + list( $subnet, $bits ) = explode( '/', $range, 2 );
1537 + } else {
1538 + $subnet = $range;
1539 + $bits = ( false === filter_var( $subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) ? 32 : 128;
1540 + }
1541 +
1542 + $bits = intval( $bits );
1543 +
1544 + if ( false !== filter_var( $subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
1545 + $subnet_bin = inet_pton( $subnet );
1546 + $ip_bin = inet_pton( $ip );
1547 +
1548 + if ( false === $subnet_bin || false === $ip_bin ) {
1549 + return false;
1550 + }
1551 +
1552 + $subnet_bin = str_pad( $subnet_bin, 16, "\0" );
1553 + $ip_bin = str_pad( $ip_bin, 16, "\0" );
1554 +
1555 + for ( $i = 0; $i * 8 < $bits; $i ++ ) {
1556 + if ( $bits >= ( $i + 1 ) * 8 && $subnet_bin[ $i ] !== $ip_bin[ $i ] ) {
1557 + return false;
1558 + } elseif ( $bits > $i * 8 ) {
1559 + $bitmask = 0xff00 >> ( $bits % 8 );
1560 + if ( ( ord( $subnet_bin[ $i ] ) & $bitmask ) !== ( ord( $ip_bin[ $i ] ) & $bitmask ) ) {
1561 + return false;
1562 + }
1563 + }
1564 + }
1565 +
1566 + return true;
1567 + }
1568 +
1569 + if ( false === filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
1570 + return false;
1571 + }
1572 +
1573 + $subnet_decimal = ip2long( $subnet );
1574 + $ip_decimal = ip2long( $ip );
1575 + $mask_decimal = - 1 << ( 32 - $bits );
1576 +
1577 + return ( $subnet_decimal & $mask_decimal ) === ( $ip_decimal & $mask_decimal );
1578 +}
1579 +
1580 +/**
1581 + * Check if the dev mode is active
1582 + *
1583 + * @return bool
1584 + * @since 3.6
1585 + */
1586 +function is_dev_mode_active() {
1587 + // Check global config first (fast)
1588 + if ( ! empty( $GLOBALS['powered_cache_options']['dev_mode'] ) ) {
1589 + return true;
1590 + }
1591 +
1592 + // Fallback to database option
1593 + $settings = \PoweredCache\Utils\get_settings();
1594 +
1595 + return ! empty( $settings['dev_mode'] );
1326 1596 }