Files
nextcloud-server-mirror/lib/private/BackgroundJob/JobClassesRegistry.php
Git'Fellow 4920bd1725 refactor: add PHP 8.3 types to BC-safe class constants
PHP 8.3 allows class constants to declare a type. Adopt it where doing so
cannot break third-party apps.

Typing an inheritable constant is a hard BC break: a subclass that
redeclares it untyped fails to load with "Type of C::FOO must be
compatible with P::FOO of type string". Changes are therefore limited to
constants that cannot be redeclared by a subclass:

  - private const (not inherited)
  - final public/protected const
  - constants declared in a final class or an enum

Interface constants, trait constants, and public/protected constants in
non-final (including abstract) classes are left untyped, as is all of
lib/public (OCP) and lib/unstable (NCU).

One review-requested exception: the public OBJECT_PREFIX/OBJECT_SUFFIX
constants of the app-internal CalDAV import helpers (TextImporter,
XmlImporter) are typed as well; these classes are not public API and
have no subclasses.

Only string, int and array are used. float is avoided because it would
silently coerce an int literal and change === comparisons. No constant
name or value is modified: every changed line adds only the type token,
so runtime behaviour is unchanged.

Signed-off-by: Git'Fellow <12234510+solracsf@users.noreply.github.com>
2026-08-11 13:52:52 +01:00

101 lines
2.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\BackgroundJob;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use InvalidArgumentException;
use OCP\BackgroundJob\IJob;
use OCP\IDBConnection;
use OCP\Snowflake\ISnowflakeGenerator;
/**
* Map background job classes and their ID in database
*/
final class JobClassesRegistry {
/**
* @var array<string,string>
*/
private array $registry = [];
private const string TABLE = 'job_classes_registry';
public function __construct(
private readonly IDBConnection $connection,
private readonly ISnowflakeGenerator $snowflakeGenerator,
) {
}
private function loadRegistry(): void {
if ($this->registry !== []) {
return;
}
$qb = $this->connection->getQueryBuilder();
$result = $qb->select('class_id', 'class_name')->from(self::TABLE)->executeQuery();
foreach ($result->iterateAssociative() as $row) {
$this->registry[$row['class_name']] = (string)$row['class_id'];
}
}
/**
* Resolve current ID or generates a new one
*/
public function getId(string $className): string {
$this->loadRegistry();
if (isset($this->registry[$className])) {
return $this->registry[$className];
}
if (!class_exists($className)) {
throw new InvalidArgumentException('Class ' . $className . ' doesn’t exists');
}
if (!is_a($className, IJob::class, true)) {
throw new InvalidArgumentException('Class ' . $className . ' isn’t an instance of ' . IJob::class);
}
$qb = $this->connection->getQueryBuilder();
try {
$classId = $this->snowflakeGenerator->nextId();
$qb
->insert(self::TABLE)
->values([
'class_id' => $qb->createNamedParameter($classId),
'class_name' => $qb->createNamedParameter($className),
])
->executeStatement();
$this->registry[$className] = $classId;
return $classId;
} catch (UniqueConstraintViolationException $e) {
// Class was probably added by a concurrent process
// Try to load it
$result = $qb->select('class_id')->from(self::TABLE)->where($qb->expr()->eq('class_name', $className))->executeQuery();
if ($classId = $result->fetchOne()) {
$classId = (string)$classId;
$this->registry[$className] = $classId;
return $classId;
}
}
throw new \Exception('Fail to retrieve ' . $className . ' ID', previous: $e);
}
public function getName(string|int $classId): string {
$this->loadRegistry();
$classId = (string)$classId;
$className = array_search($classId, $this->registry, true);
if ($className === false) {
throw new InvalidArgumentException('Class ID ' . $classId . ' doesn’t match any class name');
}
return $className;
}
}