From: Philipp Holzer Date: Sun, 3 Feb 2019 21:22:04 +0000 (+0100) Subject: 3) Introducing ConfigFactory X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=4af0119b7310e9731e60b11a14fd917580b95715;p=friendica.git 3) Introducing ConfigFactory --- 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] = '!!'; @@ -100,7 +112,7 @@ class JITConfigAdapter implements IConfigAdapter return true; } - Config::setConfigValue($cat, $k, $value); + $this->config->set($cat, $k, $value); // Assign the just added value to the cache $this->cache[$cat][$k] = $dbvalue; diff --git a/src/Core/Config/JITPConfigAdapter.php b/src/Core/Config/JITPConfigAdapter.php index ac9a56076c..223cc542dc 100644 --- a/src/Core/Config/JITPConfigAdapter.php +++ b/src/Core/Config/JITPConfigAdapter.php @@ -1,7 +1,6 @@ config = $config; + } + public function load($uid, $cat) { $pconfigs = DBA::select('pconfig', ['v', 'k'], ['cat' => $cat, 'uid' => $uid]); @@ -22,13 +35,13 @@ class JITPConfigAdapter implements IPConfigAdapter while ($pconfig = DBA::fetch($pconfigs)) { $k = $pconfig['k']; - PConfig::setPConfigValue($uid, $cat, $k, $pconfig['v']); + $this->config->setP($uid, $cat, $k, $pconfig['v']); $this->in_db[$uid][$cat][$k] = true; } } else if ($cat != 'config') { // Negative caching - PConfig::setPConfigValue($uid, $cat, null, "!!"); + $this->config->setP($uid, $cat, null, "!!"); } DBA::close($pconfigs); } @@ -37,17 +50,17 @@ class JITPConfigAdapter implements IPConfigAdapter { if (!$refresh) { // Looking if the whole family isn't set - if (PConfig::getPConfigValue($uid, $cat) !== null) { - if (PConfig::getPConfigValue($uid, $cat) === '!!') { + if ($this->config->getP($uid, $cat) !== null) { + if ($this->config->getP($uid, $cat) === '!!') { return $default_value; } } - if (PConfig::getPConfigValue($uid, $cat, $k) !== null) { - if (PConfig::getPConfigValue($uid, $cat, $k) === '!!') { + if ($this->config->getP($uid, $cat, $k) !== null) { + if ($this->config->getP($uid, $cat, $k) === '!!') { return $default_value; } - return PConfig::getPConfigValue($uid, $cat, $k); + return $this->config->getP($uid, $cat, $k); } } @@ -55,13 +68,13 @@ class JITPConfigAdapter implements IPConfigAdapter if (DBA::isResult($pconfig)) { $val = (preg_match("|^a:[0-9]+:{.*}$|s", $pconfig['v']) ? unserialize($pconfig['v']) : $pconfig['v']); - PConfig::setPConfigValue($uid, $cat, $k, $val); + $this->config->setP($uid, $cat, $k, $val); $this->in_db[$uid][$cat][$k] = true; return $val; } else { - PConfig::setPConfigValue($uid, $cat, $k, '!!'); + $this->config->setP($uid, $cat, $k, '!!'); $this->in_db[$uid][$cat][$k] = false; @@ -82,7 +95,7 @@ class JITPConfigAdapter implements IPConfigAdapter return true; } - PConfig::setPConfigValue($uid, $cat, $k, $value); + $this->config->setP($uid, $cat, $k, $value); // manage array value $dbvalue = (is_array($value) ? serialize($value) : $dbvalue); @@ -98,7 +111,7 @@ class JITPConfigAdapter implements IPConfigAdapter public function delete($uid, $cat, $k) { - PConfig::deletePConfigValue($uid, $cat, $k); + $this->config->deleteP($uid, $cat, $k); if (!empty($this->in_db[$uid][$cat][$k])) { unset($this->in_db[$uid][$cat][$k]); diff --git a/src/Core/Config/PreloadConfigAdapter.php b/src/Core/Config/PreloadConfigAdapter.php index dc5c45379f..4461105e14 100644 --- a/src/Core/Config/PreloadConfigAdapter.php +++ b/src/Core/Config/PreloadConfigAdapter.php @@ -3,7 +3,6 @@ namespace Friendica\Core\Config; use Exception; -use Friendica\Core\Config; use Friendica\Database\DBA; /** @@ -17,8 +16,17 @@ class PreloadConfigAdapter implements IConfigAdapter { private $config_loaded = false; - public function __construct() + /** + * @var IConfigCache The config cache of this driver + */ + private $config; + + /** + * @param IConfigCache $config The config cache of this driver + */ + public function __construct($config) { + $this->config = $config; $this->load(); } @@ -30,7 +38,7 @@ class PreloadConfigAdapter implements IConfigAdapter $configs = DBA::select('config', ['cat', 'v', 'k']); while ($config = DBA::fetch($configs)) { - Config::setConfigValue($config['cat'], $config['k'], $config['v']); + $this->config->set($config['cat'], $config['k'], $config['v']); } DBA::close($configs); @@ -42,11 +50,11 @@ class PreloadConfigAdapter implements IConfigAdapter if ($refresh) { $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $k]); if (DBA::isResult($config)) { - Config::setConfigValue($cat, $k, $config['v']); + $this->config->set($cat, $k, $config['v']); } } - $return = Config::getConfigValue($cat, $k, $default_value); + $return = $this->config->get($cat, $k, $default_value); return $return; } @@ -58,11 +66,11 @@ class PreloadConfigAdapter implements IConfigAdapter // The exception are array values. $compare_value = !is_array($value) ? (string)$value : $value; - if (Config::getConfigValue($cat, $k) === $compare_value) { + if ($this->config->get($cat, $k) === $compare_value) { return true; } - Config::setConfigValue($cat, $k, $value); + $this->config->set($cat, $k, $value); // manage array value $dbvalue = is_array($value) ? serialize($value) : $value; @@ -77,7 +85,7 @@ class PreloadConfigAdapter implements IConfigAdapter public function delete($cat, $k) { - Config::deleteConfigValue($cat, $k); + $this->config->delete($cat, $k); $result = DBA::delete('config', ['cat' => $cat, 'k' => $k]); diff --git a/src/Core/Config/PreloadPConfigAdapter.php b/src/Core/Config/PreloadPConfigAdapter.php index 88774f2979..311518bbe9 100644 --- a/src/Core/Config/PreloadPConfigAdapter.php +++ b/src/Core/Config/PreloadPConfigAdapter.php @@ -3,7 +3,6 @@ namespace Friendica\Core\Config; use Exception; -use Friendica\Core\PConfig; use Friendica\Database\DBA; /** @@ -17,8 +16,19 @@ class PreloadPConfigAdapter implements IPConfigAdapter { private $config_loaded = false; - public function __construct($uid) + /** + * The config cache of this adapter + * @var IPConfigCache + */ + private $config; + + /** + * @param int $uid The UID of the current user + * @param IPConfigCache $config The config cache of this adapter + */ + public function __construct($uid, $config) { + $this->config = $config; $this->load($uid, 'config'); } @@ -34,7 +44,7 @@ class PreloadPConfigAdapter implements IPConfigAdapter $pconfigs = DBA::select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]); while ($pconfig = DBA::fetch($pconfigs)) { - PConfig::setPConfigValue($uid, $pconfig['cat'], $pconfig['k'], $pconfig['v']); + $this->config->setP($uid, $pconfig['cat'], $pconfig['k'], $pconfig['v']); } DBA::close($pconfigs); @@ -50,15 +60,13 @@ class PreloadPConfigAdapter implements IPConfigAdapter if ($refresh) { $config = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $k]); if (DBA::isResult($config)) { - PConfig::setPConfigValue($uid, $cat, $k, $config['v']); + $this->config->setP($uid, $cat, $k, $config['v']); } else { - PConfig::deletePConfigValue($uid, $cat, $k); + $this->config->deleteP($uid, $cat, $k); } } - $return = PConfig::getPConfigValue($uid, $cat, $k, $default_value); - - return $return; + return $this->config->getP($uid, $cat, $k, $default_value);; } public function set($uid, $cat, $k, $value) @@ -71,11 +79,11 @@ class PreloadPConfigAdapter implements IPConfigAdapter // The exception are array values. $compare_value = !is_array($value) ? (string)$value : $value; - if (PConfig::getPConfigValue($uid, $cat, $k) === $compare_value) { + if ($this->config->getP($uid, $cat, $k) === $compare_value) { return true; } - PConfig::setPConfigValue($uid, $cat, $k, $value); + $this->config->setP($uid, $cat, $k, $value); // manage array value $dbvalue = is_array($value) ? serialize($value) : $value; @@ -94,7 +102,7 @@ class PreloadPConfigAdapter implements IPConfigAdapter $this->load($uid, $cat); } - PConfig::deletePConfigValue($uid, $cat, $k); + $this->config->deleteP($uid, $cat, $k); $result = DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $k]); diff --git a/src/Core/Logger.php b/src/Core/Logger.php index 277a091641..6b8112796f 100644 --- a/src/Core/Logger.php +++ b/src/Core/Logger.php @@ -5,8 +5,8 @@ namespace Friendica\Core; use Friendica\BaseObject; +use Friendica\Factory\LoggerFactory; use Friendica\Network\HTTPException\InternalServerErrorException; -use Friendica\Util\LoggerFactory; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; diff --git a/src/Core/PConfig.php b/src/Core/PConfig.php index 75403aaebe..1f9f36638d 100644 --- a/src/Core/PConfig.php +++ b/src/Core/PConfig.php @@ -8,8 +8,7 @@ */ namespace Friendica\Core; -use Friendica\App; -use Friendica\BaseObject; +use Friendica\Core\Config\IPConfigCache; /** * @brief Management of user configuration storage @@ -18,29 +17,36 @@ use Friendica\BaseObject; * The PConfig::get() functions return boolean false for keys that are unset, * and this could lead to subtle bugs. */ -class PConfig extends BaseObject +class PConfig { - private static $config; - /** * @var \Friendica\Core\Config\IPConfigAdapter */ - private static $adapter = null; + private static $adapter; - public static function init($uid) - { - $a = self::getApp(); + /** + * @var IPConfigCache + */ + private static $config; - // Database isn't ready or populated yet - if (!$a->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { - return; - } + /** + * Initialize the config with only the cache + * + * @param IPConfigCache $config The configuration cache + */ + public static function init($config) + { + self::$config = $config; + } - if (Config::getConfigValue('system', 'config_adapter') == 'preload') { - self::$adapter = new Config\PreloadPConfigAdapter($uid); - } else { - self::$adapter = new Config\JITPConfigAdapter(); - } + /** + * Add the adapter for DB-backend + * + * @param $adapter + */ + public static function setAdapter($adapter) + { + self::$adapter = $adapter; } /** @@ -57,15 +63,10 @@ class PConfig extends BaseObject */ public static function load($uid, $family) { - // 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($uid); - } - self::$adapter->load($uid, $family); } @@ -83,17 +84,11 @@ class PConfig 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($uid, $family, $key, $default_value = null, $refresh = false) { - // Database isn't ready or populated yet - if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) { - return; - } - - if (empty(self::$adapter)) { - self::init($uid); + if (!isset(self::$adapter)) { + return self::$config->getP($uid, $family, $key, $default_value); } return self::$adapter->get($uid, $family, $key, $default_value, $refresh); @@ -113,17 +108,11 @@ class PConfig extends BaseObject * @param mixed $value The value to store * * @return bool Operation success - * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function set($uid, $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($uid); + if (!isset(self::$adapter)) { + return self::$config->setP($uid, $family, $key, $value); } return self::$adapter->set($uid, $family, $key, $value); @@ -144,83 +133,10 @@ class PConfig extends BaseObject */ public static function delete($uid, $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($uid); + if (!isset(self::$adapter)) { + return self::$config->deleteP($uid, $family, $key); } return self::$adapter->delete($uid, $family, $key); } - - - /** - * Retrieves a value from the user config cache - * - * @param int $uid User Id - * @param string $cat Config category - * @param string $k Config key - * @param mixed $default Default value if key isn't set - * - * @return string The value of the config entry - */ - public static function getPConfigValue($uid, $cat, $k = null, $default = null) - { - $return = $default; - - if (isset(self::$config[$uid][$cat][$k])) { - $return = self::$config[$uid][$cat][$k]; - } elseif ($k == null && isset(self::$config[$uid][$cat])) { - $return = self::$config[$uid][$cat]; - } - - return $return; - } - - /** - * Sets a value in the user config cache - * - * Accepts raw output from the pconfig table - * - * @param int $uid User Id - * @param string $cat Config category - * @param string $k Config key - * @param mixed $v Value to set - */ - public static function setPConfigValue($uid, $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 (!isset(self::$config[$uid]) || !is_array(self::$config[$uid])) { - self::$config[$uid] = []; - } - - if (!isset(self::$config[$uid][$cat]) || !is_array(self::$config[$uid][$cat])) { - self::$config[$uid][$cat] = []; - } - - if ($k === null) { - self::$config[$uid][$cat] = $value; - } else { - self::$config[$uid][$cat][$k] = $value; - } - } - - /** - * Deletes a value from the user config cache - * - * @param int $uid User Id - * @param string $cat Config category - * @param string $k Config key - */ - public static function deletePConfigValue($uid, $cat, $k) - { - if (isset(self::$config[$uid][$cat][$k])) { - unset(self::$config[$uid][$cat][$k]); - } - } } diff --git a/src/Core/System.php b/src/Core/System.php index ba3d03e622..c1d23a9ea1 100644 --- a/src/Core/System.php +++ b/src/Core/System.php @@ -286,6 +286,23 @@ class System extends BaseObject exit(); } + /** + * @brief Returns the system user that is executing the script + * + * This mostly returns something like "www-data". + * + * @return string system username + */ + public static function getUser() + { + if (!function_exists('posix_getpwuid') || !function_exists('posix_geteuid')) { + return ''; + } + + $processUser = posix_getpwuid(posix_geteuid()); + return $processUser['name']; + } + /// @todo Move the following functions from boot.php /* function killme() diff --git a/src/Database/DBA.php b/src/Database/DBA.php index 51b9be967e..62789589ce 100644 --- a/src/Database/DBA.php +++ b/src/Database/DBA.php @@ -2,9 +2,7 @@ namespace Friendica\Database; -// Do not use native get/set/load of Core\Config in this class at risk of infinite loop. -// Please use Core\Config::getConfigVariable() instead. -use Friendica\Core\Config; +use Friendica\Core\Config\ConfigCache; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Util\DateTimeFormat; @@ -33,6 +31,10 @@ class DBA public static $connected = false; + /** + * @var ConfigCache + */ + private static $config; private static $server_info = ''; private static $connection; private static $driver; @@ -48,13 +50,14 @@ class DBA private static $db_name = ''; private static $db_charset = ''; - public static function connect($serveraddr, $user, $pass, $db, $charset = null) + public static function connect($config, $serveraddr, $user, $pass, $db, $charset = null) { if (!is_null(self::$connection) && self::connected()) { return true; } // We are storing these values for being able to perform a reconnect + self::$config = $config; self::$db_serveraddr = $serveraddr; self::$db_user = $user; self::$db_pass = $pass; @@ -155,7 +158,7 @@ class DBA public static function reconnect() { self::disconnect(); - $ret = self::connect(self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset); + $ret = self::connect(self::$config, self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset); return $ret; } @@ -209,9 +212,8 @@ class DBA * @throws \Exception */ private static function logIndex($query) { - $a = \get_app(); - if (!Config::getConfigValue('system', 'db_log_index')) { + if (!self::$config->get('system', 'db_log_index')) { return; } @@ -230,18 +232,18 @@ class DBA return; } - $watchlist = explode(',', Config::getConfigValue('system', 'db_log_index_watch')); - $blacklist = explode(',', Config::getConfigValue('system', 'db_log_index_blacklist')); + $watchlist = explode(',', self::$config->get('system', 'db_log_index_watch')); + $blacklist = explode(',', self::$config->get('system', 'db_log_index_blacklist')); while ($row = self::fetch($r)) { - if ((intval(Config::getConfigValue('system', 'db_loglimit_index')) > 0)) { + if ((intval(self::$config->get('system', 'db_loglimit_index')) > 0)) { $log = (in_array($row['key'], $watchlist) && - ($row['rows'] >= intval(Config::getConfigValue('system', 'db_loglimit_index')))); + ($row['rows'] >= intval(self::$config->get('system', 'db_loglimit_index')))); } else { $log = false; } - if ((intval(Config::getConfigValue('system', 'db_loglimit_index_high')) > 0) && ($row['rows'] >= intval($Config::getConfigValue('system', 'db_loglimit_index_high')))) { + if ((intval(self::$config->get('system', 'db_loglimit_index_high')) > 0) && ($row['rows'] >= intval($Config::getConfigValue('system', 'db_loglimit_index_high')))) { $log = true; } @@ -251,7 +253,7 @@ class DBA if ($log) { $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - @file_put_contents(Config::getConfigValue('system', 'db_log_index'), DateTimeFormat::utcNow()."\t". + @file_put_contents(self::$config->get('system', 'db_log_index'), DateTimeFormat::utcNow()."\t". $row['key']."\t".$row['rows']."\t".$row['Extra']."\t". basename($backtrace[1]["file"])."\t". $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t". @@ -421,7 +423,7 @@ class DBA $orig_sql = $sql; - if (Config::getConfigValue('system', 'db_callstack')) { + if (self::$config->get('system', 'db_callstack')) { $sql = "/*".System::callstack()." */ ".$sql; } @@ -582,15 +584,15 @@ class DBA $a->saveTimestamp($stamp1, 'database'); - if (Config::getConfigValue('system', 'db_log')) { + if (self::$config->get('system', 'db_log')) { $stamp2 = microtime(true); $duration = (float)($stamp2 - $stamp1); - if (($duration > Config::getConfigValue('system', 'db_loglimit'))) { + if (($duration > self::$config->get('system', 'db_loglimit'))) { $duration = round($duration, 3); $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - @file_put_contents(Config::getConfigValue('system', 'db_log'), DateTimeFormat::utcNow()."\t".$duration."\t". + @file_put_contents(self::$config->get('system', 'db_log'), DateTimeFormat::utcNow()."\t".$duration."\t". basename($backtrace[1]["file"])."\t". $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t". substr(self::replaceParameters($sql, $args), 0, 2000)."\n", FILE_APPEND); diff --git a/src/Database/DBStructure.php b/src/Database/DBStructure.php index 7d52e60cae..2724670e7b 100644 --- a/src/Database/DBStructure.php +++ b/src/Database/DBStructure.php @@ -101,12 +101,11 @@ class DBStructure * @return array * @throws Exception */ - public static function definition($with_addons_structure = true) + public static function definition($basepath, $with_addons_structure = true) { if (!self::$definition) { - $a = \Friendica\BaseObject::getApp(); - $filename = $a->getBasePath() . '/config/dbstructure.config.php'; + $filename = $basepath . '/config/dbstructure.config.php'; if (!is_readable($filename)) { throw new Exception('Missing database structure config file config/dbstructure.config.php'); diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php new file mode 100644 index 0000000000..1b0b66eec3 --- /dev/null +++ b/src/Factory/ConfigFactory.php @@ -0,0 +1,45 @@ +loadConfigFiles($configCache); + + return $configCache; + } + + /** + * @param string $type The adapter type + * @param Config\IConfigCache $config The config cache of this adapter + * @return Config\IConfigAdapter + */ + public static function createConfig($type, $config) + { + if ($type == 'preload') { + return new Config\PreloadConfigAdapter($config); + } else { + return new Config\JITConfigAdapter($config); + } + } + + /** + * @param string $type The adapter type + * @param int $uid The UID of the current user + * @param Config\IPConfigCache $config The config cache of this adapter + * @return Config\IPConfigAdapter + */ + public static function createPConfig($type, $uid, $config) + { + if ($type == 'preload') { + return new Config\PreloadPConfigAdapter($uid, $config); + } else { + return new Config\JITPConfigAdapter($config); + } + } +} diff --git a/src/Factory/LoggerFactory.php b/src/Factory/LoggerFactory.php new file mode 100644 index 0000000000..658d2c9bee --- /dev/null +++ b/src/Factory/LoggerFactory.php @@ -0,0 +1,135 @@ +pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); + $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); + $logger->pushProcessor(new Monolog\Processor\UidProcessor()); + $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger'])); + + if (isset($config)) { + $debugging = $config->get('system', 'debugging'); + $stream = $config->get('system', 'logfile'); + $level = $config->get('system', 'loglevel'); + + if ($debugging) { + static::addStreamHandler($logger, $stream, $level); + } + } + + return $logger; + } + + /** + * Creates a new PSR-3 compliant develop logger + * + * If you want to debug only interactions from your IP or the IP of a remote server for federation debug, + * you'll use this logger instance for the duration of your work. + * + * It should never get filled during normal usage of Friendica + * + * @param string $channel The channel of the logger instance + * @param string $developerIp The IP of the developer who wants to use the logger + * + * @return LoggerInterface The PSR-3 compliant logger instance + */ + public static function createDev($channel, $developerIp) + { + $logger = new Monolog\Logger($channel); + $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); + $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); + $logger->pushProcessor(new Monolog\Processor\UidProcessor()); + $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger'])); + + + $logger->pushHandler(new FriendicaDevelopHandler($developerIp)); + + return $logger; + } + + /** + * Adding a handler to a given logger instance + * + * @param LoggerInterface $logger The logger instance + * @param mixed $stream The stream which handles the logger output + * @param string $level The level, for which this handler at least should handle logging + * + * @return void + * + * @throws InternalServerErrorException if the logger is incompatible to the logger factory + * @throws \Exception in case of general failures + */ + public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE) + { + if ($logger instanceof Monolog\Logger) { + $loglevel = Monolog\Logger::toMonologLevel($level); + + // fallback to notice if an invalid loglevel is set + if (!is_int($loglevel)) { + $loglevel = LogLevel::NOTICE; + } + $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel); + + $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n"); + $fileHandler->setFormatter($formatter); + + $logger->pushHandler($fileHandler); + } else { + throw new InternalServerErrorException('Logger instance incompatible for MonologFactory'); + } + } + + /** + * This method enables the test mode of a given logger + * + * @param LoggerInterface $logger The logger + * + * @return Monolog\Handler\TestHandler the Handling for tests + * + * @throws InternalServerErrorException if the logger is incompatible to the logger factory + */ + public static function enableTest($logger) + { + if ($logger instanceof Monolog\Logger) { + // disable every handler so far + $logger->pushHandler(new Monolog\Handler\NullHandler()); + + // enable the test handler + $fileHandler = new Monolog\Handler\TestHandler(); + $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n"); + $fileHandler->setFormatter($formatter); + + $logger->pushHandler($fileHandler); + + return $fileHandler; + } else { + throw new InternalServerErrorException('Logger instance incompatible for MonologFactory'); + } + } +} diff --git a/src/LegacyModule.php b/src/LegacyModule.php index a0b23a5419..4fdfc17d9f 100644 --- a/src/LegacyModule.php +++ b/src/LegacyModule.php @@ -68,7 +68,7 @@ class LegacyModule extends BaseModule if (\function_exists($function_name)) { $a = self::getApp(); - return $function_name($a); + return $function_name($a, $a->getConfig()); } else { return parent::{$function_suffix}(); } diff --git a/src/Util/BasePath.php b/src/Util/BasePath.php new file mode 100644 index 0000000000..56b0fa1fe9 --- /dev/null +++ b/src/Util/BasePath.php @@ -0,0 +1,93 @@ +pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); - $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); - $logger->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger'])); - - return $logger; - } - - /** - * Creates a new PSR-3 compliant develop logger - * - * If you want to debug only interactions from your IP or the IP of a remote server for federation debug, - * you'll use this logger instance for the duration of your work. - * - * It should never get filled during normal usage of Friendica - * - * @param string $channel The channel of the logger instance - * @param string $developerIp The IP of the developer who wants to use the logger - * - * @return LoggerInterface The PSR-3 compliant logger instance - */ - public static function createDev($channel, $developerIp) - { - $logger = new Monolog\Logger($channel); - $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); - $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); - $logger->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger'])); - - - $logger->pushHandler(new FriendicaDevelopHandler($developerIp)); - - return $logger; - } - - /** - * Adding a handler to a given logger instance - * - * @param LoggerInterface $logger The logger instance - * @param mixed $stream The stream which handles the logger output - * @param string $level The level, for which this handler at least should handle logging - * - * @return void - * - * @throws InternalServerErrorException if the logger is incompatible to the logger factory - * @throws \Exception in case of general failures - */ - public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE) - { - if ($logger instanceof Monolog\Logger) { - $loglevel = Monolog\Logger::toMonologLevel($level); - - // fallback to notice if an invalid loglevel is set - if (!is_int($loglevel)) { - $loglevel = LogLevel::NOTICE; - } - $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel); - - $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n"); - $fileHandler->setFormatter($formatter); - - $logger->pushHandler($fileHandler); - } else { - throw new InternalServerErrorException('Logger instance incompatible for MonologFactory'); - } - } - - /** - * This method enables the test mode of a given logger - * - * @param LoggerInterface $logger The logger - * - * @return Monolog\Handler\TestHandler the Handling for tests - * - * @throws InternalServerErrorException if the logger is incompatible to the logger factory - */ - public static function enableTest($logger) - { - if ($logger instanceof Monolog\Logger) { - // disable every handler so far - $logger->pushHandler(new Monolog\Handler\NullHandler()); - - // enable the test handler - $fileHandler = new Monolog\Handler\TestHandler(); - $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n"); - $fileHandler->setFormatter($formatter); - - $logger->pushHandler($fileHandler); - - return $fileHandler; - } else { - throw new InternalServerErrorException('Logger instance incompatible for MonologFactory'); - } - } -}