diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index 11df438952..bf6d069d12 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -32,10 +32,7 @@ * */ -use Friendica\App; -use Friendica\Core\Config; use Friendica\Factory; -use Friendica\Util\BasePath; use Friendica\Util\ExAuth; if (sizeof($_SERVER["argv"]) == 0) { @@ -54,12 +51,7 @@ chdir($directory); require dirname(__DIR__) . '/vendor/autoload.php'; -$basedir = BasePath::create(dirname(__DIR__), $_SERVER); -$configLoader = new Config\ConfigCacheLoader($basedir); -$config = Factory\ConfigFactory::createCache($configLoader); -$logger = Factory\LoggerFactory::create('auth_ejabberd', $config); - -$a = new App($config, $logger); +$a = Factory\DependencyFactory::setUp('auth_ejabbered', dirname(__DIR__)); if ($a->getMode()->isNormal()) { $oAuth = new ExAuth(); diff --git a/bin/console.php b/bin/console.php index 9061824d87..410eabda09 100755 --- a/bin/console.php +++ b/bin/console.php @@ -3,16 +3,9 @@ require dirname(__DIR__) . '/vendor/autoload.php'; -use Friendica\Core\Config; use Friendica\Factory; -use Friendica\Util\BasePath; -$basedir = BasePath::create(dirname(__DIR__), $_SERVER); -$configLoader = new Config\ConfigCacheLoader($basedir); -$config = Factory\ConfigFactory::createCache($configLoader); -$logger = Factory\LoggerFactory::create('console', $config); - -$a = new Friendica\App($config, $logger); +$a = Factory\DependencyFactory::setUp('console', dirname(__DIR__)); \Friendica\BaseObject::setApp($a); (new Friendica\Core\Console($argv))->execute(); diff --git a/bin/daemon.php b/bin/daemon.php index 5c014a9270..047bf71be7 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -7,12 +7,11 @@ * This script was taken from http://php.net/manual/en/function.pcntl-fork.php */ -use Friendica\App; use Friendica\Core\Config; +use Friendica\Core\Logger; use Friendica\Core\Worker; use Friendica\Database\DBA; use Friendica\Factory; -use Friendica\Util\BasePath; // Get options $shortopts = 'f'; @@ -33,12 +32,7 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) { require dirname(__DIR__) . '/vendor/autoload.php'; -$basedir = BasePath::create(dirname(__DIR__), $_SERVER); -$configLoader = new Config\ConfigCacheLoader($basedir); -$config = Factory\ConfigFactory::createCache($configLoader); -$logger = Factory\LoggerFactory::create('daemon', $config); - -$a = new App($config, $logger); +$a = Factory\DependencyFactory::setUp('daemon', dirname(__DIR__)); if ($a->getMode()->isInstall()) { die("Friendica isn't properly installed yet.\n"); @@ -108,7 +102,7 @@ if ($mode == "stop") { unlink($pidfile); - $logger->notice("Worker daemon process was killed", ["pid" => $pid]); + Logger::notice("Worker daemon process was killed", ["pid" => $pid]); Config::set('system', 'worker_daemon_mode', false); die("Worker daemon process $pid was killed.\n"); @@ -118,7 +112,7 @@ if (!empty($pid) && posix_kill($pid, 0)) { die("Daemon process $pid is already running.\n"); } -$logger->notice('Starting worker daemon.', ["pid" => $pid]); +Logger::notice('Starting worker daemon.', ["pid" => $pid]); if (!$foreground) { echo "Starting worker daemon.\n"; @@ -150,7 +144,7 @@ if (!$foreground) { file_put_contents($pidfile, $pid); // We lose the database connection upon forking - $a->loadDatabase(); + Factory\DBFactory::init($a->getConfigCache(), $a->getProfiler(), $_SERVER); } Config::set('system', 'worker_daemon_mode', true); @@ -166,7 +160,7 @@ $last_cron = 0; // Now running as a daemon. while (true) { if (!$do_cron && ($last_cron + $wait_interval) < time()) { - $logger->info('Forcing cron worker call.', ["pid" => $pid]); + Logger::info('Forcing cron worker call.', ["pid" => $pid]); $do_cron = true; } @@ -180,7 +174,7 @@ while (true) { $last_cron = time(); } - $logger->info("Sleeping", ["pid" => $pid]); + Logger::info("Sleeping", ["pid" => $pid]); $start = time(); do { $seconds = (time() - $start); @@ -197,10 +191,10 @@ while (true) { if ($timeout) { $do_cron = true; - $logger->info("Woke up after $wait_interval seconds.", ["pid" => $pid, 'sleep' => $wait_interval]); + Logger::info("Woke up after $wait_interval seconds.", ["pid" => $pid, 'sleep' => $wait_interval]); } else { $do_cron = false; - $logger->info("Worker jobs are calling to be forked.", ["pid" => $pid]); + Logger::info("Worker jobs are calling to be forked.", ["pid" => $pid]); } } diff --git a/bin/worker.php b/bin/worker.php index 553e984977..c7174d81e1 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -10,7 +10,6 @@ use Friendica\Core\Config; use Friendica\Core\Update; use Friendica\Core\Worker; use Friendica\Factory; -use Friendica\Util\BasePath; // Get options $shortopts = 'sn'; @@ -31,12 +30,7 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) { require dirname(__DIR__) . '/vendor/autoload.php'; -$basedir = BasePath::create(dirname(__DIR__), $_SERVER); -$configLoader = new Config\ConfigCacheLoader($basedir); -$config = Factory\ConfigFactory::createCache($configLoader); -$logger = Factory\LoggerFactory::create('worker', $config); - -$a = new App($config, $logger); +$a = Factory\DependencyFactory::setUp('worker', dirname(__DIR__)); // Check the database structure and possibly fixes it Update::check($a->getBasePath(), true); diff --git a/composer.json b/composer.json index 39d9358926..3638a94959 100644 --- a/composer.json +++ b/composer.json @@ -34,13 +34,15 @@ "lightopenid/lightopenid": "dev-master", "michelf/php-markdown": "^1.7", "mobiledetect/mobiledetectlib": "2.8.*", + "monolog/monolog": "^1.24", "paragonie/random_compat": "^2.0", - "pear/Text_LanguageDetect": "1.*", + "pear/text_languagedetect": "1.*", + "psr/container": "^1.0", "seld/cli-prompt": "^1.0", "smarty/smarty": "^3.1", "fxp/composer-asset-plugin": "~1.3", "bower-asset/base64": "^1.0", - "bower-asset/Chart-js": "^2.7", + "bower-asset/chart-js": "^2.7", "bower-asset/perfect-scrollbar": "^0.6", "bower-asset/vue": "^2.5", "npm-asset/jquery": "^2.0", @@ -49,8 +51,7 @@ "npm-asset/jgrowl": "^1.4", "npm-asset/fullcalendar": "^3.0.1", "npm-asset/cropperjs": "1.2.2", - "npm-asset/imagesloaded": "4.1.4", - "monolog/monolog": "^1.24" + "npm-asset/imagesloaded": "4.1.4" }, "repositories": [ { @@ -96,7 +97,7 @@ "phpunit/dbunit": "^2.0", "phpdocumentor/reflection-docblock": "^3.0.2", "phpunit/php-token-stream": "^1.4.2", - "mikey179/vfsStream": "^1.6", + "mikey179/vfsstream": "^1.6", "mockery/mockery": "^1.2", "johnkary/phpunit-speedtrap": "1.1" }, diff --git a/composer.lock b/composer.lock index f91b833de7..e554b11999 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b944adfb6ede89430ba3b7e9ebb45acb", + "content-hash": "19fabb14e0dd5d806ef841e51d5f6a0b", "packages": [ { "name": "asika/simple-console", @@ -1882,6 +1882,55 @@ ], "time": "2016-08-06T20:24:11+00:00" }, + { + "name": "psr/container", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "time": "2017-02-14T16:28:37+00:00" + }, { "name": "psr/http-message", "version": "1.0.1", @@ -2650,6 +2699,7 @@ "testing", "xunit" ], + "abandoned": true, "time": "2016-12-02T14:39:14+00:00" }, { diff --git a/include/api.php b/include/api.php index 052d32b17b..13ff75bdb5 100644 --- a/include/api.php +++ b/include/api.php @@ -326,69 +326,7 @@ function api_call(App $a) Logger::info(API_LOG_PREFIX . 'username {username}', ['module' => 'api', 'action' => 'call', 'username' => $a->user['username'], 'duration' => round($duration, 2)]); - if (Config::get("system", "profiler")) { - $duration = microtime(true)-$a->performance["start"]; - - /// @TODO round() really everywhere? - Logger::debug( - API_LOG_PREFIX . 'performance', - [ - 'module' => 'api', - 'action' => 'call', - 'database_read' => round($a->performance["database"] - $a->performance["database_write"], 3), - 'database_write' => round($a->performance["database_write"], 3), - 'cache_read' => round($a->performance["cache"], 3), - 'cache_write' => round($a->performance["cache_write"], 3), - 'network_io' => round($a->performance["network"], 2), - 'file_io' => round($a->performance["file"], 2), - 'other_io' => round($duration - ($a->performance["database"] - + $a->performance["cache"] + $a->performance["cache_write"] - + $a->performance["network"] + $a->performance["file"]), 2), - 'total' => round($duration, 2) - ] - ); - - if (Config::get("rendertime", "callstack")) { - $o = "Database Read:\n"; - foreach ($a->callstack["database"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func . ": " . $time . "\n"; - } - } - $o .= "\nDatabase Write:\n"; - foreach ($a->callstack["database_write"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func . ": " . $time . "\n"; - } - } - - $o = "Cache Read:\n"; - foreach ($a->callstack["cache"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func . ": " . $time . "\n"; - } - } - $o .= "\nCache Write:\n"; - foreach ($a->callstack["cache_write"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func . ": " . $time . "\n"; - } - } - - $o .= "\nNetwork:\n"; - foreach ($a->callstack["network"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func . ": " . $time . "\n"; - } - } - Logger::debug(API_LOG_PREFIX . $o, ['module' => 'api', 'action' => 'call']); - } - } + $a->getProfiler()->saveLog($a->getLogger(), API_LOG_PREFIX . 'performance'); if (false === $return) { /* diff --git a/index.php b/index.php index 7e7396785f..47264362c5 100644 --- a/index.php +++ b/index.php @@ -4,10 +4,7 @@ * Friendica */ -use Friendica\App; -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.'); @@ -15,13 +12,6 @@ if (!file_exists(__DIR__ . '/vendor/autoload.php')) { require __DIR__ . '/vendor/autoload.php'; -$basedir = BasePath::create(__DIR__, $_SERVER); -$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($config, $logger, false); +$a = Factory\DependencyFactory::setUp('index', __DIR__, true); $a->runFrontend(); diff --git a/src/App.php b/src/App.php index b9e22dd1b5..ca2b6dfc21 100644 --- a/src/App.php +++ b/src/App.php @@ -8,11 +8,12 @@ use Detection\MobileDetect; use DOMDocument; use DOMXPath; use Exception; -use Friendica\Core\Config\ConfigCache; -use Friendica\Core\Config\ConfigCacheLoader; +use Friendica\Core\Config\Cache\ConfigCacheLoader; +use Friendica\Core\Config\Cache\IConfigCache; +use Friendica\Core\Config\Configuration; use Friendica\Database\DBA; -use Friendica\Factory\ConfigFactory; use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\Profiler; use Psr\Log\LoggerInterface; /** @@ -53,8 +54,6 @@ class App public $identities; public $is_mobile = false; public $is_tablet = false; - public $performance = []; - public $callstack = []; public $theme_info = []; public $category; // Allow themes to control internal parameters @@ -110,23 +109,28 @@ class App public $mobileDetect; /** - * @var LoggerInterface The current logger of this App - */ - private $logger; - - /** - * @var ConfigCache The cached config + * @var Configuration The config */ private $config; + /** + * @var LoggerInterface The logger + */ + private $logger; + + /** + * @var Profiler The profiler of this app + */ + private $profiler; + /** * Returns the current config cache of this node * - * @return ConfigCache + * @return IConfigCache */ - public function getConfig() + public function getConfigCache() { - return $this->config; + return $this->config->getCache(); } /** @@ -139,6 +143,26 @@ class App return $this->basePath; } + /** + * The Logger of this app + * + * @return LoggerInterface + */ + public function getLogger() + { + return $this->logger; + } + + /** + * The profiler of this app + * + * @return Profiler + */ + public function getProfiler() + { + return $this->profiler; + } + /** * Register a stylesheet file path to be included in the tag of every page. * Inclusion is done in App->initHead(). @@ -181,16 +205,18 @@ class App /** * @brief App constructor. * - * @param ConfigCache $config The Cached Config - * @param LoggerInterface $logger Logger of this application + * @param Configuration $config The Configuration + * @param LoggerInterface $logger The current app logger + * @param Profiler $profiler The profiler 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(ConfigCache $config, LoggerInterface $logger, $isBackend = true) + public function __construct(Configuration $config, LoggerInterface $logger, Profiler $profiler, $isBackend = true) { - $this->config = $config; $this->logger = $logger; + $this->config = $config; + $this->profiler = $profiler; $this->basePath = $this->config->get('system', 'basepath'); if (!Core\System::isDirectoryUsable($this->basePath, false)) { @@ -203,26 +229,7 @@ class App $this->checkBackend($isBackend); $this->checkFriendicaApp(); - $this->performance['start'] = microtime(true); - $this->performance['database'] = 0; - $this->performance['database_write'] = 0; - $this->performance['cache'] = 0; - $this->performance['cache_write'] = 0; - $this->performance['network'] = 0; - $this->performance['file'] = 0; - $this->performance['rendering'] = 0; - $this->performance['parser'] = 0; - $this->performance['marktime'] = 0; - $this->performance['markstart'] = microtime(true); - - $this->callstack['database'] = []; - $this->callstack['database_write'] = []; - $this->callstack['cache'] = []; - $this->callstack['cache_write'] = []; - $this->callstack['network'] = []; - $this->callstack['file'] = []; - $this->callstack['rendering'] = []; - $this->callstack['parser'] = []; + $this->profiler->reset(); $this->mode = new App\Mode($this->basePath); @@ -341,52 +348,20 @@ class App return $this->mode; } - /** - * Returns the Logger of the Application - * - * @return LoggerInterface The Logger - * @throws InternalServerErrorException when the logger isn't created - */ - public function getLogger() - { - if (empty($this->logger)) { - throw new InternalServerErrorException('Logger of the Application is not defined'); - } - - return $this->logger; - } - /** * Reloads the whole app instance */ public function reload() { - Core\Config::init($this->config); - Core\PConfig::init($this->config); - - $this->loadDatabase(); - - $this->getMode()->determine($this->basePath); - $this->determineURLPath(); - 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::createPConfig($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(); $loader = new ConfigCacheLoader($this->basePath); Core\Hook::callAll('load_config', $loader); - $this->config->loadConfigArray($loader->loadCoreConfig('addon'), true); + $this->config->getCache()->load($loader->loadCoreConfig('addon'), true); } $this->loadDefaultTimezone(); @@ -394,8 +369,6 @@ class App Core\L10n::init(); $this->process_id = Core\System::processID('log'); - - Core\Logger::setLogger($this->logger); } /** @@ -456,49 +429,6 @@ class App } } - public function loadDatabase() - { - if (DBA::connected()) { - return; - } - - $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')) - && !empty(getenv('MYSQL_USERNAME') || !empty(getenv('MYSQL_USER'))) - && getenv('MYSQL_PASSWORD') !== false - && !empty(getenv('MYSQL_DATABASE'))) - { - $db_host = getenv('MYSQL_HOST'); - if (!empty(getenv('MYSQL_PORT'))) { - $db_host .= ':' . getenv('MYSQL_PORT'); - } - if (!empty(getenv('MYSQL_USERNAME'))) { - $db_user = getenv('MYSQL_USERNAME'); - } else { - $db_user = getenv('MYSQL_USER'); - } - $db_pass = (string) getenv('MYSQL_PASSWORD'); - $db_data = getenv('MYSQL_DATABASE'); - } - - $stamp1 = microtime(true); - - if (DBA::connect($this->config, $db_host, $db_user, $db_pass, $db_data, $charset)) { - // Loads DB_UPDATE_VERSION constant - Database\DBStructure::definition($this->basePath, false); - } - - unset($db_host, $db_user, $db_pass, $db_data, $charset); - - $this->saveTimestamp($stamp1, 'network'); - } - public function getScheme() { return $this->scheme; @@ -740,41 +670,6 @@ class App } } - /** - * Saves a timestamp for a value - f.e. a call - * Necessary for profiling Friendica - * - * @param int $timestamp the Timestamp - * @param string $value A value to profile - */ - public function saveTimestamp($timestamp, $value) - { - $profiler = $this->config->get('system', 'profiler'); - - if (!isset($profiler) || !$profiler) { - return; - } - - $duration = (float) (microtime(true) - $timestamp); - - if (!isset($this->performance[$value])) { - // Prevent ugly E_NOTICE - $this->performance[$value] = 0; - } - - $this->performance[$value] += (float) $duration; - $this->performance['marktime'] += (float) $duration; - - $callstack = Core\System::callstack(); - - if (!isset($this->callstack[$value][$callstack])) { - // Prevent ugly E_NOTICE - $this->callstack[$value][$callstack] = 0; - } - - $this->callstack[$value][$callstack] += (float) $duration; - } - /** * Returns the current UserAgent as a String * @@ -1225,7 +1120,7 @@ class App if (!$this->isBackend()) { $stamp1 = microtime(true); session_start(); - $this->saveTimestamp($stamp1, 'parser'); + $this->profiler->saveTimestamp($stamp1, 'parser', Core\System::callstack()); Core\L10n::setSessionVariable(); Core\L10n::setLangFromSession(); } else { diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index a8b5ec2025..e9b5f98d0b 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1027,7 +1027,7 @@ class BBCode extends BaseObject @curl_exec($ch); $curl_info = @curl_getinfo($ch); - $a->saveTimestamp($stamp1, "network"); + $a->getProfiler()->saveTimestamp($stamp1, "network", System::callstack()); if (substr($curl_info["content_type"], 0, 6) == "image/") { $text = "[url=" . $match[1] . "]" . $match[1] . "[/url]"; @@ -1086,7 +1086,7 @@ class BBCode extends BaseObject @curl_exec($ch); $curl_info = @curl_getinfo($ch); - $a->saveTimestamp($stamp1, "network"); + $a->getProfiler()->saveTimestamp($stamp1, "network", System::callstack()); // if its a link to a picture then embed this picture if (substr($curl_info["content_type"], 0, 6) == "image/") { @@ -1915,7 +1915,7 @@ class BBCode extends BaseObject // unmask the special chars back to HTML $text = str_replace(['&\_lt\_;', '&\_gt\_;', '&\_amp\_;'], ['<', '>', '&'], $text); - $a->saveTimestamp($stamp1, "parser"); + $a->getProfiler()->saveTimestamp($stamp1, "parser", System::callstack()); // Libertree has a problem with escaped hashtags. $text = str_replace(['\#'], ['#'], $text); diff --git a/src/Content/Text/Markdown.php b/src/Content/Text/Markdown.php index ceb5b043b3..e3e2cd3ac9 100644 --- a/src/Content/Text/Markdown.php +++ b/src/Content/Text/Markdown.php @@ -7,6 +7,7 @@ namespace Friendica\Content\Text; use Friendica\BaseObject; +use Friendica\Core\System; use Friendica\Model\Contact; use Michelf\MarkdownExtra; @@ -36,7 +37,7 @@ class Markdown extends BaseObject $html = $MarkdownParser->transform($text); $html = preg_replace('/getProfiler()->saveTimestamp($stamp1, "parser", System::callstack()); return $html; } diff --git a/src/Core/Addon.php b/src/Core/Addon.php index 6697a44aea..7957e08350 100644 --- a/src/Core/Addon.php +++ b/src/Core/Addon.php @@ -219,7 +219,7 @@ class Addon extends BaseObject $stamp1 = microtime(true); $f = file_get_contents("addon/$addon/$addon.php"); - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); $r = preg_match("|/\*.*\*/|msU", $f, $m); diff --git a/src/Core/Cache.php b/src/Core/Cache.php index 39c29566d3..cadb2444b5 100644 --- a/src/Core/Cache.php +++ b/src/Core/Cache.php @@ -63,7 +63,7 @@ class Cache extends \Friendica\BaseObject $return = self::getDriver()->getAllKeys($prefix); - self::getApp()->saveTimestamp($time, 'cache'); + self::getApp()->getProfiler()->saveTimestamp($time, 'cache', System::callstack()); return $return; } @@ -82,7 +82,7 @@ class Cache extends \Friendica\BaseObject $return = self::getDriver()->get($key); - self::getApp()->saveTimestamp($time, 'cache'); + self::getApp()->getProfiler()->saveTimestamp($time, 'cache', System::callstack()); return $return; } @@ -105,7 +105,7 @@ class Cache extends \Friendica\BaseObject $return = self::getDriver()->set($key, $value, $duration); - self::getApp()->saveTimestamp($time, 'cache_write'); + self::getApp()->getProfiler()->saveTimestamp($time, 'cache_write', System::callstack()); return $return; } @@ -124,7 +124,7 @@ class Cache extends \Friendica\BaseObject $return = self::getDriver()->delete($key); - self::getApp()->saveTimestamp($time, 'cache_write'); + self::getApp()->getProfiler()->saveTimestamp($time, 'cache_write', System::callstack()); return $return; } diff --git a/src/Core/Config.php b/src/Core/Config.php index 559ee83ece..4bf9c5b11d 100644 --- a/src/Core/Config.php +++ b/src/Core/Config.php @@ -8,10 +8,6 @@ */ namespace Friendica\Core; -use Friendica\Core\Config\ConfigCache; -use Friendica\Core\Config\IConfigAdapter; -use Friendica\Core\Config\IConfigCache; - /** * @brief Arbitrary system configuration storage * @@ -22,116 +18,76 @@ use Friendica\Core\Config\IConfigCache; class Config { /** - * @var Config\IConfigAdapter|null + * @var Config\Configuration */ - private static $adapter; + private static $config; /** - * @var Config\IConfigCache - */ - private static $cache; - - /** - * Initialize the config with only the cache + * Initialize the config * - * @param Config\IConfigCache $cache The configuration cache + * @param Config\Configuration $config */ - public static function init(Config\IConfigCache $cache) + public static function init(Config\Configuration $config) { - self::$cache = $cache; - } - - /** - * Add the adapter for DB-backend - * - * @param Config\IConfigAdapter $adapter - */ - public static function setAdapter(Config\IConfigAdapter $adapter) - { - self::$adapter = $adapter; + self::$config = $config; } /** * @brief Loads all configuration values of family into a cached storage. * - * All configuration values of the system are stored in the cache ( @see IConfigCache ) - * - * @param string $family The category of the configuration value + * @param string $cat The category of the configuration value * * @return void */ - public static function load($family = "config") + public static function load($cat = "config") { - if (!isset(self::$adapter) || !self::$adapter->isConnected()) { - return; - } - - self::$adapter->load($family); + self::$config->load($cat); } /** * @brief Get a particular user's config variable given the category name * ($family) and a key. * - * Get a particular config value from the given category ($family) - * and the $key from a cached storage either from the self::$adapter - * (@see IConfigAdapter ) or from the static::$cache (@see IConfigCache ). - * - * @param string $family The category of the configuration value + * @param string $cat The category of the configuration value * @param string $key The configuration key to query * @param mixed $default_value optional, The value to return if key is not set (default: null) * @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 */ - public static function get($family, $key, $default_value = null, $refresh = false) + public static function get($cat, $key, $default_value = null, $refresh = false) { - if (!isset(self::$adapter) || !self::$adapter->isConnected()) { - return self::$cache->get($family, $key, $default_value); - } - - return self::$adapter->get($family, $key, $default_value, $refresh); + return self::$config->get($cat, $key, $default_value, $refresh); } /** * @brief Sets a configuration value for system config * - * Stores a config value ($value) in the category ($family) under the key ($key) + * Stores a config value ($value) in the category ($cat) under the key ($key) * * Note: Please do not store booleans - convert to 0/1 integer values! * - * @param string $family The category of the configuration value + * @param string $cat The category of the configuration value * @param string $key The configuration key to set * @param mixed $value The value to store * * @return bool Operation success */ - public static function set($family, $key, $value) + public static function set($cat, $key, $value) { - if (!isset(self::$adapter) || !self::$adapter->isConnected()) { - return self::$cache->set($family, $key, $value); - } - - return self::$adapter->set($family, $key, $value); + return self::$config->set($cat, $key, $value); } /** * @brief Deletes the given key from the system configuration. * - * Removes the configured value from the stored cache in self::$config - * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ). - * - * @param string $family The category of the configuration value + * @param string $cat The category of the configuration value * @param string $key The configuration key to delete * - * @return mixed + * @return bool */ - public static function delete($family, $key) + public static function delete($cat, $key) { - if (!isset(self::$adapter) || !self::$adapter->isConnected()) { - self::$cache->delete($family, $key); - } - - return self::$adapter->delete($family, $key); + return self::$config->delete($cat, $key); } } diff --git a/src/Core/Config/AbstractDbaConfigAdapter.php b/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php similarity index 52% rename from src/Core/Config/AbstractDbaConfigAdapter.php rename to src/Core/Config/Adapter/AbstractDbaConfigAdapter.php index bae75122a3..770dfd2c95 100644 --- a/src/Core/Config/AbstractDbaConfigAdapter.php +++ b/src/Core/Config/Adapter/AbstractDbaConfigAdapter.php @@ -1,12 +1,19 @@ connected = DBA::connected(); + } + public function isConnected() { return $this->connected; diff --git a/src/Core/Config/IConfigAdapter.php b/src/Core/Config/Adapter/IConfigAdapter.php similarity index 50% rename from src/Core/Config/IConfigAdapter.php rename to src/Core/Config/Adapter/IConfigAdapter.php index 70e141484e..21cd9a4b2a 100644 --- a/src/Core/Config/IConfigAdapter.php +++ b/src/Core/Config/Adapter/IConfigAdapter.php @@ -1,6 +1,6 @@ !" if it does not exist */ - public function get($cat, $k, $default_value = null, $refresh = false); + public function get($cat, $key); /** - * Stores a config value ($value) in the category ($family) under the key ($key) - * for the user_id $uid. + * Stores a config value ($value) in the category ($family) under the key ($key). * * Note: Please do not store booleans - convert to 0/1 integer values! * * @param string $cat The category of the configuration value - * @param string $k The configuration key to set + * @param string $key The configuration key to set * @param mixed $value The value to store * * @return bool Operation success */ - public function set($cat, $k, $value); + public function set($cat, $key, $value); /** * Removes the configured value from the stored cache * and removes it from the database. * * @param string $cat The category of the configuration value - * @param string $k The configuration key to delete + * @param string $key The configuration key to delete * * @return mixed */ - public function delete($cat, $k); + public function delete($cat, $key); /** * Checks, if the current adapter is connected to the backend @@ -61,4 +58,14 @@ interface IConfigAdapter * @return bool */ public function isConnected(); + + /** + * Checks, if a config key ($key) in the category ($cat) is already loaded. + * + * @param string $cat The configuration category + * @param string $key The configuration key + * + * @return bool + */ + public function isLoaded($cat, $key); } diff --git a/src/Core/Config/IPConfigAdapter.php b/src/Core/Config/Adapter/IPConfigAdapter.php similarity index 55% rename from src/Core/Config/IPConfigAdapter.php rename to src/Core/Config/Adapter/IPConfigAdapter.php index e62fc9c93f..8e6c050b27 100644 --- a/src/Core/Config/IPConfigAdapter.php +++ b/src/Core/Config/Adapter/IPConfigAdapter.php @@ -6,7 +6,7 @@ * and open the template in the editor. */ -namespace Friendica\Core\Config; +namespace Friendica\Core\Config\Adapter; /** * @@ -15,12 +15,12 @@ namespace Friendica\Core\Config; interface IPConfigAdapter { /** - * Loads all configuration values of a user's config family into a cached storage. + * Loads all configuration values of a user's config family and returns the loaded category as an array. * * @param string $uid The user_id * @param string $cat The category of the configuration value * - * @return void + * @return array */ public function load($uid, $cat); @@ -30,13 +30,11 @@ interface IPConfigAdapter * * @param string $uid The user_id * @param string $cat The category of the configuration value - * @param string $k The configuration key to query - * @param mixed $default_value optional, The value to return if key is not set (default: null) - * @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) + * @param string $key The configuration key to query * - * @return mixed Stored value or null if it does not exist + * @return mixed Stored value or "!!" if it does not exist */ - public function get($uid, $cat, $k, $default_value = null, $refresh = false); + public function get($uid, $cat, $key); /** * Stores a config value ($value) in the category ($family) under the key ($key) @@ -46,12 +44,12 @@ interface IPConfigAdapter * * @param string $uid The user_id * @param string $cat The category of the configuration value - * @param string $k The configuration key to set + * @param string $key The configuration key to set * @param string $value The value to store * * @return bool Operation success */ - public function set($uid, $cat, $k, $value); + public function set($uid, $cat, $key, $value); /** * Removes the configured value from the stored cache @@ -59,9 +57,27 @@ interface IPConfigAdapter * * @param string $uid The user_id * @param string $cat The category of the configuration value - * @param string $k The configuration key to delete + * @param string $key The configuration key to delete * - * @return mixed + * @return bool */ - public function delete($uid, $cat, $k); + public function delete($uid, $cat, $key); + + /** + * Checks, if the current adapter is connected to the backend + * + * @return bool + */ + public function isConnected(); + + /** + * Checks, if a config key ($key) in the category ($cat) is already loaded for the user_id $uid. + * + * @param string $uid The user_id + * @param string $cat The configuration category + * @param string $key The configuration key + * + * @return bool + */ + public function isLoaded($uid, $cat, $key); } diff --git a/src/Core/Config/Adapter/JITConfigAdapter.php b/src/Core/Config/Adapter/JITConfigAdapter.php new file mode 100644 index 0000000000..95211777f3 --- /dev/null +++ b/src/Core/Config/Adapter/JITConfigAdapter.php @@ -0,0 +1,135 @@ + + */ +class JITConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter +{ + private $in_db; + + /** + * {@inheritdoc} + */ + public function load($cat = "config") + { + $return = []; + + if (!$this->isConnected()) { + return $return; + } + + // We don't preload "system" anymore. + // This reduces the number of database reads a lot. + if ($cat === 'system') { + return $return; + } + + $configs = DBA::select('config', ['v', 'k'], ['cat' => $cat]); + while ($config = DBA::fetch($configs)) { + $key = $config['k']; + + $return[$key] = $config['v']; + $this->in_db[$cat][$key] = true; + } + DBA::close($configs); + + return [$cat => $config]; + } + + /** + * {@inheritdoc} + */ + public function get($cat, $key) + { + if (!$this->isConnected()) { + return '!!'; + } + + $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); + if (DBA::isResult($config)) { + // manage array value + $value = (preg_match("|^a:[0-9]+:{.*}$|s", $config['v']) ? unserialize($config['v']) : $config['v']); + + $this->in_db[$cat][$key] = true; + return $value; + } else { + + $this->in_db[$cat][$key] = false; + return '!!'; + } + } + + /** + * {@inheritdoc} + */ + public function set($cat, $key, $value) + { + if (!$this->isConnected()) { + return false; + } + + // We store our setting values in a string variable. + // So we have to do the conversion here so that the compare below works. + // The exception are array values. + $dbvalue = (!is_array($value) ? (string)$value : $value); + + $stored = $this->get($cat, $key); + + if (!isset($this->in_db[$cat])) { + $this->in_db[$cat] = []; + } + if (!isset($this->in_db[$cat][$key])) { + $this->in_db[$cat][$key] = false; + } + + if (($stored === $dbvalue) && $this->in_db[$cat][$key]) { + return true; + } + + // manage array value + $dbvalue = (is_array($value) ? serialize($value) : $dbvalue); + + $result = DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); + + $this->in_db[$cat][$key] = $result; + + return $result; + } + + /** + * {@inheritdoc} + */ + public function delete($cat, $key) + { + if (!$this->isConnected()) { + return false; + } + + if (isset($this->cache[$cat][$key])) { + unset($this->in_db[$cat][$key]); + } + + $result = DBA::delete('config', ['cat' => $cat, 'k' => $key]); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function isLoaded($cat, $key) + { + if (!$this->isConnected()) { + return false; + } + + return (isset($this->in_db[$cat][$key])) && $this->in_db[$cat][$key]; + } +} diff --git a/src/Core/Config/Adapter/JITPConfigAdapter.php b/src/Core/Config/Adapter/JITPConfigAdapter.php new file mode 100644 index 0000000000..c5f3a381ed --- /dev/null +++ b/src/Core/Config/Adapter/JITPConfigAdapter.php @@ -0,0 +1,138 @@ + + */ +class JITPConfigAdapter extends AbstractDbaConfigAdapter implements IPConfigAdapter +{ + private $in_db; + + /** + * {@inheritdoc} + */ + public function load($uid, $cat) + { + $return = []; + + if (!$this->isConnected()) { + return $return; + } + + $pconfigs = DBA::select('pconfig', ['v', 'k'], ['cat' => $cat, 'uid' => $uid]); + if (DBA::isResult($pconfigs)) { + while ($pconfig = DBA::fetch($pconfigs)) { + $key = $pconfig['k']; + + $return[$key] = $pconfig['v']; + + $this->in_db[$uid][$cat][$key] = true; + } + } else if ($cat != 'config') { + // Negative caching + $return = "!!"; + } + DBA::close($pconfigs); + + return [$cat => $return]; + } + + /** + * {@inheritdoc} + */ + public function get($uid, $cat, $key) + { + if (!$this->isConnected()) { + return '!!'; + } + + $pconfig = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $key]); + if (DBA::isResult($pconfig)) { + // manage array value + $value = (preg_match("|^a:[0-9]+:{.*}$|s", $pconfig['v']) ? unserialize($pconfig['v']) : $pconfig['v']); + + $this->in_db[$uid][$cat][$key] = true; + return $value; + } else { + + $this->in_db[$uid][$cat][$key] = false; + return '!!'; + } + } + + /** + * {@inheritdoc} + */ + public function set($uid, $cat, $key, $value) + { + if (!$this->isConnected()) { + return false; + } + + // We store our setting values in a string variable. + // So we have to do the conversion here so that the compare below works. + // The exception are array values. + $dbvalue = (!is_array($value) ? (string)$value : $value); + + $stored = $this->get($uid, $cat, $key); + + if (!isset($this->in_db[$uid])) { + $this->in_db[$uid] = []; + } + if (!isset($this->in_db[$uid][$cat])) { + $this->in_db[$uid][$cat] = []; + } + if (!isset($this->in_db[$uid][$cat][$key])) { + $this->in_db[$uid][$cat][$key] = false; + } + + if (($stored === $dbvalue) && $this->in_db[$uid][$cat][$key]) { + return true; + } + + // manage array value + $dbvalue = (is_array($value) ? serialize($value) : $dbvalue); + + $result = DBA::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true); + + $this->in_db[$uid][$cat][$key] = $result; + + return $result; + } + + /** + * {@inheritdoc} + */ + public function delete($uid, $cat, $key) + { + if (!$this->isConnected()) { + return false; + } + + if (!empty($this->in_db[$uid][$cat][$key])) { + unset($this->in_db[$uid][$cat][$key]); + } + + $result = DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]); + + return $result; + } + + /** + * {@inheritdoc} + */ + public function isLoaded($uid, $cat, $key) + { + if (!$this->isConnected()) { + return false; + } + + return (isset($this->in_db[$uid][$cat][$key])) && $this->in_db[$uid][$cat][$key]; + } +} diff --git a/src/Core/Config/PreloadConfigAdapter.php b/src/Core/Config/Adapter/PreloadConfigAdapter.php similarity index 51% rename from src/Core/Config/PreloadConfigAdapter.php rename to src/Core/Config/Adapter/PreloadConfigAdapter.php index 96331e7a2c..fa691a16d3 100644 --- a/src/Core/Config/PreloadConfigAdapter.php +++ b/src/Core/Config/Adapter/PreloadConfigAdapter.php @@ -1,8 +1,7 @@ configCache = $configCache; - $this->connected = DBA::connected(); - $this->load(); - } - /** * {@inheritdoc} */ - public function load($family = 'config') + public function load($cat = 'config') { + $return = []; + if (!$this->isConnected()) { - return; + return $return; } if ($this->config_loaded) { - return; + return $return; } $configs = DBA::select('config', ['cat', 'v', 'k']); while ($config = DBA::fetch($configs)) { - $this->configCache->set($config['cat'], $config['k'], $config['v']); + $return[$config['cat']][$config['k']] = $config['v']; } DBA::close($configs); $this->config_loaded = true; - } - - /** - * {@inheritdoc} - */ - public function get($cat, $k, $default_value = null, $refresh = false) - { - if (!$this->isConnected()) { - return $default_value; - } - - if ($refresh) { - $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $k]); - if (DBA::isResult($config)) { - $this->configCache->set($cat, $k, $config['v']); - } - } - - $return = $this->configCache->get($cat, $k, $default_value); return $return; } @@ -77,7 +44,28 @@ class PreloadConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAd /** * {@inheritdoc} */ - public function set($cat, $k, $value) + public function get($cat, $key) + { + if (!$this->isConnected()) { + return '!!'; + } + + $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $key]); + if (DBA::isResult($config)) { + // manage array value + $value = (preg_match("|^a:[0-9]+:{.*}$|s", $config['v']) ? unserialize($config['v']) : $config['v']); + + return $value; + } else { + + return '!!'; + } + } + + /** + * {@inheritdoc} + */ + public function set($cat, $key, $value) { if (!$this->isConnected()) { return false; @@ -88,36 +76,41 @@ class PreloadConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAd // The exception are array values. $compare_value = !is_array($value) ? (string)$value : $value; - if ($this->configCache->get($cat, $k) === $compare_value) { + if ($this->get($cat, $key) === $compare_value) { return true; } - $this->configCache->set($cat, $k, $value); - // manage array value $dbvalue = is_array($value) ? serialize($value) : $value; - $result = DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $k], true); - if (!$result) { - throw new Exception('Unable to store config value in [' . $cat . '][' . $k . ']'); - } + $result = DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $key], true); - return true; + return $result; } /** * {@inheritdoc} */ - public function delete($cat, $k) + public function delete($cat, $key) { if (!$this->isConnected()) { return false; } - $this->configCache->delete($cat, $k); - - $result = DBA::delete('config', ['cat' => $cat, 'k' => $k]); + $result = DBA::delete('config', ['cat' => $cat, 'k' => $key]); return $result; } + + /** + * {@inheritdoc} + */ + public function isLoaded($cat, $key) + { + if (!$this->isConnected()) { + return false; + } + + return $this->config_loaded; + } } diff --git a/src/Core/Config/PreloadPConfigAdapter.php b/src/Core/Config/Adapter/PreloadPConfigAdapter.php similarity index 50% rename from src/Core/Config/PreloadPConfigAdapter.php rename to src/Core/Config/Adapter/PreloadPConfigAdapter.php index af97815ade..e79a4a1e3c 100644 --- a/src/Core/Config/PreloadPConfigAdapter.php +++ b/src/Core/Config/Adapter/PreloadPConfigAdapter.php @@ -1,8 +1,7 @@ */ -class PreloadPConfigAdapter implements IPConfigAdapter +class PreloadPConfigAdapter extends AbstractDbaConfigAdapter implements IPConfigAdapter { private $config_loaded = false; /** - * The config cache of this adapter - * @var IPConfigCache + * @param int $uid The UID of the current user */ - private $configCache; - - /** - * @param IPConfigCache $configCache The config cache of this adapter - * @param int $uid The UID of the current user - */ - public function __construct(IPConfigCache $configCache, $uid = null) + public function __construct($uid = null) { - $this->configCache = $configCache; + parent::__construct(); + if (isset($uid)) { $this->load($uid, 'config'); } @@ -37,51 +30,62 @@ class PreloadPConfigAdapter implements IPConfigAdapter /** * {@inheritdoc} */ - public function load($uid, $family) + public function load($uid, $cat) { + $return = []; + if ($this->config_loaded) { - return; + return $return; } if (empty($uid)) { - return; + return $return; } $pconfigs = DBA::select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]); while ($pconfig = DBA::fetch($pconfigs)) { - $this->configCache->setP($uid, $pconfig['cat'], $pconfig['k'], $pconfig['v']); + $return[$pconfig['cat']][$pconfig['k']] = $pconfig['v']; } DBA::close($pconfigs); $this->config_loaded = true; + + return $return; } /** * {@inheritdoc} */ - public function get($uid, $cat, $k, $default_value = null, $refresh = false) + public function get($uid, $cat, $key) { + if (!$this->isConnected()) { + return '!!'; + } + if (!$this->config_loaded) { $this->load($uid, $cat); } - if ($refresh) { - $config = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $k]); - if (DBA::isResult($config)) { - $this->configCache->setP($uid, $cat, $k, $config['v']); - } else { - $this->configCache->deleteP($uid, $cat, $k); - } - } + $config = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $key]); + if (DBA::isResult($config)) { + // manage array value + $value = (preg_match("|^a:[0-9]+:{.*}$|s", $config['v']) ? unserialize($config['v']) : $config['v']); - return $this->configCache->getP($uid, $cat, $k, $default_value);; + return $value; + } else { + return '!!'; + } } /** * {@inheritdoc} */ - public function set($uid, $cat, $k, $value) + public function set($uid, $cat, $key, $value) { + if (!$this->isConnected()) { + return false; + } + if (!$this->config_loaded) { $this->load($uid, $cat); } @@ -90,36 +94,45 @@ class PreloadPConfigAdapter implements IPConfigAdapter // The exception are array values. $compare_value = !is_array($value) ? (string)$value : $value; - if ($this->configCache->getP($uid, $cat, $k) === $compare_value) { + if ($this->get($uid, $cat, $key) === $compare_value) { return true; } - $this->configCache->setP($uid, $cat, $k, $value); - // manage array value $dbvalue = is_array($value) ? serialize($value) : $value; - $result = DBA::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $k], true); - if (!$result) { - throw new Exception('Unable to store config value in [' . $uid . '][' . $cat . '][' . $k . ']'); - } + $result = DBA::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true); - return true; + return $result; } /** * {@inheritdoc} */ - public function delete($uid, $cat, $k) + public function delete($uid, $cat, $key) { + if (!$this->isConnected()) { + return false; + } + if (!$this->config_loaded) { $this->load($uid, $cat); } - $this->configCache->deleteP($uid, $cat, $k); - - $result = DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $k]); + $result = DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]); return $result; } + + /** + * {@inheritdoc} + */ + public function isLoaded($uid, $cat, $key) + { + if (!$this->isConnected()) { + return false; + } + + return $this->config_loaded; + } } diff --git a/src/Core/Config/ConfigCache.php b/src/Core/Config/Cache/ConfigCache.php similarity index 51% rename from src/Core/Config/ConfigCache.php rename to src/Core/Config/Cache/ConfigCache.php index b86ec3860a..54da327dba 100644 --- a/src/Core/Config/ConfigCache.php +++ b/src/Core/Config/Cache/ConfigCache.php @@ -1,6 +1,6 @@ loadConfigArray($config); + $this->load($config); } /** - * 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 + * {@inheritdoc} */ - public function loadConfigArray(array $config, $overwrite = false) + public function load(array $config, $overwrite = false) { - foreach ($config as $category => $values) { - foreach ($values as $key => $value) { - if ($overwrite) { - $this->set($category, $key, $value); - } else { - $this->setDefault($category, $key, $value); + $categories = array_keys($config); + + foreach ($categories as $category) { + if (isset($config[$category]) && is_array($config[$category])) { + $keys = array_keys($config[$category]); + + foreach ($keys as $key) { + if (isset($config[$category][$key])) { + if ($overwrite) { + $this->set($category, $key, $config[$category][$key]); + } else { + $this->setDefault($category, $key, $config[$category][$key]); + } + } } } } @@ -44,23 +51,24 @@ class ConfigCache implements IConfigCache, IPConfigCache /** * {@inheritdoc} */ - public function get($cat, $key = null, $default = null) + public function get($cat, $key = null) { - $return = $default; - - if ($cat === 'config') { - if (isset($this->config[$key])) { - $return = $this->config[$key]; - } + if (isset($this->config[$cat][$key])) { + return $this->config[$cat][$key]; + } elseif ($key == null && isset($this->config[$cat])) { + return $this->config[$cat]; } 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 $return; + /** + * {@inheritdoc} + */ + public function has($cat, $key = null) + { + return (isset($this->config[$cat][$key]) && $this->config[$cat][$key] !== '!!') || + ($key == null && isset($this->config[$cat]) && $this->config[$cat] !== '!!' && is_array($this->config[$cat])); } /** @@ -85,34 +93,48 @@ class ConfigCache implements IConfigCache, IPConfigCache // 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; + if (!isset($this->config[$cat])) { + $this->config[$cat] = []; } + $this->config[$cat][$key] = $value; + return true; } + /** + * {@inheritdoc} + */ + public function hasP($uid, $cat, $key = null) + { + return (isset($this->config[$uid][$cat][$key]) && $this->config[$uid][$cat][$key] !== '!!') || + ($key == null && isset($this->config[$uid][$cat]) && $this->config[$uid][$cat] !== '!!' && is_array($this->config[$uid][$cat])); + } + /** * {@inheritdoc} */ public function delete($cat, $key) { - if ($cat === 'config') { - if (isset($this->config[$key])) { - unset($this->config[$key]); + if (isset($this->config[$cat][$key])) { + unset($this->config[$cat][$key]); + if (count($this->config[$cat]) == 0) { + unset($this->config[$cat]); } + return true; } else { - if (isset($this->config[$cat][$key])) { - unset($this->config[$cat][$key]); - if (count($this->config[$cat]) == 0) { - unset($this->config[$cat]); - } + return false; + } + } + + /** + * {@inheritdoc} + */ + public function loadP($uid, array $config) + { + foreach ($config as $category => $values) { + foreach ($values as $key => $value) { + $this->setP($uid, $category, $key, $value); } } } @@ -120,17 +142,15 @@ class ConfigCache implements IConfigCache, IPConfigCache /** * {@inheritdoc} */ - public function getP($uid, $cat, $key = null, $default = null) + public function getP($uid, $cat, $key = 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 $this->config[$uid][$cat][$key]; + } elseif ($key == null && isset($this->config[$uid][$cat])) { + return $this->config[$uid][$cat]; + } else { + return '!!'; } - - return $return; } /** @@ -145,15 +165,13 @@ class ConfigCache implements IConfigCache, IPConfigCache $this->config[$uid] = []; } - if (!isset($this->config[$uid][$cat]) || !is_array($this->config[$uid][$cat])) { + if (!isset($this->config[$uid][$cat])) { $this->config[$uid][$cat] = []; } - if ($key === null) { - $this->config[$uid][$cat] = $value; - } else { - $this->config[$uid][$cat][$key] = $value; - } + $this->config[$uid][$cat][$key] = $value; + + return true; } /** @@ -169,6 +187,10 @@ class ConfigCache implements IConfigCache, IPConfigCache unset($this->config[$uid]); } } + + return true; + } else { + return false; } } diff --git a/src/Core/Config/ConfigCacheLoader.php b/src/Core/Config/Cache/ConfigCacheLoader.php similarity index 93% rename from src/Core/Config/ConfigCacheLoader.php rename to src/Core/Config/Cache/ConfigCacheLoader.php index 3a6a3c803e..b728d10821 100644 --- a/src/Core/Config/ConfigCacheLoader.php +++ b/src/Core/Config/Cache/ConfigCacheLoader.php @@ -1,6 +1,6 @@ set('system', 'basepath', $this->baseDir); - $config->loadConfigArray($this->loadCoreConfig('defaults')); - $config->loadConfigArray($this->loadCoreConfig('settings')); + $config->load($this->loadCoreConfig('defaults')); + $config->load($this->loadCoreConfig('settings')); - $config->loadConfigArray($this->loadLegacyConfig('htpreconfig'), true); - $config->loadConfigArray($this->loadLegacyConfig('htconfig'), true); + $config->load($this->loadLegacyConfig('htpreconfig'), true); + $config->load($this->loadLegacyConfig('htconfig'), true); - $config->loadConfigArray($this->loadCoreConfig('local'), true); + $config->load($this->loadCoreConfig('local'), true); } /** diff --git a/src/Core/Config/Cache/IConfigCache.php b/src/Core/Config/Cache/IConfigCache.php new file mode 100644 index 0000000000..9d948527d4 --- /dev/null +++ b/src/Core/Config/Cache/IConfigCache.php @@ -0,0 +1,65 @@ +!' if not set + */ + function get($cat, $key = null); + + /** + * Sets a value in the config cache. Accepts raw output from the config table + * + * @param string $cat Config category + * @param string $key Config key + * @param mixed $value Value to set + * + * @return bool True, if the value is set + */ + function set($cat, $key, $value); + + /** + * Deletes a value from the config cache. + * + * @param string $cat Config category + * @param string $key Config key + * + * @return bool true, if deleted + */ + function delete($cat, $key); + + /** + * Checks if a value is set in the config cache. + * + * @param string $cat Config category + * @param string $key Config key + * @return bool + */ + function has($cat, $key = null); + + /** + * Returns the whole configuration cache + * + * @return array + */ + function getAll(); +} diff --git a/src/Core/Config/Cache/IPConfigCache.php b/src/Core/Config/Cache/IPConfigCache.php new file mode 100644 index 0000000000..4ac21481a1 --- /dev/null +++ b/src/Core/Config/Cache/IPConfigCache.php @@ -0,0 +1,70 @@ +!' if not set + */ + function getP($uid, $cat, $key = null); + + /** + * 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 $key Config key + * @param mixed $value Value to set + */ + function setP($uid, $cat, $key, $value); + + /** + * Deletes a value from the user config cache + * + * @param int $uid User Id + * @param string $cat Config category + * @param string $key Config key + * + * @return bool true, if deleted + */ + function deleteP($uid, $cat, $key); + + + /** + * Checks if a value is set in the user config cache. + * + * @param int $uid User Id + * @param string $cat Config category + * @param string $key Config key + * @return bool + */ + function hasP($uid, $cat, $key = null); + + /** + * Returns the whole configuration cache + * + * @return array + */ + function getAll(); +} diff --git a/src/Core/Config/Configuration.php b/src/Core/Config/Configuration.php new file mode 100644 index 0000000000..2ac0da0ad1 --- /dev/null +++ b/src/Core/Config/Configuration.php @@ -0,0 +1,153 @@ +configCache = $configCache; + $this->configAdapter = $configAdapter; + + $this->load(); + } + + /** + * Returns the Config Cache + * + * @return Cache\IConfigCache + */ + public function getCache() + { + return $this->configCache; + } + + /** + * @brief Loads all configuration values of family into a cached storage. + * + * All configuration values of the system are stored in the cache ( @see IConfigCache ) + * + * @param string $cat The category of the configuration value + * + * @return void + */ + public function load($cat = 'config') + { + // If not connected, do nothing + if (!$this->configAdapter->isConnected()) { + return; + } + + // load the whole category out of the DB into the cache + $this->configCache->load($this->configAdapter->load($cat), true); + } + + /** + * @brief Get a particular user's config variable given the category name + * ($cat) and a $key. + * + * Get a particular config value from the given category ($cat) + * and the $key from a cached storage either from the $this->configAdapter + * (@see IConfigAdapter ) or from the $this->configCache (@see IConfigCache ). + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * @param mixed $default_value optional, The value to return if key is not set (default: null) + * @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 + */ + public function get($cat, $key, $default_value = null, $refresh = false) + { + // if the value isn't loaded or refresh is needed, load it to the cache + if ($this->configAdapter->isConnected() && + (!$this->configAdapter->isLoaded($cat, $key) || + $refresh)) { + $dbvalue = $this->configAdapter->get($cat, $key); + + if ($dbvalue !== '!!') { + $this->configCache->set($cat, $key, $dbvalue); + return $dbvalue; + } + } + + // use the config cache for return + if ($this->configCache->has($cat, $key)) { + return $this->configCache->get($cat, $key); + } else { + return $default_value; + } + } + + /** + * @brief Sets a configuration value for system config + * + * Stores a config value ($value) in the category ($cat) under the key ($key) + * + * Note: Please do not store booleans - convert to 0/1 integer values! + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to set + * @param mixed $value The value to store + * + * @return bool Operation success + */ + public function set($cat, $key, $value) + { + // set the cache first + $cached = $this->configCache->set($cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configAdapter->isConnected()) { + return $cached; + } + + $stored = $this->configAdapter->set($cat, $key, $value); + + return $cached && $stored; + } + + /** + * @brief Deletes the given key from the system configuration. + * + * Removes the configured value from the stored cache in $this->configCache + * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ). + * + * @param string $cat The category of the configuration value + * @param string $key The configuration key to delete + * + * @return bool + */ + public function delete($cat, $key) + { + $cacheRemoved = $this->configCache->delete($cat, $key); + + if (!$this->configAdapter->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configAdapter->delete($cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Core/Config/IConfigCache.php b/src/Core/Config/IConfigCache.php deleted file mode 100644 index 898e3c0f86..0000000000 --- a/src/Core/Config/IConfigCache.php +++ /dev/null @@ -1,39 +0,0 @@ - - */ -class JITConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter -{ - private $cache; - private $in_db; - - /** - * @var IConfigCache The config cache of this driver - */ - private $configCache; - - /** - * @param IConfigCache $configCache The config cache of this driver - */ - public function __construct(IConfigCache $configCache) - { - $this->configCache = $configCache; - $this->connected = DBA::connected(); - } - - /** - * {@inheritdoc} - */ - public function load($cat = "config") - { - if (!$this->isConnected()) { - return; - } - - // We don't preload "system" anymore. - // This reduces the number of database reads a lot. - if ($cat === 'system') { - return; - } - - $configs = DBA::select('config', ['v', 'k'], ['cat' => $cat]); - while ($config = DBA::fetch($configs)) { - $k = $config['k']; - - $this->configCache->set($cat, $k, $config['v']); - - if ($cat !== 'config') { - $this->cache[$cat][$k] = $config['v']; - $this->in_db[$cat][$k] = true; - } - } - DBA::close($configs); - } - - /** - * {@inheritdoc} - */ - public function get($cat, $k, $default_value = null, $refresh = false) - { - if (!$this->isConnected()) { - return $default_value; - } - - if (!$refresh) { - // Do we have the cached value? Then return it - if (isset($this->cache[$cat][$k])) { - if ($this->cache[$cat][$k] === '!!') { - return $default_value; - } else { - return $this->cache[$cat][$k]; - } - } - } - - $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $k]); - if (DBA::isResult($config)) { - // manage array value - $value = (preg_match("|^a:[0-9]+:{.*}$|s", $config['v']) ? unserialize($config['v']) : $config['v']); - - // Assign the value from the database to the cache - $this->cache[$cat][$k] = $value; - $this->in_db[$cat][$k] = true; - return $value; - } elseif ($this->configCache->get($cat, $k) !== null) { - // Assign the value (mostly) from config/local.config.php file to the cache - $this->cache[$cat][$k] = $this->configCache->get($cat, $k); - $this->in_db[$cat][$k] = false; - - return $this->configCache->get($cat, $k); - } elseif ($this->configCache->get('config', $k) !== null) { - // Assign the value (mostly) from config/local.config.php file to the cache - $this->cache[$k] = $this->configCache->get('config', $k); - $this->in_db[$k] = false; - - return $this->configCache->get('config', $k); - } - - $this->cache[$cat][$k] = '!!'; - $this->in_db[$cat][$k] = false; - - return $default_value; - } - - /** - * {@inheritdoc} - */ - public function set($cat, $k, $value) - { - if (!$this->isConnected()) { - return false; - } - - // We store our setting values in a string variable. - // So we have to do the conversion here so that the compare below works. - // The exception are array values. - $dbvalue = (!is_array($value) ? (string)$value : $value); - - $stored = $this->get($cat, $k, null, true); - - if (!isset($this->in_db[$cat])) { - $this->in_db[$cat] = []; - } - if (!isset($this->in_db[$cat][$k])) { - $this->in_db[$cat] = false; - } - - if (($stored === $dbvalue) && $this->in_db[$cat][$k]) { - return true; - } - - $this->configCache->set($cat, $k, $value); - - // Assign the just added value to the cache - $this->cache[$cat][$k] = $dbvalue; - - // manage array value - $dbvalue = (is_array($value) ? serialize($value) : $dbvalue); - - $result = DBA::update('config', ['v' => $dbvalue], ['cat' => $cat, 'k' => $k], true); - - if ($result) { - $this->in_db[$cat][$k] = true; - } - - return $result; - } - - /** - * {@inheritdoc} - */ - public function delete($cat, $k) - { - if (!$this->isConnected()) { - return false; - } - - if (isset($this->cache[$cat][$k])) { - unset($this->cache[$cat][$k]); - unset($this->in_db[$cat][$k]); - } - - $result = DBA::delete('config', ['cat' => $cat, 'k' => $k]); - - return $result; - } -} diff --git a/src/Core/Config/JITPConfigAdapter.php b/src/Core/Config/JITPConfigAdapter.php deleted file mode 100644 index b1a15601cc..0000000000 --- a/src/Core/Config/JITPConfigAdapter.php +++ /dev/null @@ -1,136 +0,0 @@ - - */ -class JITPConfigAdapter implements IPConfigAdapter -{ - private $in_db; - - /** - * The config cache of this adapter - * @var IPConfigCache - */ - private $configCache; - - /** - * @param IPConfigCache $configCache The config cache of this adapter - */ - public function __construct(IPConfigCache $configCache) - { - $this->configCache = $configCache; - } - - /** - * {@inheritdoc} - */ - public function load($uid, $cat) - { - $pconfigs = DBA::select('pconfig', ['v', 'k'], ['cat' => $cat, 'uid' => $uid]); - if (DBA::isResult($pconfigs)) { - while ($pconfig = DBA::fetch($pconfigs)) { - $k = $pconfig['k']; - - $this->configCache->setP($uid, $cat, $k, $pconfig['v']); - - $this->in_db[$uid][$cat][$k] = true; - } - } else if ($cat != 'config') { - // Negative caching - $this->configCache->setP($uid, $cat, null, "!!"); - } - DBA::close($pconfigs); - } - - /** - * {@inheritdoc} - */ - public function get($uid, $cat, $k, $default_value = null, $refresh = false) - { - if (!$refresh) { - // Looking if the whole family isn't set - if ($this->configCache->getP($uid, $cat) !== null) { - if ($this->configCache->getP($uid, $cat) === '!!') { - return $default_value; - } - } - - if ($this->configCache->getP($uid, $cat, $k) !== null) { - if ($this->configCache->getP($uid, $cat, $k) === '!!') { - return $default_value; - } - return $this->configCache->getP($uid, $cat, $k); - } - } - - $pconfig = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $k]); - if (DBA::isResult($pconfig)) { - $val = (preg_match("|^a:[0-9]+:{.*}$|s", $pconfig['v']) ? unserialize($pconfig['v']) : $pconfig['v']); - - $this->configCache->setP($uid, $cat, $k, $val); - - $this->in_db[$uid][$cat][$k] = true; - - return $val; - } else { - $this->configCache->setP($uid, $cat, $k, '!!'); - - $this->in_db[$uid][$cat][$k] = false; - - return $default_value; - } - } - - /** - * {@inheritdoc} - */ - public function set($uid, $cat, $k, $value) - { - // We store our setting values in a string variable. - // So we have to do the conversion here so that the compare below works. - // The exception are array values. - $dbvalue = (!is_array($value) ? (string)$value : $value); - - $stored = $this->get($uid, $cat, $k, null, true); - - if (($stored === $dbvalue) && $this->in_db[$uid][$cat][$k]) { - return true; - } - - $this->configCache->setP($uid, $cat, $k, $value); - - // manage array value - $dbvalue = (is_array($value) ? serialize($value) : $dbvalue); - - $result = DBA::update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $k], true); - - if ($result) { - $this->in_db[$uid][$cat][$k] = true; - } - - return $result; - } - - /** - * {@inheritdoc} - */ - public function delete($uid, $cat, $k) - { - $this->configCache->deleteP($uid, $cat, $k); - - if (!empty($this->in_db[$uid][$cat][$k])) { - unset($this->in_db[$uid][$cat][$k]); - } - - $result = DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $k]); - - return $result; - } -} diff --git a/src/Core/Config/PConfiguration.php b/src/Core/Config/PConfiguration.php new file mode 100644 index 0000000000..99b1aa1469 --- /dev/null +++ b/src/Core/Config/PConfiguration.php @@ -0,0 +1,149 @@ +configCache = $configCache; + $this->configAdapter = $configAdapter; + } + + /** + * @brief Loads all configuration values of a user's config family into a cached storage. + * + * All configuration values of the given user are stored with the $uid in + * the cache ( @see IPConfigCache ) + * + * @param string $uid The user_id + * @param string $cat The category of the configuration value + * + * @return void + */ + public function load($uid, $cat = 'config') + { + // If not connected, do nothing + if (!$this->configAdapter->isConnected()) { + return; + } + + // load the whole category out of the DB into the cache + $this->configCache->loadP($uid, $this->configAdapter->load($uid, $cat)); + } + + /** + * @brief Get a particular user's config variable given the category name + * ($cat) and a key. + * + * Get a particular user's config value from the given category ($cat) + * and the $key with the $uid from a cached storage either from the $this->configAdapter + * (@see IConfigAdapter ) or from the $this->configCache (@see IConfigCache ). + * + * @param string $uid The user_id + * @param string $cat The category of the configuration value + * @param string $key The configuration key to query + * @param mixed $default_value optional, The value to return if key is not set (default: null) + * @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 + */ + public function get($uid, $cat, $key, $default_value = null, $refresh = false) + { + // if the value isn't loaded or refresh is needed, load it to the cache + if ($this->configAdapter->isConnected() && + (!$this->configAdapter->isLoaded($uid, $cat, $key) || + $refresh)) { + $dbValue = $this->configAdapter->get($uid, $cat, $key); + + if ($dbValue !== '!!') { + $this->configCache->setP($uid, $cat, $key, $dbValue); + return $dbValue; + } + } + + // use the config cache for return + if ($this->configCache->hasP($uid, $cat, $key)) { + return $this->configCache->getP($uid, $cat, $key); + } else { + return $default_value; + } + } + + /** + * @brief Sets a configuration value for a user + * + * Stores a config value ($value) in the category ($family) under the key ($key) + * for the user_id $uid. + * + * @note Please do not store booleans - convert to 0/1 integer values! + * + * @param string $uid The user_id + * @param string $cat The category of the configuration value + * @param string $key The configuration key to set + * @param mixed $value The value to store + * + * @return bool Operation success + */ + public function set($uid, $cat, $key, $value) + { + // set the cache first + $cached = $this->configCache->setP($uid, $cat, $key, $value); + + // If there is no connected adapter, we're finished + if (!$this->configAdapter->isConnected()) { + return $cached; + } + + $stored = $this->configAdapter->set($uid, $cat, $key, $value); + + return $cached && $stored; + } + + /** + * @brief Deletes the given key from the users's configuration. + * + * Removes the configured value from the stored cache in $this->configCache + * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ) + * with the given $uid. + * + * @param string $uid The user_id + * @param string $cat The category of the configuration value + * @param string $key The configuration key to delete + * + * @return bool + */ + public function delete($uid, $cat, $key) + { + $cacheRemoved = $this->configCache->deleteP($uid, $cat, $key); + + if (!$this->configAdapter->isConnected()) { + return $cacheRemoved; + } + + $storeRemoved = $this->configAdapter->delete($uid, $cat, $key); + + return $cacheRemoved || $storeRemoved; + } +} diff --git a/src/Core/Console/AutomaticInstallation.php b/src/Core/Console/AutomaticInstallation.php index 7f2585e63a..682d109712 100644 --- a/src/Core/Console/AutomaticInstallation.php +++ b/src/Core/Console/AutomaticInstallation.php @@ -100,10 +100,10 @@ HELP; } } - $db_host = $a->getConfig()->get('database', 'hostname'); - $db_user = $a->getConfig()->get('database', 'username'); - $db_pass = $a->getConfig()->get('database', 'password'); - $db_data = $a->getConfig()->get('database', 'database'); + $db_host = $a->getConfigCache()->get('database', 'hostname'); + $db_user = $a->getConfigCache()->get('database', 'username'); + $db_pass = $a->getConfigCache()->get('database', 'password'); + $db_data = $a->getConfigCache()->get('database', 'database'); } else { // Creating config file $this->out("Creating config file...\n"); @@ -146,7 +146,7 @@ HELP; $installer->resetChecks(); - if (!$installer->checkDB($a->getConfig(), $db_host, $db_user, $db_pass, $db_data)) { + if (!$installer->checkDB($a->getConfigCache(), $a->getProfiler(), $db_host, $db_user, $db_pass, $db_data)) { $errorMessage = $this->extractErrors($installer->getChecks()); throw new RuntimeException($errorMessage); } diff --git a/src/Core/Console/Config.php b/src/Core/Console/Config.php index b1c3df54e0..cf5c09fc0a 100644 --- a/src/Core/Console/Config.php +++ b/src/Core/Console/Config.php @@ -124,9 +124,9 @@ HELP; $cat = $this->getArgument(0); Core\Config::load($cat); - if ($a->getConfig()->get($cat) !== null) { + if ($a->getConfigCache()->get($cat) !== null) { $this->out("[{$cat}]"); - $catVal = $a->getConfig()->get($cat); + $catVal = $a->getConfigCache()->get($cat); foreach ($catVal as $key => $value) { if (is_array($value)) { foreach ($value as $k => $v) { @@ -148,7 +148,7 @@ HELP; $this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only'); } - $config = $a->getConfig()->getAll(); + $config = $a->getConfigCache()->getAll(); foreach ($config as $cat => $section) { if (is_array($section)) { foreach ($section as $key => $value) { diff --git a/src/Core/Console/Typo.php b/src/Core/Console/Typo.php index 32ba6ded35..8d07051e83 100644 --- a/src/Core/Console/Typo.php +++ b/src/Core/Console/Typo.php @@ -43,7 +43,7 @@ HELP; throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments'); } - $php_path = BaseObject::getApp()->getConfig()->get('config', 'php_path', 'php'); + $php_path = BaseObject::getApp()->getConfigCache()->get('config', 'php_path', 'php'); if ($this->getOption('v')) { $this->out('Directory: src'); diff --git a/src/Core/Installer.php b/src/Core/Installer.php index fdeb940a25..ed47210141 100644 --- a/src/Core/Installer.php +++ b/src/Core/Installer.php @@ -6,11 +6,12 @@ namespace Friendica\Core; use DOMDocument; use Exception; -use Friendica\Core\Config\ConfigCache; +use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\Object\Image; use Friendica\Util\Network; +use Friendica\Util\Profiler; use Friendica\Util\Strings; /** @@ -590,7 +591,8 @@ class Installer /** * Checking the Database connection and if it is available for the current installation * - * @param ConfigCache $configCache The configuration cache + * @param IConfigCache $configCache The configuration cache + * @param Profiler $profiler The profiler of this app * @param string $dbhost Hostname/IP of the Friendica Database * @param string $dbuser Username of the Database connection credentials * @param string $dbpass Password of the Database connection credentials @@ -599,9 +601,9 @@ class Installer * @return bool true if the check was successful, otherwise false * @throws Exception */ - public function checkDB(ConfigCache $configCache, $dbhost, $dbuser, $dbpass, $dbdata) + public function checkDB(IConfigCache $configCache, Profiler $profiler, $dbhost, $dbuser, $dbpass, $dbdata) { - if (!DBA::connect($configCache, $dbhost, $dbuser, $dbpass, $dbdata)) { + if (!DBA::connect($configCache, $profiler, $dbhost, $dbuser, $dbpass, $dbdata)) { $this->addCheck(L10n::t('Could not connect to database.'), false, true, ''); return false; diff --git a/src/Core/Logger.php b/src/Core/Logger.php index 6b8112796f..3cb22e1e47 100644 --- a/src/Core/Logger.php +++ b/src/Core/Logger.php @@ -5,8 +5,6 @@ namespace Friendica\Core; use Friendica\BaseObject; -use Friendica\Factory\LoggerFactory; -use Friendica\Network\HTTPException\InternalServerErrorException; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; @@ -67,73 +65,22 @@ class Logger extends BaseObject /** * Sets the default logging handler for Friendica. - * @todo Can be combined with other handlers too if necessary, could be configurable. * * @param LoggerInterface $logger The Logger instance of this Application - * - * @throws InternalServerErrorException if the logger factory is incompatible to this logger */ - public static function setLogger($logger) + public static function init(LoggerInterface $logger) { - $debugging = Config::get('system', 'debugging'); - $logfile = Config::get('system', 'logfile'); - $loglevel = Config::get('system', 'loglevel'); - - if (!$debugging || !$logfile) { - return; - } - - $loglevel = self::mapLegacyConfigDebugLevel((string)$loglevel); - - LoggerFactory::addStreamHandler($logger, $logfile, $loglevel); - self::$logger = $logger; - - $logfile = Config::get('system', 'dlogfile'); - - if (!$logfile) { - return; - } - - $developIp = Config::get('system', 'dlogip'); - - self::$devLogger = LoggerFactory::createDev('develop', $developIp); - LoggerFactory::addStreamHandler(self::$devLogger, $logfile, LogLevel::DEBUG); } /** - * Mapping a legacy level to the PSR-3 compliant levels - * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel + * Sets the default dev-logging handler for Friendica. * - * @param string $level the level to be mapped - * - * @return string the PSR-3 compliant level + * @param LoggerInterface $logger The Logger instance of this Application */ - private static function mapLegacyConfigDebugLevel($level) + public static function setDevLogger(LoggerInterface $logger) { - switch ($level) { - // legacy WARNING - case "0": - return LogLevel::ERROR; - // legacy INFO - case "1": - return LogLevel::WARNING; - // legacy TRACE - case "2": - return LogLevel::NOTICE; - // legacy DEBUG - case "3": - return LogLevel::INFO; - // legacy DATA - case "4": - return LogLevel::DEBUG; - // legacy ALL - case "5": - return LogLevel::DEBUG; - // default if nothing set - default: - return $level; - } + self::$devLogger = $logger; } /** @@ -155,7 +102,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->emergency($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->GetProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -179,7 +126,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->alert($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -202,7 +149,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->critical($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -225,7 +172,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->error($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -249,7 +196,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->warning($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -270,7 +217,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->notice($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -293,7 +240,7 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->info($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } /** @@ -314,28 +261,28 @@ class Logger extends BaseObject $stamp1 = microtime(true); self::$logger->debug($message, $context); - self::getApp()->saveTimestamp($stamp1, 'file'); + self::getApp()->getProfiler()->saveTimestamp($stamp1, 'file', System::callstack()); } - /** - * @brief Logs the given message at the given log level - * - * @param string $msg - * @param string $level + /** + * @brief Logs the given message at the given log level + * + * @param string $msg + * @param string $level * * @throws \Exception * @deprecated since 2019.03 Use Logger::debug() Logger::info() , ... instead - */ - public static function log($msg, $level = LogLevel::INFO) - { + */ + public static function log($msg, $level = LogLevel::INFO) + { if (!isset(self::$logger)) { return; } - $stamp1 = microtime(true); + $stamp1 = microtime(true); self::$logger->log($level, $msg); - self::getApp()->saveTimestamp($stamp1, "file"); - } + self::getApp()->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); + } /** * @brief An alternative logger for development. @@ -347,14 +294,14 @@ class Logger extends BaseObject * @param string $level * @throws \Exception */ - public static function devLog($msg, $level = LogLevel::DEBUG) - { + public static function devLog($msg, $level = LogLevel::DEBUG) + { if (!isset(self::$logger)) { return; } - $stamp1 = microtime(true); - self::$devLogger->log($level, $msg); - self::getApp()->saveTimestamp($stamp1, "file"); - } + $stamp1 = microtime(true); + self::$devLogger->log($level, $msg); + self::getApp()->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); + } } diff --git a/src/Core/PConfig.php b/src/Core/PConfig.php index df024f0f34..f62b59f476 100644 --- a/src/Core/PConfig.php +++ b/src/Core/PConfig.php @@ -18,123 +18,78 @@ namespace Friendica\Core; class PConfig { /** - * @var Config\IPConfigAdapter + * @var Config\PConfiguration */ - private static $adapter; - - /** - * @var Config\IPConfigCache - */ - private static $cache; + private static $config; /** * Initialize the config with only the cache * - * @param Config\IPConfigCache $cache The configuration cache + * @param Config\PConfiguration $config The configuration cache */ - public static function init(Config\IPConfigCache $cache) + public static function init(Config\PConfiguration $config) { - self::$cache = $cache; - } - - /** - * Add the adapter for DB-backend - * - * @param Config\IPConfigAdapter $adapter - */ - public static function setAdapter(Config\IPConfigAdapter $adapter) - { - self::$adapter = $adapter; + self::$config = $config; } /** * @brief Loads all configuration values of a user's config family into a cached storage. * - * All configuration values of the given user are stored with the $uid in - * the cache ( @see IPConfigCache ) - * - * @param string $uid The user_id - * @param string $family The category of the configuration value + * @param string $uid The user_id + * @param string $cat The category of the configuration value * * @return void */ - public static function load($uid, $family) + public static function load($uid, $cat) { - if (!isset(self::$adapter)) { - return; - } - - self::$adapter->load($uid, $family); + self::$config->load($uid, $cat); } /** * @brief Get a particular user's config variable given the category name - * ($family) and a key. - * - * Get a particular user's config value from the given category ($family) - * and the $key with the $uid from a cached storage either from the self::$adapter - * (@see IConfigAdapter ) or from the static::$cache (@see IConfigCache ). + * ($cat) and a key. * * @param string $uid The user_id - * @param string $family The category of the configuration value + * @param string $cat The category of the configuration value * @param string $key The configuration key to query * @param mixed $default_value optional, The value to return if key is not set (default: null) * @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 */ - public static function get($uid, $family, $key, $default_value = null, $refresh = false) + public static function get($uid, $cat, $key, $default_value = null, $refresh = false) { - if (!isset(self::$adapter)) { - return self::$cache->getP($uid, $family, $key, $default_value); - } - - return self::$adapter->get($uid, $family, $key, $default_value, $refresh); + return self::$config->get($uid, $cat, $key, $default_value, $refresh); } /** * @brief Sets a configuration value for a user * - * Stores a config value ($value) in the category ($family) under the key ($key) - * for the user_id $uid. - * * @note Please do not store booleans - convert to 0/1 integer values! * * @param string $uid The user_id - * @param string $family The category of the configuration value + * @param string $cat The category of the configuration value * @param string $key The configuration key to set * @param mixed $value The value to store * * @return bool Operation success */ - public static function set($uid, $family, $key, $value) + public static function set($uid, $cat, $key, $value) { - if (!isset(self::$adapter)) { - return self::$cache->setP($uid, $family, $key, $value); - } - - return self::$adapter->set($uid, $family, $key, $value); + return self::$config->set($uid, $cat, $key, $value); } /** * @brief Deletes the given key from the users's configuration. * - * Removes the configured value from the stored cache in self::$config - * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ) - * with the given $uid. + * @param string $uid The user_id + * @param string $cat The category of the configuration value + * @param string $key The configuration key to delete * - * @param string $uid The user_id - * @param string $family The category of the configuration value - * @param string $key The configuration key to delete - * - * @return mixed + * @return bool */ - public static function delete($uid, $family, $key) + public static function delete($uid, $cat, $key) { - if (!isset(self::$adapter)) { - return self::$cache->deleteP($uid, $family, $key); - } - - return self::$adapter->delete($uid, $family, $key); + return self::$config->delete($uid, $cat, $key); } } diff --git a/src/Core/Renderer.php b/src/Core/Renderer.php index 67bc5e3bab..8844f26881 100644 --- a/src/Core/Renderer.php +++ b/src/Core/Renderer.php @@ -74,7 +74,7 @@ class Renderer extends BaseObject exit(); } - $a->saveTimestamp($stamp1, "rendering"); + $a->getProfiler()->saveTimestamp($stamp1, "rendering", System::callstack()); return $output; } @@ -101,7 +101,7 @@ class Renderer extends BaseObject exit(); } - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); return $template; } diff --git a/src/Core/Theme.php b/src/Core/Theme.php index 62dfaa51f3..5479f8f46f 100644 --- a/src/Core/Theme.php +++ b/src/Core/Theme.php @@ -51,7 +51,7 @@ class Theme $a = \get_app(); $stamp1 = microtime(true); $theme_file = file_get_contents("view/theme/$theme/theme.php"); - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); $result = preg_match("|/\*.*\*/|msU", $theme_file, $matches); diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 75d4e48740..dff60e9fa5 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -380,20 +380,7 @@ class Worker // We use the callstack here to analyze the performance of executed worker entries. // For this reason the variables have to be initialized. - if (Config::get("system", "profiler")) { - $a->performance["start"] = microtime(true); - $a->performance["database"] = 0; - $a->performance["database_write"] = 0; - $a->performance["cache"] = 0; - $a->performance["cache_write"] = 0; - $a->performance["network"] = 0; - $a->performance["file"] = 0; - $a->performance["rendering"] = 0; - $a->performance["parser"] = 0; - $a->performance["marktime"] = 0; - $a->performance["markstart"] = microtime(true); - $a->callstack = []; - } + $a->getProfiler()->reset(); // For better logging create a new process id for every worker call // But preserve the old one for the worker @@ -452,76 +439,7 @@ class Worker Logger::log("Process ".$mypid." - Prio ".$queue["priority"]." - ID ".$queue["id"].": ".$funcname." - done in ".number_format($duration, 4)." seconds. Process PID: ".$new_process_id); - // Write down the performance values into the log - if (Config::get("system", "profiler")) { - $duration = microtime(true)-$a->performance["start"]; - - $o = ''; - if (Config::get("rendertime", "callstack")) { - if (isset($a->callstack["database"])) { - $o .= "\nDatabase Read:\n"; - foreach ($a->callstack["database"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func.": ".$time."\n"; - } - } - } - if (isset($a->callstack["database_write"])) { - $o .= "\nDatabase Write:\n"; - foreach ($a->callstack["database_write"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func.": ".$time."\n"; - } - } - } - if (isset($a->callstack["dache"])) { - $o .= "\nCache Read:\n"; - foreach ($a->callstack["dache"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func.": ".$time."\n"; - } - } - } - if (isset($a->callstack["dache_write"])) { - $o .= "\nCache Write:\n"; - foreach ($a->callstack["dache_write"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func.": ".$time."\n"; - } - } - } - if (isset($a->callstack["network"])) { - $o .= "\nNetwork:\n"; - foreach ($a->callstack["network"] as $func => $time) { - $time = round($time, 3); - if ($time > 0) { - $o .= $func.": ".$time."\n"; - } - } - } - } - - Logger::log( - "ID ".$queue["id"].": ".$funcname.": ".sprintf( - "DB: %s/%s, Cache: %s/%s, Net: %s, I/O: %s, Other: %s, Total: %s".$o, - number_format($a->performance["database"] - $a->performance["database_write"], 2), - number_format($a->performance["database_write"], 2), - number_format($a->performance["cache"], 2), - number_format($a->performance["cache_write"], 2), - number_format($a->performance["network"], 2), - number_format($a->performance["file"], 2), - number_format($duration - ($a->performance["database"] - + $a->performance["cache"] + $a->performance["cache_write"] - + $a->performance["network"] + $a->performance["file"]), 2), - number_format($duration, 2) - ), - Logger::DEBUG - ); - } + $a->getProfiler()->saveLog("ID " . $queue["id"] . ": " . $funcname); $cooldown = Config::get("system", "worker_cooldown", 0); diff --git a/src/Database/DBA.php b/src/Database/DBA.php index d2a739e931..1c17d9aca5 100644 --- a/src/Database/DBA.php +++ b/src/Database/DBA.php @@ -2,10 +2,11 @@ namespace Friendica\Database; -use Friendica\Core\Config\IConfigCache; +use Friendica\Core\Config\Cache\IConfigCache; use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Util\DateTimeFormat; +use Friendica\Util\Profiler; use mysqli; use mysqli_result; use mysqli_stmt; @@ -35,6 +36,10 @@ class DBA * @var IConfigCache */ private static $configCache; + /** + * @var Profiler + */ + private static $profiler; private static $server_info = ''; private static $connection; private static $driver; @@ -50,7 +55,7 @@ class DBA private static $db_name = ''; private static $db_charset = ''; - public static function connect($configCache, $serveraddr, $user, $pass, $db, $charset = null) + public static function connect(IConfigCache $configCache, Profiler $profiler, $serveraddr, $user, $pass, $db, $charset = null) { if (!is_null(self::$connection) && self::connected()) { return true; @@ -58,6 +63,7 @@ class DBA // We are storing these values for being able to perform a reconnect self::$configCache = $configCache; + self::$profiler = $profiler; self::$db_serveraddr = $serveraddr; self::$db_user = $user; self::$db_pass = $pass; @@ -158,7 +164,7 @@ class DBA public static function reconnect() { self::disconnect(); - $ret = self::connect(self::$configCache, self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset); + $ret = self::connect(self::$configCache, self::$profiler, self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset); return $ret; } @@ -392,7 +398,6 @@ class DBA * @throws \Exception */ public static function p($sql) { - $a = \get_app(); $stamp1 = microtime(true); @@ -582,7 +587,7 @@ class DBA self::$errorno = $errorno; } - $a->saveTimestamp($stamp1, 'database'); + self::$profiler->saveTimestamp($stamp1, 'database', System::callstack()); if (self::$configCache->get('system', 'db_log')) { $stamp2 = microtime(true); @@ -611,7 +616,6 @@ class DBA * @throws \Exception */ public static function e($sql) { - $a = \get_app(); $stamp = microtime(true); @@ -654,7 +658,7 @@ class DBA self::$errorno = $errorno; } - $a->saveTimestamp($stamp, "database_write"); + self::$profiler->saveTimestamp($stamp, "database_write", System::callstack()); return $retval; } @@ -777,7 +781,6 @@ class DBA * @return array current row */ public static function fetch($stmt) { - $a = \get_app(); $stamp1 = microtime(true); @@ -824,7 +827,7 @@ class DBA } } - $a->saveTimestamp($stamp1, 'database'); + self::$profiler->saveTimestamp($stamp1, 'database', System::callstack()); return $columns; } @@ -1534,7 +1537,6 @@ class DBA * @return boolean was the close successful? */ public static function close($stmt) { - $a = \get_app(); $stamp1 = microtime(true); @@ -1562,7 +1564,7 @@ class DBA break; } - $a->saveTimestamp($stamp1, 'database'); + self::$profiler->saveTimestamp($stamp1, 'database', System::callstack()); return $ret; } diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php index 269daea8b8..6a30cf0e05 100644 --- a/src/Factory/ConfigFactory.php +++ b/src/Factory/ConfigFactory.php @@ -2,51 +2,66 @@ namespace Friendica\Factory; +use Friendica\Core; use Friendica\Core\Config; +use Friendica\Core\Config\Adapter; +use Friendica\Core\Config\Cache; class ConfigFactory { /** - * @param Config\ConfigCacheLoader $loader The Config Cache loader (INI/config/.htconfig) + * @param Cache\ConfigCacheLoader $loader The Config Cache loader (INI/config/.htconfig) * - * @return Config\ConfigCache + * @return Cache\ConfigCache */ - public static function createCache(Config\ConfigCacheLoader $loader) + public static function createCache(Cache\ConfigCacheLoader $loader) { - $configCache = new Config\ConfigCache(); + $configCache = new Cache\ConfigCache(); $loader->loadConfigFiles($configCache); return $configCache; } /** - * @param string $type The adapter type - * @param Config\IConfigCache $config The config cache of this adapter + * @param Cache\ConfigCache $configCache The config cache of this adapter * - * @return Config\IConfigAdapter + * @return Config\Configuration */ - public static function createConfig($type, Config\IConfigCache $config) + public static function createConfig(Cache\ConfigCache $configCache) { - if ($type == 'preload') { - return new Config\PreloadConfigAdapter($config); + if ($configCache->get('system', 'config_adapter') === 'preload') { + $configAdapter = new Adapter\PreloadConfigAdapter(); } else { - return new Config\JITConfigAdapter($config); + $configAdapter = new Adapter\JITConfigAdapter(); } + + $configuration = new Config\Configuration($configCache, $configAdapter); + + // Set the config in the static container for legacy usage + Core\Config::init($configuration); + + return $configuration; } /** - * @param string $type The adapter type - * @param Config\IPConfigCache $config The config cache of this adapter - * @param int $uid The UID of the current user + * @param Cache\ConfigCache $configCache The config cache of this adapter + * @param int $uid The UID of the current user * - * @return Config\IPConfigAdapter + * @return Config\PConfiguration */ - public static function createPConfig($type, Config\IPConfigCache $config, $uid = null) + public static function createPConfig(Cache\ConfigCache $configCache, $uid = null) { - if ($type == 'preload') { - return new Config\PreloadPConfigAdapter($config, $uid); + if ($configCache->get('system', 'config_adapter') === 'preload') { + $configAdapter = new Adapter\PreloadPConfigAdapter($uid); } else { - return new Config\JITPConfigAdapter($config); + $configAdapter = new Adapter\JITPConfigAdapter(); } + + $configuration = new Config\PConfiguration($configCache, $configAdapter); + + // Set the config in the static container for legacy usage + Core\PConfig::init($configuration); + + return $configuration; } } diff --git a/src/Factory/DBFactory.php b/src/Factory/DBFactory.php new file mode 100644 index 0000000000..c1a7965013 --- /dev/null +++ b/src/Factory/DBFactory.php @@ -0,0 +1,58 @@ +get('database', 'hostname'); + $db_user = $configCache->get('database', 'username'); + $db_pass = $configCache->get('database', 'password'); + $db_data = $configCache->get('database', 'database'); + $charset = $configCache->get('database', 'charset'); + + // Use environment variables for mysql if they are set beforehand + if (!empty($server['MYSQL_HOST']) + && !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER'])) + && $server['MYSQL_PASSWORD'] !== false + && !empty($server['MYSQL_DATABASE'])) + { + $db_host = $server['MYSQL_HOST']; + if (!empty($server['MYSQL_PORT'])) { + $db_host .= ':' . $server['MYSQL_PORT']; + } + if (!empty($server['MYSQL_USERNAME'])) { + $db_user = $server['MYSQL_USERNAME']; + } else { + $db_user = $server['MYSQL_USER']; + } + $db_pass = (string) $server['MYSQL_PASSWORD']; + $db_data = $server['MYSQL_DATABASE']; + } + + if (Database\DBA::connect($configCache, $profiler, $db_host, $db_user, $db_pass, $db_data, $charset)) { + // Loads DB_UPDATE_VERSION constant + Database\DBStructure::definition($configCache->get('system', 'basepath'), false); + } + + unset($db_host, $db_user, $db_pass, $db_data, $charset); + } +} diff --git a/src/Factory/DependencyFactory.php b/src/Factory/DependencyFactory.php new file mode 100644 index 0000000000..acbf4bfaf7 --- /dev/null +++ b/src/Factory/DependencyFactory.php @@ -0,0 +1,37 @@ +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->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, [Logger::class, Profiler::class])); - if (isset($config)) { - $debugging = $config->get('system', 'debugging'); - $stream = $config->get('system', 'logfile'); - $level = $config->get('system', 'loglevel'); + $debugging = $config->get('system', 'debugging'); + $stream = $config->get('system', 'logfile'); + $level = $config->get('system', 'loglevel'); - if ($debugging) { - static::addStreamHandler($logger, $stream, $level); - } + if ($debugging) { + $loglevel = self::mapLegacyConfigDebugLevel((string)$level); + static::addStreamHandler($logger, $stream, $loglevel); + } else { + static::addVoidHandler($logger); } + Logger::init($logger); + return $logger; } @@ -54,25 +59,71 @@ class LoggerFactory * * 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 + * @param string $channel The channel of the logger instance + * @param Configuration $config The config * * @return LoggerInterface The PSR-3 compliant logger instance */ - public static function createDev($channel, $developerIp) + public static function createDev($channel, Configuration $config) { + $debugging = $config->get('system', 'debugging'); + $stream = $config->get('system', 'dlogfile'); + $developerIp = $config->get('system', 'dlogip'); + + if (!isset($developerIp) || !$debugging) { + return null; + } + $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)); + static::addStreamHandler($logger, $stream, LogLevel::DEBUG); + + Logger::setDevLogger($logger); + return $logger; } + /** + * Mapping a legacy level to the PSR-3 compliant levels + * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel + * + * @param string $level the level to be mapped + * + * @return string the PSR-3 compliant level + */ + private static function mapLegacyConfigDebugLevel($level) + { + switch ($level) { + // legacy WARNING + case "0": + return LogLevel::ERROR; + // legacy INFO + case "1": + return LogLevel::WARNING; + // legacy TRACE + case "2": + return LogLevel::NOTICE; + // legacy DEBUG + case "3": + return LogLevel::INFO; + // legacy DATA + case "4": + return LogLevel::DEBUG; + // legacy ALL + case "5": + return LogLevel::DEBUG; + // default if nothing set + default: + return $level; + } + } + /** * Adding a handler to a given logger instance * @@ -105,31 +156,10 @@ class LoggerFactory } } - /** - * 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) + public static function addVoidHandler($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/Factory/ProfilerFactory.php b/src/Factory/ProfilerFactory.php new file mode 100644 index 0000000000..26a156639e --- /dev/null +++ b/src/Factory/ProfilerFactory.php @@ -0,0 +1,26 @@ +get('system', 'profiler'); + $enabled = isset($enabled) && $enabled !== '!!'; + $renderTime = $configCache->get('rendertime', 'callstack'); + $renderTime = isset($renderTime) && $renderTime !== '!!'; + + return new Profiler($enabled, $renderTime); + } +} diff --git a/src/Module/Install.php b/src/Module/Install.php index 6948dee6e0..3414e887fa 100644 --- a/src/Module/Install.php +++ b/src/Module/Install.php @@ -75,7 +75,7 @@ class Install extends BaseModule $dbdata = Strings::escapeTags(trim(defaults($_POST, 'dbdata', ''))); // If we cannot connect to the database, return to the previous step - if (!self::$installer->checkDB($a->getConfig(), $dbhost, $dbuser, $dbpass, $dbdata)) { + if (!self::$installer->checkDB($a->getConfigCache(), $a->getProfiler(), $dbhost, $dbuser, $dbpass, $dbdata)) { self::$currentWizardStep = self::DATABASE_CONFIG; } @@ -92,7 +92,7 @@ class Install extends BaseModule $adminmail = Strings::escapeTags(trim(defaults($_POST, 'adminmail', ''))); // If we cannot connect to the database, return to the Database config wizard - if (!self::$installer->checkDB($a->getConfig(), $dbhost, $dbuser, $dbpass, $dbdata)) { + if (!self::$installer->checkDB($a->getConfigCache(), $a->getProfiler(), $dbhost, $dbuser, $dbpass, $dbdata)) { self::$currentWizardStep = self::DATABASE_CONFIG; return; } diff --git a/src/Object/Image.php b/src/Object/Image.php index 9143c23c16..45c8bedeaf 100644 --- a/src/Object/Image.php +++ b/src/Object/Image.php @@ -5,6 +5,7 @@ */ namespace Friendica\Object; +use Exception; use Friendica\App; use Friendica\Core\Cache; use Friendica\Core\Config; @@ -14,7 +15,6 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\Model\Photo; use Friendica\Util\Network; -use Exception; use Imagick; use ImagickPixel; @@ -656,7 +656,7 @@ class Image $stamp1 = microtime(true); file_put_contents($path, $string); - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); } /** @@ -802,7 +802,7 @@ class Image $a = \get_app(); $stamp1 = microtime(true); file_put_contents($tempfile, $img_str); - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); $data = getimagesize($tempfile); unlink($tempfile); @@ -910,7 +910,7 @@ class Image $stamp1 = microtime(true); $imagedata = @file_get_contents($url); - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); } $maximagesize = Config::get('system', 'maximagesize'); @@ -924,7 +924,7 @@ class Image $stamp1 = microtime(true); file_put_contents($tempfile, $imagedata); - $a->saveTimestamp($stamp1, "file"); + $a->getProfiler()->saveTimestamp($stamp1, "file", System::callstack()); $data = getimagesize($tempfile); diff --git a/src/Util/Network.php b/src/Util/Network.php index de4b45da9a..cda8c9a71d 100644 --- a/src/Util/Network.php +++ b/src/Util/Network.php @@ -4,13 +4,13 @@ */ namespace Friendica\Util; +use DOMDocument; +use DomXPath; +use Friendica\Core\Config; use Friendica\Core\Hook; use Friendica\Core\Logger; use Friendica\Core\System; -use Friendica\Core\Config; use Friendica\Network\CurlResult; -use DOMDocument; -use DomXPath; class Network { @@ -232,7 +232,7 @@ class Network @curl_close($ch); - $a->saveTimestamp($stamp1, 'network'); + $a->getProfiler()->saveTimestamp($stamp1, 'network', System::callstack()); return $curlResponse; } @@ -334,7 +334,7 @@ class Network curl_close($ch); - $a->saveTimestamp($stamp1, 'network'); + $a->getProfiler()->saveTimestamp($stamp1, 'network', System::callstack()); Logger::log('post_url: end ' . $url, Logger::DATA); @@ -641,7 +641,7 @@ class Network $http_code = $curl_info['http_code']; curl_close($ch); - $a->saveTimestamp($stamp1, "network"); + $a->getProfiler()->saveTimestamp($stamp1, "network", System::callstack()); if ($http_code == 0) { return $url; @@ -683,7 +683,7 @@ class Network $body = curl_exec($ch); curl_close($ch); - $a->saveTimestamp($stamp1, "network"); + $a->getProfiler()->saveTimestamp($stamp1, "network", System::callstack()); if (trim($body) == "") { return $url; diff --git a/src/Util/Profiler.php b/src/Util/Profiler.php new file mode 100644 index 0000000000..2d3da3a9c0 --- /dev/null +++ b/src/Util/Profiler.php @@ -0,0 +1,239 @@ +enabled = $enabled; + $this->rendertime = $renderTime; + $this->reset(); + } + + /** + * Saves a timestamp for a value - f.e. a call + * Necessary for profiling Friendica + * + * @param int $timestamp the Timestamp + * @param string $value A value to profile + * @param string $callstack The callstack of the current profiling data + */ + public function saveTimestamp($timestamp, $value, $callstack = '') + { + if (!$this->enabled) { + return; + } + + $duration = (float) (microtime(true) - $timestamp); + + if (!isset($this->performance[$value])) { + // Prevent ugly E_NOTICE + $this->performance[$value] = 0; + } + + $this->performance[$value] += (float) $duration; + $this->performance['marktime'] += (float) $duration; + + if (!isset($this->callstack[$value][$callstack])) { + // Prevent ugly E_NOTICE + $this->callstack[$value][$callstack] = 0; + } + + $this->callstack[$value][$callstack] += (float) $duration; + } + + /** + * Resets the performance and callstack profiling + */ + public function reset() + { + $this->resetPerformance(); + $this->resetCallstack(); + } + + /** + * Resets the performance profiling data + */ + public function resetPerformance() + { + $this->performance = []; + $this->performance['start'] = microtime(true); + $this->performance['database'] = 0; + $this->performance['database_write'] = 0; + $this->performance['cache'] = 0; + $this->performance['cache_write'] = 0; + $this->performance['network'] = 0; + $this->performance['file'] = 0; + $this->performance['rendering'] = 0; + $this->performance['parser'] = 0; + $this->performance['marktime'] = 0; + $this->performance['marktime'] = microtime(true); + } + + /** + * Resets the callstack profiling data + */ + public function resetCallstack() + { + $this->callstack = []; + $this->callstack['database'] = []; + $this->callstack['database_write'] = []; + $this->callstack['cache'] = []; + $this->callstack['cache_write'] = []; + $this->callstack['network'] = []; + $this->callstack['file'] = []; + $this->callstack['rendering'] = []; + $this->callstack['parser'] = []; + } + + /** + * Save the current profiling data to a log entry + * + * @param LoggerInterface $logger The logger to save the current log + * @param string $message Additional message for the log + */ + public function saveLog(LoggerInterface $logger, $message = '') + { + // Write down the performance values into the log + if (!$this->enabled) { + return; + } + $duration = microtime(true) - $this->get('start'); + $logger->info( + $message, + [ + 'action' => 'profiling', + 'database_read' => round($this->get('database') - $this->get('database_write'), 3), + 'database_write' => round($this->get('database_write'), 3), + 'cache_read' => round($this->get('cache'), 3), + 'cache_write' => round($this->get('cache_write'), 3), + 'network_io' => round($this->get('network'), 2), + 'file_io' => round($this->get('file'), 2), + 'other_io' => round($duration - ($this->get('database') + + $this->get('cache') + $this->get('cache_write') + + $this->get('network') + $this->get('file')), 2), + 'total' => round($duration, 2) + ] + ); + + if (!$this->rendertime) { + return; + } + + $o = ''; + if (isset($this->callstack["database"])) { + $o .= "\nDatabase Read:\n"; + foreach ($this->callstack["database"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func . ": " . $time . "\n"; + } + } + } + if (isset($this->callstack["database_write"])) { + $o .= "\nDatabase Write:\n"; + foreach ($this->callstack["database_write"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func . ": " . $time . "\n"; + } + } + } + if (isset($this->callstack["cache"])) { + $o .= "\nCache Read:\n"; + foreach ($this->callstack["cache"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func . ": " . $time . "\n"; + } + } + } + if (isset($this->callstack["cache_write"])) { + $o .= "\nCache Write:\n"; + foreach ($this->callstack["cache_write"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func . ": " . $time . "\n"; + } + } + } + if (isset($this->callstack["network"])) { + $o .= "\nNetwork:\n"; + foreach ($this->callstack["network"] as $func => $time) { + $time = round($time, 3); + if ($time > 0) { + $o .= $func . ": " . $time . "\n"; + } + } + } + $logger->info($message . ": " . $o, ['action' => 'profiling']); + } + + /** + * Finds an entry of the container by its identifier and returns it. + * + * @param string $id Identifier of the entry to look for. + * + * @throws NotFoundExceptionInterface No entry was found for **this** identifier. + * @throws ContainerExceptionInterface Error while retrieving the entry. + * + * @return int Entry. + */ + public function get($id) + { + if (!$this->has($id)) { + return 0; + } else { + return $this->performance[$id]; + } + } + + /** + * Returns true if the container can return an entry for the given identifier. + * Returns false otherwise. + * + * `has($id)` returning true does not mean that `get($id)` will not throw an exception. + * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. + * + * @param string $id Identifier of the entry to look for. + * + * @return bool + */ + public function has($id) + { + return isset($this->performance[$id]); + } +} diff --git a/tests/DatabaseTest.php b/tests/DatabaseTest.php index 79af5b5468..dde61e856b 100644 --- a/tests/DatabaseTest.php +++ b/tests/DatabaseTest.php @@ -5,10 +5,11 @@ namespace Friendica\Test; -use Friendica\Core\Config; +use Friendica\Core\Config\Cache; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Util\BasePath; +use Friendica\Util\Profiler; use PHPUnit\DbUnit\DataSet\YamlDataSet; use PHPUnit\DbUnit\TestCaseTrait; use PHPUnit_Extensions_Database_DB_IDatabaseConnection; @@ -40,11 +41,14 @@ abstract class DatabaseTest extends MockedTest } $basedir = BasePath::create(dirname(__DIR__)); - $configLoader = new Config\ConfigCacheLoader($basedir); + $configLoader = new Cache\ConfigCacheLoader($basedir); $config = Factory\ConfigFactory::createCache($configLoader); + $profiler = \Mockery::mock(Profiler::class); + DBA::connect( $config, + $profiler, getenv('MYSQL_HOST'), getenv('MYSQL_USERNAME'), getenv('MYSQL_PASSWORD'), diff --git a/tests/Util/AppMockTrait.php b/tests/Util/AppMockTrait.php index 18188239f1..817570dd58 100644 --- a/tests/Util/AppMockTrait.php +++ b/tests/Util/AppMockTrait.php @@ -5,8 +5,8 @@ namespace Friendica\Test\Util; use Friendica\App; use Friendica\BaseObject; use Friendica\Core\Config; -use Friendica\Core\Config\ConfigCache; use Friendica\Render\FriendicaSmartyEngine; +use Friendica\Util\Profiler; use Mockery\MockInterface; use org\bovigo\vfs\vfsStreamDirectory; @@ -21,74 +21,83 @@ trait AppMockTrait protected $app; /** - * @var MockInterface|ConfigCache The mocked Config Cache + * @var MockInterface|Config\Configuration The mocked Config Cache */ - protected $configCache; + protected $configMock; + + /** + * @var MockInterface|Profiler The mocked profiler + */ + protected $profilerMock; /** * Mock the App * * @param vfsStreamDirectory $root The root directory - * @param MockInterface|ConfigCache $config The config cache */ - public function mockApp($root, $config) + public function mockApp($root) { - $this->configCache = $config; + $this->configMock = \Mockery::mock(Config\Cache\IConfigCache::class); + $configAdapterMock = \Mockery::mock(Config\Adapter\IConfigAdapter::class); + // Disable the adapter + $configAdapterMock->shouldReceive('isConnected')->andReturn(false); + + $config = new Config\Configuration($this->configMock, $configAdapterMock); + // Initialize empty Config + Config::init($config); + // Mocking App and most used functions $this->app = \Mockery::mock(App::class); $this->app ->shouldReceive('getBasePath') ->andReturn($root->url()); - $config + $this->configMock + ->shouldReceive('has') + ->andReturn(true); + $this->configMock ->shouldReceive('get') ->with('database', 'hostname') ->andReturn(getenv('MYSQL_HOST')); - $config + $this->configMock ->shouldReceive('get') ->with('database', 'username') ->andReturn(getenv('MYSQL_USERNAME')); - $config + $this->configMock ->shouldReceive('get') ->with('database', 'password') ->andReturn(getenv('MYSQL_PASSWORD')); - $config + $this->configMock ->shouldReceive('get') ->with('database', 'database') ->andReturn(getenv('MYSQL_DATABASE')); - $config + $this->configMock ->shouldReceive('get') ->with('config', 'hostname') ->andReturn('localhost'); - $config + $this->configMock ->shouldReceive('get') - ->with('system', 'theme', NULL) + ->with('system', 'theme') ->andReturn('system_theme'); - $this->app - ->shouldReceive('getConfig') - ->andReturn($config); + $this->profilerMock = \Mockery::mock(Profiler::class); + $this->profilerMock->shouldReceive('saveTimestamp'); + $this->app + ->shouldReceive('getConfigCache') + ->andReturn($this->configMock); $this->app ->shouldReceive('getTemplateEngine') ->andReturn(new FriendicaSmartyEngine()); $this->app ->shouldReceive('getCurrentTheme') ->andReturn('Smarty3'); - $this->app - ->shouldReceive('saveTimestamp') - ->andReturn(true); $this->app ->shouldReceive('getBaseUrl') ->andReturn('http://friendica.local'); - - // Initialize empty Config - Config::init($config); - $configAdapter = \Mockery::mock('Friendica\Core\Config\IConfigAdapter'); - $configAdapter - ->shouldReceive('isConnected') - ->andReturn(false); - Config::setAdapter($configAdapter); + $this->app + ->shouldReceive('getProfiler') + ->andReturn($this->profilerMock); BaseObject::setApp($this->app); } diff --git a/tests/Util/DBStructureMockTrait.php b/tests/Util/DBStructureMockTrait.php index 87c120d3f2..92ec412cb7 100644 --- a/tests/Util/DBStructureMockTrait.php +++ b/tests/Util/DBStructureMockTrait.php @@ -2,6 +2,7 @@ namespace Friendica\Test\Util; +use Friendica\Database\DBStructure; use Mockery\MockInterface; /** @@ -16,6 +17,7 @@ trait DBStructureMockTrait /** * Mocking DBStructure::update() + * @see DBStructure::update(); * * @param array $args The arguments for the update call * @param bool $return True, if the connect was successful, otherwise false diff --git a/tests/include/ApiTest.php b/tests/include/ApiTest.php index be70d923bd..289b3fcea5 100644 --- a/tests/include/ApiTest.php +++ b/tests/include/ApiTest.php @@ -7,6 +7,7 @@ namespace Friendica\Test; use Friendica\App; use Friendica\Core\Config; +use Friendica\Core\Config\Cache; use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\System; @@ -36,11 +37,14 @@ class ApiTest extends DatabaseTest public function setUp() { $basedir = BasePath::create(dirname(__DIR__) . '/../'); - $configLoader = new Config\ConfigCacheLoader($basedir); - $config = Factory\ConfigFactory::createCache($configLoader); + $configLoader = new Cache\ConfigCacheLoader($basedir); + $configCache = Factory\ConfigFactory::createCache($configLoader); + $profiler = Factory\ProfilerFactory::create($configCache); + Factory\DBFactory::init($configCache, $profiler, $_SERVER); + $config = Factory\ConfigFactory::createConfig($configCache); + Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config); - $this->app = new App($config, $logger, false); - $this->logOutput = FActory\LoggerFactory::enableTest($this->app->getLogger()); + $this->app = new App($config, $logger, $profiler, false); parent::setUp(); diff --git a/tests/src/App/ModeTest.php b/tests/src/App/ModeTest.php index 19dad07cd6..9059e8bebf 100644 --- a/tests/src/App/ModeTest.php +++ b/tests/src/App/ModeTest.php @@ -90,19 +90,14 @@ class ModeTest extends MockedTest $this->mockConnected(true, 1); $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1); - $config = \Mockery::mock('Friendica\Core\Config\ConfigCache'); + $config = \Mockery::mock(Config\Configuration::class); $config ->shouldReceive('get') - ->with('system', 'maintenance', null) + ->with('system', 'maintenance', null, false) ->andReturn(true) ->once(); // Initialize empty Config Config::init($config); - $configAdapter = \Mockery::mock('Friendica\Core\Config\IConfigAdapter'); - $configAdapter - ->shouldReceive('isConnected') - ->andReturn(false); - Config::setAdapter($configAdapter); $mode = new Mode($this->root->url()); $mode->determine(); @@ -123,19 +118,14 @@ class ModeTest extends MockedTest $this->mockConnected(true, 1); $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1); - $config = \Mockery::mock('Friendica\Core\Config\ConfigCache'); + $config = \Mockery::mock(Config\Configuration::class); $config ->shouldReceive('get') - ->with('system', 'maintenance', null) + ->with('system', 'maintenance', null, false) ->andReturn(false) ->once(); // Initialize empty Config Config::init($config); - $configAdapter = \Mockery::mock('Friendica\Core\Config\IConfigAdapter'); - $configAdapter - ->shouldReceive('isConnected') - ->andReturn(false); - Config::setAdapter($configAdapter); $mode = new Mode($this->root->url()); $mode->determine(); diff --git a/tests/src/BaseObjectTest.php b/tests/src/BaseObjectTest.php index 784944c3a0..3341503915 100644 --- a/tests/src/BaseObjectTest.php +++ b/tests/src/BaseObjectTest.php @@ -31,8 +31,7 @@ class BaseObjectTest extends TestCase { $baseObject = new BaseObject(); $this->setUpVfsDir(); - $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache'); - $this->mockApp($this->root, $configMock); + $this->mockApp($this->root); $this->assertNull($baseObject->setApp($this->app)); $this->assertEquals($this->app, $baseObject->getApp()); diff --git a/tests/src/Core/Cache/CacheTest.php b/tests/src/Core/Cache/CacheTest.php index e8bd65cbfe..ef97f5a172 100644 --- a/tests/src/Core/Cache/CacheTest.php +++ b/tests/src/Core/Cache/CacheTest.php @@ -67,8 +67,7 @@ abstract class CacheTest extends MockedTest protected function setUp() { $this->setUpVfsDir(); - $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache'); - $this->mockApp($this->root, $configMock); + $this->mockApp($this->root); $this->app ->shouldReceive('getHostname') ->andReturn('friendica.local'); diff --git a/tests/src/Core/Cache/MemcacheCacheDriverTest.php b/tests/src/Core/Cache/MemcacheCacheDriverTest.php index 7832344a89..f9df9eaba0 100644 --- a/tests/src/Core/Cache/MemcacheCacheDriverTest.php +++ b/tests/src/Core/Cache/MemcacheCacheDriverTest.php @@ -12,14 +12,14 @@ class MemcacheCacheDriverTest extends MemoryCacheTest { protected function getInstance() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'memcache_host', NULL) + ->with('system', 'memcache_host') ->andReturn('localhost'); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'memcache_port', NULL) + ->with('system', 'memcache_port') ->andReturn(11211); $this->cache = CacheDriverFactory::create('memcache'); diff --git a/tests/src/Core/Cache/MemcachedCacheDriverTest.php b/tests/src/Core/Cache/MemcachedCacheDriverTest.php index fe401f97dd..4e16ef947f 100644 --- a/tests/src/Core/Cache/MemcachedCacheDriverTest.php +++ b/tests/src/Core/Cache/MemcachedCacheDriverTest.php @@ -12,9 +12,9 @@ class MemcachedCacheDriverTest extends MemoryCacheTest { protected function getInstance() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'memcached_hosts', NULL) + ->with('system', 'memcached_hosts') ->andReturn([0 => 'localhost, 11211']); $this->cache = CacheDriverFactory::create('memcached'); diff --git a/tests/src/Core/Cache/RedisCacheDriverTest.php b/tests/src/Core/Cache/RedisCacheDriverTest.php index 0a3dba439d..20fe7eb53f 100644 --- a/tests/src/Core/Cache/RedisCacheDriverTest.php +++ b/tests/src/Core/Cache/RedisCacheDriverTest.php @@ -12,14 +12,14 @@ class RedisCacheDriverTest extends MemoryCacheTest { protected function getInstance() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'redis_host', NULL) + ->with('system', 'redis_host') ->andReturn('localhost'); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'redis_port', NULL) + ->with('system', 'redis_port') ->andReturn(null); $this->cache = CacheDriverFactory::create('redis'); diff --git a/tests/src/Core/Config/ConfigCacheLoaderTest.php b/tests/src/Core/Config/Cache/ConfigCacheLoaderTest.php similarity index 95% rename from tests/src/Core/Config/ConfigCacheLoaderTest.php rename to tests/src/Core/Config/Cache/ConfigCacheLoaderTest.php index 6be89bc40f..f91f267c71 100644 --- a/tests/src/Core/Config/ConfigCacheLoaderTest.php +++ b/tests/src/Core/Config/Cache/ConfigCacheLoaderTest.php @@ -1,9 +1,9 @@ delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . @@ -91,6 +92,7 @@ class ConfigCacheLoaderTest extends MockedTest $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . @@ -122,6 +124,7 @@ class ConfigCacheLoaderTest extends MockedTest $this->delConfigFile('local.config.php'); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . @@ -160,6 +163,7 @@ class ConfigCacheLoaderTest extends MockedTest vfsStream::create($structure, $this->root); $file = dirname(__DIR__) . DIRECTORY_SEPARATOR . + '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'datasets' . DIRECTORY_SEPARATOR . diff --git a/tests/src/Core/Config/ConfigCacheTest.php b/tests/src/Core/Config/Cache/ConfigCacheTest.php similarity index 55% rename from tests/src/Core/Config/ConfigCacheTest.php rename to tests/src/Core/Config/Cache/ConfigCacheTest.php index 25ea6030f7..ac9fae540b 100644 --- a/tests/src/Core/Config/ConfigCacheTest.php +++ b/tests/src/Core/Config/Cache/ConfigCacheTest.php @@ -1,8 +1,8 @@ loadConfigArray($data); + $configCache->load($data); $this->assertConfigValues($data, $configCache); } @@ -67,18 +67,38 @@ class ConfigCacheTest extends MockedTest ]; $configCache = new ConfigCache(); - $configCache->loadConfigArray($data); - $configCache->loadConfigArray($override); + $configCache->load($data); + $configCache->load($override); $this->assertConfigValues($data, $configCache); // override the value - $configCache->loadConfigArray($override, true); + $configCache->load($override, true); $this->assertEquals($override['system']['test'], $configCache->get('system', 'test')); $this->assertEquals($override['system']['boolTrue'], $configCache->get('system', 'boolTrue')); } + /** + * Test the loadConfigArray() method with wrong/empty datasets + */ + public function testLoadConfigArrayWrong() + { + $configCache = new ConfigCache(); + + // empty dataset + $configCache->load([]); + $this->assertEmpty($configCache->getAll()); + + // wrong dataset + $configCache->load(['system' => 'not_array']); + $this->assertEmpty($configCache->getAll()); + + // incomplete dataset (key is integer ID of the array) + $configCache->load(['system' => ['value']]); + $this->assertEquals('value', $configCache->get('system', 0)); + } + /** * Test the getAll() method * @dataProvider dataTests @@ -86,14 +106,12 @@ class ConfigCacheTest extends MockedTest public function testGetAll($data) { $configCache = new ConfigCache(); - $configCache->loadConfigArray($data); + $configCache->load($data); $all = $configCache->getAll(); $this->assertContains($data['system'], $all); - - // config values are stored directly in the array base - $this->assertEquals($data['config']['a'], $all['a']); + $this->assertContains($data['config'], $all); } /** @@ -113,6 +131,54 @@ class ConfigCacheTest extends MockedTest $this->assertConfigValues($data, $configCache); } + /** + * Test the get() method without a value + */ + public function testGetEmpty() + { + $configCache = new ConfigCache(); + + $this->assertEquals('!!', $configCache->get('something', 'value')); + } + + /** + * Test the has() method + */ + public function testHas() + { + $configCache = new ConfigCache(); + + $this->assertFalse($configCache->has('system', 'test')); + $this->assertFalse($configCache->has('system')); + + $configCache->set('system', 'test', 'it'); + $this->assertTrue($configCache->has('system', 'test')); + $this->assertTrue($configCache->has('system')); + } + + /** + * Test the get() method with a category + */ + public function testGetCat() + { + $configCache = new ConfigCache([ + 'system' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ], + 'config' => [ + 'key3' => 'value3', + ], + ]); + + $this->assertTrue($configCache->has('system')); + + $this->assertEquals([ + 'key1' => 'value1', + 'key2' => 'value2', + ], $configCache->get('system')); + } + /** * Test the delete() method * @dataProvider dataTests @@ -149,6 +215,32 @@ class ConfigCacheTest extends MockedTest } + /** + * Test the getP() method with a category + */ + public function testGetPCat() + { + $configCache = new ConfigCache(); + $uid = 345; + + $configCache->loadP($uid, [ + 'system' => [ + 'key1' => 'value1', + 'key2' => 'value2', + ], + 'config' => [ + 'key3' => 'value3', + ], + ]); + + $this->assertTrue($configCache->hasP($uid,'system')); + + $this->assertEquals([ + 'key1' => 'value1', + 'key2' => 'value2', + ], $configCache->get($uid, 'system')); + } + /** * Test the deleteP() method * @dataProvider dataTests @@ -172,4 +264,20 @@ class ConfigCacheTest extends MockedTest $this->assertEmpty($configCache->getAll()); } + + /** + * Test the hasP() method + */ + public function testHasP() + { + $configCache = new ConfigCache(); + $uid = 345; + + $this->assertFalse($configCache->hasP($uid, 'system', 'test')); + $this->assertFalse($configCache->hasP($uid, 'system')); + + $configCache->setP($uid, 'system', 'test', 'it'); + $this->assertTrue($configCache->hasP($uid, 'system', 'test')); + $this->assertTrue($configCache->hasP($uid, 'system')); + } } diff --git a/tests/src/Core/Config/ConfigurationTest.php b/tests/src/Core/Config/ConfigurationTest.php new file mode 100644 index 0000000000..2e4fcd4f58 --- /dev/null +++ b/tests/src/Core/Config/ConfigurationTest.php @@ -0,0 +1,276 @@ + ['data' => 'it'], + 'boolTrue' => ['data' => true], + 'boolFalse' => ['data' => false], + 'integer' => ['data' => 235], + 'decimal' => ['data' => 2.456], + 'array' => ['data' => ['1', 2, '3', true, false]], + 'boolIntTrue' => ['data' => 1], + 'boolIntFalse' => ['Data' => 0], + ]; + } + + /** + * Test the configuration initialization + */ + public function testSetUp() + { + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->once(); + + $configuration = new Configuration($configCache, $configAdapter); + + $this->assertInstanceOf(IConfigCache::class, $configuration->getCache()); + } + + /** + * Test the configuration load() method + */ + public function testCacheLoad() + { + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); + // constructor loading + $configAdapter->shouldReceive('load')->andReturn([])->once(); + // expected loading + $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'it']])->once(); + $configAdapter->shouldReceive('isLoaded')->with('testing', 'test')->andReturn(true)->once(); + + $configuration = new Configuration($configCache, $configAdapter); + $configuration->load('testing'); + + $this->assertEquals('it', $configuration->get('testing', 'test')); + $this->assertEquals('it', $configuration->getCache()->get('testing', 'test')); + } + + /** + * Test the configuration load() method with overwrite + */ + public function testCacheLoadDouble() + { + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(5); + // constructor loading + $configAdapter->shouldReceive('load')->andReturn([])->once(); + // expected loading + $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'it']])->once(); + $configAdapter->shouldReceive('isLoaded')->with('testing', 'test')->andReturn(true)->twice(); + // expected next loading + $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'again']])->once(); + + $configuration = new Configuration($configCache, $configAdapter); + $configuration->load('testing'); + + $this->assertEquals('it', $configuration->get('testing', 'test')); + $this->assertEquals('it', $configuration->getCache()->get('testing', 'test')); + + $configuration->load('testing'); + + $this->assertEquals('again', $configuration->get('testing', 'test')); + $this->assertEquals('again', $configuration->getCache()->get('testing', 'test')); + } + + /** + * Test the configuration get() and set() methods without adapter + * @dataProvider dataTests + */ + public function testSetGetWithoutDB($data) + { + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(3); + + $configuration = new Configuration($configCache, $configAdapter); + + $this->assertTrue($configuration->set('test', 'it', $data)); + + $this->assertEquals($data, $configuration->get('test', 'it')); + $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + } + + /** + * Test the configuration get() and set() methods with adapter + * @dataProvider dataTests + */ + public function testSetGetWithDB($data) + { + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); + // constructor loading + $configAdapter->shouldReceive('load')->andReturn([])->once(); + $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); + $configAdapter->shouldReceive('set')->with('test', 'it', $data)->andReturn(true)->once(); + + $configuration = new Configuration($configCache, $configAdapter); + + $this->assertTrue($configuration->set('test', 'it', $data)); + + $this->assertEquals($data, $configuration->get('test', 'it')); + $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + } + + /** + * Test the configuration get() method with wrong value and no db + */ + public function testGetWrongWithoutDB() + { + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); + + $configuration = new Configuration($configCache, $configAdapter); + + // without refresh + $this->assertNull($configuration->get('test', 'it')); + + /// beware that the cache returns '!!' and not null for a non existing value + $this->assertEquals('!!', $configuration->getCache()->get('test', 'it')); + + // with default value + $this->assertEquals('default', $configuration->get('test', 'it', 'default')); + + // with default value and refresh + $this->assertEquals('default', $configuration->get('test', 'it', 'default', true)); + } + + /** + * Test the configuration get() method with refresh + * @dataProvider dataTests + */ + public function testGetWithRefresh($data) + { + $configCache = new ConfigCache(['test' => ['it' => 'now']]); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); + // constructor loading + $configAdapter->shouldReceive('load')->andReturn([])->once(); + $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->twice(); + $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn($data)->once(); + $configAdapter->shouldReceive('isLoaded')->with('test', 'not')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with('test', 'not')->andReturn('!!')->once(); + + $configuration = new Configuration($configCache, $configAdapter); + + // without refresh + $this->assertEquals('now', $configuration->get('test', 'it')); + $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + + // with refresh + $this->assertEquals($data, $configuration->get('test', 'it', null, true)); + $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + + // without refresh and wrong value and default + $this->assertEquals('default', $configuration->get('test', 'not', 'default')); + $this->assertEquals('!!', $configuration->getCache()->get('test', 'not')); + } + + /** + * Test the configuration get() method with different isLoaded settings + * @dataProvider dataTests + */ + public function testGetWithoutLoaded($data) + { + $configCache = new ConfigCache(['test' => ['it' => 'now']]); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); + // constructor loading + $configAdapter->shouldReceive('load')->andReturn([])->once(); + + $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn('!!')->once(); + + $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with('test', 'it')->andReturn($data)->once(); + + $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); + + $configuration = new Configuration($configCache, $configAdapter); + + // first run is not loaded and no data is found in the DB + $this->assertEquals('now', $configuration->get('test', 'it')); + $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + + // second run is not loaded, but now data is found in the db (overwrote cache) + $this->assertEquals($data, $configuration->get('test', 'it')); + $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + + // third run is loaded and therefore cache is used + $this->assertEquals($data, $configuration->get('test', 'it')); + $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + } + + /** + * Test the configuration delete() method without adapter + * @dataProvider dataTests + */ + public function testDeleteWithoutDB($data) + { + $configCache = new ConfigCache(['test' => ['it' => $data]]); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); + + $configuration = new Configuration($configCache, $configAdapter); + + $this->assertEquals($data, $configuration->get('test', 'it')); + $this->assertEquals($data, $configuration->getCache()->get('test', 'it')); + + $this->assertTrue($configuration->delete('test', 'it')); + $this->assertNull($configuration->get('test', 'it')); + $this->assertEquals('!!', $configuration->getCache()->get('test', 'it')); + + $this->assertEmpty($configuration->getCache()->getAll()); + } + + /** + * Test the configuration delete() method with adapter + */ + public function testDeleteWithDB() + { + $configCache = new ConfigCache(['test' => ['it' => 'now', 'quarter' => 'true']]); + $configAdapter = \Mockery::mock(IConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(6); + // constructor loading + $configAdapter->shouldReceive('load')->andReturn([])->once(); + $configAdapter->shouldReceive('isLoaded')->with('test', 'it')->andReturn(true)->once(); + + $configAdapter->shouldReceive('delete')->with('test', 'it')->andReturn(false)->once(); + + $configAdapter->shouldReceive('delete')->with('test', 'second')->andReturn(true)->once(); + $configAdapter->shouldReceive('delete')->with('test', 'third')->andReturn(false)->once(); + $configAdapter->shouldReceive('delete')->with('test', 'quarter')->andReturn(true)->once(); + + $configuration = new Configuration($configCache, $configAdapter); + + $this->assertEquals('now', $configuration->get('test', 'it')); + $this->assertEquals('now', $configuration->getCache()->get('test', 'it')); + + // delete from cache only + $this->assertTrue($configuration->delete('test', 'it')); + // delete from db only + $this->assertTrue($configuration->delete('test', 'second')); + // no delete + $this->assertFalse($configuration->delete('test', 'third')); + // delete both + $this->assertTrue($configuration->delete('test', 'quarter')); + + $this->assertEmpty($configuration->getCache()->getAll()); + } +} diff --git a/tests/src/Core/Config/PConfigurationTest.php b/tests/src/Core/Config/PConfigurationTest.php new file mode 100644 index 0000000000..0259944147 --- /dev/null +++ b/tests/src/Core/Config/PConfigurationTest.php @@ -0,0 +1,247 @@ + ['data' => 'it'], + 'boolTrue' => ['data' => true], + 'boolFalse' => ['data' => false], + 'integer' => ['data' => 235], + 'decimal' => ['data' => 2.456], + 'array' => ['data' => ['1', 2, '3', true, false]], + 'boolIntTrue' => ['data' => 1], + 'boolIntFalse' => ['Data' => 0], + ]; + } + + /** + * Test the configuration load() method + */ + public function testCacheLoad() + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->twice(); + // expected loading + $configAdapter->shouldReceive('load') + ->with($uid, 'testing') + ->andReturn(['testing' => ['test' => 'it']]) + ->once(); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'testing', 'test')->andReturn(true)->once(); + + $configuration = new PConfiguration($configCache, $configAdapter); + $configuration->load($uid, 'testing'); + + $this->assertEquals('it', $configuration->get($uid, 'testing', 'test')); + } + + /** + * Test the configuration load() method with overwrite + */ + public function testCacheLoadDouble() + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); + // expected loading + $configAdapter->shouldReceive('load')->with($uid, 'testing')->andReturn(['testing' => ['test' => 'it']])->once(); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'testing', 'test')->andReturn(true)->twice(); + // expected next loading + $configAdapter->shouldReceive('load')->andReturn(['testing' => ['test' => 'again']])->once(); + + $configuration = new PConfiguration($configCache, $configAdapter); + $configuration->load($uid, 'testing'); + + $this->assertEquals('it', $configuration->get($uid, 'testing', 'test')); + + $configuration->load($uid, 'testing'); + + $this->assertEquals('again', $configuration->get($uid, 'testing', 'test')); + } + + /** + * Test the configuration get() and set() methods without adapter + * @dataProvider dataTests + */ + public function testSetGetWithoutDB($data) + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(2); + + $configuration = new PConfiguration($configCache, $configAdapter); + + $this->assertTrue($configuration->set($uid, 'test', 'it', $data)); + + $this->assertEquals($data, $configuration->get($uid, 'test', 'it')); + } + + /** + * Test the configuration get() and set() methods with adapter + * @dataProvider dataTests + */ + public function testSetGetWithDB($data) + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(2); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once(); + $configAdapter->shouldReceive('set')->with($uid, 'test', 'it', $data)->andReturn(true)->once(); + + $configuration = new PConfiguration($configCache, $configAdapter); + + $this->assertTrue($configuration->set($uid, 'test', 'it', $data)); + + $this->assertEquals($data, $configuration->get($uid, 'test', 'it')); + } + + /** + * Test the configuration get() method with wrong value and no db + */ + public function testGetWrongWithoutDB() + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(3); + + $configuration = new PConfiguration($configCache, $configAdapter); + + // without refresh + $this->assertNull($configuration->get($uid, 'test', 'it')); + + // with default value + $this->assertEquals('default', $configuration->get($uid, 'test', 'it', 'default')); + + // with default value and refresh + $this->assertEquals('default', $configuration->get($uid, 'test', 'it', 'default', true)); + } + + /** + * Test the configuration get() method with refresh + * @dataProvider dataTests + */ + public function testGetWithRefresh($data) + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(4); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn('now')->once(); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->twice(); + $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn($data)->once(); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'not')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with($uid, 'test', 'not')->andReturn('!!')->once(); + + $configuration = new PConfiguration($configCache, $configAdapter); + + // without refresh + $this->assertEquals('now', $configuration->get($uid, 'test', 'it')); + // use the cache again + $this->assertEquals('now', $configuration->get($uid, 'test', 'it')); + + // with refresh (and load the second value out of the db) + $this->assertEquals($data, $configuration->get($uid, 'test', 'it', null, true)); + + // without refresh and wrong value and default + $this->assertEquals('default', $configuration->get($uid, 'test', 'not', 'default')); + } + + /** + * Test the configuration get() method with different isLoaded settings + * @dataProvider dataTests + */ + public function testGetWithoutLoaded($data) + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(3); + + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn('!!')->once(); + + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(false)->once(); + $configAdapter->shouldReceive('get')->with($uid, 'test', 'it')->andReturn($data)->once(); + + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once(); + + $configuration = new PConfiguration($configCache, $configAdapter); + + // first run is not loaded and no data is found in the DB + $this->assertNull($configuration->get($uid, 'test', 'it')); + + // second run is not loaded, but now data is found in the db (overwrote cache) + $this->assertEquals($data, $configuration->get($uid,'test', 'it')); + + // third run is loaded and therefore cache is used + $this->assertEquals($data, $configuration->get($uid,'test', 'it')); + } + + /** + * Test the configuration delete() method without adapter + * @dataProvider dataTests + */ + public function testDeleteWithoutDB($data) + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(false)->times(4); + + $configuration = new PConfiguration($configCache, $configAdapter); + + $this->assertTrue($configuration->set($uid, 'test', 'it', $data)); + $this->assertEquals($data, $configuration->get($uid, 'test', 'it')); + + $this->assertTrue($configuration->delete($uid, 'test', 'it')); + $this->assertNull($configuration->get($uid, 'test', 'it')); + } + + /** + * Test the configuration delete() method with adapter + */ + public function testDeleteWithDB() + { + $uid = 234; + $configCache = new ConfigCache(); + $configAdapter = \Mockery::mock(IPConfigAdapter::class); + $configAdapter->shouldReceive('isConnected')->andReturn(true)->times(6); + $configAdapter->shouldReceive('set')->with($uid, 'test', 'it', 'now')->andReturn(false)->once(); + $configAdapter->shouldReceive('isLoaded')->with($uid, 'test', 'it')->andReturn(true)->once(); + + $configAdapter->shouldReceive('delete')->with($uid, 'test', 'it')->andReturn(false)->once(); + + $configAdapter->shouldReceive('delete')->with($uid, 'test', 'second')->andReturn(true)->once(); + $configAdapter->shouldReceive('delete')->with($uid, 'test', 'third')->andReturn(false)->once(); + $configAdapter->shouldReceive('delete')->with($uid, 'test', 'quarter')->andReturn(true)->once(); + + $configuration = new PConfiguration($configCache, $configAdapter); + + $this->assertFalse($configuration->set($uid, 'test', 'it', 'now')); + $this->assertEquals('now', $configuration->get($uid, 'test', 'it')); + + // delete from set + $this->assertTrue($configuration->delete($uid, 'test', 'it')); + // delete from db only + $this->assertTrue($configuration->delete($uid, 'test', 'second')); + // no delete + $this->assertFalse($configuration->delete($uid, 'test', 'third')); + // delete both + $this->assertTrue($configuration->delete($uid, 'test', 'quarter')); + } +} diff --git a/tests/src/Core/Console/AutomaticInstallationConsoleTest.php b/tests/src/Core/Console/AutomaticInstallationConsoleTest.php index 41ccce0b28..73b6835fb5 100644 --- a/tests/src/Core/Console/AutomaticInstallationConsoleTest.php +++ b/tests/src/Core/Console/AutomaticInstallationConsoleTest.php @@ -52,9 +52,9 @@ class AutomaticInstallationConsoleTest extends ConsoleTest $this->db_user = getenv('MYSQL_USERNAME') . getenv('MYSQL_USER'); $this->db_pass = getenv('MYSQL_PASSWORD'); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('config', 'php_path', NULL) + ->with('config', 'php_path') ->andReturn(false); $this->mockL10nT(); diff --git a/tests/src/Core/Console/ConfigConsoleTest.php b/tests/src/Core/Console/ConfigConsoleTest.php index 505c4f794d..579b28e026 100644 --- a/tests/src/Core/Console/ConfigConsoleTest.php +++ b/tests/src/Core/Console/ConfigConsoleTest.php @@ -32,14 +32,14 @@ class ConfigConsoleTest extends ConsoleTest } function testSetGetKeyValue() { - $this->configCache + $this->configMock ->shouldReceive('set') ->with('config', 'test', 'now') ->andReturn(true) ->once(); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('config', 'test', NULL) + ->with('config', 'test') ->andReturn('now') ->twice(); @@ -50,9 +50,9 @@ class ConfigConsoleTest extends ConsoleTest $txt = $this->dumpExecute($console); $this->assertEquals("config.test <= now\n", $txt); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('config', 'test', null) + ->with('config', 'test') ->andReturn('now') ->once(); @@ -62,9 +62,9 @@ class ConfigConsoleTest extends ConsoleTest $txt = $this->dumpExecute($console); $this->assertEquals("config.test => now\n", $txt); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('config', 'test', null) + ->with('config', 'test') ->andReturn(null) ->once(); @@ -77,9 +77,9 @@ class ConfigConsoleTest extends ConsoleTest function testSetArrayValue() { $testArray = [1, 2, 3]; - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('config', 'test', null) + ->with('config', 'test') ->andReturn($testArray) ->once(); @@ -105,9 +105,9 @@ class ConfigConsoleTest extends ConsoleTest } function testVerbose() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('test', 'it', null) + ->with('test', 'it') ->andReturn('now') ->once(); $console = new Config($this->consoleArgv); @@ -133,14 +133,14 @@ CONF; } function testUnableToSet() { - $this->configCache + $this->configMock ->shouldReceive('set') ->with('test', 'it', 'now') ->andReturn(false) ->once(); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('test', 'it', NULL) + ->with('test', 'it') ->andReturn(NULL) ->once(); $console = new Config(); diff --git a/tests/src/Core/Console/ConsoleTest.php b/tests/src/Core/Console/ConsoleTest.php index 905d214cac..4f7acc9c42 100644 --- a/tests/src/Core/Console/ConsoleTest.php +++ b/tests/src/Core/Console/ConsoleTest.php @@ -29,8 +29,7 @@ abstract class ConsoleTest extends MockedTest Intercept::setUp(); $this->setUpVfsDir(); - $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache'); - $this->mockApp($this->root, $configMock); + $this->mockApp($this->root); } /** diff --git a/tests/src/Core/Lock/LockTest.php b/tests/src/Core/Lock/LockTest.php index ab8e1b2f2e..6dc170e514 100644 --- a/tests/src/Core/Lock/LockTest.php +++ b/tests/src/Core/Lock/LockTest.php @@ -27,8 +27,7 @@ abstract class LockTest extends MockedTest { // Reusable App object $this->setUpVfsDir(); - $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache'); - $this->mockApp($this->root, $configMock); + $this->mockApp($this->root); $this->app ->shouldReceive('getHostname') ->andReturn('friendica.local'); diff --git a/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php b/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php index 46f29f52e2..ad20f5bfdb 100644 --- a/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php +++ b/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php @@ -13,14 +13,14 @@ class MemcacheCacheLockDriverTest extends LockTest { protected function getInstance() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'memcache_host', NULL) + ->with('system', 'memcache_host') ->andReturn('localhost'); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'memcache_port', NULL) + ->with('system', 'memcache_port') ->andReturn(11211); return new CacheLockDriver(CacheDriverFactory::create('memcache')); diff --git a/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php b/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php index 72271c98b9..a5bdeaedb8 100644 --- a/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php +++ b/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php @@ -13,9 +13,9 @@ class MemcachedCacheLockDriverTest extends LockTest { protected function getInstance() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'memcached_hosts', NULL) + ->with('system', 'memcached_hosts') ->andReturn([0 => 'localhost, 11211']); return new CacheLockDriver(CacheDriverFactory::create('memcached')); diff --git a/tests/src/Core/Lock/RedisCacheLockDriverTest.php b/tests/src/Core/Lock/RedisCacheLockDriverTest.php index 0c9deea16e..5f047bc664 100644 --- a/tests/src/Core/Lock/RedisCacheLockDriverTest.php +++ b/tests/src/Core/Lock/RedisCacheLockDriverTest.php @@ -13,14 +13,14 @@ class RedisCacheLockDriverTest extends LockTest { protected function getInstance() { - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'redis_host', NULL) + ->with('system', 'redis_host') ->andReturn('localhost'); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'redis_port', NULL) + ->with('system', 'redis_port') ->andReturn(null); return new CacheLockDriver(CacheDriverFactory::create('redis')); diff --git a/tests/src/Core/Lock/SemaphoreLockDriverTest.php b/tests/src/Core/Lock/SemaphoreLockDriverTest.php index c2b9414572..a37fbffbed 100644 --- a/tests/src/Core/Lock/SemaphoreLockDriverTest.php +++ b/tests/src/Core/Lock/SemaphoreLockDriverTest.php @@ -12,9 +12,9 @@ class SemaphoreLockDriverTest extends LockTest $this->app->shouldReceive('getHostname')->andReturn('friendica.local'); - $this->configCache + $this->configMock ->shouldReceive('get') - ->with('system', 'temppath', NULL) + ->with('system', 'temppath') ->andReturn('/tmp/'); } diff --git a/tests/src/Database/DBATest.php b/tests/src/Database/DBATest.php index f2a5cc5558..e8b9c68b19 100644 --- a/tests/src/Database/DBATest.php +++ b/tests/src/Database/DBATest.php @@ -3,6 +3,7 @@ namespace Friendica\Test\Database; use Friendica\App; use Friendica\Core\Config; +use Friendica\Core\Config\Cache; use Friendica\Database\DBA; use Friendica\Factory; use Friendica\Test\DatabaseTest; @@ -13,11 +14,14 @@ class DBATest extends DatabaseTest public function setUp() { $basedir = BasePath::create(dirname(__DIR__) . '/../../'); - $configLoader = new Config\ConfigCacheLoader($basedir); - $config = Factory\ConfigFactory::createCache($configLoader); + $configLoader = new Cache\ConfigCacheLoader($basedir); + $configCache = Factory\ConfigFactory::createCache($configLoader); + $profiler = Factory\ProfilerFactory::create($configCache); + Factory\DBFactory::init($configCache, $profiler, $_SERVER); + $config = Factory\ConfigFactory::createConfig($configCache); + Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config); - $this->app = new App($config, $logger, false); - $this->logOutput = FActory\LoggerFactory::enableTest($this->app->getLogger()); + $this->app = new App($config, $logger, $profiler, false); parent::setUp(); diff --git a/tests/src/Database/DBStructureTest.php b/tests/src/Database/DBStructureTest.php index bc50a0a691..325ad4e5e7 100644 --- a/tests/src/Database/DBStructureTest.php +++ b/tests/src/Database/DBStructureTest.php @@ -3,7 +3,7 @@ namespace Friendica\Test\Database; use Friendica\App; -use Friendica\Core\Config; +use Friendica\Core\Config\Cache; use Friendica\Database\DBStructure; use Friendica\Factory; use Friendica\Test\DatabaseTest; @@ -14,11 +14,14 @@ class DBStructureTest extends DatabaseTest public function setUp() { $basedir = BasePath::create(dirname(__DIR__) . '/../../'); - $configLoader = new Config\ConfigCacheLoader($basedir); - $config = Factory\ConfigFactory::createCache($configLoader); + $configLoader = new Cache\ConfigCacheLoader($basedir); + $configCache = Factory\ConfigFactory::createCache($configLoader); + $profiler = Factory\ProfilerFactory::create($configCache); + Factory\DBFactory::init($configCache, $profiler, $_SERVER); + $config = Factory\ConfigFactory::createConfig($configCache); + Factory\ConfigFactory::createPConfig($configCache); $logger = Factory\LoggerFactory::create('test', $config); - $this->app = new App($config, $logger, false); - $this->logOutput = FActory\LoggerFactory::enableTest($this->app->getLogger()); + $this->app = new App($config, $logger, $profiler, false); parent::setUp(); } diff --git a/tests/src/Util/ProfilerTest.php b/tests/src/Util/ProfilerTest.php new file mode 100644 index 0000000000..f242fd43c5 --- /dev/null +++ b/tests/src/Util/ProfilerTest.php @@ -0,0 +1,181 @@ +logger = \Mockery::mock('Psr\Log\LoggerInterface'); + } + + /** + * Test the Profiler setup + */ + public function testSetUp() + { + $profiler = new Profiler(true, true); + } + + /** + * A dataset for different profiling settings + * @return array + */ + public function dataPerformance() + { + return [ + 'database' => [ + 'timestamp' => time(), + 'name' => 'database', + 'functions' => ['test', 'it'], + ], + 'database_write' => [ + 'timestamp' => time(), + 'name' => 'database_write', + 'functions' => ['test', 'it2'], + ], + 'cache' => [ + 'timestamp' => time(), + 'name' => 'cache', + 'functions' => ['test', 'it3'], + ], + 'cache_write' => [ + 'timestamp' => time(), + 'name' => 'cache_write', + 'functions' => ['test', 'it4'], + ], + 'network' => [ + 'timestamp' => time(), + 'name' => 'network', + 'functions' => ['test', 'it5'], + ], + 'file' => [ + 'timestamp' => time(), + 'name' => 'file', + 'functions' => [], + ], + 'rendering' => [ + 'timestamp' => time(), + 'name' => 'rendering', + 'functions' => ['test', 'it7'], + ], + 'parser' => [ + 'timestamp' => time(), + 'name' => 'parser', + 'functions' => ['test', 'it8'], + ], + 'marktime' => [ + 'timestamp' => time(), + 'name' => 'parser', + 'functions' => ['test'], + ], + // This one isn't set during reset + 'unknown' => [ + 'timestamp' => time(), + 'name' => 'unknown', + 'functions' => ['test'], + ], + ]; + } + + /** + * Test the Profiler savetimestamp + * @dataProvider dataPerformance + */ + public function testSaveTimestamp($timestamp, $name, array $functions) + { + $profiler = new Profiler(true, true); + + foreach ($functions as $function) { + $profiler->saveTimestamp($timestamp, $name, $function); + } + + $this->assertGreaterThanOrEqual(0, $profiler->get($name)); + } + + /** + * Test the Profiler reset + * @dataProvider dataPerformance + */ + public function testReset($timestamp, $name, array $functions) + { + $profiler = new Profiler(true, true); + + $profiler->saveTimestamp($timestamp, $name); + $profiler->reset(); + + $this->assertEquals(0, $profiler->get($name)); + } + + public function dataBig() + { + return [ + 'big' => [ + 'data' => [ + 'database' => [ + 'timestamp' => time(), + 'name' => 'database', + 'functions' => ['test', 'it'], + ], + 'database_write' => [ + 'timestamp' => time(), + 'name' => 'database_write', + 'functions' => ['test', 'it2'], + ], + 'cache' => [ + 'timestamp' => time(), + 'name' => 'cache', + 'functions' => ['test', 'it3'], + ], + 'cache_write' => [ + 'timestamp' => time(), + 'name' => 'cache_write', + 'functions' => ['test', 'it4'], + ], + 'network' => [ + 'timestamp' => time(), + 'name' => 'network', + 'functions' => ['test', 'it5'], + ], + ] + ] + ]; + } + + /** + * Test the output of the Profiler + * @dataProvider dataBig + */ + public function testSaveLog($data) + { + $this->logger + ->shouldReceive('info') + ->with('test', \Mockery::any()) + ->once(); + $this->logger + ->shouldReceive('info') + ->once(); + + $profiler = new Profiler(true, true); + + foreach ($data as $perf => $items) { + foreach ($items['functions'] as $function) { + $profiler->saveTimestamp($items['timestamp'], $items['name'], $function); + } + } + + $profiler->saveLog($this->logger, 'test'); + } +}