Completed
Push — try/sync-term-relationship-bug ( e8d890 )
by
unknown
11:08 queued 04:38
created

Term_Relationships::recalculate_total()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/**
3
 * Term relationships sync module.
4
 *
5
 * @package automattic/jetpack-sync
6
 */
7
8
namespace Automattic\Jetpack\Sync\Modules;
9
10
use Automattic\Jetpack\Sync\Listener;
11
use Automattic\Jetpack\Sync\Settings;
12
13
/**
14
 * Class to handle sync for term relationships.
15
 */
16
class Term_Relationships extends Module {
17
18
	/**
19
	 * Max terms to return in one single query
20
	 *
21
	 * @access public
22
	 *
23
	 * @const int
24
	 */
25
	const QUERY_LIMIT = 1000;
26
27
	/**
28
	 * Max mysql integer
29
	 *
30
	 * @access public
31
	 *
32
	 * @const int
33
	 */
34
	const MAX_INT = 999999999;
35
36
	/**
37
	 * Sync module name.
38
	 *
39
	 * @access public
40
	 *
41
	 * @return string
42
	 */
43
	public function name() {
44
		return 'term_relationships';
45
	}
46
47
	/**
48
	 * The id field in the database.
49
	 *
50
	 * @access public
51
	 *
52
	 * @return string
53
	 */
54
	public function id_field() {
55
		return 'object_id';
56
	}
57
58
	/**
59
	 * The table in the database.
60
	 *
61
	 * @access public
62
	 *
63
	 * @return string
64
	 */
65
	public function table_name() {
66
		return 'term_relationships';
67
	}
68
69
	/**
70
	 * Initialize term relationships action listeners for full sync.
71
	 *
72
	 * @access public
73
	 *
74
	 * @param callable $callable Action handler callable.
75
	 */
76
	public function init_full_sync_listeners( $callable ) {
77
		add_action( 'jetpack_full_sync_term_relationships', $callable, 10, 2 );
78
	}
79
80
	/**
81
	 * Initialize the module in the sender.
82
	 *
83
	 * @access public
84
	 */
85
	public function init_before_send() {
86
		// Full sync.
87
		add_filter( 'jetpack_sync_before_send_jetpack_full_sync_term_relationships', array( $this, 'expand_term_relationships' ) );
88
	}
89
90
	/**
91
	 * Enqueue the term relationships actions for full sync.
92
	 *
93
	 * @access public
94
	 *
95
	 * @param array  $config Full sync configuration for this sync module.
96
	 * @param int    $max_items_to_enqueue Maximum number of items to enqueue.
97
	 * @param object $last_object_enqueued Last object enqueued.
98
	 *
99
	 * @return array Number of actions enqueued, and next module state.
100
	 * @todo This method has similarities with Automattic\Jetpack\Sync\Modules\Module::enqueue_all_ids_as_action. Refactor to keep DRY.
101
	 * @see Automattic\Jetpack\Sync\Modules\Module::enqueue_all_ids_as_action
102
	 */
103
	public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $last_object_enqueued ) {
104
		global $wpdb;
105
		$term_relationships_full_sync_item_size = Settings::get_setting( 'term_relationships_full_sync_item_size' );
106
		$limit                                  = min( $max_items_to_enqueue * $term_relationships_full_sync_item_size, self::QUERY_LIMIT );
107
		$items_enqueued_count                   = 0;
108
		$last_object_enqueued                   = $last_object_enqueued ? $last_object_enqueued : array(
109
			'object_id'        => self::MAX_INT,
110
			'term_taxonomy_id' => self::MAX_INT,
111
		);
112
113
		while ( $limit > 0 ) {
114
			/*
115
			 * SELECT object_id, term_taxonomy_id
116
			 *  FROM $wpdb->term_relationships
117
			 *  WHERE ( object_id = 11 AND term_taxonomy_id < 14 ) OR ( object_id < 11 )
118
			 *  ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT 1000
119
			 */
120
			$objects = $wpdb->get_results( $wpdb->prepare( "SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d ) ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT %d", $last_object_enqueued['object_id'], $last_object_enqueued['term_taxonomy_id'], $last_object_enqueued['object_id'], $limit ), ARRAY_A );
121
			// Request term relationships in groups of N for efficiency.
122
			$objects_count = count( $objects );
123
			if ( ! count( $objects ) ) {
124
				return array( $items_enqueued_count, true );
125
			}
126
			$items                 = array_chunk( $objects, $term_relationships_full_sync_item_size );
127
			$last_object_enqueued  = $this->bulk_enqueue_full_sync_term_relationships( $items, $last_object_enqueued );
0 ignored issues
show
Bug introduced by
It seems like $last_object_enqueued can also be of type object; however, Automattic\Jetpack\Sync\...nc_term_relationships() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
128
			$items_enqueued_count += count( $items );
129
			$limit                 = min( $limit - $objects_count, self::QUERY_LIMIT );
130
		}
131
132
		// We need to do this extra check in case $max_items_to_enqueue * $term_relationships_full_sync_item_size == relationships objects left.
133
		$count = $this->count_remaining_items( $last_object_enqueued );
0 ignored issues
show
Bug introduced by
It seems like $last_object_enqueued defined by $last_object_enqueued ? ...y_id' => self::MAX_INT) on line 108 can also be of type object; however, Automattic\Jetpack\Sync\...count_remaining_items() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
134
		if ( intval( $count ) === 0 ) {
135
			return array( $items_enqueued_count, true );
136
		}
137
138
		return array( $items_enqueued_count, $last_object_enqueued );
139
	}
