PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / database / TableReadinessGate.php

TableReadinessGate.php in 404 Solution trunk, at includes/database/TableReadinessGate.php

85 lines 3.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 require_once __DIR__ . '/DatabaseTableNameResolver.php';
8
9 /**
10 * Decides whether a read may be skipped because its table is definitely absent.
11 *
12 * A fresh install genuinely does not have the plugin's tables until the upgrade
13 * runs, so callers that would otherwise aggregate over them ask here first.
14 * The whole point of this class is the distinction the underlying probe makes
15 * and a boolean cannot: SHOW TABLES LIKE returns nothing both when the engine
16 * looked and found nothing AND when the query never ran at all -- a lost
17 * connection, a revoked SHOW grant, a driver that does not speak the statement.
18 *
19 * Only an engine that ANSWERED "no such table" earns a suppressed read. A probe
20 * that could not be answered reports NOT absent, so the caller goes ahead: if
21 * the table really is gone that costs one error the centralized handler already
22 * knows how to log, repair and retry, which is strictly better than a confident
23 * wrong answer produced by a query nobody ran and therefore nobody logged.
24 *
25 * Keeping that policy here rather than in each caller is what makes the wrong
26 * reading unavailable: the tri-state constants stay an implementation detail,
27 * and a caller can only ask the question whose answer is safe to act on.
28 *
29 * A table observed PRESENT is remembered for the life of this instance, keyed
30 * by its fully-resolved name so a multisite blog switch cannot reuse another
31 * blog's answer. Any other outcome is deliberately re-probed, so an in-request
32 * repair can make later reads available without rebuilding the service graph.
33 *
34 * This is a schema-metadata collaborator: it issues one metadata probe and
35 * holds no business or presentation logic.
36 */
37 class ABJ_404_Solution_TableReadinessGate {
38
39 /** @var ABJ_404_Solution_DatabaseQueryInterface */
40 private $dbCore;
41
42 /** @var ABJ_404_Solution_DatabaseTableNameResolver */
43 private $tableNameResolver;
44
45 /** @var array<string, bool> Resolved table name => observed present. */
46 private $observedPresent = array();
47
48 /**
49 * @param ABJ_404_Solution_DatabaseQueryInterface $dbCore Supplies table-name replacement.
50 * @param ABJ_404_Solution_DatabaseTableNameResolver $tableNameResolver Runs the probe.
51 */
52 public function __construct(
53 ABJ_404_Solution_DatabaseQueryInterface $dbCore,
54 ABJ_404_Solution_DatabaseTableNameResolver $tableNameResolver
55 ) {
56 $this->dbCore = $dbCore;
57 $this->tableNameResolver = $tableNameResolver;
58 }
59
60 /**
61 * Whether the engine positively reported this table as not existing.
62 *
63 * Asked in the negative on purpose, so that the answer a caller acts on is
64 * the only one that is safe to act on. False means "not known to be
65 * absent", which covers both a table that is there and a probe that could
66 * not be answered -- in both cases the caller should do its work.
67 *
68 * @param string $tableToken Unresolved token, e.g. '{wp_abj404_redirects}'.
69 */
70 public function isKnownAbsent(string $tableToken): bool {
71 $table = $this->dbCore->doTableNameReplacements($tableToken);
72 if (!empty($this->observedPresent[$table])) {
73 return false;
74 }
75
76 $status = $this->tableNameResolver->tableExistenceStatus($table);
77 if ($status === ABJ_404_Solution_DatabaseTableNameResolver::TABLE_PRESENT) {
78 $this->observedPresent[$table] = true;
79 return false;
80 }
81
82 return $status === ABJ_404_Solution_DatabaseTableNameResolver::TABLE_ABSENT;
83 }
84 }
85