Skip to content

Commit e35c0a7

Browse files
authored
Merge pull request #63388 from nextcloud/fix/clean-app-autoloading-register
Clean up application autoloading registration
2 parents d0e216b + dd5baf9 commit e35c0a7

55 files changed

Lines changed: 129 additions & 117 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/sharing/tests/CapabilitiesTest.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
/**
46
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
57
* SPDX-License-Identifier: AGPL-3.0-or-later
68
*/
79

8-
declare(strict_types=1);
10+
namespace OCA\Sharing\Tests;
911

1012
use NCU\Sharing\ISharingRegistry;
1113
use OCA\Sharing\AppInfo\Application;

apps/sharing/tests/Command/CommandTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
/**
46
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
57
* SPDX-License-Identifier: AGPL-3.0-or-later
68
*/
79

8-
declare(strict_types=1);
10+
namespace OCA\Sharing\Tests\Command;
911

12+
use Exception;
1013
use NCU\Sharing\ISharingManager;
1114
use NCU\Sharing\ISharingRegistry;
1215
use NCU\Sharing\Permission\SharePermission;
@@ -37,7 +40,9 @@
3740
use OCP\IUserManager;
3841
use OCP\L10N\IFactory;
3942
use OCP\Server;
43+
use Override;
4044
use PHPUnit\Framework\Attributes\Group;
45+
use RuntimeException;
4146
use Symfony\Component\Console\Input\Input;
4247
use Symfony\Component\Console\Output\ConsoleOutput;
4348
use Symfony\Component\Console\Output\Output;

apps/sharing/tests/Controller/ApiV1ControllerTest.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
/**
46
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
57
* SPDX-License-Identifier: AGPL-3.0-or-later
68
*/
79

8-
declare(strict_types=1);
10+
namespace OCA\Sharing\Tests\Controller;
911

12+
use Closure;
1013
use NCU\Sharing\ISharingManager;
1114
use NCU\Sharing\Permission\SharePermission;
1215
use NCU\Sharing\Property\ShareProperty;
@@ -25,6 +28,7 @@
2528
use OCP\IUserSession;
2629
use OCP\L10N\IFactory;
2730
use OCP\Server;
31+
use Override;
2832
use PHPUnit\Framework\Attributes\Group;
2933
use Test\Sharing\AbstractSharingManagerTests;
3034