140
141
	/**
142
	 *
143
	 * Enqueue all $items within `jetpack_full_sync_term_relationships` actions.
144
	 *
145
	 * @param array $items Groups of objects to sync.
146
	 * @param array $previous_interval_end Last item enqueued.
147
	 *
148
	 * @return array Last enqueued object.
149
	 */
150
	public function bulk_enqueue_full_sync_term_relationships( $items, $previous_interval_end ) {
151
		$listener                         = Listener::get_instance();
152
		$items_with_previous_interval_end = $this->get_chunks_with_preceding_end( $items, $previous_interval_end );
0 ignored issues
show
Documentation introduced by
$previous_interval_end is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
153
		$listener->bulk_enqueue_full_sync_actions( 'jetpack_full_sync_term_relationships', $items_with_previous_interval_end );
154
		$last_item = end( $items );
155
		return end( $last_item );
156
	}
157
158
	/**
159
	 * Retrieve an estimated number of actions that will be enqueued.
160
	 *
161
	 * @access public
162
	 *
163
	 * @param array $config Full sync configuration for this sync module.
164
	 * @return int Number of items yet to be enqueued.
165
	 */
166
	public function estimate_full_sync_actions( $config ) {
167
		global $wpdb;
168
169
		$query = "SELECT COUNT(*) FROM $wpdb->term_relationships";
170
171
		// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
172
		$count = $wpdb->get_var( $query );
173
174
		return (int) ceil( $count / Settings::get_setting( 'term_relationships_full_sync_item_size' ) );
175
	}
176
177
	/**
178
	 * Use to calculate the new total.
179
	 *
180
	 * @param array $previous_enqueue_status
181
	 *
182
	 * @return int The number of items that we should be sending.
183
	 */
184
	public function recalculate_total( $previous_enqueue_status ) {
185
186
		list( $previous_total, $previously_enqueued_item_count, $previous_enqueue_state ) = $previous_enqueue_status;
187
		if ( ! is_array( $previous_enqueue_status[2] ) ) {
188
			return $previous_total;
189
		}
190
		$count = $this->count_remaining_items( $previous_enqueue_state );
191
192
		return (int) ceil( $count / Settings::get_setting( 'term_relationships_full_sync_item_size' ) ) + $previously_enqueued_item_count;
193
	}
194
195
	/**
196
	 * Helper function that counts the remaining items give the last object enqueued.
197
	 *
198
	 * @param array $last_object_enqueued Array containing the place where we last left of.
199
	 *
200
	 * @return int The number of term relationships that are left to enqueue.
201
	 */
202
	private function count_remaining_items( $last_object_enqueued ) {
203
		global $wpdb;
204
		return $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d ) ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT %d", $last_object_enqueued['object_id'], $last_object_enqueued['term_taxonomy_id'], $last_object_enqueued['object_id'], 1 ) );
205
206
	}
207
208
	/**
209
	 * Retrieve the actions that will be sent for this module during a full sync.
210
	 *
211
	 * @access public
212
	 *
213
	 * @return array Full sync actions of this module.
214
	 */
215
	public function get_full_sync_actions() {
216
		return array( 'jetpack_full_sync_term_relationships' );
217
	}
218
219
	/**
220
	 * Expand the term relationships within a hook before they are serialized and sent to the server.
221
	 *
222
	 * @access public
223
	 *
224
	 * @param array $args The hook parameters.
225
	 * @return array $args The expanded hook parameters.
226
	 */
227
	public function expand_term_relationships( $args ) {
228
		list( $term_relationships, $previous_end ) = $args;
229
230
		return array(
231
			'term_relationships' => $term_relationships,
232
			'previous_end'       => $previous_end,
233
		);
234
	}
235
}
236