diff --git a/scripts/repository/reparse.php b/scripts/repository/reparse.php index c6849f7c79..9a755a0be8 100755 --- a/scripts/repository/reparse.php +++ b/scripts/repository/reparse.php @@ -1,264 +1,265 @@ #!/usr/bin/env php <?php /* - * Copyright 2011 Facebook, Inc. + * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ $root = dirname(dirname(dirname(__FILE__))); require_once $root.'/scripts/__init_script__.php'; phutil_require_module('phutil', 'console'); $is_all = false; $reparse_message = false; $reparse_change = false; $reparse_herald = false; $reparse_owners = false; $reparse_what = false; $force = false; $args = array_slice($argv, 1); foreach ($args as $arg) { if (!strncmp($arg, '--', 2)) { $flag = substr($arg, 2); switch ($flag) { case 'all': $is_all = true; break; case 'message': case 'messages': $reparse_message = true; break; case 'change': case 'changes': $reparse_change = true; break; case 'herald': $reparse_herald = true; break; case 'owners': $reparse_owners = true; break; case 'force': $force = true; break; case 'trace': PhutilServiceProfiler::installEchoListener(); break; case 'help': help(); break; default: usage("Unknown flag '{$arg}'."); + break; } } else { if ($reparse_what) { usage("Specify exactly one thing to reparse."); } $reparse_what = $arg; } } if (!$reparse_what) { usage("Specify a commit or repository to reparse."); } if (!$reparse_message && !$reparse_change && !$reparse_herald && !$reparse_owners) { usage("Specify what information to reparse with --message, --change, ". "--herald, and/or --owners"); } if ($reparse_owners && !$force) { echo phutil_console_wrap( "You are about to recreate the relationship entries between the commits ". "and the packages they touch. This might delete some existing ". "relationship entries for some old commits."); if (!phutil_console_confirm('Are you ready to continue?')) { echo "Cancelled.\n"; exit(1); } } $commits = array(); if ($is_all) { $repository = id(new PhabricatorRepository())->loadOneWhere( 'callsign = %s OR phid = %s', $reparse_what, $reparse_what); if (!$repository) { throw new Exception("Unknown repository '{$reparse_what}'!"); } $commits = id(new PhabricatorRepositoryCommit())->loadAllWhere( 'repositoryID = %d', $repository->getID()); if (!$commits) { throw new Exception("No commits have been discovered in that repository!"); } $callsign = $repository->getCallsign(); } else { $matches = null; if (!preg_match('/r([A-Z]+)([a-z0-9]+)/', $reparse_what, $matches)) { throw new Exception("Can't parse commit identifier!"); } $callsign = $matches[1]; $commit_identifier = $matches[2]; $repository = id(new PhabricatorRepository())->loadOneWhere( 'callsign = %s', $callsign); if (!$repository) { throw new Exception("No repository with callsign '{$callsign}'!"); } $commit = id(new PhabricatorRepositoryCommit())->loadOneWhere( 'repositoryID = %d AND commitIdentifier = %s', $repository->getID(), $commit_identifier); if (!$commit) { throw new Exception( "No matching commit '{$commit_identifier}' in repository '{$callsign}'. ". "(For git and mercurial repositories, you must specify the entire ". "commit hash.)"); } $commits = array($commit); } if ($is_all) { echo phutil_console_format( '**NOTE**: This script will queue tasks to reparse the data. Once the '. 'tasks have been queued, you need to run Taskmaster daemons to execute '. 'them.'); echo "\n\n"; echo "QUEUEING TASKS (".number_format(count($commits))." Commits):\n"; } $tasks = array(); foreach ($commits as $commit) { $classes = array(); switch ($repository->getVersionControlSystem()) { case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT: if ($reparse_message) { $classes[] = 'PhabricatorRepositoryGitCommitMessageParserWorker'; } if ($reparse_change) { $classes[] = 'PhabricatorRepositoryGitCommitChangeParserWorker'; } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL: if ($reparse_message) { $classes[] = 'PhabricatorRepositoryMercurialCommitMessageParserWorker'; } if ($reparse_change) { $classes[] = 'PhabricatorRepositoryMercurialCommitChangeParserWorker'; } break; case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN: if ($reparse_message) { $classes[] = 'PhabricatorRepositorySvnCommitMessageParserWorker'; } if ($reparse_change) { $classes[] = 'PhabricatorRepositorySvnCommitChangeParserWorker'; } break; } if ($reparse_herald) { $classes[] = 'PhabricatorRepositoryCommitHeraldWorker'; } if ($reparse_owners) { $classes[] = 'PhabricatorRepositoryCommitOwnersWorker'; } $spec = array( 'commitID' => $commit->getID(), 'only' => true, ); if ($is_all) { foreach ($classes as $class) { $task = new PhabricatorWorkerTask(); $task->setTaskClass($class); $task->setData($spec); $task->save(); $commit_name = 'r'.$callsign.$commit->getCommitIdentifier(); echo " Queued '{$class}' for commit '{$commit_name}'.\n"; } } else { foreach ($classes as $class) { $worker = newv($class, array($spec)); echo "Running '{$class}'...\n"; $worker->doWork(); } } } echo "\nDone.\n"; function usage($message) { echo "Usage Error: {$message}"; echo "\n\n"; echo "Run 'reparse.php --help' for detailed help.\n"; exit(1); } function help() { $help = <<<EOHELP **SUMMARY** **reparse.php** __what__ __which_parts__ [--trace] [--force] Rerun the Diffusion parser on specific commits and repositories. Mostly useful for debugging changes to Diffusion. __what__: what to reparse __commit__ Reparse one commit. This mode will reparse the commit in-process. --all __repository_callsign__ --all __repository_phid__ Reparse all commits in the specified repository. These modes queue parsers into the task queue, you must run taskmasters to actually do the parses them. __which_parts__: which parts of the thing to reparse __--message__ Reparse commit messages. __--change__ Reparse changes. __--herald__ Reevaluate Herald rules (may send huge amounts of email!) __--owners__ Reevaluate related commits for owners packages (may delete existing relationship entries between your package and some old commits!) __--force__: act noninteractively, without prompting __--trace__: run with debug tracing __--help__: show this help **EXAMPLES** reparse.php rX123 --change # Reparse change for "rX123". reparse.php --all E --message # Reparse all messages in "E" repository. EOHELP; echo phutil_console_format($help); exit(1); } diff --git a/src/applications/phid/handle/PhabricatorObjectHandle.php b/src/applications/phid/handle/PhabricatorObjectHandle.php index 1d3633c51c..95262f5486 100644 --- a/src/applications/phid/handle/PhabricatorObjectHandle.php +++ b/src/applications/phid/handle/PhabricatorObjectHandle.php @@ -1,230 +1,231 @@ <?php /* * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class PhabricatorObjectHandle { private $uri; private $phid; private $type; private $name; private $email; private $fullName; private $imageURI; private $timestamp; private $alternateID; private $status = 'open'; private $complete; private $disabled; public function setURI($uri) { $this->uri = $uri; return $this; } public function getURI() { return $this->uri; } public function setPHID($phid) { $this->phid = $phid; return $this; } public function getPHID() { return $this->phid; } public function setName($name) { $this->name = $name; return $this; } public function getName() { return $this->name; } public function setStatus($status) { $this->status = $status; return $this; } public function getStatus() { return $this->status; } public function setFullName($full_name) { $this->fullName = $full_name; return $this; } public function getFullName() { if ($this->fullName !== null) { return $this->fullName; } return $this->getName(); } public function setType($type) { $this->type = $type; return $this; } public function getType() { return $this->type; } public function setEmail($email) { $this->email = $email; return $this; } public function getEmail() { return $this->email; } public function setImageURI($uri) { $this->imageURI = $uri; return $this; } public function getImageURI() { return $this->imageURI; } public function setTimestamp($timestamp) { $this->timestamp = $timestamp; return $this; } public function getTimestamp() { return $this->timestamp; } public function setAlternateID($alternate_id) { $this->alternateID = $alternate_id; return $this; } public function getAlternateID() { return $this->alternateID; } public function getTypeName() { static $map = array( PhabricatorPHIDConstants::PHID_TYPE_USER => 'User', PhabricatorPHIDConstants::PHID_TYPE_TASK => 'Task', PhabricatorPHIDConstants::PHID_TYPE_DREV => 'Revision', PhabricatorPHIDConstants::PHID_TYPE_CMIT => 'Commit', PhabricatorPHIDConstants::PHID_TYPE_WIKI => 'Phriction', ); return idx($map, $this->getType()); } /** * Set whether or not the underlying object is complete. See * @{method:getComplete} for an explanation of what it means to be complete. * * @param bool True if the handle represents a complete object. * @return this */ public function setComplete($complete) { $this->complete = $complete; return $this; } /** * Determine if the handle represents an object which was completely loaded * (i.e., the underlying object exists) vs an object which could not be * completely loaded (e.g., the type or data for the PHID could not be * identified or located). * * Basically, @{class:PhabricatorObjectHandleData} gives you back a handle for * any PHID you give it, but it gives you a complete handle only for valid * PHIDs. * * @return bool True if the handle represents a complete object. */ public function isComplete() { return $this->complete; } /** * Set whether or not the underlying object is disabled. See * @{method:getDisabled} for an explanation of what it means to be disabled. * * @param bool True if the handle represents a disabled object. * @return this */ public function setDisabled($disabled) { $this->disabled = $disabled; return $this; } /** * Determine if the handle represents an object which has been disabled -- * for example, disabled users, archived projects, etc. These objects are * complete and exist, but should be excluded from some system interactions * (for instance, they usually should not appear in typeaheads, and should * not have mail/notifications delivered to or about them). * * @return bool True if the handle represents a disabled object. */ public function isDisabled() { return $this->disabled; } public function renderLink() { $name = $this->getLinkName(); $class = null; if ($this->status != PhabricatorObjectHandleStatus::STATUS_OPEN) { $class .= ' handle-status-'.$this->status; } if ($this->disabled) { $class .= ' handle-disabled'; } return phutil_render_tag( 'a', array( 'href' => $this->getURI(), 'class' => $class, ), phutil_escape_html($name)); } public function getLinkName() { switch ($this->getType()) { case PhabricatorPHIDConstants::PHID_TYPE_USER: case PhabricatorPHIDConstants::PHID_TYPE_CMIT: $name = $this->getName(); break; default: $name = $this->getFullName(); + break; } return $name; } } diff --git a/src/storage/qsprintf/qsprintf.php b/src/storage/qsprintf/qsprintf.php index c2b41657d1..7fe0ef750d 100644 --- a/src/storage/qsprintf/qsprintf.php +++ b/src/storage/qsprintf/qsprintf.php @@ -1,313 +1,314 @@ <?php /* - * Copyright 2011 Facebook, Inc. + * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Format an SQL query. This function behaves like sprintf(), except that * all the normal conversions (like %s) will be properly escaped, and * additional conversions are supported: * * %nd, %ns, %nf * "Nullable" versions of %d, %s and %f. Will produce 'NULL' if the * argument is a strict null. * * %=d, %=s, %=f * "Nullable Test" versions of %d, %s and %f. If you pass a value, you * get "= 3"; if you pass null, you get "IS NULL". For instance, this * will work properly if `hatID' is a nullable column and $hat is null. * * qsprintf($conn, 'WHERE hatID %=d', $hat); * * %Ld, %Ls, %Lf * "List" versions of %d, %s and %f. These are appropriate for use in * an "IN" clause. For example: * * qsprintf($conn, 'WHERE hatID IN(%Ld)', $list_of_hats); * * %T ("Table") * Escapes a table name. * * %C, %LC * Escapes a column name or a list of column names. * * %K ("Comment") * Escapes a comment. * * %Q ("Query Fragment") * Injects a raw query fragment. Extremely dangerous! Not escaped! * * %~ ("Substring") * Escapes a substring query for a LIKE (or NOT LIKE) clause. For example: * * // Find all rows with $search as a substing of `name`. * qsprintf($conn, 'WHERE name LIKE %~', $search); * * See also %> and %<. * * %> ("Prefix") * Escapes a prefix query for a LIKE clause. For example: * * // Find all rows where `name` starts with $prefix. * qsprintf($conn, 'WHERE name LIKE %>', $prefix); * * %< ("Suffix") * Escapes a suffix query for a LIKE clause. For example: * * // Find all rows where `name` ends with $suffix. * qsprintf($conn, 'WHERE name LIKE %<', $suffix); * * @group storage */ function qsprintf($conn, $pattern/*, ... */) { $args = func_get_args(); array_shift($args); return xsprintf('xsprintf_query', $conn, $args); } /** * @group storage */ function vqsprintf($conn, $pattern, array $argv) { array_unshift($argv, $pattern); return xsprintf('xsprintf_query', $conn, $argv); } /** * xsprintf() callback for encoding SQL queries. See qsprintf(). * @group storage */ function xsprintf_query($userdata, &$pattern, &$pos, &$value, &$length) { $type = $pattern[$pos]; $conn = $userdata; $next = (strlen($pattern) > $pos + 1) ? $pattern[$pos + 1] : null; $nullable = false; $done = false; $prefix = ''; if (!($conn instanceof AphrontDatabaseConnection)) { throw new Exception("Invalid database connection!"); } switch ($type) { case '=': // Nullable test switch ($next) { case 'd': case 'f': case 's': $pattern = substr_replace($pattern, '', $pos, 1); $length = strlen($pattern); $type = 's'; if ($value === null) { $value = 'IS NULL'; $done = true; } else { $prefix = '= '; $type = $next; } break; default: throw new Exception('Unknown conversion, try %=d, %=s, or %=f.'); } break; case 'n': // Nullable... switch ($next) { case 'd': // ...integer. case 'f': // ...float. case 's': // ...string. $pattern = substr_replace($pattern, '', $pos, 1); $length = strlen($pattern); $type = $next; $nullable = true; break; default: throw new Exception('Unknown conversion, try %nd or %ns.'); } break; case 'L': // List of.. _qsprintf_check_type($value, "L{$next}", $pattern); $pattern = substr_replace($pattern, '', $pos, 1); $length = strlen($pattern); $type = 's'; $done = true; switch ($next) { case 'd': // ...integers. $value = implode(', ', array_map('intval', $value)); break; case 's': // ...strings. foreach ($value as $k => $v) { $value[$k] = "'".$conn->escapeString($v)."'"; } $value = implode(', ', $value); break; case 'C': // ...columns. foreach ($value as $k => $v) { $value[$k] = $conn->escapeColumnName($v); } $value = implode(', ', $value); break; default: throw new Exception("Unknown conversion %L{$next}."); } break; } if (!$done) { _qsprintf_check_type($value, $type, $pattern); switch ($type) { case 's': // String if ($nullable && $value === null) { $value = 'NULL'; } else { $value = "'".$conn->escapeString($value)."'"; } $type = 's'; break; case 'Q': // Query Fragment $type = 's'; break; case '~': // Like Substring case '>': // Like Prefix case '<': // Like Suffix $value = $conn->escapeStringForLikeClause($value); switch ($type) { case '~': $value = "'%".$value."%'"; break; case '>': $value = "'" .$value."%'"; break; case '<': $value = "'%".$value. "'"; break; } $type = 's'; break; case 'f': // Float if ($nullable && $value === null) { $value = 'NULL'; } else { $value = (float)$value; } $type = 's'; break; case 'd': // Integer if ($nullable && $value === null) { $value = 'NULL'; } else { $value = (int)$value; } $type = 's'; break; case 'T': // Table case 'C': // Column $value = $conn->escapeColumnName($value); $type = 's'; break; case 'K': // Komment $value = $conn->escapeMultilineComment($value); $type = 's'; break; default: throw new Exception("Unknown conversion '%{$type}'."); } } if ($prefix) { $value = $prefix.$value; } $pattern[$pos] = $type; } /** * @group storage */ function _qsprintf_check_type($value, $type, $query) { switch ($type) { case 'Ld': case 'Ls': case 'LC': case 'LA': case 'LO': if (!is_array($value)) { throw new AphrontQueryParameterException( $query, "Expected array argument for %{$type} conversion."); } if (empty($value)) { throw new AphrontQueryParameterException( $query, "Array for %{$type} conversion is empty."); } foreach ($value as $scalar) { _qsprintf_check_scalar_type($scalar, $type, $query); } break; default: _qsprintf_check_scalar_type($value, $type, $query); + break; } } /** * @group storage */ function _qsprintf_check_scalar_type($value, $type, $query) { switch ($type) { case 'Q': case 'LC': case 'T': case 'C': if (!is_string($value)) { throw new AphrontQueryParameterException( $query, "Expected a string for %{$type} conversion."); } break; case 'Ld': case 'd': case 'f': if (!is_null($value) && !is_numeric($value)) { throw new AphrontQueryParameterException( $query, "Expected a numeric scalar or null for %{$type} conversion."); } break; case 'Ls': case 's': case '~': case '>': case '<': case 'K': if (!is_null($value) && !is_scalar($value)) { throw new AphrontQueryParameterException( $query, "Expected a scalar or null for %{$type} conversion."); } break; case 'LA': case 'LO': if (!is_null($value) && !is_scalar($value) && !(is_array($value) && !empty($value))) { throw new AphrontQueryParameterException( $query, "Expected a scalar or null or non-empty array for ". "%{$type} conversion."); } break; default: throw new Exception("Unknown conversion '{$type}'."); } } diff --git a/webroot/index.php b/webroot/index.php index afcc80fa0b..810ccdcf8b 100644 --- a/webroot/index.php +++ b/webroot/index.php @@ -1,307 +1,307 @@ <?php /* * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ $__start__ = microtime(true); error_reporting(E_ALL | E_STRICT); phabricator_detect_insane_memory_limit(); ini_set('memory_limit', -1); $env = getenv('PHABRICATOR_ENV'); // Apache if (!$env) { if (isset($_ENV['PHABRICATOR_ENV'])) { $env = $_ENV['PHABRICATOR_ENV']; } } if (!$env) { phabricator_fatal_config_error( "The 'PHABRICATOR_ENV' environmental variable is not defined. Modify ". "your httpd.conf to include 'SetEnv PHABRICATOR_ENV <env>', where '<env>' ". "is one of 'development', 'production', or a custom environment."); } if (!function_exists('mysql_connect')) { phabricator_fatal_config_error( "The PHP MySQL extension is not installed. This extension is required."); } if (!isset($_REQUEST['__path__'])) { phabricator_fatal_config_error( "__path__ is not set. Your rewrite rules are not configured correctly."); } if (get_magic_quotes_gpc()) { phabricator_fatal_config_error( "Your server is configured with PHP 'magic_quotes_gpc' enabled. This ". "feature is 'highly discouraged' by PHP's developers and you must ". "disable it to run Phabricator. Consult the PHP manual for instructions."); } register_shutdown_function('phabricator_shutdown'); require_once dirname(dirname(__FILE__)).'/conf/__init_conf__.php'; try { setup_aphront_basics(); $conf = phabricator_read_config_file($env); $conf['phabricator.env'] = $env; phutil_require_module('phabricator', 'infrastructure/env'); PhabricatorEnv::setEnvConfig($conf); phutil_require_module('phabricator', 'aphront/console/plugin/xhprof/api'); DarkConsoleXHProfPluginAPI::hookProfiler(); phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api'); PhutilErrorHandler::initialize(); } catch (Exception $ex) { phabricator_fatal("[Initialization Exception] ".$ex->getMessage()); } $tz = PhabricatorEnv::getEnvConfig('phabricator.timezone'); if ($tz) { date_default_timezone_set($tz); } phutil_require_module('phabricator', 'aphront/console/plugin/errorlog/api'); phutil_require_module('phutil', 'error'); PhutilErrorHandler::setErrorListener( array('DarkConsoleErrorLogPluginAPI', 'handleErrors')); foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) { phutil_load_library($library); } if (PhabricatorEnv::getEnvConfig('phabricator.setup')) { PhabricatorSetup::runSetup(); return; } phabricator_detect_bad_base_uri(); $host = $_SERVER['HTTP_HOST']; $path = $_REQUEST['__path__']; switch ($host) { default: $config_key = 'aphront.default-application-configuration-class'; $config_class = PhabricatorEnv::getEnvConfig($config_key); PhutilSymbolLoader::loadClass($config_class); $application = newv($config_class, array()); break; } $application->setHost($host); $application->setPath($path); $application->willBuildRequest(); $request = $application->buildRequest(); $write_guard = new AphrontWriteGuard($request); PhabricatorEventEngine::initialize(); $application->setRequest($request); list($controller, $uri_data) = $application->buildController(); try { $response = $controller->willBeginExecution(); if (!$response) { $controller->willProcessRequest($uri_data); $response = $controller->processRequest(); } } catch (AphrontRedirectException $ex) { $response = id(new AphrontRedirectResponse()) ->setURI($ex->getURI()); } catch (Exception $ex) { $response = $application->handleException($ex); } try { $response = $application->willSendResponse($response); $response->setRequest($request); $response_string = $response->buildResponseString(); } catch (Exception $ex) { $write_guard->dispose(); phabricator_fatal('[Rendering Exception] '.$ex->getMessage()); } $write_guard->dispose(); // TODO: Share the $sink->writeResponse() pathway here? $sink = new AphrontPHPHTTPSink(); $sink->writeHTTPStatus($response->getHTTPResponseCode()); $headers = $response->getCacheHeaders(); $headers = array_merge($headers, $response->getHeaders()); $sink->writeHeaders($headers); // TODO: This shouldn't be possible in a production-configured environment. if (isset($_REQUEST['__profile__']) && ($_REQUEST['__profile__'] == 'all')) { $profile = DarkConsoleXHProfPluginAPI::stopProfiler(); $profile = '<div style="text-align: center; background: #ff00ff; padding: 1em; font-size: 24px; font-weight: bold;">'. '<a href="/xhprof/profile/'.$profile.'/">'. '>>> View Profile <<<'. '</a>'. '</div>'; if (strpos($response_string, '<body>') !== false) { $response_string = str_replace( '<body>', '<body>'.$profile, $response_string); } else { $sink->writeData($profile); } } $sink->writeData($response_string); /** * @group aphront */ function setup_aphront_basics() { $aphront_root = dirname(dirname(__FILE__)); $libraries_root = dirname($aphront_root); $root = null; if (!empty($_SERVER['PHUTIL_LIBRARY_ROOT'])) { $root = $_SERVER['PHUTIL_LIBRARY_ROOT']; } ini_set('include_path', $libraries_root.':'.ini_get('include_path')); @include_once $root.'libphutil/src/__phutil_library_init__.php'; if (!@constant('__LIBPHUTIL__')) { echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ". "include the parent directory of libphutil/.\n"; exit(1); } // Load Phabricator itself using the absolute path, so we never end up doing // anything surprising (loading index.php and libraries from different // directories). phutil_load_library($aphront_root.'/src'); phutil_load_library('arcanist/src'); } function phabricator_fatal_config_error($msg) { phabricator_fatal("CONFIG ERROR: ".$msg."\n"); } function phabricator_detect_bad_base_uri() { $conf = PhabricatorEnv::getEnvConfig('phabricator.base-uri'); $uri = new PhutilURI($conf); switch ($uri->getProtocol()) { case 'http': case 'https': break; default: - phabricator_fatal_config_error( + return phabricator_fatal_config_error( "'phabricator.base-uri' is set to '{$conf}', which is invalid. ". "The URI must start with 'http://' or 'https://'."); } if (strpos($uri->getDomain(), '.') === false) { phabricator_fatal_config_error( "'phabricator.base-uri' is set to '{$conf}', which is invalid. The URI ". "must contain a dot ('.'), like 'http://example.com/', not just ". "'http://example/'. Some web browsers will not set cookies on domains ". "with no TLD, and Phabricator requires cookies for login. ". "If you are using localhost, create an entry in the hosts file like ". "'127.0.0.1 example.com', and access the localhost with ". "'http://example.com/'."); } } function phabricator_detect_insane_memory_limit() { $memory_limit = ini_get('memory_limit'); $char_limit = 12; if (strlen($memory_limit) <= $char_limit) { return; } // colmdoyle ran into an issue on an Ubuntu box with Suhosin where his // 'memory_limit' was set to: // // 3232323232323232323232323232323232323232323232323232323232323232M // // Not a typo. A wizard did it. // // Anyway, with this 'memory_limit', the machine would immediately fatal // when executing the ini_set() later. I wasn't able to reproduce this on my // EC2 Ubuntu + Suhosin box, but verified that it caused the problem on his // machine and that setting it to a more sensible value fixed it. Since I // have no idea how to actually trigger the issue, we look for a coarse // approximation of it (a memory_limit setting more than 12 characters in // length). phabricator_fatal_config_error( "Your PHP 'memory_limit' is set to something ridiculous ". "(\"{$memory_limit}\"). Set it to a more reasonable value (it must be no ". "more than {$char_limit} characters long)."); } function phabricator_shutdown() { $event = error_get_last(); if (!$event) { return; } if ($event['type'] != E_ERROR && $event['type'] != E_PARSE) { return; } $msg = ">>> UNRECOVERABLE FATAL ERROR <<<\n\n"; if ($event) { // Even though we should be emitting this as text-plain, escape things just // to be sure since we can't really be sure what the program state is when // we get here. $msg .= phutil_escape_html($event['message'])."\n\n"; $msg .= phutil_escape_html($event['file'].':'.$event['line']); } // flip dem tables $msg .= "\n\n\n"; $msg .= "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb\x20\xef\xb8\xb5\x20\xc2\xaf". "\x5c\x5f\x28\xe3\x83\x84\x29\x5f\x2f\xc2\xaf\x20\xef\xb8\xb5\x20". "\xe2\x94\xbb\xe2\x94\x81\xe2\x94\xbb"; phabricator_fatal($msg); } function phabricator_fatal($msg) { header( 'Content-Type: text/plain; charset=utf-8', $replace = true, $http_error = 500); error_log($msg); echo $msg; exit(1); }