queue_id = $queue_id; } /** * Insert an item in the queue. * * @param string $item_id The item ID. * @param string $item Serialized item data. * * @return bool If the item was added. */ public function insert_item( $item_id, $item ) { global $wpdb; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery $rows_added = $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES (%s, %s,%s)", $item_id, $item, 'no' ) ); return ( 0 !== $rows_added ); } /** * Fetch items from the queue. * * @param int|null $item_count How many items to fetch from the queue. * Null for no limit. * @param string $order Sort direction for the items. Accepts 'ASC' or 'DESC'. * Any other value will be treated as 'ASC'. * * @return array|object|null Array of result objects on success, or null on failure. */ public function fetch_items( $item_count, $order = 'ASC' ) { global $wpdb; $order = 'DESC' === $order ? 'DESC' : 'ASC'; $sql_order = "ORDER BY option_name {$order}"; $sql = "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name LIKE %s {$sql_order}"; $params = array( "jpsq_{$this->queue_id}-%" ); if ( $item_count ) { $sql .= ' LIMIT %d'; $params[] = $item_count; } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $items = $wpdb->get_results( $wpdb->prepare( $sql, $params ), // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared OBJECT ); return $items; } /** * Fetches items with specific IDs from the Queue. * * @param array $items_ids Items IDs to fetch from the queue. * * @return \stdClass[]|null */ public function fetch_items_by_ids( $items_ids ) { global $wpdb; // return early if $items_ids is empty or not an array. if ( empty( $items_ids ) || ! is_array( $items_ids ) ) { return array(); } $ids_placeholders = implode( ', ', array_fill( 0, count( $items_ids ), '%s' ) ); $query_with_placeholders = "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name IN ( $ids_placeholders )"; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery $items = $wpdb->get_results( $wpdb->prepare( $query_with_placeholders, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $items_ids ), OBJECT ); return $items; } /** * Clear out the queue. * * @return bool|int|\mysqli_result|resource|null */ public function clear_queue() { global $wpdb; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name LIKE %s", "jpsq_{$this->queue_id}-%" ) ); } /** * Check how many items are in the queue. * * @return int */ public function get_item_count() { global $wpdb; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery return (int) $wpdb->get_var( $wpdb->prepare( "SELECT count(*) FROM $wpdb->options WHERE option_name LIKE %s", "jpsq_{$this->queue_id}-%" ) ); } /** * Return the lag amount for the queue. * * @param float|int|null $now A timestamp to use as starting point when calculating the lag. * * @return float|int The lag amount. */ public function get_lag( $now = null ) { global $wpdb; // TODO replace with peek and a flag to fetch only the name. // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery $first_item_name = $wpdb->get_var( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT 1", "jpsq_{$this->queue_id}-%" ) ); if ( ! $first_item_name ) { return 0; } if ( null === $now ) { $now = microtime( true ); } // Break apart the item name to get the timestamp. $matches = null; if ( preg_match( '/^jpsq_' . $this->queue_id . '-(\d+\.\d+)-/', $first_item_name, $matches ) ) { return $now - (float) $matches[1]; } else { return 0; } } /** * Add multiple items to the queue at once. * * @param array $items Array of items to add. * @param string $id_prefix Prefix to use for all the items. * * @return bool|int|\mysqli_result|resource|null */ public function add_all( $items, $id_prefix ) { global $wpdb; $query = "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES "; $rows = array(); $count_items = count( $items ); for ( $i = 0; $i < $count_items; ++$i ) { // skip empty items. if ( empty( $items[ $i ] ) ) { continue; } try { $option_name = esc_sql( $id_prefix . '-' . $i ); $option_value = esc_sql( serialize( $items[ $i ] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize $rows[] = "('$option_name', '$option_value', 'no')"; } catch ( \Exception $e ) { // Item cannot be serialized so skip. continue; } } $rows_added = $wpdb->query( $query . implode( ',', $rows ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $rows_added; } /** * Return $max_count items from the queue, including their value string length. * * @param int $max_count How many items to fetch from the queue. * * @return object[]|null */ public function get_items_ids_with_size( $max_count ) { global $wpdb; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->get_results( $wpdb->prepare( "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", "jpsq_{$this->queue_id}-%", $max_count ), OBJECT ); } /** * Delete items with specific IDs from the queue. * * @param array $ids IDs of the items to remove from the queue. * * @return bool|int|\mysqli_result|resource|null */ public function delete_items_by_ids( $ids ) { global $wpdb; if ( ! is_array( $ids ) || empty( $ids ) ) { return false; } // TODO check if it's working properly - no need to delete all options in the table if the params are not right $ids_placeholders = implode( ', ', array_fill( 0, count( $ids ), '%s' ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->query( $wpdb->prepare( /** * Ignoring the linting warning, as there's still no placeholder replacement for DB field name, * in this case this is `$ids_placeholders`, as we're preparing them above and are a dynamic count. */ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare "DELETE FROM {$wpdb->options} WHERE option_name IN ( $ids_placeholders )", $ids ) ); } }