PluginProbe
Redirection / 5.5.0
Redirection v5.5.0
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / models / redirect / redirect-cache.php

redirect-cache.php in Redirection 5.5.0, at models/redirect/redirect-cache.php

218 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Redirect caching.
5 *
6 * This is based on server requests and not database requests.
7 *
8 * The client requests a URL. We use the requested URL, the `cache_key` setting, and the plugin version, to look for a cache entry.
9 *
10 * The `cache_key` is updated each time *any* redirect is updated. This is because a URL can be affected by other redirects, such as regular expressions
11 * and redirects with dynamic conditions (i.e. cookie, login status etc).
12 *
13 * We include the plugin version as data can change between plugin versions, and it is safest to use new cache entries.
14 *
15 * If we have a cache hit then the data is used to perform the redirect.php
16 *
17 * If we do not have a cache hit then we request the URL from the database and perform redirect matches.
18 *
19 * After matching has been performed we then try and update the cache:
20 * - if no match was found, cache an empty result
21 * - if a match was found and no dynamic redirects were encountered, then cache that redirect only
22 * - if a match was found and dynamic redirects were involved then cache all redirects
23 *
24 * We have a maximum number of redirects that can be cached to avoid saturating the cache.
25 */
26 class Redirect_Cache {
27 const EMPTY_VALUE = 'empty';
28 const CACHE_MAX = 10;
29
30 /**
31 * Singleton
32 *
33 * @var Redirect_Cache|null
34 */
35 private static $instance = null;
36
37 /**
38 * Array of URLs that have been cached
39 *
40 * @var array
41 */
42 private $cached = [];
43
44 /**
45 * Cache key. Changed to current time whenever a redirect is updated.
46 *
47 * @var integer
48 */
49 private $key = 0;
50
51 /**
52 * Initialiser
53 *
54 * @return Redirect_Cache
55 */
56 public static function init() {
57 if ( is_null( self::$instance ) ) {
58 self::$instance = new Redirect_Cache();
59 }
60
61 return self::$instance;
62 }
63
64 /**
65 * Constructor
66 */
67 public function __construct() {
68 $this->reset();
69 }
70
71 public function reset() {
72 $settings = red_get_options();
73 $this->key = $settings['cache_key'];
74 $this->cached = [];
75 }
76
77 /**
78 * Is the cache enabled?
79 *
80 * @return boolean
81 */
82 public function can_cache() {
83 return $this->key > 0;
84 }
85
86 /**
87 * Get the current cache key
88 *
89 * @param string $url URL we are looking at.
90 * @return string
91 */
92 private function get_key( $url ) {
93 return apply_filters( 'redirection_cache_key', md5( $url ) . '-' . (string) $this->key . '-' . REDIRECTION_VERSION );
94 }
95
96 /**
97 * Get the cache entry for a URL
98 *
99 * @param string $url Requested URL.
100 * @return Red_Item[]|bool
101 */
102 public function get( $url ) {
103 if ( ! $this->can_cache() ) {
104 return false;
105 }
106
107 $cache_key = $this->get_key( $url );
108
109 // Look in cache
110 $false = false;
111 $result = wp_cache_get( $cache_key, 'redirection', false, $false );
112
113 // If a result was found then remember we are using the cache so we don't need to re-save it later
114 if ( $result !== false ) {
115 $this->cached[ $url ] = true;
116 }
117
118 // Empty value is a special case. Storing [] in the cache doesn't work, so we store the special EMPTY_VALUE to represent []
119 if ( $result === self::EMPTY_VALUE ) {
120 return [];
121 }
122
123 return $result;
124 }
125
126 /**
127 * Set the cache for a URL
128 *
129 * @param string $url URL to cache.
130 * @param Red_Item|false $matched The matched redirect.
131 * @param Red_Item[] $redirects All of the redirects the match the URL.
132 * @return boolean
133 */
134 public function set( $url, $matched, $redirects ) {
135 if ( ! $this->can_cache() || isset( $this->cached[ $url ] ) ) {
136 return false;
137 }
138
139 $cache_key = $this->get_key( $url );
140
141 // Default store the match redirect
142 $rows = [];
143 if ( $matched ) {
144 $rows[] = $matched;
145 }
146
147 // Are any of the redirects before, and including, the match a dynamic redirect?
148 $dynamic = $this->get_dynamic_matched( $redirects, $matched );
149 if ( count( $dynamic ) > 0 ) {
150 // Store all dynamic redirects
151 $rows = $dynamic;
152 }
153
154 // Have we exceeded our limit?
155 if ( count( $rows ) > self::CACHE_MAX ) {
156 return false;
157 }
158
159 $converted = $this->convert_to_rows( $rows );
160 $value = count( $converted ) === 0 ? self::EMPTY_VALUE : $converted;
161
162 wp_cache_set( $cache_key, $value, 'redirection' );
163
164 return true;
165 }
166
167 /**
168 * Convert a Red_Item to a format suitable for storing in the cache
169 *
170 * @param Red_Item[] $rows Redirects.
171 * @return array
172 */
173 private function convert_to_rows( array $rows ) {
174 $converted = [];
175
176 foreach ( $rows as $row ) {
177 $converted[] = $row->to_sql();
178 }
179
180 return $converted;
181 }
182
183 /**
184 * If there are dynamic redirects before the matched redirect then return all dynamic redirects (including the matched one), otherwise return nothing.
185 *
186 * If the matched redirect is a static redirect then we include it in the list, but don't include any redirects after.
187 *
188 * @param Red_Item[] $redirects Array of redirects.
189 * @param Red_Item|false $matched The matched item.
190 * @return Red_Item[]
191 */
192 private function get_dynamic_matched( array $redirects, $matched ) {
193 $dynamic = [];
194
195 foreach ( $redirects as $redirect ) {
196 if ( $redirect->is_dynamic() ) {
197 $dynamic[] = $redirect;
198 }
199
200 // Is this the matched redirect?
201 if ( $matched === $redirect ) {
202 // Yes. Do we have any dynamic redirects so far?
203 if ( count( $dynamic ) === 0 ) {
204 // No. Just return an empty array
205 return [];
206 }
207
208 if ( ! $matched->is_dynamic() ) {
209 // We need to include the non-dynamic redirect in the list
210 return array_merge( $dynamic, [ $matched ] );
211 }
212 }
213 }
214
215 return $dynamic;
216 }
217 }
218