Merge remote-tracking branch 'upstream/develop' into error-handling

This commit is contained in:
Michael 2021-10-31 05:25:39 +00:00
commit 516018861e
235 changed files with 10885 additions and 10716 deletions

View file

@ -4,7 +4,7 @@ Friendica Storage Backend Addon development
* [Home](help) * [Home](help)
Storage backends can be added via addons. Storage backends can be added via addons.
A storage backend is implemented as a class, and the plugin register the class to make it avaiable to the system. A storage backend is implemented as a class, and the plugin register the class to make it available to the system.
## The Storage Backend Class ## The Storage Backend Class
@ -12,14 +12,14 @@ The class must live in `Friendica\Addon\youraddonname` namespace, where `youradd
There are two different interfaces you need to implement. There are two different interfaces you need to implement.
### `IWritableStorage` ### `ICanWriteToStorage`
The class must implement `Friendica\Model\Storage\IWritableStorage` interface. All method in the interface must be implemented: The class must implement `Friendica\Core\Storage\Capability\ICanWriteToStorage` interface. All method in the interface must be implemented:
```php ```php
namespace Friendica\Model\Storage\IWritableStorage; namespace Friendica\Core\Storage\Capability\ICanWriteToStorage;
interface IWritableStorage interface ICanWriteToStorage
{ {
public function get(string $reference); public function get(string $reference);
public function put(string $data, string $reference = ''); public function put(string $data, string $reference = '');
@ -33,17 +33,17 @@ interface IWritableStorage
- `put(string $data, string $reference)` saves data in `$data` to position `$reference`, or a new position if `$reference` is empty. - `put(string $data, string $reference)` saves data in `$data` to position `$reference`, or a new position if `$reference` is empty.
- `delete(string $reference)` delete data pointed by `$reference` - `delete(string $reference)` delete data pointed by `$reference`
### `IStorageConfiguration` ### `ICanConfigureStorage`
Each storage backend can have options the admin can set in admin page. Each storage backend can have options the admin can set in admin page.
To make the options possible, you need to implement the `Friendica\Model\Storage\IStorageConfiguration` interface. To make the options possible, you need to implement the `Friendica\Core\Storage\Capability\ICanConfigureStorage` interface.
All methods in the interface must be implemented: All methods in the interface must be implemented:
```php ```php
namespace Friendica\Model\Storage\IStorageConfiguration; namespace Friendica\Core\Storage\Capability\ICanConfigureStorage;
interface IStorageConfiguration interface ICanConfigureStorage
{ {
public function getOptions(); public function getOptions();
public function saveOptions(array $data); public function saveOptions(array $data);
@ -108,7 +108,7 @@ When the plugin is uninstalled, registered backends must be unregistered using
`DI::facStorage()->unregister(string $class)`. `DI::facStorage()->unregister(string $class)`.
You have to register a new hook in your addon, listening on `storage_instance(App $a, array $data)`. You have to register a new hook in your addon, listening on `storage_instance(App $a, array $data)`.
In case `$data['name']` is your storage class name, you have to instance a new instance of your `Friendica\Model\Storage\IStorage` class. In case `$data['name']` is your storage class name, you have to instance a new instance of your `Friendica\Core\Storage\Capability\ICanReadFromStorage` class.
Set the instance of your class as `$data['storage']` to pass it back to the backend. Set the instance of your class as `$data['storage']` to pass it back to the backend.
This is necessary because it isn't always clear, if you need further construction arguments. This is necessary because it isn't always clear, if you need further construction arguments.
@ -124,7 +124,7 @@ Add a new test class which's naming convention is `StorageClassTest`, which exte
Override the two necessary instances: Override the two necessary instances:
```php ```php
use Friendica\Model\Storage\IWritableStorage; use Friendica\Core\Storage\Capability\ICanWriteToStorage;
abstract class StorageTest abstract class StorageTest
{ {
@ -132,7 +132,7 @@ abstract class StorageTest
abstract protected function getInstance(); abstract protected function getInstance();
// Assertion for the option array you return for your new StorageClass // Assertion for the option array you return for your new StorageClass
abstract protected function assertOption(IWritableStorage $storage); abstract protected function assertOption(ICanWriteToStorage $storage);
} }
``` ```
@ -156,16 +156,16 @@ If there's a predecessor to this exception (e.g. you caught an exception and are
Example: Example:
```php ```php
use Friendica\Model\Storage\IWritableStorage; use Friendica\Core\Storage\Capability\ICanWriteToStorage;
class ExampleStorage implements IWritableStorage class ExampleStorage implements ICanWriteToStorage
{ {
public function get(string $reference) : string public function get(string $reference) : string
{ {
try { try {
throw new Exception('a real bad exception'); throw new Exception('a real bad exception');
} catch (Exception $exception) { } catch (Exception $exception) {
throw new \Friendica\Model\Storage\StorageException(sprintf('The Example Storage throws an exception for reference %s', $reference), 500, $exception); throw new \Friendica\Core\Storage\Exception\StorageException(sprintf('The Example Storage throws an exception for reference %s', $reference), 500, $exception);
} }
} }
} }
@ -186,12 +186,12 @@ The file will be `addon/samplestorage/SampleStorageBackend.php`:
<?php <?php
namespace Friendica\Addon\samplestorage; namespace Friendica\Addon\samplestorage;
use Friendica\Model\Storage\IWritableStorage; use Friendica\Core\Storage\Capability\ICanWriteToStorage;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n; use Friendica\Core\L10n;
class SampleStorageBackend implements IWritableStorage class SampleStorageBackend implements ICanWriteToStorage
{ {
const NAME = 'Sample Storage'; const NAME = 'Sample Storage';
@ -247,14 +247,14 @@ class SampleStorageBackend implements IWritableStorage
<?php <?php
namespace Friendica\Addon\samplestorage; namespace Friendica\Addon\samplestorage;
use Friendica\Model\Storage\IStorageConfiguration; use Friendica\Core\Storage\Capability\ICanConfigureStorage;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n; use Friendica\Core\L10n;
class SampleStorageBackendConfig implements IStorageConfiguration class SampleStorageBackendConfig implements ICanConfigureStorage
{ {
/** @var IConfig */ /** @var \Friendica\Core\Config\Capability\IManageConfigValues */
private $config; private $config;
/** @var L10n */ /** @var L10n */
private $l10n; private $l10n;
@ -265,7 +265,7 @@ class SampleStorageBackendConfig implements IStorageConfiguration
* You can add here every dynamic class as dependency you like and add them to a private field * You can add here every dynamic class as dependency you like and add them to a private field
* Friendica automatically creates these classes and passes them as argument to the constructor * Friendica automatically creates these classes and passes them as argument to the constructor
*/ */
public function __construct(IConfig $config, L10n $l10n) public function __construct(IManageConfigValues $config, L10n $l10n)
{ {
$this->config = $config; $this->config = $config;
$this->l10n = $l10n; $this->l10n = $l10n;
@ -357,8 +357,8 @@ function samplestorage_storage_config(App $a, array &$data)
**Theoretically - until tests for Addons are enabled too - create a test class with the name `addon/tests/SampleStorageTest.php`: **Theoretically - until tests for Addons are enabled too - create a test class with the name `addon/tests/SampleStorageTest.php`:
```php ```php
use Friendica\Model\Storage\IWritableStorage; use Friendica\Core\Storage\Capability\ICanWriteToStorage;
use Friendica\Test\src\Model\Storage\StorageTest; use Friendica\Test\src\Core\Storage\StorageTest;
class SampleStorageTest extends StorageTest class SampleStorageTest extends StorageTest
{ {
@ -371,7 +371,7 @@ class SampleStorageTest extends StorageTest
} }
// Assertion for the option array you return for your new StorageClass // Assertion for the option array you return for your new StorageClass
protected function assertOption(IWritableStorage $storage) protected function assertOption(ICanWriteToStorage $storage)
{ {
$this->assertEquals([ $this->assertEquals([
'filename' => [ 'filename' => [

View file

@ -544,7 +544,7 @@ Called when a custom storage is used (e.g. webdav_storage)
Hook data: Hook data:
- **name** (input): the name of the used storage backend - **name** (input): the name of the used storage backend
- **data['storage']** (output): the storage instance to use (**must** implement `\Friendica\Model\Storage\IWritableStorage`) - **data['storage']** (output): the storage instance to use (**must** implement `\Friendica\Core\Storage\IWritableStorage`)
### storage_config ### storage_config
@ -552,7 +552,7 @@ Called when the admin of the node wants to configure a custom storage (e.g. webd
Hook data: Hook data:
- **name** (input): the name of the used storage backend - **name** (input): the name of the used storage backend
- **data['storage_config']** (output): the storage configuration instance to use (**must** implement `\Friendica\Model\Storage\IStorageConfiguration`) - **data['storage_config']** (output): the storage configuration instance to use (**must** implement `\Friendica\Core\Storage\Capability\IConfigureStorage`)
## Complete list of hook callbacks ## Complete list of hook callbacks

View file

@ -43,7 +43,7 @@ $a = \Friendica\DI::app();
$a->runFrontend( $a->runFrontend(
$dice->create(\Friendica\App\Module::class), $dice->create(\Friendica\App\Module::class),
$dice->create(\Friendica\App\Router::class), $dice->create(\Friendica\App\Router::class),
$dice->create(\Friendica\Core\PConfig\IPConfig::class), $dice->create(\Friendica\Core\PConfig\Capability\IManagePersonalConfigValues::class),
$dice->create(\Friendica\Security\Authentication::class), $dice->create(\Friendica\Security\Authentication::class),
$dice->create(\Friendica\App\Page::class), $dice->create(\Friendica\App\Page::class),
$start_time $start_time

View file

@ -22,7 +22,7 @@
use Friendica\App; use Friendica\App;
use Friendica\Content\ForumManager; use Friendica\Content\ForumManager;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Core\Cache\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;

View file

@ -25,12 +25,12 @@ use Exception;
use Friendica\App\Arguments; use Friendica\App\Arguments;
use Friendica\App\BaseURL; use Friendica\App\BaseURL;
use Friendica\App\Module; use Friendica\App\Module;
use Friendica\Factory\ConfigFactory; use Friendica\Core\Config\Factory\Config;
use Friendica\Module\Maintenance; use Friendica\Module\Maintenance;
use Friendica\Security\Authentication; use Friendica\Security\Authentication;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\PConfig\IPConfig; use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Core\Theme; use Friendica\Core\Theme;
@ -39,7 +39,6 @@ use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Module\Special\HTTPException as ModuleHTTPException; use Friendica\Module\Special\HTTPException as ModuleHTTPException;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Util\ConfigFileLoader;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPSignature; use Friendica\Util\HTTPSignature;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
@ -89,7 +88,7 @@ class App
private $currentMobileTheme; private $currentMobileTheme;
/** /**
* @var IConfig The config * @var IManageConfigValues The config
*/ */
private $config; private $config;
@ -124,7 +123,7 @@ class App
private $process; private $process;
/** /**
* @var IPConfig * @var IManagePersonalConfigValues
*/ */
private $pConfig; private $pConfig;
@ -306,18 +305,18 @@ class App
} }
/** /**
* @param Database $database The Friendica Database * @param Database $database The Friendica Database
* @param IConfig $config The Configuration * @param IManageConfigValues $config The Configuration
* @param App\Mode $mode The mode of this Friendica app * @param App\Mode $mode The mode of this Friendica app
* @param BaseURL $baseURL The full base URL of this Friendica app * @param BaseURL $baseURL The full base URL of this Friendica app
* @param LoggerInterface $logger The current app logger * @param LoggerInterface $logger The current app logger
* @param Profiler $profiler The profiler of this application * @param Profiler $profiler The profiler of this application
* @param L10n $l10n The translator instance * @param L10n $l10n The translator instance
* @param App\Arguments $args The Friendica Arguments of the call * @param App\Arguments $args The Friendica Arguments of the call
* @param Core\Process $process The process methods * @param Core\Process $process The process methods
* @param IPConfig $pConfig Personal configuration * @param IManagePersonalConfigValues $pConfig Personal configuration
*/ */
public function __construct(Database $database, IConfig $config, App\Mode $mode, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, L10n $l10n, Arguments $args, Core\Process $process, IPConfig $pConfig) public function __construct(Database $database, IManageConfigValues $config, App\Mode $mode, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, L10n $l10n, Arguments $args, Core\Process $process, IManagePersonalConfigValues $pConfig)
{ {
$this->database = $database; $this->database = $database;
$this->config = $config; $this->config = $config;
@ -358,7 +357,7 @@ class App
$this->profiler->update($this->config); $this->profiler->update($this->config);
Core\Hook::loadHooks(); Core\Hook::loadHooks();
$loader = (new ConfigFactory())->createConfigFileLoader($this->getBasePath(), $_SERVER); $loader = (new Config())->createConfigFileLoader($this->getBasePath(), $_SERVER);
Core\Hook::callAll('load_config', $loader); Core\Hook::callAll('load_config', $loader);
} }
@ -553,16 +552,16 @@ class App
* *
* This probably should change to limit the size of this monster method. * This probably should change to limit the size of this monster method.
* *
* @param App\Module $module The determined module * @param App\Module $module The determined module
* @param App\Router $router * @param App\Router $router
* @param IPConfig $pconfig * @param IManagePersonalConfigValues $pconfig
* @param Authentication $auth The Authentication backend of the node * @param Authentication $auth The Authentication backend of the node
* @param App\Page $page The Friendica page printing container * @param App\Page $page The Friendica page printing container
* *
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
*/ */
public function runFrontend(App\Module $module, App\Router $router, IPConfig $pconfig, Authentication $auth, App\Page $page, float $start_time) public function runFrontend(App\Module $module, App\Router $router, IManagePersonalConfigValues $pconfig, Authentication $auth, App\Page $page, float $start_time)
{ {
$this->profiler->set($start_time, 'start'); $this->profiler->set($start_time, 'start');
$this->profiler->set(microtime(true), 'classinit'); $this->profiler->set(microtime(true), 'classinit');

View file

@ -21,7 +21,7 @@
namespace Friendica\App; namespace Friendica\App;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Util\Network; use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
@ -56,7 +56,7 @@ class BaseURL
/** /**
* The Friendica Config * The Friendica Config
* *
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
@ -272,10 +272,10 @@ class BaseURL
} }
/** /**
* @param IConfig $config The Friendica IConfiguration * @param IManageConfigValues $config The Friendica IConfiguration
* @param array $server The $_SERVER array * @param array $server The $_SERVER array
*/ */
public function __construct(IConfig $config, array $server) public function __construct(IManageConfigValues $config, array $server)
{ {
$this->config = $config; $this->config = $config;
$this->server = $server; $this->server = $server;

View file

@ -22,7 +22,7 @@
namespace Friendica\App; namespace Friendica\App;
use Detection\MobileDetect; use Detection\MobileDetect;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Util\BasePath; use Friendica\Util\BasePath;

View file

@ -24,6 +24,7 @@ namespace Friendica\App;
use Friendica\App; use Friendica\App;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Core; use Friendica\Core;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\LegacyModule; use Friendica\LegacyModule;
use Friendica\Module\Home; use Friendica\Module\Home;
use Friendica\Module\HTTPException\MethodNotAllowed; use Friendica\Module\HTTPException\MethodNotAllowed;
@ -169,15 +170,15 @@ class Module
/** /**
* Determine the class of the current module * Determine the class of the current module
* *
* @param Arguments $args The Friendica execution arguments * @param Arguments $args The Friendica execution arguments
* @param Router $router The Friendica routing instance * @param Router $router The Friendica routing instance
* @param Core\Config\IConfig $config The Friendica Configuration * @param IManageConfigValues $config The Friendica Configuration
* *
* @return Module The determined module of this call * @return Module The determined module of this call
* *
* @throws \Exception * @throws \Exception
*/ */
public function determineClass(Arguments $args, Router $router, Core\Config\IConfig $config) public function determineClass(Arguments $args, Router $router, IManageConfigValues $config)
{ {
$printNotAllowedAddon = false; $printNotAllowedAddon = false;

View file

@ -26,8 +26,8 @@ use DOMDocument;
use DOMXPath; use DOMXPath;
use Friendica\App; use Friendica\App;
use Friendica\Content\Nav; use Friendica\Content\Nav;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\PConfig\IPConfig; use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
@ -190,15 +190,15 @@ class Page implements ArrayAccess
* - Infinite scroll data * - Infinite scroll data
* - head.tpl template * - head.tpl template
* *
* @param App $app The Friendica App instance * @param App $app The Friendica App instance
* @param Module $module The loaded Friendica module * @param Module $module The loaded Friendica module
* @param L10n $l10n The l10n language instance * @param L10n $l10n The l10n language instance
* @param IConfig $config The Friendica configuration * @param IManageConfigValues $config The Friendica configuration
* @param IPConfig $pConfig The Friendica personal configuration (for user) * @param IManagePersonalConfigValues $pConfig The Friendica personal configuration (for user)
* *
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
private function initHead(App $app, Module $module, L10n $l10n, IConfig $config, IPConfig $pConfig) private function initHead(App $app, Module $module, L10n $l10n, IManageConfigValues $config, IManagePersonalConfigValues $pConfig)
{ {
$interval = ((local_user()) ? $pConfig->get(local_user(), 'system', 'update_interval') : 40000); $interval = ((local_user()) ? $pConfig->get(local_user(), 'system', 'update_interval') : 40000);
@ -367,17 +367,17 @@ class Page implements ArrayAccess
/** /**
* Executes the creation of the current page and prints it to the screen * Executes the creation of the current page and prints it to the screen
* *
* @param App $app The Friendica App * @param App $app The Friendica App
* @param BaseURL $baseURL The Friendica Base URL * @param BaseURL $baseURL The Friendica Base URL
* @param Mode $mode The current node mode * @param Mode $mode The current node mode
* @param Module $module The loaded Friendica module * @param Module $module The loaded Friendica module
* @param L10n $l10n The l10n language class * @param L10n $l10n The l10n language class
* @param IConfig $config The Configuration of this node * @param IManageConfigValues $config The Configuration of this node
* @param IPConfig $pconfig The personal/user configuration * @param IManagePersonalConfigValues $pconfig The personal/user configuration
* *
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public function run(App $app, BaseURL $baseURL, Mode $mode, Module $module, L10n $l10n, Profiler $profiler, IConfig $config, IPConfig $pconfig) public function run(App $app, BaseURL $baseURL, Mode $mode, Module $module, L10n $l10n, Profiler $profiler, IManageConfigValues $config, IManagePersonalConfigValues $pconfig)
{ {
$moduleName = $module->getName(); $moduleName = $module->getName();

View file

@ -26,11 +26,11 @@ use FastRoute\DataGenerator\GroupCountBased;
use FastRoute\Dispatcher; use FastRoute\Dispatcher;
use FastRoute\RouteCollector; use FastRoute\RouteCollector;
use FastRoute\RouteParser\Std; use FastRoute\RouteParser\Std;
use Friendica\Core\Cache\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\ICache; use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\Lock\ILock; use Friendica\Core\Lock\Capability\ICanLock;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
/** /**
@ -77,10 +77,10 @@ class Router
/** @var L10n */ /** @var L10n */
private $l10n; private $l10n;
/** @var ICache */ /** @var ICanCache */
private $cache; private $cache;
/** @var ILock */ /** @var ICanLock */
private $lock; private $lock;
/** @var string */ /** @var string */
@ -90,10 +90,10 @@ class Router
* @param array $server The $_SERVER variable * @param array $server The $_SERVER variable
* @param string $baseRoutesFilepath The path to a base routes file to leverage cache, can be empty * @param string $baseRoutesFilepath The path to a base routes file to leverage cache, can be empty
* @param L10n $l10n * @param L10n $l10n
* @param ICache $cache * @param ICanCache $cache
* @param RouteCollector|null $routeCollector * @param RouteCollector|null $routeCollector
*/ */
public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICache $cache, ILock $lock, RouteCollector $routeCollector = null) public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICanCache $cache, ICanLock $lock, RouteCollector $routeCollector = null)
{ {
$this->baseRoutesFilepath = $baseRoutesFilepath; $this->baseRoutesFilepath = $baseRoutesFilepath;
$this->l10n = $l10n; $this->l10n = $l10n;

View file

@ -24,8 +24,8 @@ namespace Friendica\Console;
use Asika\SimpleConsole\Console; use Asika\SimpleConsole\Console;
use Friendica\App; use Friendica\App;
use Friendica\App\BaseURL; use Friendica\App\BaseURL;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\Installer; use Friendica\Core\Installer;
use Friendica\Core\Theme; use Friendica\Core\Theme;
use Friendica\Database\Database; use Friendica\Database\Database;
@ -36,9 +36,9 @@ class AutomaticInstallation extends Console
{ {
/** @var App\Mode */ /** @var App\Mode */
private $appMode; private $appMode;
/** @var Cache */ /** @var \Friendica\Core\Config\ValueObject\Cache */
private $configCache; private $configCache;
/** @var IConfig */ /** @var IManageConfigValues */
private $config; private $config;
/** @var Database */ /** @var Database */
private $dba; private $dba;
@ -98,7 +98,7 @@ Examples
HELP; HELP;
} }
public function __construct(App\Mode $appMode, Cache $configCache, IConfig $config, Database $dba, array $argv = null) public function __construct(App\Mode $appMode, Cache $configCache, IManageConfigValues $config, Database $dba, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);
@ -252,8 +252,8 @@ HELP;
} }
/** /**
* @param Installer $installer The Installer instance * @param Installer $installer The Installer instance
* @param Cache $configCache The config cache * @param \Friendica\Core\Config\ValueObject\Cache $configCache The config cache
* *
* @return bool true if checks were successfully, otherwise false * @return bool true if checks were successfully, otherwise false
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException

View file

@ -23,8 +23,8 @@ namespace Friendica\Console;
use Asika\SimpleConsole\CommandArgsException; use Asika\SimpleConsole\CommandArgsException;
use Friendica\App; use Friendica\App;
use Friendica\Core\Cache\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\ICache; use Friendica\Core\Cache\Capability\ICanCache;
use RuntimeException; use RuntimeException;
/** /**
@ -44,7 +44,7 @@ class Cache extends \Asika\SimpleConsole\Console
private $appMode; private $appMode;
/** /**
* @var ICache * @var ICanCache
*/ */
private $cache; private $cache;
@ -82,7 +82,7 @@ HELP;
return $help; return $help;
} }
public function __construct(App\Mode $appMode, ICache $cache, array $argv = null) public function __construct(App\Mode $appMode, ICanCache $cache, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -23,7 +23,7 @@ namespace Friendica\Console;
use Asika\SimpleConsole\CommandArgsException; use Asika\SimpleConsole\CommandArgsException;
use Friendica\App; use Friendica\App;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use RuntimeException; use RuntimeException;
/** /**
@ -56,7 +56,7 @@ class Config extends \Asika\SimpleConsole\Console
*/ */
private $appMode; private $appMode;
/** /**
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
@ -94,7 +94,7 @@ HELP;
return $help; return $help;
} }
public function __construct(App\Mode $appMode, IConfig $config, array $argv = null) public function __construct(App\Mode $appMode, IManageConfigValues $config, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -21,7 +21,7 @@
namespace Friendica\Console; namespace Friendica\Console;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\Update; use Friendica\Core\Update;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Database\DBStructure; use Friendica\Database\DBStructure;

View file

@ -23,7 +23,7 @@ namespace Friendica\Console;
use Asika\SimpleConsole\CommandArgsException; use Asika\SimpleConsole\CommandArgsException;
use Friendica\App; use Friendica\App;
use Friendica\Core\Lock\ILock; use Friendica\Core\Lock\Capability\ICanLock;
use RuntimeException; use RuntimeException;
/** /**
@ -42,7 +42,7 @@ class Lock extends \Asika\SimpleConsole\Console
private $appMode; private $appMode;
/** /**
* @var ILock * @var ICanLock
*/ */
private $lock; private $lock;
@ -76,7 +76,7 @@ HELP;
return $help; return $help;
} }
public function __construct(App\Mode $appMode, ILock $lock, array $argv = null) public function __construct(App\Mode $appMode, ICanLock $lock, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -22,7 +22,7 @@
namespace Friendica\Console; namespace Friendica\Console;
use Friendica\App; use Friendica\App;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
/** /**
* Sets maintenance mode for this node * Sets maintenance mode for this node
@ -36,7 +36,7 @@ class Maintenance extends \Asika\SimpleConsole\Console
*/ */
private $appMode; private $appMode;
/** /**
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
@ -69,7 +69,7 @@ HELP;
return $help; return $help;
} }
public function __construct(App\Mode $appMode, IConfig $config, $argv = null) public function __construct(App\Mode $appMode, IManageConfigValues $config, $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -22,7 +22,7 @@
namespace Friendica\Console; namespace Friendica\Console;
use Friendica\App; use Friendica\App;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\Update; use Friendica\Core\Update;
@ -38,7 +38,7 @@ class PostUpdate extends \Asika\SimpleConsole\Console
*/ */
private $appMode; private $appMode;
/** /**
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
/** /**
@ -60,7 +60,7 @@ HELP;
return $help; return $help;
} }
public function __construct(App\Mode $appMode, IConfig $config, L10n $l10n, array $argv = null) public function __construct(App\Mode $appMode, IManageConfigValues $config, L10n $l10n, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -24,7 +24,7 @@ namespace Friendica\Console;
use Asika\SimpleConsole\CommandArgsException; use Asika\SimpleConsole\CommandArgsException;
use Asika\SimpleConsole\Console; use Asika\SimpleConsole\Console;
use Console_Table; use Console_Table;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
/** /**
* Manage blocked servers * Manage blocked servers
@ -39,7 +39,7 @@ class ServerBlock extends Console
protected $helpOptions = ['h', 'help', '?']; protected $helpOptions = ['h', 'help', '?'];
/** /**
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
@ -72,7 +72,7 @@ HELP;
return $help; return $help;
} }
public function __construct(IConfig $config, $argv = null) public function __construct(IManageConfigValues $config, $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);
@ -105,9 +105,9 @@ HELP;
* Exports the list of blocked domains including the reason for the * Exports the list of blocked domains including the reason for the
* block to a CSV file. * block to a CSV file.
* *
* @param IConfig $config * @param IManageConfigValues $config
*/ */
private function exportBlockedServers(IConfig $config) private function exportBlockedServers(IManageConfigValues $config)
{ {
$filename = $this->getArgument(1); $filename = $this->getArgument(1);
$blocklist = $config->get('system', 'blocklist', []); $blocklist = $config->get('system', 'blocklist', []);
@ -123,9 +123,9 @@ HELP;
* Imports a list of domains and a reason for the block from a CSV * Imports a list of domains and a reason for the block from a CSV
* file, e.g. created with the export function. * file, e.g. created with the export function.
* *
* @param IConfig $config * @param IManageConfigValues $config
*/ */
private function importBlockedServers(IConfig $config) private function importBlockedServers(IManageConfigValues $config)
{ {
$filename = $this->getArgument(1); $filename = $this->getArgument(1);
$currBlockList = $config->get('system', 'blocklist', []); $currBlockList = $config->get('system', 'blocklist', []);
@ -167,9 +167,9 @@ HELP;
/** /**
* Prints the whole list of blocked domains including the reason * Prints the whole list of blocked domains including the reason
* *
* @param IConfig $config * @param IManageConfigValues $config
*/ */
private function printBlockedServers(IConfig $config) private function printBlockedServers(IManageConfigValues $config)
{ {
$table = new Console_Table(); $table = new Console_Table();
$table->setHeaders(['Domain', 'Reason']); $table->setHeaders(['Domain', 'Reason']);
@ -183,11 +183,11 @@ HELP;
/** /**
* Adds a server to the blocked list * Adds a server to the blocked list
* *
* @param IConfig $config * @param IManageConfigValues $config
* *
* @return int The return code (0 = success, 1 = failed) * @return int The return code (0 = success, 1 = failed)
*/ */
private function addBlockedServer(IConfig $config) private function addBlockedServer(IManageConfigValues $config)
{ {
if (count($this->args) < 2 || count($this->args) > 3) { if (count($this->args) < 2 || count($this->args) > 3) {
throw new CommandArgsException('Add needs a domain and optional a reason.'); throw new CommandArgsException('Add needs a domain and optional a reason.');
@ -235,11 +235,11 @@ HELP;
/** /**
* Removes a server from the blocked list * Removes a server from the blocked list
* *
* @param IConfig $config * @param IManageConfigValues $config
* *
* @return int The return code (0 = success, 1 = failed) * @return int The return code (0 = success, 1 = failed)
*/ */
private function removeBlockedServer(IConfig $config) private function removeBlockedServer(IManageConfigValues $config)
{ {
if (count($this->args) !== 2) { if (count($this->args) !== 2) {
throw new CommandArgsException('Remove needs a second parameter.'); throw new CommandArgsException('Remove needs a second parameter.');

View file

@ -22,9 +22,9 @@
namespace Friendica\Console; namespace Friendica\Console;
use Asika\SimpleConsole\CommandArgsException; use Asika\SimpleConsole\CommandArgsException;
use Friendica\Core\StorageManager; use Friendica\Core\Storage\Repository\StorageManager;
use Friendica\Model\Storage\ReferenceStorageException; use Friendica\Core\Storage\Exception\ReferenceStorageException;
use Friendica\Model\Storage\StorageException; use Friendica\Core\Storage\Exception\StorageException;
/** /**
* tool to manage storage backend and stored data from CLI * tool to manage storage backend and stored data from CLI

View file

@ -21,7 +21,7 @@
namespace Friendica\Console; namespace Friendica\Console;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
/** /**
* Tired of chasing typos and finding them after a commit. * Tired of chasing typos and finding them after a commit.
@ -32,7 +32,7 @@ class Typo extends \Asika\SimpleConsole\Console
protected $helpOptions = ['h', 'help', '?']; protected $helpOptions = ['h', 'help', '?'];
/** /**
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
@ -53,7 +53,7 @@ HELP;
return $help; return $help;
} }
public function __construct(IConfig $config, array $argv = null) public function __construct(IManageConfigValues $config, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -25,7 +25,7 @@ use Console_Table;
use Friendica\App; use Friendica\App;
use Friendica\Content\Pager; use Friendica\Content\Pager;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\PConfig\IPConfig; use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Model\Register; use Friendica\Model\Register;
use Friendica\Model\User as UserModel; use Friendica\Model\User as UserModel;
use Friendica\Util\Temporal; use Friendica\Util\Temporal;
@ -48,7 +48,7 @@ class User extends \Asika\SimpleConsole\Console
*/ */
private $l10n; private $l10n;
/** /**
* @var IPConfig * @var IManagePersonalConfigValues
*/ */
private $pConfig; private $pConfig;
@ -88,7 +88,7 @@ HELP;
return $help; return $help;
} }
public function __construct(App\Mode $appMode, L10n $l10n, IPConfig $pConfig, array $argv = null) public function __construct(App\Mode $appMode, L10n $l10n, IManagePersonalConfigValues $pConfig, array $argv = null)
{ {
parent::__construct($argv); parent::__construct($argv);

View file

@ -26,10 +26,10 @@ use Friendica\App\Arguments;
use Friendica\App\BaseURL; use Friendica\App\BaseURL;
use Friendica\BaseModule; use Friendica\BaseModule;
use Friendica\Core\ACL; use Friendica\Core\ACL;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Core\PConfig\IPConfig; use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Core\Session; use Friendica\Core\Session;
@ -66,11 +66,11 @@ class Conversation
private $item; private $item;
/** @var App\Arguments */ /** @var App\Arguments */
private $args; private $args;
/** @var IPConfig */ /** @var IManagePersonalConfigValues */
private $pConfig; private $pConfig;
/** @var BaseURL */ /** @var BaseURL */
private $baseURL; private $baseURL;
/** @var IConfig */ /** @var IManageConfigValues */
private $config; private $config;
/** @var App */ /** @var App */
private $app; private $app;
@ -79,7 +79,7 @@ class Conversation
/** @var App\Mode */ /** @var App\Mode */
private $mode; private $mode;
public function __construct(LoggerInterface $logger, Profiler $profiler, Activity $activity, L10n $l10n, Item $item, Arguments $args, BaseURL $baseURL, IConfig $config, IPConfig $pConfig, App\Page $page, App\Mode $mode, App $app) public function __construct(LoggerInterface $logger, Profiler $profiler, Activity $activity, L10n $l10n, Item $item, Arguments $args, BaseURL $baseURL, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, App\Page $page, App\Mode $mode, App $app)
{ {
$this->activity = $activity; $this->activity = $activity;
$this->item = $item; $this->item = $item;
@ -629,7 +629,7 @@ class Conversation
$body_html = ItemModel::prepareBody($item, true, $preview); $body_html = ItemModel::prepareBody($item, true, $preview);
list($categories, $folders) = $this->item->determineCategoriesTerms($item, local_user()); [$categories, $folders] = $this->item->determineCategoriesTerms($item, local_user());
if (!empty($item['content-warning']) && $this->pConfig->get(local_user(), 'system', 'disable_cw', false)) { if (!empty($item['content-warning']) && $this->pConfig->get(local_user(), 'system', 'disable_cw', false)) {
$title = ucfirst($item['content-warning']); $title = ucfirst($item['content-warning']);

View file

@ -190,7 +190,7 @@ class Nav
$nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Status'), '', DI::l10n()->t('Your posts and conversations')]; $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Status'), '', DI::l10n()->t('Your posts and conversations')];
$nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')]; $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')];
$nav['usermenu'][] = ['photos/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')]; $nav['usermenu'][] = ['photos/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')];
$nav['usermenu'][] = ['media/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Media'), '', DI::l10n()->t('Your postings with media')]; $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/media', DI::l10n()->t('Media'), '', DI::l10n()->t('Your postings with media')];
$nav['usermenu'][] = ['events/', DI::l10n()->t('Events'), '', DI::l10n()->t('Your events')]; $nav['usermenu'][] = ['events/', DI::l10n()->t('Events'), '', DI::l10n()->t('Your events')];
$nav['usermenu'][] = ['notes/', DI::l10n()->t('Personal notes'), '', DI::l10n()->t('Your personal notes')]; $nav['usermenu'][] = ['notes/', DI::l10n()->t('Personal notes'), '', DI::l10n()->t('Your personal notes')];

View file

@ -26,7 +26,7 @@ use DOMNode;
use DOMText; use DOMText;
use DOMXPath; use DOMXPath;
use Exception; use Exception;
use Friendica\Core\Cache\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\Database; use Friendica\Database\Database;

View file

@ -39,7 +39,7 @@ use Friendica\Model\Event;
use Friendica\Model\Photo; use Friendica\Model\Photo;
use Friendica\Model\Post; use Friendica\Model\Post;
use Friendica\Model\Tag; use Friendica\Model\Tag;
use Friendica\Network\HTTPClientOptions; use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Object\Image; use Friendica\Object\Image;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\Images; use Friendica\Util\Images;
@ -1201,7 +1201,7 @@ class BBCode
$text = DI::cache()->get($cache_key); $text = DI::cache()->get($cache_key);
if (is_null($text)) { if (is_null($text)) {
$curlResult = DI::httpClient()->head($match[1], [HTTPClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]); $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]);
if ($curlResult->isSuccess()) { if ($curlResult->isSuccess()) {
$mimetype = $curlResult->getHeader('Content-Type')[0] ?? ''; $mimetype = $curlResult->getHeader('Content-Type')[0] ?? '';
} else { } else {
@ -1272,7 +1272,7 @@ class BBCode
return $text; return $text;
} }
$curlResult = DI::httpClient()->head($match[1], [HTTPClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]); $curlResult = DI::httpClient()->head($match[1], [HttpClientOptions::TIMEOUT => DI::config()->get('system', 'xrd_timeout')]);
if ($curlResult->isSuccess()) { if ($curlResult->isSuccess()) {
$mimetype = $curlResult->getHeader('Content-Type')[0] ?? ''; $mimetype = $curlResult->getHeader('Content-Type')[0] ?? '';
} else { } else {

View file

@ -22,7 +22,7 @@
namespace Friendica\Content; namespace Friendica\Content;
use Friendica\Core\Addon; use Friendica\Core\Addon;
use Friendica\Core\Cache\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;

View file

@ -19,21 +19,24 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Capability;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\Exception\CachePersistenceException;
/** /**
* Cache Interface * Interface for caches
*/ */
interface ICache interface ICanCache
{ {
/** /**
* Lists all cache keys * Lists all cache keys
* *
* @param string prefix optional a prefix to search * @param string|null prefix optional a prefix to search
* *
* @return array Empty if it isn't supported by the cache driver * @return array Empty if it isn't supported by the cache driver
*/ */
public function getAllKeys($prefix = null); public function getAllKeys(?string $prefix = null): array;
/** /**
* Fetches cached data according to the key * Fetches cached data according to the key
@ -41,41 +44,50 @@ interface ICache
* @param string $key The key to the cached data * @param string $key The key to the cached data
* *
* @return mixed Cached $value or "null" if not found * @return mixed Cached $value or "null" if not found
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function get($key); public function get(string $key);
/** /**
* Stores data in the cache identified by the key. The input $value can have multiple formats. * Stores data in the cache identified by the key. The input $value can have multiple formats.
* *
* @param string $key The cache key * @param string $key The cache key
* @param mixed $value The value to store * @param mixed $value The value to store
* @param integer $ttl The cache lifespan, must be one of the Cache constants * @param integer $ttl The cache lifespan, must be one of the Cache constants
* *
* @return bool * @return bool
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES); public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool;
/** /**
* Delete a key from the cache * Delete a key from the cache
* *
* @param string $key The cache key * @param string $key The cache key
* *
* @return bool * @return bool
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function delete($key); public function delete(string $key): bool;
/** /**
* Remove outdated data from the cache * Remove outdated data from the cache
* @param boolean $outdated just remove outdated values *
* @param boolean $outdated just remove outdated values
* *
* @return bool * @return bool
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function clear($outdated = true); public function clear(bool $outdated = true): bool;
/** /**
* Returns the name of the current cache * Returns the name of the current cache
* *
* @return string * @return string
*/ */
public function getName(); public function getName(): string;
} }

View file

@ -19,41 +19,52 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Capability;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\Exception\CachePersistenceException;
/** /**
* This interface defines methods for Memory-Caches only * This interface defines methods for Memory-Caches only
*/ */
interface IMemoryCache extends ICache interface ICanCacheInMemory extends ICanCache
{ {
/** /**
* Sets a value if it's not already stored * Sets a value if it's not already stored
* *
* @param string $key The cache key * @param string $key The cache key
* @param mixed $value The old value we know from the cache * @param mixed $value The old value we know from the cache
* @param int $ttl The cache lifespan, must be one of the Cache constants * @param int $ttl The cache lifespan, must be one of the Cache constants
*
* @return bool * @return bool
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES); public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool;
/** /**
* Compares if the old value is set and sets the new value * Compares if the old value is set and sets the new value
* *
* @param string $key The cache key * @param string $key The cache key
* @param mixed $oldValue The old value we know from the cache * @param mixed $oldValue The old value we know from the cache
* @param mixed $newValue The new value we want to set * @param mixed $newValue The new value we want to set
* @param int $ttl The cache lifespan, must be one of the Cache constants * @param int $ttl The cache lifespan, must be one of the Cache constants
* *
* @return bool * @return bool
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES); public function compareSet(string $key, $oldValue, $newValue, int $ttl = Duration::FIVE_MINUTES): bool;
/** /**
* Compares if the old value is set and removes it * Compares if the old value is set and removes it
* *
* @param string $key The cache key * @param string $key The cache key
* @param mixed $value The old value we know and want to delete * @param mixed $value The old value we know and want to delete
*
* @return bool * @return bool
*
* @throws CachePersistenceException In case the underlying cache driver has errors during persistence
*/ */
public function compareDelete($key, $value); public function compareDelete(string $key, $value): bool;
} }

View file

@ -1,138 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Cache;
use Friendica\Database\Database;
use Friendica\Util\DateTimeFormat;
use Friendica\Core\BaseCache;
/**
* Database Cache
*/
class DatabaseCache extends BaseCache implements ICache
{
/**
* @var Database
*/
private $dba;
public function __construct(string $hostname, Database $dba)
{
parent::__construct($hostname);
$this->dba = $dba;
}
/**
* (@inheritdoc)
*/
public function getAllKeys($prefix = null)
{
if (empty($prefix)) {
$where = ['`expires` >= ?', DateTimeFormat::utcNow()];
} else {
$where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
}
$stmt = $this->dba->select('cache', ['k'], $where);
$keys = [];
while ($key = $this->dba->fetch($stmt)) {
array_push($keys, $key['k']);
}
$this->dba->close($stmt);
return $keys;
}
/**
* (@inheritdoc)
*/
public function get($key)
{
$cache = $this->dba->selectFirst('cache', ['v'], ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, DateTimeFormat::utcNow()]);
if ($this->dba->isResult($cache)) {
$cached = $cache['v'];
$value = @unserialize($cached);
// Only return a value if the serialized value is valid.
// We also check if the db entry is a serialized
// boolean 'false' value (which we want to return).
if ($cached === serialize(false) || $value !== false) {
return $value;
}
}
return null;
}
/**
* (@inheritdoc)
*/
public function set($key, $value, $ttl = Duration::FIVE_MINUTES)
{
if ($ttl > 0) {
$fields = [
'v' => serialize($value),
'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds'),
'updated' => DateTimeFormat::utcNow()
];
} else {
$fields = [
'v' => serialize($value),
'expires' => -1,
'updated' => DateTimeFormat::utcNow()
];
}
return $this->dba->update('cache', $fields, ['k' => $key], true);
}
/**
* (@inheritdoc)
*/
public function delete($key)
{
return $this->dba->delete('cache', ['k' => $key]);
}
/**
* (@inheritdoc)
*/
public function clear($outdated = true)
{
if ($outdated) {
return $this->dba->delete('cache', ['`expires` < NOW()']);
} else {
return $this->dba->delete('cache', ['`k` IS NOT NULL ']);
}
}
/**
* {@inheritDoc}
*/
public function getName()
{
return Type::DATABASE;
}
}

View file

@ -19,7 +19,7 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Enum;
/** /**
* Enumeration for cache durations * Enumeration for cache durations

View file

@ -19,7 +19,7 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Enum;
/** /**
* Enumeration for cache types * Enumeration for cache types

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Cache\Exception;
use Throwable;
class CachePersistenceException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Cache\Exception;
use Throwable;
class InvalidCacheDriverException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -19,12 +19,15 @@
* *
*/ */
namespace Friendica\Factory; namespace Friendica\Core\Cache\Factory;
use Friendica\App\BaseURL; use Friendica\App\BaseURL;
use Friendica\Core\Cache; use Friendica\Core\Cache\Enum;
use Friendica\Core\Cache\ICache; use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Config\IConfig; use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
use Friendica\Core\Cache\Type;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -36,15 +39,15 @@ use Psr\Log\LoggerInterface;
* *
* A basic class to generate a CacheDriver * A basic class to generate a CacheDriver
*/ */
class CacheFactory class Cache
{ {
/** /**
* @var string The default cache if nothing set * @var string The default cache if nothing set
*/ */
const DEFAULT_TYPE = Cache\Type::DATABASE; const DEFAULT_TYPE = Enum\Type::DATABASE;
/** /**
* @var IConfig The IConfiguration to read parameters out of the config * @var IManageConfigValues The IConfiguration to read parameters out of the config
*/ */
private $config; private $config;
@ -68,7 +71,7 @@ class CacheFactory
*/ */
private $logger; private $logger;
public function __construct(BaseURL $baseURL, IConfig $config, Database $dba, Profiler $profiler, LoggerInterface $logger) public function __construct(BaseURL $baseURL, IManageConfigValues $config, Database $dba, Profiler $profiler, LoggerInterface $logger)
{ {
$this->hostname = $baseURL->getHostname(); $this->hostname = $baseURL->getHostname();
$this->config = $config; $this->config = $config;
@ -80,39 +83,41 @@ class CacheFactory
/** /**
* This method creates a CacheDriver for the given cache driver name * This method creates a CacheDriver for the given cache driver name
* *
* @param string $type The cache type to create (default is per config) * @param string|null $type The cache type to create (default is per config)
* *
* @return ICache The instance of the CacheDriver * @return ICanCache The instance of the CacheDriver
* @throws \Exception The exception if something went wrong during the CacheDriver creation *
* @throws InvalidCacheDriverException In case the underlying cache driver isn't valid or not configured properly
* @throws CachePersistenceException In case the underlying cache has errors during persistence
*/ */
public function create(string $type = null) public function create(string $type = null): ICanCache
{ {
if (empty($type)) { if (empty($type)) {
$type = $this->config->get('system', 'cache_driver', self::DEFAULT_TYPE); $type = $this->config->get('system', 'cache_driver', self::DEFAULT_TYPE);
} }
switch ($type) { switch ($type) {
case Cache\Type::MEMCACHE: case Enum\Type::MEMCACHE:
$cache = new Cache\MemcacheCache($this->hostname, $this->config); $cache = new Type\MemcacheCache($this->hostname, $this->config);
break; break;
case Cache\Type::MEMCACHED: case Enum\Type::MEMCACHED:
$cache = new Cache\MemcachedCache($this->hostname, $this->config, $this->logger); $cache = new Type\MemcachedCache($this->hostname, $this->config, $this->logger);
break; break;
case Cache\Type::REDIS: case Enum\Type::REDIS:
$cache = new Cache\RedisCache($this->hostname, $this->config); $cache = new Type\RedisCache($this->hostname, $this->config);
break; break;
case Cache\Type::APCU: case Enum\Type::APCU:
$cache = new Cache\APCuCache($this->hostname); $cache = new Type\APCuCache($this->hostname);
break; break;
default: default:
$cache = new Cache\DatabaseCache($this->hostname, $this->dba); $cache = new Type\DatabaseCache($this->hostname, $this->dba);
} }
$profiling = $this->config->get('system', 'profiling', false); $profiling = $this->config->get('system', 'profiling', false);
// In case profiling is enabled, wrap the ProfilerCache around the current cache // In case profiling is enabled, wrap the ProfilerCache around the current cache
if (isset($profiling) && $profiling !== false) { if (isset($profiling) && $profiling !== false) {
return new Cache\ProfilerCache($cache, $this->profiler); return new Type\ProfilerCacheDecorator($cache, $this->profiler);
} else { } else {
return $cache; return $cache;
} }

View file

@ -19,26 +19,30 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Exception; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\BaseCache; use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Cache\Enum\Type;
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
/** /**
* APCu Cache. * APCu Cache.
*/ */
class APCuCache extends BaseCache implements IMemoryCache class APCuCache extends AbstractCache implements ICanCacheInMemory
{ {
use TraitCompareSet; use CompareSetTrait;
use TraitCompareDelete; use CompareDeleteTrait;
/** /**
* @throws Exception * @param string $hostname
*
* @throws InvalidCacheDriverException
*/ */
public function __construct(string $hostname) public function __construct(string $hostname)
{ {
if (!self::isAvailable()) { if (!self::isAvailable()) {
throw new Exception('APCu is not available.'); throw new InvalidCacheDriverException('APCu is not available.');
} }
parent::__construct($hostname); parent::__construct($hostname);
@ -47,9 +51,9 @@ class APCuCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function getAllKeys($prefix = null) public function getAllKeys(?string $prefix = null): array
{ {
$ns = $this->getCacheKey($prefix); $ns = $this->getCacheKey($prefix ?? '');
$ns = preg_quote($ns, '/'); $ns = preg_quote($ns, '/');
if (class_exists('\APCIterator')) { if (class_exists('\APCIterator')) {
@ -69,12 +73,11 @@ class APCuCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function get($key) public function get(string $key)
{ {
$return = null; $cacheKey = $this->getCacheKey($key);
$cachekey = $this->getCacheKey($key);
$cached = apcu_fetch($cachekey, $success); $cached = apcu_fetch($cacheKey, $success);
if (!$success) { if (!$success) {
return null; return null;
} }
@ -85,30 +88,30 @@ class APCuCache extends BaseCache implements IMemoryCache
// We also check if the db entry is a serialized // We also check if the db entry is a serialized
// boolean 'false' value (which we want to return). // boolean 'false' value (which we want to return).
if ($cached === serialize(false) || $value !== false) { if ($cached === serialize(false) || $value !== false) {
$return = $value; return $value;
} }
return $return; return null;
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES) public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$cached = serialize($value); $cached = serialize($value);
if ($ttl > 0) { if ($ttl > 0) {
return apcu_store( return apcu_store(
$cachekey, $cacheKey,
$cached, $cached,
$ttl $ttl
); );
} else { } else {
return apcu_store( return apcu_store(
$cachekey, $cacheKey,
$cached $cached
); );
} }
@ -117,16 +120,16 @@ class APCuCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function delete($key) public function delete(string $key): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
return apcu_delete($cachekey); return apcu_delete($cacheKey);
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function clear($outdated = true) public function clear(bool $outdated = true): bool
{ {
if ($outdated) { if ($outdated) {
return true; return true;
@ -147,15 +150,15 @@ class APCuCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES) public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$cached = serialize($value); $cached = serialize($value);
return apcu_add($cachekey, $cached); return apcu_add($cacheKey, $cached);
} }
public static function isAvailable() public static function isAvailable(): bool
{ {
if (!extension_loaded('apcu')) { if (!extension_loaded('apcu')) {
return false; return false;
@ -174,7 +177,7 @@ class APCuCache extends BaseCache implements IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getName() public function getName(): string
{ {
return Type::APCU; return Type::APCU;
} }

View file

@ -19,14 +19,14 @@
* *
*/ */
namespace Friendica\Core; namespace Friendica\Core\Cache\Type;
use Friendica\Core\Cache\ICache; use Friendica\Core\Cache\Capability\ICanCache;
/** /**
* Abstract class for common used functions * Abstract class for common used functions
*/ */
abstract class BaseCache implements ICache abstract class AbstractCache implements ICanCache
{ {
/** /**
* @var string The hostname * @var string The hostname
@ -42,9 +42,8 @@ abstract class BaseCache implements ICache
* Returns the prefix (to avoid namespace conflicts) * Returns the prefix (to avoid namespace conflicts)
* *
* @return string * @return string
* @throws \Exception
*/ */
protected function getPrefix() protected function getPrefix(): string
{ {
// We fetch with the hostname as key to avoid problems with other applications // We fetch with the hostname as key to avoid problems with other applications
return $this->hostName; return $this->hostName;
@ -52,19 +51,20 @@ abstract class BaseCache implements ICache
/** /**
* @param string $key The original key * @param string $key The original key
*
* @return string The cache key used for the cache * @return string The cache key used for the cache
* @throws \Exception
*/ */
protected function getCacheKey($key) protected function getCacheKey(string $key): string
{ {
return $this->getPrefix() . ":" . $key; return $this->getPrefix() . ":" . $key;
} }
/** /**
* @param array $keys A list of cached keys * @param string[] $keys A list of cached keys
* @return array A list of original keys *
* @return string[] A list of original keys
*/ */
protected function getOriginalKeys($keys) protected function getOriginalKeys(array $keys): array
{ {
if (empty($keys)) { if (empty($keys)) {
return []; return [];
@ -84,12 +84,12 @@ abstract class BaseCache implements ICache
* Filters the keys of an array with a given prefix * Filters the keys of an array with a given prefix
* Returns the filtered keys as an new array * Returns the filtered keys as an new array
* *
* @param array $keys The keys, which should get filtered * @param string[] $keys The keys, which should get filtered
* @param string|null $prefix The prefix (if null, all keys will get returned) * @param string|null $prefix The prefix (if null, all keys will get returned)
* *
* @return array The filtered array with just the keys * @return string[] The filtered array with just the keys
*/ */
protected function filterArrayKeysByPrefix(array $keys, string $prefix = null) protected function filterArrayKeysByPrefix(array $keys, string $prefix = null): array
{ {
if (empty($prefix)) { if (empty($prefix)) {
return $keys; return $keys;

View file

@ -19,24 +19,25 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Friendica\Core\BaseCache; use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Cache\Enum;
/** /**
* Implementation of the IMemoryCache mainly for testing purpose * Implementation of the IMemoryCache mainly for testing purpose
*/ */
class ArrayCache extends BaseCache implements IMemoryCache class ArrayCache extends AbstractCache implements ICanCacheInMemory
{ {
use TraitCompareDelete; use CompareDeleteTrait;
/** @var array Array with the cached data */ /** @var array Array with the cached data */
protected $cachedData = array(); protected $cachedData = [];
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function getAllKeys($prefix = null) public function getAllKeys(?string $prefix = null): array
{ {
return $this->filterArrayKeysByPrefix(array_keys($this->cachedData), $prefix); return $this->filterArrayKeysByPrefix(array_keys($this->cachedData), $prefix);
} }
@ -44,7 +45,7 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function get($key) public function get(string $key)
{ {
if (isset($this->cachedData[$key])) { if (isset($this->cachedData[$key])) {
return $this->cachedData[$key]; return $this->cachedData[$key];
@ -55,7 +56,7 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES) public function set(string $key, $value, int $ttl = Enum\Duration::FIVE_MINUTES): bool
{ {
$this->cachedData[$key] = $value; $this->cachedData[$key] = $value;
return true; return true;
@ -64,7 +65,7 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function delete($key) public function delete(string $key): bool
{ {
unset($this->cachedData[$key]); unset($this->cachedData[$key]);
return true; return true;
@ -73,7 +74,7 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function clear($outdated = true) public function clear(bool $outdated = true): bool
{ {
// Array doesn't support TTL so just don't delete something // Array doesn't support TTL so just don't delete something
if ($outdated) { if ($outdated) {
@ -87,7 +88,7 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES) public function add(string $key, $value, int $ttl = Enum\Duration::FIVE_MINUTES): bool
{ {
if (isset($this->cachedData[$key])) { if (isset($this->cachedData[$key])) {
return false; return false;
@ -99,7 +100,7 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) public function compareSet(string $key, $oldValue, $newValue, int $ttl = Enum\Duration::FIVE_MINUTES): bool
{ {
if ($this->get($key) === $oldValue) { if ($this->get($key) === $oldValue) {
return $this->set($key, $newValue); return $this->set($key, $newValue);
@ -111,8 +112,8 @@ class ArrayCache extends BaseCache implements IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getName() public function getName(): string
{ {
return Type::ARRAY; return Enum\Type::ARRAY;
} }
} }

View file

@ -19,31 +19,33 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Friendica\Core\Cache\Enum\Duration;
/** /**
* Trait TraitCompareSetDelete * This Trait is to compensate nonnative "exclusive" sets/deletes in caches
*
* This Trait is to compensate non native "exclusive" sets/deletes in caches
*/ */
trait TraitCompareDelete trait CompareDeleteTrait
{ {
abstract public function get($key); abstract public function get(string $key);
abstract public function set($key, $value, $ttl = Duration::FIVE_MINUTES); abstract public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES);
abstract public function delete($key); abstract public function delete(string $key);
abstract public function add($key, $value, $ttl = Duration::FIVE_MINUTES); abstract public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES);
/** /**
* NonNative - Compares if the old value is set and removes it * NonNative - Compares if the old value is set and removes it
* *
* @param string $key The cache key * @param string $key The cache key
* @param mixed $value The old value we know and want to delete * @param mixed $value The old value we know and want to delete
*
* @return bool * @return bool
*/ */
public function compareDelete($key, $value) { public function compareDelete(string $key, $value): bool
{
if ($this->add($key . "_lock", true)) { if ($this->add($key . "_lock", true)) {
if ($this->get($key) === $value) { if ($this->get($key) === $value) {
$this->delete($key); $this->delete($key);

View file

@ -19,40 +19,41 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Friendica\Core\Cache\Enum\Duration;
/** /**
* Trait TraitCompareSetDelete * This Trait is to compensate nonnative "exclusive" sets/deletes in caches
*
* This Trait is to compensate non native "exclusive" sets/deletes in caches
*/ */
trait TraitCompareSet trait CompareSetTrait
{ {
abstract public function get($key); abstract public function get(string $key);
abstract public function set($key, $value, $ttl = Duration::FIVE_MINUTES); abstract public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES);
abstract public function delete($key); abstract public function delete(string $key);
abstract public function add($key, $value, $ttl = Duration::FIVE_MINUTES); abstract public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES);
/** /**
* NonNative - Compares if the old value is set and sets the new value * NonNative - Compares if the old value is set and sets the new value
* *
* @param string $key The cache key * @param string $key The cache key
* @param mixed $oldValue The old value we know from the cache * @param mixed $oldValue The old value we know from the cache
* @param mixed $newValue The new value we want to set * @param mixed $newValue The new value we want to set
* @param int $ttl The cache lifespan, must be one of the Cache constants * @param int $ttl The cache lifespan, must be one of the Cache constants
* *
* @return bool * @return bool
*/ */
public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) { public function compareSet(string $key, $oldValue, $newValue, int $ttl = Duration::FIVE_MINUTES): bool
{
if ($this->add($key . "_lock", true)) { if ($this->add($key . "_lock", true)) {
if ($this->get($key) === $oldValue) { if ($this->get($key) === $oldValue) {
$this->set($key, $newValue, $ttl); $this->set($key, $newValue, $ttl);
$this->delete($key . "_lock"); $this->delete($key . "_lock");
return true; return true;
} else { } else {
$this->delete($key . "_lock"); $this->delete($key . "_lock");
return false; return false;
} }

View file

@ -0,0 +1,165 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Cache\Type;
use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Cache\Enum;
use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Database\Database;
use Friendica\Util\DateTimeFormat;
/**
* Database Cache
*/
class DatabaseCache extends AbstractCache implements ICanCache
{
/**
* @var Database
*/
private $dba;
public function __construct(string $hostname, Database $dba)
{
parent::__construct($hostname);
$this->dba = $dba;
}
/**
* (@inheritdoc)
*
* @throws CachePersistenceException
*/
public function getAllKeys(?string $prefix = null): array
{
try {
if (empty($prefix)) {
$where = ['`expires` >= ?', DateTimeFormat::utcNow()];
} else {
$where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
}
$stmt = $this->dba->select('cache', ['k'], $where);
$keys = [];
while ($key = $this->dba->fetch($stmt)) {
array_push($keys, $key['k']);
}
} catch (\Exception $exception) {
throw new CachePersistenceException(sprintf('Cannot fetch all keys with prefix %s', $prefix), $exception);
} finally {
$this->dba->close($stmt);
}
return $keys;
}
/**
* (@inheritdoc)
*/
public function get(string $key)
{
try {
$cache = $this->dba->selectFirst('cache', ['v'], [
'`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, DateTimeFormat::utcNow()
]);
if ($this->dba->isResult($cache)) {
$cached = $cache['v'];
$value = @unserialize($cached);
// Only return a value if the serialized value is valid.
// We also check if the db entry is a serialized
// boolean 'false' value (which we want to return).
if ($cached === serialize(false) || $value !== false) {
return $value;
}
}
} catch (\Exception $exception) {
throw new CachePersistenceException(sprintf('Cannot get cache entry with key %s', $key), $exception);
}
return null;
}
/**
* (@inheritdoc)
*/
public function set(string $key, $value, int $ttl = Enum\Duration::FIVE_MINUTES): bool
{
try {
if ($ttl > 0) {
$fields = [
'v' => serialize($value),
'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds'),
'updated' => DateTimeFormat::utcNow()
];
} else {
$fields = [
'v' => serialize($value),
'expires' => -1,
'updated' => DateTimeFormat::utcNow()
];
}
return $this->dba->update('cache', $fields, ['k' => $key], true);
} catch (\Exception $exception) {
throw new CachePersistenceException(sprintf('Cannot set cache entry with key %s', $key), $exception);
}
}
/**
* (@inheritdoc)
*/
public function delete(string $key): bool
{
try {
return $this->dba->delete('cache', ['k' => $key]);
} catch (\Exception $exception) {
throw new CachePersistenceException(sprintf('Cannot delete cache entry with key %s', $key), $exception);
}
}
/**
* (@inheritdoc)
*/
public function clear(bool $outdated = true): bool
{
try {
if ($outdated) {
return $this->dba->delete('cache', ['`expires` < NOW()']);
} else {
return $this->dba->delete('cache', ['`k` IS NOT NULL ']);
}
} catch (\Exception $exception) {
throw new CachePersistenceException('Cannot clear cache', $exception);
}
}
/**
* {@inheritDoc}
*/
public function getName(): string
{
return Enum\Type::DATABASE;
}
}

