Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/config/option/PhabricatorApplicationConfigOptions.php b/src/applications/config/option/PhabricatorApplicationConfigOptions.php
index c08758de3e..39d66f7f5e 100644
--- a/src/applications/config/option/PhabricatorApplicationConfigOptions.php
+++ b/src/applications/config/option/PhabricatorApplicationConfigOptions.php
@@ -1,186 +1,190 @@
<?php
abstract class PhabricatorApplicationConfigOptions extends Phobject {
abstract public function getName();
abstract public function getDescription();
abstract public function getOptions();
public function validateOption(PhabricatorConfigOption $option, $value) {
if ($value === $option->getDefault()) {
return;
}
if ($value === null) {
return;
}
switch ($option->getType()) {
case 'bool':
if ($value !== true &&
$value !== false) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' is of type bool, but value is not true or false.",
$option->getKey()));
}
break;
case 'int':
if (!is_int($value)) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' is of type int, but value is not an integer.",
$option->getKey()));
}
break;
case 'string':
if (!is_string($value)) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' is of type string, but value is not a string.",
$option->getKey()));
}
break;
case 'class':
$symbols = id(new PhutilSymbolLoader())
->setType('class')
->setAncestorClass($option->getBaseClass())
->setConcreteOnly(true)
->selectSymbolsWithoutLoading();
$names = ipull($symbols, 'name', 'name');
if (empty($names[$value])) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' value must name a class extending '%s'.",
$option->getKey(),
$option->getBaseClass()));
}
break;
case 'set':
$valid = true;
if (!is_array($value)) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' must be a set, but value is not an array.",
$option->getKey()));
}
foreach ($value as $v) {
if ($v !== true) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' must be a set, but array contains values other ".
"than 'true'.",
$option->getKey()));
}
}
break;
case 'list<string>':
$valid = true;
if (!is_array($value)) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' must be a list of strings, but value is not ".
"an array.",
$option->getKey()));
}
if ($value && array_keys($value) != range(0, count($value) - 1)) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' must be a list of strings, but the value is a ".
"map with unnatural keys.",
$option->getKey()));
}
foreach ($value as $v) {
if (!is_string($v)) {
throw new PhabricatorConfigValidationException(
pht(
"Option '%s' must be a list of strings, but it contains one ".
"or more non-strings.",
$option->getKey()));
}
}
break;
case 'wild':
default:
break;
}
$this->didValidateOption($option, $value);
}
protected function didValidateOption(
PhabricatorConfigOption $option,
$value) {
// Hook for subclasses to do complex validation.
return;
}
public function getKey() {
$class = get_class($this);
$matches = null;
if (preg_match('/^Phabricator(.*)ConfigOptions$/', $class, $matches)) {
return strtolower($matches[1]);
}
return strtolower(get_class($this));
}
final protected function newOption($key, $type, $default) {
return id(new PhabricatorConfigOption())
->setKey($key)
->setType($type)
->setDefault($default)
->setGroup($this);
}
- final public static function loadAll() {
+ final public static function loadAll($external_only = false) {
$symbols = id(new PhutilSymbolLoader())
->setAncestorClass('PhabricatorApplicationConfigOptions')
->setConcreteOnly(true)
->selectAndLoadSymbols();
$groups = array();
foreach ($symbols as $symbol) {
+ if ($external_only && $symbol['library'] == 'phabricator') {
+ continue;
+ }
+
$obj = newv($symbol['name'], array());
$key = $obj->getKey();
if (isset($groups[$key])) {
$pclass = get_class($groups[$key]);
$nclass = $symbol['name'];
throw new Exception(
"Multiple PhabricatorApplicationConfigOptions subclasses have the ".
"same key ('{$key}'): {$pclass}, {$nclass}.");
}
$groups[$key] = $obj;
}
return $groups;
}
- final public static function loadAllOptions() {
- $groups = self::loadAll();
+ final public static function loadAllOptions($external_only = false) {
+ $groups = self::loadAll($external_only);
$options = array();
foreach ($groups as $group) {
foreach ($group->getOptions() as $option) {
$key = $option->getKey();
if (isset($options[$key])) {
throw new Exception(
"Mulitple PhabricatorApplicationConfigOptions subclasses contain ".
"an option named '{$key}'!");
}
$options[$key] = $option;
}
}
return $options;
}
/**
* Deformat a HEREDOC for use in remarkup by converting line breaks to
* spaces.
*/
final protected function deformat($string) {
return preg_replace('/(?<=\S)\n(?=\S)/', ' ', $string);
}
}
diff --git a/src/infrastructure/env/PhabricatorConfigDefaultSource.php b/src/infrastructure/env/PhabricatorConfigDefaultSource.php
index e8fc3bad13..0316c778ee 100644
--- a/src/infrastructure/env/PhabricatorConfigDefaultSource.php
+++ b/src/infrastructure/env/PhabricatorConfigDefaultSource.php
@@ -1,16 +1,22 @@
<?php
/**
* Configuration source which reads from defaults defined in the authoritative
* configuration definitions.
*/
final class PhabricatorConfigDefaultSource
extends PhabricatorConfigProxySource {
public function __construct() {
$options = PhabricatorApplicationConfigOptions::loadAllOptions();
$options = mpull($options, 'getDefault');
$this->setSource(new PhabricatorConfigDictionarySource($options));
}
+ public function loadExternalOptions() {
+ $options = PhabricatorApplicationConfigOptions::loadAllOptions(true);
+ $options = mpull($options, 'getDefault');
+ $this->setKeys($options);
+ }
+
}
diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php
index 09d801c6c1..79c6b40c49 100644
--- a/src/infrastructure/env/PhabricatorEnv.php
+++ b/src/infrastructure/env/PhabricatorEnv.php
@@ -1,454 +1,459 @@
<?php
/**
* Manages the execution environment configuration, exposing APIs to read
* configuration settings and other similar values that are derived directly
* from configuration settings.
*
*
* = Reading Configuration =
*
* The primary role of this class is to provide an API for reading
* Phabricator configuration, @{method:getEnvConfig}:
*
* $value = PhabricatorEnv::getEnvConfig('some.key', $default);
*
* The class also handles some URI construction based on configuration, via
* the methods @{method:getURI}, @{method:getProductionURI},
* @{method:getCDNURI}, and @{method:getDoclink}.
*
* For configuration which allows you to choose a class to be responsible for
* some functionality (e.g., which mail adapter to use to deliver email),
* @{method:newObjectFromConfig} provides a simple interface that validates
* the configured value.
*
*
* = Unit Test Support =
*
* In unit tests, you can use @{method:beginScopedEnv} to create a temporary,
* mutable environment. The method returns a scope guard object which restores
* the environment when it is destroyed. For example:
*
* public function testExample() {
* $env = PhabricatorEnv::beginScopedEnv();
* $env->overrideEnv('some.key', 'new-value-for-this-test');
*
* // Some test which depends on the value of 'some.key'.
*
* }
*
* Your changes will persist until the `$env` object leaves scope or is
* destroyed.
*
* You should //not// use this in normal code.
*
*
* @task read Reading Configuration
* @task uri URI Validation
* @task test Unit Test Support
* @task internal Internals
*/
final class PhabricatorEnv {
private static $sourceStack;
private static $repairSource;
/**
* @phutil-external-symbol class PhabricatorStartup
*/
public static function initializeWebEnvironment() {
$env = self::getSelectedEnvironmentName();
if (!$env) {
PhabricatorStartup::didFatal(
"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.");
}
self::initializeCommonEnvironment();
}
public static function initializeScriptEnvironment() {
$env = self::getSelectedEnvironmentName();
if (!$env) {
echo phutil_console_wrap(
phutil_console_format(
"**ERROR**: PHABRICATOR_ENV Not Set\n\n".
"Define the __PHABRICATOR_ENV__ environment variable before ".
"running this script. You can do it on the command line like ".
"this:\n\n".
" $ PHABRICATOR_ENV=__custom/myconfig__ %s ...\n\n".
"Replace __custom/myconfig__ with the path to your configuration ".
"file. For more information, see the 'Configuration Guide' in the ".
"Phabricator documentation.\n\n",
$GLOBALS['argv'][0]));
exit(1);
}
self::initializeCommonEnvironment();
// NOTE: This is dangerous in general, but we know we're in a script context
// and are not vulnerable to CSRF.
AphrontWriteGuard::allowDangerousUnguardedWrites(true);
// There are several places where we log information (about errors, events,
// service calls, etc.) for analysis via DarkConsole or similar. These are
// useful for web requests, but grow unboundedly in long-running scripts and
// daemons. Discard data as it arrives in these cases.
PhutilServiceProfiler::getInstance()->enableDiscardMode();
DarkConsoleErrorLogPluginAPI::enableDiscardMode();
DarkConsoleEventPluginAPI::enableDiscardMode();
}
private static function initializeCommonEnvironment() {
PhutilErrorHandler::initialize();
self::buildConfigurationSourceStack();
$tz = PhabricatorEnv::getEnvConfig('phabricator.timezone');
if ($tz) {
date_default_timezone_set($tz);
}
// Append any paths to $PATH if we need to.
$paths = PhabricatorEnv::getEnvConfig('environment.append-paths');
if (!empty($paths)) {
$current_env_path = getenv('PATH');
$new_env_paths = implode(PATH_SEPARATOR, $paths);
putenv('PATH='.$current_env_path.PATH_SEPARATOR.$new_env_paths);
}
PhabricatorEventEngine::initialize();
$translation = PhabricatorEnv::newObjectFromConfig('translation.provider');
PhutilTranslator::getInstance()
->setLanguage($translation->getLanguage())
->addTranslations($translation->getTranslations());
}
private static function buildConfigurationSourceStack() {
$stack = new PhabricatorConfigStackSource();
self::$sourceStack = $stack;
- $stack->pushSource(
- id(new PhabricatorConfigDefaultSource())
- ->setName(pht('Global Default')));
+ $defaultSource = id(new PhabricatorConfigDefaultSource())
+ ->setName(pht('Global Default'));
+ $stack->pushSource($defaultSource);
$env = self::getSelectedEnvironmentName();
$stack->pushSource(
id(new PhabricatorConfigFileSource($env))
->setName(pht("File '%s'", $env)));
$stack->pushSource(
id(new PhabricatorConfigLocalSource())
->setName(pht("Local Config")));
// If the install overrides the database adapter, we might need to load
// the database adapter class before we can push on the database config.
// This config is locked and can't be edited from the web UI anyway.
foreach (PhabricatorEnv::getEnvConfig('load-libraries') as $library) {
phutil_load_library($library);
}
+ // If custom libraries specify config options, they won't get default
+ // values as the Default source has already been loaded, so we get it to
+ // pull in all options from non-phabricator libraries now they are loaded.
+ $defaultSource->loadExternalOptions();
+
try {
$stack->pushSource(
id(new PhabricatorConfigDatabaseSource('default'))
->setName(pht("Database")));
} catch (AphrontQueryException $exception) {
// If the database is not available, just skip this configuration
// source. This happens during `bin/storage upgrade`, `bin/conf` before
// schema setup, etc.
}
}
public static function repairConfig($key, $value) {
if (!self::$repairSource) {
self::$repairSource = id(new PhabricatorConfigDictionarySource(array()))
->setName(pht("Repaired Config"));
self::$sourceStack->pushSource(self::$repairSource);
}
self::$repairSource->setKeys(array($key => $value));
}
public static function getUnrepairedEnvConfig($key, $default = null) {
foreach (self::$sourceStack->getStack() as $source) {
if ($source === self::$repairSource) {
continue;
}
$result = $source->getKeys(array($key));
if ($result) {
return $result[$key];
}
}
return $default;
}
public static function getSelectedEnvironmentName() {
$env_var = 'PHABRICATOR_ENV';
$env = idx($_SERVER, $env_var);
if (!$env) {
$env = getenv($env_var);
}
if (!$env) {
$env = idx($_ENV, $env_var);
}
if (!$env) {
$root = dirname(phutil_get_library_root('phabricator'));
$path = $root.'/conf/local/ENVIRONMENT';
if (Filesystem::pathExists($path)) {
$env = trim(Filesystem::readFile($path));
}
}
return $env;
}
/* -( Reading Configuration )---------------------------------------------- */
/**
* Get the current configuration setting for a given key.
*
* @task read
*/
public static function getEnvConfig($key, $default = null) {
$result = self::$sourceStack->getKeys(array($key));
return idx($result, $key, $default);
}
/**
* Get the fully-qualified URI for a path.
*
* @task read
*/
public static function getURI($path) {
return rtrim(self::getEnvConfig('phabricator.base-uri'), '/').$path;
}
/**
* Get the fully-qualified production URI for a path.
*
* @task read
*/
public static function getProductionURI($path) {
// If we're passed a URI which already has a domain, simply return it
// unmodified. In particular, files may have URIs which point to a CDN
// domain.
$uri = new PhutilURI($path);
if ($uri->getDomain()) {
return $path;
}
$production_domain = self::getEnvConfig('phabricator.production-uri');
if (!$production_domain) {
$production_domain = self::getEnvConfig('phabricator.base-uri');
}
return rtrim($production_domain, '/').$path;
}
/**
* Get the fully-qualified production URI for a static resource path.
*
* @task read
*/
public static function getCDNURI($path) {
$alt = self::getEnvConfig('security.alternate-file-domain');
if (!$alt) {
$alt = self::getEnvConfig('phabricator.base-uri');
}
$uri = new PhutilURI($alt);
$uri->setPath($path);
return (string)$uri;
}
/**
* Get the fully-qualified production URI for a documentation resource.
*
* @task read
*/
public static function getDoclink($resource) {
return 'http://www.phabricator.com/docs/phabricator/'.$resource;
}
/**
* Build a concrete object from a configuration key.
*
* @task read
*/
public static function newObjectFromConfig($key, $args = array()) {
$class = self::getEnvConfig($key);
return newv($class, $args);
}
/* -( Unit Test Support )-------------------------------------------------- */
/**
* @task test
*/
public static function beginScopedEnv() {
return new PhabricatorScopedEnv(self::pushTestEnvironment());
}
/**
* @task test
*/
private static function pushTestEnvironment() {
$source = new PhabricatorConfigDictionarySource(array());
self::$sourceStack->pushSource($source);
return spl_object_hash($source);
}
/**
* @task test
*/
public static function popTestEnvironment($key) {
$source = self::$sourceStack->popSource();
$stack_key = spl_object_hash($source);
if ($stack_key !== $key) {
self::$sourceStack->pushSource($source);
throw new Exception(
"Scoped environments were destroyed in a diffent order than they ".
"were initialized.");
}
}
/* -( URI Validation )----------------------------------------------------- */
/**
* Detect if a URI satisfies either @{method:isValidLocalWebResource} or
* @{method:isValidRemoteWebResource}, i.e. is a page on this server or the
* URI of some other resource which has a valid protocol. This rejects
* garbage URIs and URIs with protocols which do not appear in the
* ##uri.allowed-protocols## configuration, notably 'javascript:' URIs.
*
* NOTE: This method is generally intended to reject URIs which it may be
* unsafe to put in an "href" link attribute.
*
* @param string URI to test.
* @return bool True if the URI identifies a web resource.
* @task uri
*/
public static function isValidWebResource($uri) {
return self::isValidLocalWebResource($uri) ||
self::isValidRemoteWebResource($uri);
}
/**
* Detect if a URI identifies some page on this server.
*
* NOTE: This method is generally intended to reject URIs which it may be
* unsafe to issue a "Location:" redirect to.
*
* @param string URI to test.
* @return bool True if the URI identifies a local page.
* @task uri
*/
public static function isValidLocalWebResource($uri) {
$uri = (string)$uri;
if (!strlen($uri)) {
return false;
}
if (preg_match('/\s/', $uri)) {
// PHP hasn't been vulnerable to header injection attacks for a bunch of
// years, but we can safely reject these anyway since they're never valid.
return false;
}
// Valid URIs must begin with '/', followed by the end of the string or some
// other non-'/' character. This rejects protocol-relative URIs like
// "//evil.com/evil_stuff/".
return (bool)preg_match('@^/([^/]|$)@', $uri);
}
/**
* Detect if a URI identifies some valid remote resource.
*
* @param string URI to test.
* @return bool True if a URI idenfies a remote resource with an allowed
* protocol.
* @task uri
*/
public static function isValidRemoteWebResource($uri) {
$uri = (string)$uri;
$proto = id(new PhutilURI($uri))->getProtocol();
if (!$proto) {
return false;
}
$allowed = self::getEnvConfig('uri.allowed-protocols');
if (empty($allowed[$proto])) {
return false;
}
return true;
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
public static function envConfigExists($key) {
return array_key_exists($key, self::$sourceStack->getKeys(array($key)));
}
/**
* @task internal
*/
public static function getAllConfigKeys() {
return self::$sourceStack->getAllKeys();
}
public static function getConfigSourceStack() {
return self::$sourceStack;
}
/**
* @task internal
*/
public static function overrideTestEnvConfig($stack_key, $key, $value) {
$tmp = array();
// If we don't have the right key, we'll throw when popping the last
// source off the stack.
do {
$source = self::$sourceStack->popSource();
array_unshift($tmp, $source);
if (spl_object_hash($source) == $stack_key) {
$source->setKeys(array($key => $value));
break;
}
} while (true);
foreach ($tmp as $source) {
self::$sourceStack->pushSource($source);
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, Jul 24, 8:22 AM (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
182651
Default Alt Text
(21 KB)

Event Timeline