From 6a3a4a1a5620fd068c1fe7beef63ed6f22e3331e Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Mon, 21 Oct 2024 16:30:09 -0100 Subject: [PATCH] feat(user-prefs): iterator instead of array on search Signed-off-by: Maxence Lange --- .../Version31000Date20240814184402.php | 2 +- lib/composer/composer/autoload_classmap.php | 14 ++- lib/composer/composer/autoload_static.php | 14 ++- lib/private/AllConfig.php | 15 +-- lib/private/{ => Config}/UserPreferences.php | 102 +++++++----------- lib/private/Server.php | 4 +- .../Exceptions/IncorrectTypeException.php} | 4 +- .../Exceptions/TypeConflictException.php | 6 +- .../Exceptions/UnknownKeyException.php | 6 +- .../IUserPreferences.php | 38 +++++-- .../{UserPreferences => Config}/ValueType.php | 9 +- lib/public/IConfig.php | 2 +- .../Exceptions/IncorrectTypeException.php | 15 --- tests/lib/UserPreferencesTest.php | 20 ++-- 14 files changed, 112 insertions(+), 139 deletions(-) rename lib/private/{ => Config}/UserPreferences.php (94%) rename lib/public/{UserPreferences/Exceptions/UserPreferencesException.php => Config/Exceptions/IncorrectTypeException.php} (68%) rename lib/public/{UserPreferences => Config}/Exceptions/TypeConflictException.php (64%) rename lib/public/{UserPreferences => Config}/Exceptions/UnknownKeyException.php (64%) rename lib/public/{UserPreferences => Config}/IUserPreferences.php (95%) rename lib/public/{UserPreferences => Config}/ValueType.php (87%) delete mode 100644 lib/public/UserPreferences/Exceptions/IncorrectTypeException.php diff --git a/core/Migrations/Version31000Date20240814184402.php b/core/Migrations/Version31000Date20240814184402.php index 87b7e93db97d0..14b32a704beda 100644 --- a/core/Migrations/Version31000Date20240814184402.php +++ b/core/Migrations/Version31000Date20240814184402.php @@ -38,7 +38,7 @@ public function changeSchema(IOutput $output, Closure $schemaClosure, array $opt $table->addColumn('lazy', Types::SMALLINT, ['notnull' => true, 'default' => 0, 'length' => 1, 'unsigned' => true]); $table->addColumn('type', Types::SMALLINT, ['notnull' => true, 'default' => 0, 'unsigned' => true]); $table->addColumn('flags', Types::INTEGER, ['notnull' => true, 'default' => 0, 'unsigned' => true]); - $table->addColumn('indexed', Types::STRING, ['notnull' => true, 'default' => '', 'length' => 64]); + $table->addColumn('indexed', Types::STRING, ['notnull' => false, 'default' => '', 'length' => 64]); // removing this index from Version13000Date20170718121200 // $table->addIndex(['appid', 'configkey'], 'preferences_app_key'); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 51fa632822a9d..160fae5abe858 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -221,6 +221,11 @@ 'OCP\\Common\\Exception\\NotFoundException' => $baseDir . '/lib/public/Common/Exception/NotFoundException.php', 'OCP\\Config\\BeforePreferenceDeletedEvent' => $baseDir . '/lib/public/Config/BeforePreferenceDeletedEvent.php', 'OCP\\Config\\BeforePreferenceSetEvent' => $baseDir . '/lib/public/Config/BeforePreferenceSetEvent.php', + 'OCP\\Config\\Exceptions\\IncorrectTypeException' => $baseDir . '/lib/public/Config/Exceptions/IncorrectTypeException.php', + 'OCP\\Config\\Exceptions\\TypeConflictException' => $baseDir . '/lib/public/Config/Exceptions/TypeConflictException.php', + 'OCP\\Config\\Exceptions\\UnknownKeyException' => $baseDir . '/lib/public/Config/Exceptions/UnknownKeyException.php', + 'OCP\\Config\\IUserPreferences' => $baseDir . '/lib/public/Config/IUserPreferences.php', + 'OCP\\Config\\ValueType' => $baseDir . '/lib/public/Config/ValueType.php', 'OCP\\Console\\ConsoleEvent' => $baseDir . '/lib/public/Console/ConsoleEvent.php', 'OCP\\Console\\ReservedOptions' => $baseDir . '/lib/public/Console/ReservedOptions.php', 'OCP\\Constants' => $baseDir . '/lib/public/Constants.php', @@ -836,13 +841,6 @@ 'OCP\\UserMigration\\ISizeEstimationMigrator' => $baseDir . '/lib/public/UserMigration/ISizeEstimationMigrator.php', 'OCP\\UserMigration\\TMigratorBasicVersionHandling' => $baseDir . '/lib/public/UserMigration/TMigratorBasicVersionHandling.php', 'OCP\\UserMigration\\UserMigrationException' => $baseDir . '/lib/public/UserMigration/UserMigrationException.php', - 'OCP\\UserPreferences\\Exceptions\\IncorrectTypeException' => $baseDir . '/lib/public/UserPreferences/Exceptions/IncorrectTypeException.php', - 'OCP\\UserPreferences\\Exceptions\\TypeConflictException' => $baseDir . '/lib/public/UserPreferences/Exceptions/TypeConflictException.php', - 'OCP\\UserPreferences\\Exceptions\\UnknownKeyException' => $baseDir . '/lib/public/UserPreferences/Exceptions/UnknownKeyException.php', - 'OCP\\UserPreferences\\Exceptions\\UserPreferencesException' => $baseDir . '/lib/public/UserPreferences/Exceptions/UserPreferencesException.php', - 'OCP\\UserPreferences\\IUserPreferences' => $baseDir . '/lib/public/UserPreferences/IUserPreferences.php', - 'OCP\\UserPreferences\\ValueType' => $baseDir . '/lib/public/UserPreferences/ValueType.php', - 'OCP\\UserPreferences\\ValueTypeDefinition' => $baseDir . '/lib/public/UserPreferences/ValueTypeDefinition.php', 'OCP\\UserStatus\\IManager' => $baseDir . '/lib/public/UserStatus/IManager.php', 'OCP\\UserStatus\\IProvider' => $baseDir . '/lib/public/UserStatus/IProvider.php', 'OCP\\UserStatus\\IUserStatus' => $baseDir . '/lib/public/UserStatus/IUserStatus.php', @@ -1122,6 +1120,7 @@ 'OC\\Comments\\Manager' => $baseDir . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => $baseDir . '/lib/private/Comments/ManagerFactory.php', 'OC\\Config' => $baseDir . '/lib/private/Config.php', + 'OC\\Config\\UserPreferences' => $baseDir . '/lib/private/Config/UserPreferences.php', 'OC\\Console\\Application' => $baseDir . '/lib/private/Console/Application.php', 'OC\\Console\\TimestampFormatter' => $baseDir . '/lib/private/Console/TimestampFormatter.php', 'OC\\ContactsManager' => $baseDir . '/lib/private/ContactsManager.php', @@ -2002,7 +2001,6 @@ 'OC\\Updater\\Exceptions\\ReleaseMetadataException' => $baseDir . '/lib/private/Updater/Exceptions/ReleaseMetadataException.php', 'OC\\Updater\\ReleaseMetadata' => $baseDir . '/lib/private/Updater/ReleaseMetadata.php', 'OC\\Updater\\VersionCheck' => $baseDir . '/lib/private/Updater/VersionCheck.php', - 'OC\\UserPreferences' => $baseDir . '/lib/private/UserPreferences.php', 'OC\\UserStatus\\ISettableProvider' => $baseDir . '/lib/private/UserStatus/ISettableProvider.php', 'OC\\UserStatus\\Manager' => $baseDir . '/lib/private/UserStatus/Manager.php', 'OC\\User\\AvailabilityCoordinator' => $baseDir . '/lib/private/User/AvailabilityCoordinator.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 2e8759ebadadf..82950496fd228 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -254,6 +254,11 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Common\\Exception\\NotFoundException' => __DIR__ . '/../../..' . '/lib/public/Common/Exception/NotFoundException.php', 'OCP\\Config\\BeforePreferenceDeletedEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceDeletedEvent.php', 'OCP\\Config\\BeforePreferenceSetEvent' => __DIR__ . '/../../..' . '/lib/public/Config/BeforePreferenceSetEvent.php', + 'OCP\\Config\\Exceptions\\IncorrectTypeException' => __DIR__ . '/../../..' . '/lib/public/Config/Exceptions/IncorrectTypeException.php', + 'OCP\\Config\\Exceptions\\TypeConflictException' => __DIR__ . '/../../..' . '/lib/public/Config/Exceptions/TypeConflictException.php', + 'OCP\\Config\\Exceptions\\UnknownKeyException' => __DIR__ . '/../../..' . '/lib/public/Config/Exceptions/UnknownKeyException.php', + 'OCP\\Config\\IUserPreferences' => __DIR__ . '/../../..' . '/lib/public/Config/IUserPreferences.php', + 'OCP\\Config\\ValueType' => __DIR__ . '/../../..' . '/lib/public/Config/ValueType.php', 'OCP\\Console\\ConsoleEvent' => __DIR__ . '/../../..' . '/lib/public/Console/ConsoleEvent.php', 'OCP\\Console\\ReservedOptions' => __DIR__ . '/../../..' . '/lib/public/Console/ReservedOptions.php', 'OCP\\Constants' => __DIR__ . '/../../..' . '/lib/public/Constants.php', @@ -869,13 +874,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\UserMigration\\ISizeEstimationMigrator' => __DIR__ . '/../../..' . '/lib/public/UserMigration/ISizeEstimationMigrator.php', 'OCP\\UserMigration\\TMigratorBasicVersionHandling' => __DIR__ . '/../../..' . '/lib/public/UserMigration/TMigratorBasicVersionHandling.php', 'OCP\\UserMigration\\UserMigrationException' => __DIR__ . '/../../..' . '/lib/public/UserMigration/UserMigrationException.php', - 'OCP\\UserPreferences\\Exceptions\\IncorrectTypeException' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/Exceptions/IncorrectTypeException.php', - 'OCP\\UserPreferences\\Exceptions\\TypeConflictException' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/Exceptions/TypeConflictException.php', - 'OCP\\UserPreferences\\Exceptions\\UnknownKeyException' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/Exceptions/UnknownKeyException.php', - 'OCP\\UserPreferences\\Exceptions\\UserPreferencesException' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/Exceptions/UserPreferencesException.php', - 'OCP\\UserPreferences\\IUserPreferences' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/IUserPreferences.php', - 'OCP\\UserPreferences\\ValueType' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/ValueType.php', - 'OCP\\UserPreferences\\ValueTypeDefinition' => __DIR__ . '/../../..' . '/lib/public/UserPreferences/ValueTypeDefinition.php', 'OCP\\UserStatus\\IManager' => __DIR__ . '/../../..' . '/lib/public/UserStatus/IManager.php', 'OCP\\UserStatus\\IProvider' => __DIR__ . '/../../..' . '/lib/public/UserStatus/IProvider.php', 'OCP\\UserStatus\\IUserStatus' => __DIR__ . '/../../..' . '/lib/public/UserStatus/IUserStatus.php', @@ -1155,6 +1153,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Comments\\Manager' => __DIR__ . '/../../..' . '/lib/private/Comments/Manager.php', 'OC\\Comments\\ManagerFactory' => __DIR__ . '/../../..' . '/lib/private/Comments/ManagerFactory.php', 'OC\\Config' => __DIR__ . '/../../..' . '/lib/private/Config.php', + 'OC\\Config\\UserPreferences' => __DIR__ . '/../../..' . '/lib/private/Config/UserPreferences.php', 'OC\\Console\\Application' => __DIR__ . '/../../..' . '/lib/private/Console/Application.php', 'OC\\Console\\TimestampFormatter' => __DIR__ . '/../../..' . '/lib/private/Console/TimestampFormatter.php', 'OC\\ContactsManager' => __DIR__ . '/../../..' . '/lib/private/ContactsManager.php', @@ -2035,7 +2034,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Updater\\Exceptions\\ReleaseMetadataException' => __DIR__ . '/../../..' . '/lib/private/Updater/Exceptions/ReleaseMetadataException.php', 'OC\\Updater\\ReleaseMetadata' => __DIR__ . '/../../..' . '/lib/private/Updater/ReleaseMetadata.php', 'OC\\Updater\\VersionCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/VersionCheck.php', - 'OC\\UserPreferences' => __DIR__ . '/../../..' . '/lib/private/UserPreferences.php', 'OC\\UserStatus\\ISettableProvider' => __DIR__ . '/../../..' . '/lib/private/UserStatus/ISettableProvider.php', 'OC\\UserStatus\\Manager' => __DIR__ . '/../../..' . '/lib/private/UserStatus/Manager.php', 'OC\\User\\AvailabilityCoordinator' => __DIR__ . '/../../..' . '/lib/private/User/AvailabilityCoordinator.php', diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index b2ebe322d9eef..8f7e5359c967f 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -6,13 +6,14 @@ */ namespace OC; +use OC\Config\UserPreferences; use OCP\Cache\CappedMemoryCache; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\IUserPreferences; +use OCP\Config\ValueType; use OCP\IConfig; use OCP\IDBConnection; use OCP\PreConditionNotMetException; -use OCP\UserPreferences\Exceptions\TypeConflictException; -use OCP\UserPreferences\IUserPreferences; -use OCP\UserPreferences\ValueType; /** * Class to combine all the configuration options ownCloud offers @@ -376,11 +377,11 @@ public function getUserValueForUsers($appName, $key, $userIds) { * @param string $appName the app to get the user for * @param string $key the key to get the user for * @param string $value the value to get the user for - * @return list of user IDs + * @return array of user IDs * @deprecated 31.0.0 - use {@see IUserPreferences::searchUsersByValueString} directly */ public function getUsersForUserValue($appName, $key, $value) { - return \OCP\Server::get(IUserPreferences::class)->searchUsersByValueDeprecated($appName, $key, $value); + return iterator_to_array(\OCP\Server::get(IUserPreferences::class)->searchUsersByValueString($appName, $key, $value)); } /** @@ -389,7 +390,7 @@ public function getUsersForUserValue($appName, $key, $value) { * @param string $appName the app to get the user for * @param string $key the key to get the user for * @param string $value the value to get the user for - * @return list of user IDs + * @return array of user IDs * @deprecated 31.0.0 - use {@see IUserPreferences::searchUsersByValueString} directly */ public function getUsersForUserValueCaseInsensitive($appName, $key, $value) { @@ -397,7 +398,7 @@ public function getUsersForUserValueCaseInsensitive($appName, $key, $value) { return $this->getUsersForUserValue($appName, $key, strtolower($value)); } - return \OCP\Server::get(IUserPreferences::class)->searchUsersByValueDeprecated($appName, $key, $value, true); + return iterator_to_array(\OCP\Server::get(IUserPreferences::class)->searchUsersByValueString($appName, $key, $value, true)); } public function getSystemConfig() { diff --git a/lib/private/UserPreferences.php b/lib/private/Config/UserPreferences.php similarity index 94% rename from lib/private/UserPreferences.php rename to lib/private/Config/UserPreferences.php index 22313292e94af..6dd9c63383671 100644 --- a/lib/private/UserPreferences.php +++ b/lib/private/Config/UserPreferences.php @@ -6,21 +6,22 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OC; +namespace OC\Config; +use Generator; use InvalidArgumentException; use JsonException; +use OCP\Config\Exceptions\IncorrectTypeException; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\Exceptions\UnknownKeyException; +use OCP\Config\IUserPreferences; +use OCP\Config\ValueType; use OCP\DB\Exception as DBException; use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; use OCP\Security\ICrypto; -use OCP\UserPreferences\Exceptions\IncorrectTypeException; -use OCP\UserPreferences\Exceptions\TypeConflictException; -use OCP\UserPreferences\Exceptions\UnknownKeyException; -use OCP\UserPreferences\IUserPreferences; -use OCP\UserPreferences\ValueType; use Psr\Log\LoggerInterface; /** @@ -52,7 +53,7 @@ class UserPreferences implements IUserPreferences { private array $fastCache = []; // cache for normal preference keys /** @var array>> ['user_id' => ['app_id' => ['key' => 'value']]] */ private array $lazyCache = []; // cache for lazy preference keys - /** @var array|<'flags', int>> ['user_id' => ['app_id' => ['key' => ['type' => ValueType, 'flags' => bitflag]]] */ + /** @var array>>> ['user_id' => ['app_id' => ['key' => ['type' => ValueType, 'flags' => bitflag]]]] */ private array $valueDetails = []; // type for all preference values /** @var array>> ['user_id' => ['app_id' => ['key' => bitflag]]] */ private array $valueTypes = []; // type for all preference values @@ -358,7 +359,7 @@ public function getValuesByUsers( while ($row = $result->fetch()) { $value = $row['configvalue']; try { - $value = $this->convertTypedValue($value, $typedAs ?? ValueType::from((int) $row['type'])); + $value = $this->convertTypedValue($value, $typedAs ?? ValueType::from((int)$row['type'])); } catch (IncorrectTypeException) { } $values[$row['userid']] = $value; @@ -393,29 +394,13 @@ public function getValuesByUsers( * @param string $value preference value * @param bool $caseInsensitive non-case-sensitive search, only works if $value is a string * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): array { + public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): Generator { return $this->searchUsersByTypedValue($app, $key, $value, $caseInsensitive); } - /** - * @inheritDoc - * - * @param string $app id of the app - * @param string $key preference key - * @param string $value preference value - * @param bool $caseInsensitive non-case-sensitive search, only works if $value is a string - * @internal - * @deprecated since 31.0.0 - {@see } - * @return list - * @since 31.0.0 - */ - public function searchUsersByValueDeprecated(string $app, string $key, string $value, bool $caseInsensitive = false): array { - return $this->searchUsersByTypedValue($app, $key, $value, $caseInsensitive, true); - } - /** * @inheritDoc * @@ -423,10 +408,10 @@ public function searchUsersByValueDeprecated(string $app, string $key, string $v * @param string $key preference key * @param int $value preference value * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValueInt(string $app, string $key, int $value): array { + public function searchUsersByValueInt(string $app, string $key, int $value): Generator { return $this->searchUsersByValueString($app, $key, (string)$value); } @@ -437,10 +422,10 @@ public function searchUsersByValueInt(string $app, string $key, int $value): arr * @param string $key preference key * @param array $values list of preference values * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValues(string $app, string $key, array $values): array { + public function searchUsersByValues(string $app, string $key, array $values): Generator { return $this->searchUsersByTypedValue($app, $key, $values); } @@ -451,10 +436,10 @@ public function searchUsersByValues(string $app, string $key, array $values): ar * @param string $key preference key * @param bool $value preference value * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValueBool(string $app, string $key, bool $value): array { + public function searchUsersByValueBool(string $app, string $key, bool $value): Generator { $values = ['0', 'off', 'false', 'no']; if ($value) { $values = ['1', 'on', 'true', 'yes']; @@ -470,11 +455,10 @@ public function searchUsersByValueBool(string $app, string $key, bool $value): a * @param string $key * @param string|array $value * @param bool $caseInsensitive - * @param bool $withinNotIndexedField DEPRECATED: should only be used to stay compatible with not-indexed/pre-31 preferences value * - * @return list + * @return Generator */ - private function searchUsersByTypedValue(string $app, string $key, string|array $value, bool $caseInsensitive = false): array { + private function searchUsersByTypedValue(string $app, string $key, string|array $value, bool $caseInsensitive = false): Generator { $this->assertParams('', $app, $key, allowEmptyUser: true); $qb = $this->connection->getQueryBuilder(); @@ -483,14 +467,14 @@ private function searchUsersByTypedValue(string $app, string $key, string|array $qb->where($qb->expr()->eq('appid', $qb->createNamedParameter($app))); $qb->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter($key))); - // search within 'indexed' OR 'configvalue' (but if 'flags' is not set as indexed) + // search within 'indexed' OR 'configvalue' only if 'flags' is set as not indexed // TODO: when implementing config lexicon remove the searches on 'configvalue' if value is set as indexed $configValueColumn = ($this->connection->getDatabaseProvider() === IDBConnection::PLATFORM_ORACLE) ? $qb->expr()->castColumn('configvalue', IQueryBuilder::PARAM_STR) : 'configvalue'; if (is_array($value)) { $where = $qb->expr()->orX( $qb->expr()->in('indexed', $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR_ARRAY)), $qb->expr()->andX( - $qb->createFunction('NOT ' . $qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED)), + $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), $qb->expr()->in($configValueColumn, $qb->createNamedParameter($value, IQueryBuilder::PARAM_STR_ARRAY)) ) ); @@ -499,7 +483,7 @@ private function searchUsersByTypedValue(string $app, string $key, string|array $where = $qb->expr()->orX( $qb->expr()->eq($qb->func()->lower('indexed'), $qb->createNamedParameter(strtolower($value))), $qb->expr()->andX( - $qb->createFunction('NOT ' . $qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED)), + $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), $qb->expr()->eq($qb->func()->lower($configValueColumn), $qb->createNamedParameter(strtolower($value))) ) ); @@ -507,7 +491,7 @@ private function searchUsersByTypedValue(string $app, string $key, string|array $where = $qb->expr()->orX( $qb->expr()->eq('indexed', $qb->createNamedParameter($value)), $qb->expr()->andX( - $qb->createFunction('NOT ' . $qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED)), + $qb->expr()->neq($qb->expr()->bitwiseAnd('flags', self::FLAG_INDEXED), $qb->createNamedParameter(self::FLAG_INDEXED, IQueryBuilder::PARAM_INT)), $qb->expr()->eq($configValueColumn, $qb->createNamedParameter($value)) ) ); @@ -515,15 +499,10 @@ private function searchUsersByTypedValue(string $app, string $key, string|array } $qb->andWhere($where); - - $userIds = []; $result = $qb->executeQuery(); - $rows = $result->fetchAll(); - foreach ($rows as $row) { - $userIds[] = $row['userid']; + while ($row = $result->fetch()) { + yield $row['userid']; } - - return $userIds; } /** @@ -726,7 +705,7 @@ private function getTypedValue( bool $lazy, ValueType $type, ): string { - $this->assertParams($userId, $app, $key, valueType: $type); + $this->assertParams($userId, $app, $key); $this->loadPreferences($userId, $lazy); /** @@ -793,7 +772,7 @@ public function getValueType(string $userId, string $app, string $key, ?bool $la * @param string $key preference key * @param bool $lazy lazy loading * - * @return ValueType type of the value + * @return int flags applied to value * @throws UnknownKeyException if preference key is not known * @throws IncorrectTypeException if preferences value type is not known * @since 31.0.0 @@ -978,7 +957,7 @@ public function setValueBool( string $key, bool $value, bool $lazy = false, - int $flags = 0 + int $flags = 0, ): bool { return $this->setTypedValue( $userId, @@ -1058,7 +1037,7 @@ private function setTypedValue( int $flags, ValueType $type, ): bool { - $this->assertParams($userId, $app, $key, valueType: $type); + $this->assertParams($userId, $app, $key); $this->loadPreferences($userId, $lazy); $inserted = $refreshCache = false; @@ -1074,7 +1053,7 @@ private function setTypedValue( if ($type !== ValueType::ARRAY && $this->isFlagged(self::FLAG_INDEXED, $flags)) { if ($this->isFlagged(self::FLAG_SENSITIVE, $flags)) { $this->logger->warning('sensitive value are not to be indexed'); - } else if (strlen($value) > self::USER_MAX_LENGTH) { + } elseif (strlen($value) > self::USER_MAX_LENGTH) { $this->logger->warning('value is too lengthy to be indexed'); } else { $indexed = $value; @@ -1203,7 +1182,7 @@ private function setTypedValue( * @since 31.0.0 */ public function updateType(string $userId, string $app, string $key, ValueType $type = ValueType::MIXED): bool { - $this->assertParams($userId, $app, $key, valueType: $type); + $this->assertParams($userId, $app, $key); $this->loadPreferencesAll($userId); $this->isLazy($userId, $app, $key); // confirm key exists @@ -1348,11 +1327,11 @@ public function updateIndexed(string $userId, string $app, string $key, bool $in $update = $this->connection->getQueryBuilder(); $update->update('preferences') - ->set('flags', $update->createNamedParameter($flags, IQueryBuilder::PARAM_INT)) - ->set('indexed', $update->createNamedParameter($indexed)) - ->where($update->expr()->eq('userid', $update->createNamedParameter($userId))) - ->andWhere($update->expr()->eq('appid', $update->createNamedParameter($app))) - ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); + ->set('flags', $update->createNamedParameter($flags, IQueryBuilder::PARAM_INT)) + ->set('indexed', $update->createNamedParameter($indexed)) + ->where($update->expr()->eq('userid', $update->createNamedParameter($userId))) + ->andWhere($update->expr()->eq('appid', $update->createNamedParameter($app))) + ->andWhere($update->expr()->eq('configkey', $update->createNamedParameter($key))); $update->executeStatement(); $this->valueDetails[$userId][$app][$key]['flags'] = $flags; @@ -1634,7 +1613,6 @@ private function assertParams( string $prefKey = '', bool $allowEmptyUser = false, bool $allowEmptyApp = false, - ?ValueType $valueType = null, ): void { if (!$allowEmptyUser && $userId === '') { throw new InvalidArgumentException('userId cannot be an empty string'); @@ -1651,12 +1629,6 @@ private function assertParams( if (strlen($prefKey) > self::KEY_MAX_LENGTH) { throw new InvalidArgumentException('Value (' . $prefKey . ') for key is too long (' . self::KEY_MAX_LENGTH . ')'); } - if ($valueType !== null) { -// $valueFlag = $valueType->value; -// if (ValueType::tryFrom($valueFlag) === null) { -// throw new InvalidArgumentException('Unknown value type'); -// } - } } private function loadPreferencesAll(string $userId): void { @@ -1697,7 +1669,7 @@ private function loadPreferences(string $userId, ?bool $lazy = false): void { } else { $this->fastCache[$userId][$row['appid']][$row['configkey']] = $row['configvalue'] ?? ''; } - $this->valueDetails[$userId][$row['appid']][$row['configkey']] = ['type' => ValueType::from((int)($row['type'] ?? 0)), 'flags' => (int) $row['flags']]; + $this->valueDetails[$userId][$row['appid']][$row['configkey']] = ['type' => ValueType::from((int)($row['type'] ?? 0)), 'flags' => (int)$row['flags']]; } $result->closeCursor(); $this->setAsLoaded($userId, $lazy); diff --git a/lib/private/Server.php b/lib/private/Server.php index 81cca81bbb162..17495bfe3c2ca 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -138,6 +138,7 @@ use OCP\Collaboration\Reference\IReferenceManager; use OCP\Command\IBus; use OCP\Comments\ICommentsManager; +use OCP\Config\IUserPreferences; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\Contacts\ContactsMenu\IContactsStore; use OCP\Defaults; @@ -237,7 +238,6 @@ use OCP\User\Events\UserLoggedInWithCookieEvent; use OCP\User\Events\UserLoggedOutEvent; use OCP\User\IAvailabilityCoordinator; -use OCP\UserPreferences\IUserPreferences; use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; @@ -567,7 +567,7 @@ public function __construct($webRoot, \OC\Config $config) { }); $this->registerAlias(IAppConfig::class, \OC\AppConfig::class); - $this->registerAlias(IUserPreferences::class, \OC\UserPreferences::class); + $this->registerAlias(IUserPreferences::class, \OC\Config\UserPreferences::class); $this->registerService(IFactory::class, function (Server $c) { return new \OC\L10N\Factory( diff --git a/lib/public/UserPreferences/Exceptions/UserPreferencesException.php b/lib/public/Config/Exceptions/IncorrectTypeException.php similarity index 68% rename from lib/public/UserPreferences/Exceptions/UserPreferencesException.php rename to lib/public/Config/Exceptions/IncorrectTypeException.php index 664181d420b7e..dafbbacff78d2 100644 --- a/lib/public/UserPreferences/Exceptions/UserPreferencesException.php +++ b/lib/public/Config/Exceptions/IncorrectTypeException.php @@ -6,12 +6,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCP\UserPreferences\Exceptions; +namespace OCP\Config\Exceptions; use Exception; /** * @since 31.0.0 */ -class UserPreferencesException extends Exception { +class IncorrectTypeException extends Exception { } diff --git a/lib/public/UserPreferences/Exceptions/TypeConflictException.php b/lib/public/Config/Exceptions/TypeConflictException.php similarity index 64% rename from lib/public/UserPreferences/Exceptions/TypeConflictException.php rename to lib/public/Config/Exceptions/TypeConflictException.php index b67113fbba757..0b3c903cb385f 100644 --- a/lib/public/UserPreferences/Exceptions/TypeConflictException.php +++ b/lib/public/Config/Exceptions/TypeConflictException.php @@ -6,10 +6,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCP\UserPreferences\Exceptions; +namespace OCP\Config\Exceptions; + +use Exception; /** * @since 31.0.0 */ -class TypeConflictException extends UserPreferencesException { +class TypeConflictException extends Exception { } diff --git a/lib/public/UserPreferences/Exceptions/UnknownKeyException.php b/lib/public/Config/Exceptions/UnknownKeyException.php similarity index 64% rename from lib/public/UserPreferences/Exceptions/UnknownKeyException.php rename to lib/public/Config/Exceptions/UnknownKeyException.php index 3df666899641c..2150246f1d2ad 100644 --- a/lib/public/UserPreferences/Exceptions/UnknownKeyException.php +++ b/lib/public/Config/Exceptions/UnknownKeyException.php @@ -6,10 +6,12 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCP\UserPreferences\Exceptions; +namespace OCP\Config\Exceptions; + +use Exception; /** * @since 31.0.0 */ -class UnknownKeyException extends UserPreferencesException { +class UnknownKeyException extends Exception { } diff --git a/lib/public/UserPreferences/IUserPreferences.php b/lib/public/Config/IUserPreferences.php similarity index 95% rename from lib/public/UserPreferences/IUserPreferences.php rename to lib/public/Config/IUserPreferences.php index f2635c0ffafbf..fd828f5f9c83a 100644 --- a/lib/public/UserPreferences/IUserPreferences.php +++ b/lib/public/Config/IUserPreferences.php @@ -6,14 +6,30 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCP\UserPreferences; +namespace OCP\Config; -use OCP\UserPreferences\Exceptions\IncorrectTypeException; -use OCP\UserPreferences\Exceptions\UnknownKeyException; +use Generator; +use OCP\Config\Exceptions\IncorrectTypeException; +use OCP\Config\Exceptions\UnknownKeyException; /** + * This class provides an easy way for apps to store user preferences in the + * database. + * Supports **lazy loading** + * + * ### What is lazy loading ? + * In order to avoid loading useless user preferences into memory for each request, + * only non-lazy values are now loaded. + * + * Once a value that is lazy is requested, all lazy values will be loaded. + * + * Similarly, some methods from this class are marked with a warning about ignoring + * lazy loading. Use them wisely and only on parts of the code that are called + * during specific requests or actions to avoid loading the lazy values all the time. + * * @since 31.0.0 */ + interface IUserPreferences { public const FLAG_SENSITIVE = 1; // value is sensitive public const FLAG_INDEXED = 2; // value should be indexed @@ -191,10 +207,10 @@ public function getValuesByUsers(string $app, string $key, ?ValueType $typedAs = * @param string $value preference value * @param bool $caseInsensitive non-case-sensitive search, only works if $value is a string * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): array; + public function searchUsersByValueString(string $app, string $key, string $value, bool $caseInsensitive = false): Generator; /** * List all users storing a specific preference key/value pair. @@ -206,10 +222,10 @@ public function searchUsersByValueString(string $app, string $key, string $value * @param string $key preference key * @param int $value preference value * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValueInt(string $app, string $key, int $value): array; + public function searchUsersByValueInt(string $app, string $key, int $value): Generator; /** * List all users storing a specific preference key/value pair. @@ -221,10 +237,10 @@ public function searchUsersByValueInt(string $app, string $key, int $value): arr * @param string $key preference key * @param array $values list of possible preference values * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValues(string $app, string $key, array $values): array; + public function searchUsersByValues(string $app, string $key, array $values): Generator; /** * List all users storing a specific preference key/value pair. @@ -236,10 +252,10 @@ public function searchUsersByValues(string $app, string $key, array $values): ar * @param string $key preference key * @param bool $value preference value * - * @return list + * @return Generator * @since 31.0.0 */ - public function searchUsersByValueBool(string $app, string $key, bool $value): array; + public function searchUsersByValueBool(string $app, string $key, bool $value): Generator; /** * Get user preference assigned to a preference key. diff --git a/lib/public/UserPreferences/ValueType.php b/lib/public/Config/ValueType.php similarity index 87% rename from lib/public/UserPreferences/ValueType.php rename to lib/public/Config/ValueType.php index 280dc5a8b4248..caf510046c263 100644 --- a/lib/public/UserPreferences/ValueType.php +++ b/lib/public/Config/ValueType.php @@ -6,15 +6,14 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -namespace OCP\UserPreferences; +namespace OCP\Config; -use OCP\UserPreferences\Exceptions\IncorrectTypeException; +use OCP\Config\Exceptions\IncorrectTypeException; use UnhandledMatchError; /** - * Listing of available value type for user preferences + * Listing of available value type for typed config value * - * @see IUserPreferences * @since 31.0.0 */ enum ValueType: int { @@ -50,7 +49,7 @@ public static function fromStringDefinition(string $definition): self { 'bool' => self::BOOL, 'array' => self::ARRAY }; - } catch (\UnhandledMatchError ) { + } catch (\UnhandledMatchError) { throw new IncorrectTypeException('unknown string definition'); } } diff --git a/lib/public/IConfig.php b/lib/public/IConfig.php index 3bc64c5e13337..f434f838d45c3 100644 --- a/lib/public/IConfig.php +++ b/lib/public/IConfig.php @@ -245,7 +245,7 @@ public function deleteAppFromAllUsers($appName); * @param string $appName the app to get the user for * @param string $key the key to get the user for * @param string $value the value to get the user for - * @return list of user IDs + * @return array of user IDs * @since 31.0.0 return type of `list` * @since 8.0.0 */ diff --git a/lib/public/UserPreferences/Exceptions/IncorrectTypeException.php b/lib/public/UserPreferences/Exceptions/IncorrectTypeException.php deleted file mode 100644 index 5c8f83dee5efe..0000000000000 --- a/lib/public/UserPreferences/Exceptions/IncorrectTypeException.php +++ /dev/null @@ -1,15 +0,0 @@ -connection, $this->logger, $this->crypto, @@ -775,7 +775,7 @@ public function testSearchUsersByValueString( array $result, ): void { $preferences = $this->generateUserPreferences(); - $this->assertEqualsCanonicalizing($result, $preferences->searchUsersByValueString($app, $key, $value, $ci)); + $this->assertEqualsCanonicalizing($result, iterator_to_array($preferences->searchUsersByValueString($app, $key, $value, $ci))); } public function providerSearchValuesByValueInt(): array { @@ -796,7 +796,7 @@ public function testSearchUsersByValueInt( array $result, ): void { $preferences = $this->generateUserPreferences(); - $this->assertEqualsCanonicalizing($result, $preferences->searchUsersByValueInt($app, $key, $value)); + $this->assertEqualsCanonicalizing($result, iterator_to_array($preferences->searchUsersByValueInt($app, $key, $value))); } public function providerSearchValuesByValues(): array { @@ -816,7 +816,7 @@ public function testSearchUsersByValues( array $result, ): void { $preferences = $this->generateUserPreferences(); - $this->assertEqualsCanonicalizing($result, $preferences->searchUsersByValues($app, $key, $values)); + $this->assertEqualsCanonicalizing($result, iterator_to_array($preferences->searchUsersByValues($app, $key, $values))); } public function providerSearchValuesByValueBool(): array { @@ -836,7 +836,7 @@ public function testSearchUsersByValueBool( array $result, ): void { $preferences = $this->generateUserPreferences(); - $this->assertEqualsCanonicalizing($result, $preferences->searchUsersByValueBool($app, $key, $value)); + $this->assertEqualsCanonicalizing($result, iterator_to_array($preferences->searchUsersByValueBool($app, $key, $value))); } public function providerGetValueMixed(): array {