mirror of
https://github.com/nextcloud/server.git
synced 2026-06-09 00:32:29 -04:00
Merge pull request #59002 from nextcloud/fix/remove-static-vars
Remove usage of static vars or properties
This commit is contained in:
commit
a8428f883d
40 changed files with 411 additions and 265 deletions
|
|
@ -56,7 +56,10 @@ class Encryption implements IEncryptionModule {
|
|||
/** @var int Current version of the file */
|
||||
private int $version = 0;
|
||||
|
||||
/** @var array remember encryption signature version */
|
||||
/**
|
||||
* @var array remember encryption signature version
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static $rememberVersion = [];
|
||||
|
||||
public function __construct(
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ class BackendService {
|
|||
|
||||
/** @var IConfigHandler[] */
|
||||
private array $configHandlers = [];
|
||||
private bool $eventSent = false;
|
||||
|
||||
public function __construct(
|
||||
protected readonly IAppConfig $appConfig,
|
||||
|
|
@ -79,14 +80,14 @@ class BackendService {
|
|||
$this->backendProviders[] = $provider;
|
||||
}
|
||||
|
||||
private function callForRegistrations() {
|
||||
static $eventSent = false;
|
||||
if (!$eventSent) {
|
||||
private function callForRegistrations(): void {
|
||||
$instance = Server::get(self::class);
|
||||
if (!$instance->eventSent) {
|
||||
Server::get(IEventDispatcher::class)->dispatch(
|
||||
'OCA\\Files_External::loadAdditionalBackends',
|
||||
new GenericEvent()
|
||||
);
|
||||
$eventSent = true;
|
||||
$instance->eventSent = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,9 @@ class SharedStorage extends Jail implements LegacyISharedStorage, ISharedStorage
|
|||
private IAppConfig $appConfig;
|
||||
private IShareManager $shareManager;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static int $initDepth = 0;
|
||||
|
||||
public function __construct(array $parameters) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class RestoreAllFiles extends Base {
|
|||
private const SCOPE_USER = 1;
|
||||
private const SCOPE_GROUPFOLDERS = 2;
|
||||
|
||||
private static array $SCOPE_MAP = [
|
||||
private const SCOPE_MAP = [
|
||||
'user' => self::SCOPE_USER,
|
||||
'groupfolders' => self::SCOPE_GROUPFOLDERS,
|
||||
'all' => self::SCOPE_ALL
|
||||
|
|
@ -221,8 +221,8 @@ class RestoreAllFiles extends Base {
|
|||
}
|
||||
|
||||
protected function parseScope(string $scope): int {
|
||||
if (isset(self::$SCOPE_MAP[$scope])) {
|
||||
return self::$SCOPE_MAP[$scope];
|
||||
if (isset(self::SCOPE_MAP[$scope])) {
|
||||
return self::SCOPE_MAP[$scope];
|
||||
}
|
||||
|
||||
throw new InvalidOptionException("Invalid scope '$scope'");
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions;
|
||||
|
||||
use OCA\Files_Versions\Events\CreateVersionEvent;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Listener;
|
||||
|
||||
use OCA\Files_Versions\BlockVersioningOperation;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ declare(strict_types=1);
|
|||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCA\Files_Versions\Listener;
|
||||
|
||||
use OCA\Files_Versions\BlockVersioningOperation;
|
||||
|
|
|
|||
|
|
@ -56,11 +56,17 @@ class Storage {
|
|||
public const DELETE_TRIGGER_QUOTA_EXCEEDED = 2;
|
||||
|
||||
// files for which we can remove the versions after the delete operation was successful
|
||||
private static $deletedFiles = [];
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static array $deletedFiles = [];
|
||||
|
||||
private static $sourcePathAndUser = [];
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static array $sourcePathAndUser = [];
|
||||
|
||||
private static $max_versions_per_interval = [
|
||||
private const MAX_VERSIONS_PER_INTERVAL = [
|
||||
//first 10sec, one version every 2sec
|
||||
1 => ['intervalEndsAfter' => 10, 'step' => 2],
|
||||
//next minute, one version every 10sec
|
||||
|
|
@ -763,12 +769,8 @@ class Storage {
|
|||
$toDelete = []; // versions we want to delete
|
||||
|
||||
$interval = 1;
|
||||
$step = Storage::$max_versions_per_interval[$interval]['step'];
|
||||
if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
|
||||
$nextInterval = -1;
|
||||
} else {
|
||||
$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
|
||||
}
|
||||
$step = Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['step'];
|
||||
$nextInterval = $time - Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'];
|
||||
|
||||
$firstVersion = reset($versions);
|
||||
|
||||
|
|
@ -797,12 +799,16 @@ class Storage {
|
|||
$newInterval = false; // version checked so we can move to the next one
|
||||
} else { // time to move on to the next interval
|
||||
$interval++;
|
||||
$step = Storage::$max_versions_per_interval[$interval]['step'];
|
||||
if ($interval > count(Storage::MAX_VERSIONS_PER_INTERVAL)) {
|
||||
/* Should never happen, as last interval has -1 as nextInterval */
|
||||
throw new \Exception('MAX_VERSIONS_PER_INTERVAL is malformed or there is a logic issue');
|
||||
}
|
||||
$step = Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['step'];
|
||||
$nextVersion = $prevTimestamp - $step;
|
||||
if (Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'] === -1) {
|
||||
if (Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'] === -1) {
|
||||
$nextInterval = -1;
|
||||
} else {
|
||||
$nextInterval = $time - Storage::$max_versions_per_interval[$interval]['intervalEndsAfter'];
|
||||
$nextInterval = $time - Storage::MAX_VERSIONS_PER_INTERVAL[$interval]['intervalEndsAfter'];
|
||||
}
|
||||
$newInterval = true; // we changed the interval -> check same version with new interval
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use OCA\User_LDAP\Exceptions\NoMoreResults;
|
|||
use OCA\User_LDAP\Mapping\AbstractMapping;
|
||||
use OCA\User_LDAP\User\Manager;
|
||||
use OCA\User_LDAP\User\OfflineUser;
|
||||
use OCP\Cache\CappedMemoryCache;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\HintException;
|
||||
use OCP\IAppConfig;
|
||||
|
|
@ -49,6 +50,7 @@ class Access extends LDAPUtility {
|
|||
protected $groupMapper;
|
||||
|
||||
private string $lastCookie = '';
|
||||
private CappedMemoryCache $intermediates;
|
||||
|
||||
public function __construct(
|
||||
ILDAPWrapper $ldap,
|
||||
|
|
@ -62,6 +64,7 @@ class Access extends LDAPUtility {
|
|||
) {
|
||||
parent::__construct($ldap);
|
||||
$this->userManager->setLdapAccess($this);
|
||||
$this->intermediates = new CappedMemoryCache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -484,8 +487,7 @@ class Access extends LDAPUtility {
|
|||
* @throws \Exception
|
||||
*/
|
||||
public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, ?array $record = null, bool $autoMapping = true) {
|
||||
static $intermediates = [];
|
||||
if (isset($intermediates[($isUser ? 'user-' : 'group-') . $fdn])) {
|
||||
if (isset($this->intermediates[($isUser ? 'user-' : 'group-') . $fdn])) {
|
||||
return false; // is a known intermediate
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +534,7 @@ class Access extends LDAPUtility {
|
|||
$record = $this->readAttributes($fdn, $attributesToRead, $filter);
|
||||
if ($record === false) {
|
||||
$this->logger->debug('Cannot read attributes for ' . $fdn . '. Skipping.', ['filter' => $filter]);
|
||||
$intermediates[($isUser ? 'user-' : 'group-') . $fdn] = true;
|
||||
$this->intermediates[($isUser ? 'user-' : 'group-') . $fdn] = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -579,7 +581,7 @@ class Access extends LDAPUtility {
|
|||
$ldapName = $record[$nameAttribute];
|
||||
if (!isset($ldapName[0]) || empty($ldapName[0])) {
|
||||
$this->logger->debug('No or empty name for ' . $fdn . ' with filter ' . $filter . '.', ['app' => 'user_ldap']);
|
||||
$intermediates['group-' . $fdn] = true;
|
||||
$this->intermediates['group-' . $fdn] = true;
|
||||
return false;
|
||||
}
|
||||
$ldapName = $ldapName[0];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
|
@ -7,6 +9,8 @@
|
|||
|
||||
namespace OCA\User_LDAP;
|
||||
|
||||
use OCA\User_LDAP\Mapping\GroupMapping;
|
||||
use OCA\User_LDAP\Mapping\UserMapping;
|
||||
use OCA\User_LDAP\User\Manager;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\IAppConfig;
|
||||
|
|
@ -15,6 +19,8 @@ use OCP\Server;
|
|||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class AccessFactory {
|
||||
/** @var array<string,Access> */
|
||||
private array $accesses = [];
|
||||
|
||||
public function __construct(
|
||||
private ILDAPWrapper $ldap,
|
||||
|
|
@ -23,6 +29,8 @@ class AccessFactory {
|
|||
private IUserManager $ncUserManager,
|
||||
private LoggerInterface $logger,
|
||||
private IEventDispatcher $dispatcher,
|
||||
private UserMapping $userMapping,
|
||||
private GroupMapping $groupMapping,
|
||||
) {
|
||||
}
|
||||
|
||||
|
|
@ -39,4 +47,19 @@ class AccessFactory {
|
|||
$this->dispatcher,
|
||||
);
|
||||
}
|
||||
|
||||
public function getAccessForPrefix(string $configPrefix): Access {
|
||||
if (!isset(self::$accesses[$configPrefix])) {
|
||||
$this->addAccess($configPrefix);
|
||||
}
|
||||
return $this->accesses[$configPrefix];
|
||||
}
|
||||
|
||||
private function addAccess(string $configPrefix): void {
|
||||
$connector = new Connection($this->ldap, $configPrefix);
|
||||
$access = $this->get($connector);
|
||||
$access->setUserMapper($this->userMapping);
|
||||
$access->setGroupMapper($this->groupMapping);
|
||||
$this->accesses[$configPrefix] = $access;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -139,6 +139,9 @@ class Connection extends LDAPUtility {
|
|||
protected LoggerInterface $logger;
|
||||
private IL10N $l10n;
|
||||
|
||||
/** @psalm-suppress ImpureStaticProperty This is a cache for whether php-ldap is installed, which cannot change mid-process */
|
||||
private static bool $phpLDAPinstalled = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $configPrefix a string with the prefix for the configkey column (appconfig table)
|
||||
|
|
@ -589,8 +592,7 @@ class Connection extends LDAPUtility {
|
|||
if (!$this->configuration->ldapConfigurationActive) {
|
||||
return null;
|
||||
}
|
||||
static $phpLDAPinstalled = true;
|
||||
if (!$phpLDAPinstalled) {
|
||||
if (!static::$phpLDAPinstalled) {
|
||||
return false;
|
||||
}
|
||||
if (!$this->ignoreValidation && !$this->configured) {
|
||||
|
|
@ -602,7 +604,7 @@ class Connection extends LDAPUtility {
|
|||
}
|
||||
if (!$this->ldapConnectionRes) {
|
||||
if (!$this->ldap->areLDAPFunctionsAvailable()) {
|
||||
$phpLDAPinstalled = false;
|
||||
static::$phpLDAPinstalled = false;
|
||||
$this->logger->error(
|
||||
'function ldap_connect is not available. Make sure that the PHP ldap module is installed.',
|
||||
['app' => 'user_ldap']
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
namespace OCA\User_LDAP;
|
||||
|
||||
use OCA\User_LDAP\Mapping\GroupMapping;
|
||||
use OCA\User_LDAP\Mapping\UserMapping;
|
||||
use OCP\ICache;
|
||||
use OCP\ICacheFactory;
|
||||
use OCP\Server;
|
||||
|
|
@ -18,8 +16,6 @@ use OCP\Server;
|
|||
* @template T
|
||||
*/
|
||||
abstract class Proxy {
|
||||
/** @var array<string,Access> */
|
||||
private static array $accesses = [];
|
||||
private ?bool $isSingleBackend = null;
|
||||
private ?ICache $cache = null;
|
||||
|
||||
|
|
@ -71,22 +67,8 @@ abstract class Proxy {
|
|||
return $this->backends[$configPrefix];
|
||||
}
|
||||
|
||||
private function addAccess(string $configPrefix): void {
|
||||
$userMap = Server::get(UserMapping::class);
|
||||
$groupMap = Server::get(GroupMapping::class);
|
||||
|
||||
$connector = new Connection($this->ldap, $configPrefix);
|
||||
$access = $this->accessFactory->get($connector);
|
||||
$access->setUserMapper($userMap);
|
||||
$access->setGroupMapper($groupMap);
|
||||
self::$accesses[$configPrefix] = $access;
|
||||
}
|
||||
|
||||
protected function getAccess(string $configPrefix): Access {
|
||||
if (!isset(self::$accesses[$configPrefix])) {
|
||||
$this->addAccess($configPrefix);
|
||||
}
|
||||
return self::$accesses[$configPrefix];
|
||||
return $this->accessFactory->getAccessForPrefix($configPrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
46
build/psalm/StaticVarsChecker.php
Normal file
46
build/psalm/StaticVarsChecker.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\Stmt\Static_;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\IssueBuffer;
|
||||
use Psalm\Plugin\EventHandler\AfterStatementAnalysisInterface;
|
||||
use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent;
|
||||
|
||||
/**
|
||||
* Complains about static property in classes and static vars in methods
|
||||
*/
|
||||
class StaticVarsChecker implements AfterStatementAnalysisInterface {
|
||||
public static function afterStatementAnalysis(AfterStatementAnalysisEvent $event): ?bool {
|
||||
$stmt = $event->getStmt();
|
||||
if ($stmt instanceof Property) {
|
||||
if ($stmt->isStatic()) {
|
||||
IssueBuffer::maybeAdd(
|
||||
// ImpureStaticProperty is close enough, all static properties are impure to my eyes
|
||||
new \Psalm\Issue\ImpureStaticProperty(
|
||||
'Static property should not be used as they do not follow requests lifecycle',
|
||||
new CodeLocation($event->getStatementsSource(), $stmt),
|
||||
),
|
||||
$event->getStatementsSource()->getSuppressedIssues(),
|
||||
);
|
||||
}
|
||||
} elseif ($stmt instanceof Static_) {
|
||||
IssueBuffer::maybeAdd(
|
||||
// Same logic
|
||||
new \Psalm\Issue\ImpureStaticVariable(
|
||||
'Static var should not be used as they do not follow requests lifecycle and are hard to reset',
|
||||
new CodeLocation($event->getStatementsSource(), $stmt),
|
||||
),
|
||||
$event->getStatementsSource()->getSuppressedIssues(),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
23
build/psalm/StaticVarsTest.php
Normal file
23
build/psalm/StaticVarsTest.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
class StaticVarsTest {
|
||||
public static $forbiddenStaticProperty;
|
||||
protected static $forbiddenProtectedStaticProperty;
|
||||
private static $forbiddenPrivateStaticProperty;
|
||||
private static $forbiddenPrivateStaticPropertyWithValue = [];
|
||||
|
||||
public function normalFunction(): void {
|
||||
static $forbiddenStaticVar = false;
|
||||
}
|
||||
|
||||
public static function staticFunction(): void {
|
||||
static $forbiddenStaticVar = false;
|
||||
}
|
||||
}
|
||||
18
lib/base.php
18
lib/base.php
|
|
@ -47,38 +47,56 @@ class OC {
|
|||
/**
|
||||
* The installation path for Nextcloud on the server (e.g. /srv/http/nextcloud)
|
||||
* @internal Use auto-loaded $serverRoot with DI instead.
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static string $SERVERROOT = '';
|
||||
/**
|
||||
* the current request path relative to the Nextcloud root (e.g. files/index.php)
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static string $SUBURI = '';
|
||||
/**
|
||||
* the Nextcloud root path for http requests (e.g. /nextcloud)
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static string $WEBROOT = '';
|
||||
/**
|
||||
* The installation path array of the apps folder on the server (e.g. /srv/http/nextcloud) 'path' and
|
||||
* web path in 'url'
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static array $APPSROOTS = [];
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static string $configDir;
|
||||
|
||||
/**
|
||||
* requested app
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static string $REQUESTEDAPP = '';
|
||||
|
||||
/**
|
||||
* check if Nextcloud runs in cli mode
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static bool $CLI = false;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static \Composer\Autoload\ClassLoader $composerAutoloader;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static \OC\Server $server;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static \OC\Config $config;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1939,6 +1939,7 @@ return array(
|
|||
'OC\\Memcache\\Factory' => $baseDir . '/lib/private/Memcache/Factory.php',
|
||||
'OC\\Memcache\\LoggerWrapperCache' => $baseDir . '/lib/private/Memcache/LoggerWrapperCache.php',
|
||||
'OC\\Memcache\\Memcached' => $baseDir . '/lib/private/Memcache/Memcached.php',
|
||||
'OC\\Memcache\\MemcachedFactory' => $baseDir . '/lib/private/Memcache/MemcachedFactory.php',
|
||||
'OC\\Memcache\\NullCache' => $baseDir . '/lib/private/Memcache/NullCache.php',
|
||||
'OC\\Memcache\\ProfilerWrapperCache' => $baseDir . '/lib/private/Memcache/ProfilerWrapperCache.php',
|
||||
'OC\\Memcache\\Redis' => $baseDir . '/lib/private/Memcache/Redis.php',
|
||||
|
|
|
|||
|
|
@ -1980,6 +1980,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Memcache\\Factory' => __DIR__ . '/../../..' . '/lib/private/Memcache/Factory.php',
|
||||
'OC\\Memcache\\LoggerWrapperCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/LoggerWrapperCache.php',
|
||||
'OC\\Memcache\\Memcached' => __DIR__ . '/../../..' . '/lib/private/Memcache/Memcached.php',
|
||||
'OC\\Memcache\\MemcachedFactory' => __DIR__ . '/../../..' . '/lib/private/Memcache/MemcachedFactory.php',
|
||||
'OC\\Memcache\\NullCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/NullCache.php',
|
||||
'OC\\Memcache\\ProfilerWrapperCache' => __DIR__ . '/../../..' . '/lib/private/Memcache/ProfilerWrapperCache.php',
|
||||
'OC\\Memcache\\Redis' => __DIR__ . '/../../..' . '/lib/private/Memcache/Redis.php',
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use function class_exists;
|
|||
* SimpleContainer is a simple implementation of a container on basis of Pimple
|
||||
*/
|
||||
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
|
||||
/** @psalm-suppress ImpureStaticProperty A static property is the only way to pass the information from config to autoload */
|
||||
public static bool $useLazyObjects = false;
|
||||
|
||||
private Container $container;
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use OCP\FilesMetadata\IMetadataQuery;
|
|||
*/
|
||||
class SearchBuilder {
|
||||
/** @var array<string, string> */
|
||||
protected static $searchOperatorMap = [
|
||||
private const SEARCH_OPERATOR_MAP = [
|
||||
ISearchComparison::COMPARE_LIKE => 'iLike',
|
||||
ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE => 'like',
|
||||
ISearchComparison::COMPARE_EQUAL => 'eq',
|
||||
|
|
@ -37,7 +37,7 @@ class SearchBuilder {
|
|||
];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected static $searchOperatorNegativeMap = [
|
||||
private const SEARCH_OPERATOR_NEGATIVE_MAP = [
|
||||
ISearchComparison::COMPARE_LIKE => 'notLike',
|
||||
ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE => 'notLike',
|
||||
ISearchComparison::COMPARE_EQUAL => 'neq',
|
||||
|
|
@ -50,7 +50,7 @@ class SearchBuilder {
|
|||
];
|
||||
|
||||
/** @var array<string, string> */
|
||||
protected static $fieldTypes = [
|
||||
private const FIELD_TYPES = [
|
||||
'mimetype' => 'string',
|
||||
'mtime' => 'integer',
|
||||
'name' => 'string',
|
||||
|
|
@ -69,14 +69,14 @@ class SearchBuilder {
|
|||
];
|
||||
|
||||
/** @var array<string, int|string> */
|
||||
protected static $paramTypeMap = [
|
||||
private const PARAM_TYPE_MAP = [
|
||||
'string' => IQueryBuilder::PARAM_STR,
|
||||
'integer' => IQueryBuilder::PARAM_INT,
|
||||
'boolean' => IQueryBuilder::PARAM_BOOL,
|
||||
];
|
||||
|
||||
/** @var array<string, int> */
|
||||
protected static $paramArrayTypeMap = [
|
||||
private const PARAM_ARRAY_TYPE_MAP = [
|
||||
'string' => IQueryBuilder::PARAM_STR_ARRAY,
|
||||
'integer' => IQueryBuilder::PARAM_INT_ARRAY,
|
||||
'boolean' => IQueryBuilder::PARAM_INT_ARRAY,
|
||||
|
|
@ -134,7 +134,7 @@ class SearchBuilder {
|
|||
case ISearchBinaryOperator::OPERATOR_NOT:
|
||||
$negativeOperator = $operator->getArguments()[0];
|
||||
if ($negativeOperator instanceof ISearchComparison) {
|
||||
return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap, $metadataQuery);
|
||||
return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::SEARCH_OPERATOR_NEGATIVE_MAP, $metadataQuery);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Binary operators inside "not" is not supported');
|
||||
}
|
||||
|
|
@ -147,7 +147,7 @@ class SearchBuilder {
|
|||
throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType());
|
||||
}
|
||||
} elseif ($operator instanceof ISearchComparison) {
|
||||
return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap, $metadataQuery);
|
||||
return $this->searchComparisonToDBExpr($builder, $operator, self::SEARCH_OPERATOR_MAP, $metadataQuery);
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator));
|
||||
}
|
||||
|
|
@ -193,7 +193,7 @@ class SearchBuilder {
|
|||
* @return list{string, ParamValue, string, string}
|
||||
*/
|
||||
private function getOperatorFieldAndValueInner(string $field, mixed $value, string $type, bool $pathEqHash): array {
|
||||
$paramType = self::$fieldTypes[$field];
|
||||
$paramType = self::FIELD_TYPES[$field];
|
||||
if ($type === ISearchComparison::COMPARE_IN) {
|
||||
$resultField = $field;
|
||||
$values = [];
|
||||
|
|
@ -263,10 +263,10 @@ class SearchBuilder {
|
|||
'upload_time' => ['eq', 'gt', 'lt', 'gte', 'lte'],
|
||||
];
|
||||
|
||||
if (!isset(self::$fieldTypes[$operator->getField()])) {
|
||||
if (!isset(self::FIELD_TYPES[$operator->getField()])) {
|
||||
throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField());
|
||||
}
|
||||
$type = self::$fieldTypes[$operator->getField()];
|
||||
$type = self::FIELD_TYPES[$operator->getField()];
|
||||
if ($operator->getType() === ISearchComparison::COMPARE_IN) {
|
||||
if (!is_array($operator->getValue())) {
|
||||
throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField());
|
||||
|
|
@ -316,9 +316,9 @@ class SearchBuilder {
|
|||
$value = $value->getTimestamp();
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$type = self::$paramArrayTypeMap[$paramType];
|
||||
$type = self::PARAM_ARRAY_TYPE_MAP[$paramType];
|
||||
} else {
|
||||
$type = self::$paramTypeMap[$paramType];
|
||||
$type = self::PARAM_TYPE_MAP[$paramType];
|
||||
}
|
||||
return $builder->createNamedParameter($value, $type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,14 +27,36 @@ use OCP\Server;
|
|||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Filesystem {
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static ?Mount\Manager $mounts = null;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
public static bool $loaded = false;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static ?View $defaultInstance = null;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static ?FilenameValidator $validator = null;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static ?StorageFactory $loader = null;
|
||||
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
private static bool $logWarningWhenAddingStorageWrapper = true;
|
||||
|
||||
/**
|
||||
* classname which used for hooks handling
|
||||
* used as signalclass in OC_Hooks::emit()
|
||||
|
|
@ -152,10 +174,6 @@ class Filesystem {
|
|||
public const signal_param_mount_type = 'mounttype';
|
||||
public const signal_param_users = 'users';
|
||||
|
||||
private static ?StorageFactory $loader = null;
|
||||
|
||||
private static bool $logWarningWhenAddingStorageWrapper = true;
|
||||
|
||||
/**
|
||||
* @param bool $shouldLog
|
||||
* @return bool previous value
|
||||
|
|
|
|||
|
|
@ -16,19 +16,16 @@ use Icewind\Streams\Wrapper;
|
|||
class SeekableHttpStream implements File {
|
||||
private const PROTOCOL = 'httpseek';
|
||||
|
||||
private static bool $registered = false;
|
||||
|
||||
/**
|
||||
* Registers the stream wrapper using the `httpseek://` url scheme
|
||||
* $return void
|
||||
*/
|
||||
private static function registerIfNeeded() {
|
||||
if (!self::$registered) {
|
||||
if (!in_array(self::PROTOCOL, stream_get_wrappers())) {
|
||||
stream_wrapper_register(
|
||||
self::PROTOCOL,
|
||||
self::class
|
||||
);
|
||||
self::$registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,73 +8,18 @@
|
|||
|
||||
namespace OC\Memcache;
|
||||
|
||||
use OC\SystemConfig;
|
||||
use OCP\HintException;
|
||||
use OCP\IConfig;
|
||||
use OCP\IMemcache;
|
||||
use OCP\Server;
|
||||
|
||||
class Memcached extends Cache implements IMemcache {
|
||||
use CASTrait;
|
||||
|
||||
/**
|
||||
* @var \Memcached $cache
|
||||
*/
|
||||
private static $cache = null;
|
||||
private \Memcached $cache;
|
||||
|
||||
use CADTrait;
|
||||
|
||||
public function __construct($prefix = '') {
|
||||
parent::__construct($prefix);
|
||||
if (is_null(self::$cache)) {
|
||||
self::$cache = new \Memcached();
|
||||
|
||||
$defaultOptions = [
|
||||
\Memcached::OPT_CONNECT_TIMEOUT => 50,
|
||||
\Memcached::OPT_RETRY_TIMEOUT => 50,
|
||||
\Memcached::OPT_SEND_TIMEOUT => 50,
|
||||
\Memcached::OPT_RECV_TIMEOUT => 50,
|
||||
\Memcached::OPT_POLL_TIMEOUT => 50,
|
||||
|
||||
// Enable compression
|
||||
\Memcached::OPT_COMPRESSION => true,
|
||||
|
||||
// Turn on consistent hashing
|
||||
\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
|
||||
|
||||
// Enable Binary Protocol
|
||||
\Memcached::OPT_BINARY_PROTOCOL => true,
|
||||
];
|
||||
/**
|
||||
* By default enable igbinary serializer if available
|
||||
*
|
||||
* Psalm checks depend on if igbinary is installed or not with memcached
|
||||
* @psalm-suppress RedundantCondition
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*/
|
||||
if (\Memcached::HAVE_IGBINARY) {
|
||||
$defaultOptions[\Memcached::OPT_SERIALIZER]
|
||||
= \Memcached::SERIALIZER_IGBINARY;
|
||||
}
|
||||
$options = Server::get(IConfig::class)->getSystemValue('memcached_options', []);
|
||||
if (is_array($options)) {
|
||||
$options = $options + $defaultOptions;
|
||||
self::$cache->setOptions($options);
|
||||
} else {
|
||||
throw new HintException("Expected 'memcached_options' config to be an array, got $options");
|
||||
}
|
||||
|
||||
$servers = Server::get(SystemConfig::class)->getValue('memcached_servers');
|
||||
if (!$servers) {
|
||||
$server = Server::get(SystemConfig::class)->getValue('memcached_server');
|
||||
if ($server) {
|
||||
$servers = [$server];
|
||||
} else {
|
||||
$servers = [['localhost', 11211]];
|
||||
}
|
||||
}
|
||||
self::$cache->addServers($servers);
|
||||
}
|
||||
$this->cache = \OCP\Server::get(MemcachedFactory::class)->getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,8 +31,8 @@ class Memcached extends Cache implements IMemcache {
|
|||
|
||||
#[\Override]
|
||||
public function get($key) {
|
||||
$result = self::$cache->get($this->getNameSpace() . $key);
|
||||
if ($result === false && self::$cache->getResultCode() === \Memcached::RES_NOTFOUND) {
|
||||
$result = $this->cache->get($this->getNameSpace() . $key);
|
||||
if ($result === false && $this->cache->getResultCode() === \Memcached::RES_NOTFOUND) {
|
||||
return null;
|
||||
} else {
|
||||
return $result;
|
||||
|
|
@ -97,29 +42,29 @@ class Memcached extends Cache implements IMemcache {
|
|||
#[\Override]
|
||||
public function set($key, $value, $ttl = 0) {
|
||||
if ($ttl > 0) {
|
||||
$result = self::$cache->set($this->getNameSpace() . $key, $value, $ttl);
|
||||
$result = $this->cache->set($this->getNameSpace() . $key, $value, $ttl);
|
||||
} else {
|
||||
$result = self::$cache->set($this->getNameSpace() . $key, $value);
|
||||
$result = $this->cache->set($this->getNameSpace() . $key, $value);
|
||||
}
|
||||
return $result || $this->isSuccess();
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function hasKey($key) {
|
||||
self::$cache->get($this->getNameSpace() . $key);
|
||||
return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
$this->cache->get($this->getNameSpace() . $key);
|
||||
return $this->cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function remove($key) {
|
||||
$result = self::$cache->delete($this->getNameSpace() . $key);
|
||||
return $result || $this->isSuccess() || self::$cache->getResultCode() === \Memcached::RES_NOTFOUND;
|
||||
$result = $this->cache->delete($this->getNameSpace() . $key);
|
||||
return $result || $this->isSuccess() || $this->cache->getResultCode() === \Memcached::RES_NOTFOUND;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function clear($prefix = '') {
|
||||
// Newer Memcached doesn't like getAllKeys(), flush everything
|
||||
self::$cache->flush();
|
||||
$this->cache->flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -133,7 +78,7 @@ class Memcached extends Cache implements IMemcache {
|
|||
*/
|
||||
#[\Override]
|
||||
public function add($key, $value, $ttl = 0) {
|
||||
$result = self::$cache->add($this->getPrefix() . $key, $value, $ttl);
|
||||
$result = $this->cache->add($this->getPrefix() . $key, $value, $ttl);
|
||||
return $result || $this->isSuccess();
|
||||
}
|
||||
|
||||
|
|
@ -147,9 +92,9 @@ class Memcached extends Cache implements IMemcache {
|
|||
#[\Override]
|
||||
public function inc($key, $step = 1) {
|
||||
$this->add($key, 0);
|
||||
$result = self::$cache->increment($this->getPrefix() . $key, $step);
|
||||
$result = $this->cache->increment($this->getPrefix() . $key, $step);
|
||||
|
||||
if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
if ($this->cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -165,9 +110,9 @@ class Memcached extends Cache implements IMemcache {
|
|||
*/
|
||||
#[\Override]
|
||||
public function dec($key, $step = 1) {
|
||||
$result = self::$cache->decrement($this->getPrefix() . $key, $step);
|
||||
$result = $this->cache->decrement($this->getPrefix() . $key, $step);
|
||||
|
||||
if (self::$cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
if ($this->cache->getResultCode() !== \Memcached::RES_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -180,6 +125,6 @@ class Memcached extends Cache implements IMemcache {
|
|||
}
|
||||
|
||||
private function isSuccess(): bool {
|
||||
return self::$cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
return $this->cache->getResultCode() === \Memcached::RES_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
81
lib/private/Memcache/MemcachedFactory.php
Normal file
81
lib/private/Memcache/MemcachedFactory.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
namespace OC\Memcache;
|
||||
|
||||
use OC\SystemConfig;
|
||||
use OCP\HintException;
|
||||
use OCP\IConfig;
|
||||
use OCP\Server;
|
||||
|
||||
/**
|
||||
* Factory for \Memcached instance, to create a singleton
|
||||
*/
|
||||
class MemcachedFactory {
|
||||
private ?\Memcached $instance = null;
|
||||
|
||||
public function getInstance(): \Memcached {
|
||||
if ($this->instance === null) {
|
||||
$this->init();
|
||||
}
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
/** @psalm-assert \Memcached $this->instance */
|
||||
public function init(): void {
|
||||
$this->instance = new \Memcached();
|
||||
|
||||
$defaultOptions = [
|
||||
\Memcached::OPT_CONNECT_TIMEOUT => 50,
|
||||
\Memcached::OPT_RETRY_TIMEOUT => 50,
|
||||
\Memcached::OPT_SEND_TIMEOUT => 50,
|
||||
\Memcached::OPT_RECV_TIMEOUT => 50,
|
||||
\Memcached::OPT_POLL_TIMEOUT => 50,
|
||||
|
||||
// Enable compression
|
||||
\Memcached::OPT_COMPRESSION => true,
|
||||
|
||||
// Turn on consistent hashing
|
||||
\Memcached::OPT_LIBKETAMA_COMPATIBLE => true,
|
||||
|
||||
// Enable Binary Protocol
|
||||
\Memcached::OPT_BINARY_PROTOCOL => true,
|
||||
];
|
||||
/**
|
||||
* By default enable igbinary serializer if available
|
||||
*
|
||||
* Psalm checks depend on if igbinary is installed or not with memcached
|
||||
* @psalm-suppress RedundantCondition
|
||||
* @psalm-suppress TypeDoesNotContainType
|
||||
*/
|
||||
if (\Memcached::HAVE_IGBINARY) {
|
||||
$defaultOptions[\Memcached::OPT_SERIALIZER]
|
||||
= \Memcached::SERIALIZER_IGBINARY;
|
||||
}
|
||||
$options = Server::get(IConfig::class)->getSystemValue('memcached_options', []);
|
||||
if (is_array($options)) {
|
||||
$options = $options + $defaultOptions;
|
||||
$this->instance->setOptions($options);
|
||||
} else {
|
||||
throw new HintException("Expected 'memcached_options' config to be an array, got $options");
|
||||
}
|
||||
|
||||
$servers = Server::get(SystemConfig::class)->getValue('memcached_servers');
|
||||
if (!$servers) {
|
||||
$server = Server::get(SystemConfig::class)->getValue('memcached_server');
|
||||
if ($server) {
|
||||
$servers = [$server];
|
||||
} else {
|
||||
$servers = [['localhost', 11211]];
|
||||
}
|
||||
}
|
||||
$this->instance->addServers($servers);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
namespace OC\Memcache;
|
||||
|
||||
use OC\RedisFactory;
|
||||
use OCP\IMemcacheTTL;
|
||||
use OCP\Server;
|
||||
|
||||
|
|
@ -38,24 +39,20 @@ class Redis extends Cache implements IMemcacheTTL {
|
|||
|
||||
private const MAX_TTL = 30 * 24 * 60 * 60; // 1 month
|
||||
|
||||
/**
|
||||
* @var \Redis|\RedisCluster $cache
|
||||
*/
|
||||
private static $cache = null;
|
||||
private \Redis|\RedisCluster|null $cache = null;
|
||||
|
||||
public function __construct($prefix = '', string $logFile = '') {
|
||||
parent::__construct($prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Redis|\RedisCluster|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getCache() {
|
||||
if (is_null(self::$cache)) {
|
||||
self::$cache = Server::get('RedisFactory')->getInstance();
|
||||
public function getCache(): \Redis|\RedisCluster {
|
||||
if ($this->cache === null) {
|
||||
$this->cache = \OCP\Server::get(RedisFactory::class)->getInstance();
|
||||
}
|
||||
return self::$cache;
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ class Generator {
|
|||
public const SEMAPHORE_ID_ALL = 0x0a11;
|
||||
public const SEMAPHORE_ID_NEW = 0x07ea;
|
||||
|
||||
private array $cachedNumConcurrentPreviews = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly IConfig $config,
|
||||
private readonly IAppConfig $appConfig,
|
||||
|
|
@ -259,22 +261,14 @@ class Generator {
|
|||
*
|
||||
* @return int number of concurrent threads, or 0 if it cannot be determined
|
||||
*/
|
||||
public static function getHardwareConcurrency(): int {
|
||||
static $width;
|
||||
|
||||
if (!isset($width)) {
|
||||
if (function_exists('ini_get')) {
|
||||
$openBasedir = ini_get('open_basedir');
|
||||
if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) {
|
||||
$width = is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0;
|
||||
} else {
|
||||
$width = 0;
|
||||
}
|
||||
} else {
|
||||
$width = 0;
|
||||
private static function getHardwareConcurrency(): int {
|
||||
if (function_exists('ini_get')) {
|
||||
$openBasedir = ini_get('open_basedir');
|
||||
if (empty($openBasedir) || strpos($openBasedir, '/proc/cpuinfo') !== false) {
|
||||
return is_readable('/proc/cpuinfo') ? substr_count(file_get_contents('/proc/cpuinfo'), 'processor') : 0;
|
||||
}
|
||||
}
|
||||
return $width;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -293,9 +287,8 @@ class Generator {
|
|||
* @return int number of concurrent preview generations, or -1 if $type is invalid
|
||||
*/
|
||||
public function getNumConcurrentPreviews(string $type): int {
|
||||
static $cached = [];
|
||||
if (array_key_exists($type, $cached)) {
|
||||
return $cached[$type];
|
||||
if (array_key_exists($type, $this->cachedNumConcurrentPreviews)) {
|
||||
return $this->cachedNumConcurrentPreviews[$type];
|
||||
}
|
||||
|
||||
$hardwareConcurrency = self::getHardwareConcurrency();
|
||||
|
|
@ -304,16 +297,19 @@ class Generator {
|
|||
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency * 2 : 8;
|
||||
$concurrency_all = $this->config->getSystemValueInt($type, $fallback);
|
||||
$concurrency_new = $this->getNumConcurrentPreviews('preview_concurrency_new');
|
||||
$cached[$type] = max($concurrency_all, $concurrency_new);
|
||||
$this->cachedNumConcurrentPreviews[$type] = max($concurrency_all, $concurrency_new);
|
||||
break;
|
||||
case 'preview_concurrency_new':
|
||||
$fallback = $hardwareConcurrency > 0 ? $hardwareConcurrency : 4;
|
||||
$cached[$type] = $this->config->getSystemValueInt($type, $fallback);
|
||||
$this->cachedNumConcurrentPreviews[$type] = $this->config->getSystemValueInt($type, $fallback);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return $cached[$type];
|
||||
if ($this->cachedNumConcurrentPreviews[$type] < 1) {
|
||||
$this->cachedNumConcurrentPreviews[$type] = 1;
|
||||
}
|
||||
return $this->cachedNumConcurrentPreviews[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -131,15 +131,14 @@ class RedisFactory {
|
|||
}
|
||||
|
||||
public function getInstance(): \Redis|\RedisCluster {
|
||||
if (!$this->isAvailable()) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
}
|
||||
if ($this->instance === null) {
|
||||
if (!$this->isAvailable()) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
}
|
||||
$this->create();
|
||||
}
|
||||
|
||||
if ($this->instance === null) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
if ($this->instance === null) {
|
||||
throw new \Exception('Redis support is not available');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->instance;
|
||||
|
|
|
|||
|
|
@ -660,10 +660,7 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
});
|
||||
$this->registerAlias(ICacheFactory::class, Factory::class);
|
||||
|
||||
$this->registerService('RedisFactory', function (Server $c) {
|
||||
$systemConfig = $c->get(SystemConfig::class);
|
||||
return new RedisFactory($systemConfig, $c->get(IEventLogger::class));
|
||||
});
|
||||
$this->registerDeprecatedAlias('RedisFactory', RedisFactory::class);
|
||||
|
||||
$this->registerService(\OCP\Activity\IManager::class, function (Server $c) {
|
||||
$l10n = $this->get(IFactory::class)->get('lib');
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class Tags implements ITags {
|
|||
/**
|
||||
* Used for storing objectid/categoryname pairs while rescanning.
|
||||
*/
|
||||
private static array $relations = [];
|
||||
private array $relations = [];
|
||||
private array $tags = [];
|
||||
|
||||
/**
|
||||
|
|
@ -362,7 +362,7 @@ class Tags implements ITags {
|
|||
}
|
||||
if (!is_null($id)) {
|
||||
// Insert $objectid, $categoryid pairs if not exist.
|
||||
self::$relations[] = ['objid' => $id, 'tag' => $name];
|
||||
$this->relations[] = ['objid' => $id, 'tag' => $name];
|
||||
}
|
||||
}
|
||||
$this->tags = array_merge($this->tags, $newones);
|
||||
|
|
@ -395,10 +395,7 @@ class Tags implements ITags {
|
|||
$this->logger->debug(__METHOD__ . 'tags' . print_r($this->tags, true), ['app' => 'core']);
|
||||
// Loop through temporarily cached objectid/tagname pairs
|
||||
// and save relations.
|
||||
$tags = $this->tags;
|
||||
// For some reason this is needed or array_search(i) will return 0..?
|
||||
ksort($tags);
|
||||
foreach (self::$relations as $relation) {
|
||||
foreach ($this->relations as $relation) {
|
||||
$tagId = $this->getTagId($relation['tag']);
|
||||
$this->logger->debug(__METHOD__ . 'catid ' . $relation['tag'] . ' ' . $tagId, ['app' => 'core']);
|
||||
if ($tagId) {
|
||||
|
|
@ -419,7 +416,7 @@ class Tags implements ITags {
|
|||
}
|
||||
}
|
||||
}
|
||||
self::$relations = []; // reset
|
||||
$this->relations = []; // reset
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -76,12 +76,12 @@ class Task extends Entity {
|
|||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress', 'webhook_uri', 'webhook_method', 'scheduled_at', 'started_at', 'ended_at', 'allow_cleanup', 'user_facing_error_message', 'include_watermark'];
|
||||
public const COLUMNS = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'custom_id', 'completion_expected_at', 'error_message', 'progress', 'webhook_uri', 'webhook_method', 'scheduled_at', 'started_at', 'ended_at', 'allow_cleanup', 'user_facing_error_message', 'include_watermark'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress', 'webhookUri', 'webhookMethod', 'scheduledAt', 'startedAt', 'endedAt', 'allowCleanup', 'userFacingErrorMessage', 'includeWatermark'];
|
||||
public const FIELDS = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'customId', 'completionExpectedAt', 'errorMessage', 'progress', 'webhookUri', 'webhookMethod', 'scheduledAt', 'startedAt', 'endedAt', 'allowCleanup', 'userFacingErrorMessage', 'includeWatermark'];
|
||||
|
||||
public function __construct() {
|
||||
// add types in constructor
|
||||
|
|
@ -108,9 +108,9 @@ class Task extends Entity {
|
|||
}
|
||||
|
||||
public function toRow(): array {
|
||||
return array_combine(self::$columns, array_map(function ($field) {
|
||||
return array_combine(self::COLUMNS, array_map(function ($field) {
|
||||
return $this->{'get' . ucfirst($field)}();
|
||||
}, self::$fields));
|
||||
}, self::FIELDS));
|
||||
}
|
||||
|
||||
public static function fromPublicTask(OCPTask $task): self {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function find(int $id): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
|
|
@ -53,7 +53,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findOldestScheduledByType(array $taskTypes, array $taskIdsToIgnore): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('status', $qb->createPositionalParameter(\OCP\TaskProcessing\Task::STATUS_SCHEDULED, IQueryBuilder::PARAM_INT)))
|
||||
->setMaxResults(1)
|
||||
|
|
@ -85,7 +85,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findByIdAndUser(int $id, ?string $userId): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
if ($userId === null) {
|
||||
|
|
@ -105,7 +105,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findByUserAndTaskType(?string $userId, ?string $taskType = null, ?string $customId = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)));
|
||||
if ($taskType !== null) {
|
||||
|
|
@ -126,7 +126,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findUserTasksByApp(?string $userId, string $appId, ?string $customId = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
|
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
|
||||
|
|
@ -151,7 +151,7 @@ class TaskMapper extends QBMapper {
|
|||
?string $userId, ?string $taskType = null, ?string $appId = null, ?string $customId = null,
|
||||
?int $status = null, ?int $scheduleAfter = null, ?int $endedBefore = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName);
|
||||
|
||||
// empty string: no userId filter
|
||||
|
|
@ -205,7 +205,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function getTasksToCleanup(int $timeout, bool $force = false): \Generator {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->lt('last_updated', $qb->createPositionalParameter($this->timeFactory->getDateTime()->getTimestamp() - $timeout)));
|
||||
if (!$force) {
|
||||
|
|
@ -244,7 +244,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findNOldestScheduledByType(array $taskTypes, array $taskIdsToIgnore, int $numberOfTasks) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('status', $qb->createPositionalParameter(\OCP\TaskProcessing\Task::STATUS_SCHEDULED, IQueryBuilder::PARAM_INT)))
|
||||
->setMaxResults($numberOfTasks)
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ use OCP\Template\ITemplateManager;
|
|||
use OCP\Util;
|
||||
|
||||
class TemplateLayout {
|
||||
private static string $versionHash = '';
|
||||
private string $versionHash = '';
|
||||
/** @var string[] */
|
||||
private static array $cacheBusterCache = [];
|
||||
private array $cacheBusterCache = [];
|
||||
|
||||
public ?CSSResourceLocator $cssLocator = null;
|
||||
public ?JSResourceLocator $jsLocator = null;
|
||||
|
|
@ -214,13 +214,13 @@ class TemplateLayout {
|
|||
$page->assign('enabledThemes', $themesService?->getEnabledThemes() ?? []);
|
||||
|
||||
if ($this->config->getSystemValueBool('installed', false)) {
|
||||
if (empty(self::$versionHash)) {
|
||||
if (empty($this->versionHash)) {
|
||||
$v = $this->appManager->getAppInstalledVersions(true);
|
||||
$v['core'] = implode('.', $this->serverVersion->getVersion());
|
||||
self::$versionHash = substr(md5(implode(',', $v)), 0, 8);
|
||||
$this->versionHash = substr(md5(implode(',', $v)), 0, 8);
|
||||
}
|
||||
} else {
|
||||
self::$versionHash = md5('not installed');
|
||||
$this->versionHash = md5('not installed');
|
||||
}
|
||||
|
||||
// Add the js files
|
||||
|
|
@ -250,7 +250,7 @@ class TemplateLayout {
|
|||
if (Server::get(ContentSecurityPolicyNonceManager::class)->browserSupportsCspV3()) {
|
||||
$page->assign('inline_ocjs', $config);
|
||||
} else {
|
||||
$page->append('jsfiles', Server::get(IURLGenerator::class)->linkToRoute('core.OCJS.getConfig', ['v' => self::$versionHash]));
|
||||
$page->append('jsfiles', Server::get(IURLGenerator::class)->linkToRoute('core.OCJS.getConfig', ['v' => $this->versionHash]));
|
||||
}
|
||||
}
|
||||
foreach ($jsFiles as $info) {
|
||||
|
|
@ -283,7 +283,7 @@ class TemplateLayout {
|
|||
|
||||
$page->assign('cssfiles', []);
|
||||
$page->assign('printcssfiles', []);
|
||||
$this->initialState->provideInitialState('core', 'versionHash', self::$versionHash);
|
||||
$this->initialState->provideInitialState('core', 'versionHash', $this->versionHash);
|
||||
foreach ($cssFiles as $info) {
|
||||
$web = $info[1];
|
||||
$file = $info[2];
|
||||
|
|
@ -323,7 +323,7 @@ class TemplateLayout {
|
|||
|
||||
if ($this->config->getSystemValueBool('installed', false) === false) {
|
||||
// if not installed just return the version hash
|
||||
return '?v=' . self::$versionHash;
|
||||
return '?v=' . $this->versionHash;
|
||||
}
|
||||
|
||||
$hash = false;
|
||||
|
|
@ -337,7 +337,7 @@ class TemplateLayout {
|
|||
}
|
||||
// As a last resort we use the server version hash
|
||||
if ($hash === false) {
|
||||
$hash = self::$versionHash;
|
||||
$hash = $this->versionHash;
|
||||
}
|
||||
|
||||
// The theming app is force-enabled thus the cache buster is always available
|
||||
|
|
@ -347,7 +347,7 @@ class TemplateLayout {
|
|||
}
|
||||
|
||||
private function getVersionHashByPath(string $path): string|false {
|
||||
if (array_key_exists($path, self::$cacheBusterCache) === false) {
|
||||
if (array_key_exists($path, $this->cacheBusterCache) === false) {
|
||||
// Not yet cached, so lets find the cache buster string
|
||||
$appId = $this->getAppNamefromPath($path);
|
||||
if ($appId === false) {
|
||||
|
|
@ -357,20 +357,20 @@ class TemplateLayout {
|
|||
|
||||
if ($appId === 'core') {
|
||||
// core is not a real app but the server itself
|
||||
$hash = self::$versionHash;
|
||||
$hash = $this->versionHash;
|
||||
} else {
|
||||
$appVersion = $this->appManager->getAppVersion($appId);
|
||||
// For shipped apps the app version is not a single source of truth, we rather also need to consider the Nextcloud version
|
||||
if ($this->appManager->isShipped($appId)) {
|
||||
$appVersion .= '-' . self::$versionHash;
|
||||
$appVersion .= '-' . $this->versionHash;
|
||||
}
|
||||
|
||||
$hash = substr(md5($appVersion), 0, 8);
|
||||
}
|
||||
self::$cacheBusterCache[$path] = $hash;
|
||||
$this->cacheBusterCache[$path] = $hash;
|
||||
}
|
||||
|
||||
return self::$cacheBusterCache[$path];
|
||||
return $this->cacheBusterCache[$path];
|
||||
}
|
||||
|
||||
private function findStylesheetFiles(array $styles): array {
|
||||
|
|
|
|||
|
|
@ -46,12 +46,12 @@ class Task extends Entity {
|
|||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $columns = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier', 'completion_expected_at'];
|
||||
public const COLUMNS = ['id', 'last_updated', 'type', 'input', 'output', 'status', 'user_id', 'app_id', 'identifier', 'completion_expected_at'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $fields = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier', 'completionExpectedAt'];
|
||||
public const FIELDS = ['id', 'lastUpdated', 'type', 'input', 'output', 'status', 'userId', 'appId', 'identifier', 'completionExpectedAt'];
|
||||
|
||||
public function __construct() {
|
||||
// add types in constructor
|
||||
|
|
@ -68,9 +68,9 @@ class Task extends Entity {
|
|||
}
|
||||
|
||||
public function toRow(): array {
|
||||
return array_combine(self::$columns, array_map(function ($field) {
|
||||
return array_combine(self::COLUMNS, array_map(function ($field) {
|
||||
return $this->{'get' . ucfirst($field)}();
|
||||
}, self::$fields));
|
||||
}, self::FIELDS));
|
||||
}
|
||||
|
||||
public static function fromPublicTask(OCPTask $task): Task {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function find(int $id): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
|
|
@ -53,7 +53,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findByIdAndUser(int $id, ?string $userId): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
if ($userId === null) {
|
||||
|
|
@ -73,7 +73,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findUserTasksByApp(string $userId, string $appId, ?string $identifier = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
|
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
|
||||
|
|
|
|||
|
|
@ -44,7 +44,10 @@ class Manager implements IManager {
|
|||
/** @var ?IProvider[] */
|
||||
private ?array $providers = null;
|
||||
|
||||
private static array $taskProcessingCompatibleTaskTypes = [
|
||||
/**
|
||||
* @var array<class-string, string>
|
||||
*/
|
||||
private const COMPATIBLE_TASK_TYPES = [
|
||||
FreePromptTaskType::class => TextToText::ID,
|
||||
HeadlineTaskType::class => TextToTextHeadline::ID,
|
||||
SummaryTaskType::class => TextToTextSummary::ID,
|
||||
|
|
@ -93,7 +96,7 @@ class Manager implements IManager {
|
|||
public function hasProviders(): bool {
|
||||
// check if task processing equivalent types are available
|
||||
$taskTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
|
||||
foreach (self::$taskProcessingCompatibleTaskTypes as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
foreach (self::COMPATIBLE_TASK_TYPES as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
if (isset($taskTaskTypes[$taskTaskTypeId])) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -118,7 +121,7 @@ class Manager implements IManager {
|
|||
|
||||
// check if task processing equivalent types are available
|
||||
$taskTaskTypes = $this->taskProcessingManager->getAvailableTaskTypes();
|
||||
foreach (self::$taskProcessingCompatibleTaskTypes as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
foreach (self::COMPATIBLE_TASK_TYPES as $textTaskTypeClass => $taskTaskTypeId) {
|
||||
if (isset($taskTaskTypes[$taskTaskTypeId])) {
|
||||
$tasks[$textTaskTypeClass] = true;
|
||||
}
|
||||
|
|
@ -138,9 +141,10 @@ class Manager implements IManager {
|
|||
public function runTask(OCPTask $task): string {
|
||||
// try to run a task processing task if possible
|
||||
$taskTypeClass = $task->getType();
|
||||
if (isset(self::$taskProcessingCompatibleTaskTypes[$taskTypeClass]) && isset($this->taskProcessingManager->getAvailableTaskTypes()[self::$taskProcessingCompatibleTaskTypes[$taskTypeClass]])) {
|
||||
/** @psalm-suppress InvalidArrayOffset false-positive */
|
||||
if (isset(self::COMPATIBLE_TASK_TYPES[$taskTypeClass]) && isset($this->taskProcessingManager->getAvailableTaskTypes()[self::COMPATIBLE_TASK_TYPES[$taskTypeClass]])) {
|
||||
try {
|
||||
$taskProcessingTaskTypeId = self::$taskProcessingCompatibleTaskTypes[$taskTypeClass];
|
||||
$taskProcessingTaskTypeId = self::COMPATIBLE_TASK_TYPES[$taskTypeClass];
|
||||
$taskProcessingTask = new \OCP\TaskProcessing\Task(
|
||||
$taskProcessingTaskTypeId,
|
||||
['input' => $task->getInput()],
|
||||
|
|
@ -226,9 +230,10 @@ class Manager implements IManager {
|
|||
}
|
||||
$task->setStatus(OCPTask::STATUS_SCHEDULED);
|
||||
$providers = $this->getPreferredProviders($task);
|
||||
/** @psalm-suppress InvalidArrayOffset false-positive */
|
||||
$equivalentTaskProcessingTypeAvailable = (
|
||||
isset(self::$taskProcessingCompatibleTaskTypes[$task->getType()])
|
||||
&& isset($this->taskProcessingManager->getAvailableTaskTypes()[self::$taskProcessingCompatibleTaskTypes[$task->getType()]])
|
||||
isset(self::COMPATIBLE_TASK_TYPES[$task->getType()])
|
||||
&& isset($this->taskProcessingManager->getAvailableTaskTypes()[self::COMPATIBLE_TASK_TYPES[$task->getType()]])
|
||||
);
|
||||
if (count($providers) === 0 && !$equivalentTaskProcessingTypeAvailable) {
|
||||
throw new PreConditionNotMetException('No LanguageModel provider is installed that can handle this task');
|
||||
|
|
|
|||
|
|
@ -49,12 +49,12 @@ class Task extends Entity {
|
|||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $columns = ['id', 'last_updated', 'input', 'status', 'user_id', 'app_id', 'identifier', 'number_of_images', 'completion_expected_at'];
|
||||
public const COLUMNS = ['id', 'last_updated', 'input', 'status', 'user_id', 'app_id', 'identifier', 'number_of_images', 'completion_expected_at'];
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
public static array $fields = ['id', 'lastUpdated', 'input', 'status', 'userId', 'appId', 'identifier', 'numberOfImages', 'completionExpectedAt'];
|
||||
public const FIELDS = ['id', 'lastUpdated', 'input', 'status', 'userId', 'appId', 'identifier', 'numberOfImages', 'completionExpectedAt'];
|
||||
|
||||
public function __construct() {
|
||||
// add types in constructor
|
||||
|
|
@ -70,9 +70,9 @@ class Task extends Entity {
|
|||
}
|
||||
|
||||
public function toRow(): array {
|
||||
return array_combine(self::$columns, array_map(function ($field) {
|
||||
return array_combine(self::COLUMNS, array_map(function ($field) {
|
||||
return $this->{'get' . ucfirst($field)}();
|
||||
}, self::$fields));
|
||||
}, self::FIELDS));
|
||||
}
|
||||
|
||||
public static function fromPublicTask(OCPTask $task): Task {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function find(int $id): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
return $this->findEntity($qb);
|
||||
|
|
@ -54,7 +54,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findByIdAndUser(int $id, ?string $userId): Task {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('id', $qb->createPositionalParameter($id)));
|
||||
if ($userId === null) {
|
||||
|
|
@ -74,7 +74,7 @@ class TaskMapper extends QBMapper {
|
|||
*/
|
||||
public function findUserTasksByApp(?string $userId, string $appId, ?string $identifier = null): array {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select(Task::$columns)
|
||||
$qb->select(Task::COLUMNS)
|
||||
->from($this->tableName)
|
||||
->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId)))
|
||||
->andWhere($qb->expr()->eq('app_id', $qb->createPositionalParameter($appId)));
|
||||
|
|
|
|||
|
|
@ -24,9 +24,14 @@ use Psr\Container\ContainerExceptionInterface;
|
|||
* @since 4.0.0
|
||||
*/
|
||||
class Util {
|
||||
/** @psalm-suppress ImpureStaticProperty legacy stuff, keep them for now */
|
||||
private static array $scriptsInit = [];
|
||||
/** @psalm-suppress ImpureStaticProperty */
|
||||
private static array $scripts = [];
|
||||
/** @psalm-suppress ImpureStaticProperty */
|
||||
private static array $scriptDeps = [];
|
||||
/** @psalm-suppress ImpureStaticProperty */
|
||||
private static ?bool $needUpgradeCache = null;
|
||||
|
||||
/**
|
||||
* get the current installed version of Nextcloud
|
||||
|
|
@ -571,8 +576,6 @@ class Util {
|
|||
return \OC_Util::isDefaultExpireDateEnforced();
|
||||
}
|
||||
|
||||
protected static $needUpgradeCache = null;
|
||||
|
||||
/**
|
||||
* Checks whether the current version needs upgrade.
|
||||
*
|
||||
|
|
@ -580,7 +583,7 @@ class Util {
|
|||
* @since 7.0.0
|
||||
*/
|
||||
public static function needUpgrade() {
|
||||
if (!isset(self::$needUpgradeCache)) {
|
||||
if (self::$needUpgradeCache === null) {
|
||||
self::$needUpgradeCache = \OC_Util::needUpgrade(Server::get(\OC\SystemConfig::class));
|
||||
}
|
||||
return self::$needUpgradeCache;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
<plugin filename="build/psalm/AttributeNamedParameters.php" />
|
||||
<plugin filename="build/psalm/LogicalOperatorChecker.php" />
|
||||
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
|
||||
<plugin filename="build/psalm/StaticVarsChecker.php" />
|
||||
</plugins>
|
||||
<projectFiles>
|
||||
<directory name="apps/admin_audit"/>
|
||||
|
|
@ -238,5 +239,10 @@
|
|||
<directory name="apps/*/tests" />
|
||||
</errorLevel>
|
||||
</DeprecatedTrait>
|
||||
<PluginIssue name="ImpureStaticProperty">
|
||||
<errorLevel type="suppress">
|
||||
<directory name="lib/private/legacy" />
|
||||
</errorLevel>
|
||||
</PluginIssue>
|
||||
</issueHandlers>
|
||||
</psalm>
|
||||
|
|
|
|||
|
|
@ -49,8 +49,8 @@ class TemplateLayoutTest extends \Test\TestCase {
|
|||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('dataVersionHash')]
|
||||
public function testVersionHash(
|
||||
string|false $path,
|
||||
string|false $file,
|
||||
string $path,
|
||||
string $file,
|
||||
bool $installed,
|
||||
bool $debug,
|
||||
string $expected,
|
||||
|
|
@ -58,13 +58,13 @@ class TemplateLayoutTest extends \Test\TestCase {
|
|||
$this->appManager->expects(self::any())
|
||||
->method('getAppVersion')
|
||||
->willReturnCallback(fn ($appId) => match ($appId) {
|
||||
'shippedApp' => 'shipped_1',
|
||||
'otherApp' => 'other_2',
|
||||
'shipped' => 'shipped_1',
|
||||
'other' => 'other_2',
|
||||
default => "$appId",
|
||||
});
|
||||
$this->appManager->expects(self::any())
|
||||
->method('isShipped')
|
||||
->willReturnCallback(fn (string $app) => $app === 'shippedApp');
|
||||
->willReturnCallback(fn (string $app) => $app === 'shipped');
|
||||
|
||||
$this->config->expects(self::atLeastOnce())
|
||||
->method('getSystemValueBool')
|
||||
|
|
@ -80,31 +80,20 @@ class TemplateLayoutTest extends \Test\TestCase {
|
|||
$this->request->method('getPathInfo')
|
||||
->willReturn('/' . $path);
|
||||
|
||||
$this->templateLayout = $this->getMockBuilder(TemplateLayout::class)
|
||||
->onlyMethods(['getAppNamefromPath'])
|
||||
->setConstructorArgs([
|
||||
$this->config,
|
||||
$this->appConfig,
|
||||
$this->appManager,
|
||||
$this->initialState,
|
||||
$this->navigationManager,
|
||||
$this->templateManager,
|
||||
$this->serverVersion,
|
||||
$this->request,
|
||||
])
|
||||
->getMock();
|
||||
$this->templateLayout = new TemplateLayout(
|
||||
$this->config,
|
||||
$this->appConfig,
|
||||
$this->appManager,
|
||||
$this->initialState,
|
||||
$this->navigationManager,
|
||||
$this->templateManager,
|
||||
$this->serverVersion,
|
||||
$this->request,
|
||||
);
|
||||
|
||||
$layout = $this->templateLayout->getPageTemplate(TemplateResponse::RENDER_AS_ERROR, '');
|
||||
|
||||
self::invokePrivate(TemplateLayout::class, 'versionHash', ['version_hash']);
|
||||
|
||||
$this->templateLayout->expects(self::any())
|
||||
->method('getAppNamefromPath')
|
||||
->willReturnCallback(fn ($appName) => match($appName) {
|
||||
'apps/shipped' => 'shippedApp',
|
||||
'other/app.css' => 'otherApp',
|
||||
default => false,
|
||||
});
|
||||
self::invokePrivate($this->templateLayout, 'versionHash', ['version_hash']);
|
||||
|
||||
$hash = self::invokePrivate($this->templateLayout, 'getVersionHashSuffix', [$path, $file]);
|
||||
self::assertEquals($expected, $hash);
|
||||
|
|
@ -114,9 +103,8 @@ class TemplateLayoutTest extends \Test\TestCase {
|
|||
return [
|
||||
'no hash if in debug mode' => ['apps/shipped', 'style.css', true, true, ''],
|
||||
'only version hash if not installed' => ['apps/shipped', 'style.css', false, false, '?v=version_hash'],
|
||||
'version hash with cache buster if app not found' => ['unknown/path', '', true, false, '?v=version_hash-42'],
|
||||
'version hash with cache buster if neither path nor file provided' => [false, false, true, false, '?v=version_hash-42'],
|
||||
'app version hash if external app' => ['', 'other/app.css', true, false, '?v=' . substr(md5('other_2'), 0, 8) . '-42'],
|
||||
'version hash with cache buster if neither path nor file provided' => ['', '', true, false, '?v=version_hash-42'],
|
||||
'app version hash if external app' => ['apps/other', 'app.css', true, false, '?v=' . substr(md5('other_2'), 0, 8) . '-42'],
|
||||
'app version and version hash if shipped app' => ['apps/shipped', 'style.css', true, false, '?v=' . substr(md5('shipped_1-version_hash'), 0, 8) . '-42'],
|
||||
'prefer path over file' => ['apps/shipped', 'other/app.css', true, false, '?v=' . substr(md5('shipped_1-version_hash'), 0, 8) . '-42'],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ use Psr\Container\ContainerExceptionInterface;
|
|||
abstract class TestCase extends \PHPUnit\Framework\TestCase {
|
||||
private QueueBus $commandBus;
|
||||
|
||||
/** @psalm-suppress ImpureStaticProperty For tests it's not an issue */
|
||||
protected static ?IDBConnection $realDatabase = null;
|
||||
/** @psalm-suppress ImpureStaticProperty */
|
||||
private static bool $wasDatabaseAllowed = false;
|
||||
protected array $services = [];
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue