22/**
33 * Plugin Name: Tiny navigation menu cache (MU)
44 * Description: Cache nav menu's HTML content in persistent object cache.
5- * Version: 0.1.3
5+ * Version: 0.2.0
66 * Constants: TINY_CACHE_NAV_MENU_EXCLUDES
77 */
88
99class Tiny_Nav_Menu_Cache {
1010
11- const GROUP = 'navmenu ' ;
11+ /**
12+ * @var string Name of the cache group.
13+ */
14+ private const GROUP = 'navmenu ' ;
15+
16+ /**
17+ * @var array List of whitelisted query string fields (these do not prevent cache write).
18+ */
19+ private const WHITELISTED_QUERY_STRING_FIELDS = [
20+ // https://support.google.com/searchads/answer/7342044
21+ 'gclid ' ,
22+ 'gclsrc ' ,
23+ // https://www.facebook.com/business/help/330994334179410 "URL in ad can't contain Facebook Click ID" section
24+ 'fbclid ' ,
25+ // https://en.wikipedia.org/wiki/UTM_parameters
26+ 'utm_campaign ' ,
27+ 'utm_content ' ,
28+ 'utm_medium ' ,
29+ 'utm_source ' ,
30+ 'utm_term ' ,
31+ ];
1232
1333 public function __construct () {
1434
@@ -40,32 +60,41 @@ public function init() {
4060 add_filter ( 'wp_nav_menu ' , array ( $ this , 'save_nav_menu ' ), PHP_INT_MAX , 2 );
4161 }
4262
43- public function get_nav_menu ( $ nav_menu , $ args ) {
63+ /**
64+ * @param string $nav_menu_html
65+ * @param object $args
66+ * @return string
67+ */
68+ public function get_nav_menu ( $ nav_menu_html , $ args ) {
4469
45- $ key = $ this ->get_cache_key ( $ args );
46- // Check excluded nav menus
47- if ( false !== $ key ) {
70+ $ enabled = $ this ->is_enabled ( $ args );
71+ if ( $ enabled ) {
4872 $ found = null ;
49- $ cache = wp_cache_get ( $ key , self ::GROUP , false , $ found );
73+ $ cache = wp_cache_get ( $ this -> get_cache_key ( $ args ) , self ::GROUP , false , $ found );
5074 if ( $ found ) {
5175
5276 return $ cache ;
5377 }
5478 }
5579
56- return $ nav_menu ;
80+ return $ nav_menu_html ;
5781 }
5882
59- public function save_nav_menu ( $ nav_menu , $ args ) {
60-
61- $ key = $ this ->get_cache_key ( $ args );
62- // Check excluded nav menus
63- if ( false !== $ key ) {
64- wp_cache_set ( $ key , $ nav_menu , self ::GROUP , DAY_IN_SECONDS );
83+ /**
84+ * @param string $nav_menu_html
85+ * @param object $args
86+ * @return string
87+ */
88+ public function save_nav_menu ( $ nav_menu_html , $ args ) {
89+
90+ $ enabled = $ this ->is_enabled ( $ args );
91+ if ( $ enabled ) {
92+ $ key = $ this ->get_cache_key ( $ args );
93+ wp_cache_set ( $ key , $ nav_menu_html , self ::GROUP , DAY_IN_SECONDS );
6594 $ this ->remember_key ( $ key );
6695 }
6796
68- return $ nav_menu ;
97+ return $ nav_menu_html ;
6998 }
7099
71100 public function flush_all () {
@@ -76,6 +105,9 @@ public function flush_all() {
76105 wp_cache_delete ( 'key_list ' , self ::GROUP );
77106 }
78107
108+ /**
109+ * @param string $key
110+ */
79111 private function remember_key ( $ key ) {
80112
81113 // @TODO Not atomic
@@ -89,6 +121,9 @@ private function remember_key( $key ) {
89121 wp_cache_set ( 'key_list ' , $ key_list , self ::GROUP , DAY_IN_SECONDS );
90122 }
91123
124+ /**
125+ * @return array
126+ */
92127 private function get_all_keys () {
93128
94129 $ found = null ;
@@ -100,9 +135,15 @@ private function get_all_keys() {
100135 return explode ( '| ' , $ key_list );
101136 }
102137
103- private function get_cache_key ( $ args ) {
138+ /**
139+ * Check excluded nav menus and the query string.
140+ *
141+ * @param object $args
142+ * @return bool
143+ */
144+ private function is_enabled ( $ args ) {
104145
105- // Excluded theme locations
146+ // Excluded theme locations.
106147 if ( defined ( 'TINY_CACHE_NAV_MENU_EXCLUDES ' ) && TINY_CACHE_NAV_MENU_EXCLUDES ) {
107148 $ excludes = explode ( '| ' , TINY_CACHE_NAV_MENU_EXCLUDES );
108149
@@ -114,6 +155,21 @@ private function get_cache_key( $args ) {
114155 }
115156 }
116157
158+ // Do not cache requests with query string except whitelisted ones.
159+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
160+ if ( [] !== array_diff ( array_keys ( $ _GET ), self ::WHITELISTED_QUERY_STRING_FIELDS ) ) {
161+ return false ;
162+ }
163+
164+ return true ;
165+ }
166+
167+ /**
168+ * @param object $args
169+ * @return string
170+ */
171+ private function get_cache_key ( $ args ) {
172+
117173 $ request_uri = isset ( $ _SERVER ['REQUEST_URI ' ] )
118174 ? $ _SERVER ['REQUEST_URI ' ]
119175 : '' ; // WPCS: sanitization, input var OK.
0 commit comments