Completed
Push — update/non-admin-view ( b1ee9d...762240 )
by
unknown
10:19
created

Jetpack_Sync_Module_Full_Sync::get_status()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 4
b 0
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 contains 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 obtain a full list of object IDs to sync via a single API call (hoping that since they're ints, they can all fit in RAM)
11
 * - we load the full objects for those IDs in chunks of Jetpack_Sync_Full::ARRAY_CHUNK_SIZE (to reduce the number of MySQL calls)
12
 * - we fire a trigger for the entire array which the Jetpack_Sync_Sender 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 = 'jetpack_full_sync_status';
19
	const FULL_SYNC_TIMEOUT = 3600;
20
21
	private $sender;
0 ignored issues
show
Unused Code introduced by
The property $sender is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
22
23
	public function name() {
24
		return 'full-sync';
25
	}
26
27
	function init_listeners( $callable ) {
28
		// synthetic actions for full sync
29
		add_action( 'jetpack_full_sync_start', $callable );
30
		add_action( 'jetpack_full_sync_end', $callable );
31
	}
32
33
	function init_before_send() {
34
		// this is triggered after actions have been processed on the server
35
		add_action( 'jetpack_sync_processed_actions', array( $this, 'update_sent_progress_action' ) );
36
	}
37
38
	function start() {
39
		if( ! $this->should_start_full_sync() ) {
40
			return false;
41
		}
42
43
		// ensure listener is loaded so we can guarantee full sync actions are enqueued
44
		require_once dirname( __FILE__ ) . '/class.jetpack-sync-listener.php';
45
		Jetpack_Sync_Listener::getInstance();
46
47
		/**
48
		 * Fires when a full sync begins. This action is serialized
49
		 * and sent to the server so that it knows a full sync is coming.
50
		 *
51
		 * @since 4.2.0
52
		 */
53
		do_action( 'jetpack_full_sync_start' );
54
		$this->set_status_queuing_started();
55
56
		foreach( Jetpack_Sync_Modules::get_modules() as $module ) {
57
			$items_enqueued = $module->enqueue_full_sync_actions();
58
			$module_name = $module->name();
59
			if ( $items_enqueued !== 0 ) {
60
				$status = $this->get_status();
61
62
				if ( ! isset( $status['queue'][ $module_name ] ) ) {
63
					$status['queue'][ $module_name ] = 0;	
64
				}
65
66
				$status['queue'][ $module_name ] += $items_enqueued;
67
			}
68
			$this->update_status( $status );
0 ignored issues
show
Bug introduced by
The variable $status does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
69
		}
70
71
		$this->set_status_queuing_finished();
72
73
		$store = new Jetpack_Sync_WP_Replicastore();
74
75
		/**
76
		 * Fires when a full sync ends. This action is serialized
77
		 * and sent to the server with checksums so that we can confirm the
78
		 * sync was successful.
79
		 *
80
		 * @since 4.2.0
81
		 */
82
		do_action( 'jetpack_full_sync_end', $store->checksum_all() );
83
		return true;
84
	}
85
86
	private function should_start_full_sync() {
87
		$status = $this->get_status();
88
		
89
		// We should try sync if we haven't started it yet or if we have finished it.
90
		if( is_null( $status['started'] ) || is_integer( $status['finished'] ) ) {
91
			return true;
92
		}
93
94
		// allow enqueing if last full sync was started more than FULL_SYNC_TIMEOUT seconds ago
95
		if ( intval( $status['started'] ) + self::FULL_SYNC_TIMEOUT < time() ) {
96
			return true;
97
		}
98
99
		return false;
100
	}
101
102
	function update_sent_progress_action( $actions ) {
103
		// quick way to map to first items with an array of arrays
104
		$actions_with_counts = array_count_values( array_map( 'reset', $actions ) );
105
106
		$status = $this->get_status();
107
		if ( is_null( $status['started'] ) || $status['finished'] ) {
108
			return;
109
		}
110
111
		if ( isset( $actions_with_counts[ 'jetpack_full_sync_start' ] ) ) {
112
			$status['sent_started'] = time();
113
		}
114
115
		foreach( Jetpack_Sync_Modules::get_modules() as $module ) {
116
			$module_name = $module->name();
117
			$module_actions = $module->get_full_sync_actions();
118
			foreach( $module_actions as $module_action ) {
119
				if ( isset( $actions_with_counts[ $module_action ] ) ) {
120
					if ( ! isset( $status[ 'sent' ][ $module_name ] ) ) {
121
						$status['sent'][ $module_name ] = 0;	
122
					}
123
					$status['sent'][ $module_name ] += $actions_with_counts[ $module_action ];	
124
				}
125
			}
126
		}
127
128
		if ( isset( $actions_with_counts[ 'jetpack_full_sync_end' ] ) ) {
129
			$status['finished'] = time();
130
		}
131
132
		$this->update_status( $status );
133
	}
134
135
	private function set_status_queuing_started() {
136
		$status = $this->initial_status;
137
		$status[ 'started' ] = time();
138
		$this->update_status( $status );
139
	}
140
141
	private function set_status_queuing_finished() {
142
		$this->update_status( array( 'queue_finished' => time() ) );
143
	}
144
145
	private $initial_status = array(
146
		'started' => null,
147
		'queue_finished' => null,
148
		'sent_started' => null,
149
		'finished' => null,
150
		'sent' => array(),
151
		'queue' => array(),
152
	);
153
154
	public function get_status() {
155
		return get_option( self::STATUS_OPTION, $this->initial_status );
156
	}
157
158
	public function update_status( $status ) {
159
		return update_option(
160
			self::STATUS_OPTION,
161
			array_merge( $this->get_status(), $status )
162
		);
163
	}
164
165
	public function clear_status() {
166
		delete_option( self::STATUS_OPTION );
167
	}
168
}
169