Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Sync sender. |
||
| 4 | * |
||
| 5 | * @package automattic/jetpack-sync |
||
| 6 | */ |
||
| 7 | |||
| 8 | namespace Automattic\Jetpack\Sync; |
||
| 9 | |||
| 10 | use Automattic\Jetpack\Connection\Manager; |
||
| 11 | use Automattic\Jetpack\Constants; |
||
| 12 | |||
| 13 | /** |
||
| 14 | * This class grabs pending actions from the queue and sends them |
||
| 15 | */ |
||
| 16 | class Sender { |
||
| 17 | /** |
||
| 18 | * Name of the option that stores the time of the next sync. |
||
| 19 | * |
||
| 20 | * @access public |
||
| 21 | * |
||
| 22 | * @var string |
||
| 23 | */ |
||
| 24 | const NEXT_SYNC_TIME_OPTION_NAME = 'jetpack_next_sync_time'; |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Sync timeout after a WPCOM error. |
||
| 28 | * |
||
| 29 | * @access public |
||
| 30 | * |
||
| 31 | * @var int |
||
| 32 | */ |
||
| 33 | const WPCOM_ERROR_SYNC_DELAY = 60; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Sync timeout after a queue has been locked. |
||
| 37 | * |
||
| 38 | * @access public |
||
| 39 | * |
||
| 40 | * @var int |
||
| 41 | */ |
||
| 42 | const QUEUE_LOCKED_SYNC_DELAY = 10; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Maximum bytes to checkout without exceeding the memory limit. |
||
| 46 | * |
||
| 47 | * @access private |
||
| 48 | * |
||
| 49 | * @var int |
||
| 50 | */ |
||
| 51 | private $dequeue_max_bytes; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Maximum bytes in a single encoded item. |
||
| 55 | * |
||
| 56 | * @access private |
||
| 57 | * |
||
| 58 | * @var int |
||
| 59 | */ |
||
| 60 | private $upload_max_bytes; |
||
| 61 | |||
| 62 | /** |
||
| 63 | * Maximum number of sync items in a single action. |
||
| 64 | * |
||
| 65 | * @access private |
||
| 66 | * |
||
| 67 | * @var int |
||
| 68 | */ |
||
| 69 | private $upload_max_rows; |
||
| 70 | |||
| 71 | /** |
||
| 72 | * Maximum time for perfirming a checkout of items from the queue (in seconds). |
||
| 73 | * |
||
| 74 | * @access private |
||
| 75 | * |
||
| 76 | * @var int |
||
| 77 | */ |
||
| 78 | private $max_dequeue_time; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * How many seconds to wait after sending sync items after exceeding the sync wait threshold (in seconds). |
||
| 82 | * |
||
| 83 | * @access private |
||
| 84 | * |
||
| 85 | * @var int |
||
| 86 | */ |
||
| 87 | private $sync_wait_time; |
||
| 88 | |||
| 89 | /** |
||
| 90 | * How much maximum time to wait for the checkout to finish (in seconds). |
||
| 91 | * |
||
| 92 | * @access private |
||
| 93 | * |
||
| 94 | * @var int |
||
| 95 | */ |
||
| 96 | private $sync_wait_threshold; |
||
| 97 | |||
| 98 | /** |
||
| 99 | * How much maximum time to wait for the sync items to be queued for sending (in seconds). |
||
| 100 | * |
||
| 101 | * @access private |
||
| 102 | * |
||
| 103 | * @var int |
||
| 104 | */ |
||
| 105 | private $enqueue_wait_time; |
||
| 106 | |||
| 107 | /** |
||
| 108 | * Incremental sync queue object. |
||
| 109 | * |
||
| 110 | * @access private |
||
| 111 | * |
||
| 112 | * @var Automattic\Jetpack\Sync\Queue |
||
| 113 | */ |
||
| 114 | private $sync_queue; |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Full sync queue object. |
||
| 118 | * |
||
| 119 | * @access private |
||
| 120 | * |
||
| 121 | * @var Automattic\Jetpack\Sync\Queue |
||
| 122 | */ |
||
| 123 | private $full_sync_queue; |
||
| 124 | |||
| 125 | /** |
||
| 126 | * Codec object for encoding and decoding sync items. |
||
| 127 | * |
||
| 128 | * @access private |
||
| 129 | * |
||
| 130 | * @var Automattic\Jetpack\Sync\Codec_Interface |
||
| 131 | */ |
||
| 132 | private $codec; |
||
| 133 | |||
| 134 | /** |
||
| 135 | * The current user before we change or clear it. |
||
| 136 | * |
||
| 137 | * @access private |
||
| 138 | * |
||
| 139 | * @var \WP_User |
||
| 140 | */ |
||
| 141 | private $old_user; |
||
| 142 | |||
| 143 | /** |
||
| 144 | * Container for the singleton instance of this class. |
||
| 145 | * |
||
| 146 | * @access private |
||
| 147 | * @static |
||
| 148 | * |
||
| 149 | * @var Automattic\Jetpack\Sync\Sender |
||
| 150 | */ |
||
| 151 | private static $instance; |
||
| 152 | |||
| 153 | /** |
||
| 154 | * Retrieve the singleton instance of this class. |
||
| 155 | * |
||
| 156 | * @access public |
||
| 157 | * @static |
||
| 158 | * |
||
| 159 | * @return Sender |
||
| 160 | */ |
||
| 161 | public static function get_instance() { |
||
| 162 | if ( null === self::$instance ) { |
||
| 163 | self::$instance = new self(); |
||
| 164 | } |
||
| 165 | |||
| 166 | return self::$instance; |
||
| 167 | } |
||
| 168 | |||
| 169 | /** |
||
| 170 | * Constructor. |
||
| 171 | * This is necessary because you can't use "new" when you declare instance properties >:( |
||
| 172 | * |
||
| 173 | * @access protected |
||
| 174 | * @static |
||
| 175 | */ |
||
| 176 | protected function __construct() { |
||
| 177 | $this->set_defaults(); |
||
| 178 | $this->init(); |
||
| 179 | } |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Initialize the sender. |
||
| 183 | * Prepares the current user and initializes all sync modules. |
||
| 184 | * |
||
| 185 | * @access private |
||
| 186 | */ |
||
| 187 | private function init() { |
||
| 188 | add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_set_user_from_token' ), 1 ); |
||
| 189 | add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_clear_user_from_token' ), 20 ); |
||
| 190 | add_filter( 'jetpack_xmlrpc_methods', array( $this, 'register_jetpack_xmlrpc_methods' ) ); |
||
| 191 | foreach ( Modules::get_modules() as $module ) { |
||
| 192 | $module->init_before_send(); |
||
| 193 | } |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * Detect if this is a XMLRPC request with a valid signature. |
||
| 198 | * If so, changes the user to the new one. |
||
| 199 | * |
||
| 200 | * @access public |
||
| 201 | */ |
||
| 202 | public function maybe_set_user_from_token() { |
||
| 203 | $connection = new Manager(); |
||
| 204 | $verified_user = $connection->verify_xml_rpc_signature(); |
||
| 205 | if ( Constants::is_true( 'XMLRPC_REQUEST' ) && |
||
| 206 | ! is_wp_error( $verified_user ) |
||
| 207 | && $verified_user |
||
| 208 | ) { |
||
| 209 | $old_user = wp_get_current_user(); |
||
| 210 | $this->old_user = isset( $old_user->ID ) ? $old_user->ID : 0; |
||
| 211 | wp_set_current_user( $verified_user['user_id'] ); |
||
| 212 | } |
||
| 213 | } |
||
| 214 | |||
| 215 | /** |
||
| 216 | * If we used to have a previous current user, revert back to it. |
||
| 217 | * |
||
| 218 | * @access public |
||
| 219 | */ |
||
| 220 | public function maybe_clear_user_from_token() { |
||
| 221 | if ( isset( $this->old_user ) ) { |
||
| 222 | wp_set_current_user( $this->old_user ); |
||
| 223 | } |
||
| 224 | } |
||
| 225 | |||
| 226 | /** |
||
| 227 | * Retrieve the next sync time. |
||
| 228 | * |
||
| 229 | * @access public |
||
| 230 | * |
||
| 231 | * @param string $queue_name Name of the queue. |
||
| 232 | * @return float Timestamp of the next sync. |
||
| 233 | */ |
||
| 234 | public function get_next_sync_time( $queue_name ) { |
||
| 235 | return (float) get_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, 0 ); |
||
| 236 | } |
||
| 237 | |||
| 238 | /** |
||
| 239 | * Set the next sync time. |
||
| 240 | * |
||
| 241 | * @access public |
||
| 242 | * |
||
| 243 | * @param int $time Timestamp of the next sync. |
||
| 244 | * @param string $queue_name Name of the queue. |
||
| 245 | * @return boolean True if update was successful, false otherwise. |
||
| 246 | */ |
||
| 247 | public function set_next_sync_time( $time, $queue_name ) { |
||
| 248 | return update_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, $time, true ); |
||
| 249 | } |
||
| 250 | |||
| 251 | /** |
||
| 252 | * Trigger a full sync. |
||
| 253 | * |
||
| 254 | * @access public |
||
| 255 | * |
||
| 256 | * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise. |
||
| 257 | */ |
||
| 258 | public function do_full_sync() { |
||
| 259 | $sync_module = Modules::get_module( 'full-sync' ); |
||
| 260 | if ( ! $sync_module ) { |
||
| 261 | return; |
||
| 262 | } |
||
| 263 | if ( ! Settings::get_setting( 'full_sync_sender_enabled' ) ) { |
||
| 264 | return; |
||
| 265 | } |
||
| 266 | $this->continue_full_sync_enqueue(); |
||
| 267 | // immediate full sync sends data in continue_full_sync_enqueue. |
||
| 268 | if ( false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) { |
||
| 269 | return $this->do_sync_and_set_delays( $this->full_sync_queue ); |
||
| 270 | } else { |
||
| 271 | $status = $sync_module->get_status(); |
||
| 272 | // Sync not started or Sync finished. |
||
| 273 | if ( false === $status['started'] || ( ! empty( $status['started'] ) && ! empty( $status['finished'] ) ) ) { |
||
| 274 | return false; |
||
| 275 | } else { |
||
| 276 | return true; |
||
| 277 | } |
||
| 278 | } |
||
| 279 | } |
||
| 280 | |||
| 281 | /** |
||
| 282 | * Enqueue the next sync items for sending. |
||
| 283 | * Will not be done if the current request is a WP import one. |
||
| 284 | * Will be delayed until the next sync time comes. |
||
| 285 | * |
||
| 286 | * @access private |
||
| 287 | */ |
||
| 288 | private function continue_full_sync_enqueue() { |
||
| 289 | if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) { |
||
| 290 | return false; |
||
| 291 | } |
||
| 292 | |||
| 293 | if ( $this->get_next_sync_time( 'full-sync-enqueue' ) > microtime( true ) ) { |
||
| 294 | return false; |
||
| 295 | } |
||
| 296 | |||
| 297 | Modules::get_module( 'full-sync' )->continue_enqueuing(); |
||
| 298 | |||
| 299 | $this->set_next_sync_time( time() + $this->get_enqueue_wait_time(), 'full-sync-enqueue' ); |
||
| 300 | } |
||
| 301 | |||
| 302 | /** |
||
| 303 | * Trigger incremental sync. |
||
| 304 | * |
||
| 305 | * @access public |
||
| 306 | * |
||
| 307 | * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise. |
||
| 308 | */ |
||
| 309 | public function do_sync() { |
||
| 310 | return $this->do_sync_and_set_delays( $this->sync_queue ); |
||
| 311 | } |
||
| 312 | |||
| 313 | /** |
||
| 314 | * Trigger sync for a certain sync queue. |
||
| 315 | * Responsible for setting next sync time. |
||
| 316 | * Will not be delayed if the current request is a WP import one. |
||
| 317 | * Will be delayed until the next sync time comes. |
||
| 318 | * |
||
| 319 | * @access public |
||
| 320 | * |
||
| 321 | * @param Automattic\Jetpack\Sync\Queue $queue Queue object. |
||
| 322 | * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise. |
||
| 323 | */ |
||
| 324 | public function do_sync_and_set_delays( $queue ) { |
||
| 325 | // Don't sync if importing. |
||
| 326 | if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) { |
||
| 327 | return new \WP_Error( 'is_importing' ); |
||
| 328 | } |
||
| 329 | |||
| 330 | // Don't sync if request is marked as read only. |
||
| 331 | if ( Constants::is_true( 'JETPACK_SYNC_READ_ONLY' ) ) { |
||
| 332 | return new \WP_Error( 'jetpack_sync_read_only' ); |
||
|
0 ignored issues
–
show
|
|||
| 333 | } |
||
| 334 | |||
| 335 | if ( ! Settings::is_sender_enabled( $queue->id ) ) { |
||
| 336 | return new \WP_Error( 'sender_disabled_for_queue_' . $queue->id ); |
||
| 337 | } |
||
| 338 | |||
| 339 | // Don't sync if we are throttled. |
||
| 340 | if ( $this->get_next_sync_time( $queue->id ) > microtime( true ) ) { |
||
| 341 | return new \WP_Error( 'sync_throttled' ); |
||
| 342 | } |
||
| 343 | |||
| 344 | $start_time = microtime( true ); |
||
| 345 | |||
| 346 | Settings::set_is_syncing( true ); |
||
| 347 | |||
| 348 | $sync_result = $this->do_sync_for_queue( $queue ); |
||
| 349 | |||
| 350 | Settings::set_is_syncing( false ); |
||
| 351 | |||
| 352 | $exceeded_sync_wait_threshold = ( microtime( true ) - $start_time ) > (float) $this->get_sync_wait_threshold(); |
||
| 353 | |||
| 354 | if ( is_wp_error( $sync_result ) ) { |
||
| 355 | if ( 'unclosed_buffer' === $sync_result->get_error_code() ) { |
||
| 356 | $this->set_next_sync_time( time() + self::QUEUE_LOCKED_SYNC_DELAY, $queue->id ); |
||
| 357 | } |
||
| 358 | if ( 'wpcom_error' === $sync_result->get_error_code() ) { |
||
| 359 | $this->set_next_sync_time( time() + self::WPCOM_ERROR_SYNC_DELAY, $queue->id ); |
||
| 360 | } |
||
| 361 | } elseif ( $exceeded_sync_wait_threshold ) { |
||
| 362 | // If we actually sent data and it took a while, wait before sending again. |
||
| 363 | $this->set_next_sync_time( time() + $this->get_sync_wait_time(), $queue->id ); |
||
| 364 | } |
||
| 365 | |||
| 366 | return $sync_result; |
||
| 367 | } |
||
| 368 | |||
| 369 | /** |
||
| 370 | * Retrieve the next sync items to send. |
||
| 371 | * |
||
| 372 | * @access public |
||
| 373 | * |
||
| 374 | * @param (array|Automattic\Jetpack\Sync\Queue_Buffer) $buffer_or_items Queue buffer or array of objects. |
||
| 375 | * @param boolean $encode Whether to encode the items. |
||
| 376 | * @return array Sync items to send. |
||
| 377 | */ |
||
| 378 | public function get_items_to_send( $buffer_or_items, $encode = true ) { |
||
| 379 | // Track how long we've been processing so we can avoid request timeouts. |
||
| 380 | $start_time = microtime( true ); |
||
| 381 | $upload_size = 0; |
||
| 382 | $items_to_send = array(); |
||
| 383 | $items = is_array( $buffer_or_items ) ? $buffer_or_items : $buffer_or_items->get_items(); |
||
| 384 | // Set up current screen to avoid errors rendering content. |
||
| 385 | require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php'; |
||
| 386 | require_once ABSPATH . 'wp-admin/includes/screen.php'; |
||
| 387 | set_current_screen( 'sync' ); |
||
| 388 | $skipped_items_ids = array(); |
||
| 389 | /** |
||
| 390 | * We estimate the total encoded size as we go by encoding each item individually. |
||
| 391 | * This is expensive, but the only way to really know :/ |
||
| 392 | */ |
||
| 393 | foreach ( $items as $key => $item ) { |
||
| 394 | // Suspending cache addition help prevent overloading in memory cache of large sites. |
||
| 395 | wp_suspend_cache_addition( true ); |
||
| 396 | /** |
||
| 397 | * Modify the data within an action before it is serialized and sent to the server |
||
| 398 | * For example, during full sync this expands Post ID's into full Post objects, |
||
| 399 | * so that we don't have to serialize the whole object into the queue. |
||
| 400 | * |
||
| 401 | * @since 4.2.0 |
||
| 402 | * |
||
| 403 | * @param array The action parameters |
||
| 404 | * @param int The ID of the user who triggered the action |
||
| 405 | */ |
||
| 406 | $item[1] = apply_filters( 'jetpack_sync_before_send_' . $item[0], $item[1], $item[2] ); |
||
| 407 | wp_suspend_cache_addition( false ); |
||
| 408 | if ( false === $item[1] ) { |
||
| 409 | $skipped_items_ids[] = $key; |
||
| 410 | continue; |
||
| 411 | } |
||
| 412 | $encoded_item = $encode ? $this->codec->encode( $item ) : $item; |
||
| 413 | $upload_size += strlen( $encoded_item ); |
||
| 414 | if ( $upload_size > $this->upload_max_bytes && count( $items_to_send ) > 0 ) { |
||
| 415 | break; |
||
| 416 | } |
||
| 417 | $items_to_send[ $key ] = $encoded_item; |
||
| 418 | if ( microtime( true ) - $start_time > $this->max_dequeue_time ) { |
||
| 419 | break; |
||
| 420 | } |
||
| 421 | } |
||
| 422 | |||
| 423 | return array( $items_to_send, $skipped_items_ids, $items, microtime( true ) - $start_time ); |
||
| 424 | } |
||
| 425 | |||
| 426 | /** |
||
| 427 | * If supported, flush all response data to the client and finish the request. |
||
| 428 | * This allows for time consuming tasks to be performed without leaving the connection open. |
||
| 429 | * |
||
| 430 | * @access private |
||
| 431 | */ |
||
| 432 | private function fastcgi_finish_request() { |
||
| 433 | if ( function_exists( 'fastcgi_finish_request' ) && version_compare( phpversion(), '7.0.16', '>=' ) ) { |
||
| 434 | fastcgi_finish_request(); |
||
| 435 | } |
||
| 436 | } |
||
| 437 | |||
| 438 | /** |
||
| 439 | * Perform sync for a certain sync queue. |
||
| 440 | * |
||
| 441 | * @access public |
||
| 442 | * |
||
| 443 | * @param Automattic\Jetpack\Sync\Queue $queue Queue object. |
||
| 444 | * @return boolean|\WP_Error True if this sync sending was successful, error object otherwise. |
||
| 445 | */ |
||
| 446 | public function do_sync_for_queue( $queue ) { |
||
| 447 | do_action( 'jetpack_sync_before_send_queue_' . $queue->id ); |
||
| 448 | if ( $queue->size() === 0 ) { |
||
| 449 | return new \WP_Error( 'empty_queue_' . $queue->id ); |
||
| 450 | } |
||
| 451 | |||
| 452 | /** |
||
| 453 | * Now that we're sure we are about to sync, try to ignore user abort |
||
| 454 | * so we can avoid getting into a bad state. |
||
| 455 | */ |
||
| 456 | if ( function_exists( 'ignore_user_abort' ) ) { |
||
| 457 | ignore_user_abort( true ); |
||
| 458 | } |
||
| 459 | |||
| 460 | /* Don't make the request block till we finish, if possible. */ |
||
| 461 | if ( Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'XMLRPC_REQUEST' ) ) { |
||
| 462 | $this->fastcgi_finish_request(); |
||
| 463 | } |
||
| 464 | |||
| 465 | $checkout_start_time = microtime( true ); |
||
| 466 | |||
| 467 | $buffer = $queue->checkout_with_memory_limit( $this->dequeue_max_bytes, $this->upload_max_rows ); |
||
| 468 | |||
| 469 | if ( ! $buffer ) { |
||
| 470 | // Buffer has no items. |
||
| 471 | return new \WP_Error( 'empty_buffer' ); |
||
| 472 | } |
||
| 473 | |||
| 474 | if ( is_wp_error( $buffer ) ) { |
||
| 475 | return $buffer; |
||
| 476 | } |
||
| 477 | |||
| 478 | $checkout_duration = microtime( true ) - $checkout_start_time; |
||
| 479 | |||
| 480 | list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $buffer, true ); |
||
| 481 | if ( ! empty( $items_to_send ) ) { |
||
| 482 | /** |
||
| 483 | * Fires when data is ready to send to the server. |
||
| 484 | * Return false or WP_Error to abort the sync (e.g. if there's an error) |
||
| 485 | * The items will be automatically re-sent later |
||
| 486 | * |
||
| 487 | * @since 4.2.0 |
||
| 488 | * |
||
| 489 | * @param array $data The action buffer |
||
| 490 | * @param string $codec The codec name used to encode the data |
||
| 491 | * @param double $time The current time |
||
| 492 | * @param string $queue The queue used to send ('sync' or 'full_sync') |
||
| 493 | * @param float $checkout_duration The duration of the checkout operation. |
||
| 494 | * @param float $preprocess_duration The duration of the pre-process operation. |
||
| 495 | * @param int $queue_size The size of the sync queue at the time of processing. |
||
| 496 | */ |
||
| 497 | Settings::set_is_sending( true ); |
||
| 498 | $processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->codec->name(), microtime( true ), $queue->id, $checkout_duration, $preprocess_duration, $queue->size(), $buffer->id ); |
||
| 499 | Settings::set_is_sending( false ); |
||
| 500 | } else { |
||
| 501 | $processed_item_ids = $skipped_items_ids; |
||
| 502 | $skipped_items_ids = array(); |
||
| 503 | } |
||
| 504 | |||
| 505 | if ( 'non-blocking' !== $processed_item_ids ) { |
||
| 506 | if ( ! $processed_item_ids || is_wp_error( $processed_item_ids ) ) { |
||
| 507 | $checked_in_item_ids = $queue->checkin( $buffer ); |
||
| 508 | if ( is_wp_error( $checked_in_item_ids ) ) { |
||
| 509 | // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log |
||
| 510 | error_log( 'Error checking in buffer: ' . $checked_in_item_ids->get_error_message() ); |
||
| 511 | $queue->force_checkin(); |
||
| 512 | } |
||
| 513 | if ( is_wp_error( $processed_item_ids ) ) { |
||
| 514 | return new \WP_Error( 'wpcom_error', $processed_item_ids->get_error_code() ); |
||
| 515 | } |
||
| 516 | |||
| 517 | // Returning a wpcom_error is a sign to the caller that we should wait a while before syncing again. |
||
| 518 | return new \WP_Error( 'wpcom_error', 'jetpack_sync_send_data_false' ); |
||
| 519 | } else { |
||
| 520 | // Detect if the last item ID was an error. |
||
| 521 | $had_wp_error = is_wp_error( end( $processed_item_ids ) ); |
||
| 522 | if ( $had_wp_error ) { |
||
| 523 | $wp_error = array_pop( $processed_item_ids ); |
||
| 524 | } |
||
| 525 | // Also checkin any items that were skipped. |
||
| 526 | if ( count( $skipped_items_ids ) > 0 ) { |
||
| 527 | $processed_item_ids = array_merge( $processed_item_ids, $skipped_items_ids ); |
||
| 528 | } |
||
| 529 | $processed_items = array_intersect_key( $items, array_flip( $processed_item_ids ) ); |
||
| 530 | /** |
||
| 531 | * Allows us to keep track of all the actions that have been sent. |
||
| 532 | * Allows us to calculate the progress of specific actions. |
||
| 533 | * |
||
| 534 | * @since 4.2.0 |
||
| 535 | * |
||
| 536 | * @param array $processed_actions The actions that we send successfully. |
||
| 537 | */ |
||
| 538 | do_action( 'jetpack_sync_processed_actions', $processed_items ); |
||
| 539 | $queue->close( $buffer, $processed_item_ids ); |
||
| 540 | // Returning a WP_Error is a sign to the caller that we should wait a while before syncing again. |
||
| 541 | if ( $had_wp_error ) { |
||
| 542 | return new \WP_Error( 'wpcom_error', $wp_error->get_error_code() ); |
||
| 543 | } |
||
| 544 | } |
||
| 545 | } |
||
| 546 | |||
| 547 | return true; |
||
| 548 | } |
||
| 549 | |||
| 550 | /** |
||
| 551 | * Immediately sends a single item without firing or enqueuing it |
||
| 552 | * |
||
| 553 | * @param string $action_name The action. |
||
| 554 | * @param array $data The data associated with the action. |
||
| 555 | * |
||
| 556 | * @return Items processed. TODO: this doesn't make much sense anymore, it should probably be just a bool. |
||
| 557 | */ |
||
| 558 | public function send_action( $action_name, $data = null ) { |
||
| 559 | if ( ! Settings::is_sender_enabled( 'full_sync' ) ) { |
||
| 560 | return array(); |
||
| 561 | } |
||
| 562 | |||
| 563 | // Compose the data to be sent. |
||
| 564 | $action_to_send = $this->create_action_to_send( $action_name, $data ); |
||
| 565 | |||
| 566 | list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $action_to_send, true ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable |
||
| 567 | Settings::set_is_sending( true ); |
||
| 568 | $processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->get_codec()->name(), microtime( true ), 'immediate-send', 0, $preprocess_duration ); |
||
| 569 | Settings::set_is_sending( false ); |
||
| 570 | |||
| 571 | /** |
||
| 572 | * Allows us to keep track of all the actions that have been sent. |
||
| 573 | * Allows us to calculate the progress of specific actions. |
||
| 574 | * |
||
| 575 | * @param array $processed_actions The actions that we send successfully. |
||
| 576 | * |
||
| 577 | * @since 4.2.0 |
||
| 578 | */ |
||
| 579 | do_action( 'jetpack_sync_processed_actions', $action_to_send ); |
||
| 580 | |||
| 581 | return $processed_item_ids; |
||
| 582 | } |
||
| 583 | |||
| 584 | /** |
||
| 585 | * Create an synthetic action for direct sending to WPCOM during full sync (for example) |
||
| 586 | * |
||
| 587 | * @access private |
||
| 588 | * |
||
| 589 | * @param string $action_name The action. |
||
| 590 | * @param array $data The data associated with the action. |
||
| 591 | * @return array An array of synthetic sync actions keyed by current microtime(true) |
||
| 592 | */ |
||
| 593 | private function create_action_to_send( $action_name, $data ) { |
||
| 594 | return array( |
||
| 595 | (string) microtime( true ) => array( |
||
| 596 | $action_name, |
||
| 597 | $data, |
||
| 598 | get_current_user_id(), |
||
| 599 | microtime( true ), |
||
| 600 | Settings::is_importing(), |
||
| 601 | ), |
||
| 602 | ); |
||
| 603 | } |
||
| 604 | |||
| 605 | /** |
||
| 606 | * Returns any object that is able to be synced. |
||
| 607 | * |
||
| 608 | * @access public |
||
| 609 | * |
||
| 610 | * @param array $args the synchronized object parameters. |
||
| 611 | * @return string Encoded sync object. |
||
| 612 | */ |
||
| 613 | public function sync_object( $args ) { |
||
| 614 | // For example: posts, post, 5. |
||
| 615 | list( $module_name, $object_type, $id ) = $args; |
||
| 616 | |||
| 617 | $sync_module = Modules::get_module( $module_name ); |
||
| 618 | $codec = $this->get_codec(); |
||
| 619 | |||
| 620 | return $codec->encode( $sync_module->get_object_by_id( $object_type, $id ) ); |
||
| 621 | } |
||
| 622 | |||
| 623 | /** |
||
| 624 | * Register additional sync XML-RPC methods available to Jetpack for authenticated users. |
||
| 625 | * |
||
| 626 | * @access public |
||
| 627 | * @since 7.8 |
||
| 628 | * |
||
| 629 | * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server. |
||
| 630 | * @return array Filtered XML-RPC methods. |
||
| 631 | */ |
||
| 632 | public function register_jetpack_xmlrpc_methods( $jetpack_methods ) { |
||
| 633 | $jetpack_methods['jetpack.syncObject'] = array( $this, 'sync_object' ); |
||
| 634 | return $jetpack_methods; |
||
| 635 | } |
||
| 636 | |||
| 637 | /** |
||
| 638 | * Get the incremental sync queue object. |
||
| 639 | * |
||
| 640 | * @access public |
||
| 641 | * |
||
| 642 | * @return Automattic\Jetpack\Sync\Queue Queue object. |
||
| 643 | */ |
||
| 644 | public function get_sync_queue() { |
||
| 645 | return $this->sync_queue; |
||
| 646 | } |
||
| 647 | |||
| 648 | /** |
||
| 649 | * Get the full sync queue object. |
||
| 650 | * |
||
| 651 | * @access public |
||
| 652 | * |
||
| 653 | * @return Automattic\Jetpack\Sync\Queue Queue object. |
||
| 654 | */ |
||
| 655 | public function get_full_sync_queue() { |
||
| 656 | return $this->full_sync_queue; |
||
| 657 | } |
||
| 658 | |||
| 659 | /** |
||
| 660 | * Get the codec object. |
||
| 661 | * |
||
| 662 | * @access public |
||
| 663 | * |
||
| 664 | * @return Automattic\Jetpack\Sync\Codec_Interface Codec object. |
||
| 665 | */ |
||
| 666 | public function get_codec() { |
||
| 667 | return $this->codec; |
||
| 668 | } |
||
| 669 | |||
| 670 | /** |
||
| 671 | * Determine the codec object. |
||
| 672 | * Use gzip deflate if supported. |
||
| 673 | * |
||
| 674 | * @access public |
||
| 675 | */ |
||
| 676 | public function set_codec() { |
||
| 677 | if ( function_exists( 'gzinflate' ) ) { |
||
| 678 | $this->codec = new JSON_Deflate_Array_Codec(); |
||
| 679 | } else { |
||
| 680 | $this->codec = new Simple_Codec(); |
||
| 681 | } |
||
| 682 | } |
||
| 683 | |||
| 684 | /** |
||
| 685 | * Compute and send all the checksums. |
||
| 686 | * |
||
| 687 | * @access public |
||
| 688 | */ |
||
| 689 | public function send_checksum() { |
||
| 690 | $store = new Replicastore(); |
||
| 691 | do_action( 'jetpack_sync_checksum', $store->checksum_all() ); |
||
| 692 | } |
||
| 693 | |||
| 694 | /** |
||
| 695 | * Reset the incremental sync queue. |
||
| 696 | * |
||
| 697 | * @access public |
||
| 698 | */ |
||
| 699 | public function reset_sync_queue() { |
||
| 700 | $this->sync_queue->reset(); |
||
| 701 | } |
||
| 702 | |||
| 703 | /** |
||
| 704 | * Reset the full sync queue. |
||
| 705 | * |
||
| 706 | * @access public |
||
| 707 | */ |
||
| 708 | public function reset_full_sync_queue() { |
||
| 709 | $this->full_sync_queue->reset(); |
||
| 710 | } |
||
| 711 | |||
| 712 | /** |
||
| 713 | * Set the maximum bytes to checkout without exceeding the memory limit. |
||
| 714 | * |
||
| 715 | * @access public |
||
| 716 | * |
||
| 717 | * @param int $size Maximum bytes to checkout. |
||
| 718 | */ |
||
| 719 | public function set_dequeue_max_bytes( $size ) { |
||
| 720 | $this->dequeue_max_bytes = $size; |
||
| 721 | } |
||
| 722 | |||
| 723 | /** |
||
| 724 | * Set the maximum bytes in a single encoded item. |
||
| 725 | * |
||
| 726 | * @access public |
||
| 727 | * |
||
| 728 | * @param int $max_bytes Maximum bytes in a single encoded item. |
||
| 729 | */ |
||
| 730 | public function set_upload_max_bytes( $max_bytes ) { |
||
| 731 | $this->upload_max_bytes = $max_bytes; |
||
| 732 | } |
||
| 733 | |||
| 734 | /** |
||
| 735 | * Set the maximum number of sync items in a single action. |
||
| 736 | * |
||
| 737 | * @access public |
||
| 738 | * |
||
| 739 | * @param int $max_rows Maximum number of sync items. |
||
| 740 | */ |
||
| 741 | public function set_upload_max_rows( $max_rows ) { |
||
| 742 | $this->upload_max_rows = $max_rows; |
||
| 743 | } |
||
| 744 | |||
| 745 | /** |
||
| 746 | * Set the sync wait time (in seconds). |
||
| 747 | * |
||
| 748 | * @access public |
||
| 749 | * |
||
| 750 | * @param int $seconds Sync wait time. |
||
| 751 | */ |
||
| 752 | public function set_sync_wait_time( $seconds ) { |
||
| 753 | $this->sync_wait_time = $seconds; |
||
| 754 | } |
||
| 755 | |||
| 756 | /** |
||
| 757 | * Get current sync wait time (in seconds). |
||
| 758 | * |
||
| 759 | * @access public |
||
| 760 | * |
||
| 761 | * @return int Sync wait time. |
||
| 762 | */ |
||
| 763 | public function get_sync_wait_time() { |
||
| 764 | return $this->sync_wait_time; |
||
| 765 | } |
||
| 766 | |||
| 767 | /** |
||
| 768 | * Set the enqueue wait time (in seconds). |
||
| 769 | * |
||
| 770 | * @access public |
||
| 771 | * |
||
| 772 | * @param int $seconds Enqueue wait time. |
||
| 773 | */ |
||
| 774 | public function set_enqueue_wait_time( $seconds ) { |
||
| 775 | $this->enqueue_wait_time = $seconds; |
||
| 776 | } |
||
| 777 | |||
| 778 | /** |
||
| 779 | * Get current enqueue wait time (in seconds). |
||
| 780 | * |
||
| 781 | * @access public |
||
| 782 | * |
||
| 783 | * @return int Enqueue wait time. |
||
| 784 | */ |
||
| 785 | public function get_enqueue_wait_time() { |
||
| 786 | return $this->enqueue_wait_time; |
||
| 787 | } |
||
| 788 | |||
| 789 | /** |
||
| 790 | * Set the sync wait threshold (in seconds). |
||
| 791 | * |
||
| 792 | * @access public |
||
| 793 | * |
||
| 794 | * @param int $seconds Sync wait threshold. |
||
| 795 | */ |
||
| 796 | public function set_sync_wait_threshold( $seconds ) { |
||
| 797 | $this->sync_wait_threshold = $seconds; |
||
| 798 | } |
||
| 799 | |||
| 800 | /** |
||
| 801 | * Get current sync wait threshold (in seconds). |
||
| 802 | * |
||
| 803 | * @access public |
||
| 804 | * |
||
| 805 | * @return int Sync wait threshold. |
||
| 806 | */ |
||
| 807 | public function get_sync_wait_threshold() { |
||
| 808 | return $this->sync_wait_threshold; |
||
| 809 | } |
||
| 810 | |||
| 811 | /** |
||
| 812 | * Set the maximum time for perfirming a checkout of items from the queue (in seconds). |
||
| 813 | * |
||
| 814 | * @access public |
||
| 815 | * |
||
| 816 | * @param int $seconds Maximum dequeue time. |
||
| 817 | */ |
||
| 818 | public function set_max_dequeue_time( $seconds ) { |
||
| 819 | $this->max_dequeue_time = $seconds; |
||
| 820 | } |
||
| 821 | |||
| 822 | /** |
||
| 823 | * Initialize the sync queues, codec and set the default settings. |
||
| 824 | * |
||
| 825 | * @access public |
||
| 826 | */ |
||
| 827 | public function set_defaults() { |
||
| 828 | $this->sync_queue = new Queue( 'sync' ); |
||
| 829 | $this->full_sync_queue = new Queue( 'full_sync' ); |
||
| 830 | $this->set_codec(); |
||
| 831 | |||
| 832 | // Saved settings. |
||
| 833 | Settings::set_importing( null ); |
||
| 834 | $settings = Settings::get_settings(); |
||
| 835 | $this->set_dequeue_max_bytes( $settings['dequeue_max_bytes'] ); |
||
| 836 | $this->set_upload_max_bytes( $settings['upload_max_bytes'] ); |
||
| 837 | $this->set_upload_max_rows( $settings['upload_max_rows'] ); |
||
| 838 | $this->set_sync_wait_time( $settings['sync_wait_time'] ); |
||
| 839 | $this->set_enqueue_wait_time( $settings['enqueue_wait_time'] ); |
||
| 840 | $this->set_sync_wait_threshold( $settings['sync_wait_threshold'] ); |
||
| 841 | $this->set_max_dequeue_time( Defaults::get_max_sync_execution_time() ); |
||
| 842 | } |
||
| 843 | |||
| 844 | /** |
||
| 845 | * Reset sync queues, modules and settings. |
||
| 846 | * |
||
| 847 | * @access public |
||
| 848 | */ |
||
| 849 | public function reset_data() { |
||
| 850 | $this->reset_sync_queue(); |
||
| 851 | $this->reset_full_sync_queue(); |
||
| 852 | |||
| 853 | foreach ( Modules::get_modules() as $module ) { |
||
| 854 | $module->reset_data(); |
||
| 855 | } |
||
| 856 | |||
| 857 | foreach ( array( 'sync', 'full_sync', 'full-sync-enqueue' ) as $queue_name ) { |
||
| 858 | delete_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name ); |
||
| 859 | } |
||
| 860 | |||
| 861 | Settings::reset_data(); |
||
| 862 | } |
||
| 863 | |||
| 864 | /** |
||
| 865 | * Perform cleanup at the event of plugin uninstallation. |
||
| 866 | * |
||
| 867 | * @access public |
||
| 868 | */ |
||
| 869 | public function uninstall() { |
||
| 870 | // Lets delete all the other fun stuff like transient and option and the sync queue. |
||
| 871 | $this->reset_data(); |
||
| 872 | |||
| 873 | // Delete the full sync status. |
||
| 874 | delete_option( 'jetpack_full_sync_status' ); |
||
| 875 | |||
| 876 | // Clear the sync cron. |
||
| 877 | wp_clear_scheduled_hook( 'jetpack_sync_cron' ); |
||
| 878 | wp_clear_scheduled_hook( 'jetpack_sync_full_cron' ); |
||
| 879 | } |
||
| 880 | } |
||
| 881 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignorePhpDoc annotation to the duplicate definition and it will be ignored.