PluginProbe
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score / 3.6
Powered Cache – Caching and Optimization for WordPress – Easily Improve PageSpeed & Web Vitals Score v3.6
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 +415 -149 2.23.6 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,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 - '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,
108 131 // 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',
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',
119 142 // 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' => '',
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' => '',
133 157 // misc
134 - 'cache_footprint' => true,
158 + 'cache_footprint' => true,
159 + 'async_cache_cleaning' => false,
160 + 'dev_mode' => false,
135 161 // new options needs to migrate from extensions
136 - 'enable_google_tracking' => false,
137 - 'enable_fb_tracking' => false,
162 + 'enable_google_tracking' => false,
163 + 'enable_fb_tracking' => false,
138 164 ];
139 165
140 166 /**
141 167 * Filter default settings.
@@ -181,9 +207,8 @@
181 207 * Object cache methods keys will use as option
182 208 *
183 209 * @return array $object_caches
184 210 * @since 1.2 apcu added
185 - *
186 211 * @since 1.0
187 212 */
188 213 function get_object_cache_dropins() {
189 214
@@ -343,8 +368,10 @@
343 368
344 369 /**
345 370 * Supported js execution methods
346 371 *
372 + * @depreacated since 3.2
373 + *
347 374 * @return mixed|void
348 375 */
349 376 function js_execution_methods() {
350 377 $methods = [
@@ -350,8 +377,9 @@
350 377 $methods = [
351 378 'blocking' => esc_html__( 'Blocking – (default)', 'powered-cache' ),
352 379 'async' => esc_html__( 'Non-blocking using async', 'powered-cache' ),
353 380 'defer' => esc_html__( 'Non-blocking using defer', 'powered-cache' ),
381 + 'delayed' => esc_html__( 'Delayed for user interaction', 'powered-cache' ),
354 382 ];
355 383
356 384 /**
357 385 * Filter supported JS execution methods.
@@ -485,9 +513,8 @@
485 513 }
486 514
487 515 /**
488 516 * remove directories recursively
489 - *
490 517 * Adopted from W3TC Utility
491 518 *
492 519 * @param string $path The target path
493 520 * @param array $exclude list of the files that will excluded
@@ -691,80 +718,172 @@
691 718 * @since 1.0
692 719 * @since 1.1 powered_cache_post_related_urls filter added
693 720 */
694 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 );
695 726
696 - $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';
697 731
698 - // array to collect all our URLs
699 - $related_urls = array();
732 + $related_urls = [];
700 733
701 - if ( get_permalink( $post_id ) ) {
702 - // 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 );
703 737
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 ) );
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 . '/';
709 746 }
710 747 }
711 748
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 ) );
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;
717 774 }
718 - }
719 775
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 ) ) );
776 + $terms = get_the_terms( $post_id, $taxonomy->name );
722 777
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 ) ) );
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 + }
726 813 }
727 814
728 - // Post URL
729 - 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 + }
730 823
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/' );
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 );
736 834 }
737 835
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 ) );
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 );
741 840 }
742 841
743 - // Regular AMP url for posts
744 - array_push( $related_urls, get_permalink( $post_id ) . 'amp/' );
842 + $post_date = strtotime( $post->post_date );
745 843
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 ) );
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 );
748 849
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' ) ) );
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 );
755 863 }
756 864 }
757 865 }
758 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 +
759 877 /**
760 878 * Filters post related urls.
761 879 *
762 - * @hook powered_cache_post_related_urls
880 + * @hook powered_cache_post_related_urls
763 881 *
764 - * @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.
765 883 *
766 - * @since 1.0
884 + * @return {array} New value.
885 + * @since 1.0
767 886 */
768 887 $related_urls = apply_filters( 'powered_cache_post_related_urls', $related_urls );
769 888
770 889 return $related_urls;
@@ -773,17 +892,17 @@
773 892
774 893 /**
775 894 * Delete cache file
776 895 *
777 - * @param string $url Target URL
896 + * @param string $url Target URL
897 + * @param bool $delete_subdirectories Whether delete subdirectories or not
778 898 *
779 899 * @return bool true when found cache dir, otherwise false
780 900 * @since 1.0
781 901 */
782 -function delete_page_cache( $url ) {
902 +function delete_page_cache( $url, $delete_subdirectories = false ) {
903 + $dir = get_url_dir( trim( $url ) );
783 904
784 - $dir = trailingslashit( get_url_dir( trim( $url ) ) );
785 -
786 905 if ( is_dir( $dir ) ) {
787 906 $files = scandir( $dir );
788 907 foreach ( $files as $file ) {
789 908 /**
@@ -789,15 +908,15 @@
789 908 /**
790 909 * Don't need to lookup for index-https, index-https-mobile etc..
791 910 * Just clean that directory's files only.
792 911 */
793 - 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 ) ) {
794 913 unlink( $dir . $file );
795 914 }
915 + }
796 916
797 - if ( file_exists( $dir ) && is_dir_empty( $dir ) ) {
798 - remove_dir( $dir );
799 - }
917 + if ( file_exists( $dir ) && ( $delete_subdirectories || is_dir_empty( $dir ) ) ) {
918 + remove_dir( $dir );
800 919 }
801 920
802 921 return true;
803 922 }
@@ -822,8 +941,9 @@
822 941 $sub_dir .= $url_info['path'];
823 942 }
824 943
825 944 $path = trailingslashit( get_page_cache_dir() ) . ltrim( $sub_dir, '/' );
945 + $path = trailingslashit( $path );
826 946
827 947 /**
828 948 * Filters the path of the given url in the cache directory.
829 949 *
@@ -863,12 +983,11 @@
863 983 * Filters CDN Addresses.
864 984 *
865 985 * @hook powered_cache_cdn_addresses
866 986 *
867 - * @param {array} $cdn_addresses CDN Adresses.
987 + * @param {array} $cdn_addresses CDN Addresses.
868 988 *
869 989 * @return {array} New value.
870 - *
871 990 * @since 1.0
872 991 */
873 992 return apply_filters( 'powered_cache_cdn_addresses', $cdn_addresses );
874 993 }
@@ -928,9 +1047,8 @@
928 1047 /**
929 1048 * Fires after cache flush.
930 1049 *
931 1050 * @hook powered_cache_flushed
932 - *
933 1051 * @since 1.0
934 1052 */
935 1053 do_action( 'powered_cache_flushed' );
936 1054 }
@@ -960,9 +1078,8 @@
960 1078 *
961 1079 * @param {string} $log_message The log message.
962 1080 *
963 1081 * @return {string} New value.
964 - *
965 1082 * @since 2.0
966 1083 */
967 1084 $log_message = apply_filters( 'powered_cache_log_message', $log_message );
968 1085
@@ -970,15 +1087,14 @@
970 1087 * Filters log message type.
971 1088 *
972 1089 * @hook powered_cache_log_message_type
973 1090 *
974 - * @param {null|int} null default message type
1091 + * @param {int} 0 default message type since 3.6
975 1092 *
976 - * @return {null|int} New value.
977 - *
1093 + * @return {int} New value.
978 1094 * @since 2.0
979 1095 */
980 - $message_type = apply_filters( 'powered_cache_log_message_type', null );
1096 + $message_type = apply_filters( 'powered_cache_log_message_type', 0 );
981 1097 $destination = null;
982 1098
983 1099 if ( defined( 'POWERED_CACHE_LOG_FILE' ) ) {
984 1100 $destination = POWERED_CACHE_LOG_FILE;
@@ -992,9 +1108,8 @@
992 1108 *
993 1109 * @param {null|string} $destination The destination of the log.
994 1110 *
995 1111 * @return {null|string} New value.
996 - *
997 1112 * @since 2.0
998 1113 */
999 1114 $log_destination = apply_filters( 'powered_cache_log_destination', $destination );
1000 1115
@@ -1002,8 +1117,10 @@
1002 1117 if ( defined( 'POWERED_CACHE_LOG_IP' ) && POWERED_CACHE_LOG_IP !== get_client_ip() ) {
1003 1118 return false;
1004 1119 }
1005 1120
1121 + $message_type = absint( $message_type );
1122 +
1006 1123 return error_log( $log_message, $message_type, $log_destination ); // phpcs:ignore
1007 1124 }
1008 1125
1009 1126 /**
@@ -1012,12 +1129,14 @@
1012 1129 * @return mixed
1013 1130 */
1014 1131 function get_client_ip() {
1015 1132 if ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1016 - return $_SERVER['HTTP_X_FORWARDED_FOR'];
1133 + return wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1017 1134 }
1018 1135
1019 - return $_SERVER['REMOTE_ADDR'];
1136 + if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
1137 + return wp_unslash( $_SERVER['REMOTE_ADDR'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1138 + }
1020 1139 }
1021 1140
1022 1141 /**
1023 1142 * Fragment caching
@@ -1109,9 +1228,8 @@
1109 1228 *
1110 1229 * @param {array} $known_headers The list of known HTTP headers.
1111 1230 *
1112 1231 * @return {array} New value.
1113 - *
1114 1232 * @since 1.2
1115 1233 */
1116 1234 $known_headers = apply_filters( 'powered_cache_known_headers', $known_headers );
1117 1235
@@ -1265,12 +1383,18 @@
1265 1383 function powered_cache_is_mobile() {
1266 1384
1267 1385 global $powered_cache_mobile_browsers, $powered_cache_mobile_prefixes;
1268 1386
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 ) ), ' ' );
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 ) ), ' ' );
1271 1389
1272 - 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 ) ) ) ) {
1273 1397 return true;
1274 1398 }
1275 1399
1276 1400 return false;
@@ -1322,5 +1446,147 @@
1322 1446 */
1323 1447 $is_local = apply_filters( 'powered_cache_is_local_site', $is_local );
1324 1448
1325 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'] );
1326 1592 }