355 lines
8.6 KiB
PHP
355 lines
8.6 KiB
PHP
<?php
|
|
/**
|
|
* SyncManager common functionality
|
|
*
|
|
* @package elasticpress
|
|
* @since 3.0
|
|
*/
|
|
|
|
namespace ElasticPress;
|
|
|
|
use ElasticPress\Utils;
|
|
|
|
/**
|
|
* Abstract sync manager class to be extended for each indexable
|
|
*/
|
|
abstract class SyncManager {
|
|
|
|
/**
|
|
* Save objects for indexing later
|
|
*
|
|
* @since 3.0
|
|
* @var array
|
|
*/
|
|
public $sync_queue = [];
|
|
|
|
/**
|
|
* Indexable slug
|
|
*
|
|
* @var string
|
|
* @since 3.0
|
|
*/
|
|
public $indexable_slug;
|
|
|
|
/**
|
|
* Create new SyncManager
|
|
*
|
|
* @param string $indexable_slug Indexable slug.
|
|
* @since 3.0
|
|
*/
|
|
public function __construct( $indexable_slug ) {
|
|
$this->indexable_slug = $indexable_slug;
|
|
|
|
if ( defined( 'EP_SYNC_CHUNK_LIMIT' ) && is_numeric( EP_SYNC_CHUNK_LIMIT ) ) {
|
|
/**
|
|
* We also sync when we exceed Chunk limit set.
|
|
* This is sometimes useful when posts are generated programmatically.
|
|
*/
|
|
add_action( 'ep_after_add_to_queue', [ $this, 'index_sync_on_chunk_limit' ] );
|
|
}
|
|
/**
|
|
* We do all syncing on shutdown or redirect
|
|
*/
|
|
add_action( 'shutdown', [ $this, 'index_sync_queue' ] );
|
|
add_filter( 'wp_redirect', [ $this, 'index_sync_queue_on_redirect' ], 10, 1 );
|
|
|
|
/**
|
|
* Actions for multisite
|
|
*/
|
|
add_action( 'delete_blog', array( $this, 'action_delete_blog_from_index' ) );
|
|
add_action( 'make_delete_blog', array( $this, 'action_delete_blog_from_index' ) );
|
|
add_action( 'make_spam_blog', array( $this, 'action_delete_blog_from_index' ) );
|
|
add_action( 'archive_blog', array( $this, 'action_delete_blog_from_index' ) );
|
|
add_action( 'deactivate_blog', array( $this, 'action_delete_blog_from_index' ) );
|
|
|
|
// Implemented by children.
|
|
$this->setup();
|
|
}
|
|
|
|
/**
|
|
* Get sync queue.
|
|
*
|
|
* @since 5.0.0
|
|
* @param int $blog_id Blog ID to retrieve queue.
|
|
* @return array
|
|
*/
|
|
public function get_sync_queue( $blog_id = false ) {
|
|
if ( ! $blog_id ) {
|
|
$blog_id = get_current_blog_id();
|
|
}
|
|
|
|
if ( ! isset( $this->sync_queue[ $blog_id ] ) ) {
|
|
$this->sync_queue[ $blog_id ] = [];
|
|
}
|
|
|
|
return $this->sync_queue[ $blog_id ];
|
|
}
|
|
|
|
/**
|
|
* Add an object to the sync queue.
|
|
*
|
|
* @since 3.1.2
|
|
*
|
|
* @param int $object_id Object ID to sync.
|
|
* @return boolean
|
|
*/
|
|
public function add_to_queue( $object_id ) {
|
|
if ( ! is_numeric( $object_id ) ) {
|
|
return false;
|
|
}
|
|
|
|
$current_blog_id = get_current_blog_id();
|
|
if ( ! isset( $this->sync_queue[ $current_blog_id ] ) ) {
|
|
$this->sync_queue[ $current_blog_id ] = [];
|
|
}
|
|
|
|
$this->sync_queue[ $current_blog_id ][ $object_id ] = true;
|
|
|
|
/**
|
|
* Fires after item is added to sync queue
|
|
*
|
|
* @hook ep_after_add_to_queue
|
|
* @param {int} $object_id ID of object
|
|
* @param {array} $sync_queue Current sync queue
|
|
* @since 3.1.2
|
|
*/
|
|
do_action( 'ep_after_add_to_queue', $object_id, $this->get_sync_queue() );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Remove an object from the sync queue.
|
|
*
|
|
* @since 3.5
|
|
*
|
|
* @param int $object_id Object ID to remove from the queue.
|
|
* @return boolean
|
|
*/
|
|
public function remove_from_queue( $object_id ) {
|
|
if ( ! is_numeric( $object_id ) ) {
|
|
return false;
|
|
}
|
|
|
|
$current_blog_id = get_current_blog_id();
|
|
if ( ! isset( $this->sync_queue[ $current_blog_id ] ) ) {
|
|
$this->sync_queue[ $current_blog_id ] = [];
|
|
}
|
|
|
|
unset( $this->sync_queue[ $current_blog_id ][ $object_id ] );
|
|
|
|
/**
|
|
* Fires after item is removed from sync queue
|
|
*
|
|
* @hook ep_after_remove_from_queue
|
|
* @param {int} $object_id ID of object
|
|
* @param {array} $sync_queue Current sync queue
|
|
* @since 3.5
|
|
*/
|
|
do_action( 'ep_after_remove_from_queue', $object_id, $this->get_sync_queue() );
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Reset the sync queue.
|
|
*
|
|
* @since 5.0.0
|
|
* @param int $blog_id Blog ID to reset queue
|
|
*/
|
|
public function reset_sync_queue( $blog_id = false ) {
|
|
if ( ! $blog_id ) {
|
|
$blog_id = get_current_blog_id();
|
|
}
|
|
|
|
$this->sync_queue[ $blog_id ] = [];
|
|
}
|
|
|
|
/**
|
|
* Sync queued objects if the EP_SYNC_CHUNK_LIMIT is reached.
|
|
*
|
|
* @since 3.1.2
|
|
* @return boolean
|
|
*/
|
|
public function index_sync_on_chunk_limit() {
|
|
if ( defined( 'EP_SYNC_CHUNK_LIMIT' ) && is_numeric( EP_SYNC_CHUNK_LIMIT ) &&
|
|
is_array( $this->get_sync_queue() ) && count( $this->get_sync_queue() ) > EP_SYNC_CHUNK_LIMIT ) {
|
|
$this->index_sync_queue();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Sync queued objects before a redirect occurs. Hackish but very important since
|
|
* shutdown won't be firing
|
|
*
|
|
* @param string $location Redirect location.
|
|
* @since 3.0
|
|
* @return string
|
|
*/
|
|
public function index_sync_queue_on_redirect( $location ) {
|
|
$this->index_sync_queue();
|
|
|
|
return $location;
|
|
}
|
|
|
|
/**
|
|
* Sync objects in queue.
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
public function index_sync_queue() {
|
|
if ( empty( $this->sync_queue ) ) {
|
|
return;
|
|
}
|
|
|
|
$current_blog_id = get_current_blog_id();
|
|
foreach ( $this->sync_queue as $blog_id => $sync_queue ) {
|
|
if ( empty( $sync_queue ) ) {
|
|
continue;
|
|
}
|
|
|
|
if ( $current_blog_id !== $blog_id ) {
|
|
switch_to_blog( $blog_id );
|
|
}
|
|
|
|
/**
|
|
* Allow other code to intercept the sync process
|
|
*
|
|
* @hook pre_ep_index_sync_queue
|
|
* @param {boolean} $bail True to skip the rest of index_sync_queue(), false to continue normally
|
|
* @param {SyncManager} $sync_manager SyncManager instance for the indexable
|
|
* @param {string} $indexable_slug Slug of the indexable being synced
|
|
* @since 3.5
|
|
*/
|
|
if ( apply_filters( 'pre_ep_index_sync_queue', false, $this, $this->indexable_slug ) ) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Backwards compat for pre-3.0
|
|
*/
|
|
foreach ( $sync_queue as $object_id => $value ) {
|
|
/**
|
|
* Fires when object in queue are synced
|
|
*
|
|
* @hook ep_sync_on_meta_update_queue
|
|
* @param {int} $object_id ID of object
|
|
*/
|
|
do_action( 'ep_sync_on_meta_update', $object_id );
|
|
}
|
|
|
|
// Bulk sync them all.
|
|
Indexables::factory()->get( $this->indexable_slug )->bulk_index_dynamically( array_keys( $this->get_sync_queue( $blog_id ) ) );
|
|
|
|
/**
|
|
* Make sure to reset sync queue in case an shutdown happens before a redirect
|
|
* when a redirect has already been triggered.
|
|
*/
|
|
$this->reset_sync_queue( $blog_id );
|
|
|
|
if ( $current_blog_id !== $blog_id ) {
|
|
restore_current_blog();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if we can index content in the current blog
|
|
*
|
|
* @since 3.5
|
|
* @return boolean
|
|
*/
|
|
public function can_index_site() {
|
|
if ( ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) && ! Utils\is_site_indexable() ) {
|
|
$this->tear_down();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Determine whether syncing an indexable should take place.
|
|
*
|
|
* Returns true or false depending on the value of the WP_IMPORTING global.
|
|
* Contains the 'ep_sync_indexable_kill' filter that enables overriding the default behavior.
|
|
*
|
|
* @since 3.4.2
|
|
* @return bool
|
|
*/
|
|
public function kill_sync() {
|
|
|
|
$is_importing = defined( 'WP_IMPORTING' ) && true === WP_IMPORTING;
|
|
|
|
/**
|
|
* Filter whether to bypass sync.
|
|
*
|
|
* @since 3.4.2
|
|
* @hook ep_sync_indexable_kill
|
|
* @param {boolean} $kill True if WP_IMPORTING is defined and true, else false.
|
|
* @param {array} $indexable_slug Indexable slug.
|
|
*/
|
|
return apply_filters( 'ep_sync_indexable_kill', $is_importing, $this->indexable_slug );
|
|
}
|
|
|
|
/**
|
|
* Remove blog from index when a site is deleted, archived, or deactivated
|
|
*
|
|
* @param int $blog_id WP Blog ID.
|
|
*/
|
|
public function action_delete_blog_from_index( $blog_id ) {
|
|
if ( $this->kill_sync() ) {
|
|
return;
|
|
}
|
|
|
|
$indexable = Indexables::factory()->get( $this->indexable_slug );
|
|
|
|
// Don't delete global indexes
|
|
if ( $indexable->global ) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Filter to whether to keep index on site deletion
|
|
*
|
|
* @hook ep_keep_index
|
|
* @since 3.0
|
|
* @since 3.6.2 Moved from Post\SyncManager to the main SyncManager class
|
|
* @since 3.6.5 Added `$blog_id` and `$indexable_slug`
|
|
* @param {bool} $keep True means don't delete index
|
|
* @param {int} $blog_id WP Blog ID
|
|
* @param {string} $indexable_slug Indexable slug
|
|
* @return {bool} New value
|
|
*/
|
|
if ( $indexable->index_exists( $blog_id ) && ! apply_filters( 'ep_keep_index', false, $blog_id, $this->indexable_slug ) ) {
|
|
$indexable->delete_index( $blog_id );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear the cache of the total fields limit
|
|
*
|
|
* @since 4.7.0
|
|
*/
|
|
public function clear_index_settings_cache() {
|
|
$indexable = Indexables::factory()->get( $this->indexable_slug );
|
|
$cache_key = 'ep_index_settings_' . $indexable->get_index_name();
|
|
|
|
Utils\delete_transient( $cache_key );
|
|
}
|
|
|
|
/**
|
|
* Implementation should setup hooks/filters
|
|
*
|
|
* @since 3.0
|
|
*/
|
|
abstract public function setup();
|
|
|
|
/**
|
|
* Implementation (for multisite) should un-setup hooks/filters if applicable.
|
|
*
|
|
* @since 4.0
|
|
*/
|
|
abstract public function tear_down();
|
|
}
|