PluginProbe
ElasticPress / 5.0.0
ElasticPress v5.0.0
5.3.5 5.3.4 3.6.5 3.6.6 4.0.0 4.0.1 4.1.0 4.2.0 4.2.1 4.2.2 4.3.0 4.3.1 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.6.0 4.6.1 4.7.0 4.7.1 4.7.2 5.0.0 5.0.1 5.0.2 All 108 releases
elasticpress / includes / classes / SyncManager.php

SyncManager.php in ElasticPress 5.0.0, at includes/classes/SyncManager.php

352 lines 8.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * SyncManager common functionality
4 *
5 * @package elasticpress
6 * @since 3.0
7 */
8
9 namespace ElasticPress;
10
11 use ElasticPress\Utils;
12
13 /**
14 * Abstract sync manager class to be extended for each indexable
15 */
16 abstract class SyncManager {
17
18 /**
19 * Save objects for indexing later
20 *
21 * @since 3.0
22 * @var array
23 */
24 public $sync_queue = [];
25
26 /**
27 * Indexable slug
28 *
29 * @var string
30 * @since 3.0
31 */
32 public $indexable_slug;
33
34 /**
35 * Create new SyncManager
36 *
37 * @param string $indexable_slug Indexable slug.
38 * @since 3.0
39 */
40 public function __construct( $indexable_slug ) {
41 $this->indexable_slug = $indexable_slug;
42
43 if ( defined( 'EP_SYNC_CHUNK_LIMIT' ) && is_numeric( EP_SYNC_CHUNK_LIMIT ) ) {
44 /**
45 * We also sync when we exceed Chunk limit set.
46 * This is sometimes useful when posts are generated programatically.
47 */
48 add_action( 'ep_after_add_to_queue', [ $this, 'index_sync_on_chunk_limit' ] );
49 }
50 /**
51 * We do all syncing on shutdown or redirect
52 */
53 add_action( 'shutdown', [ $this, 'index_sync_queue' ] );
54 add_filter( 'wp_redirect', [ $this, 'index_sync_queue_on_redirect' ], 10, 1 );
55
56 /**
57 * Actions for multisite
58 */
59 add_action( 'delete_blog', array( $this, 'action_delete_blog_from_index' ) );
60 add_action( 'make_delete_blog', array( $this, 'action_delete_blog_from_index' ) );
61 add_action( 'make_spam_blog', array( $this, 'action_delete_blog_from_index' ) );
62 add_action( 'archive_blog', array( $this, 'action_delete_blog_from_index' ) );
63 add_action( 'deactivate_blog', array( $this, 'action_delete_blog_from_index' ) );
64
65 // Implemented by children.
66 $this->setup();
67 }
68
69 /**
70 * Get sync queue.
71 *
72 * @since 5.0.0
73 * @param int $blog_id Blog ID to retrieve queue.
74 * @return array
75 */
76 public function get_sync_queue( $blog_id = false ) {
77 if ( ! $blog_id ) {
78 $blog_id = get_current_blog_id();
79 }
80
81 if ( ! isset( $this->sync_queue[ $blog_id ] ) ) {
82 $this->sync_queue[ $blog_id ] = [];
83 }
84
85 return $this->sync_queue[ $blog_id ];
86 }
87
88 /**
89 * Add an object to the sync queue.
90 *
91 * @since 3.1.2
92 *
93 * @param int $object_id Object ID to sync.
94 * @return boolean
95 */
96 public function add_to_queue( $object_id ) {
97 if ( ! is_numeric( $object_id ) ) {
98 return false;
99 }
100
101 $current_blog_id = get_current_blog_id();
102 if ( ! isset( $this->sync_queue[ $current_blog_id ] ) ) {
103 $this->sync_queue[ $current_blog_id ] = [];
104 }
105
106 $this->sync_queue[ $current_blog_id ][ $object_id ] = true;
107
108 /**
109 * Fires after item is added to sync queue
110 *
111 * @hook ep_after_add_to_queue
112 * @param {int} $object_id ID of object
113 * @param {array} $sync_queue Current sync queue
114 * @since 3.1.2
115 */
116 do_action( 'ep_after_add_to_queue', $object_id, $this->get_sync_queue() );
117
118 return true;
119 }
120
121 /**
122 * Remove an object from the sync queue.
123 *
124 * @since 3.5
125 *
126 * @param int $object_id Object ID to remove from the queue.
127 * @return boolean
128 */
129 public function remove_from_queue( $object_id ) {
130 if ( ! is_numeric( $object_id ) ) {
131 return false;
132 }
133
134 $current_blog_id = get_current_blog_id();
135 if ( ! isset( $this->sync_queue[ $current_blog_id ] ) ) {
136 $this->sync_queue[ $current_blog_id ] = [];
137 }
138
139 unset( $this->sync_queue[ $current_blog_id ][ $object_id ] );
140
141 /**
142 * Fires after item is removed from sync queue
143 *
144 * @hook ep_after_remove_from_queue
145 * @param {int} $object_id ID of object
146 * @param {array} $sync_queue Current sync queue
147 * @since 3.5
148 */
149 do_action( 'ep_after_remove_from_queue', $object_id, $this->get_sync_queue() );
150
151 return true;
152 }
153
154 /**
155 * Reset the sync queue.
156 *
157 * @since 5.0.0
158 * @param int $blog_id Blog ID to reset queue
159 */
160 public function reset_sync_queue( $blog_id = false ) {
161 if ( ! $blog_id ) {
162 $blog_id = get_current_blog_id();
163 }
164
165 $this->sync_queue[ $blog_id ] = [];
166 }
167
168 /**
169 * Sync queued objects if the EP_SYNC_CHUNK_LIMIT is reached.
170 *
171 * @since 3.1.2
172 * @return boolean
173 */
174 public function index_sync_on_chunk_limit() {
175 if ( defined( 'EP_SYNC_CHUNK_LIMIT' ) && is_numeric( EP_SYNC_CHUNK_LIMIT ) &&
176 is_array( $this->get_sync_queue() ) && count( $this->get_sync_queue() ) > EP_SYNC_CHUNK_LIMIT ) {
177 $this->index_sync_queue();
178 }
179 return true;
180 }
181
182 /**
183 * Sync queued objects before a redirect occurs. Hackish but very important since
184 * shutdown won't be firing
185 *
186 * @param string $location Redirect location.
187 * @since 3.0
188 * @return string
189 */
190 public function index_sync_queue_on_redirect( $location ) {
191 $this->index_sync_queue();
192
193 return $location;
194 }
195
196 /**
197 * Sync objects in queue.
198 *
199 * @since 3.0
200 */
201 public function index_sync_queue() {
202 if ( empty( $this->sync_queue ) ) {
203 return;
204 }
205
206 $current_blog_id = get_current_blog_id();
207 foreach ( $this->sync_queue as $blog_id => $sync_queue ) {
208 if ( $current_blog_id !== $blog_id ) {
209 switch_to_blog( $blog_id );
210 }
211
212 /**
213 * Allow other code to intercept the sync process
214 *
215 * @hook pre_ep_index_sync_queue
216 * @param {boolean} $bail True to skip the rest of index_sync_queue(), false to continue normally
217 * @param {SyncManager} $sync_manager SyncManager instance for the indexable
218 * @param {string} $indexable_slug Slug of the indexable being synced
219 * @since 3.5
220 */
221 if ( apply_filters( 'pre_ep_index_sync_queue', false, $this, $this->indexable_slug ) ) {
222 return;
223 }
224
225 /**
226 * Backwards compat for pre-3.0
227 */
228 foreach ( $sync_queue as $object_id => $value ) {
229 /**
230 * Fires when object in queue are synced
231 *
232 * @hook ep_sync_on_meta_update_queue
233 * @param {int} $object_id ID of object
234 */
235 do_action( 'ep_sync_on_meta_update', $object_id );
236 }
237
238 // Bulk sync them all.
239 Indexables::factory()->get( $this->indexable_slug )->bulk_index_dynamically( array_keys( $this->get_sync_queue( $blog_id ) ) );
240
241 /**
242 * Make sure to reset sync queue in case an shutdown happens before a redirect
243 * when a redirect has already been triggered.
244 */
245 $this->reset_sync_queue( $blog_id );
246
247 if ( $current_blog_id !== $blog_id ) {
248 restore_current_blog();
249 }
250 }
251 }
252
253 /**
254 * Check if we can index content in the current blog
255 *
256 * @since 3.5
257 * @return boolean
258 */
259 public function can_index_site() {
260 if ( ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) && ! Utils\is_site_indexable() ) {
261 $this->tear_down();
262 return false;
263 }
264
265 return true;
266 }
267
268 /**
269 * Determine whether syncing an indexable should take place.
270 *
271 * Returns true or false depending on the value of the WP_IMPORTING global.
272 * Contains the 'ep_sync_indexable_kill' filter that enables overriding the default behavior.
273 *
274 * @since 3.4.2
275 * @return bool
276 */
277 public function kill_sync() {
278
279 $is_importing = defined( 'WP_IMPORTING' ) && true === WP_IMPORTING;
280
281 /**
282 * Filter whether to bypass sync.
283 *
284 * @since 3.4.2
285 * @hook ep_sync_indexable_kill
286 * @param {boolean} $kill True if WP_IMPORTING is defined and true, else false.
287 * @param {array} $indexable_slug Indexable slug.
288 */
289 return apply_filters( 'ep_sync_indexable_kill', $is_importing, $this->indexable_slug );
290 }
291
292 /**
293 * Remove blog from index when a site is deleted, archived, or deactivated
294 *
295 * @param int $blog_id WP Blog ID.
296 */
297 public function action_delete_blog_from_index( $blog_id ) {
298 if ( $this->kill_sync() ) {
299 return;
300 }
301
302 $indexable = Indexables::factory()->get( $this->indexable_slug );
303
304 // Don't delete global indexes
305 if ( $indexable->global ) {
306 return;
307 }
308
309 /**
310 * Filter to whether to keep index on site deletion
311 *
312 * @hook ep_keep_index
313 * @since 3.0
314 * @since 3.6.2 Moved from Post\SyncManager to the main SyncManager class
315 * @since 3.6.5 Added `$blog_id` and `$indexable_slug`
316 * @param {bool} $keep True means don't delete index
317 * @param {int} $blog_id WP Blog ID
318 * @param {string} $indexable_slug Indexable slug
319 * @return {bool} New value
320 */
321 if ( $indexable->index_exists( $blog_id ) && ! apply_filters( 'ep_keep_index', false, $blog_id, $this->indexable_slug ) ) {
322 $indexable->delete_index( $blog_id );
323 }
324 }
325
326 /**
327 * Clear the cache of the total fields limit
328 *
329 * @since 4.7.0
330 */
331 public function clear_index_settings_cache() {
332 $indexable = Indexables::factory()->get( $this->indexable_slug );
333 $cache_key = 'ep_index_settings_' . $indexable->get_index_name();
334
335 Utils\delete_transient( $cache_key );
336 }
337
338 /**
339 * Implementation should setup hooks/filters
340 *
341 * @since 3.0
342 */
343 abstract public function setup();
344
345 /**
346 * Implementation (for multisite) should un-setup hooks/filters if applicable.
347 *
348 * @since 4.0
349 */
350 abstract public function tear_down();
351 }
352