PluginProbe
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification / 5.5.0
Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification v5.5.0
5.5.0 5.4.0 5.3.2 5.3.1 5.1.6 5.1.5 trunk 2.1.5 2.11 2.12 2.13 2.15 3.0.0 3.0.1 3.0.2 3.0.3 3.0.5 3.0.51 3.0.60 3.0.61 3.0.62 3.0.70 3.0.71 3.0.72 3.1.0 All 34 releases
double-opt-in / src / Health / DatabaseTableHealthCheck.php

DatabaseTableHealthCheck.php in Double Opt-In for Contact Form 7 – Secure, GDPR-Compliant Email Verification 5.5.0, at src/Health/DatabaseTableHealthCheck.php

182 lines 4.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Health check: a required database table exists.
4 *
5 * @package Forge12\DoubleOptIn\Health
6 * @since 5.3.0
7 */
8
9 declare( strict_types=1 );
10
11 namespace Forge12\DoubleOptIn\Health;
12
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 /**
18 * Verifies that one `$wpdb->prefix`-scoped table is present.
19 *
20 * Reusable on purpose: the opt-out table was not the first table this
21 * project shipped without an install path, and it will not be the last.
22 * Any package that owns a table registers one of these instead of
23 * writing its own probe.
24 *
25 * Cost: a single `SHOW TABLES LIKE`, memoised per table name for the
26 * duration of the request. That is deliberate — Action Scheduler's
27 * missing-table incidents (woocommerce/action-scheduler#744) are the
28 * cautionary tale for probing storage on every page load.
29 */
30 final class DatabaseTableHealthCheck implements HealthCheckInterface {
31
32 /**
33 * Per-request memo, keyed by fully-prefixed table name.
34 *
35 * @var array<string,bool>
36 */
37 private static $existsCache = array();
38
39 /**
40 * @var string
41 */
42 private $id;
43
44 /**
45 * @var string
46 */
47 private $package;
48
49 /**
50 * Table name WITHOUT the `$wpdb->prefix`.
51 *
52 * @var string
53 */
54 private $tableSuffix;
55
56 /**
57 * What breaks when the table is missing, in the operator's terms.
58 *
59 * @var string
60 */
61 private $featureLabel;
62
63 /**
64 * @var string
65 */
66 private $actionLabel;
67
68 /**
69 * @var string
70 */
71 private $actionUrl;
72
73 public function __construct(
74 string $id,
75 string $package,
76 string $tableSuffix,
77 string $featureLabel,
78 string $actionLabel = '',
79 string $actionUrl = ''
80 ) {
81 $this->id = $id;
82 $this->package = $package;
83 $this->tableSuffix = $tableSuffix;
84 $this->featureLabel = $featureLabel;
85 $this->actionLabel = $actionLabel;
86 $this->actionUrl = $actionUrl;
87 }
88
89 public function getId(): string {
90 return $this->id;
91 }
92
93 public function getLabel(): string {
94 return $this->featureLabel;
95 }
96
97 public function getPackage(): string {
98 return $this->package;
99 }
100
101 /**
102 * Fully-prefixed table name.
103 */
104 public function getTableName(): string {
105 global $wpdb;
106
107 return $wpdb->prefix . $this->tableSuffix;
108 }
109
110 /**
111 * Probe the table, memoised per request.
112 */
113 public function tableExists(): bool {
114 $table = $this->getTableName();
115
116 if ( array_key_exists( $table, self::$existsCache ) ) {
117 return self::$existsCache[ $table ];
118 }
119
120 global $wpdb;
121
122 // `SHOW TABLES LIKE` takes a pattern, so the name has to go
123 // through esc_like() before prepare() — otherwise a prefix
124 // containing `_` would match more broadly than intended.
125 $found = $wpdb->get_var(
126 $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table ) )
127 );
128
129 self::$existsCache[ $table ] = ( $found === $table );
130
131 return self::$existsCache[ $table ];
132 }
133
134 /**
135 * Drop the memo. Only needed after a table was just created inside
136 * the same request — otherwise the check would keep reporting the
137 * pre-repair state.
138 */
139 public static function flushCache(): void {
140 self::$existsCache = array();
141 }
142
143 public function run(): HealthCheckResult {
144 $table = $this->getTableName();
145
146 if ( $this->tableExists() ) {
147 return new HealthCheckResult(
148 HealthCheckResult::STATUS_GOOD,
149 sprintf(
150 /* translators: %s: human-readable feature name, e.g. "Opt-out self-service". */
151 __( '%s: the database table is present', 'double-opt-in' ),
152 $this->featureLabel
153 ),
154 sprintf(
155 /* translators: %s: database table name. */
156 __( 'The table %s exists and is writable by the plugin.', 'double-opt-in' ),
157 $table
158 ),
159 'ok'
160 );
161 }
162
163 return new HealthCheckResult(
164 HealthCheckResult::STATUS_CRITICAL,
165 sprintf(
166 /* translators: %s: human-readable feature name, e.g. "Opt-out self-service". */
167 __( '%s: a required database table is missing', 'double-opt-in' ),
168 $this->featureLabel
169 ),
170 sprintf(
171 /* translators: 1: database table name, 2: human-readable feature name. */
172 __( 'The table %1$s does not exist, so "%2$s" cannot store anything and will fail for your visitors. Double Opt-In tries to create the table automatically whenever an administrator opens the WordPress admin. If this message persists, the database user is most likely missing the CREATE privilege — your hosting provider can grant it.', 'double-opt-in' ),
173 $table,
174 $this->featureLabel
175 ),
176 'missing',
177 $this->actionLabel,
178 $this->actionUrl
179 );
180 }
181 }
182