Completed
Push — try/statically-access-asset-to... ( e50fad...74c9e7 )
by
unknown
126:59 queued 118:11
created

Listener   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 319
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 56
lcom 2
cbo 6
dl 0
loc 319
rs 5.5199
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A get_instance() 0 7 2
A __construct() 0 5 1
A init() 0 19 2
A get_sync_queue() 0 3 1
A get_full_sync_queue() 0 3 1
A set_queue_size_limit() 0 3 1
A get_queue_size_limit() 0 3 1
A set_queue_lag_limit() 0 3 1
A get_queue_lag_limit() 0 3 1
A force_recheck_queue_limit() 0 4 1
A can_add_to_queue() 0 20 4
A full_sync_action_handler() 0 4 1
A action_handler() 0 4 1
B bulk_enqueue_full_sync_actions() 0 54 8
C enqueue_action() 0 82 9
F get_actor() 0 34 15
A should_send_user_data_with_actor() 0 14 1
A set_defaults() 0 6 1
A get_request_url() 0 6 4

How to fix   Complexity   

Complex Class

Complex classes like Listener often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Listener, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Automattic\Jetpack\Sync;
4
5
/**
6
 * This class monitors actions and logs them to the queue to be sent
7
 */
8
class Listener {
9
	const QUEUE_STATE_CHECK_TRANSIENT = 'jetpack_sync_last_checked_queue_state';
10
	const QUEUE_STATE_CHECK_TIMEOUT   = 300; // 5 minutes
11
12
	private $sync_queue;
13
	private $full_sync_queue;
14
	private $sync_queue_size_limit;
15
	private $sync_queue_lag_limit;
16
17
	// singleton functions
18
	private static $instance;
19
20
	public static function get_instance() {
21
		if ( null === self::$instance ) {
22
			self::$instance = new self();
23
		}
24
25
		return self::$instance;
26
	}
27
28
	// this is necessary because you can't use "new" when you declare instance properties >:(
29
	protected function __construct() {
30
		Main::init();
31
		$this->set_defaults();
32
		$this->init();
33
	}
34
35
	private function init() {
36
		$handler           = array( $this, 'action_handler' );
37
		$full_sync_handler = array( $this, 'full_sync_action_handler' );
38
39
		foreach ( Modules::get_modules() as $module ) {
40
			$module->init_listeners( $handler );
41
			$module->init_full_sync_listeners( $full_sync_handler );
42
		}
43
44
		// Module Activation
45
		add_action( 'jetpack_activate_module', $handler );
46
		add_action( 'jetpack_deactivate_module', $handler );
47
48
		// Jetpack Upgrade
49
		add_action( 'updating_jetpack_version', $handler, 10, 2 );
50
51
		// Send periodic checksum
52
		add_action( 'jetpack_sync_checksum', $handler );
53
	}
54
55
	function get_sync_queue() {
56
		return $this->sync_queue;
57
	}
58
59
	function get_full_sync_queue() {
60
		return $this->full_sync_queue;
61
	}
62
63
	function set_queue_size_limit( $limit ) {
64
		$this->sync_queue_size_limit = $limit;
65
	}
66
67
	function get_queue_size_limit() {
68
		return $this->sync_queue_size_limit;
69
	}
70
71
	function set_queue_lag_limit( $age ) {
72
		$this->sync_queue_lag_limit = $age;
73
	}
74
75
	function get_queue_lag_limit() {
76
		return $this->sync_queue_lag_limit;
77
	}
78
79
	function force_recheck_queue_limit() {
80
		delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->sync_queue->id );
81
		delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->full_sync_queue->id );
82
	}
83
84
	// prevent adding items to the queue if it hasn't sent an item for 15 mins
85
	// AND the queue is over 1000 items long (by default)
86
	function can_add_to_queue( $queue ) {
87
		if ( ! \Jetpack_Sync_Settings::is_sync_enabled() ) {
88
			return false;
89
		}
90
91
		$state_transient_name = self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $queue->id;
92
93
		$queue_state = get_transient( $state_transient_name );
94
95
		if ( false === $queue_state ) {
96
			$queue_state = array( $queue->size(), $queue->lag() );
97
			set_transient( $state_transient_name, $queue_state, self::QUEUE_STATE_CHECK_TIMEOUT );
98
		}
99
100
		list( $queue_size, $queue_age ) = $queue_state;
101
102
		return ( $queue_age < $this->sync_queue_lag_limit )
103
			   ||
104
			   ( ( $queue_size + 1 ) < $this->sync_queue_size_limit );
105
	}
