Completed
Push — enhance/landing-page-styles ( 6cb3da...9fe5ee )
by
unknown
102:36 queued 92:35
created

Jetpack_Sync_Module_Full_Sync::is_finished()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 2
b 1
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * This class does a full resync of the database by
5
 * enqueuing an outbound action for every single object
6
 * that we care about.
7
 *
8
 * This class, and its related class Jetpack_Sync_Module, contain a few non-obvious optimisations that should be explained:
9
 * - we fire an action called jetpack_full_sync_start so that WPCOM can erase the contents of the cached database
10
 * - for each object type, we page through the object IDs and enqueue them by firing some monitored actions
11
 * - we load the full objects for those IDs in chunks of Jetpack_Sync_Module::ARRAY_CHUNK_SIZE (to reduce the number of MySQL calls)
12
 * - we fire a trigger for the entire array which the Jetpack_Sync_Listener then serializes and queues.
13
 */
14
15
require_once 'class.jetpack-sync-wp-replicastore.php';
16
17
class Jetpack_Sync_Module_Full_Sync extends Jetpack_Sync_Module {
18
	const STATUS_OPTION_PREFIX = 'jetpack_sync_full_';
19
	const FULL_SYNC_TIMEOUT = 3600;
20
21
	public function name() {
22
		return 'full-sync';
23
	}
24
25
	function init_listeners( $callable ) {
26
		// synthetic actions for full sync
27
		add_action( 'jetpack_full_sync_start', $callable );
28
		add_action( 'jetpack_full_sync_end', $callable );
29
	}
30
31
	function init_before_send() {
32
		// this is triggered after actions have been processed on the server
33
		add_action( 'jetpack_sync_processed_actions', array( $this, 'update_sent_progress_action' ) );
34
	}
35
36
	function start( $modules = null ) {
37
		if ( ! $this->should_start_full_sync() ) {
38
			return false;
39
		}
40
41
		// ensure listener is loaded so we can guarantee full sync actions are enqueued
42
		require_once dirname( __FILE__ ) . '/class.jetpack-sync-listener.php';
43
		Jetpack_Sync_Listener::get_instance();
44
45
		/**
46
		 * Fires when a full sync begins. This action is serialized
47
		 * and sent to the server so that it knows a full sync is coming.
48
		 *
49
		 * @since 4.2.0
50
		 */
51
		do_action( 'jetpack_full_sync_start' );
52
		$this->set_status_queuing_started();
53
54
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
55
			$module_name = $module->name();
56
			if ( is_array( $modules ) && ! in_array( $module_name, $modules ) ) {
57
				continue;
58
			}
59
60
			$items_enqueued = $module->enqueue_full_sync_actions();
61
			if ( ! is_null( $items_enqueued ) && $items_enqueued > 0 ) {
62
				// TODO: only update this once every N items, then at end - why cause all that DB churn?
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
63
				$this->update_status_option( "{$module->name()}_queued", $items_enqueued );
64
			}
65
		}
66
67
		$this->set_status_queuing_finished();
68
69
		$store = new Jetpack_Sync_WP_Replicastore();
70
71
		/**
72
		 * Fires when a full sync ends. This action is serialized
73
		 * and sent to the server with checksums so that we can confirm the
74
		 * sync was successful.
75
		 *
76
		 * @since 4.2.0
77
		 */
78
		do_action( 'jetpack_full_sync_end', $store->checksum_all() );
79
80
		return true;
81
	}
82
83
	private function should_start_full_sync() {
84
85
		// We should try sync if we haven't started it yet or if we have finished it.
86
		if ( ! $this->is_started() || $this->is_finished() ) {
87
			return true;
88
		}
89
90
		// allow enqueuing if last full sync was started more than FULL_SYNC_TIMEOUT seconds ago
91
		$started_at = $this->get_status_option( "started" );
92
		if ( $started_at > 0 && $started_at + self::FULL_SYNC_TIMEOUT < time() ) {
93
			return true;
94
		}
95
96
		return false;
97
	}
