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

Move a lot of pre-request checks to PhabricatorStartup

Summary:
We have a lot of mess to get through before we can load libphutil and enter Phabricator code properly. Move it to a dedicated class.

I'm probably going to merge PhabricatorRequestOverseer into this, although the check that lives there now is kind of weird. It also does not really need to be a pre-load check and could be handled better.

I stopped shoving stuff in here once I got to ENV stuff, I'm going to tackle that next.

Test Plan: Ran phabricator normally; introduced fatals and misconfigurations. Grepped for changed symbols.

Reviewers: btrahan, vrana

Reviewed By: btrahan

CC: aran, asherkin

Maniphest Tasks: T2223

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

+241 -114
+4 -1
src/aphront/console/plugin/DarkConsoleServicesPlugin.php
··· 15 15 return 'Information about services.'; 16 16 } 17 17 18 + /** 19 + * @phutil-external-symbol class PhabricatorStartup 20 + */ 18 21 public function generateData() { 19 22 20 23 $log = PhutilServiceProfiler::getInstance()->getServiceCallLog(); ··· 130 133 } 131 134 132 135 return array( 133 - 'start' => $GLOBALS['__start__'], 136 + 'start' => PhabricatorStartup::getStartTime(), 134 137 'end' => microtime(true), 135 138 'log' => $log, 136 139 );
+3 -11
src/infrastructure/PhabricatorRequestOverseer.php
··· 19 19 * to the documentation the stream isn't available for "multipart/form-data" 20 20 * (on nginx + php-fpm it appears that it is available, though, at least) so 21 21 * any attempt to generate $_POST would be fragile. 22 + * 23 + * @phutil-external-symbol class PhabricatorStartup 22 24 */ 23 25 private function detectPostMaxSizeTriggered() { 24 26 // If this wasn't a POST, we're fine. ··· 83 85 // populated into $_POST, but it wasn't. 84 86 85 87 $config = ini_get('post_max_size'); 86 - $this->fatal( 88 + PhabricatorStartup::didFatal( 87 89 "As received by the server, this request had a nonzero content length ". 88 90 "but no POST data.\n\n". 89 91 "Normally, this indicates that it exceeds the 'post_max_size' setting ". ··· 91 93 "setting or reduce the size of the request.\n\n". 92 94 "Request size according to 'Content-Length' was '{$length}', ". 93 95 "'post_max_size' is set to '{$config}'."); 94 - } 95 - 96 - /** 97 - * Defined in webroot/index.php. 98 - * TODO: Move here. 99 - * 100 - * @phutil-external-symbol function phabricator_fatal 101 - */ 102 - public function fatal($message) { 103 - phabricator_fatal('FATAL ERROR: '.$message); 104 96 } 105 97 106 98 }
+222
support/PhabricatorStartup.php
··· 1 + <?php 2 + 3 + /** 4 + * Handle request startup, before loading the environment or libraries. This 5 + * class bootstraps the request state up to the point where we can enter 6 + * Phabricator code. 7 + * 8 + * NOTE: This class MUST NOT have any dependencies. It runs before libraries 9 + * load. 10 + * 11 + * @task info Accessing Request Information 12 + * @task hook Startup Hooks 13 + * @task apocalypse In Case Of Apocalypse 14 + * @task validation Validation 15 + */ 16 + final class PhabricatorStartup { 17 + 18 + private static $startTime; 19 + private static $globals = array(); 20 + 21 + 22 + /* -( Accessing Request Information )-------------------------------------- */ 23 + 24 + 25 + /** 26 + * @task info 27 + */ 28 + public static function getStartTime() { 29 + return self::$startTime; 30 + } 31 + 32 + 33 + /** 34 + * @task info 35 + */ 36 + public static function setGlobal($key, $value) { 37 + self::validateGlobal($key); 38 + 39 + self::$globals[$key] = $value; 40 + } 41 + 42 + 43 + /** 44 + * @task info 45 + */ 46 + public static function getGlobal($key, $default = null) { 47 + self::validateGlobal($key); 48 + 49 + if (!array_key_exists($key, self::$globals)) { 50 + return $default; 51 + } 52 + return self::$globals[$key]; 53 + } 54 + 55 + 56 + /* -( Startup Hooks )------------------------------------------------------ */ 57 + 58 + 59 + /** 60 + * @task hook 61 + */ 62 + public static function didStartup() { 63 + self::$startTime = microtime(true); 64 + self::$globals = array(); 65 + 66 + self::setupPHP(); 67 + self::verifyPHP(); 68 + 69 + self::verifyRewriteRules(); 70 + 71 + static $registered; 72 + if (!$registered) { 73 + // NOTE: This protects us against multiple calls to didStartup() in the 74 + // same request, but also against repeated requests to the same 75 + // interpreter state, which we may implement in the future. 76 + register_shutdown_function(array(__CLASS__, 'didShutdown')); 77 + $registered = true; 78 + } 79 + } 80 + 81 + 82 + /** 83 + * @task hook 84 + */ 85 + public static function didShutdown() { 86 + $event = error_get_last(); 87 + 88 + if (!$event) { 89 + return; 90 + } 91 + 92 + switch ($event['type']) { 93 + case E_ERROR: 94 + case E_PARSE: 95 + case E_COMPILE_ERROR: 96 + break; 97 + default: 98 + return; 99 + } 100 + 101 + $msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n"; 102 + if ($event) { 103 + // Even though we should be emitting this as text-plain, escape things 104 + // just to be sure since we can't really be sure what the program state 105 + // is when we get here. 106 + $msg .= htmlspecialchars( 107 + $event['message']."\n\n".$event['file'].':'.$event['line'], 108 + ENT_QUOTES, 109 + 'UTF-8'); 110 + } 111 + 112 + // flip dem tables 113 + $msg .= "\n\n\n"; 114 + $msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf". 115 + "\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20". 116 + "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb"; 117 + 118 + self::didFatal($msg); 119 + } 120 + 121 + 122 + /* -( In Case of Apocalypse )---------------------------------------------- */ 123 + 124 + 125 + /** 126 + * @task apocalypse 127 + */ 128 + public static function didFatal($message) { 129 + $access_log = self::getGlobal('log.access'); 130 + 131 + if ($access_log) { 132 + try { 133 + $access_log->setData( 134 + array( 135 + 'c' => 500, 136 + )); 137 + $access_log->write(); 138 + } catch (Exception $ex) { 139 + $message .= "\n(Moreover, unable to write to access log.)"; 140 + } 141 + } 142 + 143 + header( 144 + 'Content-Type: text/plain; charset=utf-8', 145 + $replace = true, 146 + $http_error = 500); 147 + 148 + error_log($message); 149 + echo $message; 150 + 151 + exit(1); 152 + } 153 + 154 + 155 + /* -( Validation )--------------------------------------------------------- */ 156 + 157 + 158 + /** 159 + * @task valiation 160 + */ 161 + private static function setupPHP() { 162 + error_reporting(E_ALL | E_STRICT); 163 + ini_set('memory_limit', -1); 164 + } 165 + 166 + 167 + /** 168 + * @task valiation 169 + */ 170 + private static function verifyPHP() { 171 + $required_version = '5.2.3'; 172 + if (version_compare(PHP_VERSION, $required_version) < 0) { 173 + self::didFatal( 174 + "You are running PHP version '".PHP_VERSION."', which is older than ". 175 + "the minimum version, '{$required_version}'. Update to at least ". 176 + "'{$required_version}'."); 177 + } 178 + 179 + if (get_magic_quotes_gpc()) { 180 + self::didFatal( 181 + "Your server is configured with PHP 'magic_quotes_gpc' enabled. This ". 182 + "feature is 'highly discouraged' by PHP's developers and you must ". 183 + "disable it to run Phabricator. Consult the PHP manual for ". 184 + "instructions."); 185 + } 186 + } 187 + 188 + 189 + /** 190 + * @task valiation 191 + */ 192 + private static function verifyRewriteRules() { 193 + if (isset($_REQUEST['__path__'])) { 194 + return; 195 + } 196 + 197 + if (php_sapi_name() == 'cli-server') { 198 + // Compatibility with PHP 5.4+ built-in web server. 199 + $url = parse_url($_SERVER['REQUEST_URI']); 200 + $_REQUEST['__path__'] = $url['path']; 201 + } else { 202 + self::didFatal( 203 + "Request parameter '__path__' is not set. Your rewrite rules ". 204 + "are not configured correctly."); 205 + } 206 + } 207 + 208 + 209 + /** 210 + * @task valiation 211 + */ 212 + private static function validateGlobal($key) { 213 + static $globals = array( 214 + 'log.access' => true, 215 + ); 216 + 217 + if (empty($globals[$key])) { 218 + throw new Exception("Access to unknown startup global '{$key}'!"); 219 + } 220 + } 221 + 222 + }
+12 -102
webroot/index.php
··· 1 1 <?php 2 2 3 - $__start__ = microtime(true); 4 - $access_log = null; 3 + require_once dirname(dirname(__FILE__)).'/support/PhabricatorStartup.php'; 4 + PhabricatorStartup::didStartup(); 5 5 6 - error_reporting(E_ALL | E_STRICT); 7 - 8 - $required_version = '5.2.3'; 9 - if (version_compare(PHP_VERSION, $required_version) < 0) { 10 - phabricator_fatal_config_error( 11 - "You are running PHP version '".PHP_VERSION."', which is older than ". 12 - "the minimum version, '{$required_version}'. Update to at least ". 13 - "'{$required_version}'."); 14 - } 15 - 16 - ini_set('memory_limit', -1); 6 + $access_log = null; 17 7 18 8 $env = getenv('PHABRICATOR_ENV'); // Apache 19 9 if (!$env) { ··· 23 13 } 24 14 25 15 if (!$env) { 26 - phabricator_fatal_config_error( 16 + PhabricatorStartup::didFatal( 27 17 "The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ". 28 18 "your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ". 29 19 "is one of 'development', 'production', or a custom environment."); 30 20 } 31 21 32 - if (!isset($_REQUEST['__path__'])) { 33 - if (php_sapi_name() == 'cli-server') { 34 - // Compatibility with PHP 5.4+ built-in web server. 35 - $url = parse_url($_SERVER['REQUEST_URI']); 36 - $_REQUEST['__path__'] = $url['path']; 37 - } else { 38 - phabricator_fatal_config_error( 39 - "__path__ is not set. Your rewrite rules are not configured correctly."); 40 - } 41 - } 42 - 43 - if (get_magic_quotes_gpc()) { 44 - phabricator_fatal_config_error( 45 - "Your server is configured with PHP 'magic_quotes_gpc' enabled. This ". 46 - "feature is 'highly discouraged' by PHP's developers and you must ". 47 - "disable it to run Phabricator. Consult the PHP manual for instructions."); 48 - } 49 - 50 - register_shutdown_function('phabricator_shutdown'); 51 22 52 23 require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php'; 53 24 ··· 81 52 PhabricatorAccessLog::init(); 82 53 $access_log = PhabricatorAccessLog::getLog(); 83 54 if ($access_log) { 55 + PhabricatorStartup::setGlobal('log.access', $access_log); 84 56 $access_log->setData( 85 57 array( 86 58 'R' => idx($_SERVER, 'HTTP_REFERER', '-'), ··· 193 165 $ex, 194 166 )); 195 167 } 196 - phabricator_fatal('[Rendering Exception] '.$ex->getMessage()); 168 + PhabricatorStartup::didFatal('[Rendering Exception] '.$ex->getMessage()); 197 169 } 198 170 199 171 $write_guard->dispose(); ··· 211 183 $sink->writeData($response_string); 212 184 213 185 if ($access_log) { 186 + $request_start = PhabricatorStartup::getStartTime(); 214 187 $access_log->setData( 215 188 array( 216 189 'c' => $response->getHTTPResponseCode(), 217 - 'T' => (int)(1000000 * (microtime(true) - $__start__)), 190 + 'T' => (int)(1000000 * (microtime(true) - $request_start)), 218 191 )); 219 192 $access_log->write(); 220 193 } ··· 240 213 } 241 214 242 215 } catch (Exception $ex) { 243 - phabricator_fatal("[Exception] ".$ex->getMessage()); 216 + PhabricatorStartup::didFatal("[Exception] ".$ex->getMessage()); 244 217 } 245 218 246 219 ··· 275 248 276 249 } 277 250 278 - function phabricator_fatal_config_error($msg) { 279 - phabricator_fatal("CONFIG ERROR: ".$msg."\n"); 280 - } 281 - 282 251 function phabricator_detect_bad_base_uri() { 283 252 $conf = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); 284 253 $uri = new PhutilURI($conf); ··· 287 256 case 'https': 288 257 break; 289 258 default: 290 - return phabricator_fatal_config_error( 259 + PhabricatorStartup::didFatal( 291 260 "'phabricator.base-uri' is set to '{$conf}', which is invalid. ". 292 261 "The URI must start with 'http://' or 'https://'."); 262 + return; 293 263 } 294 264 295 265 if (strpos($uri->getDomain(), '.') === false) { 296 - phabricator_fatal_config_error( 266 + PhabricatorStartup::didFatal( 297 267 "'phabricator.base-uri' is set to '{$conf}', which is invalid. The URI ". 298 268 "must contain a dot ('.'), like 'http://example.com/', not just ". 299 269 "'http://example/'. Some web browsers will not set cookies on domains ". ··· 304 274 } 305 275 } 306 276 307 - function phabricator_shutdown() { 308 - $event = error_get_last(); 309 - 310 - if (!$event) { 311 - return; 312 - } 313 - 314 - switch ($event['type']) { 315 - case E_ERROR: 316 - case E_PARSE: 317 - case E_COMPILE_ERROR: 318 - break; 319 - default: 320 - return; 321 - } 322 - 323 - $msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n"; 324 - if ($event) { 325 - // Even though we should be emitting this as text-plain, escape things just 326 - // to be sure since we can't really be sure what the program state is when 327 - // we get here. 328 - $msg .= phutil_escape_html($event['message'])."\n\n"; 329 - $msg .= phutil_escape_html($event['file'].':'.$event['line']); 330 - } 331 - 332 - // flip dem tables 333 - $msg .= "\n\n\n"; 334 - $msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf". 335 - "\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20". 336 - "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb"; 337 - 338 - phabricator_fatal($msg); 339 - } 340 - 341 - function phabricator_fatal($msg) { 342 - 343 - global $access_log; 344 - if ($access_log) { 345 - try { 346 - $access_log->setData( 347 - array( 348 - 'c' => 500, 349 - )); 350 - $access_log->write(); 351 - } catch (Exception $ex) { 352 - $msg .= "\nMoreover unable to write to access log."; 353 - } 354 - } 355 - 356 - header( 357 - 'Content-Type: text/plain; charset=utf-8', 358 - $replace = true, 359 - $http_error = 500); 360 - 361 - error_log($msg); 362 - echo $msg; 363 - 364 - exit(1); 365 - } 366 -