diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index 4c731a7..7aa395b 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -53,14 +53,9 @@ jobs:
app-name: apptemplate
php-version: "8.3"
php-test-versions: '["8.3", "8.4"]'
- # The whole range `appinfo/info.xml` declares (min-version="32",
- # max-version="34"), not just its top. With only `stable34` listed, 32 and
- # 33 were advertised as supported and tested by nothing.
- # `stable34` MUST stay FIRST: newman, playwright and journeydoc-capture
- # each read `fromJSON(inputs.nextcloud-test-refs)[0]` as their single
- # server, so element 0 is the ref those legs run against — it has to be
- # the newest supported release, not whichever ref happens to sort first.
- nextcloud-test-refs: '["stable34", "stable32", "stable33"]'
+ # No `nextcloud-test-refs` override: the shared quality.yml derives the
+ # Nextcloud matrix from appinfo/info.xml (min/max-version), so every
+ # declared major (32..35) gets a leg without editing this file.
enable-psalm: true
enable-phpstan: true
enable-phpmetrics: true
diff --git a/appinfo/info.xml b/appinfo/info.xml
index c7bfcac..63ba6c2 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -32,7 +32,7 @@
at top level, repair-steps precedes settings and navigations follows it. -->
-
+
diff --git a/composer.json b/composer.json
index 5883107..91fe402 100644
--- a/composer.json
+++ b/composer.json
@@ -27,7 +27,7 @@
"conduction/hydra-gates": "^1.18.0",
"cyclonedx/cyclonedx-php-composer": "^6.2",
"edgedesign/phpqa": "^1.27",
- "nextcloud/ocp": "^34.0",
+ "nextcloud/ocp": "^35.0",
"phpcsstandards/phpcsextra": "^1.4",
"phpmd/phpmd": "^2.15",
"phpmetrics/phpmetrics": "^2.8",
diff --git a/composer.lock b/composer.lock
index ccac174..8113c92 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "ad26c68419e9fe6ff946b6c2c755d3b9",
+ "content-hash": "c851b8dd29b9a0b4fa2d4347d568f1ae",
"packages": [],
"packages-dev": [
{
@@ -1870,30 +1870,34 @@
},
{
"name": "nextcloud/ocp",
- "version": "v34.0.2",
+ "version": "v35.0.1",
"source": {
"type": "git",
"url": "https://github.com/nextcloud-deps/ocp.git",
- "reference": "81cbb2c594afe0fa978885bf7accd0440199520a"
+ "reference": "088e38c04842e027ff255d90b689817b74a54e60"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/81cbb2c594afe0fa978885bf7accd0440199520a",
- "reference": "81cbb2c594afe0fa978885bf7accd0440199520a",
+ "url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/088e38c04842e027ff255d90b689817b74a54e60",
+ "reference": "088e38c04842e027ff255d90b689817b74a54e60",
"shasum": ""
},
"require": {
- "php": "~8.2 || ~8.3 || ~8.4 || ~8.5",
+ "php": "~8.3 || ~8.4 || ~8.5",
"psr/clock": "^1.0",
"psr/container": "^2.0.2",
"psr/event-dispatcher": "^1.0",
"psr/http-client": "^1.0.3",
- "psr/log": "^3.0.2"
+ "psr/log": "^3.0.2",
+ "symfony/polyfill-intl-normalizer": "^1.38",
+ "symfony/polyfill-php84": "^1.38",
+ "symfony/polyfill-php85": "^1.41",
+ "symfony/polyfill-php86": "^1.41"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-stable34": "34.0.0-dev"
+ "dev-stable35": "35.0.0-dev"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1913,9 +1917,9 @@
"description": "Composer package containing Nextcloud's public OCP API and the unstable NCU API",
"support": {
"issues": "https://github.com/nextcloud-deps/ocp/issues",
- "source": "https://github.com/nextcloud-deps/ocp/tree/v34.0.2"
+ "source": "https://github.com/nextcloud-deps/ocp/tree/v35.0.1"
},
- "time": "2026-07-16T01:28:13+00:00"
+ "time": "2026-09-23T02:18:13+00:00"
},
{
"name": "nikic/php-parser",
@@ -7213,6 +7217,246 @@
],
"time": "2026-05-26T12:45:58+00:00"
},
+ {
+ "name": "symfony/polyfill-php84",
+ "version": "v1.43.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php84.git",
+ "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/22c6548499cbe5c5bb072fc0c42312dff062a015",
+ "reference": "22c6548499cbe5c5bb072fc0c42312dff062a015",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php84\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php84/tree/v1.43.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-09-23T22:01:09+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php85",
+ "version": "v1.43.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php85.git",
+ "reference": "86a42cbcc07adc331481a5ab310a4024bfc227f6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/86a42cbcc07adc331481a5ab310a4024bfc227f6",
+ "reference": "86a42cbcc07adc331481a5ab310a4024bfc227f6",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php85\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.5+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php85/tree/v1.43.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-09-25T15:46:47+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php86",
+ "version": "v1.43.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php86.git",
+ "reference": "8840df45b24f33a49dc419fa18dc0057231ae1d8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php86/zipball/8840df45b24f33a49dc419fa18dc0057231ae1d8",
+ "reference": "8840df45b24f33a49dc419fa18dc0057231ae1d8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php86\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.6+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php86/tree/v1.43.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2026-09-25T15:46:47+00:00"
+ },
{
"name": "symfony/process",
"version": "v7.4.13",
@@ -7934,5 +8178,5 @@
"platform-overrides": {
"php": "8.3"
},
- "plugin-api-version": "2.9.0"
+ "plugin-api-version": "2.6.0"
}
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 58f879d..49a15c8 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -74,8 +74,20 @@ public function __construct() {
* @param IRegistrationContext $context The registration context
*
* @return void
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess) OpenRegisterAutoloader is a static
+ * prelude: this method is the composition root, so
+ * there is no container to inject it from yet.
*/
public function register(IRegistrationContext $context): void {
+ // `apptemplate` sorts before `openregister`, so Nextcloud has not put
+ // OpenRegister's PSR-4 prefix on the autoloader yet when this runs. Do
+ // it here, through public API only (NC 35 removed the private
+ // \OC_App::registerAutoloading()). Never throws; returns false when
+ // OpenRegister is absent. Keep any OCA\OpenRegister\ class USE below
+ // this line (a ::class constant alone does not autoload).
+ OpenRegisterAutoloader::register();
+
// Register deep link patterns with OpenRegister's unified search provider.
// Only fires when OpenRegister is installed and dispatches the event.
$context->registerEventListener(
diff --git a/lib/AppInfo/OpenRegisterAutoloader.php b/lib/AppInfo/OpenRegisterAutoloader.php
new file mode 100644
index 0000000..2b2c969
--- /dev/null
+++ b/lib/AppInfo/OpenRegisterAutoloader.php
@@ -0,0 +1,195 @@
+
+ * @copyright 2026 Conduction B.V.
+ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
+ * SPDX-License-Identifier: EUPL-1.2
+ *
+ * @version GIT:
+ *
+ * @link https://conduction.nl
+ */
+
+declare(strict_types=1);
+
+namespace OCA\AppTemplate\AppInfo;
+
+/**
+ * Registers OpenRegister's autoload prefix before any of its classes is used.
+ *
+ * ## Why this is needed (ADR-040)
+ *
+ * `OC_App::getEnabledApps()` does `sort($apps)`, and
+ * `Coordinator::registerApps()` walks THAT sorted list registering one app's
+ * autoloader and then calling `$app->register()`, one app at a time. So every
+ * app's `register()` runs BEFORE the PSR-4 prefix of every alphabetically-LATER
+ * app exists.
+ *
+ * Most app ids sort before `openregister`, so `OCA\OpenRegister\` is NOT
+ * autoloadable inside `Application::register()` on a perfectly healthy
+ * instance with OpenRegister enabled. A `::class` constant does not autoload
+ * and is safe; anything that does (`class_exists()`, `instanceof` against a
+ * loaded object, a static call such as AppHost's `Bootstrap::register()`)
+ * throws or answers FALSE. An uncaught `\Error` aborts the whole of
+ * `register()` while `Coordinator` logs an `emergency` and carries on, so the
+ * app stays enabled and looks fine.
+ *
+ * Apps generated from this template: call `OpenRegisterAutoloader::register()`
+ * first thing in `Application::register()` (as the template does) and keep
+ * every OpenRegister reference after it behind a `class_exists()` guard.
+ *
+ * Lives in its own class rather than inline in `Application::register()` for
+ * one reason: `Application` cannot be constructed without a Nextcloud DI
+ * container, so an inline prelude is unreachable from a unit test. Here the
+ * degraded-path contract — "this NEVER throws, whatever the instance looks
+ * like" — is directly assertable, and it is asserted.
+ */
+final class OpenRegisterAutoloader {
+
+ /**
+ * The app whose autoload prefix this prelude registers.
+ */
+ private const OPENREGISTER_APP_ID = 'openregister';
+
+ /**
+ * The PSR-4 namespace prefix OpenRegister's `lib/` serves.
+ */
+ private const OPENREGISTER_NAMESPACE = 'OCA\\OpenRegister\\';
+
+ /**
+ * The registered loader, or null when none is on the SPL chain.
+ *
+ * @var (\Closure(string): void)|null
+ */
+ private static ?\Closure $loader = null;
+
+ /**
+ * Register OpenRegister's PSR-4 prefix on the autoloader.
+ *
+ * MUST be called before any `OCA\OpenRegister\…` reference in
+ * `Application::register()`, including a `class_exists()` probe — the probe
+ * answers FALSE, not "not yet loaded", and a FALSE is indistinguishable
+ * from OpenRegister being absent.
+ *
+ * ## Public API only (Nextcloud 35)
+ *
+ * Earlier fleet copies of this prelude called `\OC_App::registerAutoloading()`.
+ * That is private API and Nextcloud 35 REMOVED it (it moved to the equally
+ * private `OC\App\AppManager::registerAutoloading()`), so the call threw,
+ * the catch below returned false and the caller's OpenRegister wiring was
+ * skipped in silence. Do not reintroduce it. For an app shipping `vendor/autoload.php` — which OpenRegister
+ * does — Nextcloud's own registration reduces to a PSR-4 prefix over `lib/`,
+ * so that is exactly what is registered here, with `spl_autoload_register()`
+ * and the public `IAppManager`. Same approach as keepiq#712.
+ *
+ * OpenRegister's `vendor/autoload.php` is deliberately NOT required: that
+ * would pull its whole dependency tree into this process, where versions
+ * differing from ours would win first-come.
+ *
+ * Deliberately NOT `IAppManager::loadApp('openregister')`: that marks
+ * OpenRegister loaded and calls `Coordinator::bootApp()`, booting it before
+ * its own `register()` has run.
+ *
+ * Idempotent: a second call while registered is a no-op.
+ *
+ * @param \OCP\App\IAppManager|null $appManager Injected for tests; resolved
+ * from the server when null.
+ *
+ * @return bool True when the prefix is registered, false when OpenRegister
+ * is absent, disabled, or otherwise unresolvable — in which
+ * case the caller MUST fall through to its degraded path.
+ *
+ * @SuppressWarnings(PHPMD.StaticAccess) `\OCP\Server::get()` is the public
+ * service locator, and this runs at the composition root where no
+ * container is available to inject from.
+ */
+ public static function register(?\OCP\App\IAppManager $appManager=null): bool {
+ try {
+ $appManager ??= \OCP\Server::get(\OCP\App\IAppManager::class);
+
+ // Checked before the short-circuit: under a long-lived worker this
+ // static outlives the request, and an OpenRegister disabled since
+ // must not stay wired.
+ if ($appManager->isEnabledForAnyone(self::OPENREGISTER_APP_ID) === false) {
+ self::unregister();
+ return false;
+ }
+
+ if (self::$loader !== null) {
+ return true;
+ }
+
+ $path = rtrim($appManager->getAppPath(self::OPENREGISTER_APP_ID), '/');
+ if (is_dir($path.'/lib') === false) {
+ return false;
+ }
+
+ self::$loader = static function (string $class) use ($path): void {
+ $file = self::classFile(appPath: $path, class: $class);
+ if ($file !== null && is_file($file) === true) {
+ require_once $file;
+ }
+ };
+ spl_autoload_register(self::$loader);
+
+ return true;
+ } catch (\Throwable) {
+ // OpenRegister absent, or the server container is not up (unit
+ // tests). The caller's class_exists() guard then skips the AppHost
+ // plumbing. Never rethrow: an exception escaping here would abort
+ // the caller's entire register(), which is the exact defect this
+ // prelude exists to prevent.
+ return false;
+ }//end try
+
+ }//end register()
+
+ /**
+ * Take the loader off the SPL chain (tests, and OpenRegister disabled).
+ *
+ * @return void
+ */
+ public static function unregister(): void {
+ if (self::$loader !== null) {
+ spl_autoload_unregister(self::$loader);
+ self::$loader = null;
+ }
+
+ }//end unregister()
+
+ /**
+ * Map an `OCA\OpenRegister\…` class name to its file under `lib/`.
+ *
+ * Returns null for any class that is not OpenRegister's, so this loader
+ * never answers for (and shadows) names another loader owns.
+ *
+ * @param string $appPath Absolute path to the openregister app, no trailing slash.
+ * @param string $class The fully qualified class name being resolved.
+ *
+ * @return string|null The candidate file, or null when not OpenRegister's.
+ */
+ public static function classFile(string $appPath, string $class): ?string {
+ if (str_starts_with($class, self::OPENREGISTER_NAMESPACE) === false) {
+ return null;
+ }
+
+ $relative = substr($class, strlen(self::OPENREGISTER_NAMESPACE));
+ if ($relative === '') {
+ return null;
+ }
+
+ return $appPath.'/lib/'.str_replace('\\', '/', $relative).'.php';
+
+ }//end classFile()
+}//end class
diff --git a/templates/settings/admin.php b/templates/settings/admin.php
index c0bca20..425fbdb 100644
--- a/templates/settings/admin.php
+++ b/templates/settings/admin.php
@@ -9,7 +9,7 @@
// Inject the app version via Nextcloud's IInitialState API so the Vue settings
// app can read it with loadState('apptemplate', 'version') — the NC-standard
// CSP-compliant approach (replaces the prior data-version DOM attribute).
-\OC::$server->get(\OCP\IInitialStateService::class)
+\OCP\Server::get(\OCP\IInitialStateService::class)
->provideInitialState($appId, 'version', $_['version'] ?? '');
// webpack splitChunks emits shared chunks that every entry-point depends on
diff --git a/tests/unit/AppInfo/OpenRegisterAutoloaderTest.php b/tests/unit/AppInfo/OpenRegisterAutoloaderTest.php
new file mode 100644
index 0000000..969cdf6
--- /dev/null
+++ b/tests/unit/AppInfo/OpenRegisterAutoloaderTest.php
@@ -0,0 +1,108 @@
+
+ * @copyright 2026 Conduction B.V.
+ * @license EUPL-1.2 https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
+ *
+ * @version GIT:
+ *
+ * @link https://conduction.nl
+ *
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
+ * SPDX-License-Identifier: EUPL-1.2
+ */
+
+declare(strict_types=1);
+
+namespace OCA\AppTemplate\Tests\Unit\AppInfo;
+
+use OCA\AppTemplate\AppInfo\OpenRegisterAutoloader;
+use OCP\App\IAppManager;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * The prelude must work on Nextcloud 35, which removed the private
+ * `OC_App::registerAutoloading()` it used to call, so it may only use public
+ * API and plain PHP — and it must never throw.
+ */
+class OpenRegisterAutoloaderTest extends TestCase {
+
+ /**
+ * Temporary fake openregister app directory.
+ *
+ * @var string
+ */
+ private string $appPath;
+
+ protected function setUp(): void {
+ parent::setUp();
+ $this->appPath = sys_get_temp_dir().'/apptemplate-or-'.bin2hex(random_bytes(4));
+ mkdir($this->appPath.'/lib/Fake', 0777, true);
+ file_put_contents(
+ $this->appPath.'/lib/Fake/Probe.php',
+ "appPath.'/lib/Fake/Probe.php');
+ @rmdir($this->appPath.'/lib/Fake');
+ @rmdir($this->appPath.'/lib');
+ @rmdir($this->appPath);
+ parent::tearDown();
+
+ }//end tearDown()
+
+ public function testSourceUsesNoPrivateOcAppApi(): void {
+ $source = (string) file_get_contents(__DIR__.'/../../../lib/AppInfo/OpenRegisterAutoloader.php');
+ $code = (string) preg_replace('#/\*.*?\*/|//[^\n]*#s', '', $source);
+ $this->assertStringNotContainsString('OC_App', $code);
+
+ }//end testSourceUsesNoPrivateOcAppApi()
+
+ public function testRegistersPsr4PrefixWhenEnabled(): void {
+ $appManager = $this->createMock(IAppManager::class);
+ $appManager->method('isEnabledForAnyone')->with('openregister')->willReturn(true);
+ $appManager->method('getAppPath')->with('openregister')->willReturn($this->appPath.'/');
+
+ $this->assertTrue(OpenRegisterAutoloader::register(appManager: $appManager));
+ $this->assertTrue(OpenRegisterAutoloader::register(appManager: $appManager));
+ $this->assertTrue(class_exists('OCA\\OpenRegister\\Fake\\Probe'));
+
+ }//end testRegistersPsr4PrefixWhenEnabled()
+
+ public function testReturnsFalseWhenDisabled(): void {
+ $appManager = $this->createMock(IAppManager::class);
+ $appManager->method('isEnabledForAnyone')->willReturn(false);
+ $appManager->expects($this->never())->method('getAppPath');
+
+ $this->assertFalse(OpenRegisterAutoloader::register(appManager: $appManager));
+
+ }//end testReturnsFalseWhenDisabled()
+
+ public function testNeverThrows(): void {
+ $appManager = $this->createMock(IAppManager::class);
+ $appManager->method('isEnabledForAnyone')->willThrowException(new \RuntimeException('boom'));
+
+ $this->assertFalse(OpenRegisterAutoloader::register(appManager: $appManager));
+
+ }//end testNeverThrows()
+
+ public function testClassFileOnlyAnswersForOpenRegister(): void {
+ $this->assertSame(
+ '/x/lib/Db/Schema.php',
+ OpenRegisterAutoloader::classFile(appPath: '/x', class: 'OCA\\OpenRegister\\Db\\Schema')
+ );
+ $this->assertNull(OpenRegisterAutoloader::classFile(appPath: '/x', class: 'OCA\\AppTemplate\\Foo'));
+ $this->assertNull(OpenRegisterAutoloader::classFile(appPath: '/x', class: 'OCA\\OpenRegister\\'));
+
+ }//end testClassFileOnlyAnswersForOpenRegister()
+}//end class