Complex classes like GravityView_Cache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use GravityView_Cache, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
6 | class GravityView_Cache { |
||
7 | |||
8 | const BLACKLIST_OPTION_NAME = 'gravityview_cache_blacklist'; |
||
9 | |||
10 | /** |
||
11 | * Form ID, or array of Form IDs |
||
12 | * |
||
13 | * @var mixed |
||
14 | */ |
||
15 | protected $form_ids; |
||
16 | |||
17 | /** |
||
18 | * Extra request parameters used to generate the query. This is used to generate the unique transient key. |
||
19 | * |
||
20 | * @var array |
||
21 | */ |
||
22 | protected $args; |
||
23 | |||
24 | /** |
||
25 | * The transient key used to store the cached item. 45 characters long. |
||
26 | * |
||
27 | * @var string |
||
28 | */ |
||
29 | private $key = ''; |
||
30 | |||
31 | /** |
||
32 | * @since 1.13.1 |
||
33 | * @var array Columns in the database for leads |
||
34 | */ |
||
35 | private $lead_db_columns = array( 'id', 'form_id', 'post_id', 'date_created', 'is_starred', 'is_read', 'ip', 'source_url', 'user_agent', 'currency', 'payment_status', 'payment_date', 'payment_amount', 'transaction_id', 'is_fulfilled', 'created_by', 'transaction_type', 'status' ); |
||
36 | |||
37 | /** |
||
38 | * |
||
39 | * @param array|int $form_ids Form ID or array of form IDs used in a request |
||
40 | * @param array $args Extra request parameters used to generate the query. This is used to generate the unique transient key. |
||
41 | */ |
||
42 | 2 | function __construct( $form_ids = NULL, $args = array() ) { |
|
43 | |||
44 | 2 | $this->add_hooks(); |
|
45 | |||
46 | 2 | if ( ! is_null( $form_ids ) ) { |
|
47 | |||
48 | 2 | $this->form_ids = $form_ids; |
|
49 | |||
50 | 2 | $this->args = $args; |
|
51 | |||
52 | 2 | $this->set_key(); |
|
53 | } |
||
54 | 2 | } |
|
55 | |||
56 | /** |
||
57 | * Add actions for clearing out caches when entries are updated. |
||
58 | */ |
||
59 | 2 | function add_hooks() { |
|
60 | |||
61 | // Schedule cleanup of expired transients |
||
62 | 2 | add_action( 'wp', array( $this, 'schedule_transient_cleanup' ) ); |
|
63 | |||
64 | // Hook in to the scheduled cleanup, if scheduled |
||
65 | 2 | add_action( 'gravityview-expired-transients', array( $this, 'delete_expired_transients' ) ); |
|
66 | |||
67 | // Trigger this when you need to prevent any results from being cached with forms that have been modified |
||
68 | 2 | add_action( 'gravityview_clear_form_cache', array( $this, 'blacklist_add' ) ); |
|
69 | |||
70 | /** |
||
71 | * @since 1.14 |
||
72 | */ |
||
73 | 2 | add_action( 'gravityview_clear_entry_cache', array( $this, 'entry_status_changed' ) ); |
|
74 | |||
75 | 2 | add_action( 'gform_after_update_entry', array( $this, 'entry_updated' ), 10, 2 ); |
|
76 | |||
77 | 2 | add_action( 'gform_entry_created', array( $this, 'entry_created' ), 10, 2 ); |
|
78 | |||
79 | 2 | add_action( 'gform_post_add_entry', array( $this, 'entry_added' ), 10, 2 ); |
|
80 | |||
81 | /** |
||
82 | * @see RGFormsModel::update_lead_property() Trigger when any entry property changes |
||
83 | */ |
||
84 | 2 | foreach( $this->lead_db_columns as $column ) { |
|
85 | 2 | add_action( 'gform_update_' . $column, array( $this, 'entry_status_changed' ), 10, 3 ); |
|
86 | } |
||
87 | |||
88 | 2 | add_action( 'gform_delete_lead', array( $this, 'entry_status_changed' ), 10 ); |
|
89 | 2 | } |
|
90 | |||
91 | /** |
||
92 | * Force refreshing a cache when an entry is deleted. |
||
93 | * |
||
94 | * The `gform_delete_lead` action is called before the lead is deleted; we fetch the entry to find out the form ID so it can be added to the blacklist. |
||
95 | * |
||
96 | * @since 1.5.1 |
||
97 | * |
||
98 | * @param int $lead_id Entry ID |
||
99 | * @param string $property_value Previous value of the lead status passed by gform_update_status hook |
||
100 | * @param string $previous_value Previous value of the lead status passed by gform_update_status hook |
||
101 | * |
||
102 | * @return void |
||
103 | */ |
||
104 | 1 | public function entry_status_changed( $lead_id, $property_value = '', $previous_value = '' ) { |
|
105 | |||
106 | /** @var array $entry */ |
||
107 | 1 | $entry = GFAPI::get_entry( $lead_id ); |
|
108 | |||
109 | 1 | if ( is_wp_error( $entry ) ) { |
|
110 | |||
111 | /** @var WP_Error $entry */ |
||
112 | gravityview()->log->error( 'Could not retrieve entry {entry_id} to delete it: {error}', array( 'entry_id' => $lead_id, 'error' => $entry->get_error_message() ) ); |
||
113 | |||
114 | return; |
||
115 | } |
||
116 | |||
117 | 1 | gravityview()->log->debug( 'adding form {form_id} to blacklist because entry #{lead_id} was deleted', array( 'form_id' => $entry['form_id'], 'entry_id' => $lead_id, 'data' => array( 'value' => $property_value, 'previous' => $previous_value ) ) ); |
|
118 | |||
119 | 1 | $this->blacklist_add( $entry['form_id'] ); |
|
120 | 1 | } |
|
121 | |||
122 | /** |
||
123 | * When an entry is updated, add the entry's form to the cache blacklist |
||
124 | * |
||
125 | * @param array $form GF form array |
||
126 | * @param int $lead_id Entry ID |
||
127 | * |
||
128 | * @return void |
||
129 | */ |
||
130 | 3 | public function entry_updated( $form, $lead_id ) { |
|
131 | |||
132 | 3 | gravityview()->log->debug(' adding form {form_id} to blacklist because entry #{entry_id} was updated', array( 'form_id' => $form['id'], 'entry_id' => $lead_id ) ); |
|
133 | |||
134 | 3 | $this->blacklist_add( $form['id'] ); |
|
135 | 3 | } |
|
136 | |||
137 | /** |
||
138 | * When an entry is created, add the entry's form to the cache blacklist |
||
139 | * |
||
140 | * We don't want old caches; when an entry is added, we want to clear the cache. |
||
141 | * |
||
142 | * @param array $entry GF entry array |
||
143 | * @param array $form GF form array |
||
144 | * |
||
145 | * @return void |
||
146 | */ |
||
147 | public function entry_created( $entry, $form ) { |
||
148 | |||
149 | gravityview()->log->debug( 'adding form {form_id} to blacklist because entry #{entry_id} was created', array( 'form_id' => $form['id'], 'entry_id' => $entry['id'] ) ); |
||
150 | |||
151 | $this->blacklist_add( $form['id'] ); |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Clear the cache when entries are added via GFAPI::add_entry(). |
||
156 | * |
||
157 | * @param array $entry The GF Entry array |
||
158 | * @param array $form The GF Form array |
||
159 | * |
||
160 | * @return void |
||
161 | */ |
||
162 | 64 | public function entry_added( $entry, $form ) { |
|
163 | 64 | if ( is_wp_error( $entry ) ) { |
|
164 | return; |
||
165 | } |
||
166 | |||
167 | 64 | gravityview()->log->debug( 'adding form {form_id} to blacklist because entry #{entry_id} was added', array( 'form_id' => $form['id'], 'entry_id' => $entry['id'] ) ); |
|
168 | |||
169 | 64 | $this->blacklist_add( $form['id'] ); |
|
170 | 64 | } |
|
171 | |||
172 | /** |
||
173 | * Calculate the prefix based on the Form IDs |
||
174 | * |
||
175 | * @param int|array $form_ids Form IDs to generate prefix for |
||
176 | * |
||
177 | * @return string Prefix for the cache string used in set_key() |
||
178 | */ |
||
179 | 2 | protected function get_cache_key_prefix( $form_ids = NULL ) { |
|
180 | |||
181 | 2 | if ( is_null( $form_ids ) ) { |
|
182 | 2 | $form_ids = $this->form_ids; |
|
183 | } |
||
184 | |||
185 | // Normally just one form, but supports multiple forms |
||
186 | // |
||
187 | // Array of IDs 12, 5, 14 would result in `f:12-f:5-f:14` |
||
188 | 2 | $forms = 'f:' . implode( '-f:', (array) $form_ids ); |
|
189 | |||
190 | // Prefix for transient keys |
||
191 | // Now the prefix would be: `gv-cache-f:12-f:5-f:14-` |
||
192 | 2 | return 'gv-cache-' . $forms . '-'; |
|
193 | |||
194 | } |
||
195 | |||
196 | /** |
||
197 | * Set the transient key based on the form IDs and the arguments passed to the class |
||
198 | */ |
||
199 | 2 | protected function set_key() { |
|
200 | |||
201 | // Don't set key if no forms have been set. |
||
202 | 2 | if ( empty( $this->form_ids ) ) { |
|
203 | return; |
||
204 | } |
||
205 | |||
206 | 2 | $key = $this->get_cache_key_prefix() . sha1( serialize( $this->args ) ); |
|
207 | |||
208 | // The transient name column can handle up to 64 characters. |
||
209 | // The `_transient_timeout_` prefix that is prepended to the string is 11 characters. |
||
210 | // 64 - 19 = 45 |
||
211 | // We make sure the key isn't too long or else WP doesn't store data. |
||
212 | 2 | $this->key = substr( $key, 0, 45 ); |
|
213 | 2 | } |
|
214 | |||
215 | /** |
||
216 | * Allow public access to get transient key |
||
217 | * |
||
218 | * @return string Transient key |
||
219 | */ |
||
220 | 2 | public function get_key() { |
|
223 | |||
224 | /** |
||
225 | * Add form IDs to a "blacklist" to force the cache to be refreshed |
||
226 | * |
||
227 | * |
||
228 | * |
||
229 | * @param int|array $form_ids Form IDs to force to be updated |
||
230 | * |
||
231 | * @return boolean False if value was not updated and true if value was updated. |
||
232 | */ |
||
233 | 64 | public function blacklist_add( $form_ids ) { |
|
256 | |||
257 | /** |
||
258 | * Remove Form IDs from blacklist |
||
259 | * |
||
260 | * @param int|array $form_ids Form IDs to add |
||
261 | * |
||
262 | * @return boolean Whether the removal was successful |
||
263 | */ |
||
264 | public function blacklist_remove( $form_ids ) { |
||
278 | |||
279 | |||
280 | /** |
||
281 | * Is a form ID in the cache blacklist |
||
282 | * |
||
283 | * @param int|array $form_ids Form IDs to check if in blacklist |
||
284 | * |
||
285 | * @return [type] [description] |
||
286 | */ |
||
287 | 3 | function in_blacklist( $form_ids = NULL ) { |
|
313 | |||
314 | |||
315 | /** |
||
316 | * Get transient result |
||
317 | * |
||
318 | * @param string $key Transient key to fetch |
||
319 | * |
||
320 | * @return mixed False: Not using cache or cache was a WP_Error object; NULL: no results found; Mixed: cache value |
||
321 | */ |
||
322 | 2 | public function get( $key = NULL ) { |
|
355 | |||
356 | /** |
||
357 | * Cache content as a transient. |
||
358 | * |
||
359 | * Cache time defaults to 1 week |
||
360 | * |
||
361 | * @param [type] $content [description] |
||
362 | * @param [type] $filter_name Name used to modify the cache time. Will be set to `gravityview_cache_time_{$filter_name}`. |
||
363 | */ |
||
364 | 2 | public function set( $content, $filter_name = '' ) { |
|
386 | |||
387 | /** |
||
388 | * Delete cached transients based on form IDs |
||
389 | * |
||
390 | * @todo Use REGEX to match forms when array of form IDs is passed, instead of using a simple LIKE |
||
391 | * @todo Rate limit deleting to prevent abuse |
||
392 | * |
||
393 | * @param int|array $form_ids Form IDs to delete |
||
394 | * |
||
395 | * @return [type] [description] |
||
396 | */ |
||
397 | 2 | public function delete( $form_ids = NULL ) { |
|
438 | |||
439 | /** |
||
440 | * Schedule expired transient cleanup twice a day. |
||
441 | * |
||
442 | * Can be overruled by the `gravityview_cleanup_transients` filter (returns boolean) |
||
443 | * |
||
444 | * @return void |
||
445 | */ |
||
446 | public function schedule_transient_cleanup() { |
||
462 | |||
463 | /** |
||
464 | * Delete expired transients. |
||
465 | * |
||
466 | * The code is copied from the Delete Expired Transients, with slight modifications to track # of results and to get the blog ID dynamically |
||
467 | * |
||
468 | * @see https://wordpress.org/plugins/delete-expired-transients/ Plugin where the code was taken from |
||
469 | * @see DelxtransCleaners::clearBlogExpired() |
||
470 | * @return void |
||
471 | */ |
||
472 | public function delete_expired_transients() { |
||
514 | |||
515 | /** |
||
516 | * Check whether to use cached results, if available |
||
517 | * |
||
518 | * If the user can edit posts, they are able to override whether to cache results by adding `cache` or `nocache` to the URL requested. |
||
519 | * |
||
520 | * @return boolean True: use cache; False: don't use cache |
||
521 | */ |
||
522 | 2 | public function use_cache() { |
|
562 | |||
563 | } |
||
564 | |||
566 |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.