Completed
Push — fix/always-sync-module-change-... ( 9738c9...5ef001 )
by
unknown
170:56 queued 160:35
created

Jetpack_Sync_Module_Full_Sync::start()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 42
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 6
eloc 16
c 3
b 0
f 0
nc 4
nop 1
dl 0
loc 42
rs 8.439
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_full_sync_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
		// ensure listener is loaded so we can guarantee full sync actions are enqueued
38
		require_once dirname( __FILE__ ) . '/class.jetpack-sync-listener.php';
39
		Jetpack_Sync_Listener::get_instance();
40
41
		/**
42
		 * Fires when a full sync begins. This action is serialized
43
		 * and sent to the server so that it knows a full sync is coming.
44
		 *
45
		 * @since 4.2.0
46
		 */
47
		do_action( 'jetpack_full_sync_start' );
48
		$this->set_status_queuing_started();
49
50
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
51
			$module_name = $module->name();
52
			if ( is_array( $modules ) && ! in_array( $module_name, $modules ) ) {
53
				continue;
54
			}
55
56
			$items_enqueued = $module->enqueue_full_sync_actions();
57
			if ( ! is_null( $items_enqueued ) && $items_enqueued > 0 ) {
58
				// 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...
59
				$this->update_status_option( "{$module->name()}_queued", $items_enqueued );
60
			}
61
		}
62
63
		$this->set_status_queuing_finished();
64
65
		$store = new Jetpack_Sync_WP_Replicastore();
66
67
		/**
68
		 * Fires when a full sync ends. This action is serialized
69
		 * and sent to the server with checksums so that we can confirm the
70
		 * sync was successful.
71
		 *
72
		 * @since 4.2.0
73
		 */
74
		do_action( 'jetpack_full_sync_end', $store->checksum_all() );
75
76
		return true;
77
	}
78
79
	function update_sent_progress_action( $actions ) {
80
81
		// quick way to map to first items with an array of arrays
82
		$actions_with_counts = array_count_values( array_map( 'reset', $actions ) );
83
84
		if ( ! $this->is_started() || $this->is_finished() ) {
85
			return;
86
		}
87
88
		if ( isset( $actions_with_counts['jetpack_full_sync_start'] ) ) {
89
			$this->update_status_option( "sent_started", time() );
90
		}
91
92
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
93
			$module_actions = $module->get_full_sync_actions();
94
			$items_sent     = 0;
95
			foreach ( $module_actions as $module_action ) {
96
				if ( isset( $actions_with_counts[ $module_action ] ) ) {
97
					$items_sent += $actions_with_counts[ $module_action ];
98
				}
99
			}
100
101
			if ( $items_sent > 0 ) {
102
				$this->update_status_option( "{$module->name()}_sent", $items_sent );
103
			}	
104
		}
105
106
		if ( isset( $actions_with_counts['jetpack_full_sync_end'] ) ) {
107
			$this->update_status_option( "finished", time() );
108
		}
109
	}
110
111
	private function set_status_queuing_started() {
112
		$this->clear_status();
113
		$this->update_status_option( "started", time() );
114
	}
115
116
	private function set_status_queuing_finished() {
117
		$this->update_status_option( "queue_finished", time() );
118
	}
119
120
	public function is_started() {
121
		return !! $this->get_status_option( "started" );
122
	}
123
124
	public function is_finished() {
125
		return !! $this->get_status_option( "finished" );
126
	}
127
128
	public function get_status() {
129
		$status = array(
130
			'started'        => $this->get_status_option( 'started' ),
131
			'queue_finished' => $this->get_status_option( 'queue_finished' ),
132
			'sent_started'   => $this->get_status_option( 'sent_started' ),
133
			'finished'       => $this->get_status_option( 'finished' ),
134
			'sent'           => array(),
135
			'queue'          => array(),
136
		);
137
138
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
139
			$queued = $this->get_status_option( "{$module->name()}_queued" );
140
			$sent   = $this->get_status_option( "{$module->name()}_sent" );
141
142
			if ( $queued ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $queued of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
143
				$status[ 'queue' ][ $module->name() ] = $queued;
144
			}
145
			
146
			if ( $sent ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sent of type null|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
147
				$status[ 'sent' ][ $module->name() ] = $sent;
148
			}
149
		}
150
151
		return $status;
152
	}
153
154
	public function clear_status() {
155
		$prefix = self::STATUS_OPTION_PREFIX;
156
		delete_option( "{$prefix}_started" );
157
		delete_option( "{$prefix}_queue_finished" );
158
		delete_option( "{$prefix}_sent_started" );
159
		delete_option( "{$prefix}_finished" );
160
161
		foreach ( Jetpack_Sync_Modules::get_modules() as $module ) {
162
			delete_option( "{$prefix}_{$module->name()}_queued" );
163
			delete_option( "{$prefix}_{$module->name()}_sent" );
164
		}
165
	}
166
167
	private function get_status_option( $option ) {
168
		$prefix = self::STATUS_OPTION_PREFIX;
169
170
		$value = get_option( "{$prefix}_{$option}", null );
171
		
172
		if ( ! $value ) {
173
			return null;
174
		}
175
176
		return intval( $value );
177
	}
178
179
	private function update_status_option( $name, $value ) {
180
		$prefix = self::STATUS_OPTION_PREFIX;
181
		/**
182
		 * Allowing update_option to change autoload status only shipped in WordPress v4.2
183
		 * @link https://github.com/WordPress/WordPress/commit/305cf8b95
184
		 */
185
		if ( version_compare( $GLOBALS['wp_version'], '4.2', '>=' ) ) {
186
			update_option( "{$prefix}_{$name}", $value, false );
187
		} else {
188
			update_option( "{$prefix}_{$name}", $value );
189
		}
190
	}
191
}
192