106
107
	function full_sync_action_handler() {
108
		$args = func_get_args();
109
		$this->enqueue_action( current_filter(), $args, $this->full_sync_queue );
110
	}
111
112
	function action_handler() {
113
		$args = func_get_args();
114
		$this->enqueue_action( current_filter(), $args, $this->sync_queue );
115
	}
116
117
	// add many actions to the queue directly, without invoking them
118
119
	/**
120
	 * Bulk add action to the queue.
121
	 *
122
	 * @param $action_name String the name the full sync action.
123
	 * @param $args_array Array of chunked arguments
124
	 */
125
	function bulk_enqueue_full_sync_actions( $action_name, $args_array ) {
126
		$queue = $this->get_full_sync_queue();
127
128
		// periodically check the size of the queue, and disable adding to it if
129
		// it exceeds some limit AND the oldest item exceeds the age limit (i.e. sending has stopped)
130
		if ( ! $this->can_add_to_queue( $queue ) ) {
131
			return;
132
		}
133
134
		// if we add any items to the queue, we should try to ensure that our script
135
		// can't be killed before they are sent
136
		if ( function_exists( 'ignore_user_abort' ) ) {
137
			ignore_user_abort( true );
138
		}
139
140
		$data_to_enqueue = array();
141
		$user_id         = get_current_user_id();
142
		$currtime        = microtime( true );
143
		$is_importing    = \Jetpack_Sync_Settings::is_importing();
144
145
		foreach ( $args_array as $args ) {
146
			$previous_end = isset( $args['previous_end'] ) ? $args['previous_end'] : null;
147
			$args         = isset( $args['ids'] ) ? $args['ids'] : $args;
148
149
			/**
150
			 * Modify or reject the data within an action before it is enqueued locally.
151
			 *
152
			 * @since 4.2.0
153
			 *
154
			 * @module sync
155
			 *
156
			 * @param array The action parameters
157
			 */
158
			$args        = apply_filters( "jetpack_sync_before_enqueue_$action_name", $args );
159
			$action_data = array( $args );
160
			if ( ! is_null( $previous_end ) ) {
161
				$action_data[] = $previous_end;
162
			}
163
			// allow listeners to abort
164
			if ( $args === false ) {
165
				continue;
166
			}
167
168
			$data_to_enqueue[] = array(
169
				$action_name,
170
				$action_data,
171
				$user_id,
172
				$currtime,
173
				$is_importing,
174
			);
175
		}
176
177
		$queue->add_all( $data_to_enqueue );
178
	}
179
180
	function enqueue_action( $current_filter, $args, $queue ) {
181
		// don't enqueue an action during the outbound http request - this prevents recursion
182
		if ( \Jetpack_Sync_Settings::is_sending() ) {
183
			return;
184
		}
185
186
		/**
187
		 * Add an action hook to execute when anything on the whitelist gets sent to the queue to sync.
188
		 *
189
		 * @module sync
190
		 *
191
		 * @since 5.9.0
192
		 */
193
		do_action( 'jetpack_sync_action_before_enqueue' );
194
195
		/**
196
		 * Modify or reject the data within an action before it is enqueued locally.
197
		 *
198
		 * @since 4.2.0
199
		 *
200
		 * @param array The action parameters
201
		 */
202
		$args = apply_filters( "jetpack_sync_before_enqueue_$current_filter", $args );
203
204
		// allow listeners to abort
205
		if ( $args === false ) {
206
			return;
207
		}
208
209
		// periodically check the size of the queue, and disable adding to it if
210
		// it exceeds some limit AND the oldest item exceeds the age limit (i.e. sending has stopped)
211
		if ( ! $this->can_add_to_queue( $queue ) ) {
212
			return;
213
		}
214
215
		// if we add any items to the queue, we should try to ensure that our script
216
		// can't be killed before they are sent
217
		if ( function_exists( 'ignore_user_abort' ) ) {
218
			ignore_user_abort( true );
219
		}
220
221
		if (
222
			'sync' === $queue->id ||
223
			in_array(
224
				$current_filter,
225
				array(
226
					'jetpack_full_sync_start',
227
					'jetpack_full_sync_end',
228
					'jetpack_full_sync_cancel',
229
				)
230
			)
231
		) {
232
			$queue->add(
233
				array(
234
					$current_filter,
235
					$args,
236
					get_current_user_id(),
237
					microtime( true ),
238
					\Jetpack_Sync_Settings::is_importing(),
239
					$this->get_actor( $current_filter, $args ),
240
				)
241
			);
242
		} else {
243
			$queue->add(
244
				array(
245
					$current_filter,
246
					$args,
247
					get_current_user_id(),
248
					microtime( true ),
249
					\Jetpack_Sync_Settings::is_importing(),
250
				)
251
			);
252
		}
253
254
		// since we've added some items, let's try to load the sender so we can send them as quickly as possible
255
		if ( ! Actions::$sender ) {
0 ignored issues
show
Bug introduced by
The property sender cannot be accessed from this context as it is declared private in class Automattic\Jetpack\Sync\Actions.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
256
			add_filter( 'jetpack_sync_sender_should_load', '__return_true' );
257
			if ( did_action( 'init' ) ) {
258
				Actions::add_sender_shutdown();
259
			}
260
		}
261
	}
