@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.

Split setup check phases into "preflight" and "post-config"

Summary:
Ref T11589. This runs:

- preflight checks (critical checks: PHP version stuff, extensions);
- configuration;
- normal checks.

The PHP checks are split into critical ("bad version") and noncritical ("sub-optimal config").

I tidied up the extension checks slightly, we realistically depend on `cURL` nowadays.

Test Plan:
- Faked a preflight failure.
- Hit preflight check.
- Got expected error screen.
- Loaded normal pages.
- Hit a normal setup check.
- Used DarkConsole "Startup" tab to verify that preflight checks take <1ms to run (we run them on every page without caching, at least for now, but they only do trivial checks like PHP versions).

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11589

Differential Revision: https://secure.phabricator.com/D16500

+267 -204
+2
src/__phutil_library_map__.php
··· 2998 2998 'PhabricatorPHPASTApplication' => 'applications/phpast/application/PhabricatorPHPASTApplication.php', 2999 2999 'PhabricatorPHPConfigSetupCheck' => 'applications/config/check/PhabricatorPHPConfigSetupCheck.php', 3000 3000 'PhabricatorPHPMailerConfigOptions' => 'applications/config/option/PhabricatorPHPMailerConfigOptions.php', 3001 + 'PhabricatorPHPPreflightSetupCheck' => 'applications/config/check/PhabricatorPHPPreflightSetupCheck.php', 3001 3002 'PhabricatorPackagesApplication' => 'applications/packages/application/PhabricatorPackagesApplication.php', 3002 3003 'PhabricatorPackagesController' => 'applications/packages/controller/PhabricatorPackagesController.php', 3003 3004 'PhabricatorPackagesCreatePublisherCapability' => 'applications/packages/capability/PhabricatorPackagesCreatePublisherCapability.php', ··· 7855 7856 'PhabricatorPHPASTApplication' => 'PhabricatorApplication', 7856 7857 'PhabricatorPHPConfigSetupCheck' => 'PhabricatorSetupCheck', 7857 7858 'PhabricatorPHPMailerConfigOptions' => 'PhabricatorApplicationConfigOptions', 7859 + 'PhabricatorPHPPreflightSetupCheck' => 'PhabricatorSetupCheck', 7858 7860 'PhabricatorPackagesApplication' => 'PhabricatorApplication', 7859 7861 'PhabricatorPackagesController' => 'PhabricatorController', 7860 7862 'PhabricatorPackagesCreatePublisherCapability' => 'PhabricatorPolicyCapability',
+9
src/aphront/configuration/AphrontApplicationConfiguration.php
··· 69 69 // request object first. 70 70 $write_guard = new AphrontWriteGuard('id'); 71 71 72 + PhabricatorStartup::beginStartupPhase('preflight'); 73 + 74 + $response = PhabricatorSetupCheck::willPreflightRequest(); 75 + if ($response) { 76 + PhabricatorStartup::endOutputCapture(); 77 + $sink->writeResponse($response); 78 + return; 79 + } 80 + 72 81 PhabricatorStartup::beginStartupPhase('env.init'); 73 82 PhabricatorEnv::initializeWebEnvironment(); 74 83
+2 -4
src/applications/config/check/PhabricatorExtensionsSetupCheck.php
··· 12 12 13 13 protected function executeChecks() { 14 14 // TODO: Make 'mbstring' and 'iconv' soft requirements. 15 - // TODO: Make 'curl' a soft requirement. 16 15 17 16 $required = array( 18 17 'hash', ··· 22 21 'iconv', 23 22 'ctype', 24 23 25 - // There is a chance we might not need this, but some configurations (like 26 - // OAuth or Amazon SES) will require it. Just mark it 'required' since 27 - // it's widely available and relatively core. 24 + // There is a tiny chance we might not need this, but a significant 25 + // number of applications require it and it's widely available. 28 26 'curl', 29 27 ); 30 28
+7 -190
src/applications/config/check/PhabricatorPHPConfigSetupCheck.php
··· 1 1 <?php 2 2 3 + /** 4 + * Noncritical PHP configuration checks. 5 + * 6 + * For critical checks, see @{class:PhabricatorPHPPreflightSetupCheck}. 7 + */ 3 8 final class PhabricatorPHPConfigSetupCheck extends PhabricatorSetupCheck { 4 9 5 10 public function getDefaultGroup() { 6 11 return self::GROUP_PHP; 7 12 } 8 13 9 - public function isPreflightCheck() { 10 - return true; 11 - } 12 - 13 14 protected function executeChecks() { 14 - if (version_compare(phpversion(), 7, '>=')) { 15 - $message = pht( 16 - 'This version of Phabricator does not support PHP 7. You '. 17 - 'are running PHP %s.', 18 - phpversion()); 19 - 20 - $this->newIssue('php.version7') 21 - ->setIsFatal(true) 22 - ->setName(pht('PHP 7 Not Supported')) 23 - ->setMessage($message) 24 - ->addLink( 25 - 'https://phurl.io/u/php7', 26 - pht('Phabricator PHP 7 Compatibility Information')); 27 - 28 - return; 29 - } 30 - 31 - $safe_mode = ini_get('safe_mode'); 32 - if ($safe_mode) { 33 - $message = pht( 34 - "You have '%s' enabled in your PHP configuration, but Phabricator ". 35 - "will not run in safe mode. Safe mode has been deprecated in PHP 5.3 ". 36 - "and removed in PHP 5.4.\n\nDisable safe mode to continue.", 37 - 'safe_mode'); 38 - 39 - $this->newIssue('php.safe_mode') 40 - ->setIsFatal(true) 41 - ->setName(pht('Disable PHP %s', 'safe_mode')) 42 - ->setMessage($message) 43 - ->addPHPConfig('safe_mode'); 44 - return; 45 - } 46 - 47 - // Check for `disable_functions` or `disable_classes`. Although it's 48 - // possible to disable a bunch of functions (say, `array_change_key_case()`) 49 - // and classes and still have Phabricator work fine, it's unreasonably 50 - // difficult for us to be sure we'll even survive setup if these options 51 - // are enabled. Phabricator needs access to the most dangerous functions, 52 - // so there is no reasonable configuration value here which actually 53 - // provides a benefit while guaranteeing Phabricator will run properly. 54 - 55 - $disable_options = array('disable_functions', 'disable_classes'); 56 - foreach ($disable_options as $disable_option) { 57 - $disable_value = ini_get($disable_option); 58 - if ($disable_value) { 59 - 60 - // By default Debian installs the pcntl extension but disables all of 61 - // its functions using configuration. Whitelist disabling these 62 - // functions so that Debian PHP works out of the box (we do not need to 63 - // call these functions from the web UI). This is pretty ridiculous but 64 - // it's not the users' fault and they haven't done anything crazy to 65 - // get here, so don't make them pay for Debian's unusual choices. 66 - // See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=605571 67 - $fatal = true; 68 - if ($disable_option == 'disable_functions') { 69 - $functions = preg_split('/[, ]+/', $disable_value); 70 - $functions = array_filter($functions); 71 - foreach ($functions as $k => $function) { 72 - if (preg_match('/^pcntl_/', $function)) { 73 - unset($functions[$k]); 74 - } 75 - } 76 - if (!$functions) { 77 - $fatal = false; 78 - } 79 - } 80 - 81 - if ($fatal) { 82 - $message = pht( 83 - "You have '%s' enabled in your PHP configuration.\n\n". 84 - "This option is not compatible with Phabricator. Remove ". 85 - "'%s' from your configuration to continue.", 86 - $disable_option, 87 - $disable_option); 88 - 89 - $this->newIssue('php.'.$disable_option) 90 - ->setIsFatal(true) 91 - ->setName(pht('Remove PHP %s', $disable_option)) 92 - ->setMessage($message) 93 - ->addPHPConfig($disable_option); 94 - } 95 - } 96 - } 97 - 98 - $overload_option = 'mbstring.func_overload'; 99 - $func_overload = ini_get($overload_option); 100 - if ($func_overload) { 101 - $message = pht( 102 - "You have '%s' enabled in your PHP configuration.\n\n". 103 - "This option is not compatible with Phabricator. Disable ". 104 - "'%s' in your PHP configuration to continue.", 105 - $overload_option, 106 - $overload_option); 107 - 108 - $this->newIssue('php'.$overload_option) 109 - ->setIsFatal(true) 110 - ->setName(pht('Disable PHP %s', $overload_option)) 111 - ->setMessage($message) 112 - ->addPHPConfig($overload_option); 113 - } 114 - 115 - $open_basedir = ini_get('open_basedir'); 116 - if ($open_basedir) { 117 - 118 - // 'open_basedir' restricts which files we're allowed to access with 119 - // file operations. This might be okay -- we don't need to write to 120 - // arbitrary places in the filesystem -- but we need to access certain 121 - // resources. This setting is unlikely to be providing any real measure 122 - // of security so warn even if things look OK. 123 - 124 - $failures = array(); 125 - 126 - try { 127 - $open_libphutil = class_exists('Future'); 128 - } catch (Exception $ex) { 129 - $failures[] = $ex->getMessage(); 130 - } 131 - 132 - try { 133 - $open_arcanist = class_exists('ArcanistDiffParser'); 134 - } catch (Exception $ex) { 135 - $failures[] = $ex->getMessage(); 136 - } 137 - 138 - $open_urandom = false; 139 - try { 140 - Filesystem::readRandomBytes(1); 141 - $open_urandom = true; 142 - } catch (FilesystemException $ex) { 143 - $failures[] = $ex->getMessage(); 144 - } 145 - 146 - try { 147 - $tmp = new TempFile(); 148 - file_put_contents($tmp, '.'); 149 - $open_tmp = @fopen((string)$tmp, 'r'); 150 - if (!$open_tmp) { 151 - $failures[] = pht( 152 - "Unable to read temporary file '%s'.", 153 - (string)$tmp); 154 - } 155 - } catch (Exception $ex) { 156 - $message = $ex->getMessage(); 157 - $dir = sys_get_temp_dir(); 158 - $failures[] = pht( 159 - "Unable to open temp files from '%s': %s", 160 - $dir, 161 - $message); 162 - } 163 - 164 - $issue = $this->newIssue('php.open_basedir') 165 - ->setName(pht('Disable PHP %s', 'open_basedir')) 166 - ->addPHPConfig('open_basedir'); 167 - 168 - if ($failures) { 169 - $message = pht( 170 - "Your server is configured with '%s', which prevents Phabricator ". 171 - "from opening files it requires access to.\n\n". 172 - "Disable this setting to continue.\n\nFailures:\n\n%s", 173 - 'open_basedir', 174 - implode("\n\n", $failures)); 175 - 176 - $issue 177 - ->setIsFatal(true) 178 - ->setMessage($message); 179 - 180 - return; 181 - } else { 182 - $summary = pht( 183 - "You have '%s' configured in your PHP settings, which ". 184 - "may cause some features to fail.", 185 - 'open_basedir'); 186 - 187 - $message = pht( 188 - "You have '%s' configured in your PHP settings. Although this ". 189 - "setting appears permissive enough that Phabricator will work ". 190 - "properly, you may still run into problems because of it.\n\n". 191 - "Consider disabling '%s'.", 192 - 'open_basedir', 193 - 'open_basedir'); 194 - 195 - $issue 196 - ->setSummary($summary) 197 - ->setMessage($message); 198 - } 199 - } 200 15 201 16 if (empty($_SERVER['REMOTE_ADDR'])) { 202 17 $doc_href = PhabricatorEnv::getDocLink('Configuring a Preamble Script'); ··· 245 60 ->setMessage($message) 246 61 ->addPHPConfig('always_populate_raw_post_data'); 247 62 } 63 + 248 64 } 65 + 249 66 }
+201
src/applications/config/check/PhabricatorPHPPreflightSetupCheck.php
··· 1 + <?php 2 + 3 + final class PhabricatorPHPPreflightSetupCheck extends PhabricatorSetupCheck { 4 + 5 + public function getDefaultGroup() { 6 + return self::GROUP_PHP; 7 + } 8 + 9 + public function isPreflightCheck() { 10 + return true; 11 + } 12 + 13 + protected function executeChecks() { 14 + if (version_compare(phpversion(), 7, '>=')) { 15 + $message = pht( 16 + 'This version of Phabricator does not support PHP 7. You '. 17 + 'are running PHP %s.', 18 + phpversion()); 19 + 20 + $this->newIssue('php.version7') 21 + ->setIsFatal(true) 22 + ->setName(pht('PHP 7 Not Supported')) 23 + ->setMessage($message) 24 + ->addLink( 25 + 'https://phurl.io/u/php7', 26 + pht('Phabricator PHP 7 Compatibility Information')); 27 + 28 + return; 29 + } 30 + 31 + $safe_mode = ini_get('safe_mode'); 32 + if ($safe_mode) { 33 + $message = pht( 34 + "You have '%s' enabled in your PHP configuration, but Phabricator ". 35 + "will not run in safe mode. Safe mode has been deprecated in PHP 5.3 ". 36 + "and removed in PHP 5.4.\n\nDisable safe mode to continue.", 37 + 'safe_mode'); 38 + 39 + $this->newIssue('php.safe_mode') 40 + ->setIsFatal(true) 41 + ->setName(pht('Disable PHP %s', 'safe_mode')) 42 + ->setMessage($message) 43 + ->addPHPConfig('safe_mode'); 44 + return; 45 + } 46 + 47 + // Check for `disable_functions` or `disable_classes`. Although it's 48 + // possible to disable a bunch of functions (say, `array_change_key_case()`) 49 + // and classes and still have Phabricator work fine, it's unreasonably 50 + // difficult for us to be sure we'll even survive setup if these options 51 + // are enabled. Phabricator needs access to the most dangerous functions, 52 + // so there is no reasonable configuration value here which actually 53 + // provides a benefit while guaranteeing Phabricator will run properly. 54 + 55 + $disable_options = array('disable_functions', 'disable_classes'); 56 + foreach ($disable_options as $disable_option) { 57 + $disable_value = ini_get($disable_option); 58 + if ($disable_value) { 59 + 60 + // By default Debian installs the pcntl extension but disables all of 61 + // its functions using configuration. Whitelist disabling these 62 + // functions so that Debian PHP works out of the box (we do not need to 63 + // call these functions from the web UI). This is pretty ridiculous but 64 + // it's not the users' fault and they haven't done anything crazy to 65 + // get here, so don't make them pay for Debian's unusual choices. 66 + // See: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=605571 67 + $fatal = true; 68 + if ($disable_option == 'disable_functions') { 69 + $functions = preg_split('/[, ]+/', $disable_value); 70 + $functions = array_filter($functions); 71 + foreach ($functions as $k => $function) { 72 + if (preg_match('/^pcntl_/', $function)) { 73 + unset($functions[$k]); 74 + } 75 + } 76 + if (!$functions) { 77 + $fatal = false; 78 + } 79 + } 80 + 81 + if ($fatal) { 82 + $message = pht( 83 + "You have '%s' enabled in your PHP configuration.\n\n". 84 + "This option is not compatible with Phabricator. Remove ". 85 + "'%s' from your configuration to continue.", 86 + $disable_option, 87 + $disable_option); 88 + 89 + $this->newIssue('php.'.$disable_option) 90 + ->setIsFatal(true) 91 + ->setName(pht('Remove PHP %s', $disable_option)) 92 + ->setMessage($message) 93 + ->addPHPConfig($disable_option); 94 + } 95 + } 96 + } 97 + 98 + $overload_option = 'mbstring.func_overload'; 99 + $func_overload = ini_get($overload_option); 100 + if ($func_overload) { 101 + $message = pht( 102 + "You have '%s' enabled in your PHP configuration.\n\n". 103 + "This option is not compatible with Phabricator. Disable ". 104 + "'%s' in your PHP configuration to continue.", 105 + $overload_option, 106 + $overload_option); 107 + 108 + $this->newIssue('php'.$overload_option) 109 + ->setIsFatal(true) 110 + ->setName(pht('Disable PHP %s', $overload_option)) 111 + ->setMessage($message) 112 + ->addPHPConfig($overload_option); 113 + } 114 + 115 + $open_basedir = ini_get('open_basedir'); 116 + if ($open_basedir) { 117 + 118 + // 'open_basedir' restricts which files we're allowed to access with 119 + // file operations. This might be okay -- we don't need to write to 120 + // arbitrary places in the filesystem -- but we need to access certain 121 + // resources. This setting is unlikely to be providing any real measure 122 + // of security so warn even if things look OK. 123 + 124 + $failures = array(); 125 + 126 + try { 127 + $open_libphutil = class_exists('Future'); 128 + } catch (Exception $ex) { 129 + $failures[] = $ex->getMessage(); 130 + } 131 + 132 + try { 133 + $open_arcanist = class_exists('ArcanistDiffParser'); 134 + } catch (Exception $ex) { 135 + $failures[] = $ex->getMessage(); 136 + } 137 + 138 + $open_urandom = false; 139 + try { 140 + Filesystem::readRandomBytes(1); 141 + $open_urandom = true; 142 + } catch (FilesystemException $ex) { 143 + $failures[] = $ex->getMessage(); 144 + } 145 + 146 + try { 147 + $tmp = new TempFile(); 148 + file_put_contents($tmp, '.'); 149 + $open_tmp = @fopen((string)$tmp, 'r'); 150 + if (!$open_tmp) { 151 + $failures[] = pht( 152 + "Unable to read temporary file '%s'.", 153 + (string)$tmp); 154 + } 155 + } catch (Exception $ex) { 156 + $message = $ex->getMessage(); 157 + $dir = sys_get_temp_dir(); 158 + $failures[] = pht( 159 + "Unable to open temp files from '%s': %s", 160 + $dir, 161 + $message); 162 + } 163 + 164 + $issue = $this->newIssue('php.open_basedir') 165 + ->setName(pht('Disable PHP %s', 'open_basedir')) 166 + ->addPHPConfig('open_basedir'); 167 + 168 + if ($failures) { 169 + $message = pht( 170 + "Your server is configured with '%s', which prevents Phabricator ". 171 + "from opening files it requires access to.\n\n". 172 + "Disable this setting to continue.\n\nFailures:\n\n%s", 173 + 'open_basedir', 174 + implode("\n\n", $failures)); 175 + 176 + $issue 177 + ->setIsFatal(true) 178 + ->setMessage($message); 179 + 180 + return; 181 + } else { 182 + $summary = pht( 183 + "You have '%s' configured in your PHP settings, which ". 184 + "may cause some features to fail.", 185 + 'open_basedir'); 186 + 187 + $message = pht( 188 + "You have '%s' configured in your PHP settings. Although this ". 189 + "setting appears permissive enough that Phabricator will work ". 190 + "properly, you may still run into problems because of it.\n\n". 191 + "Consider disabling '%s'.", 192 + 'open_basedir', 193 + 'open_basedir'); 194 + 195 + $issue 196 + ->setSummary($summary) 197 + ->setMessage($message); 198 + } 199 + } 200 + } 201 + }
+35 -6
src/applications/config/check/PhabricatorSetupCheck.php
··· 129 129 )); 130 130 } 131 131 132 + final public static function willPreflightRequest() { 133 + $checks = self::loadAllChecks(); 134 + 135 + foreach ($checks as $check) { 136 + if (!$check->isPreflightCheck()) { 137 + continue; 138 + } 139 + 140 + $check->runSetupChecks(); 141 + 142 + foreach ($check->getIssues() as $key => $issue) { 143 + return self::newIssueResponse($issue); 144 + } 145 + } 146 + 147 + return null; 148 + } 149 + 150 + private static function newIssueResponse(PhabricatorSetupIssue $issue) { 151 + $view = id(new PhabricatorSetupIssueView()) 152 + ->setIssue($issue); 153 + 154 + return id(new PhabricatorConfigResponse()) 155 + ->setView($view); 156 + } 157 + 132 158 final public static function willProcessRequest() { 133 159 $issue_keys = self::getOpenSetupIssueKeys(); 134 160 if ($issue_keys === null) { 135 - $issues = self::runAllChecks(); 161 + $issues = self::runNormalChecks(); 136 162 foreach ($issues as $issue) { 137 163 if ($issue->getIsFatal()) { 138 - $view = id(new PhabricatorSetupIssueView()) 139 - ->setIssue($issue); 140 - return id(new PhabricatorConfigResponse()) 141 - ->setView($view); 164 + return self::newIssueResponse($issue); 142 165 } 143 166 } 144 167 $issue_keys = self::getUnignoredIssueKeys($issues); ··· 176 199 ->execute(); 177 200 } 178 201 179 - final public static function runAllChecks() { 202 + final public static function runNormalChecks() { 180 203 $checks = self::loadAllChecks(); 204 + 205 + foreach ($checks as $key => $check) { 206 + if ($check->isPreflightCheck()) { 207 + unset($checks[$key]); 208 + } 209 + } 181 210 182 211 $issues = array(); 183 212 foreach ($checks as $check) {
+1 -1
src/applications/config/controller/PhabricatorConfigIssueListController.php
··· 9 9 $nav = $this->buildSideNavView(); 10 10 $nav->selectFilter('issue/'); 11 11 12 - $issues = PhabricatorSetupCheck::runAllChecks(); 12 + $issues = PhabricatorSetupCheck::runNormalChecks(); 13 13 PhabricatorSetupCheck::setOpenSetupIssueKeys( 14 14 PhabricatorSetupCheck::getUnignoredIssueKeys($issues), 15 15 $update_database = true);
+1 -1
src/applications/config/controller/PhabricatorConfigIssuePanelController.php
··· 6 6 public function handleRequest(AphrontRequest $request) { 7 7 $viewer = $request->getViewer(); 8 8 $open_items = PhabricatorSetupCheck::getOpenSetupIssueKeys(); 9 - $issues = PhabricatorSetupCheck::runAllChecks(); 9 + $issues = PhabricatorSetupCheck::runNormalChecks(); 10 10 PhabricatorSetupCheck::setOpenSetupIssueKeys( 11 11 PhabricatorSetupCheck::getUnignoredIssueKeys($issues), 12 12 $update_database = true);
+1 -1
src/applications/config/controller/PhabricatorConfigIssueViewController.php
··· 7 7 $viewer = $request->getViewer(); 8 8 $issue_key = $request->getURIData('key'); 9 9 10 - $issues = PhabricatorSetupCheck::runAllChecks(); 10 + $issues = PhabricatorSetupCheck::runNormalChecks(); 11 11 PhabricatorSetupCheck::setOpenSetupIssueKeys( 12 12 PhabricatorSetupCheck::getUnignoredIssueKeys($issues), 13 13 $update_database = true);
+8 -1
src/infrastructure/env/PhabricatorEnv.php
··· 315 315 return self::$cache[$key]; 316 316 } 317 317 318 + if (!self::$sourceStack) { 319 + throw new Exception( 320 + pht( 321 + 'Trying to read configuration "%s" before configuration has been '. 322 + 'initialized.', 323 + $key)); 324 + } 325 + 318 326 $result = self::$sourceStack->getKeys(array($key)); 319 327 if (array_key_exists($key, $result)) { 320 328 self::$cache[$key] = $result[$key]; ··· 326 334 $key)); 327 335 } 328 336 } 329 - 330 337 331 338 /** 332 339 * Get the current configuration setting for a given key. If the key