oont-contents/plugins/w3-total-cache/Extension_AlwaysCached_Queue.php
2025-02-08 15:10:23 +01:00

435 lines
10 KiB
PHP

<?php
/**
* File: Extension_AlwaysCached_Queue.php
*
* AlwaysCached queue controller.
*
* @since 2.8.0
*
* @package W3TC
*/
namespace W3TC;
if ( ! defined( 'W3TC_ALWAYSCACHED_TABLE_QUEUE' ) ) {
define( 'W3TC_ALWAYSCACHED_TABLE_QUEUE', 'w3tc_alwayscached_queue' );
}
/**
* AlwaysCached queue model.
*
* @since 2.8.0
*/
class Extension_AlwaysCached_Queue {
/**
* Queue add.
*
* @since 2.8.0
*
* @param string $url URL.
* @param array $extension Extension data.
* @param integer $priority Priority.
*
* @return void
*/
public static function add( $url, $extension, $priority = 100 ) {
// Compress page_key_extension by removing empty values.
$extension = array_filter( $extension );
global $wpdb;
$table = self::table_name();
$key = self::key_by_url( $url );
/**
* The page_key_extension has to be updated since for :flush operation it contains timestamp
* to flush before has to be refreshed if duplicate found.
*/
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->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;
}
}