PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.6
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.6
28.6 28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 All 130 releases
wordpress-seo / src / expiring-store / infrastructure / wpdb-expiring-store-repository.php

wpdb-expiring-store-repository.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.6, at src/expiring-store/infrastructure/wpdb-expiring-store-repository.php

162 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Yoast\WP\SEO\Expiring_Store\Infrastructure;
4
5 use Yoast\WP\SEO\Expiring_Store\Application\Ports\Expiring_Store_Repository_Interface;
6
7 /**
8 * WPDB-backed repository for the expiring store table.
9 *
10 * Handles raw database queries against the network-wide yoast_expiring_store table.
11 */
12 class WPDB_Expiring_Store_Repository implements Expiring_Store_Repository_Interface {
13
14 /**
15 * Inserts or replaces a value in the expiring store.
16 *
17 * @param string $key The key to store.
18 * @param string $json_value The JSON-encoded value.
19 * @param string $expiration_datetime The expiration datetime in 'Y-m-d H:i:s' format.
20 *
21 * @return void
22 */
23 public function upsert( string $key, string $json_value, string $expiration_datetime ): void {
24 global $wpdb;
25
26 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table with no caching layer.
27 $wpdb->replace(
28 $this->get_table_name(),
29 [
30 'key_name' => $key,
31 'value' => $json_value,
32 'exp' => $expiration_datetime,
33 ],
34 [ '%s', '%s', '%s' ],
35 );
36 }
37
38 /**
39 * Inserts a value only if the key does not already exist or has expired.
40 *
41 * First removes any expired row for this key, then attempts an INSERT IGNORE.
42 * The primary key constraint ensures that only one concurrent caller can win the insert.
43 * Both queries use standard SQL compatible with MySQL and SQLite (via the WP SQLite plugin).
44 *
45 * @param string $key The key to store.
46 * @param string $json_value The JSON-encoded value.
47 * @param string $expiration_datetime The expiration datetime in 'Y-m-d H:i:s' format.
48 * @param string $current_datetime The current datetime in 'Y-m-d H:i:s' format.
49 *
50 * @return bool True if the value was inserted, false if the key already exists and is not expired.
51 */
52 public function insert_if_absent( string $key, string $json_value, string $expiration_datetime, string $current_datetime ): bool {
53 global $wpdb;
54
55 $table = $this->get_table_name();
56
57 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table with no caching layer.
58
59 // Remove the row only if it has expired. Non-expired rows are left untouched.
60 $wpdb->query(
61 $wpdb->prepare(
62 'DELETE FROM %i WHERE `key_name` = %s AND `exp` <= %s',
63 $table,
64 $key,
65 $current_datetime,
66 ),
67 );
68
69 // Attempt to insert. INSERT IGNORE silently skips on duplicate key without triggering wpdb errors.
70 $wpdb->query(
71 $wpdb->prepare(
72 'INSERT IGNORE INTO %i (`key_name`, `value`, `exp`) VALUES (%s, %s, %s)',
73 $table,
74 $key,
75 $json_value,
76 $expiration_datetime,
77 ),
78 );
79
80 // phpcs:enable
81
82 return $wpdb->rows_affected === 1;
83 }
84
85 /**
86 * Finds a non-expired value by key.
87 *
88 * @param string $key The key to find.
89 * @param string $current_datetime The current datetime in 'Y-m-d H:i:s' format.
90 *
91 * @return string|null The JSON-encoded value, or null if not found or expired.
92 */
93 public function find( string $key, string $current_datetime ): ?string {
94 global $wpdb;
95
96 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table with no caching layer.
97 $result = $wpdb->get_var(
98 $wpdb->prepare(
99 'SELECT `value` FROM %i WHERE `key_name` = %s AND `exp` > %s',
100 $this->get_table_name(),
101 $key,
102 $current_datetime,
103 ),
104 );
105
106 return ( $result ?? null );
107 }
108
109 /**
110 * Deletes a value by key.
111 *
112 * @param string $key The key to delete.
113 *
114 * @return void
115 */
116 public function delete( string $key ): void {
117 global $wpdb;
118
119 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table with no caching layer.
120 $wpdb->delete(
121 $this->get_table_name(),
122 [ 'key_name' => $key ],
123 [ '%s' ],
124 );
125 }
126
127 /**
128 * Deletes all expired entries.
129 *
130 * @param string $current_datetime The current datetime in 'Y-m-d H:i:s' format.
131 *
132 * @return int The number of deleted rows.
133 */
134 public function delete_expired( string $current_datetime ): int {
135 global $wpdb;
136
137 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- Custom table with no caching layer.
138 $wpdb->query(
139 $wpdb->prepare(
140 'DELETE FROM %i WHERE `exp` <= %s',
141 $this->get_table_name(),
142 $current_datetime,
143 ),
144 );
145
146 return $wpdb->rows_affected;
147 }
148
149 /**
150 * Returns the table name for the expiring store.
151 *
152 * Uses base_prefix so the table is shared across the entire multisite network.
153 *
154 * @return string The table name.
155 */
156 private function get_table_name(): string {
157 global $wpdb;
158
159 return $wpdb->base_prefix . 'yoast_expiring_store';
160 }
161 }
162