@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at recaptime-dev/main 304 lines 9.1 kB view raw
1<?php 2 3abstract class PhabricatorSetupCheck extends Phobject { 4 5 private $issues; 6 7 abstract protected function executeChecks(); 8 9 const GROUP_OTHER = 'other'; 10 const GROUP_MYSQL = 'mysql'; 11 const GROUP_PHP = 'php'; 12 const GROUP_IMPORTANT = 'important'; 13 14 public function getExecutionOrder() { 15 if ($this->isPreflightCheck()) { 16 return 0; 17 } else { 18 return 1000; 19 } 20 } 21 22 /** 23 * Should this check execute before we load configuration? 24 * 25 * The majority of checks (particularly, those checks which examine 26 * configuration) should run in the normal setup phase, after configuration 27 * loads. However, a small set of critical checks (mostly, tests for PHP 28 * setup and extensions) need to run before we can load configuration. 29 * 30 * @return bool True to execute before configuration is loaded. 31 */ 32 public function isPreflightCheck() { 33 return false; 34 } 35 36 final protected function newIssue($key) { 37 $issue = id(new PhabricatorSetupIssue()) 38 ->setIssueKey($key); 39 $this->issues[$key] = $issue; 40 41 if ($this->getDefaultGroup()) { 42 $issue->setGroup($this->getDefaultGroup()); 43 } 44 45 return $issue; 46 } 47 48 final public function getIssues() { 49 return $this->issues; 50 } 51 52 protected function addIssue(PhabricatorSetupIssue $issue) { 53 $this->issues[$issue->getIssueKey()] = $issue; 54 return $this; 55 } 56 57 public function getDefaultGroup() { 58 return null; 59 } 60 61 final public function runSetupChecks() { 62 $this->issues = array(); 63 $this->executeChecks(); 64 } 65 66 final public static function getOpenSetupIssueKeys() { 67 $cache = PhabricatorCaches::getSetupCache(); 68 return $cache->getKey('phabricator.setup.issue-keys'); 69 } 70 71 final public static function resetSetupState() { 72 $cache = PhabricatorCaches::getSetupCache(); 73 $cache->deleteKey('phabricator.setup.issue-keys'); 74 75 $server_cache = PhabricatorCaches::getServerStateCache(); 76 $server_cache->deleteKey('phabricator.in-flight'); 77 78 $use_scope = AphrontWriteGuard::isGuardActive(); 79 if ($use_scope) { 80 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 81 } else { 82 AphrontWriteGuard::allowDangerousUnguardedWrites(true); 83 } 84 85 try { 86 $db_cache = new PhabricatorKeyValueDatabaseCache(); 87 $db_cache->deleteKey('phabricator.setup.issue-keys'); 88 } catch (Exception $ex) { 89 // If we hit an exception here, just ignore it. In particular, this can 90 // happen on initial startup before the databases are initialized. 91 } 92 93 if ($use_scope) { 94 unset($unguarded); 95 } else { 96 AphrontWriteGuard::allowDangerousUnguardedWrites(false); 97 } 98 } 99 100 final public static function setOpenSetupIssueKeys( 101 array $keys, 102 $update_database) { 103 $cache = PhabricatorCaches::getSetupCache(); 104 $cache->setKey('phabricator.setup.issue-keys', $keys); 105 106 $server_cache = PhabricatorCaches::getServerStateCache(); 107 $server_cache->setKey('phabricator.in-flight', 1); 108 109 if ($update_database) { 110 $db_cache = new PhabricatorKeyValueDatabaseCache(); 111 try { 112 $json = phutil_json_encode($keys); 113 $db_cache->setKey('phabricator.setup.issue-keys', $json); 114 } catch (Exception $ex) { 115 // Ignore any write failures, since they likely just indicate that we 116 // have a database-related setup issue that needs to be resolved. 117 } 118 } 119 } 120 121 final public static function getOpenSetupIssueKeysFromDatabase() { 122 $db_cache = new PhabricatorKeyValueDatabaseCache(); 123 try { 124 $value = $db_cache->getKey('phabricator.setup.issue-keys'); 125 if (!phutil_nonempty_string($value)) { 126 return null; 127 } 128 return phutil_json_decode($value); 129 } catch (Exception $ex) { 130 return null; 131 } 132 } 133 134 /** 135 * @param array<PhabricatorSetupIssue> $all_issues 136 */ 137 final public static function getUnignoredIssueKeys(array $all_issues) { 138 assert_instances_of($all_issues, PhabricatorSetupIssue::class); 139 $keys = array(); 140 foreach ($all_issues as $issue) { 141 if (!$issue->getIsIgnored()) { 142 $keys[] = $issue->getIssueKey(); 143 } 144 } 145 return $keys; 146 } 147 148 final public static function getConfigNeedsRepair() { 149 $cache = PhabricatorCaches::getSetupCache(); 150 return $cache->getKey('phabricator.setup.needs-repair'); 151 } 152 153 final public static function setConfigNeedsRepair($needs_repair) { 154 $cache = PhabricatorCaches::getSetupCache(); 155 $cache->setKey('phabricator.setup.needs-repair', $needs_repair); 156 } 157 158 final public static function deleteSetupCheckCache() { 159 $cache = PhabricatorCaches::getSetupCache(); 160 $cache->deleteKeys( 161 array( 162 'phabricator.setup.needs-repair', 163 'phabricator.setup.issue-keys', 164 )); 165 } 166 167 final public static function willPreflightRequest() { 168 $checks = self::loadAllChecks(); 169 170 foreach ($checks as $check) { 171 if (!$check->isPreflightCheck()) { 172 continue; 173 } 174 175 $check->runSetupChecks(); 176 177 foreach ($check->getIssues() as $key => $issue) { 178 return self::newIssueResponse($issue); 179 } 180 } 181 182 return null; 183 } 184 185 public static function newIssueResponse(PhabricatorSetupIssue $issue) { 186 $view = id(new PhabricatorSetupIssueView()) 187 ->setIssue($issue); 188 189 return id(new PhabricatorConfigResponse()) 190 ->setView($view); 191 } 192 193 final public static function willProcessRequest() { 194 $issue_keys = self::getOpenSetupIssueKeys(); 195 if ($issue_keys === null) { 196 $engine = new PhabricatorSetupEngine(); 197 $response = $engine->execute(); 198 if ($response) { 199 return $response; 200 } 201 } else if ($issue_keys) { 202 // If Phabricator is configured in a cluster with multiple web devices, 203 // we can end up with setup issues cached on every device. This can cause 204 // a warning banner to show on every device so that each one needs to 205 // be dismissed individually, which is pretty annoying. See T10876. 206 207 // To avoid this, check if the issues we found have already been cleared 208 // in the database. If they have, we'll just wipe out our own cache and 209 // move on. 210 $issue_keys = self::getOpenSetupIssueKeysFromDatabase(); 211 if ($issue_keys !== null) { 212 self::setOpenSetupIssueKeys($issue_keys, $update_database = false); 213 } 214 } 215 216 // Try to repair configuration unless we have a clean bill of health on it. 217 // We need to keep doing this on every page load until all the problems 218 // are fixed, which is why it's separate from setup checks (which run 219 // once per restart). 220 $needs_repair = self::getConfigNeedsRepair(); 221 if ($needs_repair !== false) { 222 $needs_repair = self::repairConfig(); 223 self::setConfigNeedsRepair($needs_repair); 224 } 225 } 226 227 /** 228 * Test if we've survived through setup on at least one normal request 229 * without fataling. 230 * 231 * If we've made it through setup without hitting any fatals, we switch 232 * to render a more friendly error page when encountering issues like 233 * database connection failures. This gives users a smoother experience in 234 * the face of intermittent failures. 235 * 236 * @return bool True if we've made it through setup since the last restart. 237 */ 238 final public static function isInFlight() { 239 $cache = PhabricatorCaches::getServerStateCache(); 240 return (bool)$cache->getKey('phabricator.in-flight'); 241 } 242 243 final public static function loadAllChecks() { 244 return id(new PhutilClassMapQuery()) 245 ->setAncestorClass(self::class) 246 ->setSortMethod('getExecutionOrder') 247 ->execute(); 248 } 249 250 final public static function runNormalChecks() { 251 $checks = self::loadAllChecks(); 252 253 foreach ($checks as $key => $check) { 254 if ($check->isPreflightCheck()) { 255 unset($checks[$key]); 256 } 257 } 258 259 $issues = array(); 260 foreach ($checks as $check) { 261 $check->runSetupChecks(); 262 foreach ($check->getIssues() as $key => $issue) { 263 if (isset($issues[$key])) { 264 throw new Exception( 265 pht( 266 "Two setup checks raised an issue with key '%s'!", 267 $key)); 268 } 269 $issues[$key] = $issue; 270 if ($issue->getIsFatal()) { 271 break 2; 272 } 273 } 274 } 275 276 $ignore_issues = PhabricatorEnv::getEnvConfig('config.ignore-issues'); 277 foreach ($ignore_issues as $ignorable => $derp) { 278 if (isset($issues[$ignorable])) { 279 $issues[$ignorable]->setIsIgnored(true); 280 } 281 } 282 283 return $issues; 284 } 285 286 final public static function repairConfig() { 287 $needs_repair = false; 288 289 $options = PhabricatorApplicationConfigOptions::loadAllOptions(); 290 foreach ($options as $option) { 291 try { 292 $option->getGroup()->validateOption( 293 $option, 294 PhabricatorEnv::getEnvConfig($option->getKey())); 295 } catch (PhabricatorConfigValidationException $ex) { 296 PhabricatorEnv::repairConfig($option->getKey(), $option->getDefault()); 297 $needs_repair = true; 298 } 299 } 300 301 return $needs_repair; 302 } 303 304}