98
99
	function update_sent_progress_action( $actions ) {
100
101
		// quick way to map to first items with an array of arrays
102
		$actions_with_counts = array_count_values( array_map( 'reset', $actions ) );
103
104
		if ( ! $this->is_started() || $this->is_finished() ) {
105
			return;
106
		}
107
108
		if ( isset( $actions_with_counts['jetpack_full_sync_start'] ) ) {
109
			$this->update_status_option( "sent_started", time() );
110
		}
111
112
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
113
			$module_actions = $module->get_full_sync_actions();
114
			$items_sent     = 0;
115
			foreach ( $module_actions as $module_action ) {
116
				if ( isset( $actions_with_counts[ $module_action ] ) ) {
117
					$items_sent += $actions_with_counts[ $module_action ];
118
				}
119
			}
120
121
			if ( $items_sent > 0 ) {
122
				$this->update_status_option( "{$module->name()}_sent", $items_sent );
123
			}	
124
		}
125
126
		if ( isset( $actions_with_counts['jetpack_full_sync_end'] ) ) {
127
			$this->update_status_option( "finished", time() );
128
		}
129
	}
130
131
	private function set_status_queuing_started() {
132
		$this->clear_status();
133
		$this->update_status_option( "started", time() );
134
	}
135
136
	private function set_status_queuing_finished() {
137
		$this->update_status_option( "queue_finished", time() );
138
	}
139
140
	public function is_started() {
141
		return !! $this->get_status_option( "started" );
142
	}
143
144
	public function is_finished() {
145
		return !! $this->get_status_option( "finished" );
146
	}
147
148
	public function get_status() {
149
		$status = array(
150
			'started'        => $this->get_status_option( 'started' ),
151
			'queue_finished' => $this->get_status_option( 'queue_finished' ),
152
			'sent_started'   => $this->get_status_option( 'sent_started' ),
153
			'finished'       => $this->get_status_option( 'finished' ),
154
			'sent'           => array(),
155
			'queue'          => array(),
156
		);
157
158
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
159
			$queued = $this->get_status_option( "{$module->name()}_queued" );
160
			$sent   = $this->get_status_option( "{$module->name()}_sent" );
161
162
			if ( $queued > 0 ) {
163
				$status[ 'queue' ][ $module->name() ] = $queued;
164
			}
165
			
166
			if ( $sent > 0 ) {
167
				$status[ 'sent' ][ $module->name() ] = $sent;
168
			}
169
		}
170
171
		return $status;
172
	}
173
174
	public function clear_status() {
175
		$prefix = self::STATUS_OPTION_PREFIX;
176
		delete_option( "{$prefix}_started" );
177
		delete_option( "{$prefix}_queue_finished" );
178
		delete_option( "{$prefix}_sent_started" );
179
		delete_option( "{$prefix}_finished" );
180
181
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
182
			delete_option( "{$prefix}_{$module->name()}_queued" );
183
			delete_option( "{$prefix}_{$module->name()}_sent" );
184
		}
185
	}
186
187
	private function get_status_option( $option ) {
188
		$prefix = self::STATUS_OPTION_PREFIX;
189
		return intval( get_option( "{$prefix}_{$option}", 0 ) );
190
	}
191
192
	private function update_status_option( $name, $value ) {
193
		$prefix = self::STATUS_OPTION_PREFIX;
194
		/**
195
		 * Allowing update_option to change autoload status only shipped in WordPress v4.2
196
		 * @link https://github.com/WordPress/WordPress/commit/305cf8b95
197
		 */
198
		if ( version_compare( $GLOBALS['wp_version'], '4.2', '>=' ) ) {
199
			update_option( "{$prefix}_{$name}", $value, false );
200
		} else {
201
			update_option( "{$prefix}_{$name}", $value );
202
		}
203
	}
204
}
205