query( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "INSERT INTO `$table` ( `key`, url, extension, priority, to_process ) VALUES ( %s, %s, %s, %d, %s ) ON DUPLICATE KEY UPDATE extension = %s, requests_count = requests_count + 1", $key, $url, serialize( $extension ), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize $priority, gmdate( 'Y-m-d G:i:s' ), serialize( $extension ) // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize ) ); } /** * Get by url * * @since 2.8.0 * * @param string $url URL. * * @return array|object|null|void */ public static function get_by_url( $url ) { global $wpdb; $table = self::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_row( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT * FROM `$table` WHERE `key` = %s", self::key_by_url( $url ) ), ARRAY_A ); } /** * Retreives the first 10 items in queue. * * @since 2.8.0 * * @return array|null */ public static function pop_item_begin() { global $wpdb; $table = self::table_name(); // Concurrency-safe extraction. for ( $n = 0; $n < 10; $n++ ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $item = $wpdb->get_row( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT * FROM `$table` WHERE to_process <= %s ORDER BY priority, to_process LIMIT 1", gmdate( 'Y-m-d G:i:s' ) ), ARRAY_A ); if ( empty( $item ) ) { return null; } $new_to_process = gmdate( 'Y-m-d G:i:s', time() + 300 ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $count = $wpdb->query( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "UPDATE `$table` SET to_process = %s WHERE `key` = %s AND to_process = %s", $new_to_process, $item['key'], $item['to_process'] ) ); if ( 1 === $count ) { $item['to_process'] = $new_to_process; return $item; } } return null; } /** * Deletes queue item after pop. * * @since 2.8.0 * * @param array $item Queue item. * * @return void */ public static function pop_item_finish( $item ) { global $wpdb; $table = self::table_name(); // Make sure we delete only when not changed since. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $wpdb->query( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "DELETE FROM `$table` WHERE `key` = %s AND to_process = %s AND requests_count = %d", $item['key'], $item['to_process'], $item['requests_count'] ) ); } /** * Retrives queue rows. * * @since 2.8.0 * * @param string $mode Queue mode. * @param integer $offset Pagination offset. * @param integer $limit Pagination page entries limit. * @param string $search_query Search query. * * @return array|object|null */ public static function rows( $mode, $offset = 0, $limit = 15, $search_query = '' ) { global $wpdb; $table = self::table_name(); $comp = 'postponed' === $mode ? '>' : '<='; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_results( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT * FROM `$table` WHERE to_process $comp %s AND url LIKE %s ORDER BY priority, to_process LIMIT %d OFFSET %d", gmdate( 'Y-m-d G:i:s' ), '%' . $wpdb->esc_like( $search_query ) . '%', $limit, $offset ), ARRAY_A ); } /** * Retrives queue pending row count. * * @since 2.8.0 * * @param string $search_query Search query. * * @return string|null */ public static function row_count_pending( $search_query = '' ) { global $wpdb; $table = self::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT COUNT(*) FROM `$table` WHERE to_process < %s AND url LIKE %s", gmdate( 'Y-m-d G:i:s' ), '%' . $wpdb->esc_like( $search_query ) . '%' ) ); } /** * Retrives queue postponed row count. * * @since 2.8.0 * * @param string $search_query Search query. * * @return string|null */ public static function row_count_postponed( $search_query = '' ) { global $wpdb; $table = self::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching return $wpdb->get_var( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT COUNT(*) FROM `$table` WHERE to_process >= %s AND url LIKE %s", gmdate( 'Y-m-d G:i:s' ), '%' . $wpdb->esc_like( $search_query ) . '%' ) ); } /** * Deletes all queue rows. * * @since 2.8.0 * * @return int|bool */ public static function empty() { // phpcs:ignore WordPress.WhiteSpace.ControlStructureSpacing.NoSpaceAfterOpenParenthesis global $wpdb; $table = self::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared return $wpdb->query( "DELETE FROM `$table`" ); } /** * Checks if higher priority items present * * @since 2.8.0 * * @param array $item Item data. * * @return bool */ public static function exists_higher_priority( $item ) { global $wpdb; $table = self::table_name(); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $higher_item = $wpdb->get_row( $wpdb->prepare( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT * FROM `$table` WHERE to_process <= %s AND priority < %d ORDER BY priority, to_process LIMIT 1", gmdate( 'Y-m-d G:i:s' ), $item['priority'] ), ARRAY_A ); return ! empty( $higher_item ); } /** * Gets key based on URL * * @since 2.8.0 * * @param string $url URL. * * @return string */ private static function key_by_url( $url ) { return strlen( $url ) > 50 ? md5( $url ) : $url; } /** * Gets AlwaysCached queue table name. * * @since 2.8.0 * * @return string */ private static function table_name() { global $wpdb; return $wpdb->base_prefix . W3TC_ALWAYSCACHED_TABLE_QUEUE; } /** * Drops the AwaysCached queue table. * * @since 2.8.0 * * @return void * * @throws Util_Environment_Exception Exception. */ public static function drop_table() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( self::drop_table_sql() ); if ( ! $wpdb->result ) { throw new Util_Environment_Exception( esc_html__( 'Can\'t drop table ', 'w3-total-cache' ) . esc_html( self::table_name() ) ); } } /** * Creates AlwaysCached queue table. * * @since 2.8.0 * * @return void * * @throws Util_Environment_Exception Exception. */ public static function create_table() { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared $wpdb->query( self::create_table_sql() ); if ( ! $wpdb->result ) { throw new Util_Environment_Exception( esc_html__( 'Can\'t create table ', 'w3-total-cache' ) . esc_html( self::table_name() ) ); } } /** * Retrives AlwaysCached queue table drop SQL. * * @since 2.8.0 * * @return string */ public static function drop_table_sql() { global $wpdb; $table = self::table_name(); $sql = "DROP TABLE IF EXISTS `$table`"; return $sql; } /** * Retrives AlwaysCached queue table create SQL. * * @since 2.8.0 * * @return string */ public static function create_table_sql() { global $wpdb; $table = self::table_name(); $charset_collate = ''; if ( ! empty( $wpdb->charset ) ) { $charset_collate .= "DEFAULT CHARACTER SET $wpdb->charset"; } if ( ! empty( $wpdb->collate ) ) { $charset_collate .= " COLLATE $wpdb->collate"; } // priority - smaller number is higher priority. $sql = "CREATE TABLE IF NOT EXISTS `$table` ( `key` varchar(50) CHARACTER SET `ascii` NOT NULL, `url` varchar(500) NOT NULL, `extension` varchar(500) NOT NULL, `priority` tinyint NOT NULL DEFAULT 100, `requests_count` int NOT NULL DEFAULT 1, `to_process` datetime NOT NULL, PRIMARY KEY (`key`), INDEX `to_process` (`to_process`) ) $charset_collate"; return $sql; } }