diff --git a/boot.php b/boot.php index 7dc99bbe88..4be12c6a2f 100644 --- a/boot.php +++ b/boot.php @@ -19,18 +19,12 @@ use Friendica\App; use Friendica\BaseObject; -use Friendica\Core\Addon; -use Friendica\Core\Cache; use Friendica\Core\Config; -use Friendica\Core\L10n; use Friendica\Core\PConfig; use Friendica\Core\Protocol; -use Friendica\Core\System; -use Friendica\Core\Update; -use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Model\Contact; -use Friendica\Model\Conversation; +use Friendica\Util\BasePath; use Friendica\Util\DateTimeFormat; define('FRIENDICA_PLATFORM', 'Friendica'); @@ -642,7 +636,7 @@ function get_temppath() if (($temppath != "") && App::isDirectoryUsable($temppath)) { // We have a temp path and it is usable - return App::getRealPath($temppath); + return BasePath::getRealPath($temppath); } // We don't have a working preconfigured temp path, so we take the system path. @@ -651,7 +645,7 @@ function get_temppath() // Check if it is usable if (($temppath != "") && App::isDirectoryUsable($temppath)) { // Always store the real path, not the path through symlinks - $temppath = App::getRealPath($temppath); + $temppath = BasePath::getRealPath($temppath); // To avoid any interferences with other systems we create our own directory $new_temppath = $temppath . "/" . $a->getHostName(); @@ -743,7 +737,7 @@ function get_itemcachepath() $itemcache = Config::get('system', 'itemcache'); if (($itemcache != "") && App::isDirectoryUsable($itemcache)) { - return App::getRealPath($itemcache); + return BasePath::getRealPath($itemcache); } $temppath = get_temppath(); diff --git a/index.php b/index.php index c2dd31bcf2..11b7879991 100644 --- a/index.php +++ b/index.php @@ -5,7 +5,9 @@ */ use Friendica\App; -use Friendica\Util\LoggerFactory; +use Friendica\Core\Config; +use Friendica\Factory; +use Friendica\Util\BasePath; if (!file_exists(__DIR__ . '/vendor/autoload.php')) { die('Vendor path not found. Please execute "bin/composer.phar --no-dev install" on the command line in the web root.'); @@ -13,10 +15,13 @@ if (!file_exists(__DIR__ . '/vendor/autoload.php')) { require __DIR__ . '/vendor/autoload.php'; -$logger = LoggerFactory::create('index'); +$basedir = BasePath::create(__DIR__); +$configLoader = new Config\ConfigCacheLoader($basedir); +$config = Factory\ConfigFactory::createCache($configLoader); +$logger = Factory\LoggerFactory::create('index', $config); // We assume that the index.php is called by a frontend process // The value is set to "true" by default in App -$a = new App(__DIR__, $logger, false); +$a = new App($config, $logger, false); $a->runFrontend(); diff --git a/mod/admin.php b/mod/admin.php index 7723a51556..8c47877010 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -15,11 +15,11 @@ use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\Logger; use Friendica\Core\Renderer; +use Friendica\Core\StorageManager; use Friendica\Core\System; use Friendica\Core\Theme; use Friendica\Core\Update; use Friendica\Core\Worker; -use Friendica\Core\StorageManager; use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\Model\Contact; @@ -30,6 +30,7 @@ use Friendica\Module; use Friendica\Module\Login; use Friendica\Module\Tos; use Friendica\Util\Arrays; +use Friendica\Util\BasePath; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; use Friendica\Util\Strings; @@ -1375,7 +1376,7 @@ function admin_page_site_post(App $a) Config::set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed); if ($itemcache != '') { - $itemcache = App::getRealPath($itemcache); + $itemcache = BasePath::getRealPath($itemcache); } Config::set('system', 'itemcache', $itemcache); @@ -1383,13 +1384,13 @@ function admin_page_site_post(App $a) Config::set('system', 'max_comments', $max_comments); if ($temppath != '') { - $temppath = App::getRealPath($temppath); + $temppath = BasePath::getRealPath($temppath); } Config::set('system', 'temppath', $temppath); if ($basepath != '') { - $basepath = App::getRealPath($basepath); + $basepath = BasePath::getRealPath($basepath); } Config::set('system', 'basepath' , $basepath); diff --git a/mod/friendica.php b/mod/friendica.php index 10a4f93aff..e960f575e5 100644 --- a/mod/friendica.php +++ b/mod/friendica.php @@ -12,7 +12,7 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Module\Register; -function friendica_init(App $a) +function friendica_init(App $a, Config\ConfigCache $config) { if (!empty($a->argv[1]) && ($a->argv[1] == "json")) { $register_policies = [ @@ -29,7 +29,7 @@ function friendica_init(App $a) } $sql_extra = ''; - if (Config::getConfigValue('config', 'admin_nickname') !== null) { + if ($config->get('config', 'admin_nickname') !== null) { $sql_extra = sprintf(" AND `nickname` = '%s' ", DBA::escape(Config::get('config', 'admin_nickname'))); } if (!empty(Config::get('config', 'admin_email'))) { @@ -48,7 +48,7 @@ function friendica_init(App $a) Config::load('feature_lock'); $locked_features = []; - $featureLock = Config::getConfigValue('config', 'feature_lock'); + $featureLock = $config->get('config', 'feature_lock'); if (isset($featureLock)) { foreach ($featureLock as $k => $v) { if ($k === 'config_loaded') { diff --git a/src/App.php b/src/App.php index 5ebdbf92b8..caf2026348 100644 --- a/src/App.php +++ b/src/App.php @@ -8,8 +8,12 @@ use Detection\MobileDetect; use DOMDocument; use DOMXPath; use Exception; +use Friendica\Core\Config\ConfigCache; +use Friendica\Core\Config\ConfigCacheLoader; use Friendica\Database\DBA; +use Friendica\Factory\ConfigFactory; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\BasePath; use Psr\Log\LoggerInterface; /** @@ -111,6 +115,31 @@ class App */ private $logger; + /** + * @var ConfigCache The cached config + */ + private $config; + + /** + * Returns the current config cache of this node + * + * @return ConfigCache + */ + public function getConfig() + { + return $this->config; + } + + /** + * The basepath of this app + * + * @return string + */ + public function getBasePath() + { + return $this->basePath; + } + /** * Register a stylesheet file path to be included in the
tag of every page. * Inclusion is done in App->initHead(). @@ -123,7 +152,7 @@ class App */ public function registerStylesheet($path) { - $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path); + $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path); $this->stylesheets[] = trim($url, '/'); } @@ -140,7 +169,7 @@ class App */ public function registerFooterScript($path) { - $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path); + $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path); $this->footerScripts[] = trim($url, '/'); } @@ -153,23 +182,25 @@ class App /** * @brief App constructor. * - * @param string $basePath Path to the app base folder + * @param ConfigCache $config The Cached Config * @param LoggerInterface $logger Logger of this application * @param bool $isBackend Whether it is used for backend or frontend (Default true=backend) * * @throws Exception if the Basepath is not usable */ - public function __construct($basePath, LoggerInterface $logger, $isBackend = true) + public function __construct(ConfigCache $config, LoggerInterface $logger, $isBackend = true) { - $this->logger = $logger; + $this->config = $config; + $this->logger = $logger; + $this->basePath = $this->config->get('system', 'basepath'); - if (!static::isDirectoryUsable($basePath, false)) { - throw new Exception('Basepath ' . $basePath . ' isn\'t usable.'); + if (!BasePath::isDirectoryUsable($this->basePath, false)) { + throw new Exception('Basepath ' . $this->basePath . ' isn\'t usable.'); } + $this->basePath = rtrim($this->basePath, DIRECTORY_SEPARATOR); BaseObject::setApp($this); - $this->basePath = rtrim($basePath, DIRECTORY_SEPARATOR); $this->checkBackend($isBackend); $this->checkFriendicaApp(); @@ -194,7 +225,7 @@ class App $this->callstack['rendering'] = []; $this->callstack['parser'] = []; - $this->mode = new App\Mode($basePath); + $this->mode = new App\Mode($this->basePath); $this->reload(); @@ -225,9 +256,9 @@ class App set_include_path( get_include_path() . PATH_SEPARATOR - . $this->getBasePath() . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR - . $this->getBasePath() . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR - . $this->getBasePath()); + . $this->basePath . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR + . $this->basePath . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR + . $this->basePath); if (!empty($_SERVER['QUERY_STRING']) && strpos($_SERVER['QUERY_STRING'], 'pagename=') === 0) { $this->query_string = substr($_SERVER['QUERY_STRING'], 9); @@ -331,21 +362,32 @@ class App */ public function reload() { - // The order of the following calls is important to ensure proper initialization - $this->loadConfigFiles(); + Core\Config::init($this->config); + Core\PConfig::init($this->config); $this->loadDatabase(); - $this->getMode()->determine($this->getBasePath()); + $this->getMode()->determine($this->basePath); $this->determineURLPath(); - Core\Config::load(); + if ($this->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { + $adapterType = $this->config->get('system', 'config_adapter'); + $adapter = ConfigFactory::createConfig($adapterType, $this->config); + Core\Config::setAdapter($adapter); + $adapterP = ConfigFactory::createConfig($adapterType, $this->config); + Core\PConfig::setAdapter($adapterP); + Core\Config::load(); + } + + // again because DB-config could change the config + $this->getMode()->determine($this->basePath); if ($this->getMode()->has(App\Mode::DBAVAILABLE)) { Core\Hook::loadHooks(); - - $this->loadAddonConfig(); + $loader = new ConfigCacheLoader($this->basePath); + Core\Hook::callAll('load_config', $loader); + $this->config->loadConfigArray($loader->loadAddonConfig(), true); } $this->loadDefaultTimezone(); @@ -357,139 +399,6 @@ class App Core\Logger::setLogger($this->logger); } - /** - * Load the configuration files - * - * First loads the default value for all the configuration keys, then the legacy configuration files, then the - * expected local.config.php - */ - private function loadConfigFiles() - { - $this->loadConfigFile($this->getBasePath() . '/config/defaults.config.php'); - $this->loadConfigFile($this->getBasePath() . '/config/settings.config.php'); - - // Legacy .htconfig.php support - if (file_exists($this->getBasePath() . '/.htpreconfig.php')) { - $a = $this; - include $this->getBasePath() . '/.htpreconfig.php'; - } - - // Legacy .htconfig.php support - if (file_exists($this->getBasePath() . '/.htconfig.php')) { - $a = $this; - - include $this->getBasePath() . '/.htconfig.php'; - - Core\Config::setConfigValue('database', 'hostname', $db_host); - Core\Config::setConfigValue('database', 'username', $db_user); - Core\Config::setConfigValue('database', 'password', $db_pass); - Core\Config::setConfigValue('database', 'database', $db_data); - $charset = Core\Config::getConfigValue('system', 'db_charset'); - if (isset($charset)) { - Core\Config::setConfigValue('database', 'charset', $charset); - } - - unset($db_host, $db_user, $db_pass, $db_data); - - if (isset($default_timezone)) { - Core\Config::setConfigValue('system', 'default_timezone', $default_timezone); - unset($default_timezone); - } - - if (isset($pidfile)) { - Core\Config::setConfigValue('system', 'pidfile', $pidfile); - unset($pidfile); - } - - if (isset($lang)) { - Core\Config::setConfigValue('system', 'language', $lang); - unset($lang); - } - } - - if (file_exists($this->getBasePath() . '/config/local.config.php')) { - $this->loadConfigFile($this->getBasePath() . '/config/local.config.php', true); - } elseif (file_exists($this->getBasePath() . '/config/local.ini.php')) { - $this->loadINIConfigFile($this->getBasePath() . '/config/local.ini.php', true); - } - } - - /** - * Tries to load the specified legacy configuration file into the App->config array. - * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config. - * - * @deprecated since version 2018.12 - * @param string $filepath - * @param bool $overwrite Force value overwrite if the config key already exists - * @throws Exception - */ - public function loadINIConfigFile($filepath, $overwrite = false) - { - if (!file_exists($filepath)) { - throw new Exception('Error parsing non-existent INI config file ' . $filepath); - } - - $contents = include($filepath); - - $config = parse_ini_string($contents, true, INI_SCANNER_TYPED); - - if ($config === false) { - throw new Exception('Error parsing INI config file ' . $filepath); - } - - Core\Config::loadConfigArray($config, $overwrite); - } - - /** - * Tries to load the specified configuration file into the App->config array. - * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config. - * - * The config format is PHP array and the template for configuration files is the following: - * - * [ - * 'key' => 'value', - * ], - * ]; - * - * @param string $filepath - * @param bool $overwrite Force value overwrite if the config key already exists - * @throws Exception - */ - public function loadConfigFile($filepath, $overwrite = false) - { - if (!file_exists($filepath)) { - throw new Exception('Error loading non-existent config file ' . $filepath); - } - - $config = include($filepath); - - if (!is_array($config)) { - throw new Exception('Error loading config file ' . $filepath); - } - - Core\Config::loadConfigArray($config, $overwrite); - } - - /** - * Loads addons configuration files - * - * First loads all activated addons default configuration through the load_config hook, then load the local.config.php - * again to overwrite potential local addon configuration. - */ - private function loadAddonConfig() - { - // Loads addons default config - Core\Hook::callAll('load_config'); - - // Load the local addon config file to overwritten default addon config values - if (file_exists($this->getBasePath() . '/config/addon.config.php')) { - $this->loadConfigFile($this->getBasePath() . '/config/addon.config.php', true); - } elseif (file_exists($this->getBasePath() . '/config/addon.ini.php')) { - $this->loadINIConfigFile($this->getBasePath() . '/config/addon.ini.php', true); - } - } - /** * Loads the default timezone * @@ -499,8 +408,8 @@ class App */ private function loadDefaultTimezone() { - if (Core\Config::getConfigValue('system', 'default_timezone')) { - $this->timezone = Core\Config::getConfigValue('system', 'default_timezone'); + if ($this->config->get('system', 'default_timezone')) { + $this->timezone = $this->config->get('system', 'default_timezone'); } else { global $default_timezone; $this->timezone = !empty($default_timezone) ? $default_timezone : 'UTC'; @@ -526,7 +435,7 @@ class App $relative_script_path = defaults($_SERVER, 'SCRIPT_URL' , $relative_script_path); $relative_script_path = defaults($_SERVER, 'REQUEST_URI' , $relative_script_path); - $this->urlPath = Core\Config::getConfigValue('system', 'urlpath'); + $this->urlPath = $this->config->get('system', 'urlpath'); /* $relative_script_path gives /relative/path/to/friendica/module/parameter * QUERY_STRING gives pagename=module/parameter @@ -554,11 +463,11 @@ class App return; } - $db_host = Core\Config::getConfigValue('database', 'hostname'); - $db_user = Core\Config::getConfigValue('database', 'username'); - $db_pass = Core\Config::getConfigValue('database', 'password'); - $db_data = Core\Config::getConfigValue('database', 'database'); - $charset = Core\Config::getConfigValue('database', 'charset'); + $db_host = $this->config->get('database', 'hostname'); + $db_user = $this->config->get('database', 'username'); + $db_pass = $this->config->get('database', 'password'); + $db_data = $this->config->get('database', 'database'); + $charset = $this->config->get('database', 'charset'); // Use environment variables for mysql if they are set beforehand if (!empty(getenv('MYSQL_HOST')) @@ -581,9 +490,9 @@ class App $stamp1 = microtime(true); - if (DBA::connect($db_host, $db_user, $db_pass, $db_data, $charset)) { + if (DBA::connect($this->config, $db_host, $db_user, $db_pass, $db_data, $charset)) { // Loads DB_UPDATE_VERSION constant - Database\DBStructure::definition(false); + Database\DBStructure::definition($this->basePath, false); } unset($db_host, $db_user, $db_pass, $db_data, $charset); @@ -591,55 +500,6 @@ class App $this->saveTimestamp($stamp1, 'network'); } - /** - * @brief Returns the base filesystem path of the App - * - * It first checks for the internal variable, then for DOCUMENT_ROOT and - * finally for PWD - * - * @return string - * @throws InternalServerErrorException - */ - public function getBasePath() - { - $basepath = $this->basePath; - - if (!$basepath) { - $basepath = Core\Config::get('system', 'basepath'); - } - - if (!$basepath && !empty($_SERVER['DOCUMENT_ROOT'])) { - $basepath = $_SERVER['DOCUMENT_ROOT']; - } - - if (!$basepath && !empty($_SERVER['PWD'])) { - $basepath = $_SERVER['PWD']; - } - - return self::getRealPath($basepath); - } - - /** - * @brief Returns a normalized file path - * - * This is a wrapper for the "realpath" function. - * That function cannot detect the real path when some folders aren't readable. - * Since this could happen with some hosters we need to handle this. - * - * @param string $path The path that is about to be normalized - * @return string normalized path - when possible - */ - public static function getRealPath($path) - { - $normalized = realpath($path); - - if (!is_bool($normalized)) { - return $normalized; - } else { - return $path; - } - } - public function getScheme() { return $this->scheme; @@ -715,8 +575,8 @@ class App $this->urlPath = trim($parsed['path'], '\\/'); } - if (file_exists($this->getBasePath() . '/.htpreconfig.php')) { - include $this->getBasePath() . '/.htpreconfig.php'; + if (file_exists($this->basePath . '/.htpreconfig.php')) { + include $this->basePath . '/.htpreconfig.php'; } if (Core\Config::get('config', 'hostname') != '') { @@ -769,9 +629,9 @@ class App // compose the page title from the sitename and the // current module called if (!$this->module == '') { - $this->page['title'] = Core\Config::getConfigValue('config', 'sitename') . ' (' . $this->module . ')'; + $this->page['title'] = $this->config->get('config', 'sitename') . ' (' . $this->module . ')'; } else { - $this->page['title'] = Core\Config::getConfigValue('config', 'sitename'); + $this->page['title'] = $this->config->get('config', 'sitename'); } if (!empty(Core\Renderer::$theme['stylesheet'])) { @@ -892,7 +752,7 @@ class App */ public function saveTimestamp($timestamp, $value) { - $profiler = Core\Config::getConfigValue('system', 'profiler'); + $profiler = $this->config->get('system', 'profiler'); if (!isset($profiler) || !$profiler) { return; @@ -1132,7 +992,7 @@ class App return; } - $cmdline = Core\Config::getConfigValue('config', 'php_path', 'php') . ' ' . escapeshellarg($command); + $cmdline = $this->config->get('config', 'php_path', 'php') . ' ' . escapeshellarg($command); foreach ($args as $key => $value) { if (!is_null($value) && is_bool($value) && !$value) { @@ -1150,9 +1010,9 @@ class App } if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->getBasePath()); + $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->basePath); } else { - $resource = proc_open($cmdline . ' &', [], $foo, $this->getBasePath()); + $resource = proc_open($cmdline . ' &', [], $foo, $this->basePath); } if (!is_resource($resource)) { Core\Logger::log('We got no resource for command ' . $cmdline, Core\Logger::DEBUG); @@ -1161,61 +1021,6 @@ class App proc_close($resource); } - /** - * @brief Returns the system user that is executing the script - * - * This mostly returns something like "www-data". - * - * @return string system username - */ - private static function getSystemUser() - { - if (!function_exists('posix_getpwuid') || !function_exists('posix_geteuid')) { - return ''; - } - - $processUser = posix_getpwuid(posix_geteuid()); - return $processUser['name']; - } - - /** - * @brief Checks if a given directory is usable for the system - * - * @param $directory - * @param bool $check_writable - * @return boolean the directory is usable - * @throws Exception - */ - public static function isDirectoryUsable($directory, $check_writable = true) - { - if ($directory == '') { - Core\Logger::log('Directory is empty. This shouldn\'t happen.', Core\Logger::DEBUG); - return false; - } - - if (!file_exists($directory)) { - Core\Logger::log('Path "' . $directory . '" does not exist for user ' . self::getSystemUser(), Core\Logger::DEBUG); - return false; - } - - if (is_file($directory)) { - Core\Logger::log('Path "' . $directory . '" is a file for user ' . self::getSystemUser(), Core\Logger::DEBUG); - return false; - } - - if (!is_dir($directory)) { - Core\Logger::log('Path "' . $directory . '" is not a directory for user ' . self::getSystemUser(), Core\Logger::DEBUG); - return false; - } - - if ($check_writable && !is_writable($directory)) { - Core\Logger::log('Path "' . $directory . '" is not writable for user ' . self::getSystemUser(), Core\Logger::DEBUG); - return false; - } - - return true; - } - /** * Generates the site's default sender email address * diff --git a/src/Core/Config.php b/src/Core/Config.php index bec6539e27..c8edcb2928 100644 --- a/src/Core/Config.php +++ b/src/Core/Config.php @@ -8,8 +8,8 @@ */ namespace Friendica\Core; -use Friendica\App; -use Friendica\BaseObject; +use Friendica\Core\Config\IConfigAdapter; +use Friendica\Core\Config\IConfigCache; /** * @brief Arbitrary system configuration storage @@ -18,27 +18,36 @@ use Friendica\BaseObject; * If we ever would decide to return exactly the variable type as entered, * we will have fun with the additional features. :-) */ -class Config extends BaseObject +class Config { - public static $config = []; + /** + * @var IConfigAdapter + */ + private static $adapter; /** - * @var \Friendica\Core\Config\IConfigAdapter + * @var IConfigCache */ - private static $adapter = null; + private static $config; - public static function init() + /** + * Initialize the config with only the cache + * + * @param IConfigCache $config The configuration cache + */ + public static function init($config) { - // Database isn't ready or populated yet - if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { - return; - } + self::$config = $config; + } - if (self::getConfigValue('system', 'config_adapter') == 'preload') { - self::$adapter = new Config\PreloadConfigAdapter(); - } else { - self::$adapter = new Config\JITConfigAdapter(); - } + /** + * Add the adapter for DB-backend + * + * @param $adapter + */ + public static function setAdapter($adapter) + { + self::$adapter = $adapter; } /** @@ -50,19 +59,13 @@ class Config extends BaseObject * @param string $family The category of the configuration value * * @return void - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function load($family = "config") { - // Database isn't ready or populated yet - if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { + if (!isset(self::$adapter)) { return; } - if (empty(self::$adapter)) { - self::init(); - } - self::$adapter->load($family); } @@ -84,17 +87,11 @@ class Config extends BaseObject * @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) * * @return mixed Stored value or null if it does not exist - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function get($family, $key, $default_value = null, $refresh = false) { - // Database isn't ready or populated yet, fallback to file config - if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { - return self::getConfigValue($family, $key, $default_value); - } - - if (empty(self::$adapter)) { - self::init(); + if (!isset(self::$adapter)) { + return self::$config->get($family, $key, $default_value); } return self::$adapter->get($family, $key, $default_value, $refresh); @@ -113,17 +110,12 @@ class Config extends BaseObject * @param mixed $value The value to store * * @return bool Operation success - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function set($family, $key, $value) { - // Database isn't ready or populated yet - if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { - return false; - } - - if (empty(self::$adapter)) { - self::init(); + if (!isset(self::$adapter)) { + self::$config->set($family, $key, $value); + return true; } return self::$adapter->set($family, $key, $value); @@ -139,131 +131,13 @@ class Config extends BaseObject * @param string $key The configuration key to delete * * @return mixed - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function delete($family, $key) { - // Database isn't ready or populated yet - if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { - return false; - } - - if (empty(self::$adapter)) { - self::init(); + if (!isset(self::$adapter)) { + self::$config->delete($family, $key); } return self::$adapter->delete($family, $key); } - - /** - * Tries to load the specified configuration array into the App->config array. - * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config. - * - * @param array $config - * @param bool $overwrite Force value overwrite if the config key already exists - */ - public static function loadConfigArray(array $config, $overwrite = false) - { - foreach ($config as $category => $values) { - foreach ($values as $key => $value) { - if ($overwrite) { - self::setConfigValue($category, $key, $value); - } else { - self::setDefaultConfigValue($category, $key, $value); - } - } - } - } - - /** - * @param string $cat Config category - * @param string $k Config key - * @param mixed $default Default value if it isn't set - * - * @return string Returns the value of the Config entry - */ - public static function getConfigValue($cat, $k = null, $default = null) - { - $return = $default; - - if ($cat === 'config') { - if (isset(self::$config[$k])) { - $return = self::$config[$k]; - } - } else { - if (isset(self::$config[$cat][$k])) { - $return = self::$config[$cat][$k]; - } elseif ($k == null && isset(self::$config[$cat])) { - $return = self::$config[$cat]; - } - } - - return $return; - } - - /** - * Sets a default value in the config cache. Ignores already existing keys. - * - * @param string $cat Config category - * @param string $k Config key - * @param mixed $v Default value to set - */ - private static function setDefaultConfigValue($cat, $k, $v) - { - if (!isset(self::$config[$cat][$k])) { - self::setConfigValue($cat, $k, $v); - } - } - - /** - * Sets a value in the config cache. Accepts raw output from the config table - * - * @param string $cat Config category - * @param string $k Config key - * @param mixed $v Value to set - */ - public static function setConfigValue($cat, $k, $v) - { - // Only arrays are serialized in database, so we have to unserialize sparingly - $value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v; - - if ($cat === 'config') { - self::$config[$k] = $value; - } else { - if (!isset(self::$config[$cat])) { - self::$config[$cat] = []; - } - - self::$config[$cat][$k] = $value; - } - } - - /** - * Deletes a value from the config cache - * - * @param string $cat Config category - * @param string $k Config key - */ - public static function deleteConfigValue($cat, $k) - { - if ($cat === 'config') { - if (isset(self::$config[$k])) { - unset(self::$config[$k]); - } - } else { - if (isset(self::$config[$cat][$k])) { - unset(self::$config[$cat][$k]); - } - } - } - - /** - * Returns the whole configuration - * - * @return array The configuration - */ - public static function getAll() - { - return self::$config; - } } diff --git a/src/Core/Config/ConfigCache.php b/src/Core/Config/ConfigCache.php new file mode 100644 index 0000000000..d7318537e9 --- /dev/null +++ b/src/Core/Config/ConfigCache.php @@ -0,0 +1,173 @@ +config = []; + + if (isset($config)) { + $this->loadConfigArray($config, $overwrite); + } + } + + /** + * Tries to load the specified configuration array into the App->config array. + * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config. + * + * @param array $config + * @param bool $overwrite Force value overwrite if the config key already exists + */ + public function loadConfigArray(array $config, $overwrite = false) + { + foreach ($config as $category => $values) { + foreach ($values as $key => $value) { + if ($overwrite) { + self::set($category, $key, $value); + } else { + self::setDefault($category, $key, $value); + } + } + } + } + + /** + * {@inheritdoc} + */ + public function get($cat, $key = null, $default = null) + { + $return = $default; + + if ($cat === 'config') { + if (isset($this->config[$key])) { + $return = $this->config[$key]; + } + } else { + if (isset($this->config[$cat][$key])) { + $return = $this->config[$cat][$key]; + } elseif ($key == null && isset($this->config[$cat])) { + $return = $this->config[$cat]; + } + } + + return $return; + } + + /** + * Sets a default value in the config cache. Ignores already existing keys. + * + * @param string $cat Config category + * @param string $k Config key + * @param mixed $v Default value to set + */ + private function setDefault($cat, $k, $v) + { + if (!isset($this->config[$cat][$k])) { + self::set($cat, $k, $v); + } + } + + /** + * {@inheritdoc} + */ + public function set($cat, $key, $value) + { + // Only arrays are serialized in database, so we have to unserialize sparingly + $value = is_string($value) && preg_match("|^a:[0-9]+:{.*}$|s", $value) ? unserialize($value) : $value; + + if ($cat === 'config') { + $this->config[$key] = $value; + } else { + if (!isset($this->config[$cat])) { + $this->config[$cat] = []; + } + + $this->config[$cat][$key] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function delete($cat, $key) + { + if ($cat === 'config') { + if (isset($this->config[$key])) { + unset($this->config[$key]); + } + } else { + if (isset($this->config[$cat][$key])) { + unset($this->config[$cat][$key]); + } + } + } + + /** + * {@inheritdoc} + */ + public function getP($uid, $cat, $key = null, $default = null) + { + $return = $default; + + if (isset($this->config[$uid][$cat][$key])) { + $return = $this->config[$uid][$cat][$key]; + } elseif ($key == null && isset($this->config[$uid][$cat])) { + $return = $this->config[$uid][$cat]; + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setP($uid, $cat, $key, $value) + { + // Only arrays are serialized in database, so we have to unserialize sparingly + $value = is_string($value) && preg_match("|^a:[0-9]+:{.*}$|s", $value) ? unserialize($value) : $value; + + if (!isset($this->config[$uid]) || !is_array($this->config[$uid])) { + $this->config[$uid] = []; + } + + if (!isset($this->config[$uid][$cat]) || !is_array($this->config[$uid][$cat])) { + $this->config[$uid][$cat] = []; + } + + if ($key === null) { + $this->config[$uid][$cat] = $value; + } else { + $this->config[$uid][$cat][$key] = $value; + } + } + + /** + * {@inheritdoc} + */ + public function deleteP($uid, $cat, $key) + { + if (isset($this->config[$uid][$cat][$key])) { + unset($this->config[$uid][$cat][$key]); + } + } + + /** + * Returns the whole configuration + * + * @return array The configuration + */ + public function getAll() + { + return $this->config; + } +} diff --git a/src/Core/Config/ConfigCacheLoader.php b/src/Core/Config/ConfigCacheLoader.php new file mode 100644 index 0000000000..287be7d522 --- /dev/null +++ b/src/Core/Config/ConfigCacheLoader.php @@ -0,0 +1,159 @@ +baseDir = $baseDir; + $this->configDir = $baseDir . '/config/'; + } + + /** + * Load the configuration files + * + * First loads the default value for all the configuration keys, then the legacy configuration files, then the + * expected local.config.php + */ + public function loadConfigFiles(ConfigCache $config) + { + // Setting at least the basepath we know + $config->set('system', 'basepath', $this->baseDir); + + $config->loadConfigArray($this->loadConfigFile('defaults')); + $config->loadConfigArray($this->loadConfigFile('settings')); + + // Legacy .htconfig.php support + if (file_exists($this->baseDir . '/.htpreconfig.php')) { + $a = $config; + include $this->baseDir . '/.htpreconfig.php'; + } + + // Legacy .htconfig.php support + if (file_exists($this->baseDir . '/.htconfig.php')) { + $a = $config; + + include $this->baseDir . '/.htconfig.php'; + + $config->set('database', 'hostname', $db_host); + $config->set('database', 'username', $db_user); + $config->set('database', 'password', $db_pass); + $config->set('database', 'database', $db_data); + $charset = $config->get('system', 'db_charset'); + if (isset($charset)) { + $config->set('database', 'charset', $charset); + } + + unset($db_host, $db_user, $db_pass, $db_data); + + if (isset($default_timezone)) { + $config->set('system', 'default_timezone', $default_timezone); + unset($default_timezone); + } + + if (isset($pidfile)) { + $config->set('system', 'pidfile', $pidfile); + unset($pidfile); + } + + if (isset($lang)) { + $config->set('system', 'language', $lang); + unset($lang); + } + } + + if (file_exists($this->baseDir . '/config/local.config.php')) { + $config->loadConfigArray($this->loadConfigFile('local'), true); + } elseif (file_exists($this->baseDir . '/config/local.ini.php')) { + $config->loadConfigArray($this->loadINIConfigFile('local'), true); + } + } + + /** + * Tries to load the specified legacy configuration file into the App->config array. + * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config. + * + * @deprecated since version 2018.12 + * @param string $filename + * + * @return array The configuration + * @throws \Exception + */ + public function loadINIConfigFile($filename) + { + $filepath = $this->configDir . $filename . ".ini.php"; + + if (!file_exists($filepath)) { + throw new \Exception('Error parsing non-existent INI config file ' . $filepath); + } + + $contents = include($filepath); + + $config = parse_ini_string($contents, true, INI_SCANNER_TYPED); + + if ($config === false) { + throw new \Exception('Error parsing INI config file ' . $filepath); + } + + return $config; + } + + /** + * Tries to load the specified configuration file into the App->config array. + * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config. + * + * The config format is PHP array and the template for configuration files is the following: + * + * [ + * 'key' => 'value', + * ], + * ]; + * + * @param string $filename + * @return array The configuration + * @throws \Exception + */ + public function loadConfigFile($filename) + { + $filepath = $this->configDir . $filename . ".config.php"; + + if (!file_exists($filepath)) { + throw new \Exception('Error loading non-existent config file ' . $filepath); + } + + $config = include($filepath); + + if (!is_array($config)) { + throw new \Exception('Error loading config file ' . $filepath); + } + + return $config; + } + + /** + * Loads addons configuration files + * + * First loads all activated addons default configuration through the load_config hook, then load the local.config.php + * again to overwrite potential local addon configuration. + * + * @return array The config array + * + * @throws \Exception + */ + public function loadAddonConfig() + { + // Load the local addon config file to overwritten default addon config values + if (file_exists($this->configDir . 'addon.config.php')) { + return $this->loadConfigFile('addon'); + } elseif (file_exists($this->configDir . 'addon.ini.php')) { + return $this->loadINIConfigFile('addon'); + } else { + return []; + } + } +} diff --git a/src/Core/Config/IConfigCache.php b/src/Core/Config/IConfigCache.php new file mode 100644 index 0000000000..191333c44a --- /dev/null +++ b/src/Core/Config/IConfigCache.php @@ -0,0 +1,34 @@ +config = $config; + } + public function load($cat = "config") { // We don't preload "system" anymore. @@ -28,7 +40,7 @@ class JITConfigAdapter implements IConfigAdapter while ($config = DBA::fetch($configs)) { $k = $config['k']; - Config::setConfigValue($cat, $k, $config['v']); + $this->config->set($cat, $k, $config['v']); if ($cat !== 'config') { $this->cache[$cat][$k] = $config['v']; @@ -60,18 +72,18 @@ class JITConfigAdapter implements IConfigAdapter $this->cache[$cat][$k] = $value; $this->in_db[$cat][$k] = true; return $value; - } elseif (Config::getConfigValue($cat, $k) !== null) { + } elseif ($this->config->get($cat, $k) !== null) { // Assign the value (mostly) from config/local.config.php file to the cache - $this->cache[$cat][$k] = Config::getConfigValue($cat, $k); + $this->cache[$cat][$k] = $this->config->get($cat, $k); $this->in_db[$cat][$k] = false; - return Config::getConfigValue($cat, $k); - } elseif (Config::getConfigValue('config', $k) !== null) { + return $this->config->get($cat, $k); + } elseif ($this->config->get('config', $k) !== null) { // Assign the value (mostly) from config/local.config.php file to the cache - $this->cache[$k] = Config::getConfigValue('config', $k); + $this->cache[$k] = $this->config->get('config', $k); $this->in_db[$k] = false; - return Config::getConfigValue('config', $k); + return $this->config->get('config', $k); } $this->cache[$cat][$k] = '!