View file

@ -19,21 +19,24 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Exception; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\BaseCache; use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Config\IConfig; use Friendica\Core\Cache\Enum\Type;
use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Memcache; use Memcache;
/** /**
* Memcache Cache * Memcache Cache
*/ */
class MemcacheCache extends BaseCache implements IMemoryCache class MemcacheCache extends AbstractCache implements ICanCacheInMemory
{ {
use TraitCompareSet; use CompareSetTrait;
use TraitCompareDelete; use CompareDeleteTrait;
use TraitMemcacheCommand; use MemcacheCommandTrait;
/** /**
* @var Memcache * @var Memcache
@ -41,30 +44,34 @@ class MemcacheCache extends BaseCache implements IMemoryCache
private $memcache; private $memcache;
/** /**
* @throws Exception * @param string $hostname
* @param IManageConfigValues $config
*
* @throws InvalidCacheDriverException
* @throws CachePersistenceException
*/ */
public function __construct(string $hostname, IConfig $config) public function __construct(string $hostname, IManageConfigValues $config)
{ {
if (!class_exists('Memcache', false)) { if (!class_exists('Memcache', false)) {
throw new Exception('Memcache class isn\'t available'); throw new InvalidCacheDriverException('Memcache class isn\'t available');
} }
parent::__construct($hostname); parent::__construct($hostname);
$this->memcache = new Memcache(); $this->memcache = new Memcache();
$this->server = $config->get('system', 'memcache_host');; $this->server = $config->get('system', 'memcache_host');
$this->port = $config->get('system', 'memcache_port'); $this->port = $config->get('system', 'memcache_port');
if (!@$this->memcache->connect($this->server, $this->port)) { if (!@$this->memcache->connect($this->server, $this->port)) {
throw new Exception('Expected Memcache server at ' . $this->server . ':' . $this->port . ' isn\'t available'); throw new CachePersistenceException('Expected Memcache server at ' . $this->server . ':' . $this->port . ' isn\'t available');
} }
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function getAllKeys($prefix = null) public function getAllKeys(?string $prefix = null): array
{ {
$keys = $this->getOriginalKeys($this->getMemcacheKeys()); $keys = $this->getOriginalKeys($this->getMemcacheKeys());
@ -74,17 +81,16 @@ class MemcacheCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function get($key) public function get(string $key)
{ {
$return = null; $cacheKey = $this->getCacheKey($key);
$cachekey = $this->getCacheKey($key);
// We fetch with the hostname as key to avoid problems with other applications // We fetch with the hostname as key to avoid problems with other applications
$cached = $this->memcache->get($cachekey); $cached = $this->memcache->get($cacheKey);
// @see http://php.net/manual/en/memcache.get.php#84275 // @see http://php.net/manual/en/memcache.get.php#84275
if (is_bool($cached) || is_double($cached) || is_long($cached)) { if (is_bool($cached) || is_double($cached) || is_long($cached)) {
return $return; return null;
} }
$value = @unserialize($cached); $value = @unserialize($cached);
@ -93,30 +99,30 @@ class MemcacheCache extends BaseCache implements IMemoryCache
// We also check if the db entry is a serialized // We also check if the db entry is a serialized
// boolean 'false' value (which we want to return). // boolean 'false' value (which we want to return).
if ($cached === serialize(false) || $value !== false) { if ($cached === serialize(false) || $value !== false) {
$return = $value; return $value;
} }
return $return; return null;
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES) public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
// We store with the hostname as key to avoid problems with other applications // We store with the hostname as key to avoid problems with other applications
if ($ttl > 0) { if ($ttl > 0) {
return $this->memcache->set( return $this->memcache->set(
$cachekey, $cacheKey,
serialize($value), serialize($value),
MEMCACHE_COMPRESSED, MEMCACHE_COMPRESSED,
time() + $ttl time() + $ttl
); );
} else { } else {
return $this->memcache->set( return $this->memcache->set(
$cachekey, $cacheKey,
serialize($value), serialize($value),
MEMCACHE_COMPRESSED MEMCACHE_COMPRESSED
); );
@ -126,16 +132,16 @@ class MemcacheCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function delete($key) public function delete(string $key): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
return $this->memcache->delete($cachekey); return $this->memcache->delete($cacheKey);
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function clear($outdated = true) public function clear(bool $outdated = true): bool
{ {
if ($outdated) { if ($outdated) {
return true; return true;
@ -147,16 +153,16 @@ class MemcacheCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES) public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
return $this->memcache->add($cachekey, serialize($value), MEMCACHE_COMPRESSED, $ttl); return $this->memcache->add($cacheKey, serialize($value), MEMCACHE_COMPRESSED, $ttl);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getName() public function getName(): string
{ {
return Type::MEMCACHE; return Type::MEMCACHE;
} }

View file

@ -19,9 +19,9 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Friendica\Network\HTTPException\ServiceUnavailableException; use Friendica\Core\Cache\Exception\CachePersistenceException;
/** /**
* Trait for Memcache to add a custom version of the * Trait for Memcache to add a custom version of the
@ -29,7 +29,7 @@ use Friendica\Network\HTTPException\ServiceUnavailableException;
* *
* Adds the possibility to directly communicate with the memcache too * Adds the possibility to directly communicate with the memcache too
*/ */
trait TraitMemcacheCommand trait MemcacheCommandTrait
{ {
/** /**
* @var string server address * @var string server address
@ -52,23 +52,19 @@ trait TraitMemcacheCommand
* *
* @return array All keys of the memcache instance * @return array All keys of the memcache instance
* *
* @throws ServiceUnavailableException * @throws CachePersistenceException
*/ */
protected function getMemcacheKeys() protected function getMemcacheKeys(): array
{ {
$string = $this->sendMemcacheCommand("stats items"); $string = $this->sendMemcacheCommand("stats items");
$lines = explode("\r\n", $string); $lines = explode("\r\n", $string);
$slabs = [];
$keys = []; $keys = [];
foreach ($lines as $line) { foreach ($lines as $line) {
if (preg_match("/STAT items:([\d]+):number ([\d]+)/", $line, $matches) && if (preg_match("/STAT items:([\d]+):number ([\d]+)/", $line, $matches) &&
isset($matches[1]) && isset($matches[1]) &&
!in_array($matches[1], $keys)) { !in_array($matches[1], $keys)) {
$string = $this->sendMemcacheCommand("stats cachedump " . $matches[1] . " " . $matches[2]);
$slabs[] = $matches[1];
$string = $this->sendMemcacheCommand("stats cachedump " . $matches[1] . " " . $matches[2]);
preg_match_all("/ITEM (.*?) /", $string, $matches); preg_match_all("/ITEM (.*?) /", $string, $matches);
$keys = array_merge($keys, $matches[1]); $keys = array_merge($keys, $matches[1]);
} }
@ -88,20 +84,19 @@ trait TraitMemcacheCommand
* *
* @return string The returned buffer result * @return string The returned buffer result
* *
* @throws ServiceUnavailableException In case the memcache server isn't available (anymore) * @throws CachePersistenceException In case the memcache server isn't available (anymore)
*/ */
protected function sendMemcacheCommand(string $command) protected function sendMemcacheCommand(string $command): string
{ {
$s = @fsockopen($this->server, $this->port); $s = @fsockopen($this->server, $this->port);
if (!$s) { if (!$s) {
throw new ServiceUnavailableException("Cant connect to:" . $this->server . ':' . $this->port); throw new CachePersistenceException("Cant connect to:" . $this->server . ':' . $this->port);
} }
fwrite($s, $command . "\r\n"); fwrite($s, $command . "\r\n");
$buf = ''; $buf = '';
while (!feof($s)) { while (!feof($s)) {
$buf .= fgets($s, 256); $buf .= fgets($s, 256);
if (strpos($buf, "END\r\n") !== false) { // stat says end if (strpos($buf, "END\r\n") !== false) { // stat says end

View file

@ -19,22 +19,25 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Exception; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\BaseCache; use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Config\IConfig; use Friendica\Core\Cache\Enum\Type;
use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Memcached; use Memcached;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
/** /**
* Memcached Cache * Memcached Cache
*/ */
class MemcachedCache extends BaseCache implements IMemoryCache class MemcachedCache extends AbstractCache implements ICanCacheInMemory
{ {
use TraitCompareSet; use CompareSetTrait;
use TraitCompareDelete; use CompareDeleteTrait;
use TraitMemcacheCommand; use MemcacheCommandTrait;
/** /**
* @var \Memcached * @var \Memcached
@ -53,14 +56,17 @@ class MemcachedCache extends BaseCache implements IMemoryCache
* 1 => ... * 1 => ...
* } * }
* *
* @param array $memcached_hosts * @param string $hostname
* @param IManageConfigValues $config
* @param LoggerInterface $logger
* *
* @throws \Exception * @throws InvalidCacheDriverException
* @throws CachePersistenceException
*/ */
public function __construct(string $hostname, IConfig $config, LoggerInterface $logger) public function __construct(string $hostname, IManageConfigValues $config, LoggerInterface $logger)
{ {
if (!class_exists('Memcached', false)) { if (!class_exists('Memcached', false)) {
throw new Exception('Memcached class isn\'t available'); throw new InvalidCacheDriverException('Memcached class isn\'t available');
} }
parent::__construct($hostname); parent::__construct($hostname);
@ -78,19 +84,19 @@ class MemcachedCache extends BaseCache implements IMemoryCache
}); });
$this->server = $memcached_hosts[0][0] ?? 'localhost'; $this->server = $memcached_hosts[0][0] ?? 'localhost';
$this->port = $memcached_hosts[0][1] ?? 11211; $this->port = $memcached_hosts[0][1] ?? 11211;
$this->memcached->addServers($memcached_hosts); $this->memcached->addServers($memcached_hosts);
if (count($this->memcached->getServerList()) == 0) { if (count($this->memcached->getServerList()) == 0) {
throw new Exception('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true)); throw new CachePersistenceException('Expected Memcached servers aren\'t available, config:' . var_export($memcached_hosts, true));
} }
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function getAllKeys($prefix = null) public function getAllKeys(?string $prefix = null): array
{ {
$keys = $this->getOriginalKeys($this->getMemcacheKeys()); $keys = $this->getOriginalKeys($this->getMemcacheKeys());
@ -100,40 +106,40 @@ class MemcachedCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function get($key) public function get(string $key)
{ {
$return = null; $cacheKey = $this->getCacheKey($key);
$cachekey = $this->getCacheKey($key);
// We fetch with the hostname as key to avoid problems with other applications // We fetch with the hostname as key to avoid problems with other applications
$value = $this->memcached->get($cachekey); $value = $this->memcached->get($cacheKey);
if ($this->memcached->getResultCode() === Memcached::RES_SUCCESS) { if ($this->memcached->getResultCode() === Memcached::RES_SUCCESS) {
$return = $value; return $value;
} elseif ($this->memcached->getResultCode() === Memcached::RES_NOTFOUND) {
$this->logger->notice('Try to use unknown key.', ['key' => $key]);
return null;
} else { } else {
$this->logger->debug('Memcached \'get\' failed', ['result' => $this->memcached->getResultMessage()]); throw new CachePersistenceException(sprintf('Cannot get cache entry with key %s', $key), new \MemcachedException($this->memcached->getResultMessage(), $this->memcached->getResultCode()));
} }
return $return;
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES) public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
// We store with the hostname as key to avoid problems with other applications // We store with the hostname as key to avoid problems with other applications
if ($ttl > 0) { if ($ttl > 0) {
return $this->memcached->set( return $this->memcached->set(
$cachekey, $cacheKey,
$value, $value,
$ttl $ttl
); );
} else { } else {
return $this->memcached->set( return $this->memcached->set(
$cachekey, $cacheKey,
$value $value
); );
} }
@ -142,16 +148,16 @@ class MemcachedCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function delete($key) public function delete(string $key): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
return $this->memcached->delete($cachekey); return $this->memcached->delete($cacheKey);
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function clear($outdated = true) public function clear(bool $outdated = true): bool
{ {
if ($outdated) { if ($outdated) {
return true; return true;
@ -163,16 +169,16 @@ class MemcachedCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES) public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
return $this->memcached->add($cachekey, $value, $ttl); return $this->memcached->add($cacheKey, $value, $ttl);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getName() public function getName(): string
{ {
return Type::MEMCACHED; return Type::MEMCACHED;
} }

View file

@ -19,20 +19,22 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Friendica\Core\System; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
/** /**
* This class wraps cache driver so they can get profiled - in case the profiler is enabled * This class wraps cache driver, so they can get profiled - in case the profiler is enabled
* *
* It is using the decorator pattern (@see * It is using the decorator pattern (@see https://en.wikipedia.org/wiki/Decorator_pattern )
*/ */
class ProfilerCache implements ICache, IMemoryCache class ProfilerCacheDecorator implements ICanCache, ICanCacheInMemory
{ {
/** /**
* @var ICache The original cache driver * @var ICanCache The original cache driver
*/ */
private $cache; private $cache;
@ -41,7 +43,7 @@ class ProfilerCache implements ICache, IMemoryCache
*/ */
private $profiler; private $profiler;
public function __construct(ICache $cache, Profiler $profiler) public function __construct(ICanCache $cache, Profiler $profiler)
{ {
$this->cache = $cache; $this->cache = $cache;
$this->profiler = $profiler; $this->profiler = $profiler;
@ -50,7 +52,7 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getAllKeys($prefix = null) public function getAllKeys(?string $prefix = null): array
{ {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
@ -64,7 +66,7 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function get($key) public function get(string $key)
{ {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
@ -78,7 +80,7 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES) public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
@ -92,7 +94,7 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function delete($key) public function delete(string $key): bool
{ {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
@ -106,7 +108,7 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function clear($outdated = true) public function clear(bool $outdated = true): bool
{ {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
@ -120,9 +122,9 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES) public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
if ($this->cache instanceof IMemoryCache) { if ($this->cache instanceof ICanCacheInMemory) {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
$return = $this->cache->add($key, $value, $ttl); $return = $this->cache->add($key, $value, $ttl);
@ -138,9 +140,9 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) public function compareSet(string $key, $oldValue, $newValue, int $ttl = Duration::FIVE_MINUTES): bool
{ {
if ($this->cache instanceof IMemoryCache) { if ($this->cache instanceof ICanCacheInMemory) {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
$return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl); $return = $this->cache->compareSet($key, $oldValue, $newValue, $ttl);
@ -156,9 +158,9 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function compareDelete($key, $value) public function compareDelete(string $key, $value): bool
{ {
if ($this->cache instanceof IMemoryCache) { if ($this->cache instanceof ICanCacheInMemory) {
$this->profiler->startRecording('cache'); $this->profiler->startRecording('cache');
$return = $this->cache->compareDelete($key, $value); $return = $this->cache->compareDelete($key, $value);
@ -174,7 +176,7 @@ class ProfilerCache implements ICache, IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function GetName() public function GetName(): string
{ {
return $this->cache->getName() . ' (with profiler)'; return $this->cache->getName() . ' (with profiler)';
} }

View file

@ -19,17 +19,21 @@
* *
*/ */
namespace Friendica\Core\Cache; namespace Friendica\Core\Cache\Type;
use Exception; use Exception;
use Friendica\Core\BaseCache; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Config\IConfig; use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Cache\Enum\Type;
use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Redis; use Redis;
/** /**
* Redis Cache. This driver is based on Memcache driver * Redis Cache. This driver is based on Memcache driver
*/ */
class RedisCache extends BaseCache implements IMemoryCache class RedisCache extends AbstractCache implements ICanCacheInMemory
{ {
/** /**
* @var Redis * @var Redis
@ -37,12 +41,13 @@ class RedisCache extends BaseCache implements IMemoryCache
private $redis; private $redis;
/** /**
* @throws Exception * @throws InvalidCacheDriverException
* @throws CachePersistenceException
*/ */
public function __construct(string $hostname, IConfig $config) public function __construct(string $hostname, IManageConfigValues $config)
{ {
if (!class_exists('Redis', false)) { if (!class_exists('Redis', false)) {
throw new Exception('Redis class isn\'t available'); throw new InvalidCacheDriverException('Redis class isn\'t available');
} }
parent::__construct($hostname); parent::__construct($hostname);
@ -55,24 +60,24 @@ class RedisCache extends BaseCache implements IMemoryCache
$redis_db = $config->get('system', 'redis_db', 0); $redis_db = $config->get('system', 'redis_db', 0);
if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) { if (isset($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
throw new Exception('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available'); throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available');
} elseif (!@$this->redis->connect($redis_host)) { } elseif (!@$this->redis->connect($redis_host)) {
throw new Exception('Expected Redis server at ' . $redis_host . ' isn\'t available'); throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ' isn\'t available');
} }
if (isset($redis_pw) && !$this->redis->auth($redis_pw)) { if (isset($redis_pw) && !$this->redis->auth($redis_pw)) {
throw new Exception('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port); throw new CachePersistenceException('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port);
} }
if ($redis_db !== 0 && !$this->redis->select($redis_db)) { if ($redis_db !== 0 && !$this->redis->select($redis_db)) {
throw new Exception('Cannot switch to redis db ' . $redis_db . ' at ' . $redis_host . ':' . $redis_port); throw new CachePersistenceException('Cannot switch to redis db ' . $redis_db . ' at ' . $redis_host . ':' . $redis_port);
} }
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function getAllKeys($prefix = null) public function getAllKeys(?string $prefix = null): array
{ {
if (empty($prefix)) { if (empty($prefix)) {
$search = '*'; $search = '*';
@ -88,13 +93,13 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function get($key) public function get(string $key)
{ {
$return = null; $return = null;
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$cached = $this->redis->get($cachekey); $cached = $this->redis->get($cacheKey);
if ($cached === false && !$this->redis->exists($cachekey)) { if ($cached === false && !$this->redis->exists($cacheKey)) {
return null; return null;
} }
@ -113,21 +118,21 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function set($key, $value, $ttl = Duration::FIVE_MINUTES) public function set(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$cached = serialize($value); $cached = serialize($value);
if ($ttl > 0) { if ($ttl > 0) {
return $this->redis->setex( return $this->redis->setex(
$cachekey, $cacheKey,
$ttl, $ttl,
$cached $cached
); );
} else { } else {
return $this->redis->set( return $this->redis->set(
$cachekey, $cacheKey,
$cached $cached
); );
} }
@ -136,10 +141,10 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function delete($key) public function delete(string $key): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$this->redis->del($cachekey); $this->redis->del($cacheKey);
// Redis doesn't have an error state for del() // Redis doesn't have an error state for del()
return true; return true;
} }
@ -147,7 +152,7 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function clear($outdated = true) public function clear(bool $outdated = true): bool
{ {
if ($outdated) { if ($outdated) {
return true; return true;
@ -159,34 +164,30 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function add($key, $value, $ttl = Duration::FIVE_MINUTES) public function add(string $key, $value, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$cached = serialize($value); $cached = serialize($value);
return $this->redis->setnx($cachekey, $cached); return $this->redis->setnx($cacheKey, $cached);
} }
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function compareSet($key, $oldValue, $newValue, $ttl = Duration::FIVE_MINUTES) public function compareSet(string $key, $oldValue, $newValue, int $ttl = Duration::FIVE_MINUTES): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$newCached = serialize($newValue); $newCached = serialize($newValue);
$this->redis->watch($cachekey); $this->redis->watch($cacheKey);
// If the old value isn't what we expected, somebody else changed the key meanwhile // If the old value isn't what we expected, somebody else changed the key meanwhile
if ($this->get($key) === $oldValue) { if ($this->get($key) === $oldValue) {
if ($ttl > 0) { if ($ttl > 0) {
$result = $this->redis->multi() $result = $this->redis->multi()->setex($cacheKey, $ttl, $newCached)->exec();
->setex($cachekey, $ttl, $newCached)
->exec();
} else { } else {
$result = $this->redis->multi() $result = $this->redis->multi()->set($cacheKey, $newCached)->exec();
->set($cachekey, $newCached)
->exec();
} }
return $result !== false; return $result !== false;
} }
@ -197,17 +198,15 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function compareDelete($key, $value) public function compareDelete(string $key, $value): bool
{ {
$cachekey = $this->getCacheKey($key); $cacheKey = $this->getCacheKey($key);
$this->redis->watch($cachekey); $this->redis->watch($cacheKey);
// If the old value isn't what we expected, somebody else changed the key meanwhile // If the old value isn't what we expected, somebody else changed the key meanwhile
if ($this->get($key) === $value) { if ($this->get($key) === $value) {
$result = $this->redis->multi() $this->redis->multi()->del($cacheKey)->exec();
->del($cachekey) return true;
->exec();
return $result !== false;
} }
$this->redis->unwatch(); $this->redis->unwatch();
return false; return false;
@ -216,7 +215,7 @@ class RedisCache extends BaseCache implements IMemoryCache
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getName() public function getName(): string
{ {
return Type::REDIS; return Type::REDIS;
} }

View file

@ -19,30 +19,35 @@
* *
*/ */
namespace Friendica\Core\Config; namespace Friendica\Core\Config\Capability;
use Friendica\Core\Config\Exception\ConfigPersistenceException;
use Friendica\Core\Config\ValueObject\Cache;
/** /**
* Interface for accessing system wide configurations * Interface for accessing system-wide configurations
*/ */
interface IConfig interface IManageConfigValues
{ {
/** /**
* Loads all configuration values of family into a cached storage. * Loads all configuration values of family into a cached storage.
* *
* All configuration values of the system are stored in the cache ( @param string $cat The category of the configuration value * All configuration values of the system are stored in the cache.
*
* @param string $cat The category of the configuration value
* *
* @return void * @return void
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*/ */
function load(string $cat = 'config'); public function load(string $cat = 'config');
/** /**
* Get a particular user's config variable given the category name * Get a particular user's config variable given the category name
* ($cat) and a $key. * ($cat) and a $key.
* *
* Get a particular config value from the given category ($cat) * Get a particular config value from the given category ($cat)
* and the $key from a cached storage either from the $this->configAdapter * and the $key from a cached storage either from the database or from the cache.
* (@see IConfigAdapter) or from the $this->configCache (@see ConfigCache).
* *
* @param string $cat The category of the configuration value * @param string $cat The category of the configuration value
* @param string $key The configuration key to query * @param string $key The configuration key to query
@ -50,8 +55,11 @@ interface IConfig
* @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) * @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 * @return mixed Stored value or null if it does not exist
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*
*/ */
function get(string $cat, string $key, $default_value = null, bool $refresh = false); public function get(string $cat, string $key, $default_value = null, bool $refresh = false);
/** /**
* Sets a configuration value for system config * Sets a configuration value for system config
@ -65,26 +73,30 @@ interface IConfig
* @param mixed $value The value to store * @param mixed $value The value to store
* *
* @return bool Operation success * @return bool Operation success
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*/ */
function set(string $cat, string $key, $value); public function set(string $cat, string $key, $value): bool;
/** /**
* Deletes the given key from the system configuration. * Deletes the given key from the system configuration.
* *
* Removes the configured value from the stored cache in $this->configCache * Removes the configured value from the stored cache in the cache and removes it from the database.
* (@see ConfigCache) and removes it from the database (@see IConfigAdapter).
* *
* @param string $cat The category of the configuration value * @param string $cat The category of the configuration value
* @param string $key The configuration key to delete * @param string $key The configuration key to delete
* *
* @return bool * @return bool
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*
*/ */
function delete(string $cat, string $key); public function delete(string $cat, string $key): bool;
/** /**
* Returns the Config Cache * Returns the Config Cache
* *
* @return Cache * @return Cache
*/ */
function getCache(); public function getCache(): Cache;
} }

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Config\Exception;
use Throwable;
class ConfigFileException extends \RuntimeException
{
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Config\Exception;
use Throwable;
class ConfigPersistenceException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -19,16 +19,15 @@
* *
*/ */
namespace Friendica\Factory; namespace Friendica\Core\Config\Factory;
use Exception; use Friendica\Core\Config\Capability;
use Friendica\Core\Config; use Friendica\Core\Config\Repository;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\Type;
use Friendica\Model\Config\Config as ConfigModel; use Friendica\Core\Config\Util;
use Friendica\Model\Config\PConfig as PConfigModel; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Util\ConfigFileLoader;
class ConfigFactory class Config
{ {
/** /**
* The key of the $_SERVER variable to override the config directory * The key of the $_SERVER variable to override the config directory
@ -53,11 +52,11 @@ class ConfigFactory
/** /**
* @param string $basePath The basepath of FRIENDICA * @param string $basePath The basepath of FRIENDICA
* @param array $serer the $_SERVER array * @param array $server The $_SERVER array
* *
* @return ConfigFileLoader * @return Util\ConfigFileLoader
*/ */
public function createConfigFileLoader(string $basePath, array $server = []) public function createConfigFileLoader(string $basePath, array $server = []): Util\ConfigFileLoader
{ {
if (!empty($server[self::CONFIG_DIR_ENV]) && is_dir($server[self::CONFIG_DIR_ENV])) { if (!empty($server[self::CONFIG_DIR_ENV]) && is_dir($server[self::CONFIG_DIR_ENV])) {
$configDir = $server[self::CONFIG_DIR_ENV]; $configDir = $server[self::CONFIG_DIR_ENV];
@ -66,17 +65,16 @@ class ConfigFactory
} }
$staticDir = $basePath . DIRECTORY_SEPARATOR . self::STATIC_DIR; $staticDir = $basePath . DIRECTORY_SEPARATOR . self::STATIC_DIR;
return new ConfigFileLoader($basePath, $configDir, $staticDir); return new Util\ConfigFileLoader($basePath, $configDir, $staticDir);
} }
/** /**
* @param ConfigFileLoader $loader The Config Cache loader (INI/config/.htconfig) * @param Util\ConfigFileLoader $loader The Config Cache loader (INI/config/.htconfig)
* @param array $server
* *
* @return Cache * @return Cache
*
* @throws Exception
*/ */
public function createCache(ConfigFileLoader $loader, array $server = []) public function createCache(Util\ConfigFileLoader $loader, array $server = []): Cache
{ {
$configCache = new Cache(); $configCache = new Cache();
$loader->setupCache($configCache, $server); $loader->setupCache($configCache, $server);
@ -85,36 +83,17 @@ class ConfigFactory
} }
/** /**
* @param Cache $configCache The config cache of this adapter * @param Cache $configCache The config cache of this adapter
* @param ConfigModel $configModel The configuration model * @param Repository\Config $configRepo The configuration repository
* *
* @return Config\IConfig * @return Capability\IManageConfigValues
*/ */
public function createConfig(Cache $configCache, ConfigModel $configModel) public function create(Cache $configCache, Repository\Config $configRepo)
{ {
if ($configCache->get('system', 'config_adapter') === 'preload') { if ($configCache->get('system', 'config_adapter') === 'preload') {
$configuration = new Config\PreloadConfig($configCache, $configModel); $configuration = new Type\PreloadConfig($configCache, $configRepo);
} else { } else {
$configuration = new Config\JitConfig($configCache, $configModel); $configuration = new Type\JitConfig($configCache, $configRepo);
}
return $configuration;
}
/**
* @param Cache $configCache The config cache
* @param \Friendica\Core\PConfig\Cache $pConfigCache The personal config cache
* @param PConfigModel $configModel The configuration model
*
* @return \Friendica\Core\PConfig\IPConfig
*/
public function createPConfig(Cache $configCache, \Friendica\Core\PConfig\Cache $pConfigCache, PConfigModel $configModel)
{
if ($configCache->get('system', 'config_adapter') === 'preload') {
$configuration = new \Friendica\Core\PConfig\PreloadPConfig($pConfigCache, $configModel);
} else {
$configuration = new \Friendica\Core\PConfig\JitPConfig($pConfigCache, $configModel);
} }
return $configuration; return $configuration;

View file

@ -0,0 +1,187 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Config\Repository;
use Friendica\Core\Config\Exception\ConfigPersistenceException;
use Friendica\Core\Config\Util\ValueConversion;
use Friendica\Database\Database;
/**
* The Config Repository, which is using the general DB-model backend for configs
*/
class Config
{
/** @var Database */
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
protected static $table_name = 'config';
/**
* Checks if the model is currently connected
*
* @return bool
*/
public function isConnected(): bool
{
return $this->db->isConnected();
}
/**
* Loads all configuration values and returns the loaded category as an array.
*
* @param string|null $cat The category of the configuration values to load
*
* @return array The config array
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*/
public function load(?string $cat = null): array
{
$return = [];
try {
if (empty($cat)) {
$configs = $this->db->select(static::$table_name, ['cat', 'v', 'k']);
} else {
$configs = $this->db->select(static::$table_name, ['cat', 'v', 'k'], ['cat' => $cat]);
}
while ($config = $this->db->fetch($configs)) {
$key = $config['k'];
$value = ValueConversion::toConfigValue($config['v']);
// just save it in case it is set
if (isset($value)) {
$return[$config['cat']][$key] = $value;
}
}
} catch (\Exception $exception) {
throw new ConfigPersistenceException(sprintf('Cannot load config category %s', $cat), $exception);
} finally {
$this->db->close($configs);
}
return $return;
}
/**
* Get a particular, system-wide config variable out of the DB with the
* given category name ($cat) and a key ($key).
*
* Note: Boolean variables are defined as 0/1 in the database
*
* @param string $cat The category of the configuration value
* @param string $key The configuration key to query
*
* @return array|string|null Stored value or null if it does not exist
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*/
public function get(string $cat, string $key)
{
if (!$this->isConnected()) {
return null;
}
try {
$config = $this->db->selectFirst(static::$table_name, ['v'], ['cat' => $cat, 'k' => $key]);
if ($this->db->isResult($config)) {
$value = ValueConversion::toConfigValue($config['v']);
// just return it in case it is set
if (isset($value)) {
return $value;
}
}
} catch (\Exception $exception) {
throw new ConfigPersistenceException(sprintf('Cannot get config with category %s and key %s', $cat, $key), $exception);
}
return null;
}
/**
* 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
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*/
public function set(string $cat, string $key, $value): bool
{
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.
$compare_value = (!is_array($value) ? (string)$value : $value);
$stored_value = $this->get($cat, $key);
if (isset($stored_value) && ($stored_value === $compare_value)) {
return true;
}
$dbValue = ValueConversion::toDbValue($value);
try {
return $this->db->update(static::$table_name, ['v' => $dbValue], ['cat' => $cat, 'k' => $key], true);
} catch (\Exception $exception) {
throw new ConfigPersistenceException(sprintf('Cannot set config with category %s and key %s', $cat, $key), $exception);
}
}
/**
* Removes the configured value from the database.
*
* @param string $cat The category of the configuration value
* @param string $key The configuration key to delete
*
* @return bool Operation success
*
* @throws ConfigPersistenceException In case the persistence layer throws errors
*/
public function delete(string $cat, string $key): bool
{
if (!$this->isConnected()) {
return false;
}
try {
return $this->db->delete(static::$table_name, ['cat' => $cat, 'k' => $key]);
} catch (\Exception $exception) {
throw new ConfigPersistenceException(sprintf('Cannot delete config with category %s and key %s', $cat, $key), $exception);
}
}
}

View file

@ -19,19 +19,19 @@
* *
*/ */
namespace Friendica\Core; namespace Friendica\Core\Config\Type;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\Repository\Config;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Model; use Friendica\Core\Config\Capability\IManageConfigValues;
/** /**
* This class is responsible for all system-wide configuration values in Friendica * This class is responsible for all system-wide configuration values in Friendica
* There are two types of storage * There are two types of storage
* - The Config-Files (loaded into the FileCache @see Cache\ConfigCache) * - The Config-Files (loaded into the FileCache @see Cache)
* - The Config-DB-Table (per Config-DB-model @see Model\Config\Config) * - The Config-Repository (per Config-Repository @see Config )
*/ */
abstract class BaseConfig implements IConfig abstract class AbstractConfig implements IManageConfigValues
{ {
/** /**
* @var Cache * @var Cache
@ -39,24 +39,24 @@ abstract class BaseConfig implements IConfig
protected $configCache; protected $configCache;
/** /**
* @var Model\Config\Config * @var Config
*/ */
protected $configModel; protected $configRepo;
/** /**
* @param Cache $configCache The configuration cache (based on the config-files) * @param Cache $configCache The configuration cache (based on the config-files)
* @param Model\Config\Config $configModel The configuration model * @param Config $configRepo The configuration repository
*/ */
public function __construct(Cache $configCache, Model\Config\Config $configModel) public function __construct(Cache $configCache, Config $configRepo)
{ {
$this->configCache = $configCache; $this->configCache = $configCache;
$this->configModel = $configModel; $this->configRepo = $configRepo;
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getCache() public function getCache(): Cache
{ {
return $this->configCache; return $this->configCache;
} }

View file

@ -19,10 +19,10 @@
* *
*/ */
namespace Friendica\Core\Config; namespace Friendica\Core\Config\Type;
use Friendica\Core\BaseConfig; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Model; use Friendica\Core\Config\Repository\Config;
/** /**
* This class implements the Just-In-Time configuration, which will cache * This class implements the Just-In-Time configuration, which will cache
@ -31,7 +31,7 @@ use Friendica\Model;
* Default Configuration type. * Default Configuration type.
* Provides the best performance for pages loading few configuration variables. * Provides the best performance for pages loading few configuration variables.
*/ */
class JitConfig extends BaseConfig class JitConfig extends AbstractConfig
{ {
/** /**
* @var array Array of already loaded db values (even if there was no value) * @var array Array of already loaded db values (even if there was no value)
@ -39,12 +39,12 @@ class JitConfig extends BaseConfig
private $db_loaded; private $db_loaded;
/** /**
* @param Cache $configCache The configuration cache (based on the config-files) * @param Cache $configCache The configuration cache (based on the config-files)
* @param Model\Config\Config $configModel The configuration model * @param Config $configRepo The configuration model
*/ */
public function __construct(Cache $configCache, Model\Config\Config $configModel) public function __construct(Cache $configCache, Config $configRepo)
{ {
parent::__construct($configCache, $configModel); parent::__construct($configCache, $configRepo);
$this->db_loaded = []; $this->db_loaded = [];
$this->load(); $this->load();
@ -52,16 +52,15 @@ class JitConfig extends BaseConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*
*/ */
public function load(string $cat = 'config') public function load(string $cat = 'config')
{ {
// If not connected, do nothing // If not connected, do nothing
if (!$this->configModel->isConnected()) { if (!$this->configRepo->isConnected()) {
return; return;
} }
$config = $this->configModel->load($cat); $config = $this->configRepo->load($cat);
if (!empty($config[$cat])) { if (!empty($config[$cat])) {
foreach ($config[$cat] as $key => $value) { foreach ($config[$cat] as $key => $value) {
@ -79,15 +78,14 @@ class JitConfig extends BaseConfig
public function get(string $cat, string $key, $default_value = null, bool $refresh = false) public function get(string $cat, string $key, $default_value = null, bool $refresh = false)
{ {
// if the value isn't loaded or refresh is needed, load it to the cache // if the value isn't loaded or refresh is needed, load it to the cache
if ($this->configModel->isConnected() && if ($this->configRepo->isConnected() &&
(empty($this->db_loaded[$cat][$key]) || (empty($this->db_loaded[$cat][$key]) ||
$refresh)) { $refresh)) {
$dbValue = $this->configRepo->get($cat, $key);
$dbvalue = $this->configModel->get($cat, $key); if (isset($dbValue)) {
$this->configCache->set($cat, $key, $dbValue, Cache::SOURCE_DB);
if (isset($dbvalue)) { unset($dbValue);
$this->configCache->set($cat, $key, $dbvalue, Cache::SOURCE_DB);
unset($dbvalue);
} }
$this->db_loaded[$cat][$key] = true; $this->db_loaded[$cat][$key] = true;
@ -102,17 +100,17 @@ class JitConfig extends BaseConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function set(string $cat, string $key, $value) public function set(string $cat, string $key, $value): bool
{ {
// set the cache first // set the cache first
$cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB); $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB);
// If there is no connected adapter, we're finished // If there is no connected adapter, we're finished
if (!$this->configModel->isConnected()) { if (!$this->configRepo->isConnected()) {
return $cached; return $cached;
} }
$stored = $this->configModel->set($cat, $key, $value); $stored = $this->configRepo->set($cat, $key, $value);
$this->db_loaded[$cat][$key] = $stored; $this->db_loaded[$cat][$key] = $stored;
@ -122,7 +120,7 @@ class JitConfig extends BaseConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function delete(string $cat, string $key) public function delete(string $cat, string $key): bool
{ {
$cacheRemoved = $this->configCache->delete($cat, $key); $cacheRemoved = $this->configCache->delete($cat, $key);
@ -130,11 +128,11 @@ class JitConfig extends BaseConfig
unset($this->db_loaded[$cat][$key]); unset($this->db_loaded[$cat][$key]);
} }
if (!$this->configModel->isConnected()) { if (!$this->configRepo->isConnected()) {
return $cacheRemoved; return $cacheRemoved;
} }
$storeRemoved = $this->configModel->delete($cat, $key); $storeRemoved = $this->configRepo->delete($cat, $key);
return $cacheRemoved || $storeRemoved; return $cacheRemoved || $storeRemoved;
} }

View file

@ -19,10 +19,10 @@
* *
*/ */
namespace Friendica\Core\Config; namespace Friendica\Core\Config\Type;
use Friendica\Core\BaseConfig; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Model; use Friendica\Core\Config\Repository\Config;
/** /**
* This class implements the preload configuration, which will cache * This class implements the preload configuration, which will cache
@ -30,18 +30,18 @@ use Friendica\Model;
* *
* Minimizes the number of database queries to retrieve configuration values at the cost of memory. * Minimizes the number of database queries to retrieve configuration values at the cost of memory.
*/ */
class PreloadConfig extends BaseConfig class PreloadConfig extends AbstractConfig
{ {
/** @var bool */ /** @var bool */
private $config_loaded; private $config_loaded;
/** /**
* @param Cache $configCache The configuration cache (based on the config-files) * @param Cache $configCache The configuration cache (based on the config-files)
* @param Model\Config\Config $configModel The configuration model * @param Config $configRepo The configuration model
*/ */
public function __construct(Cache $configCache, Model\Config\Config $configModel) public function __construct(Cache $configCache, Config $configRepo)
{ {
parent::__construct($configCache, $configModel); parent::__construct($configCache, $configRepo);
$this->config_loaded = false; $this->config_loaded = false;
$this->load(); $this->load();
@ -51,7 +51,6 @@ class PreloadConfig extends BaseConfig
* {@inheritDoc} * {@inheritDoc}
* *
* This loads all config values everytime load is called * This loads all config values everytime load is called
*
*/ */
public function load(string $cat = 'config') public function load(string $cat = 'config')
{ {
@ -61,11 +60,11 @@ class PreloadConfig extends BaseConfig
} }
// If not connected, do nothing // If not connected, do nothing
if (!$this->configModel->isConnected()) { if (!$this->configRepo->isConnected()) {
return; return;
} }
$config = $this->configModel->load(); $config = $this->configRepo->load();
$this->config_loaded = true; $this->config_loaded = true;
// load the whole category out of the DB into the cache // load the whole category out of the DB into the cache
@ -78,8 +77,8 @@ class PreloadConfig extends BaseConfig
public function get(string $cat, string $key, $default_value = null, bool $refresh = false) public function get(string $cat, string $key, $default_value = null, bool $refresh = false)
{ {
if ($refresh) { if ($refresh) {
if ($this->configModel->isConnected()) { if ($this->configRepo->isConnected()) {
$config = $this->configModel->get($cat, $key); $config = $this->configRepo->get($cat, $key);
if (isset($config)) { if (isset($config)) {
$this->configCache->set($cat, $key, $config, Cache::SOURCE_DB); $this->configCache->set($cat, $key, $config, Cache::SOURCE_DB);
} }
@ -95,7 +94,7 @@ class PreloadConfig extends BaseConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function set(string $cat, string $key, $value) public function set(string $cat, string $key, $value): bool
{ {
if (!$this->config_loaded) { if (!$this->config_loaded) {
$this->load(); $this->load();
@ -105,11 +104,11 @@ class PreloadConfig extends BaseConfig
$cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB); $cached = $this->configCache->set($cat, $key, $value, Cache::SOURCE_DB);
// If there is no connected adapter, we're finished // If there is no connected adapter, we're finished
if (!$this->configModel->isConnected()) { if (!$this->configRepo->isConnected()) {
return $cached; return $cached;
} }
$stored = $this->configModel->set($cat, $key, $value); $stored = $this->configRepo->set($cat, $key, $value);
return $cached && $stored; return $cached && $stored;
} }
@ -117,7 +116,7 @@ class PreloadConfig extends BaseConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function delete(string $cat, string $key) public function delete(string $cat, string $key): bool
{ {
if ($this->config_loaded) { if ($this->config_loaded) {
$this->load(); $this->load();
@ -125,26 +124,12 @@ class PreloadConfig extends BaseConfig
$cacheRemoved = $this->configCache->delete($cat, $key); $cacheRemoved = $this->configCache->delete($cat, $key);
if (!$this->configModel->isConnected()) { if (!$this->configRepo->isConnected()) {
return $cacheRemoved; return $cacheRemoved;
} }
$storeRemoved = $this->configModel->delete($cat, $key); $storeRemoved = $this->configRepo->delete($cat, $key);
return $cacheRemoved || $storeRemoved; return $cacheRemoved || $storeRemoved;
} }
public function testSetDouble()
{
$this->configModel->shouldReceive('isConnected')
->andReturn(true);
// constructor loading
$this->configModel->shouldReceive('load')
->with('config')
->andReturn(['config' => ['test' => 'it']])
->once();
parent::testSetDouble();
}
} }

View file

@ -19,11 +19,11 @@
* *
*/ */
namespace Friendica\Util; namespace Friendica\Core\Config\Util;
use Exception;
use Friendica\Core\Addon; use Friendica\Core\Addon;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\Exception\ConfigFileException;
use Friendica\Core\Config\ValueObject\Cache;
/** /**
* The ConfigFileLoader loads config-files and stores them in a ConfigCache ( @see Cache ) * The ConfigFileLoader loads config-files and stores them in a ConfigCache ( @see Cache )
@ -91,7 +91,7 @@ class ConfigFileLoader
* @param array $server The $_SERVER array * @param array $server The $_SERVER array
* @param bool $raw Setup the raw config format * @param bool $raw Setup the raw config format
* *
* @throws Exception * @throws ConfigFileException
*/ */
public function setupCache(Cache $config, array $server = [], bool $raw = false) public function setupCache(Cache $config, array $server = [], bool $raw = false)
{ {
@ -122,9 +122,9 @@ class ConfigFileLoader
* *
* @return array The config array (empty if no config found) * @return array The config array (empty if no config found)
* *
* @throws Exception if the configuration file isn't readable * @throws ConfigFileException if the configuration file isn't readable
*/ */
private function loadStaticConfig($name) private function loadStaticConfig(string $name): array
{ {
$configName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.config.php'; $configName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.config.php';
$iniName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.ini.php'; $iniName = $this->staticDir . DIRECTORY_SEPARATOR . $name . '.ini.php';
@ -143,9 +143,7 @@ class ConfigFileLoader
* *
* @param Cache $config The Config cache * @param Cache $config The Config cache
* *
* @return array The config array (empty if no config found) * @throws ConfigFileException if the configuration file isn't readable
*
* @throws Exception if the configuration file isn't readable
*/ */
private function loadCoreConfig(Cache $config) private function loadCoreConfig(Cache $config)
{ {
@ -158,8 +156,6 @@ class ConfigFileLoader
foreach ($this->getConfigFiles() as $configFile) { foreach ($this->getConfigFiles() as $configFile) {
$config->load($this->loadConfigFile($configFile), Cache::SOURCE_FILE); $config->load($this->loadConfigFile($configFile), Cache::SOURCE_FILE);
} }
return [];
} }
/** /**
@ -169,15 +165,15 @@ class ConfigFileLoader
* *
* @return array The config array (empty if no config found) * @return array The config array (empty if no config found)
* *
* @throws Exception if the configuration file isn't readable * @throws ConfigFileException if the configuration file isn't readable
*/ */
public function loadAddonConfig($name) public function loadAddonConfig(string $name): array
{ {
$filepath = $this->baseDir . DIRECTORY_SEPARATOR . // /var/www/html/ $filepath = $this->baseDir . DIRECTORY_SEPARATOR . // /var/www/html/
Addon::DIRECTORY . DIRECTORY_SEPARATOR . // addon/ Addon::DIRECTORY . DIRECTORY_SEPARATOR . // addon/
$name . DIRECTORY_SEPARATOR . // openstreetmap/ $name . DIRECTORY_SEPARATOR . // openstreetmap/
'config'. DIRECTORY_SEPARATOR . // config/ 'config'. DIRECTORY_SEPARATOR . // config/
$name . ".config.php"; // openstreetmap.config.php $name . ".config.php"; // openstreetmap.config.php
if (file_exists($filepath)) { if (file_exists($filepath)) {
return $this->loadConfigFile($filepath); return $this->loadConfigFile($filepath);
@ -193,9 +189,9 @@ class ConfigFileLoader
* *
* @return array The config array (empty if no config was found) * @return array The config array (empty if no config was found)
* *
* @throws Exception if the configuration file isn't readable * @throws ConfigFileException if the configuration file isn't readable
*/ */
public function loadEnvConfig(array $server) public function loadEnvConfig(array $server): array
{ {
$filepath = $this->staticDir . DIRECTORY_SEPARATOR . // /var/www/html/static/ $filepath = $this->staticDir . DIRECTORY_SEPARATOR . // /var/www/html/static/
"env.config.php"; // env.config.php "env.config.php"; // env.config.php
@ -224,10 +220,10 @@ class ConfigFileLoader
* *
* @return array * @return array
*/ */
private function getConfigFiles(bool $ini = false) private function getConfigFiles(bool $ini = false): array
{ {
$files = scandir($this->configDir); $files = scandir($this->configDir);
$found = array(); $found = [];
$filePattern = ($ini ? '*.ini.php' : '*.config.php'); $filePattern = ($ini ? '*.ini.php' : '*.config.php');
@ -252,7 +248,7 @@ class ConfigFileLoader
* *
* @deprecated since version 2018.09 * @deprecated since version 2018.09
*/ */
private function loadLegacyConfig($name = '') private function loadLegacyConfig(string $name = ''): array
{ {
$name = !empty($name) ? $name : self::CONFIG_HTCONFIG; $name = !empty($name) ? $name : self::CONFIG_HTCONFIG;
$fullName = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php'; $fullName = $this->baseDir . DIRECTORY_SEPARATOR . '.' . $name . '.php';
@ -322,17 +318,17 @@ class ConfigFileLoader
* @param string $filepath * @param string $filepath
* *
* @return array The configuration array * @return array The configuration array
* @throws Exception * @throws ConfigFileException
* @deprecated since version 2018.12 * @deprecated since version 2018.12
*/ */
private function loadINIConfigFile($filepath) private function loadINIConfigFile(string $filepath): array
{ {
$contents = include($filepath); $contents = include($filepath);
$config = parse_ini_string($contents, true, INI_SCANNER_TYPED); $config = parse_ini_string($contents, true, INI_SCANNER_TYPED);
if ($config === false) { if ($config === false) {
throw new Exception('Error parsing INI config file ' . $filepath); throw new ConfigFileException('Error parsing INI config file ' . $filepath);
} }
return $config; return $config;
@ -353,14 +349,14 @@ class ConfigFileLoader
* *
* @return array The config array0 * @return array The config array0
* *
* @throws Exception if the config cannot get loaded. * @throws ConfigFileException if the config cannot get loaded.
*/ */
private function loadConfigFile($filepath) private function loadConfigFile(string $filepath): array
{ {
$config = include($filepath); $config = include($filepath);
if (!is_array($config)) { if (!is_array($config)) {
throw new Exception('Error loading config file ' . $filepath); throw new ConfigFileException('Error loading config file ' . $filepath);
} }
return $config; return $config;

View file

@ -0,0 +1,62 @@
<?php
namespace Friendica\Core\Config\Util;
/**
* Util class to help to convert from/to (p)config values
*/
class ValueConversion
{
/**
* Formats a DB value to a config value
* - null = The db-value isn't set
* - bool = The db-value is either '0' or '1'
* - array = The db-value is a serialized array
* - string = The db-value is a string
*
* Keep in mind that there aren't any numeric/integer config values in the database
*
* @param string|null $value
*
* @return null|array|string
*/
public static function toConfigValue(?string $value)
{
if (!isset($value)) {
return null;
}
switch (true) {
// manage array value
case preg_match("|^a:[0-9]+:{.*}$|s", $value):
return unserialize($value);
default:
return $value;
}
}
/**
* Formats a config value to a DB value (string)
*
* @param mixed $value
*
* @return string
*/
public static function toDbValue($value): string
{
// if not set, save an empty string
if (!isset($value)) {
return '';
}
switch (true) {
// manage arrays
case is_array($value):
return serialize($value);
default:
return (string)$value;
}
}
}

View file

@ -19,8 +19,9 @@
* *
*/ */
namespace Friendica\Core\Config; namespace Friendica\Core\Config\ValueObject;
use Friendica\Core\Config\Util\ConfigFileLoader;
use ParagonIE\HiddenString\HiddenString; use ParagonIE\HiddenString\HiddenString;
/** /**
@ -45,7 +46,7 @@ class Cache
/** /**
* @var array * @var array
*/ */
private $config; private $config = [];
/** /**
* @var int[][] * @var int[][]
@ -96,16 +97,16 @@ class Cache
/** /**
* Gets a value from the config cache. * Gets a value from the config cache.
* *
* @param string $cat Config category * @param string $cat Config category
* @param string $key Config key * @param string|null $key Config key
* *
* @return null|mixed Returns the value of the Config entry or null if not set * @return null|mixed Returns the value of the Config entry or null if not set
*/ */
public function get(string $cat, string $key = null) public function get(string $cat, ?string $key = null)
{ {
if (isset($this->config[$cat][$key])) { if (isset($this->config[$cat][$key])) {
return $this->config[$cat][$key]; return $this->config[$cat][$key];
} else if (!isset($key) && isset($this->config[$cat])) { } elseif (!isset($key) && isset($this->config[$cat])) {
return $this->config[$cat]; return $this->config[$cat];
} else { } else {
return null; return null;
@ -122,7 +123,7 @@ class Cache
* *
* @return bool True, if the value is set * @return bool True, if the value is set
*/ */
public function set(string $cat, string $key, $value, $source = self::SOURCE_DEFAULT) public function set(string $cat, string $key, $value, int $source = self::SOURCE_DEFAULT): bool
{ {
if (!isset($this->config[$cat])) { if (!isset($this->config[$cat])) {
$this->config[$cat] = []; $this->config[$cat] = [];
@ -155,7 +156,7 @@ class Cache
* *
* @return bool true, if deleted * @return bool true, if deleted
*/ */
public function delete(string $cat, string $key) public function delete(string $cat, string $key): bool
{ {
if (isset($this->config[$cat][$key])) { if (isset($this->config[$cat][$key])) {
unset($this->config[$cat][$key]); unset($this->config[$cat][$key]);
@ -173,9 +174,9 @@ class Cache
/** /**
* Returns the whole configuration * Returns the whole configuration
* *
* @return array The configuration * @return string[][] The configuration
*/ */
public function getAll() public function getAll(): array
{ {
return $this->config; return $this->config;
} }
@ -183,11 +184,11 @@ class Cache
/** /**
* Returns an array with missing categories/Keys * Returns an array with missing categories/Keys
* *
* @param array $config The array to check * @param string[][] $config The array to check
* *
* @return array * @return string[][]
*/ */
public function keyDiff(array $config) public function keyDiff(array $config): array
{ {
$return = []; $return = [];

View file

@ -23,7 +23,7 @@ namespace Friendica\Core;
use DOMDocument; use DOMDocument;
use Exception; use Exception;
use Friendica\Core\Config\Cache; use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Database\DBStructure; use Friendica\Database\DBStructure;
use Friendica\DI; use Friendica\DI;
@ -278,7 +278,7 @@ class Installer
$cmd = "$phppath -v"; $cmd = "$phppath -v";
$result = trim(shell_exec($cmd)); $result = trim(shell_exec($cmd));
$passed2 = (strpos($result, "(cli)") !== false); $passed2 = (strpos($result, "(cli)") !== false);
list($result) = explode("\n", $result); [$result] = explode("\n", $result);
$help = ""; $help = "";
if (!$passed2) { if (!$passed2) {
$help .= DI::l10n()->t("PHP executable is not the php cli binary \x28could be cgi-fgci version\x29") . EOL; $help .= DI::l10n()->t("PHP executable is not the php cli binary \x28could be cgi-fgci version\x29") . EOL;
@ -678,8 +678,8 @@ class Installer
/** /**
* Setup the default cache for a new installation * Setup the default cache for a new installation
* *
* @param Cache $configCache The configuration cache * @param \Friendica\Core\Config\ValueObject\Cache $configCache The configuration cache
* @param string $basePath The determined basepath * @param string $basePath The determined basepath
* *
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */

View file

@ -21,8 +21,8 @@
namespace Friendica\Core; namespace Friendica\Core;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Session\ISession; use Friendica\Core\Session\Capability\IHandleSessions;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -62,7 +62,7 @@ class L10n
*/ */
private $logger; private $logger;
public function __construct(IConfig $config, Database $dba, LoggerInterface $logger, ISession $session, array $server, array $get) public function __construct(IManageConfigValues $config, Database $dba, LoggerInterface $logger, IHandleSessions $session, array $server, array $get)
{ {
$this->dba = $dba; $this->dba = $dba;
$this->logger = $logger; $this->logger = $logger;
@ -85,7 +85,7 @@ class L10n
/** /**
* Sets the language session variable * Sets the language session variable
*/ */
private function setSessionVariable(ISession $session) private function setSessionVariable(IHandleSessions $session)
{ {
if ($session->get('authenticated') && !$session->get('language')) { if ($session->get('authenticated') && !$session->get('language')) {
$session->set('language', $this->lang); $session->set('language', $this->lang);
@ -103,7 +103,7 @@ class L10n
} }
} }
private function setLangFromSession(ISession $session) private function setLangFromSession(IHandleSessions $session)
{ {
if ($session->get('language') !== $this->lang) { if ($session->get('language') !== $this->lang) {
$this->loadTranslationTable($session->get('language')); $this->loadTranslationTable($session->get('language'));

View file

@ -1,162 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Lock;
use Friendica\Core\BaseLock;
use Friendica\Core\Cache\Duration;
use Friendica\Core\Cache\IMemoryCache;
class CacheLock extends BaseLock
{
/**
* @var string The static prefix of all locks inside the cache
*/
const CACHE_PREFIX = 'lock:';
/**
* @var \Friendica\Core\Cache\ICache;
*/
private $cache;
/**
* CacheLock constructor.
*
* @param IMemoryCache $cache The CacheDriver for this type of lock
*/
public function __construct(IMemoryCache $cache)
{
$this->cache = $cache;
}
/**
* (@inheritdoc)
*/
public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES)
{
$got_lock = false;
$start = time();
$cachekey = self::getLockKey($key);
do {
$lock = $this->cache->get($cachekey);
// When we do want to lock something that was already locked by us.
if ((int)$lock == getmypid()) {
$got_lock = true;
}
// When we do want to lock something new
if (is_null($lock)) {
// At first initialize it with "0"
$this->cache->add($cachekey, 0);
// Now the value has to be "0" because otherwise the key was used by another process meanwhile
if ($this->cache->compareSet($cachekey, 0, getmypid(), $ttl)) {
$got_lock = true;
$this->markAcquire($key);
}
}
if (!$got_lock && ($timeout > 0)) {
usleep(rand(10000, 200000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
return $got_lock;
}
/**
* (@inheritdoc)
*/
public function release($key, $override = false)
{
$cachekey = self::getLockKey($key);
if ($override) {
$return = $this->cache->delete($cachekey);
} else {
$return = $this->cache->compareDelete($cachekey, getmypid());
}
$this->markRelease($key);
return $return;
}
/**
* (@inheritdoc)
*/
public function isLocked($key)
{
$cachekey = self::getLockKey($key);
$lock = $this->cache->get($cachekey);
return isset($lock) && ($lock !== false);
}
/**
* {@inheritDoc}
*/
public function getName()
{
return $this->cache->getName();
}
/**
* {@inheritDoc}
*/
public function getLocks(string $prefix = '')
{
$locks = $this->cache->getAllKeys(self::CACHE_PREFIX . $prefix);
array_walk($locks, function (&$lock, $key) {
$lock = substr($lock, strlen(self::CACHE_PREFIX));
});
return $locks;
}
/**
* {@inheritDoc}
*/
public function releaseAll($override = false)
{
$success = parent::releaseAll($override);
$locks = $this->getLocks();
foreach ($locks as $lock) {
if (!$this->release($lock, $override)) {
$success = false;
}
}
return $success;
}
/**
* @param string $key The original key
*
* @return string The cache key used for the cache
*/
private static function getLockKey($key)
{
return self::CACHE_PREFIX . $key;
}
}

View file

@ -19,23 +19,22 @@
* *
*/ */
namespace Friendica\Core\Lock; namespace Friendica\Core\Lock\Capability;
use Friendica\Core\Cache\Duration; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Lock\Exception\LockPersistenceException;
/** /**
* Lock Interface * Lock Interface
*/ */
interface ILock interface ICanLock
{ {
/** /**
* Checks, if a key is currently locked to a or my process * Checks, if a key is currently locked to a or my process
* *
* @param string $key The name of the lock * @param string $key The name of the lock
*
* @return bool
*/ */
public function isLocked($key); public function isLocked(string $key): bool;
/** /**
* *
@ -45,9 +44,9 @@ interface ILock
* @param integer $timeout Seconds until we give up * @param integer $timeout Seconds until we give up
* @param integer $ttl Seconds The lock lifespan, must be one of the Cache constants * @param integer $ttl Seconds The lock lifespan, must be one of the Cache constants
* *
* @return boolean Was the lock successful? * @throws LockPersistenceException In case the underlying persistence throws errors
*/ */
public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES); public function acquire(string $key, int $timeout = 120, int $ttl = Duration::FIVE_MINUTES): bool;
/** /**
* Releases a lock if it was set by us * Releases a lock if it was set by us
@ -55,32 +54,36 @@ interface ILock
* @param string $key The Name of the lock * @param string $key The Name of the lock
* @param bool $override Overrides the lock to get released * @param bool $override Overrides the lock to get released
* *
* @return boolean Was the unlock successful? * @return bool Was the unlock successful?
*
* @throws LockPersistenceException In case the underlying persistence throws errors
*/ */
public function release($key, $override = false); public function release(string $key, bool $override = false): bool;
/** /**
* Releases all lock that were set by us * Releases all lock that were set by us
* *
* @param bool $override Override to release all locks * @param bool $override Override to release all locks
* *
* @return boolean Was the unlock of all locks successful? * @return bool Was the unlock of all locks successful?
*
* @throws LockPersistenceException In case the underlying persistence throws errors
*/ */
public function releaseAll($override = false); public function releaseAll(bool $override = false): bool;
/** /**
* Returns the name of the current lock * Returns the name of the current lock
*
* @return string
*/ */
public function getName(); public function getName(): string;
/** /**
* Lists all locks * Lists all locks
* *
* @param string prefix optional a prefix to search * @param string prefix optional a prefix to search
* *
* @return array Empty if it isn't supported by the cache driver * @return string[] Empty if it isn't supported by the cache driver
*
* @throws LockPersistenceException In case the underlying persistence throws errors
*/ */
public function getLocks(string $prefix = ''); public function getLocks(string $prefix = ''): array;
} }

View file

@ -1,178 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Lock;
use Friendica\Core\BaseLock;
use Friendica\Core\Cache\Duration;
use Friendica\Database\Database;
use Friendica\Util\DateTimeFormat;
/**
* Locking driver that stores the locks in the database
*/
class DatabaseLock extends BaseLock
{
/**
* The current ID of the process
*
* @var int
*/
private $pid;
/**
* @var Database The database connection of Friendica
*/
private $dba;
/**
* @param null|int $pid The Id of the current process (null means determine automatically)
*/
public function __construct(Database $dba, $pid = null)
{
$this->dba = $dba;
$this->pid = isset($pid) ? $pid : getmypid();
}
/**
* (@inheritdoc)
*/
public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES)
{
$got_lock = false;
$start = time();
do {
$this->dba->lock('locks');
$lock = $this->dba->selectFirst('locks', ['locked', 'pid'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
if ($this->dba->isResult($lock)) {
if ($lock['locked']) {
// We want to lock something that was already locked by us? So we got the lock.
if ($lock['pid'] == $this->pid) {
$got_lock = true;
}
}
if (!$lock['locked']) {
$this->dba->update('locks', ['locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]);
$got_lock = true;
}
} else {
$this->dba->insert('locks', ['name' => $key, 'locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]);
$got_lock = true;
$this->markAcquire($key);
}
$this->dba->unlock();
if (!$got_lock && ($timeout > 0)) {
usleep(rand(100000, 2000000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
return $got_lock;
}
/**
* (@inheritdoc)
*/
public function release($key, $override = false)
{
if ($override) {
$where = ['name' => $key];
} else {
$where = ['name' => $key, 'pid' => $this->pid];
}
if ($this->dba->exists('locks', $where)) {
$return = $this->dba->delete('locks', $where);
} else {
$return = false;
}
$this->markRelease($key);
return $return;
}
/**
* (@inheritdoc)
*/
public function releaseAll($override = false)
{
$success = parent::releaseAll($override);
if ($override) {
$where = ['1 = 1'];
} else {
$where = ['pid' => $this->pid];
}
$return = $this->dba->delete('locks', $where);
$this->acquiredLocks = [];
return $return && $success;
}
/**
* (@inheritdoc)
*/
public function isLocked($key)
{
$lock = $this->dba->selectFirst('locks', ['locked'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
if ($this->dba->isResult($lock)) {
return $lock['locked'] !== false;
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
public function getName()
{
return Type::DATABASE;
}
/**
* {@inheritDoc}
*/
public function getLocks(string $prefix = '')
{
if (empty($prefix)) {
$where = ['`expires` >= ?', DateTimeFormat::utcNow()];
} else {
$where = ['`expires` >= ? AND `name` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
}
$stmt = $this->dba->select('locks', ['name'], $where);
$keys = [];
while ($key = $this->dba->fetch($stmt)) {
array_push($keys, $key['name']);
}
$this->dba->close($stmt);
return $keys;
}
}

View file

@ -19,9 +19,9 @@
* *
*/ */
namespace Friendica\Core\Lock; namespace Friendica\Core\Lock\Enum;
use Friendica\Core\Cache\Type as CacheType; use Friendica\Core\Cache\Enum\Type as CacheType;
/** /**
* Enumeration for lock types * Enumeration for lock types

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Lock\Exception;
use Throwable;
class InvalidLockDriverException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Lock\Exception;
use Throwable;
class LockPersistenceException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -19,12 +19,14 @@
* *
*/ */
namespace Friendica\Factory; namespace Friendica\Core\Lock\Factory;
use Friendica\Core\Cache\IMemoryCache; use Friendica\Core\Cache\Factory\Cache;
use Friendica\Core\Cache\Type; use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Config\IConfig; use Friendica\Core\Cache\Enum;
use Friendica\Core\Lock; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Lock\Capability\ICanLock;
use Friendica\Core\Lock\Type;
use Friendica\Database\Database; use Friendica\Database\Database;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -35,7 +37,7 @@ use Psr\Log\LoggerInterface;
* *
* A basic class to generate a LockDriver * A basic class to generate a LockDriver
*/ */
class LockFactory class Lock
{ {
/** /**
* @var string The default driver for caching * @var string The default driver for caching
@ -43,7 +45,7 @@ class LockFactory
const DEFAULT_DRIVER = 'default'; const DEFAULT_DRIVER = 'default';
/** /**
* @var IConfig The configuration to read parameters out of the config * @var IManageConfigValues The configuration to read parameters out of the config
*/ */
private $config; private $config;
@ -53,7 +55,7 @@ class LockFactory
private $dba; private $dba;
/** /**
* @var CacheFactory The memory cache driver in case we use it * @var Cache The memory cache driver in case we use it
*/ */
private $cacheFactory; private $cacheFactory;
@ -62,7 +64,7 @@ class LockFactory
*/ */
private $logger; private $logger;
public function __construct(CacheFactory $cacheFactory, IConfig $config, Database $dba, LoggerInterface $logger) public function __construct(Cache $cacheFactory, IManageConfigValues $config, Database $dba, LoggerInterface $logger)
{ {
$this->cacheFactory = $cacheFactory; $this->cacheFactory = $cacheFactory;
$this->config = $config; $this->config = $config;
@ -76,24 +78,24 @@ class LockFactory
try { try {
switch ($lock_type) { switch ($lock_type) {
case Type::MEMCACHE: case Enum\Type::MEMCACHE:
case Type::MEMCACHED: case Enum\Type::MEMCACHED:
case Type::REDIS: case Enum\Type::REDIS:
case Type::APCU: case Enum\Type::APCU:
$cache = $this->cacheFactory->create($lock_type); $cache = $this->cacheFactory->create($lock_type);
if ($cache instanceof IMemoryCache) { if ($cache instanceof ICanCacheInMemory) {
return new Lock\CacheLock($cache); return new Type\CacheLock($cache);
} else { } else {
throw new \Exception(sprintf('Incompatible cache driver \'%s\' for lock used', $lock_type)); throw new \Exception(sprintf('Incompatible cache driver \'%s\' for lock used', $lock_type));
} }
break; break;
case 'database': case 'database':
return new Lock\DatabaseLock($this->dba); return new Type\DatabaseLock($this->dba);
break; break;
case 'semaphore': case 'semaphore':
return new Lock\SemaphoreLock(); return new Type\SemaphoreLock();
break; break;
default: default:
@ -113,14 +115,14 @@ class LockFactory
* 2. Cache Locking * 2. Cache Locking
* 3. Database Locking * 3. Database Locking
* *
* @return Lock\ILock * @return ICanLock
*/ */
private function useAutoDriver() private function useAutoDriver()
{ {
// 1. Try to use Semaphores for - local - locking // 1. Try to use Semaphores for - local - locking
if (function_exists('sem_get')) { if (function_exists('sem_get')) {
try { try {
return new Lock\SemaphoreLock(); return new Type\SemaphoreLock();
} catch (\Exception $exception) { } catch (\Exception $exception) {
$this->logger->warning('Using Semaphore driver for locking failed.', ['exception' => $exception]); $this->logger->warning('Using Semaphore driver for locking failed.', ['exception' => $exception]);
} }
@ -128,11 +130,11 @@ class LockFactory
// 2. Try to use Cache Locking (don't use the DB-Cache Locking because it works different!) // 2. Try to use Cache Locking (don't use the DB-Cache Locking because it works different!)
$cache_type = $this->config->get('system', 'cache_driver', 'database'); $cache_type = $this->config->get('system', 'cache_driver', 'database');
if ($cache_type != Type::DATABASE) { if ($cache_type != Enum\Type::DATABASE) {
try { try {
$cache = $this->cacheFactory->create($cache_type); $cache = $this->cacheFactory->create($cache_type);
if ($cache instanceof IMemoryCache) { if ($cache instanceof ICanCacheInMemory) {
return new Lock\CacheLock($cache); return new Type\CacheLock($cache);
} }
} catch (\Exception $exception) { } catch (\Exception $exception) {
$this->logger->warning('Using Cache driver for locking failed.', ['exception' => $exception]); $this->logger->warning('Using Cache driver for locking failed.', ['exception' => $exception]);
@ -140,6 +142,6 @@ class LockFactory
} }
// 3. Use Database Locking as a Fallback // 3. Use Database Locking as a Fallback
return new Lock\DatabaseLock($this->dba); return new Type\DatabaseLock($this->dba);
} }
} }

View file

@ -19,14 +19,14 @@
* *
*/ */
namespace Friendica\Core; namespace Friendica\Core\Lock\Type;
use Friendica\Core\Lock\ILock; use Friendica\Core\Lock\Capability\ICanLock;
/** /**
* Basic class for Locking with common functions (local acquired locks, releaseAll, ..) * Basic class for Locking with common functions (local acquired locks, releaseAll, ..)
*/ */
abstract class BaseLock implements ILock abstract class AbstractLock implements ICanLock
{ {
/** /**
* @var array The local acquired locks * @var array The local acquired locks
@ -40,7 +40,7 @@ abstract class BaseLock implements ILock
* *
* @return bool Returns true if the lock is set * @return bool Returns true if the lock is set
*/ */
protected function hasAcquiredLock($key) protected function hasAcquiredLock(string $key): bool
{ {
return isset($this->acquireLock[$key]) && $this->acquiredLocks[$key] === true; return isset($this->acquireLock[$key]) && $this->acquiredLocks[$key] === true;
} }
@ -50,7 +50,7 @@ abstract class BaseLock implements ILock
* *
* @param string $key The Name of the lock * @param string $key The Name of the lock
*/ */
protected function markAcquire($key) protected function markAcquire(string $key)
{ {
$this->acquiredLocks[$key] = true; $this->acquiredLocks[$key] = true;
} }
@ -60,7 +60,7 @@ abstract class BaseLock implements ILock
* *
* @param string $key The Name of the lock * @param string $key The Name of the lock
*/ */
protected function markRelease($key) protected function markRelease(string $key)
{ {
unset($this->acquiredLocks[$key]); unset($this->acquiredLocks[$key]);
} }
@ -68,7 +68,7 @@ abstract class BaseLock implements ILock
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function releaseAll($override = false) public function releaseAll(bool $override = false): bool
{ {
$return = true; $return = true;

View file

@ -0,0 +1,180 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Lock\Type;
use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Core\Lock\Exception\LockPersistenceException;
class CacheLock extends AbstractLock
{
/**
* @var string The static prefix of all locks inside the cache
*/
const CACHE_PREFIX = 'lock:';
/**
* @var ICanCache;
*/
private $cache;
/**
* CacheLock constructor.
*
* @param ICanCacheInMemory $cache The CacheDriver for this type of lock
*/
public function __construct(ICanCacheInMemory $cache)
{
$this->cache = $cache;
}
/**
* (@inheritdoc)
*/
public function acquire(string $key, int $timeout = 120, int $ttl = Duration::FIVE_MINUTES): bool
{
$got_lock = false;
$start = time();
$lockKey = self::getLockKey($key);
try {
do {
$lock = $this->cache->get($lockKey);
// When we do want to lock something that was already locked by us.
if ((int)$lock == getmypid()) {
$got_lock = true;
}
// When we do want to lock something new
if (is_null($lock)) {
// At first initialize it with "0"
$this->cache->add($lockKey, 0);
// Now the value has to be "0" because otherwise the key was used by another process meanwhile
if ($this->cache->compareSet($lockKey, 0, getmypid(), $ttl)) {
$got_lock = true;
$this->markAcquire($key);
}
}
if (!$got_lock && ($timeout > 0)) {
usleep(rand(10000, 200000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
} catch (CachePersistenceException $exception) {
throw new LockPersistenceException(sprintf('Cannot acquire lock for key %s', $key), $exception);
}
return $got_lock;
}
/**
* (@inheritdoc)
*/
public function release(string $key, bool $override = false): bool
{
$lockKey = self::getLockKey($key);
try {
if ($override) {
$return = $this->cache->delete($lockKey);
} else {
$return = $this->cache->compareDelete($lockKey, getmypid());
}
} catch (CachePersistenceException $exception) {
throw new LockPersistenceException(sprintf('Cannot release lock for key %s (override %b)', $key, $override), $exception);
}
$this->markRelease($key);
return $return;
}
/**
* (@inheritdoc)
*/
public function isLocked(string $key): bool
{
$lockKey = self::getLockKey($key);
try {
$lock = $this->cache->get($lockKey);
} catch (CachePersistenceException $exception) {
throw new LockPersistenceException(sprintf('Cannot check lock state for key %s', $key), $exception);
}
return isset($lock) && ($lock !== false);
}
/**
* {@inheritDoc}
*/
public function getName(): string
{
return $this->cache->getName();
}
/**
* {@inheritDoc}
*/
public function getLocks(string $prefix = ''): array
{
try {
$locks = $this->cache->getAllKeys(self::CACHE_PREFIX . $prefix);
} catch (CachePersistenceException $exception) {
throw new LockPersistenceException(sprintf('Cannot get locks with prefix %s', $prefix), $exception);
}
array_walk($locks, function (&$lock) {
$lock = substr($lock, strlen(self::CACHE_PREFIX));
});
return $locks;
}
/**
* {@inheritDoc}
*/
public function releaseAll(bool $override = false): bool
{
$success = parent::releaseAll($override);
$locks = $this->getLocks();
foreach ($locks as $lock) {
if (!$this->release($lock, $override)) {
$success = false;
}
}
return $success;
}
/**
* @param string $key The original key
*
* @return string The cache key used for the cache
*/
private static function getLockKey(string $key): string
{
return self::CACHE_PREFIX . $key;
}
}

View file

@ -0,0 +1,212 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\Lock\Type;
use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Lock\Enum\Type;
use Friendica\Core\Lock\Exception\LockPersistenceException;
use Friendica\Database\Database;
use Friendica\Util\DateTimeFormat;
/**
* Locking driver that stores the locks in the database
*/
class DatabaseLock extends AbstractLock
{
/**
* The current ID of the process
*
* @var int
*/
private $pid;
/**
* @var Database The database connection of Friendica
*/
private $dba;
/**
* @param int|null $pid The id of the current process (null means determine automatically)
*/
public function __construct(Database $dba, ?int $pid = null)
{
$this->dba = $dba;
$this->pid = $pid ?? getmypid();
}
/**
* (@inheritdoc)
*/
public function acquire(string $key, int $timeout = 120, int $ttl = Duration::FIVE_MINUTES): bool
{
$got_lock = false;
$start = time();
try {
do {
$this->dba->lock('locks');
$lock = $this->dba->selectFirst('locks', ['locked', 'pid'], [
'`name` = ? AND `expires` >= ?', $key,DateTimeFormat::utcNow()
]);
if ($this->dba->isResult($lock)) {
if ($lock['locked']) {
// We want to lock something that was already locked by us? So we got the lock.
if ($lock['pid'] == $this->pid) {
$got_lock = true;
}
}
if (!$lock['locked']) {
$this->dba->update('locks', [
'locked' => true,
'pid' => $this->pid,
'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')
], ['name' => $key]);
$got_lock = true;
}
} else {
$this->dba->insert('locks', [
'name' => $key,
'locked' => true,
'pid' => $this->pid,
'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]);
$got_lock = true;
$this->markAcquire($key);
}
$this->dba->unlock();
if (!$got_lock && ($timeout > 0)) {
usleep(rand(100000, 2000000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
} catch (\Exception $exception) {
throw new LockPersistenceException(sprintf('Cannot acquire lock for key %s', $key), $exception);
}
return $got_lock;
}
/**
* (@inheritdoc)
*/
public function release(string $key, bool $override = false): bool
{
if ($override) {
$where = ['name' => $key];
} else {
$where = ['name' => $key, 'pid' => $this->pid];
}
try {
if ($this->dba->exists('locks', $where)) {
$return = $this->dba->delete('locks', $where);
} else {
$return = false;
}
} catch (\Exception $exception) {
throw new LockPersistenceException(sprintf('Cannot release lock for key %s (override %b)', $key, $override), $exception);
}
$this->markRelease($key);
return $return;
}
/**
* (@inheritdoc)
*/
public function releaseAll(bool $override = false): bool
{
$success = parent::releaseAll($override);
if ($override) {
$where = ['1 = 1'];
} else {
$where = ['pid' => $this->pid];
}
try {
$return = $this->dba->delete('locks', $where);
} catch (\Exception $exception) {
throw new LockPersistenceException(sprintf('Cannot release all lock (override %b)', $override), $exception);
}
$this->acquiredLocks = [];
return $return && $success;
}
/**
* (@inheritdoc)
*/
public function isLocked(string $key): bool
{
try {
$lock = $this->dba->selectFirst('locks', ['locked'], [
'`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
} catch (\Exception $exception) {
throw new LockPersistenceException(sprintf('Cannot check lock state for key %s', $key), $exception);
}
if ($this->dba->isResult($lock)) {
return $lock['locked'] !== false;
} else {
return false;
}
}
/**
* {@inheritDoc}
*/
public function getName(): string
{
return Type::DATABASE;
}
/**
* {@inheritDoc}
*/
public function getLocks(string $prefix = ''): array
{
try {
if (empty($prefix)) {
$where = ['`expires` >= ?', DateTimeFormat::utcNow()];
} else {
$where = ['`expires` >= ? AND `name` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
}
$stmt = $this->dba->select('locks', ['name'], $where);
$keys = [];
while ($key = $this->dba->fetch($stmt)) {
array_push($keys, $key['name']);
}
} catch (\Exception $exception) {
throw new LockPersistenceException(sprintf('Cannot get lock with prefix %s', $prefix), $exception);
} finally {
$this->dba->close($stmt);
}
return $keys;
}
}

View file

@ -19,19 +19,21 @@
* *
*/ */
namespace Friendica\Core\Lock; namespace Friendica\Core\Lock\Type;
use Friendica\Core\BaseLock; use Friendica\Core\Cache\Enum\Duration;
use Friendica\Core\Cache\Duration; use Friendica\Core\Lock\Enum\Type;
use Friendica\Core\Lock\Exception\InvalidLockDriverException;
use function get_temppath;
class SemaphoreLock extends BaseLock class SemaphoreLock extends AbstractLock
{ {
private static $semaphore = []; private static $semaphore = [];
public function __construct() public function __construct()
{ {
if (!function_exists('sem_get')) { if (!function_exists('sem_get')) {
throw new \Exception('Semaphore lock not supported'); throw new InvalidLockDriverException('Semaphore lock not supported');
} }
} }
@ -56,11 +58,11 @@ class SemaphoreLock extends BaseLock
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES) public function acquire(string $key, int $timeout = 120, int $ttl = Duration::FIVE_MINUTES): bool
{ {
self::$semaphore[$key] = sem_get(self::semaphoreKey($key)); self::$semaphore[$key] = sem_get(self::semaphoreKey($key));
if (!empty(self::$semaphore[$key])) { if (!empty(self::$semaphore[$key])) {
if ((bool)sem_acquire(self::$semaphore[$key], ($timeout === 0))) { if (sem_acquire(self::$semaphore[$key], ($timeout === 0))) {
$this->markAcquire($key); $this->markAcquire($key);
return true; return true;
} }
@ -75,7 +77,7 @@ class SemaphoreLock extends BaseLock
* @param bool $override not necessary parameter for semaphore locks since the lock lives as long as the execution * @param bool $override not necessary parameter for semaphore locks since the lock lives as long as the execution
* of the using function * of the using function
*/ */
public function release($key, $override = false) public function release(string $key, bool $override = false): bool
{ {
$success = false; $success = false;
@ -95,7 +97,7 @@ class SemaphoreLock extends BaseLock
/** /**
* (@inheritdoc) * (@inheritdoc)
*/ */
public function isLocked($key) public function isLocked(string $key): bool
{ {
return isset(self::$semaphore[$key]); return isset(self::$semaphore[$key]);
} }
@ -103,7 +105,7 @@ class SemaphoreLock extends BaseLock
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getName() public function getName(): string
{ {
return Type::SEMAPHORE; return Type::SEMAPHORE;
} }
@ -111,7 +113,7 @@ class SemaphoreLock extends BaseLock
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getLocks(string $prefix = '') public function getLocks(string $prefix = ''): array
{ {
// We can just return our own semaphore keys, since we don't know // We can just return our own semaphore keys, since we don't know
// the state of other semaphores, even if the .sem files exists // the state of other semaphores, even if the .sem files exists
@ -135,7 +137,7 @@ class SemaphoreLock extends BaseLock
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function releaseAll($override = false) public function releaseAll(bool $override = false): bool
{ {
// Semaphores are just alive during a run, so there is no need to release // Semaphores are just alive during a run, so there is no need to release
// You can just release your own locks // You can just release your own locks

View file

@ -22,7 +22,7 @@
namespace Friendica\Core; namespace Friendica\Core;
use Friendica\DI; use Friendica\DI;
use Friendica\Util\Logger\WorkerLogger; use Friendica\Core\Logger\Type\WorkerLogger;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Logger\Exception;
use Throwable;
class LogLevelException extends \InvalidArgumentException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Logger\Exception;
use Throwable;
class LoggerArgumentException extends \InvalidArgumentException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\Logger\Exception;
use Throwable;
class LoggerException extends \Exception
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -19,45 +19,45 @@
* *
*/ */
namespace Friendica\Factory; namespace Friendica\Core\Logger\Factory;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Logger; use Friendica\Core\Logger\Exception\LoggerException;
use Friendica\Core;
use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Util\FileSystem; use Friendica\Util\FileSystem;
use Friendica\Util\Introspection; use Friendica\Core\Logger\Util\Introspection;
use Friendica\Util\Logger\Monolog\DevelopHandler; use Friendica\Core\Logger\Type\Monolog\DevelopHandler;
use Friendica\Util\Logger\Monolog\IntrospectionProcessor; use Friendica\Core\Logger\Type\Monolog\IntrospectionProcessor;
use Friendica\Util\Logger\ProfilerLogger; use Friendica\Core\Logger\Type\ProfilerLogger;
use Friendica\Util\Logger\StreamLogger; use Friendica\Core\Logger\Type\StreamLogger;
use Friendica\Util\Logger\SyslogLogger; use Friendica\Core\Logger\Type\SyslogLogger;
use Friendica\Util\Logger\VoidLogger;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Monolog; use Monolog;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use Psr\Log\NullLogger;
/** /**
* A logger factory * A logger factory
*
* Currently only Monolog is supported
*/ */
class LoggerFactory class Logger
{ {
const DEV_CHANNEL = 'dev'; const DEV_CHANNEL = 'dev';
/** /**
* A list of classes, which shouldn't get logged * A list of classes, which shouldn't get logged
* *
* @var array * @var string[]
*/ */
private static $ignoreClassList = [ private static $ignoreClassList = [
Logger::class, Core\Logger::class,
Profiler::class, Profiler::class,
'Friendica\\Util\\Logger', 'Friendica\\Core\\Logger\\Type',
]; ];
/** @var string The log-channel (app, worker, ...) */
private $channel; private $channel;
public function __construct(string $channel) public function __construct(string $channel)
@ -68,24 +68,25 @@ class LoggerFactory
/** /**
* Creates a new PSR-3 compliant logger instances * Creates a new PSR-3 compliant logger instances
* *
* @param Database $database The Friendica Database instance * @param Database $database The Friendica Database instance
* @param IConfig $config The config * @param IManageConfigValues $config The config
* @param Profiler $profiler The profiler of the app * @param Profiler $profiler The profiler of the app
* @param FileSystem $fileSystem FileSystem utils * @param FileSystem $fileSystem FileSystem utils
* @param string|null $minLevel (optional) Override minimum Loglevel to log
* *
* @return LoggerInterface The PSR-3 compliant logger instance * @return LoggerInterface The PSR-3 compliant logger instance
*/ */
public function create(Database $database, IConfig $config, Profiler $profiler, FileSystem $fileSystem) public function create(Database $database, IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem, ?string $minLevel = null): LoggerInterface
{ {
if (empty($config->get('system', 'debugging', false))) { if (empty($config->get('system', 'debugging', false))) {
$logger = new VoidLogger(); $logger = new NullLogger();
$database->setLogger($logger); $database->setLogger($logger);
return $logger; return $logger;
} }
$introspection = new Introspection(self::$ignoreClassList); $introspection = new Introspection(self::$ignoreClassList);
$level = $config->get('system', 'loglevel'); $minLevel = $minLevel ?? $config->get('system', 'loglevel');
$loglevel = self::mapLegacyConfigDebugLevel((string)$level); $loglevel = self::mapLegacyConfigDebugLevel((string)$minLevel);
switch ($config->get('system', 'logger_config', 'stream')) { switch ($config->get('system', 'logger_config', 'stream')) {
case 'monolog': case 'monolog':
@ -106,7 +107,12 @@ class LoggerFactory
static::addStreamHandler($logger, $stream, $loglevel); static::addStreamHandler($logger, $stream, $loglevel);
} catch (\Throwable $e) { } catch (\Throwable $e) {
// No Logger .. // No Logger ..
$logger = new VoidLogger(); try {
$logger = new SyslogLogger($this->channel, $introspection, $loglevel);
} catch (\Throwable $e) {
// No logger ...
$logger = new NullLogger();
}
} }
} }
break; break;
@ -114,9 +120,13 @@ class LoggerFactory
case 'syslog': case 'syslog':
try { try {
$logger = new SyslogLogger($this->channel, $introspection, $loglevel); $logger = new SyslogLogger($this->channel, $introspection, $loglevel);
} catch (LogLevelException $exception) {
// If there's a wrong config value for loglevel, try again with standard
$logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE);
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
} catch (\Throwable $e) { } catch (\Throwable $e) {
// No logger ... // No logger ...
$logger = new VoidLogger(); $logger = new NullLogger();
} }
break; break;
@ -127,12 +137,25 @@ class LoggerFactory
if (!is_file($stream) || is_writable($stream)) { if (!is_file($stream) || is_writable($stream)) {
try { try {
$logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel); $logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel);
} catch (LogLevelException $exception) {
// If there's a wrong config value for loglevel, try again with standard
$logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE);
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
} catch (\Throwable $t) { } catch (\Throwable $t) {
// No logger ... // No logger ...
$logger = new VoidLogger(); $logger = new NullLogger();
} }
} else { } else {
$logger = new VoidLogger(); try {
$logger = new SyslogLogger($this->channel, $introspection, $loglevel);
} catch (LogLevelException $exception) {
// If there's a wrong config value for loglevel, try again with standard
$logger = $this->create($database, $config, $profiler, $fileSystem, LogLevel::NOTICE);
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
} catch (\Throwable $e) {
// No logger ...
$logger = new NullLogger();
}
} }
break; break;
} }
@ -156,25 +179,22 @@ class LoggerFactory
* *
* It should never get filled during normal usage of Friendica * It should never get filled during normal usage of Friendica
* *
* @param IConfig $config The config * @param IManageConfigValues $config The config
* @param Profiler $profiler The profiler of the app * @param Profiler $profiler The profiler of the app
* @param FileSystem $fileSystem FileSystem utils * @param FileSystem $fileSystem FileSystem utils
* *
* @return LoggerInterface The PSR-3 compliant logger instance * @return LoggerInterface The PSR-3 compliant logger instance
*
* @throws InternalServerErrorException
* @throws \Exception * @throws \Exception
*/ */
public static function createDev(IConfig $config, Profiler $profiler, FileSystem $fileSystem) public static function createDev(IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem)
{ {
$debugging = $config->get('system', 'debugging'); $debugging = $config->get('system', 'debugging');
$stream = $config->get('system', 'dlogfile'); $stream = $config->get('system', 'dlogfile');
$developerIp = $config->get('system', 'dlogip'); $developerIp = $config->get('system', 'dlogip');
if ((!isset($developerIp) || !$debugging) && if ((!isset($developerIp) || !$debugging) &&
(!is_file($stream) || is_writable($stream))) { (!is_file($stream) || is_writable($stream))) {
$logger = new VoidLogger(); return new NullLogger();
return $logger;
} }
$loggerTimeZone = new \DateTimeZone('UTC'); $loggerTimeZone = new \DateTimeZone('UTC');
@ -228,7 +248,7 @@ class LoggerFactory
* *
* @return string the PSR-3 compliant level * @return string the PSR-3 compliant level
*/ */
private static function mapLegacyConfigDebugLevel($level) private static function mapLegacyConfigDebugLevel(string $level): string
{ {
switch ($level) { switch ($level) {
// legacy WARNING // legacy WARNING
@ -263,9 +283,9 @@ class LoggerFactory
* *
* @return void * @return void
* *
* @throws \Exception in case of general failures * @throws LoggerException
*/ */
public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE) public static function addStreamHandler(LoggerInterface $logger, $stream, string $level = LogLevel::NOTICE)
{ {
if ($logger instanceof Monolog\Logger) { if ($logger instanceof Monolog\Logger) {
$loglevel = Monolog\Logger::toMonologLevel($level); $loglevel = Monolog\Logger::toMonologLevel($level);
@ -275,19 +295,16 @@ class LoggerFactory
$loglevel = LogLevel::NOTICE; $loglevel = LogLevel::NOTICE;
} }
$fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel); try {
$fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
$formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n"); $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
$fileHandler->setFormatter($formatter); $fileHandler->setFormatter($formatter);
$logger->pushHandler($fileHandler); $logger->pushHandler($fileHandler);
} } catch (\Exception $exception) {
} throw new LoggerException('Cannot create Monolog Logger.', $exception);
}
public static function addVoidHandler($logger)
{
if ($logger instanceof Monolog\Logger) {
$logger->pushHandler(new Monolog\Handler\NullHandler());
} }
} }
} }

View file

@ -19,9 +19,10 @@
* *
*/ */
namespace Friendica\Util\Logger; namespace Friendica\Core\Logger\Type;
use Friendica\Util\Introspection; use Friendica\Core\Logger\Exception\LoggerException;
use Friendica\Core\Logger\Util\Introspection;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
@ -58,29 +59,35 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* Adds a new entry to the log * Adds a new entry to the log
* *
* @param int $level * @param mixed $level
* @param string $message * @param string $message
* @param array $context * @param array $context
* *
* @return void * @return void
*/ */
abstract protected function addEntry($level, $message, $context = []); abstract protected function addEntry($level, string $message, array $context = []);
/** /**
* @param string $channel The output channel * @param string $channel The output channel
* @param Introspection $introspection The introspection of the current call * @param Introspection $introspection The introspection of the current call
* *
* @throws \Exception * @throws LoggerException
*/ */
public function __construct($channel, Introspection $introspection) public function __construct(string $channel, Introspection $introspection)
{ {
$this->channel = $channel; $this->channel = $channel;
$this->introspection = $introspection; $this->introspection = $introspection;
$this->logUid = Strings::getRandomHex(6);
try {
$this->logUid = Strings::getRandomHex(6);
} catch (\Exception $exception) {
throw new LoggerException('Cannot generate log Id', $exception);
}
} }
/** /**
* Simple interpolation of PSR-3 compliant replacements ( variables between '{' and '}' ) * Simple interpolation of PSR-3 compliant replacements ( variables between '{' and '}' )
*
* @see https://www.php-fig.org/psr/psr-3/#12-message * @see https://www.php-fig.org/psr/psr-3/#12-message
* *
* @param string $message * @param string $message
@ -88,7 +95,7 @@ abstract class AbstractLogger implements LoggerInterface
* *
* @return string the interpolated message * @return string the interpolated message
*/ */
protected function psrInterpolate($message, array $context = array()) protected function psrInterpolate(string $message, array $context = []): string
{ {
$replace = []; $replace = [];
foreach ($context as $key => $value) { foreach ($context as $key => $value) {
@ -104,7 +111,7 @@ abstract class AbstractLogger implements LoggerInterface
} }
/** /**
* JSON Encodes an complete array including objects with "__toString()" methods * JSON Encodes a complete array including objects with "__toString()" methods
* *
* @param array $input an Input Array to encode * @param array $input an Input Array to encode
* *
@ -128,7 +135,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function emergency($message, array $context = array()) public function emergency($message, array $context = [])
{ {
$this->addEntry(LogLevel::EMERGENCY, (string) $message, $context); $this->addEntry(LogLevel::EMERGENCY, (string) $message, $context);
} }
@ -136,7 +143,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function alert($message, array $context = array()) public function alert($message, array $context = [])
{ {
$this->addEntry(LogLevel::ALERT, (string) $message, $context); $this->addEntry(LogLevel::ALERT, (string) $message, $context);
} }
@ -144,7 +151,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function critical($message, array $context = array()) public function critical($message, array $context = [])
{ {
$this->addEntry(LogLevel::CRITICAL, (string) $message, $context); $this->addEntry(LogLevel::CRITICAL, (string) $message, $context);
} }
@ -152,7 +159,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function error($message, array $context = array()) public function error($message, array $context = [])
{ {
$this->addEntry(LogLevel::ERROR, (string) $message, $context); $this->addEntry(LogLevel::ERROR, (string) $message, $context);
} }
@ -160,7 +167,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function warning($message, array $context = array()) public function warning($message, array $context = [])
{ {
$this->addEntry(LogLevel::WARNING, (string) $message, $context); $this->addEntry(LogLevel::WARNING, (string) $message, $context);
} }
@ -168,7 +175,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function notice($message, array $context = array()) public function notice($message, array $context = [])
{ {
$this->addEntry(LogLevel::NOTICE, (string) $message, $context); $this->addEntry(LogLevel::NOTICE, (string) $message, $context);
} }
@ -176,7 +183,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function info($message, array $context = array()) public function info($message, array $context = [])
{ {
$this->addEntry(LogLevel::INFO, (string) $message, $context); $this->addEntry(LogLevel::INFO, (string) $message, $context);
} }
@ -184,7 +191,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function debug($message, array $context = array()) public function debug($message, array $context = [])
{ {
$this->addEntry(LogLevel::DEBUG, (string) $message, $context); $this->addEntry(LogLevel::DEBUG, (string) $message, $context);
} }
@ -192,7 +199,7 @@ abstract class AbstractLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function log($level, $message, array $context = array()) public function log($level, $message, array $context = [])
{ {
$this->addEntry($level, (string) $message, $context); $this->addEntry($level, (string) $message, $context);
} }

View file

@ -19,7 +19,7 @@
* *
*/ */
namespace Friendica\Util\Logger\Monolog; namespace Friendica\Core\Logger\Type\Monolog;
use Monolog\Handler; use Monolog\Handler;
use Monolog\Logger; use Monolog\Logger;
@ -42,7 +42,7 @@ class DevelopHandler extends Handler\AbstractHandler
* @param int $level The minimum logging level at which this handler will be triggered * @param int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/ */
public function __construct($developerIp, $level = Logger::DEBUG, $bubble = true) public function __construct($developerIp, $level = Logger::DEBUG, bool $bubble = true)
{ {
parent::__construct($level, $bubble); parent::__construct($level, $bubble);
@ -52,15 +52,14 @@ class DevelopHandler extends Handler\AbstractHandler
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function handle(array $record) public function handle(array $record): bool
{ {
if (!$this->isHandling($record)) { if (!$this->isHandling($record)) {
return false; return false;
} }
/// Just in case the remote IP is the same as the developer IP log the output /// Just in case the remote IP is the same as the developer IP log the output
if (!is_null($this->developerIp) && $_SERVER['REMOTE_ADDR'] != $this->developerIp) if (!is_null($this->developerIp) && $_SERVER['REMOTE_ADDR'] != $this->developerIp) {
{
return false; return false;
} }

View file

@ -19,9 +19,9 @@
* *
*/ */
namespace Friendica\Util\Logger\Monolog; namespace Friendica\Core\Logger\Type\Monolog;
use Friendica\Util\Introspection; use Friendica\Core\Logger\Util\Introspection;
use Monolog\Logger; use Monolog\Logger;
use Monolog\Processor\ProcessorInterface; use Monolog\Processor\ProcessorInterface;
@ -41,11 +41,11 @@ class IntrospectionProcessor implements ProcessorInterface
public function __construct(Introspection $introspection, $level = Logger::DEBUG) public function __construct(Introspection $introspection, $level = Logger::DEBUG)
{ {
$this->level = Logger::toMonologLevel($level); $this->level = Logger::toMonologLevel($level);
$introspection->addClasses(array('Monolog\\')); $introspection->addClasses(['Monolog\\']);
$this->introspection = $introspection; $this->introspection = $introspection;
} }
public function __invoke(array $record) public function __invoke(array $record): array
{ {
// return if the level is not high enough // return if the level is not high enough
if ($record['level'] < $this->level) { if ($record['level'] < $this->level) {

View file

@ -19,9 +19,8 @@
* *
*/ */
namespace Friendica\Util\Logger; namespace Friendica\Core\Logger\Type;
use Friendica\Core\System;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -50,14 +49,14 @@ class ProfilerLogger implements LoggerInterface
*/ */
public function __construct(LoggerInterface $logger, Profiler $profiler) public function __construct(LoggerInterface $logger, Profiler $profiler)
{ {
$this->logger = $logger; $this->logger = $logger;
$this->profiler = $profiler; $this->profiler = $profiler;
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function emergency($message, array $context = array()) public function emergency($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->emergency($message, $context); $this->logger->emergency($message, $context);
@ -67,7 +66,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function alert($message, array $context = array()) public function alert($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->alert($message, $context); $this->logger->alert($message, $context);
@ -77,7 +76,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function critical($message, array $context = array()) public function critical($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->critical($message, $context); $this->logger->critical($message, $context);
@ -87,7 +86,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function error($message, array $context = array()) public function error($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->error($message, $context); $this->logger->error($message, $context);
@ -97,7 +96,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function warning($message, array $context = array()) public function warning($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->warning($message, $context); $this->logger->warning($message, $context);
@ -107,7 +106,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function notice($message, array $context = array()) public function notice($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->notice($message, $context); $this->logger->notice($message, $context);
@ -117,7 +116,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function info($message, array $context = array()) public function info($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->info($message, $context); $this->logger->info($message, $context);
@ -127,7 +126,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function debug($message, array $context = array()) public function debug($message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->debug($message, $context); $this->logger->debug($message, $context);
@ -137,7 +136,7 @@ class ProfilerLogger implements LoggerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function log($level, $message, array $context = array()) public function log($level, $message, array $context = [])
{ {
$this->profiler->startRecording('file'); $this->profiler->startRecording('file');
$this->logger->log($level, $message, $context); $this->logger->log($level, $message, $context);

View file

@ -19,11 +19,14 @@
* *
*/ */
namespace Friendica\Util\Logger; namespace Friendica\Core\Logger\Type;
use Friendica\Core\Logger\Exception\LoggerArgumentException;
use Friendica\Core\Logger\Exception\LoggerException;
use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\FileSystem; use Friendica\Util\FileSystem;
use Friendica\Util\Introspection; use Friendica\Core\Logger\Util\Introspection;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
/** /**
@ -80,9 +83,10 @@ class StreamLogger extends AbstractLogger
* @param string|resource $stream The stream to write with this logger (either a file or a stream, i.e. stdout) * @param string|resource $stream The stream to write with this logger (either a file or a stream, i.e. stdout)
* @param string $level The minimum loglevel at which this logger will be triggered * @param string $level The minimum loglevel at which this logger will be triggered
* *
* @throws \Exception * @throws LoggerArgumentException
* @throws LogLevelException
*/ */
public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, $level = LogLevel::DEBUG) public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG)
{ {
$this->fileSystem = $fileSystem; $this->fileSystem = $fileSystem;
@ -93,14 +97,14 @@ class StreamLogger extends AbstractLogger
} elseif (is_string($stream)) { } elseif (is_string($stream)) {
$this->url = $stream; $this->url = $stream;
} else { } else {
throw new \InvalidArgumentException('A stream must either be a resource or a string.'); throw new LoggerArgumentException('A stream must either be a resource or a string.');
} }
$this->pid = getmypid(); $this->pid = getmypid();
if (array_key_exists($level, $this->levelToInt)) { if (array_key_exists($level, $this->levelToInt)) {
$this->logLevel = $this->levelToInt[$level]; $this->logLevel = $this->levelToInt[$level];
} else { } else {
throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level)); throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
} }
$this->checkStream(); $this->checkStream();
@ -118,16 +122,19 @@ class StreamLogger extends AbstractLogger
/** /**
* Adds a new entry to the log * Adds a new entry to the log
* *
* @param int $level * @param mixed $level
* @param string $message * @param string $message
* @param array $context * @param array $context
* *
* @return void * @return void
*
* @throws LoggerException
* @throws LogLevelException
*/ */
protected function addEntry($level, $message, $context = []) protected function addEntry($level, string $message, array $context = [])
{ {
if (!array_key_exists($level, $this->levelToInt)) { if (!array_key_exists($level, $this->levelToInt)) {
throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level)); throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
} }
$logLevel = $this->levelToInt[$level]; $logLevel = $this->levelToInt[$level];
@ -145,19 +152,24 @@ class StreamLogger extends AbstractLogger
/** /**
* Formats a log record for the syslog output * Formats a log record for the syslog output
* *
* @param int $level The loglevel/priority * @param mixed $level The loglevel/priority
* @param string $message The message * @param string $message The message
* @param array $context The context of this call * @param array $context The context of this call
* *
* @return string the formatted syslog output * @return string the formatted syslog output
*
* @throws LoggerException
*/ */
private function formatLog($level, $message, $context = []) private function formatLog($level, string $message, array $context = []): string
{ {
$record = $this->introspection->getRecord(); $record = $this->introspection->getRecord();
$record = array_merge($record, ['uid' => $this->logUid, 'process_id' => $this->pid]); $record = array_merge($record, ['uid' => $this->logUid, 'process_id' => $this->pid]);
$logMessage = '';
$logMessage .= DateTimeFormat::utcNow(DateTimeFormat::ATOM) . ' '; try {
$logMessage = DateTimeFormat::utcNow(DateTimeFormat::ATOM) . ' ';
} catch (\Exception $exception) {
throw new LoggerException('Cannot get current datetime.', $exception);
}
$logMessage .= $this->channel . ' '; $logMessage .= $this->channel . ' ';
$logMessage .= '[' . strtoupper($level) . ']: '; $logMessage .= '[' . strtoupper($level) . ']: ';
$logMessage .= $this->psrInterpolate($message, $context) . ' '; $logMessage .= $this->psrInterpolate($message, $context) . ' ';
@ -168,6 +180,12 @@ class StreamLogger extends AbstractLogger
return $logMessage; return $logMessage;
} }
/**
* Checks the current stream
*
* @throws LoggerException
* @throws LoggerArgumentException
*/
private function checkStream() private function checkStream()
{ {
if (is_resource($this->stream)) { if (is_resource($this->stream)) {
@ -175,9 +193,13 @@ class StreamLogger extends AbstractLogger
} }
if (empty($this->url)) { if (empty($this->url)) {
throw new \LogicException('Missing stream URL.'); throw new LoggerArgumentException('Missing stream URL.');
} }
$this->stream = $this->fileSystem->createStream($this->url); try {
$this->stream = $this->fileSystem->createStream($this->url);
} catch (\UnexpectedValueException $exception) {
throw new LoggerException('Cannot create stream.', $exception);
}
} }
} }

View file

@ -19,10 +19,11 @@
* *
*/ */
namespace Friendica\Util\Logger; namespace Friendica\Core\Logger\Type;
use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Core\Logger\Exception\LoggerException;
use Friendica\Util\Introspection; use Friendica\Core\Logger\Exception\LogLevelException;
use Friendica\Core\Logger\Util\Introspection;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
/** /**
@ -97,27 +98,29 @@ class SyslogLogger extends AbstractLogger
* @param int $logOpts Indicates what logging options will be used when generating a log message * @param int $logOpts Indicates what logging options will be used when generating a log message
* @param int $logFacility Used to specify what type of program is logging the message * @param int $logFacility Used to specify what type of program is logging the message
* *
* @throws \Exception * @throws LogLevelException
* @throws LoggerException
*/ */
public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER) public function __construct($channel, Introspection $introspection, string $level = LogLevel::NOTICE, int $logOpts = LOG_PID, int $logFacility = LOG_USER)
{ {
parent::__construct($channel, $introspection); parent::__construct($channel, $introspection);
$this->logOpts = $logOpts; $this->logOpts = $logOpts;
$this->logFacility = $logFacility; $this->logFacility = $logFacility;
$this->logLevel = $this->mapLevelToPriority($level); $this->logLevel = $this->mapLevelToPriority($level);
$this->introspection->addClasses(array(self::class)); $this->introspection->addClasses([self::class]);
} }
/** /**
* Adds a new entry to the syslog * Adds a new entry to the syslog
* *
* @param int $level * @param mixed $level
* @param string $message * @param string $message
* @param array $context * @param array $context
* *
* @throws InternalServerErrorException if the syslog isn't available * @throws LogLevelException in case the level isn't valid
* @throws LoggerException In case the syslog cannot be opened for writing
*/ */
protected function addEntry($level, $message, $context = []) protected function addEntry($level, string $message, array $context = [])
{ {
$logLevel = $this->mapLevelToPriority($level); $logLevel = $this->mapLevelToPriority($level);
@ -136,12 +139,12 @@ class SyslogLogger extends AbstractLogger
* *
* @return int The SysLog priority * @return int The SysLog priority
* *
* @throws \Psr\Log\InvalidArgumentException If the loglevel isn't valid * @throws LogLevelException If the loglevel isn't valid
*/ */
public function mapLevelToPriority($level) public function mapLevelToPriority(string $level): int
{ {
if (!array_key_exists($level, $this->logLevels)) { if (!array_key_exists($level, $this->logLevels)) {
throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level)); throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
} }
return $this->logLevels[$level]; return $this->logLevels[$level];
@ -157,21 +160,22 @@ class SyslogLogger extends AbstractLogger
/** /**
* Writes a message to the syslog * Writes a message to the syslog
*
* @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters * @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters
* *
* @param int $priority The Priority * @param int $priority The Priority
* @param string $message The message of the log * @param string $message The message of the log
* *
* @throws InternalServerErrorException if syslog cannot be used * @throws LoggerException In case the syslog cannot be opened/written
*/ */
private function write($priority, $message) private function write(int $priority, string $message)
{ {
set_error_handler([$this, 'customErrorHandler']); set_error_handler([$this, 'customErrorHandler']);
$opened = openlog(self::IDENT, $this->logOpts, $this->logFacility); $opened = openlog(self::IDENT, $this->logOpts, $this->logFacility);
restore_error_handler(); restore_error_handler();
if (!$opened) { if (!$opened) {
throw new \UnexpectedValueException(sprintf('Can\'t open syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility)); throw new LoggerException(sprintf('Can\'t open syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility));
} }
$this->syslogWrapper($priority, $message); $this->syslogWrapper($priority, $message);
@ -186,13 +190,12 @@ class SyslogLogger extends AbstractLogger
* *
* @return string the formatted syslog output * @return string the formatted syslog output
*/ */
private function formatLog($level, $message, $context = []) private function formatLog(int $level, string $message, array $context = []): string
{ {
$record = $this->introspection->getRecord(); $record = $this->introspection->getRecord();
$record = array_merge($record, ['uid' => $this->logUid]); $record = array_merge($record, ['uid' => $this->logUid]);
$logMessage = '';
$logMessage .= $this->channel . ' '; $logMessage = $this->channel . ' ';
$logMessage .= '[' . $this->logToString[$level] . ']: '; $logMessage .= '[' . $this->logToString[$level] . ']: ';
$logMessage .= $this->psrInterpolate($message, $context) . ' '; $logMessage .= $this->psrInterpolate($message, $context) . ' ';
$logMessage .= $this->jsonEncodeArray($context) . ' - '; $logMessage .= $this->jsonEncodeArray($context) . ' - ';
@ -211,15 +214,17 @@ class SyslogLogger extends AbstractLogger
* *
* @param int $level The syslog priority * @param int $level The syslog priority
* @param string $entry The message to send to the syslog function * @param string $entry The message to send to the syslog function
*
* @throws LoggerException
*/ */
protected function syslogWrapper($level, $entry) protected function syslogWrapper(int $level, string $entry)
{ {
set_error_handler([$this, 'customErrorHandler']); set_error_handler([$this, 'customErrorHandler']);
$written = syslog($level, $entry); $written = syslog($level, $entry);
restore_error_handler(); restore_error_handler();
if (!$written) { if (!$written) {
throw new \UnexpectedValueException(sprintf('Can\'t write into syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility)); throw new LoggerException(sprintf('Can\'t write into syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility));
} }
} }
} }

View file

@ -19,13 +19,14 @@
* *
*/ */
namespace Friendica\Util\Logger; namespace Friendica\Core\Logger\Type;
use Friendica\Core\Logger\Exception\LoggerException;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
/** /**
* A Logger for specific worker tasks, which adds an additional woker-id to it. * A Logger for specific worker tasks, which adds a worker id to it.
* Uses the decorator pattern (https://en.wikipedia.org/wiki/Decorator_pattern) * Uses the decorator pattern (https://en.wikipedia.org/wiki/Decorator_pattern)
*/ */
class WorkerLogger implements LoggerInterface class WorkerLogger implements LoggerInterface
@ -46,15 +47,21 @@ class WorkerLogger implements LoggerInterface
private $functionName; private $functionName;
/** /**
* @param LoggerInterface $logger The logger for worker entries * @param LoggerInterface $logger The logger for worker entries
* @param string $functionName The current function name of the worker * @param string $functionName The current function name of the worker
* @param int $idLength The length of the generated worker ID * @param int $idLength The length of the generated worker ID
*
* @throws LoggerException
*/ */
public function __construct(LoggerInterface $logger, $functionName = '', $idLength = 7) public function __construct(LoggerInterface $logger, string $functionName = '', int $idLength = 7)
{ {
$this->logger = $logger; $this->logger = $logger;
$this->functionName = $functionName; $this->functionName = $functionName;
$this->workerId = Strings::getRandomHex($idLength); try {
$this->workerId = Strings::getRandomHex($idLength);
} catch (\Exception $exception) {
throw new LoggerException('Cannot generate random Hex.', $exception);
}
} }
/** /**
@ -74,7 +81,7 @@ class WorkerLogger implements LoggerInterface
*/ */
private function addContext(array &$context) private function addContext(array &$context)
{ {
$context['worker_id'] = $this->workerId; $context['worker_id'] = $this->workerId;
$context['worker_cmd'] = $this->functionName; $context['worker_cmd'] = $this->functionName;
} }
@ -83,7 +90,7 @@ class WorkerLogger implements LoggerInterface
* *
* @return string * @return string
*/ */
public function getWorkerId() public function getWorkerId(): string
{ {
return $this->workerId; return $this->workerId;
} }

View file

@ -19,15 +19,17 @@
* *
*/ */
namespace Friendica\Util; namespace Friendica\Core\Logger\Util;
/** /**
* Get Introspection information about the current call * Get Introspection information about the current call
*/ */
class Introspection class Introspection
{ {
/** @var int */
private $skipStackFramesCount; private $skipStackFramesCount;
/** @var string[] */
private $skipClassesPartials; private $skipClassesPartials;
private $skipFunctions = [ private $skipFunctions = [
@ -36,10 +38,10 @@ class Introspection
]; ];
/** /**
* @param array $skipClassesPartials An array of classes to skip during logging * @param string[] $skipClassesPartials An array of classes to skip during logging
* @param int $skipStackFramesCount If the logger should use information from other hierarchy levels of the call * @param int $skipStackFramesCount If the logger should use information from other hierarchy levels of the call
*/ */
public function __construct($skipClassesPartials = array(), $skipStackFramesCount = 0) public function __construct(array $skipClassesPartials = [], int $skipStackFramesCount = 0)
{ {
$this->skipClassesPartials = $skipClassesPartials; $this->skipClassesPartials = $skipClassesPartials;
$this->skipStackFramesCount = $skipStackFramesCount; $this->skipStackFramesCount = $skipStackFramesCount;
@ -47,6 +49,7 @@ class Introspection
/** /**
* Adds new classes to get skipped * Adds new classes to get skipped
*
* @param array $classNames * @param array $classNames
*/ */
public function addClasses(array $classNames) public function addClasses(array $classNames)
@ -59,7 +62,7 @@ class Introspection
* *
* @return array * @return array
*/ */
public function getRecord() public function getRecord(): array
{ {
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
@ -73,8 +76,8 @@ class Introspection
return [ return [
'file' => isset($trace[$i - 1]['file']) ? basename($trace[$i - 1]['file']) : null, 'file' => isset($trace[$i - 1]['file']) ? basename($trace[$i - 1]['file']) : null,
'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, 'line' => $trace[$i - 1]['line'] ?? null,
'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, 'function' => $trace[$i]['function'] ?? null,
]; ];
} }
@ -86,7 +89,7 @@ class Introspection
* *
* @return bool True if the class or function should get skipped, otherwise false * @return bool True if the class or function should get skipped, otherwise false
*/ */
private function isTraceClassOrSkippedFunction(array $trace, $index) private function isTraceClassOrSkippedFunction(array $trace, int $index): bool
{ {
if (!isset($trace[$index])) { if (!isset($trace[$index])) {
return false; return false;

View file

@ -19,35 +19,34 @@
* *
*/ */
namespace Friendica\Core\PConfig; namespace Friendica\Core\PConfig\Capability;
use Friendica\Core\PConfig\ValueObject;
/** /**
* Interface for accessing user specific configurations * Interface for accessing user specific configurations
*/ */
interface IPConfig interface IManagePersonalConfigValues
{ {
/** /**
* Loads all configuration values of a user's config family into a cached storage. * 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 * All configuration values of the given user are stored with the $uid in the cache
* *
* @param int $uid The user_id * @param int $uid The user_id
* @param string $cat The category of the configuration value * @param string $cat The category of the configuration value
* *
* @return array The loaded config array * @return array The loaded config array
* @see Cache
*
*/ */
function load(int $uid, string $cat = 'config'); public function load(int $uid, string $cat = 'config'): array;
/** /**
* Get a particular user's config variable given the category name * Get a particular user's config variable given the category name
* ($cat) and a key. * ($cat) and a key.
* *
* Get a particular user's config value from the given category ($cat) * 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 * and the $key with the $uid from a cached storage either from the database
* (@see IConfigAdapter) or from the $this->configCache (@see PConfigCache). * or from the configCache
* *
* @param int $uid The user_id * @param int $uid The user_id
* @param string $cat The category of the configuration value * @param string $cat The category of the configuration value
@ -56,8 +55,9 @@ interface IPConfig
* @param boolean $refresh optional, If true the config is loaded from the db and not from the cache (default: false) * @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 * @return mixed Stored value or null if it does not exist
*
*/ */
function get(int $uid, string $cat, string $key, $default_value = null, bool $refresh = false); public function get(int $uid, string $cat, string $key, $default_value = null, bool $refresh = false);
/** /**
* Sets a configuration value for a user * Sets a configuration value for a user
@ -74,28 +74,26 @@ interface IPConfig
* *
* @return bool Operation success * @return bool Operation success
*/ */
function set(int $uid, string $cat, string $key, $value); public function set(int $uid, string $cat, string $key, $value): bool;
/** /**
* Deletes the given key from the users's configuration. * Deletes the given key from the users configuration.
* *
* Removes the configured value from the stored cache in $this->configCache * Removes the configured value from the stored cache and removes it from the database with the given $uid.
* (@see ConfigCache) and removes it from the database (@see IConfigAdapter)
* with the given $uid.
* *
* @param int $uid The user_id * @param int $uid The user_id
* @param string $cat The category of the configuration value * @param string $cat The category of the configuration value
* @param string $key The configuration key to delete * @param string $key The configuration key to delete
* *
* @return bool * @return bool
*/ */
function delete(int $uid, string $cat, string $key); public function delete(int $uid, string $cat, string $key): bool;
/** /**
* Returns the Config Cache * Returns the Config Cache
* *
* @return Cache * @return ValueObject\Cache
*/ */
function getCache(); public function getCache(): ValueObject\Cache;
} }

View file

@ -0,0 +1,13 @@
<?php
namespace Friendica\Core\PConfig\Exception;
use Throwable;
class PConfigPersistenceException extends \RuntimeException
{
public function __construct($message = "", Throwable $previous = null)
{
parent::__construct($message, 500, $previous);
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @copyright Copyright (C) 2010-2021, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Core\PConfig\Factory;
use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\PConfig\Repository;
use Friendica\Core\PConfig\Type;
use Friendica\Core\PConfig\ValueObject;
class PConfig
{
/**
* @param Cache $configCache The config cache
* @param ValueObject\Cache $pConfigCache The personal config cache
* @param Repository\PConfig $configRepo The configuration model
*
* @return IManagePersonalConfigValues
*/
public function create(Cache $configCache, ValueObject\Cache $pConfigCache, Repository\PConfig $configRepo): IManagePersonalConfigValues
{
if ($configCache->get('system', 'config_adapter') === 'preload') {
$configuration = new Type\PreloadPConfig($pConfigCache, $configRepo);
} else {
$configuration = new Type\JitPConfig($pConfigCache, $configRepo);
}
return $configuration;
}
}

View file

@ -19,44 +19,72 @@
* *
*/ */
namespace Friendica\Model\Config; namespace Friendica\Core\PConfig\Repository;
use Friendica\Core\Config\Util\ValueConversion;
use Friendica\Core\PConfig\Exception\PConfigPersistenceException;
use Friendica\Database\Database;
/** /**
* The Config model backend for users, which is using the general DB-model backend for user-configs * The Config model backend for users, which is using the general DB-model backend for user-configs
*/ */
class PConfig extends DbaConfig class PConfig
{ {
protected static $table_name = 'pconfig';
/** @var Database */
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
/**
* Checks if the model is currently connected
*
* @return bool
*/
public function isConnected(): bool
{
return $this->db->isConnected();
}
/** /**
* Loads all configuration values and returns the loaded category as an array. * Loads all configuration values and returns the loaded category as an array.
* *
* @param int $uid The id of the user to load * @param int $uid The id of the user to load
* @param string|null $cat The category of the configuration values to load * @param string|null $cat The category of the configuration values to load
* *
* @return array The config array * @return string[][] The config array
* *
* @throws \Exception In case DB calls are invalid * @throws PConfigPersistenceException In case the persistence layer throws errors
*/ */
public function load(int $uid, string $cat = null) public function load(int $uid, ?string $cat = null): array
{ {
$return = []; $return = [];
if (empty($cat)) { try {
$configs = $this->dba->select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]); if (empty($cat)) {
} else { $configs = $this->db->select(static::$table_name, ['cat', 'v', 'k'], ['uid' => $uid]);
$configs = $this->dba->select('pconfig', ['cat', 'v', 'k'], ['cat' => $cat, 'uid' => $uid]); } else {
} $configs = $this->db->select(static::$table_name, ['cat', 'v', 'k'], ['cat' => $cat, 'uid' => $uid]);
while ($config = $this->dba->fetch($configs)) {
$key = $config['k'];
$value = $this->toConfigValue($config['v']);
// just save it in case it is set
if (isset($value)) {
$return[$config['cat']][$key] = $value;
} }
while ($config = $this->db->fetch($configs)) {
$key = $config['k'];
$value = ValueConversion::toConfigValue($config['v']);
// just save it in case it is set
if (isset($value)) {
$return[$config['cat']][$key] = $value;
}
}
} catch (\Exception $exception) {
throw new PConfigPersistenceException(sprintf('Cannot load config category %s for user %d', $cat, $uid), $exception);
} finally {
$this->db->close($configs);
} }
$this->dba->close($configs);
return $return; return $return;
} }
@ -73,7 +101,7 @@ class PConfig extends DbaConfig
* *
* @return array|string|null Stored value or null if it does not exist * @return array|string|null Stored value or null if it does not exist
* *
* @throws \Exception In case DB calls are invalid * @throws PConfigPersistenceException In case the persistence layer throws errors
*/ */
public function get(int $uid, string $cat, string $key) public function get(int $uid, string $cat, string $key)
{ {
@ -81,14 +109,18 @@ class PConfig extends DbaConfig
return null; return null;
} }
$config = $this->dba->selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $key]); try {
if ($this->dba->isResult($config)) { $config = $this->db->selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $key]);
$value = $this->toConfigValue($config['v']); if ($this->db->isResult($config)) {
$value = ValueConversion::toConfigValue($config['v']);
// just return it in case it is set // just return it in case it is set
if (isset($value)) { if (isset($value)) {
return $value; return $value;
}
} }
} catch (\Exception $exception) {
throw new PConfigPersistenceException(sprintf('Cannot get config value for category %s, key %s and user %d', $cat, $key, $uid), $exception);
} }
return null; return null;
@ -107,9 +139,9 @@ class PConfig extends DbaConfig
* *
* @return bool Operation success * @return bool Operation success
* *
* @throws \Exception In case DB calls are invalid * @throws PConfigPersistenceException In case the persistence layer throws errors
*/ */
public function set(int $uid, string $cat, string $key, $value) public function set(int $uid, string $cat, string $key, $value): bool
{ {
if (!$this->isConnected()) { if (!$this->isConnected()) {
return false; return false;
@ -125,11 +157,12 @@ class PConfig extends DbaConfig
return true; return true;
} }
$dbvalue = $this->toDbValue($value); try {
$dbValue = ValueConversion::toDbValue($value);
$result = $this->dba->update('pconfig', ['v' => $dbvalue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true); return $this->db->update(static::$table_name, ['v' => $dbValue], ['uid' => $uid, 'cat' => $cat, 'k' => $key], true);
} catch (\Exception $exception) {
return $result; throw new PConfigPersistenceException(sprintf('Cannot set config value for category %s, key %s and user %d', $cat, $key, $uid), $exception);
}
} }
/** /**
@ -141,14 +174,18 @@ class PConfig extends DbaConfig
* *
* @return bool Operation success * @return bool Operation success
* *
* @throws \Exception In case DB calls are invalid * @throws PConfigPersistenceException In case the persistence layer throws errors
*/ */
public function delete(int $uid, string $cat, string $key) public function delete(int $uid, string $cat, string $key): bool
{ {
if (!$this->isConnected()) { if (!$this->isConnected()) {
return false; return false;
} }
return $this->dba->delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]); try {
return $this->db->delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $key]);
} catch (\Exception $exception) {
throw new PConfigPersistenceException(sprintf('Cannot delete config value for category %s, key %s and user %d', $cat, $key, $uid), $exception);
}
} }
} }

View file

@ -19,20 +19,20 @@
* *
*/ */
namespace Friendica\Core; namespace Friendica\Core\PConfig\Type;
use Friendica\Core\PConfig\Cache; use Friendica\Core\PConfig\Repository;
use Friendica\Core\PConfig\IPConfig; use Friendica\Core\PConfig\ValueObject\Cache;
use Friendica\Model; use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
/** /**
* This class is responsible for the user-specific configuration values in Friendica * This class is responsible for the user-specific configuration values in Friendica
* The values are set through the Config-DB-Table (per Config-DB-model @see Model\Config\PConfig) * The values are set through the Config-DB-Table (per Config-DB-model @see Repository\PConfig)
* *
* The configuration cache (@see Cache\PConfigCache) is used for temporary caching of database calls. This will * The configuration cache (@see Cache) is used for temporary caching of database calls. This will
* increase the performance. * increase the performance.
*/ */
abstract class BasePConfig implements IPConfig abstract class AbstractPConfigValues implements IManagePersonalConfigValues
{ {
/** /**
* @var Cache * @var Cache
@ -40,18 +40,18 @@ abstract class BasePConfig implements IPConfig
protected $configCache; protected $configCache;
/** /**
* @var Model\Config\PConfig * @var Repository\PConfig
*/ */
protected $configModel; protected $configModel;
/** /**
* @param Cache $configCache The configuration cache * @param Cache $configCache The configuration cache
* @param Model\Config\PConfig $configModel The configuration model * @param Repository\PConfig $configRepo The configuration model
*/ */
public function __construct(Cache $configCache, Model\Config\PConfig $configModel) public function __construct(Cache $configCache, Repository\PConfig $configRepo)
{ {
$this->configCache = $configCache; $this->configCache = $configCache;
$this->configModel = $configModel; $this->configModel = $configRepo;
} }
/** /**
@ -59,7 +59,7 @@ abstract class BasePConfig implements IPConfig
* *
* @return Cache * @return Cache
*/ */
public function getCache() public function getCache(): Cache
{ {
return $this->configCache; return $this->configCache;
} }

View file

@ -19,10 +19,10 @@
* *
*/ */
namespace Friendica\Core\PConfig; namespace Friendica\Core\PConfig\Type;
use Friendica\Core\BasePConfig; use Friendica\Core\PConfig\Repository;
use Friendica\Model; use Friendica\Core\PConfig\ValueObject;
/** /**
* This class implements the Just-In-Time configuration, which will cache * This class implements the Just-In-Time configuration, which will cache
@ -31,7 +31,7 @@ use Friendica\Model;
* Default Configuration type. * Default Configuration type.
* Provides the best performance for pages loading few configuration variables. * Provides the best performance for pages loading few configuration variables.
*/ */
class JitPConfig extends BasePConfig class JitPConfig extends AbstractPConfigValues
{ {
/** /**
* @var array Array of already loaded db values (even if there was no value) * @var array Array of already loaded db values (even if there was no value)
@ -39,12 +39,12 @@ class JitPConfig extends BasePConfig
private $db_loaded; private $db_loaded;
/** /**
* @param Cache $configCache The configuration cache * @param ValueObject\Cache $configCache The configuration cache
* @param Model\Config\PConfig $configModel The configuration model * @param Repository\PConfig $configRepo The configuration model
*/ */
public function __construct(Cache $configCache, Model\Config\PConfig $configModel) public function __construct(ValueObject\Cache $configCache, Repository\PConfig $configRepo)
{ {
parent::__construct($configCache, $configModel); parent::__construct($configCache, $configRepo);
$this->db_loaded = []; $this->db_loaded = [];
} }
@ -52,11 +52,11 @@ class JitPConfig extends BasePConfig
* {@inheritDoc} * {@inheritDoc}
* *
*/ */
public function load(int $uid, string $cat = 'config') public function load(int $uid, string $cat = 'config'): array
{ {
// If not connected or no uid, do nothing // If not connected or no uid, do nothing
if (!$uid || !$this->configModel->isConnected()) { if (!$uid || !$this->configModel->isConnected()) {
return; return [];
} }
$config = $this->configModel->load($uid, $cat); $config = $this->configModel->load($uid, $cat);
@ -84,14 +84,12 @@ class JitPConfig extends BasePConfig
// if the value isn't loaded or refresh is needed, load it to the cache // if the value isn't loaded or refresh is needed, load it to the cache
if ($this->configModel->isConnected() && if ($this->configModel->isConnected() &&
(empty($this->db_loaded[$uid][$cat][$key]) || (empty($this->db_loaded[$uid][$cat][$key]) || $refresh)) {
$refresh)) { $dbValue = $this->configModel->get($uid, $cat, $key);
$dbvalue = $this->configModel->get($uid, $cat, $key); if (isset($dbValue)) {
$this->configCache->set($uid, $cat, $key, $dbValue);
if (isset($dbvalue)) { unset($dbValue);
$this->configCache->set($uid, $cat, $key, $dbvalue);
unset($dbvalue);
} }
$this->db_loaded[$uid][$cat][$key] = true; $this->db_loaded[$uid][$cat][$key] = true;
@ -106,7 +104,7 @@ class JitPConfig extends BasePConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function set(int $uid, string $cat, string $key, $value) public function set(int $uid, string $cat, string $key, $value): bool
{ {
if (!$uid) { if (!$uid) {
return false; return false;
@ -130,7 +128,7 @@ class JitPConfig extends BasePConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function delete(int $uid, string $cat, string $key) public function delete(int $uid, string $cat, string $key): bool
{ {
if (!$uid) { if (!$uid) {
return false; return false;

View file

@ -19,10 +19,10 @@
* *
*/ */
namespace Friendica\Core\PConfig; namespace Friendica\Core\PConfig\Type;
use Friendica\Core\BasePConfig; use Friendica\Core\PConfig\Repository;
use Friendica\Model; use Friendica\Core\PConfig\ValueObject;
/** /**
* This class implements the preload configuration, which will cache * This class implements the preload configuration, which will cache
@ -30,18 +30,18 @@ use Friendica\Model;
* *
* Minimizes the number of database queries to retrieve configuration values at the cost of memory. * Minimizes the number of database queries to retrieve configuration values at the cost of memory.
*/ */
class PreloadPConfig extends BasePConfig class PreloadPConfig extends AbstractPConfigValues
{ {
/** @var array */ /** @var array */
private $config_loaded; private $config_loaded;
/** /**
* @param Cache $configCache The configuration cache * @param ValueObject\Cache $configCache The configuration cache
* @param Model\Config\PConfig $configModel The configuration model * @param Repository\PConfig $configRepo The configuration model
*/ */
public function __construct(Cache $configCache, Model\Config\PConfig $configModel) public function __construct(ValueObject\Cache $configCache, Repository\PConfig $configRepo)
{ {
parent::__construct($configCache, $configModel); parent::__construct($configCache, $configRepo);
$this->config_loaded = []; $this->config_loaded = [];
} }
@ -51,16 +51,16 @@ class PreloadPConfig extends BasePConfig
* This loads all config values everytime load is called * This loads all config values everytime load is called
* *
*/ */
public function load(int $uid, string $cat = 'config') public function load(int $uid, string $cat = 'config'): array
{ {
// Don't load the whole configuration twice or with invalid uid // Don't load the whole configuration twice or with invalid uid
if (!$uid || !empty($this->config_loaded[$uid])) { if (!$uid || !empty($this->config_loaded[$uid])) {
return; return [];
} }
// If not connected, do nothing // If not connected, do nothing
if (!$this->configModel->isConnected()) { if (!$this->configModel->isConnected()) {
return; return [];
} }
$config = $this->configModel->load($uid); $config = $this->configModel->load($uid);
@ -101,7 +101,7 @@ class PreloadPConfig extends BasePConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function set(int $uid, string $cat, string $key, $value) public function set(int $uid, string $cat, string $key, $value): bool
{ {
if (!$uid) { if (!$uid) {
return false; return false;
@ -127,7 +127,7 @@ class PreloadPConfig extends BasePConfig
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function delete(int $uid, string $cat, string $key) public function delete(int $uid, string $cat, string $key): bool
{ {
if (!$uid) { if (!$uid) {
return false; return false;

View file

@ -19,7 +19,7 @@
* *
*/ */
namespace Friendica\Core\PConfig; namespace Friendica\Core\PConfig\ValueObject;
use ParagonIE\HiddenString\HiddenString; use ParagonIE\HiddenString\HiddenString;
@ -31,7 +31,7 @@ class Cache
/** /**
* @var array * @var array
*/ */
private $config; private $config = [];
/** /**
* @var bool * @var bool
@ -53,7 +53,7 @@ class Cache
* @param int $uid * @param int $uid
* @param array $config * @param array $config
*/ */
public function load($uid, array $config) public function load(int $uid, array $config)
{ {
if (!is_int($uid)) { if (!is_int($uid)) {
return; return;
@ -63,7 +63,6 @@ class Cache
foreach ($categories as $category) { foreach ($categories as $category) {
if (isset($config[$category]) && is_array($config[$category])) { if (isset($config[$category]) && is_array($config[$category])) {
$keys = array_keys($config[$category]); $keys = array_keys($config[$category]);
foreach ($keys as $key) { foreach ($keys as $key) {
@ -81,11 +80,11 @@ class Cache
* *
* @param int $uid User Id * @param int $uid User Id
* @param string $cat Config category * @param string $cat Config category
* @param string $key Config key * @param string|null $key Config key
* *
* @return null|string The value of the config entry or null if not set * @return null|mixed The value of the config entry or null if not set
*/ */
public function get($uid, string $cat, string $key = null) public function get(int $uid, string $cat, ?string $key = null)
{ {
if (!is_int($uid)) { if (!is_int($uid)) {
return null; return null;
@ -112,7 +111,7 @@ class Cache
* *
* @return bool Set successful * @return bool Set successful
*/ */
public function set($uid, string $cat, string $key, $value) public function set(int $uid, string $cat, string $key, $value): bool
{ {
if (!is_int($uid)) { if (!is_int($uid)) {
return false; return false;
@ -127,8 +126,8 @@ class Cache
} }
if ($this->hidePasswordOutput && if ($this->hidePasswordOutput &&
$key == 'password' && $key == 'password' &&
!empty($value) && is_string($value)) { !empty($value) && is_string($value)) {
$this->config[$uid][$cat][$key] = new HiddenString((string)$value); $this->config[$uid][$cat][$key] = new HiddenString((string)$value);
} else { } else {
$this->config[$uid][$cat][$key] = $value; $this->config[$uid][$cat][$key] = $value;
@ -147,7 +146,7 @@ class Cache
* *
* @return bool true, if deleted * @return bool true, if deleted
*/ */
public function delete($uid, string $cat, string $key) public function delete(int $uid, string $cat, string $key): bool
{ {
if (!is_int($uid)) { if (!is_int($uid)) {
return false; return false;
@ -171,9 +170,9 @@ class Cache
/** /**
* Returns the whole configuration * Returns the whole configuration
* *
* @return array The configuration * @return string[][] The configuration
*/ */
public function getAll() public function getAll(): array
{ {
return $this->config; return $this->config;
} }
@ -181,11 +180,11 @@ class Cache
/** /**
* Returns an array with missing categories/Keys * Returns an array with missing categories/Keys
* *
* @param array $config The array to check * @param string[][] $config The array to check
* *
* @return array * @return string[][]
*/ */
public function keyDiff(array $config) public function keyDiff(array $config): array
{ {
$return = []; $return = [];

View file

@ -22,7 +22,7 @@
namespace Friendica\Core; namespace Friendica\Core;
use Friendica\App; use Friendica\App;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Model; use Friendica\Model;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -48,7 +48,7 @@ class Process
private $mode; private $mode;
/** /**
* @var IConfig * @var IManageConfigValues
*/ */
private $config; private $config;
@ -67,7 +67,7 @@ class Process
*/ */
private $pid; private $pid;
public function __construct(LoggerInterface $logger, App\Mode $mode, IConfig $config, Model\Process $processModel, string $basepath, int $pid) public function __construct(LoggerInterface $logger, App\Mode $mode, IManageConfigValues $config, Model\Process $processModel, string $basepath, int $pid)
{ {
$this->logger = $logger; $this->logger = $logger;
$this->mode = $mode; $this->mode = $mode;
@ -176,7 +176,7 @@ class Process
if (count($data) != 2) { if (count($data) != 2) {
continue; continue;
} }
list($key, $val) = $data; [$key, $val] = $data;
$meminfo[$key] = (int)trim(str_replace('kB', '', $val)); $meminfo[$key] = (int)trim(str_replace('kB', '', $val));
$meminfo[$key] = (int)($meminfo[$key] / 1024); $meminfo[$key] = (int)($meminfo[$key] / 1024);
} }

View file

@ -24,7 +24,7 @@ namespace Friendica\Core;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Network\HTTPClientOptions; use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Object\Search\ContactResult; use Friendica\Object\Search\ContactResult;
use Friendica\Object\Search\ResultList; use Friendica\Object\Search\ResultList;
use Friendica\Util\Network; use Friendica\Util\Network;
@ -228,7 +228,7 @@ class Search
$return = Contact::searchByName($search, $mode); $return = Contact::searchByName($search, $mode);
} else { } else {
$p = $page > 1 ? 'p=' . $page : ''; $p = $page > 1 ? 'p=' . $page : '';
$curlResult = DI::httpClient()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), [HTTPClientOptions::ACCEPT_CONTENT => ['application/json']]); $curlResult = DI::httpClient()->get(self::getGlobalDirectory() . '/search/people?' . $p . '&q=' . urlencode($search), [HttpClientOptions::ACCEPT_CONTENT => ['application/json']]);
if ($curlResult->isSuccess()) { if ($curlResult->isSuccess()) {
$searchResult = json_decode($curlResult->getBody(), true); $searchResult = json_decode($curlResult->getBody(), true);
if (!empty($searchResult['profiles'])) { if (!empty($searchResult['profiles'])) {

View file

@ -19,19 +19,19 @@
* *
*/ */
namespace Friendica\Core\Session; namespace Friendica\Core\Session\Capability;
/** /**
* Contains all global supported Session methods * Contains all global supported Session methods
*/ */
interface ISession interface IHandleSessions
{ {
/** /**
* Start the current session * Start the current session
* *
* @return self The own Session instance * @return self The own Session instance
*/ */
public function start(); public function start(): IHandleSessions;
/** /**
* Checks if the key exists in this session * Checks if the key exists in this session
@ -40,7 +40,7 @@ interface ISession
* *
* @return boolean True, if it exists * @return boolean True, if it exists
*/ */
public function exists(string $name); public function exists(string $name): bool;
/** /**
* Retrieves a key from the session super global or the defaults if the key is missing or the value is falsy. * Retrieves a key from the session super global or the defaults if the key is missing or the value is falsy.

View file

@ -19,14 +19,15 @@
* *
*/ */
namespace Friendica\Factory; namespace Friendica\Core\Session\Factory;
use Friendica\App; use Friendica\App;
use Friendica\Core\Cache\ICache; use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Cache\Type; use Friendica\Core\Cache\Enum;
use Friendica\Core\Config\IConfig; use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\Session; use Friendica\Core\Session\Capability\IHandleSessions;
use Friendica\Core\System; use Friendica\Core\Session\Type;
use Friendica\Core\Session\Handler;
use Friendica\Database\Database; use Friendica\Database\Database;
use Friendica\Util\Profiler; use Friendica\Util\Profiler;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
@ -34,7 +35,7 @@ use Psr\Log\LoggerInterface;
/** /**
* Factory for creating a valid Session for this run * Factory for creating a valid Session for this run
*/ */
class SessionFactory class Session
{ {
/** @var string The plain, PHP internal session management */ /** @var string The plain, PHP internal session management */
const HANDLER_NATIVE = 'native'; const HANDLER_NATIVE = 'native';
@ -46,43 +47,44 @@ class SessionFactory
const HANDLER_DEFAULT = self::HANDLER_DATABASE; const HANDLER_DEFAULT = self::HANDLER_DATABASE;
/** /**
* @param App\Mode $mode * @param App\Mode $mode
* @param App\BaseURL $baseURL * @param App\BaseURL $baseURL
* @param IConfig $config * @param IManageConfigValues $config
* @param Database $dba * @param Database $dba
* @param ICache $cache * @param ICanCache $cache
* @param LoggerInterface $logger * @param LoggerInterface $logger
* @param array $server * @param Profiler $profiler
* @param array $server
* *
* @return Session\ISession * @return IHandleSessions
*/ */
public function createSession(App\Mode $mode, App\BaseURL $baseURL, IConfig $config, Database $dba, ICache $cache, LoggerInterface $logger, Profiler $profiler, array $server = []) public function createSession(App\Mode $mode, App\BaseURL $baseURL, IManageConfigValues $config, Database $dba, ICanCache $cache, LoggerInterface $logger, Profiler $profiler, array $server = [])
{ {
$profiler->startRecording('session'); $profiler->startRecording('session');
$session = null; $session = null;
try { try {
if ($mode->isInstall() || $mode->isBackend()) { if ($mode->isInstall() || $mode->isBackend()) {
$session = new Session\Memory(); $session = new Type\Memory();
} else { } else {
$session_handler = $config->get('system', 'session_handler', self::HANDLER_DEFAULT); $session_handler = $config->get('system', 'session_handler', self::HANDLER_DEFAULT);
$handler = null; $handler = null;
switch ($session_handler) { switch ($session_handler) {
case self::HANDLER_DATABASE: case self::HANDLER_DATABASE:
$handler = new Session\Handler\Database($dba, $logger, $server); $handler = new Handler\Database($dba, $logger, $server);
break; break;
case self::HANDLER_CACHE: case self::HANDLER_CACHE:
// In case we're using the db as cache driver, use the native db session, not the cache // In case we're using the db as cache driver, use the native db session, not the cache
if ($config->get('system', 'cache_driver') === Type::DATABASE) { if ($config->get('system', 'cache_driver') === Enum\Type::DATABASE) {
$handler = new Session\Handler\Database($dba, $logger, $server); $handler = new Handler\Database($dba, $logger, $server);
} else { } else {
$handler = new Session\Handler\Cache($cache); $handler = new Handler\Cache($cache, $logger);
} }
break; break;
} }
$session = new Session\Native($baseURL, $handler); $session = new Type\Native($baseURL, $handler);
} }
} finally { } finally {
$profiler->stopRecording(); $profiler->stopRecording();

View file

@ -21,8 +21,10 @@
namespace Friendica\Core\Session\Handler; namespace Friendica\Core\Session\Handler;
use Friendica\Core\Cache\ICache; use Friendica\Core\Cache\Capability\ICanCache;
use Friendica\Core\Cache\Exception\CachePersistenceException;
use Friendica\Core\Session; use Friendica\Core\Session;
use Psr\Log\LoggerInterface;
use SessionHandlerInterface; use SessionHandlerInterface;
/** /**
@ -30,29 +32,37 @@ use SessionHandlerInterface;
*/ */
class Cache implements SessionHandlerInterface class Cache implements SessionHandlerInterface
{ {
/** @var ICache */ /** @var ICanCache */
private $cache; private $cache;
/** @var LoggerInterface */
private $logger;
public function __construct(ICache $cache) public function __construct(ICanCache $cache, LoggerInterface $logger)
{ {
$this->cache = $cache; $this->cache = $cache;
$this->logger = $logger;
} }
public function open($save_path, $session_name) public function open($path, $name): bool
{ {
return true; return true;
} }
public function read($session_id) public function read($id)
{ {
if (empty($session_id)) { if (empty($id)) {
return ''; return '';
} }
$data = $this->cache->get('session:' . $session_id); try {
if (!empty($data)) { $data = $this->cache->get('session:' . $id);
Session::$exists = true; if (!empty($data)) {
return $data; Session::$exists = true;
return $data;
}
} catch (CachePersistenceException $exception) {
$this->logger->warning('Cannot read session.'. ['id' => $id, 'exception' => $exception]);
return '';
} }
return ''; return '';
@ -65,36 +75,45 @@ class Cache implements SessionHandlerInterface
* on the case. Uses the Session::expire for existing session, 5 minutes * on the case. Uses the Session::expire for existing session, 5 minutes
* for newly created session. * for newly created session.
* *
* @param string $session_id Session ID with format: [a-z0-9]{26} * @param string $id Session ID with format: [a-z0-9]{26}
* @param string $session_data Serialized session data * @param string $data Serialized session data
* *
* @return boolean Returns false if parameters are missing, true otherwise * @return bool Returns false if parameters are missing, true otherwise
* @throws \Exception
*/ */
public function write($session_id, $session_data) public function write($id, $data): bool
{ {
if (!$session_id) { if (!$id) {
return false; return false;
} }
if (!$session_data) { if (!$data) {
return $this->destroy($session_id); return $this->destroy($id);
} }
return $this->cache->set('session:' . $session_id, $session_data, Session::$expire); try {
return $this->cache->set('session:' . $id, $data, Session::$expire);
} catch (CachePersistenceException $exception) {
$this->logger->warning('Cannot write session', ['id' => $id, 'exception' => $exception]);
return false;
}
} }
public function close() public function close(): bool
{ {
return true; return true;
} }
public function destroy($id) public function destroy($id): bool
{ {
return $this->cache->delete('session:' . $id); try {
return $this->cache->delete('session:' . $id);
} catch (CachePersistenceException $exception) {
$this->logger->warning('Cannot destroy session', ['id' => $id, 'exception' => $exception]);
return false;
}
} }
public function gc($maxlifetime) public function gc($max_lifetime): bool
{ {
return true; return true;
} }

View file

@ -52,24 +52,29 @@ class Database implements SessionHandlerInterface
$this->server = $server; $this->server = $server;
} }
public function open($save_path, $session_name) public function open($path, $name): bool
{ {
return true; return true;
} }
public function read($session_id) public function read($id)
{ {
if (empty($session_id)) { if (empty($id)) {
return ''; return '';
} }
$session = $this->dba->selectFirst('session', ['data'], ['sid' => $session_id]); try {
if ($this->dba->isResult($session)) { $session = $this->dba->selectFirst('session', ['data'], ['sid' => $id]);
Session::$exists = true; if ($this->dba->isResult($session)) {
return $session['data']; Session::$exists = true;
return $session['data'];
}
} catch (\Exception $exception) {
$this->logger->warning('Cannot read session.'. ['id' => $id, 'exception' => $exception]);
return '';
} }
$this->logger->notice('no data for session', ['session_id' => $session_id, 'uri' => $this->server['REQUEST_URI'] ?? '']); $this->logger->notice('no data for session', ['session_id' => $id, 'uri' => $this->server['REQUEST_URI'] ?? '']);
return ''; return '';
} }
@ -81,49 +86,63 @@ class Database implements SessionHandlerInterface
* on the case. Uses the Session::expire global for existing session, 5 minutes * on the case. Uses the Session::expire global for existing session, 5 minutes
* for newly created session. * for newly created session.
* *
* @param string $session_id Session ID with format: [a-z0-9]{26} * @param string $id Session ID with format: [a-z0-9]{26}
* @param string $session_data Serialized session data * @param string $data Serialized session data
* *
* @return boolean Returns false if parameters are missing, true otherwise * @return bool Returns false if parameters are missing, true otherwise
* @throws \Exception
*/ */
public function write($session_id, $session_data) public function write($id, $data): bool
{ {
if (!$session_id) { if (!$id) {
return false; return false;
} }
if (!$session_data) { if (!$data) {
return $this->destroy($session_id); return $this->destroy($id);
} }
$expire = time() + Session::$expire; $expire = time() + Session::$expire;
$default_expire = time() + 300; $default_expire = time() + 300;
if (Session::$exists) { try {
$fields = ['data' => $session_data, 'expire' => $expire]; if (Session::$exists) {
$condition = ["`sid` = ? AND (`data` != ? OR `expire` != ?)", $session_id, $session_data, $expire]; $fields = ['data' => $data, 'expire' => $expire];
$this->dba->update('session', $fields, $condition); $condition = ["`sid` = ? AND (`data` != ? OR `expire` != ?)", $id, $data, $expire];
} else { $this->dba->update('session', $fields, $condition);
$fields = ['sid' => $session_id, 'expire' => $default_expire, 'data' => $session_data]; } else {
$this->dba->insert('session', $fields); $fields = ['sid' => $id, 'expire' => $default_expire, 'data' => $data];
$this->dba->insert('session', $fields);
}
} catch (\Exception $exception) {
$this->logger->warning('Cannot write session.'. ['id' => $id, 'exception' => $exception]);
return false;
} }
return true; return true;
} }
public function close() public function close(): bool
{ {
return true; return true;
} }
public function destroy($id) public function destroy($id): bool
{ {
return $this->dba->delete('session', ['sid' => $id]); try {
return $this->dba->delete('session', ['sid' => $id]);
} catch (\Exception $exception) {
$this->logger->warning('Cannot destroy session.'. ['id' => $id, 'exception' => $exception]);
return false;
}
} }
public function gc($maxlifetime) public function gc($max_lifetime): bool
{ {
return $this->dba->delete('session', ["`expire` < ?", time()]); try {
return $this->dba->delete('session', ["`expire` < ?", time()]);
} catch (\Exception $exception) {
$this->logger->warning('Cannot use garbage collector.'. ['exception' => $exception]);
return false;
}
} }
} }

View file

@ -19,17 +19,19 @@
* *
*/ */
namespace Friendica\Core\Session; namespace Friendica\Core\Session\Type;
use Friendica\Core\Session\Capability\IHandleSessions;
/** /**
* Contains the base methods for $_SESSION interaction * Contains the base methods for $_SESSION interaction
*/ */
class AbstractSession class AbstractSession implements IHandleSessions
{ {
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function start() public function start(): IHandleSessions
{ {
return $this; return $this;
} }
@ -37,7 +39,7 @@ class AbstractSession
/** /**
* {@inheritDoc}} * {@inheritDoc}}
*/ */
public function exists(string $name) public function exists(string $name): bool
{ {
return isset($_SESSION[$name]); return isset($_SESSION[$name]);
} }

View file

@ -19,14 +19,16 @@
* *
*/ */
namespace Friendica\Core\Session; namespace Friendica\Core\Session\Type;
use Friendica\Core\Session\Capability\IHandleSessions;
/** /**
* Usable for backend processes (daemon/worker) and testing * Usable for backend processes (daemon/worker) and testing
* *
* @todo after replacing the last direct $_SESSION call, use a internal array instead of the global variable * @todo after replacing the last direct $_SESSION call, use a internal array instead of the global variable
*/ */
class Memory extends AbstractSession implements ISession class Memory extends AbstractSession implements IHandleSessions
{ {
public function __construct() public function __construct()
{ {

View file

@ -19,16 +19,17 @@
* *
*/ */
namespace Friendica\Core\Session; namespace Friendica\Core\Session\Type;
use Friendica\App; use Friendica\App;
use Friendica\Core\Session\Capability\IHandleSessions;
use Friendica\Model\User\Cookie; use Friendica\Model\User\Cookie;
use SessionHandlerInterface; use SessionHandlerInterface;
/** /**
* The native Session class which uses the PHP internal Session functions * The native Session class which uses the PHP internal Session functions
*/ */
class Native extends AbstractSession implements ISession class Native extends AbstractSession implements IHandleSessions
{ {
public function __construct(App\BaseURL $baseURL, SessionHandlerInterface $handler = null) public function __construct(App\BaseURL $baseURL, SessionHandlerInterface $handler = null)
{ {
@ -48,7 +49,7 @@ class Native extends AbstractSession implements ISession
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function start() public function start(): IHandleSessions
{ {
session_start(); session_start();
return $this; return $this;

Some files were not shown because too many files have changed in this diff Show more