262
263
	function get_actor( $current_filter, $args ) {
264
		if ( 'wp_login' === $current_filter ) {
265
			$user = get_user_by( 'ID', $args[1]->data->ID );
266
		} else {
267
			$user = wp_get_current_user();
268
		}
269
270
		$translated_role = \Jetpack::translate_user_to_role( $user );
271
272
		$actor = array(
273
			'wpcom_user_id'    => null,
274
			'external_user_id' => isset( $user->ID ) ? $user->ID : null,
275
			'display_name'     => isset( $user->display_name ) ? $user->display_name : null,
276
			'user_email'       => isset( $user->user_email ) ? $user->user_email : null,
277
			'user_roles'       => isset( $user->roles ) ? $user->roles : null,
278
			'translated_role'  => $translated_role ? $translated_role : null,
279
			'is_cron'          => defined( 'DOING_CRON' ) ? DOING_CRON : false,
280
			'is_rest'          => defined( 'REST_API_REQUEST' ) ? REST_API_REQUEST : false,
281
			'is_xmlrpc'        => defined( 'XMLRPC_REQUEST' ) ? XMLRPC_REQUEST : false,
282
			'is_wp_rest'       => defined( 'REST_REQUEST' ) ? REST_REQUEST : false,
283
			'is_ajax'          => defined( 'DOING_AJAX' ) ? DOING_AJAX : false,
284
			'is_wp_admin'      => is_admin(),
285
			'is_cli'           => defined( 'WP_CLI' ) ? WP_CLI : false,
286
			'from_url'         => $this->get_request_url(),
287
		);
288
289
		if ( $this->should_send_user_data_with_actor( $current_filter ) ) {
290
			require_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
291
			$actor['ip']         = jetpack_protect_get_ip();
292
			$actor['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown';
293
		}
294
295
		return $actor;
296
	}
297
298
	function should_send_user_data_with_actor( $current_filter ) {
299
		$should_send = in_array( $current_filter, array( 'jetpack_wp_login', 'wp_logout', 'jetpack_valid_failed_login_attempt' ) );
300
		/**
301
		 * Allow or deny sending actor's user data ( IP and UA ) during a sync event
302
		 *
303
		 * @since 5.8.0
304
		 *
305
		 * @module sync
306
		 *
307
		 * @param bool True if we should send user data
308
		 * @param string The current filter that is performing the sync action
309
		 */
310
		return apply_filters( 'jetpack_sync_actor_user_data', $should_send, $current_filter );
311
	}
312
313
	function set_defaults() {
314
		$this->sync_queue      = new Queue( 'sync' );
315
		$this->full_sync_queue = new Queue( 'full_sync' );
316
		$this->set_queue_size_limit( \Jetpack_Sync_Settings::get_setting( 'max_queue_size' ) );
317
		$this->set_queue_lag_limit( \Jetpack_Sync_Settings::get_setting( 'max_queue_lag' ) );
318
	}
319
320
	function get_request_url() {
321
		if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
322
			return 'http' . ( isset( $_SERVER['HTTPS'] ) ? 's' : '' ) . '://' . "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
323
		}
324
		return is_admin() ? get_admin_url( get_current_blog_id() ) : home_url();
325
	}
326
}
327