From febcdd72c7b1011eea9016d60185e6fc5fd5c4ae Mon Sep 17 00:00:00 2001 From: Art4 Date: Tue, 20 May 2025 07:32:04 +0000 Subject: [PATCH] move repositories and models into own namespace, create interfaces --- src/Core/Cache/Type/DatabaseCache.php | 2 +- src/Database/DatabaseService.php | 7 +- src/Database/Entity/CacheEntity.php | 65 +-------- src/Database/Repository/CacheRepository.php | 113 ++------------- .../Repository/DeletedUserRepository.php | 33 +++++ src/Model/CacheModel.php | 83 +++++++++++ src/Model/User.php | 6 +- src/Module/Profile/Profile.php | 10 +- src/Module/User/Import.php | 10 +- src/Repository/CacheRepository.php | 135 ++++++++++++++++++ .../UserdRepository.php} | 5 +- .../Repository/CacheRepositoryTest.php | 4 +- .../UserdRepositoryTest.php} | 22 ++- 13 files changed, 301 insertions(+), 194 deletions(-) create mode 100644 src/Database/Repository/DeletedUserRepository.php create mode 100644 src/Model/CacheModel.php create mode 100644 src/Repository/CacheRepository.php rename src/{Database/Repository/UserDeletedRepository.php => Repository/UserdRepository.php} (87%) rename tests/Unit/{Database => }/Repository/CacheRepositoryTest.php (95%) rename tests/Unit/{Database/Repository/UserDeletedRepositoryTest.php => Repository/UserdRepositoryTest.php} (73%) diff --git a/src/Core/Cache/Type/DatabaseCache.php b/src/Core/Cache/Type/DatabaseCache.php index 14e22bfa72..af79b111ec 100644 --- a/src/Core/Cache/Type/DatabaseCache.php +++ b/src/Core/Cache/Type/DatabaseCache.php @@ -11,7 +11,7 @@ use Friendica\Core\Cache\Capability\ICanCache; use Friendica\Core\Cache\Enum; use Friendica\Core\Cache\Exception\CachePersistenceException; use Friendica\Database\Database; -use Friendica\Database\Repository\CacheRepository; +use Friendica\Repository\CacheRepository; use Friendica\Util\DateTimeFormat; /** diff --git a/src/Database/DatabaseService.php b/src/Database/DatabaseService.php index 3b46c95f1d..8905c7e00c 100644 --- a/src/Database/DatabaseService.php +++ b/src/Database/DatabaseService.php @@ -10,7 +10,8 @@ declare(strict_types=1); namespace Friendica\Database; use Friendica\Database\Database; -use Friendica\Database\Repository\UserDeletedRepository; +use Friendica\Database\Repository\DeletedUserRepository; +use Friendica\Repository\UserdRepository; final class DatabaseService { @@ -21,8 +22,8 @@ final class DatabaseService $this->database = $database; } - public function getUserDeletedRepository(): UserDeletedRepository + public function getDeletedUserRepository(): DeletedUserRepository { - return new UserDeletedRepository($this->database); + return new UserdRepository($this->database); } } diff --git a/src/Database/Entity/CacheEntity.php b/src/Database/Entity/CacheEntity.php index 295f4a2c26..53d4e9048f 100644 --- a/src/Database/Entity/CacheEntity.php +++ b/src/Database/Entity/CacheEntity.php @@ -9,74 +9,13 @@ declare(strict_types=1); namespace Friendica\Database\Entity; -use Exception; -use Friendica\Database\DBA; - /** * Entity for a row in the cache table */ -final class CacheEntity +interface CacheEntity { - /** - * Validates the row array and creates the entity - * - * @throws Exception If $data['v'] contains invalid data that could not unserialize. - */ - public static function createFromArray(array $data): self - { - $rawValue = array_key_exists('v', $data) ? (string) $data['v'] : ''; - $value = @unserialize($rawValue); - - // 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 ($value === false && $rawValue !== 'b:0;' ) { - throw new Exception(sprintf('Invalid value data for cache object.')); - } - - $entity = new self(); - array_key_exists('k', $data) ?? $entity->k = (string) $data['k']; - $entity->v = $rawValue; - $entity->value = $value; - array_key_exists('expired', $data) ?? $entity->expired = (string) $data['expired']; - array_key_exists('updated', $data) ?? $entity->updated = (string) $data['updated']; - - return $entity; - } - - /** - * cache key - */ - private string $k = ''; - - /** - * cached serialized value - */ - private string $v = ''; - - /** - * - * @var mixed $value cached unserialized value - */ - private $value; - - /** - * datetime of cache expiration - */ - private string $expired = DBA::NULL_DATETIME; - - /** - * datetime of cache insertion - */ - private string $updated = DBA::NULL_DATETIME; - - private function __construct() {} - /** * @return mixed */ - public function getValue() - { - return $this->value; - } + public function getValue(); } diff --git a/src/Database/Repository/CacheRepository.php b/src/Database/Repository/CacheRepository.php index 3d2e09bd15..bdd46a870d 100644 --- a/src/Database/Repository/CacheRepository.php +++ b/src/Database/Repository/CacheRepository.php @@ -9,125 +9,32 @@ declare(strict_types=1); namespace Friendica\Database\Repository; -use Friendica\Database\Database; use Friendica\Database\DatabaseException; use Friendica\Database\Entity\CacheEntity; -use Throwable; /** - * Repository for cache table + * Interface for a cache repository */ -final class CacheRepository +interface CacheRepository { - private Database $database; - - public function __construct(Database $database) - { - $this->database = $database; - } + /** + * @throws DatabaseException + * + * @return array + */ + public function getAllKeysValidUntil(string $expires): array; /** * @throws DatabaseException * * @return array */ - public function getAllKeysValidUntil(string $expires): array - { - $throw = $this->database->throwExceptionsOnErrors(true); - - try { - return $this->getAllKeys($expires, null); - } catch (Throwable $th) { - if (! $th instanceof DatabaseException) { - $th = new DatabaseException('Cannot fetch all keys without prefix', 0, '', $th); - } - - throw $th; - } finally { - $this->database->throwExceptionsOnErrors($throw); - } - } - - /** - * @throws DatabaseException - * - * @return array - */ - public function getAllKeysValidUntilWithPrefix(string $expires, string $prefix): array - { - $throw = $this->database->throwExceptionsOnErrors(true); - - try { - return $this->getAllKeys($expires, $prefix); - } catch (Throwable $th) { - if (! $th instanceof DatabaseException) { - $th = new DatabaseException(sprintf('Cannot fetch all keys with prefix `%s`', $prefix), 0, '', $th); - } - - throw $th; - } finally { - $this->database->throwExceptionsOnErrors($throw); - } - } + public function getAllKeysValidUntilWithPrefix(string $expires, string $prefix): array; /** * @throws DatabaseException * * @return CacheEntity|null */ - public function findOneByKeyValidUntil(string $key, string $expires) - { - $throw = $this->database->throwExceptionsOnErrors(true); - - try { - $cacheArray = $this->database->selectFirst( - 'cache', - ['v'], - ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, $expires] - ); - - if (!$this->database->isResult($cacheArray)) { - return null; - } - } catch (Throwable $th) { - if (! $th instanceof DatabaseException) { - $th = new DatabaseException(sprintf('Cannot get cache entry with key `%s`', $key), 0, '', $th); - } - - throw $th; - } finally { - $this->database->throwExceptionsOnErrors($throw); - } - - try { - $entity = CacheEntity::createFromArray($cacheArray); - } catch (Throwable $th) { - return null; - } - - return $entity; - } - - private function getAllKeys(string $expires, ?string $prefix = null): array - { - if ($prefix === null) { - $where = ['`expires` >= ?', $expires]; - } else { - $where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', $expires, $prefix]; - } - - $stmt = $this->database->select('cache', ['k'], $where); - - $keys = []; - - try { - while ($key = $this->database->fetch($stmt)) { - array_push($keys, $key['k']); - } - } finally { - $this->database->close($stmt); - } - - return $keys; - } + public function findOneByKeyValidUntil(string $key, string $expires); } diff --git a/src/Database/Repository/DeletedUserRepository.php b/src/Database/Repository/DeletedUserRepository.php new file mode 100644 index 0000000000..7eeea4456f --- /dev/null +++ b/src/Database/Repository/DeletedUserRepository.php @@ -0,0 +1,33 @@ +k = (string) $data['k']; + $entity->v = $rawValue; + $entity->value = $value; + array_key_exists('expired', $data) ?? $entity->expired = (string) $data['expired']; + array_key_exists('updated', $data) ?? $entity->updated = (string) $data['updated']; + + return $entity; + } + + /** + * cache key + */ + private string $k = ''; + + /** + * cached serialized value + */ + private string $v = ''; + + /** + * + * @var mixed $value cached unserialized value + */ + private $value; + + /** + * datetime of cache expiration + */ + private string $expired = DBA::NULL_DATETIME; + + /** + * datetime of cache insertion + */ + private string $updated = DBA::NULL_DATETIME; + + private function __construct() {} + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } +} diff --git a/src/Model/User.php b/src/Model/User.php index 9767d942dd..dec9400604 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -266,7 +266,7 @@ class User return $system_actor_name; } - $userDeletedRepository = DI::databaseService()->getUserDeletedRepository(); + $userDeletedRepository = DI::databaseService()->getDeletedUserRepository(); // List of possible actor names $possible_accounts = ['friendica', 'actor', 'system', 'internal']; @@ -1301,7 +1301,7 @@ class User throw new Exception(DI::l10n()->t('Your nickname can only contain a-z, 0-9 and _.')); } - $userDeletedRepository = DI::databaseService()->getUserDeletedRepository(); + $userDeletedRepository = DI::databaseService()->getDeletedUserRepository(); // Check existing and deleted accounts for this nickname. if ( @@ -1816,7 +1816,7 @@ class User $user = $hook_data['user'] ?? $user; - $userDeletedRepository = DI::databaseService()->getUserDeletedRepository(); + $userDeletedRepository = DI::databaseService()->getDeletedUserRepository(); // save username (actually the nickname as it is guaranteed // unique), so it cannot be re-registered in the future. diff --git a/src/Module/Profile/Profile.php b/src/Module/Profile/Profile.php index 9355bd7190..12f593a870 100644 --- a/src/Module/Profile/Profile.php +++ b/src/Module/Profile/Profile.php @@ -23,7 +23,7 @@ use Friendica\Core\Renderer; use Friendica\Core\Session\Capability\IHandleUserSessions; use Friendica\Database\Database; use Friendica\Database\DBA; -use Friendica\Database\Repository\UserDeletedRepository; +use Friendica\Database\Repository\DeletedUserRepository; use Friendica\Event\HtmlFilterEvent; use Friendica\Model\Contact; use Friendica\Model\Profile as ProfileModel; @@ -48,7 +48,7 @@ class Profile extends BaseProfile { /** @var Database */ private $database; - private UserDeletedRepository $userDeletedRepository; + private DeletedUserRepository $deletedUserRepository; /** @var AppHelper */ private $appHelper; /** @var IHandleUserSessions */ @@ -68,7 +68,7 @@ class Profile extends BaseProfile IHandleUserSessions $session, AppHelper $appHelper, Database $database, - UserDeletedRepository $userDeletedRepository, + DeletedUserRepository $deletedUserRepository, EventDispatcherInterface $eventDispatcher, L10n $l10n, BaseURL $baseUrl, @@ -82,7 +82,7 @@ class Profile extends BaseProfile parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); $this->database = $database; - $this->userDeletedRepository = $userDeletedRepository; + $this->deletedUserRepository = $deletedUserRepository; $this->appHelper = $appHelper; $this->session = $session; $this->config = $config; @@ -106,7 +106,7 @@ class Profile extends BaseProfile } } - if ($this->userDeletedRepository->existsByUsername($this->parameters['nickname'])) { + if ($this->deletedUserRepository->existsByUsername($this->parameters['nickname'])) { // Known deleted user $data = ActivityPub\Transmitter::getDeletedUser($this->parameters['nickname']); diff --git a/src/Module/User/Import.php b/src/Module/User/Import.php index 55095a07a5..1855755a06 100644 --- a/src/Module/User/Import.php +++ b/src/Module/User/Import.php @@ -20,7 +20,7 @@ use Friendica\Core\Worker; use Friendica\Database\Database; use Friendica\Database\DBA; use Friendica\Database\DBStructure; -use Friendica\Database\Repository\UserDeletedRepository; +use Friendica\Database\Repository\DeletedUserRepository; use Friendica\Model\Photo; use Friendica\Model\Profile; use Friendica\Module\Response; @@ -49,7 +49,7 @@ class Import extends \Friendica\BaseModule /** @var Database */ private $database; - private UserDeletedRepository $userDeletedRepository; + private DeletedUserRepository $deletedUserRepository; /** @var PermissionSet */ private $permissionSet; @@ -62,7 +62,7 @@ class Import extends \Friendica\BaseModule PermissionSet $permissionSet, IManagePersonalConfigValues $pconfig, Database $database, - UserDeletedRepository $userDeletedRepository, + DeletedUserRepository $deletedUserRepository, SystemMessages $systemMessages, IManageConfigValues $config, L10n $l10n, @@ -80,7 +80,7 @@ class Import extends \Friendica\BaseModule $this->pconfig = $pconfig; $this->systemMessages = $systemMessages; $this->database = $database; - $this->userDeletedRepository = $userDeletedRepository; + $this->deletedUserRepository = $deletedUserRepository; $this->permissionSet = $permissionSet; $this->session = $session; } @@ -233,7 +233,7 @@ class Import extends \Friendica\BaseModule // check for username // check if username matches deleted account if ($this->database->exists('user', ['nickname' => $account['user']['nickname']]) - || $this->userDeletedRepository->existsByUsername($account['user']['nickname'])) { + || $this->deletedUserRepository->existsByUsername($account['user']['nickname'])) { $this->systemMessages->addNotice($this->t("User '%s' already exists on this server!", $account['user']['nickname'])); return; } diff --git a/src/Repository/CacheRepository.php b/src/Repository/CacheRepository.php new file mode 100644 index 0000000000..6cdba5eafc --- /dev/null +++ b/src/Repository/CacheRepository.php @@ -0,0 +1,135 @@ +database = $database; + } + + /** + * @throws DatabaseException + * + * @return array + */ + public function getAllKeysValidUntil(string $expires): array + { + $throw = $this->database->throwExceptionsOnErrors(true); + + try { + return $this->getAllKeys($expires, null); + } catch (Throwable $th) { + if (! $th instanceof DatabaseException) { + $th = new DatabaseException('Cannot fetch all keys without prefix', 0, '', $th); + } + + throw $th; + } finally { + $this->database->throwExceptionsOnErrors($throw); + } + } + + /** + * @throws DatabaseException + * + * @return array + */ + public function getAllKeysValidUntilWithPrefix(string $expires, string $prefix): array + { + $throw = $this->database->throwExceptionsOnErrors(true); + + try { + return $this->getAllKeys($expires, $prefix); + } catch (Throwable $th) { + if (! $th instanceof DatabaseException) { + $th = new DatabaseException(sprintf('Cannot fetch all keys with prefix `%s`', $prefix), 0, '', $th); + } + + throw $th; + } finally { + $this->database->throwExceptionsOnErrors($throw); + } + } + + /** + * @throws DatabaseException + * + * @return CacheEntity|null + */ + public function findOneByKeyValidUntil(string $key, string $expires) + { + $throw = $this->database->throwExceptionsOnErrors(true); + + try { + $cacheArray = $this->database->selectFirst( + 'cache', + ['v'], + ['`k` = ? AND (`expires` >= ? OR `expires` = -1)', $key, $expires] + ); + + if (!$this->database->isResult($cacheArray)) { + return null; + } + } catch (Throwable $th) { + if (! $th instanceof DatabaseException) { + $th = new DatabaseException(sprintf('Cannot get cache entry with key `%s`', $key), 0, '', $th); + } + + throw $th; + } finally { + $this->database->throwExceptionsOnErrors($throw); + } + + try { + $entity = CacheModel::createFromArray($cacheArray); + } catch (Throwable $th) { + return null; + } + + return $entity; + } + + private function getAllKeys(string $expires, ?string $prefix = null): array + { + if ($prefix === null) { + $where = ['`expires` >= ?', $expires]; + } else { + $where = ['`expires` >= ? AND `k` LIKE CONCAT(?, \'%\')', $expires, $prefix]; + } + + $stmt = $this->database->select('cache', ['k'], $where); + + $keys = []; + + try { + while ($key = $this->database->fetch($stmt)) { + array_push($keys, $key['k']); + } + } finally { + $this->database->close($stmt); + } + + return $keys; + } +} diff --git a/src/Database/Repository/UserDeletedRepository.php b/src/Repository/UserdRepository.php similarity index 87% rename from src/Database/Repository/UserDeletedRepository.php rename to src/Repository/UserdRepository.php index bd3e2301f7..b6f01772c8 100644 --- a/src/Database/Repository/UserDeletedRepository.php +++ b/src/Repository/UserdRepository.php @@ -7,16 +7,17 @@ declare(strict_types=1); -namespace Friendica\Database\Repository; +namespace Friendica\Repository; use Exception; use Friendica\Database\Database; use Friendica\Database\DatabaseException; +use Friendica\Database\Repository\DeletedUserRepository; /** * Repository for deleted users */ -final class UserDeletedRepository +final class UserdRepository implements DeletedUserRepository { private Database $database; diff --git a/tests/Unit/Database/Repository/CacheRepositoryTest.php b/tests/Unit/Repository/CacheRepositoryTest.php similarity index 95% rename from tests/Unit/Database/Repository/CacheRepositoryTest.php rename to tests/Unit/Repository/CacheRepositoryTest.php index b5cc1d453e..01a1f01348 100644 --- a/tests/Unit/Database/Repository/CacheRepositoryTest.php +++ b/tests/Unit/Repository/CacheRepositoryTest.php @@ -7,11 +7,11 @@ declare(strict_types=1); -namespace Friendica\Test\Unit\Database\Repository; +namespace Friendica\Test\Unit\Repository; use Friendica\Database\Database; use Friendica\Database\DatabaseException; -use Friendica\Database\Repository\CacheRepository; +use Friendica\Repository\CacheRepository; use PHPUnit\Framework\TestCase; use Throwable; diff --git a/tests/Unit/Database/Repository/UserDeletedRepositoryTest.php b/tests/Unit/Repository/UserdRepositoryTest.php similarity index 73% rename from tests/Unit/Database/Repository/UserDeletedRepositoryTest.php rename to tests/Unit/Repository/UserdRepositoryTest.php index 80b706dd46..ebfeb5f3b2 100644 --- a/tests/Unit/Database/Repository/UserDeletedRepositoryTest.php +++ b/tests/Unit/Repository/UserdRepositoryTest.php @@ -7,15 +7,23 @@ declare(strict_types=1); -namespace Friendica\Test\Unit\Database\Repository; +namespace Friendica\Test\Unit\Repository; use Friendica\Database\Database; use Friendica\Database\DatabaseException; -use Friendica\Database\Repository\UserDeletedRepository; +use Friendica\Database\Repository\DeletedUserRepository; +use Friendica\Repository\UserdRepository; use PHPUnit\Framework\TestCase; -class UserDeletedRepositoryTest extends TestCase +class UserdRepositoryTest extends TestCase { + public function testImplementationOfInterfaces(): void + { + $repo = new UserdRepository($this->createMock(Database::class)); + + $this->assertInstanceOf(DeletedUserRepository::class, $repo); + } + public function testInsertByUsernameCallsDatabase(): void { $database = $this->createMock(Database::class); @@ -23,7 +31,7 @@ class UserDeletedRepositoryTest extends TestCase ['userd', ['username' => 'test'], 0, true], ]); - $repo = new UserDeletedRepository($database); + $repo = new UserdRepository($database); $repo->insertByUsername('test'); } @@ -36,7 +44,7 @@ class UserDeletedRepositoryTest extends TestCase new DatabaseException('An error occured.', 0, 'SQL query') ); - $repo = new UserDeletedRepository($database); + $repo = new UserdRepository($database); $this->expectException(DatabaseException::class); @@ -50,7 +58,7 @@ class UserDeletedRepositoryTest extends TestCase ['userd', ['username' => 'test'], true], ]); - $repo = new UserDeletedRepository($database); + $repo = new UserdRepository($database); $this->assertTrue($repo->existsByUsername('test')); } @@ -62,7 +70,7 @@ class UserDeletedRepositoryTest extends TestCase ['userd', ['username' => 'test'], false], ]); - $repo = new UserDeletedRepository($database); + $repo = new UserdRepository($database); $this->assertFalse($repo->existsByUsername('test')); }