PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-sync / src / sync-queue / class-queue-storage-options.php

class-queue-storage-options.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-sync/src/sync-queue/class-queue-storage-options.php

294 lines 8.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The class responsible for storing Queue events in the `wp_options` table.
4 *
5 * Used by class Queue.
6 *
7 * @see \Automattic\Jetpack\Sync\Queue
8 *
9 * @package automattic/jetpack-sync
10 */
11
12 namespace Automattic\Jetpack\Sync\Queue;
13
14 /**
15 * `wp_options` storage backend for the Queue.
16 */
17 class Queue_Storage_Options {
18 /**
19 * What queue is this instance responsible for.
20 *
21 * @var string
22 */
23 public $queue_id = '';
24
25 /**
26 * Class constructor.
27 *
28 * @param string $queue_id The queue name this instance will be responsible for.
29 *
30 * @throws \Exception If queue name was not provided.
31 */
32 public function __construct( $queue_id ) {
33 if ( empty( $queue_id ) ) {
34 // TODO what should we return here or throw an exception?
35 throw new \Exception( 'Invalid queue_id provided' );
36 }
37
38 // TODO validate the value maybe?
39 $this->queue_id = $queue_id;
40 }
41
42 /**
43 * Insert an item in the queue.
44 *
45 * @param string $item_id The item ID.
46 * @param string $item Serialized item data.
47 *
48 * @return bool If the item was added.
49 */
50 public function insert_item( $item_id, $item ) {
51 global $wpdb;
52
53 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
54 $rows_added = $wpdb->query(
55 $wpdb->prepare(
56 "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES (%s, %s,%s)",
57 $item_id,
58 $item,
59 'no'
60 )
61 );
62
63 return ( 0 !== $rows_added );
64 }
65
66 /**
67 * Fetch items from the queue.
68 *
69 * @param int|null $item_count How many items to fetch from the queue.
70 * Null for no limit.
71 * @param string $order Sort direction for the items. Accepts 'ASC' or 'DESC'.
72 * Any other value will be treated as 'ASC'.
73 *
74 * @return array|object|null Array of result objects on success, or null on failure.
75 */
76 public function fetch_items( $item_count, $order = 'ASC' ) {
77 global $wpdb;
78
79 $order = 'DESC' === $order ? 'DESC' : 'ASC';
80
81 $sql_order = "ORDER BY option_name {$order}";
82
83 $sql = "SELECT option_name AS id, option_value AS value
84 FROM $wpdb->options
85 WHERE option_name LIKE %s
86 {$sql_order}";
87
88 $params = array( "jpsq_{$this->queue_id}-%" );
89
90 if ( $item_count ) {
91 $sql .= ' LIMIT %d';
92 $params[] = $item_count;
93 }
94
95 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
96 $items = $wpdb->get_results(
97 $wpdb->prepare( $sql, $params ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
98 OBJECT
99 );
100
101 return $items;
102 }
103
104 /**
105 * Fetches items with specific IDs from the Queue.
106 *
107 * @param array $items_ids Items IDs to fetch from the queue.
108 *
109 * @return \stdClass[]|null
110 */
111 public function fetch_items_by_ids( $items_ids ) {
112 global $wpdb;
113
114 // return early if $items_ids is empty or not an array.
115 if ( empty( $items_ids ) || ! is_array( $items_ids ) ) {
116 return array();
117 }
118
119 $ids_placeholders = implode( ', ', array_fill( 0, count( $items_ids ), '%s' ) );
120
121 $query_with_placeholders = "SELECT option_name AS id, option_value AS value
122 FROM $wpdb->options
123 WHERE option_name IN ( $ids_placeholders )";
124
125 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
126 $items = $wpdb->get_results(
127 $wpdb->prepare(
128 $query_with_placeholders, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
129 $items_ids
130 ),
131 OBJECT
132 );
133
134 return $items;
135 }
136
137 /**
138 * Clear out the queue.
139 *
140 * @return bool|int|\mysqli_result|resource|null
141 */
142 public function clear_queue() {
143 global $wpdb;
144
145 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
146 return $wpdb->query(
147 $wpdb->prepare(
148 "DELETE FROM $wpdb->options WHERE option_name LIKE %s",
149 "jpsq_{$this->queue_id}-%"
150 )
151 );
152 }
153
154 /**
155 * Check how many items are in the queue.
156 *
157 * @return int
158 */
159 public function get_item_count() {
160 global $wpdb;
161
162 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
163 return (int) $wpdb->get_var(
164 $wpdb->prepare(
165 "SELECT count(*) FROM $wpdb->options WHERE option_name LIKE %s",
166 "jpsq_{$this->queue_id}-%"
167 )
168 );
169 }
170
171 /**
172 * Return the lag amount for the queue.
173 *
174 * @param float|int|null $now A timestamp to use as starting point when calculating the lag.
175 *
176 * @return float|int The lag amount.
177 */
178 public function get_lag( $now = null ) {
179 global $wpdb;
180
181 // TODO replace with peek and a flag to fetch only the name.
182 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
183 $first_item_name = $wpdb->get_var(
184 $wpdb->prepare(
185 "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT 1",
186 "jpsq_{$this->queue_id}-%"
187 )
188 );
189
190 if ( ! $first_item_name ) {
191 return 0;
192 }
193
194 if ( null === $now ) {
195 $now = microtime( true );
196 }
197
198 // Break apart the item name to get the timestamp.
199 $matches = null;
200 if ( preg_match( '/^jpsq_' . $this->queue_id . '-(\d+\.\d+)-/', $first_item_name, $matches ) ) {
201 return $now - (float) $matches[1];
202 } else {
203 return 0;
204 }
205 }
206
207 /**
208 * Add multiple items to the queue at once.
209 *
210 * @param array $items Array of items to add.
211 * @param string $id_prefix Prefix to use for all the items.
212 *
213 * @return bool|int|\mysqli_result|resource|null
214 */
215 public function add_all( $items, $id_prefix ) {
216 global $wpdb;
217
218 $query = "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES ";
219
220 $rows = array();
221 $count_items = count( $items );
222 for ( $i = 0; $i < $count_items; ++$i ) {
223 // skip empty items.
224 if ( empty( $items[ $i ] ) ) {
225 continue;
226 }
227 try {
228 $option_name = esc_sql( $id_prefix . '-' . $i );
229 $option_value = esc_sql( serialize( $items[ $i ] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
230 $rows[] = "('$option_name', '$option_value', 'no')";
231 } catch ( \Exception $e ) {
232 // Item cannot be serialized so skip.
233 continue;
234 }
235 }
236
237 $rows_added = $wpdb->query( $query . implode( ',', $rows ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
238
239 return $rows_added;
240 }
241
242 /**
243 * Return $max_count items from the queue, including their value string length.
244 *
245 * @param int $max_count How many items to fetch from the queue.
246 *
247 * @return object[]|null
248 */
249 public function get_items_ids_with_size( $max_count ) {
250 global $wpdb;
251
252 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
253 return $wpdb->get_results(
254 $wpdb->prepare(
255 "SELECT option_name AS id, LENGTH(option_value) AS value_size FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT %d",
256 "jpsq_{$this->queue_id}-%",
257 $max_count
258 ),
259 OBJECT
260 );
261 }
262
263 /**
264 * Delete items with specific IDs from the queue.
265 *
266 * @param array $ids IDs of the items to remove from the queue.
267 *
268 * @return bool|int|\mysqli_result|resource|null
269 */
270 public function delete_items_by_ids( $ids ) {
271 global $wpdb;
272
273 if ( ! is_array( $ids ) || empty( $ids ) ) {
274 return false;
275 }
276
277 // TODO check if it's working properly - no need to delete all options in the table if the params are not right
278 $ids_placeholders = implode( ', ', array_fill( 0, count( $ids ), '%s' ) );
279
280 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
281 return $wpdb->query(
282 $wpdb->prepare(
283 /**
284 * Ignoring the linting warning, as there's still no placeholder replacement for DB field name,
285 * in this case this is `$ids_placeholders`, as we're preparing them above and are a dynamic count.
286 */
287 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare
288 "DELETE FROM {$wpdb->options} WHERE option_name IN ( $ids_placeholders )",
289 $ids
290 )
291 );
292 }
293 }
294