lib/private/App/AppManager.php

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class AppManager implements IAppManager {
8383

8484
/** @var array<string, true> */
8585
private array $loadedApps = [];
86+
/** @var array<string, true> */
87+
private array $registeredApps = [];
8688

8789
/** @var string[] */
8890
private $namespaceCache = [];
@@ -267,22 +269,12 @@ public function loadApps(array $types = []): bool {
267269

268270
// Load the enabled apps here
269271
$apps = $this->getEnabledApps();
270-
271-
// Add each apps' folder as allowed class path
272-
foreach ($apps as $app) {
272+
$appsToRegister = array_filter(
273+
$apps,
273274
// If the app is already loaded then autoloading it makes no sense
274-
if (!$this->isAppLoaded($app) && ($types === [] || $this->isType($app, $types))) {
275-
try {
276-
$path = $this->getAppPath($app);
277-
\OC_App::registerAutoloading($app, $path);
278-
} catch (AppPathNotFoundException $e) {
279-
$this->logger->info('Error during app loading: ' . $e->getMessage(), [
280-
'exception' => $e,
281-
'app' => $app,
282-
]);
283-
}
284-
}
285-
}
275+
fn (string $app) => (!$this->isAppLoaded($app) && ($types === [] || $this->isType($app, $types))),
276+
);
277+
$this->registerAppsAutoloading($appsToRegister);
286278

287279
// prevent app loading from printing output
288280
ob_start();
@@ -487,7 +479,7 @@ public function loadApp(string $app): void {
487479
$eventLogger->start("bootstrap:load_app:$app", "Load app: $app");
488480

489481
// in case someone calls loadApp() directly
490-
\OC_App::registerAutoloading($app, $appPath);
482+
$this->registerAppsAutoloading([$app]);
491483

492484
if (is_file($appPath . '/appinfo/app.php')) {
493485
$this->logger->error('/appinfo/app.php is not supported anymore, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [
@@ -578,6 +570,52 @@ public function loadApp(string $app): void {
578570
$eventLogger->end("bootstrap:load_app:$app");
579571
}
580572

573+
/**
574+
* @internal
575+
*/
576+
public function registerAppsAutoloading(array $apps): void {
577+
foreach ($apps as $app) {
578+
if (!isset($this->registeredApps[$app])) {
579+
try {
580+
$path = $this->getAppPath($app);
581+
$this->registerAutoloading($app, $path);
582+
} catch (AppPathNotFoundException $e) {
583+
$this->logger->info('Error during app loading: ' . $e->getMessage(), [
584+
'exception' => $e,
585+
'app' => $app,
586+
]);
587+
}
588+
}
589+
}
590+
}
591+
592+
/**
593+
* @internal
594+
*/
595+
public function registerAutoloading(string $app, string $path, bool $force = false): void {
596+
if (!$force && isset($this->registeredApps[$app])) {
597+
return;
598+
}
599+
600+
$this->registeredApps[$app] = true;
601+
602+
// Register on PSR-4 composer autoloader
603+
$appNamespace = $this->getAppNamespace($app);
604+
\OC::$server->registerNamespace($app, $appNamespace);
605+
606+
if (file_exists($path . '/composer/autoload.php')) {
607+
require_once $path . '/composer/autoload.php';
608+
} elseif (is_dir($path . '/lib')) {
609+
// autoloader crashes on non-existing dir
610+
\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
611+
}
612+
613+
// Register Test namespace only when testing
614+
if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
615+
\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
616+
}
617+
}
618+
581619
/**
582620
* Check if an app is loaded
583621
* @param string $app app id
@@ -1107,7 +1145,7 @@ public function upgradeApp(string $appId): bool {
11071145
$ignoreMax = in_array($appId, $ignoreMaxApps, true);
11081146
$this->checkAppDependencies($appId, $ignoreMax);
11091147

1110-
\OC_App::registerAutoloading($appId, $appPath, true);
1148+
$this->registerAutoloading($appId, $appPath, true);
11111149
$this->executeRepairSteps($appId, $appInfo['repair-steps']['pre-migration']);
11121150

11131151
$ms = new MigrationService($appId, Server::get(\OC\DB\Connection::class));

lib/private/AppFramework/Bootstrap/Coordinator.php

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,8 @@
99

1010
namespace OC\AppFramework\Bootstrap;
1111

12+
use OC\App\AppManager;
1213
use OC\Support\CrashReport\Registry;
13-
use OC_App;
14-
use OCP\App\AppPathNotFoundException;
15-
use OCP\App\IAppManager;
1614
use OCP\AppFramework\App;
1715
use OCP\AppFramework\Bootstrap\IBootstrap;
1816
use OCP\AppFramework\QueryException;
@@ -40,7 +38,7 @@ public function __construct(
4038
private IManager $dashboardManager,
4139
private IEventDispatcher $eventDispatcher,
4240
private IEventLogger $eventLogger,
43-
private IAppManager $appManager,
41+
private AppManager $appManager,
4442
private LoggerInterface $logger,
4543
) {
4644
}
@@ -66,21 +64,12 @@ private function registerApps(array $appIds): void {
6664
if ($this->registrationContext === null) {
6765
$this->registrationContext = new RegistrationContext($this->logger);
6866
}
67+
$this->eventLogger->start('bootstrap:register_app:autoloader', 'Setup autoloader for apps');
68+
$this->appManager->registerAppsAutoloading($appIds);
69+
$this->eventLogger->end('bootstrap:register_app:autoloader');
6970
$apps = [];
7071
foreach ($appIds as $appId) {
7172
$this->eventLogger->start("bootstrap:register_app:$appId", "Register $appId");
72-
$this->eventLogger->start("bootstrap:register_app:$appId:autoloader", "Setup autoloader for $appId");
73-
/*
74-
* First, we have to enable the app's autoloader
75-
*/
76-
try {
77-
$path = $this->appManager->getAppPath($appId);
78-
OC_App::registerAutoloading($appId, $path);
79-
} catch (AppPathNotFoundException) {
80-
// Ignore
81-
continue;
82-
}
83-
$this->eventLogger->end("bootstrap:register_app:$appId:autoloader");
8473

8574
/*
8675
* Next we check if there is an application class, and it implements

lib/private/Console/Application.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ public function loadCommands(
126126
}
127127
}
128128
// load from register_command.php
129-
\OC_App::registerAutoloading($app, $appPath);
130129
$file = $appPath . '/appinfo/register_command.php';
131130
if (file_exists($file)) {
132131
try {

lib/private/Installer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ public function installShippedApps(bool $softErrors = false, ?IOutput $output =
534534
}
535535

536536
private function installAppLastSteps(string $appPath, array $info, ?IOutput $output = null, string $enabled = 'no'): string {
537-
\OC_App::registerAutoloading($info['id'], $appPath);
537+
$this->appManager->registerAutoloading($info['id'], $appPath, true);
538538

539539
$previousVersion = $this->config->getAppValue($info['id'], 'installed_version', '');
540540
$ms = new MigrationService($info['id'], Server::get(Connection::class));

lib/private/legacy/OC_App.php

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
88
* SPDX-License-Identifier: AGPL-3.0-only
99
*/
10+
1011
use OC\App\AppManager;
1112
use OC\AppFramework\Bootstrap\Coordinator;
1213
use OC\Installer;
@@ -106,33 +107,6 @@ public static function loadApp(string $app): void {
106107
Server::get(IAppManager::class)->loadApp($app);
107108
}
108109

109-
/**
110-
* @internal
111-
*/
112-
public static function registerAutoloading(string $app, string $path, bool $force = false): void {
113-
$key = $app . '-' . $path;
114-
if (!$force && isset(self::$alreadyRegistered[$key])) {
115-
return;
116-
}
117-
118-
self::$alreadyRegistered[$key] = true;
119-
120-
// Register on PSR-4 composer autoloader
121-
$appNamespace = Server::get(IAppManager::class)->getAppNamespace($app);
122-
\OC::$server->registerNamespace($app, $appNamespace);
123-
124-
if (file_exists($path . '/composer/autoload.php')) {
125-
require_once $path . '/composer/autoload.php';
126-
} else {
127-
\OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true);
128-
}
129-
130-
// Register Test namespace only when testing
131-
if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) {
132-
\OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true);
133-
}
134-
}
135-
136110
/**
137111
* Check if an app is of a specific type
138112
*

tests/Core/Command/Config/System/CastHelperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* SPDX-License-Identifier: AGPL-3.0-only
66
*/
77

8-
namespace Core\Command\Config\System;
8+
namespace Tests\Core\Command\Config\System;
99

1010
use OC\Core\Command\Config\System\CastHelper;
1111
use Test\TestCase;

tests/Core/Command/Group/AddTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* SPDX-License-Identifier: AGPL-3.0-or-later
66
*/
77

8-
namespace Test\Core\Command\Group;
8+
namespace Tests\Core\Command\Group;
99

1010
use OC\Core\Command\Group\Add;
1111
use OCP\IGroup;

0 commit comments

Comments
 (0)