-
Notifications
You must be signed in to change notification settings - Fork 9
New data structure #63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 5 commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
8e5c5fd
delay get_option( 'option_optimizer' ) to 'shutdown'
ilicfilip 168ff77
already set
ilicfilip 0bc9aee
update data every 5 mins
ilicfilip 7aa9288
hello custom table
ilicfilip 16a1ea9
Merge branch 'develop' into filip/new-data-structure
ilicfilip fdcfd13
change namespace
ilicfilip fddd0dc
update links and phpcs prefix
ilicfilip 15df693
Merge branch 'develop' into filip/new-data-structure
ilicfilip ee94c46
avoid PHP fatal error in case of downgrade
ilicfilip 1272145
adjust migration condition
ilicfilip c9bbfbf
Merge branch 'develop' into filip/new-data-structure
ilicfilip bc6dc26
composer.lock
ilicfilip ad54e71
one more prefix for phpcs
ilicfilip 435c60b
now fix indend phpcs errors
ilicfilip d877980
Merge branch 'develop' into filip/new-data-structure
ilicfilip c813a6e
split into chunks
ilicfilip 39307c6
remove batch write, not needed with custom tables
ilicfilip 90ea8bc
fix docblock and phpstan error
ilicfilip 3e06c2f
prevent race conditions / sqlite lock errors
ilicfilip c8e4b9d
put back, for backwards compat
ilicfilip ae2daae
prevent triggering migration in parallel
ilicfilip 491bf06
add ajax migration
ilicfilip 75ec11d
phpstan fixes
ilicfilip File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| <?php | ||
| /** | ||
| * Database functionality for AAA Option Optimizer. | ||
| * | ||
| * @package Emilia\OptionOptimizer | ||
| */ | ||
|
|
||
| namespace Emilia\OptionOptimizer; | ||
|
|
||
| /** | ||
| * Handles custom database table for tracking options. | ||
| */ | ||
| class Database { | ||
|
|
||
| /** | ||
| * The database table name (without prefix). | ||
| * | ||
| * @var string | ||
| */ | ||
| const TABLE_NAME = 'option_optimizer_tracked'; | ||
|
|
||
| /** | ||
| * Get the full table name with prefix. | ||
| * | ||
| * @return string | ||
| */ | ||
| public static function get_table_name() { | ||
| global $wpdb; | ||
| return $wpdb->prefix . self::TABLE_NAME; | ||
| } | ||
|
|
||
| /** | ||
| * Create the custom table. | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function create_table() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = self::get_table_name(); | ||
| $charset_collate = $wpdb->get_charset_collate(); | ||
|
|
||
| $sql = "CREATE TABLE {$table_name} ( | ||
| option_name VARCHAR(191) NOT NULL, | ||
| access_count BIGINT UNSIGNED DEFAULT 1, | ||
| created_at DATETIME DEFAULT CURRENT_TIMESTAMP, | ||
| PRIMARY KEY (option_name) | ||
| ) {$charset_collate};"; | ||
|
|
||
| require_once ABSPATH . 'wp-admin/includes/upgrade.php'; | ||
| \dbDelta( $sql ); | ||
| } | ||
|
|
||
| /** | ||
| * Drop the custom table. | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function drop_table() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = self::get_table_name(); | ||
|
|
||
| // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is safe (from constant). | ||
| $wpdb->query( "DROP TABLE IF EXISTS {$table_name}" ); | ||
| } | ||
|
|
||
| /** | ||
| * Check if the table exists. | ||
| * | ||
| * @return bool | ||
| */ | ||
| public static function table_exists() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = self::get_table_name(); | ||
|
|
||
| // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching | ||
| return $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name; | ||
| } | ||
|
|
||
| /** | ||
| * Migrate data from the old option format to the custom table. | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function maybe_migrate() { | ||
| $option_data = \get_option( 'option_optimizer' ); | ||
|
|
||
| // No data or already migrated (no used_options key). | ||
| if ( ! \is_array( $option_data ) || ! isset( $option_data['used_options'] ) ) { | ||
| return; | ||
| } | ||
|
|
||
| // Ensure table exists. | ||
| if ( ! self::table_exists() ) { | ||
| self::create_table(); | ||
| } | ||
|
|
||
| // Batch insert old data to custom table. | ||
| if ( ! empty( $option_data['used_options'] ) ) { | ||
| self::batch_insert( $option_data['used_options'] ); | ||
| } | ||
|
|
||
| // Remove used_options from the option, keep metadata. | ||
| unset( $option_data['used_options'] ); | ||
| \update_option( 'option_optimizer', $option_data, false ); | ||
| } | ||
|
|
||
| /** | ||
| * Batch insert or update option counts. | ||
| * | ||
| * @param array<string, int> $options Array of option_name => count. | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function batch_insert( $options ) { | ||
| global $wpdb; | ||
|
|
||
| if ( empty( $options ) ) { | ||
| return; | ||
| } | ||
|
|
||
| $table_name = self::get_table_name(); | ||
| $values = []; | ||
| $placeholders = []; | ||
|
|
||
| foreach ( $options as $option_name => $count ) { | ||
| $placeholders[] = '(%s, %d, NOW())'; | ||
| $values[] = $option_name; | ||
| $values[] = (int) $count; | ||
| } | ||
|
|
||
| $sql = "INSERT INTO {$table_name} (option_name, access_count, created_at) | ||
| VALUES " . implode( ', ', $placeholders ) . ' | ||
| ON DUPLICATE KEY UPDATE access_count = access_count + VALUES(access_count)'; | ||
|
|
||
| // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared | ||
|
ilicfilip marked this conversation as resolved.
Outdated
|
||
| $wpdb->query( $wpdb->prepare( $sql, ...$values ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Get all tracked options as an associative array. | ||
| * | ||
| * @return array<string, int> Array of option_name => access_count. | ||
| */ | ||
| public static function get_tracked_options() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = self::get_table_name(); | ||
|
|
||
| // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is safe (from constant). | ||
| $results = $wpdb->get_results( "SELECT option_name, access_count FROM {$table_name}", ARRAY_A ); | ||
|
|
||
| if ( empty( $results ) ) { | ||
| return []; | ||
| } | ||
|
|
||
| $options = []; | ||
| foreach ( $results as $row ) { | ||
| $options[ $row['option_name'] ] = (int) $row['access_count']; | ||
| } | ||
|
|
||
| return $options; | ||
| } | ||
|
|
||
| /** | ||
| * Get tracked option names as a keyed array for efficient lookups. | ||
| * | ||
| * @return array<string, bool> Array of option_name => true. | ||
| */ | ||
| public static function get_tracked_option_keys() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = self::get_table_name(); | ||
|
|
||
| // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is safe (from constant). | ||
| $option_names = $wpdb->get_col( "SELECT option_name FROM {$table_name}" ); | ||
|
|
||
| if ( empty( $option_names ) ) { | ||
| return []; | ||
| } | ||
|
|
||
| return array_fill_keys( $option_names, true ); | ||
| } | ||
|
|
||
| /** | ||
| * Clear all tracked options from the table. | ||
| * | ||
| * @return void | ||
| */ | ||
| public static function clear_tracked_options() { | ||
| global $wpdb; | ||
|
|
||
| $table_name = self::get_table_name(); | ||
|
|
||
| // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name is safe (from constant). | ||
| $wpdb->query( "TRUNCATE TABLE {$table_name}" ); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.