From 728901f7eb10fd94581955953a377aa3e1192fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 16 Jul 2026 11:13:19 +0200 Subject: [PATCH 01/10] feat: Add a PhpDumpCache service for caching arrays as PHP files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This leverages opcache to be as fast as possible. Can be used for autoloading class maps, but also for other kind of pre-computed cached arrays (routes, occ commands, …). Signed-off-by: Côme Chilliet --- lib/private/PhpDumpCache.php | 111 +++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 lib/private/PhpDumpCache.php diff --git a/lib/private/PhpDumpCache.php b/lib/private/PhpDumpCache.php new file mode 100644 index 0000000000000..4bc4af2b3fbe8 --- /dev/null +++ b/lib/private/PhpDumpCache.php @@ -0,0 +1,111 @@ +tempDirectory = $dir; + return $this; + } + + /** + * Loads class list from cache. + */ + public function loadCache(array $cacheKey): ?array { + $file = $this->generateCacheFileName($cacheKey); + + // Solving atomicity to work everywhere + // 1) We want to do as little as possible IO calls on production and also directory and file can be not writable (#19) + // so on Linux we include the file directly without shared lock, therefore, the file must be created atomically by renaming. + // 2) On Windows file cannot be renamed-to while is open (ie by include() #11), so we have to acquire a lock. + $lock = defined('PHP_WINDOWS_VERSION_BUILD') + ? $this->acquireLock("$file.lock", LOCK_SH) + : null; + + try { + $data = @include $file; // @ file may not exist + if (is_array($data)) { + return $data; + } + + return null; + } finally { + if ($lock) { + flock($lock, LOCK_UN); // release shared lock + } + } + } + + /** + * Writes class list to cache. + * @param ?resource $lock + */ + public function saveCache(array $cacheKey, array $data, $lock = null): void { + // we have to acquire a lock to be able safely rename file + // on Linux: that another thread does not rename the same named file earlier + // on Windows: that the file is not read by another thread + $file = $this->generateCacheFileName($cacheKey); + $lock = $lock ?: $this->acquireLock("$file.lock", LOCK_EX); + $code = "tempDirectory) { + throw new \LogicException('Set path to temporary directory using setTempDirectory().'); + } + + return $this->tempDirectory . '/' . md5(serialize($cacheKey)) . '.php'; + } +} From 965abd36ca8fbbfe06220e545932658f8e85c9f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 16 Jul 2026 11:15:42 +0200 Subject: [PATCH 02/10] feat: Use a custom autoloader instead of composer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The idea is to cache to disk a static classmap with all classes from core and applications. Signed-off-by: Côme Chilliet --- console.php | 1 + lib/OC.php | 17 +- lib/private/App/AppManager.php | 14 +- lib/private/Autoloader.php | 292 +++++++++++++++++++++++++++++++++ tests/autoload.php | 3 +- tests/bootstrap.php | 2 +- 6 files changed, 321 insertions(+), 8 deletions(-) create mode 100644 lib/private/Autoloader.php diff --git a/console.php b/console.php index baf1a520e0841..0128c7cbc2522 100644 --- a/console.php +++ b/console.php @@ -115,6 +115,7 @@ function exceptionHandler($exception) { $exitCode = 255; } + var_dump(\OC::$autoloader->getStats()); exit($exitCode); } catch (Exception $ex) { exceptionHandler($ex); diff --git a/lib/OC.php b/lib/OC.php index 251aa8b9a2bc1..5513667a6b1f6 100644 --- a/lib/OC.php +++ b/lib/OC.php @@ -41,6 +41,9 @@ require_once __DIR__ . '/public/Constants.php'; +// There is no autoloading for functions so we need to hardcode it +require_once __DIR__ . '/public/Log/functions.php'; + /** * Class that is a namespace for all global OC variables * @internal @@ -90,6 +93,7 @@ class OC { * @psalm-suppress ImpureStaticProperty */ public static \Composer\Autoload\ClassLoader $composerAutoloader; + public static \OC\Autoloader $autoloader; /** * @psalm-suppress ImpureStaticProperty @@ -684,9 +688,16 @@ public static function boot(): void { self::$CLI = (php_sapi_name() === 'cli'); - // Add default composer PSR-4 autoloader, ensure apcu to be disabled - self::$composerAutoloader = require_once OC::$SERVERROOT . '/lib/composer/autoload.php'; - self::$composerAutoloader->setApcuPrefix(null); + require_once __DIR__ . '/private/PhpDumpCache.php'; + require_once __DIR__ . '/private/Autoloader.php'; + $phpDumpCache = new \OC\PhpDumpCache(OC::$SERVERROOT . '/temp'); + self::$autoloader = new \OC\Autoloader($phpDumpCache); + self::$autoloader->addPsr4('OC', OC::$SERVERROOT . '/lib/private'); + self::$autoloader->addPsr4('OCP', OC::$SERVERROOT . '/lib/public'); + self::$autoloader->addPsr4('NCU', OC::$SERVERROOT . '/lib/unstable'); + self::$autoloader->addPsr4('OC\\Core', OC::$SERVERROOT . '/core'); + self::$autoloader->addPsr4('', OC::$SERVERROOT . '/lib/private/legacy'); + self::$autoloader->register(); // setup 3rdparty autoloader $vendorAutoLoad = OC::$SERVERROOT . '/3rdparty/autoload.php'; diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index b86a17bcfecdf..13aedc6727396 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -581,11 +581,13 @@ public function loadApp(string $app): void { * @internal */ public function registerAppsAutoloading(array $apps): void { + $reload = false; foreach ($apps as $app) { if (!isset($this->registeredApps[$app])) { try { $path = $this->getAppPath($app); $this->registerAutoloading($app, $path); + $reload = true; } catch (AppPathNotFoundException $e) { $this->logger->info('Error during app loading: ' . $e->getMessage(), [ 'exception' => $e, @@ -594,6 +596,9 @@ public function registerAppsAutoloading(array $apps): void { } } } + if ($reload) { + \OC::$autoloader->triggerReload(); + } } /** @@ -614,12 +619,15 @@ public function registerAutoloading(string $app, string $path, bool $force = fal require_once $path . '/composer/autoload.php'; } elseif (is_dir($path . '/lib')) { // autoloader crashes on non-existing dir - \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true); + \OC::$autoloader->addPsr4($appNamespace, $path . '/lib'); } // Register Test namespace only when testing - if (defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) { - \OC::$composerAutoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/', true); + if ((defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) && is_dir($path . '/tests/')) { + \OC::$autoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/'); + } + if ($force) { + \OC::$autoloader->triggerReload(); } } diff --git a/lib/private/Autoloader.php b/lib/private/Autoloader.php new file mode 100644 index 0000000000000..7a6aee88a31a2 --- /dev/null +++ b/lib/private/Autoloader.php @@ -0,0 +1,292 @@ + namespace => path */ + private array $psr4Paths = []; + + /** @var string[] */ + private array $excludeDirs = []; + + /** @var array class => [file, time] */ + private array $classes = []; + private bool $cacheLoaded = false; + private bool $refreshed = false; + + /** @var array class => counter */ + private array $missingClasses = []; + + private bool $needSave = false; + + private int $loadsFromCache = 0; + private int $diskScans = 0; + + public function __construct( + private PhpDumpCache $dumpCache, + ) { + if (!extension_loaded('tokenizer')) { + throw new \LogicException('PHP extension Tokenizer is not loaded.'); + } + } + + public function __destruct() { + if ($this->needSave) { + $this->saveCache(); + } + } + + /** + * Register autoloader. + */ + public function register(bool $prepend = false): static { + spl_autoload_register([$this, 'tryLoad'], prepend: $prepend); + return $this; + } + + /** + * Handles autoloading of classes, interfaces or traits. + */ + public function tryLoad(string $type): void { + try { + $this->loadCache(); + + $missing = $this->missingClasses[$type] ?? 0; + if ($missing >= self::RetryLimit) { + return; + } + + [$file, $mtime] = $this->classes[$type] ?? null; + + if ($file) { + (static function ($file) { + require $file; + })($file); + } + } catch (\Throwable $t) { + throw $t; + } + } + + /** + * Add path for given namespace + * + * @param string $namespace The namespace to register, with no \ at either end. + * @param string $path The path to register, with a beginning / and no ending /. + */ + public function addPsr4(string $namespace, string $path): static { + $this->psr4Paths[$namespace] = $path; + return $this; + } + + public function triggerReload(): void { + $this->refreshed = false; + $this->cacheLoaded = false; + } + + public function reportParseErrors(bool $on = true): static { + $this->reportParseErrors = $on; + return $this; + } + + /** + * Excludes path or paths from list. + */ + public function excludeDirectory(string ...$paths): static { + $this->excludeDirs = array_merge($this->excludeDirs, $paths); + return $this; + } + + /** + * @return array class => filename + */ + public function getIndexedClasses(): array { + $this->loadCache(); + $res = []; + foreach ($this->classes as $class => [$file]) { + $res[$class] = $file; + } + + return $res; + } + + /** + * Rebuilds class list cache. + */ + public function rebuild(): void { + $this->cacheLoaded = true; + $this->classes = $this->missingClasses = []; + $this->refreshClasses(); + $this->saveCache(); + } + + /** + * Refreshes class list cache. + */ + public function refresh(): void { + $this->loadCache(); + if (!$this->refreshed) { + $this->refreshClasses(); + $this->saveCache(); + } + } + + /** + * Refreshes $this->classes. + */ + private function refreshClasses(): void { + $this->refreshed = true; // prevents calling refreshClasses() in tryLoad() + $files = []; + $classes = []; + foreach ($this->classes as $class => [$file, $mtime]) { + $files[$file] = $mtime; + $classes[$file][] = $class; + } + + $this->classes = []; + + foreach ($this->psr4Paths as $namespace => $path) { + $iterator = $this->createFileIterator($path); + // Length of path + separator + $pathLen = strlen($path) + 1; + if ($namespace === '') { + $prefix = ''; + } else { + $prefix = $namespace . '\\'; + } + foreach ($iterator as $file) { + $class = $prefix . str_replace('/', '\\', substr($file, $pathLen, -4)); + if (isset($this->classes[$class])) { + throw new \RuntimeException(sprintf( + 'Ambiguous class %s resolution; defined in %s and in %s.', + $class, + $this->classes[$class][0], + $file, + )); + } + + //FIXME needed? + $mtime = filemtime($file); + + $this->classes[$class] = [$file, $mtime]; + unset($this->missingClasses[$class]); + } + } + + $this->diskScans++; + } + + /** + * Creates an iterator scanning directory for PHP files and subdirectories. + * @throws \RuntimeException if path is not found + */ + private function createFileIterator(string $dir): \Generator { + if (!is_dir($dir)) { + throw new \RuntimeException(sprintf("Directory '%s' not found.", $dir)); + } + + $dir = realpath($dir) ?: $dir; // realpath does not work in phar + $disallow = []; + foreach (array_merge($this->ignoreDirs, $this->excludeDirs) as $item) { + if ($item = realpath($item)) { + $disallow[$item] = true; + } + } + + yield from $this->traverseDir($dir, $disallow); + } + + private function traverseDir(string $dir, array $disallow): \Generator { + try { + $files = new \FilesystemIterator($dir, \FilesystemIterator::FOLLOW_SYMLINKS | \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME | \FilesystemIterator::UNIX_PATHS); + } catch (\RuntimeException) { + return; + } + + foreach ($files as $file) { + $realPath = realpath($file); + $file = $realPath ?: $file; + if ($realPath && isset($disallow[$realPath])) { + continue; + } elseif (is_dir($file) && !self::matches(basename($file), $this->ignoreDirs)) { + yield from $this->traverseDir($file, $disallow); + } elseif (is_file($file) && self::matches(basename($file), $this->acceptFiles)) { + yield $file; + } + } + } + + private static function matches(string $file, array $masks): bool { + foreach ($masks as $mask) { + if (fnmatch($mask, $file)) { + return true; + } + } + return false; + } + + /********************* caching *******************/ + + /** + * Loads class list from cache. + */ + private function loadCache(): void { + if ($this->cacheLoaded) { + return; + } + + $this->cacheLoaded = true; + + $data = $this->dumpCache->loadCache($this->generateCacheKey()); + if (is_array($data)) { + [$this->classes, $this->missingClasses] = $data; + $this->loadsFromCache++; + return; + } + + $this->classes = $this->missingClasses = []; + $this->refreshClasses(); + $this->saveCache(); + } + + /** + * Writes class list to cache. + * @param resource $lock + */ + private function saveCache(): void { + $this->dumpCache->saveCache($this->generateCacheKey(), [$this->classes, $this->missingClasses]); + } + + protected function generateCacheKey(): array { + return [$this->psr4Paths,$this->ignoreDirs, $this->acceptFiles, $this->excludeDirs]; + } + + public function getStats(): array { + return [ + 'Loads from cache' => $this->loadsFromCache, + 'Disk scans' => $this->diskScans, + ]; + } +} diff --git a/tests/autoload.php b/tests/autoload.php index 05fc38529242f..c25319e45f381 100644 --- a/tests/autoload.php +++ b/tests/autoload.php @@ -13,4 +13,5 @@ * This is a file that applications can require to be able to autoload the class Test\TestCase from Nextcloud tests */ -\OC::$composerAutoloader->addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/', true); +\OC::$autoloader->addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/'); +\OC::$autoloader->triggerReload(); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ecb0460a330d5..7d8090e390812 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -21,7 +21,7 @@ require_once __DIR__ . '/../lib/base.php'; require_once __DIR__ . '/autoload.php'; -\OC::$composerAutoloader->addPsr4('Tests\\Core\\', OC::$SERVERROOT . '/tests/Core/', true); +\OC::$autoloader->addPsr4('Tests\\Core', OC::$SERVERROOT . '/tests/Core'); $dontLoadApps = getenv('TEST_DONT_LOAD_APPS'); if (!$dontLoadApps) { From ec0f9ab7754b80a9fe5708ac06db7afc1ddd31fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 24 Aug 2026 15:34:57 +0200 Subject: [PATCH 03/10] chore: Make cache directory configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/OC.php | 31 ++++++++++++++++++------------- lib/private/PhpDumpCache.php | 3 ++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/OC.php b/lib/OC.php index 5513667a6b1f6..56f8bef4440a2 100644 --- a/lib/OC.php +++ b/lib/OC.php @@ -683,6 +683,23 @@ public static function boot(): void { // calculate the root directories OC::$SERVERROOT = str_replace('\\', '/', substr(__DIR__, 0, -4)); + // No autoloader yet, manually load Config class + require_once __DIR__ . '/private/Config.php'; + + // load configs + if (defined('PHPUNIT_CONFIG_DIR')) { + self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/'; + } elseif (defined('PHPUNIT_RUN') && PHPUNIT_RUN && is_dir(OC::$SERVERROOT . '/tests/config/')) { + self::$configDir = OC::$SERVERROOT . '/tests/config/'; + } elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { + self::$configDir = rtrim($dir, '/') . '/'; + } else { + self::$configDir = OC::$SERVERROOT . '/config/'; + } + self::$config = new \OC\Config(self::$configDir); + + $cacheDirectory = self::$config->getValue('cachedirectory', OC::$SERVERROOT . '/cache'); + // register autoloader self::$loaderStart = microtime(true); @@ -690,7 +707,7 @@ public static function boot(): void { require_once __DIR__ . '/private/PhpDumpCache.php'; require_once __DIR__ . '/private/Autoloader.php'; - $phpDumpCache = new \OC\PhpDumpCache(OC::$SERVERROOT . '/temp'); + $phpDumpCache = new \OC\PhpDumpCache($cacheDirectory); self::$autoloader = new \OC\Autoloader($phpDumpCache); self::$autoloader->addPsr4('OC', OC::$SERVERROOT . '/lib/private'); self::$autoloader->addPsr4('OCP', OC::$SERVERROOT . '/lib/public'); @@ -708,18 +725,6 @@ public static function boot(): void { self::$loaderEnd = microtime(true); - // load configs - if (defined('PHPUNIT_CONFIG_DIR')) { - self::$configDir = OC::$SERVERROOT . '/' . PHPUNIT_CONFIG_DIR . '/'; - } elseif (defined('PHPUNIT_RUN') && PHPUNIT_RUN && is_dir(OC::$SERVERROOT . '/tests/config/')) { - self::$configDir = OC::$SERVERROOT . '/tests/config/'; - } elseif ($dir = getenv('NEXTCLOUD_CONFIG_DIR')) { - self::$configDir = rtrim($dir, '/') . '/'; - } else { - self::$configDir = OC::$SERVERROOT . '/config/'; - } - self::$config = new \OC\Config(self::$configDir); - // Enable lazy loading if activated \OC\AppFramework\Utility\SimpleContainer::$useLazyObjects = (bool)self::$config->getValue('enable_lazy_objects', true); diff --git a/lib/private/PhpDumpCache.php b/lib/private/PhpDumpCache.php index 4bc4af2b3fbe8..dfa1215559935 100644 --- a/lib/private/PhpDumpCache.php +++ b/lib/private/PhpDumpCache.php @@ -14,8 +14,9 @@ class PhpDumpCache { public function __construct( - private ?string $tempDirectory = null, + private string $tempDirectory, ) { + $this->setTempDirectory($tempDirectory); } /** From e3f4db9afb7985e5685ed297543fc7ddbd970dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 17 Aug 2026 11:37:25 +0200 Subject: [PATCH 04/10] feat: Cache application states and autoloading directly from AppManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoids parsing info.xml from each app and recomputing namespaces. For now Autoloader still has its own calls to PhpDumpCache which is a bit dirty, we might want to clean that up later. Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 57 ++++++++++++++++--- .../AppFramework/Bootstrap/Coordinator.php | 20 +++---- lib/private/Autoloader.php | 20 +++++++ lib/private/Server.php | 3 + 4 files changed, 78 insertions(+), 22 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 13aedc6727396..711e30720c215 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -90,6 +90,7 @@ class AppManager implements IAppManager { /** @var string[] */ private $namespaceCache = []; + private $appinfoCache = []; private ?AppConfig $appConfig = null; private ?IURLGenerator $urlGenerator = null; @@ -109,6 +110,8 @@ public function __construct( private ServerVersion $serverVersion, private ConfigManager $configManager, private DependencyAnalyzer $dependencyAnalyzer, + private \OC\PhpDumpCache $phpDumpCache, + private IEventLogger $eventLogger, ) { $this->enabledAppsForUserCache = new CappedMemoryCache(); } @@ -279,9 +282,11 @@ public function loadApps(array $types = []): bool { $appsToRegister = array_filter( $apps, // If the app is already loaded then autoloading it makes no sense - fn (string $app) => (!$this->isAppLoaded($app) && ($types === [] || $this->isType($app, $types))), + fn (string $app) => (!isset($this->registeredApps[$app]) && ($types === [] || $this->isType($app, $types))), ); - $this->registerAppsAutoloading($appsToRegister); + if (count($appsToRegister) > 0) { + $this->registerAppsAutoloading($appsToRegister); + } // prevent app loading from printing output ob_start(); @@ -482,11 +487,12 @@ public function loadApp(string $app): void { ]); return; } - $eventLogger = Server::get(IEventLogger::class); - $eventLogger->start("bootstrap:load_app:$app", "Load app: $app"); + $this->eventLogger->start("bootstrap:load_app:$app", "Load app: $app"); // in case someone calls loadApp() directly - $this->registerAppsAutoloading([$app]); + if (!isset($this->registeredApps[$app])) { + $this->registerAppsAutoloading([$app]); + } if (is_file($appPath . '/appinfo/app.php')) { $this->logger->error('/appinfo/app.php is not supported anymore, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ @@ -497,7 +503,7 @@ public function loadApp(string $app): void { $coordinator = Server::get(Coordinator::class); $coordinator->bootApp($app); - $eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it"); + $this->eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it"); $info = $this->getAppInfo($app); if (!empty($info['activity'])) { $activityManager = Server::get(IActivityManager::class); @@ -572,15 +578,46 @@ public function loadApp(string $app): void { } } } - $eventLogger->end("bootstrap:load_app:$app:info"); + $this->eventLogger->end("bootstrap:load_app:$app:info"); - $eventLogger->end("bootstrap:load_app:$app"); + $this->eventLogger->end("bootstrap:load_app:$app"); } /** * @internal */ public function registerAppsAutoloading(array $apps): void { + $this->eventLogger->start('bootstrap:register_apps_autoloading', ''); + $loadedApps = array_unique(array_merge($apps, array_keys($this->registeredApps))); + sort($loadedApps); + $cacheKey = [self::class, ...$loadedApps]; + $this->eventLogger->start('bootstrap:register_apps_autoloading:load_cache', ''); + $cachedInfo = $this->phpDumpCache->loadCache($cacheKey); + if ($cachedInfo !== null) { + // echo "loading cache, key is ".json_encode($cacheKey).' '.md5(serialize($cacheKey))."\n"; + $this->eventLogger->end('bootstrap:register_apps_autoloading:load_cache'); + [$this->namespaceCache, $this->appInfos, $autoloaderProperties] = $cachedInfo; + \OC::$autoloader->loadFromArray($autoloaderProperties); + + $this->eventLogger->start('bootstrap:register_apps_autoloading:apply_cache', ''); + foreach ($apps as $app) { + if (isset($this->registeredApps[$app])) { + continue; + } + $this->registeredApps[$app] = true; + + $appNamespace = $this->getAppNamespace($app); + \OC::$server->registerNamespace($app, $appNamespace); + $path = $this->getAppPath($app); + if (file_exists($path . '/composer/autoload.php')) { + // FIXME needed? + require_once $path . '/composer/autoload.php'; + } + } + $this->eventLogger->end('bootstrap:register_apps_autoloading:apply_cache'); + $this->eventLogger->end('bootstrap:register_apps_autoloading'); + return; + } $reload = false; foreach ($apps as $app) { if (!isset($this->registeredApps[$app])) { @@ -597,8 +634,10 @@ public function registerAppsAutoloading(array $apps): void { } } if ($reload) { - \OC::$autoloader->triggerReload(); + \OC::$autoloader->rebuild(); } + $this->phpDumpCache->saveCache($cacheKey, [$this->namespaceCache, $this->appInfos, \OC::$autoloader->serializeToArray()]); + $this->eventLogger->end('bootstrap:register_apps_autoloading'); } /** diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php index 9770c036f777d..b9c58a805a755 100644 --- a/lib/private/AppFramework/Bootstrap/Coordinator.php +++ b/lib/private/AppFramework/Bootstrap/Coordinator.php @@ -65,22 +65,16 @@ private function registerApps(array $appIds): void { if ($this->registrationContext === null) { $this->registrationContext = new RegistrationContext($this->logger); } + $this->eventLogger->start('bootstrap:register_app:autoloader', 'Setup autoloader for apps'); + if ($appIds === []) { + $this->appManager->registerAppsAutoloading($this->appManager->getAlwaysEnabledApps()); + } else { + $this->appManager->registerAppsAutoloading($appIds); + } + $this->eventLogger->end('bootstrap:register_app:autoloader'); $apps = []; foreach ($appIds as $appId) { $this->eventLogger->start("bootstrap:register_app:$appId", "Register $appId"); - $this->eventLogger->start("bootstrap:register_app:$appId:autoloader", "Setup autoloader for app $appId"); - try { - $path = $this->appManager->getAppPath($appId); - $this->appManager->registerAutoloading($appId, $path); - } catch (AppPathNotFoundException $e) { - $this->logger->info('Error during app loading: ' . $e->getMessage(), [ - 'exception' => $e, - 'app' => $appId, - ]); - continue; - } - $this->eventLogger->end("bootstrap:register_app:$appId:autoloader"); - /* * Next we check if there is an application class, and it implements * the \OCP\AppFramework\Bootstrap\IBootstrap interface diff --git a/lib/private/Autoloader.php b/lib/private/Autoloader.php index 7a6aee88a31a2..5bbb3632ffd69 100644 --- a/lib/private/Autoloader.php +++ b/lib/private/Autoloader.php @@ -289,4 +289,24 @@ public function getStats(): array { 'Disk scans' => $this->diskScans, ]; } + + public function serializeToArray(): array { + return [ + 'psr4Paths' => $this->psr4Paths, + 'excludeDirs' => $this->excludeDirs, + 'classes' => $this->classes, + 'missingClasses' => $this->missingClasses, + ]; + } + + public function loadFromArray(array $properties): void { + $this->psr4Paths = $properties['psr4Paths']; + $this->excludeDirs = $properties['excludeDirs']; + $this->classes = $properties['classes']; + $this->missingClasses = $properties['missingClasses']; + + /* Avoid any refresh */ + $this->cacheLoaded = true; + $this->refreshed = true; + } } diff --git a/lib/private/Server.php b/lib/private/Server.php index f54a9568e55cf..9276ebe999233 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -323,6 +323,9 @@ public function __construct( return $this; }); $this->registerService(ContainerInterface::class, static fn (ContainerInterface $c) => $c); + $this->registerService(\OC\PhpDumpCache::class, function (ContainerInterface $c) { + return new \OC\PhpDumpCache($c->get(SystemConfig::class)->getValue('cachedirectory', \OC::$SERVERROOT . '/cache')); + }); $this->registerAlias(\OCP\Calendar\IManager::class, \OC\Calendar\Manager::class); From 166abe5c7bb888f8eb224caec8a87f356ffa1a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 17 Aug 2026 11:39:12 +0200 Subject: [PATCH 05/10] feat: Use PhpDumpCache in Console/Application to avoid loading all commmands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This saves 100ms on occ runs by avoiding to load all commands to only run one of them. The list of commands is cached and loading is skipped when command is known. Signed-off-by: Côme Chilliet --- lib/private/Console/Application.php | 30 ++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php index 49d9fe04550ce..5cce7871b954e 100644 --- a/lib/private/Console/Application.php +++ b/lib/private/Console/Application.php @@ -11,6 +11,7 @@ use ArgumentCountError; use OC\MemoryInfo; use OC\NeedsUpdateException; +use OC\PhpDumpCache; use OC\SystemConfig; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; @@ -43,6 +44,7 @@ public function __construct( private MemoryInfo $memoryInfo, private IAppManager $appManager, private Defaults $defaults, + private PhpDumpCache $dumpCache, ) { $this->application = new SymfonyApplication($defaults->getName(), $serverVersion->getVersionString()); } @@ -83,11 +85,11 @@ public function loadCommands( } try { - require_once __DIR__ . '/../../../core/register_command.php'; if ($this->config->getSystemValueBool('installed', false)) { if (Util::needUpgrade()) { throw new NeedsUpdateException(); } elseif ($this->config->getSystemValueBool('maintenance')) { + require_once __DIR__ . '/../../../core/register_command.php'; if ($this->appManager->isEnabledForAnyone('app_api')) { // AppAPI must stay usable during maintenance mode; // loading commands from register_command.php is intentionally skipped. @@ -106,6 +108,16 @@ public function loadCommands( } $this->writeMaintenanceModeInfo($input, $output); } else { + $cachedCommandList = $this->dumpCache->loadCache([self::class]); + if (is_array($cachedCommandList)) { + $firstArg = $input->getArgument('command'); + if ($firstArg !== null && isset($cachedCommandList[$firstArg])) { + $this->appManager->loadApps(); + $this->application->add(Server::get($cachedCommandList[$firstArg])); + return; + } + } + require_once __DIR__ . '/../../../core/register_command.php'; $this->appManager->loadApps(); foreach ($this->appManager->getEnabledApps() as $app) { try { @@ -150,6 +162,9 @@ public function loadCommands( } } + /* To cover branches from above if that skipped register_command */ + require_once __DIR__ . '/../../../core/register_command.php'; + if ($input->getFirstArgument() !== 'check') { $errors = \OC_Util::checkServer(Server::get(SystemConfig::class)); if (!empty($errors)) { @@ -161,6 +176,19 @@ public function loadCommands( throw new \Exception('Environment not properly prepared.'); } } + $commands = $this->application->all(); + $cache = []; + foreach ($commands as $command) { + $name = $command->getName(); + if ($name !== null) { + $cache[$name] = $command::class; + } + + foreach ($command->getAliases() as $alias) { + $cache[$alias] = $command::class; + } + } + $this->dumpCache->saveCache([self::class], $cache); } /** From 84fd83dadc31a7e64baa527dc7eb75984ef9c6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 17 Aug 2026 14:05:26 +0200 Subject: [PATCH 06/10] fix: Extract caching logic out of Autoloader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cache autoloader from the outside, once in OC.php for core autoloading, once in AppManager for apps autoloading. Signed-off-by: Côme Chilliet --- lib/OC.php | 25 +++++++++--- lib/private/Autoloader.php | 79 ++------------------------------------ 2 files changed, 22 insertions(+), 82 deletions(-) diff --git a/lib/OC.php b/lib/OC.php index 56f8bef4440a2..49ce8e2888d6f 100644 --- a/lib/OC.php +++ b/lib/OC.php @@ -708,12 +708,25 @@ public static function boot(): void { require_once __DIR__ . '/private/PhpDumpCache.php'; require_once __DIR__ . '/private/Autoloader.php'; $phpDumpCache = new \OC\PhpDumpCache($cacheDirectory); - self::$autoloader = new \OC\Autoloader($phpDumpCache); - self::$autoloader->addPsr4('OC', OC::$SERVERROOT . '/lib/private'); - self::$autoloader->addPsr4('OCP', OC::$SERVERROOT . '/lib/public'); - self::$autoloader->addPsr4('NCU', OC::$SERVERROOT . '/lib/unstable'); - self::$autoloader->addPsr4('OC\\Core', OC::$SERVERROOT . '/core'); - self::$autoloader->addPsr4('', OC::$SERVERROOT . '/lib/private/legacy'); + self::$autoloader = new \OC\Autoloader( + [ + 'OC' => OC::$SERVERROOT . '/lib/private', + 'OCP' => OC::$SERVERROOT . '/lib/public', + 'NCU' => OC::$SERVERROOT . '/lib/unstable', + 'OC\\Core' => OC::$SERVERROOT . '/core', + '' => OC::$SERVERROOT . '/lib/private/legacy', + ] + ); + + $cacheKey = [self::class]; + $cachedInfo = $phpDumpCache->loadCache($cacheKey); + if ($cachedInfo !== null) { + self::$autoloader->loadFromArray($cachedInfo); + } else { + self::$autoloader->rebuild(); + $phpDumpCache->saveCache($cacheKey, self::$autoloader->serializeToArray()); + } + self::$autoloader->register(); // setup 3rdparty autoloader diff --git a/lib/private/Autoloader.php b/lib/private/Autoloader.php index 5bbb3632ffd69..7eae116eabff6 100644 --- a/lib/private/Autoloader.php +++ b/lib/private/Autoloader.php @@ -25,37 +25,22 @@ class Autoloader { public array $acceptFiles = ['*.php']; private bool $reportParseErrors = true; - /** @var array namespace => path */ - private array $psr4Paths = []; - /** @var string[] */ private array $excludeDirs = []; /** @var array class => [file, time] */ private array $classes = []; - private bool $cacheLoaded = false; - private bool $refreshed = false; /** @var array class => counter */ private array $missingClasses = []; - private bool $needSave = false; - private int $loadsFromCache = 0; private int $diskScans = 0; public function __construct( - private PhpDumpCache $dumpCache, + /** @var array namespace => path */ + private array $psr4Paths = [], ) { - if (!extension_loaded('tokenizer')) { - throw new \LogicException('PHP extension Tokenizer is not loaded.'); - } - } - - public function __destruct() { - if ($this->needSave) { - $this->saveCache(); - } } /** @@ -71,8 +56,6 @@ public function register(bool $prepend = false): static { */ public function tryLoad(string $type): void { try { - $this->loadCache(); - $missing = $this->missingClasses[$type] ?? 0; if ($missing >= self::RetryLimit) { return; @@ -101,11 +84,6 @@ public function addPsr4(string $namespace, string $path): static { return $this; } - public function triggerReload(): void { - $this->refreshed = false; - $this->cacheLoaded = false; - } - public function reportParseErrors(bool $on = true): static { $this->reportParseErrors = $on; return $this; @@ -123,7 +101,6 @@ public function excludeDirectory(string ...$paths): static { * @return array class => filename */ public function getIndexedClasses(): array { - $this->loadCache(); $res = []; foreach ($this->classes as $class => [$file]) { $res[$class] = $file; @@ -136,28 +113,14 @@ public function getIndexedClasses(): array { * Rebuilds class list cache. */ public function rebuild(): void { - $this->cacheLoaded = true; $this->classes = $this->missingClasses = []; $this->refreshClasses(); - $this->saveCache(); - } - - /** - * Refreshes class list cache. - */ - public function refresh(): void { - $this->loadCache(); - if (!$this->refreshed) { - $this->refreshClasses(); - $this->saveCache(); - } } /** * Refreshes $this->classes. */ private function refreshClasses(): void { - $this->refreshed = true; // prevents calling refreshClasses() in tryLoad() $files = []; $classes = []; foreach ($this->classes as $class => [$file, $mtime]) { @@ -249,40 +212,6 @@ private static function matches(string $file, array $masks): bool { /********************* caching *******************/ - /** - * Loads class list from cache. - */ - private function loadCache(): void { - if ($this->cacheLoaded) { - return; - } - - $this->cacheLoaded = true; - - $data = $this->dumpCache->loadCache($this->generateCacheKey()); - if (is_array($data)) { - [$this->classes, $this->missingClasses] = $data; - $this->loadsFromCache++; - return; - } - - $this->classes = $this->missingClasses = []; - $this->refreshClasses(); - $this->saveCache(); - } - - /** - * Writes class list to cache. - * @param resource $lock - */ - private function saveCache(): void { - $this->dumpCache->saveCache($this->generateCacheKey(), [$this->classes, $this->missingClasses]); - } - - protected function generateCacheKey(): array { - return [$this->psr4Paths,$this->ignoreDirs, $this->acceptFiles, $this->excludeDirs]; - } - public function getStats(): array { return [ 'Loads from cache' => $this->loadsFromCache, @@ -305,8 +234,6 @@ public function loadFromArray(array $properties): void { $this->classes = $properties['classes']; $this->missingClasses = $properties['missingClasses']; - /* Avoid any refresh */ - $this->cacheLoaded = true; - $this->refreshed = true; + $this->loadsFromCache++; } } From d4f6b82f39b01ceb13505c90a4b5ee229a5d8127 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 17 Aug 2026 15:04:51 +0200 Subject: [PATCH 07/10] fix(autoloader): Remove unused mtime information for each file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/Autoloader.php | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/lib/private/Autoloader.php b/lib/private/Autoloader.php index 7eae116eabff6..43159b742929c 100644 --- a/lib/private/Autoloader.php +++ b/lib/private/Autoloader.php @@ -28,7 +28,7 @@ class Autoloader { /** @var string[] */ private array $excludeDirs = []; - /** @var array class => [file, time] */ + /** @var array class => file */ private array $classes = []; /** @var array class => counter */ @@ -61,7 +61,7 @@ public function tryLoad(string $type): void { return; } - [$file, $mtime] = $this->classes[$type] ?? null; + $file = $this->classes[$type] ?? null; if ($file) { (static function ($file) { @@ -101,12 +101,7 @@ public function excludeDirectory(string ...$paths): static { * @return array class => filename */ public function getIndexedClasses(): array { - $res = []; - foreach ($this->classes as $class => [$file]) { - $res[$class] = $file; - } - - return $res; + return $this->classes; } /** @@ -121,13 +116,6 @@ public function rebuild(): void { * Refreshes $this->classes. */ private function refreshClasses(): void { - $files = []; - $classes = []; - foreach ($this->classes as $class => [$file, $mtime]) { - $files[$file] = $mtime; - $classes[$file][] = $class; - } - $this->classes = []; foreach ($this->psr4Paths as $namespace => $path) { @@ -145,15 +133,12 @@ private function refreshClasses(): void { throw new \RuntimeException(sprintf( 'Ambiguous class %s resolution; defined in %s and in %s.', $class, - $this->classes[$class][0], + $this->classes[$class], $file, )); } - //FIXME needed? - $mtime = filemtime($file); - - $this->classes[$class] = [$file, $mtime]; + $this->classes[$class] = $file; unset($this->missingClasses[$class]); } } From ce25eb4b5c0e2c034abb589b793bcf5cf4b0ab5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Mon, 17 Aug 2026 15:05:25 +0200 Subject: [PATCH 08/10] chore: Remove windows-specific code and use faster hash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/PhpDumpCache.php | 37 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/lib/private/PhpDumpCache.php b/lib/private/PhpDumpCache.php index dfa1215559935..d44f82cc3fbe7 100644 --- a/lib/private/PhpDumpCache.php +++ b/lib/private/PhpDumpCache.php @@ -4,13 +4,14 @@ /** * SPDX-FileCopyrightText: 2004 David Grudl (https://davidgrudl.com) - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: BSD-3-Clause */ namespace OC; // TODO: cleanup? TTL? + class PhpDumpCache { public function __construct( @@ -36,38 +37,24 @@ public function setTempDirectory(string $dir): static { public function loadCache(array $cacheKey): ?array { $file = $this->generateCacheFileName($cacheKey); - // Solving atomicity to work everywhere - // 1) We want to do as little as possible IO calls on production and also directory and file can be not writable (#19) - // so on Linux we include the file directly without shared lock, therefore, the file must be created atomically by renaming. - // 2) On Windows file cannot be renamed-to while is open (ie by include() #11), so we have to acquire a lock. - $lock = defined('PHP_WINDOWS_VERSION_BUILD') - ? $this->acquireLock("$file.lock", LOCK_SH) - : null; - - try { - $data = @include $file; // @ file may not exist - if (is_array($data)) { - return $data; - } - - return null; - } finally { - if ($lock) { - flock($lock, LOCK_UN); // release shared lock - } + $data = @include $file; // @ file may not exist + if (is_array($data)) { + return $data; } + + return null; } /** * Writes class list to cache. * @param ?resource $lock */ - public function saveCache(array $cacheKey, array $data, $lock = null): void { + public function saveCache(array $cacheKey, array $data): void { // we have to acquire a lock to be able safely rename file // on Linux: that another thread does not rename the same named file earlier // on Windows: that the file is not read by another thread $file = $this->generateCacheFileName($cacheKey); - $lock = $lock ?: $this->acquireLock("$file.lock", LOCK_EX); + $lock = $this->acquireLock("$file.lock", LOCK_EX); $code = "tempDirectory) { - throw new \LogicException('Set path to temporary directory using setTempDirectory().'); - } - - return $this->tempDirectory . '/' . md5(serialize($cacheKey)) . '.php'; + return $this->tempDirectory . '/' . hash('xxh3', serialize($cacheKey)) . '.php'; } } From 8a1b67d65a5a2337f9808c09282193ce52875973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Tue, 18 Aug 2026 12:36:41 +0200 Subject: [PATCH 09/10] fix: Fix Autoloading of test classes with new autoloader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- lib/private/App/AppManager.php | 13 ++++++++++--- tests/autoload.php | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 711e30720c215..9743625f77b71 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -613,6 +613,13 @@ public function registerAppsAutoloading(array $apps): void { // FIXME needed? require_once $path . '/composer/autoload.php'; } + // Register Test namespace only when testing + if ((defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) && is_dir($path . '/tests')) { + \OC::$autoloader->addPsr4($appNamespace . '\\Tests', $path . '/tests'); + } + } + if ((defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN'))) { + \OC::$autoloader->rebuild(); } $this->eventLogger->end('bootstrap:register_apps_autoloading:apply_cache'); $this->eventLogger->end('bootstrap:register_apps_autoloading'); @@ -662,11 +669,11 @@ public function registerAutoloading(string $app, string $path, bool $force = fal } // Register Test namespace only when testing - if ((defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) && is_dir($path . '/tests/')) { - \OC::$autoloader->addPsr4($appNamespace . '\\Tests\\', $path . '/tests/'); + if ((defined('PHPUNIT_RUN') || defined('CLI_TEST_RUN')) && is_dir($path . '/tests')) { + \OC::$autoloader->addPsr4($appNamespace . '\\Tests', $path . '/tests'); } if ($force) { - \OC::$autoloader->triggerReload(); + \OC::$autoloader->rebuild(); } } diff --git a/tests/autoload.php b/tests/autoload.php index c25319e45f381..3d2f179f40a31 100644 --- a/tests/autoload.php +++ b/tests/autoload.php @@ -13,5 +13,5 @@ * This is a file that applications can require to be able to autoload the class Test\TestCase from Nextcloud tests */ -\OC::$autoloader->addPsr4('Test\\', OC::$SERVERROOT . '/tests/lib/'); -\OC::$autoloader->triggerReload(); +\OC::$autoloader->addPsr4('Test', OC::$SERVERROOT . '/tests/lib'); +\OC::$autoloader->rebuild(); From 5dfb3ab069cf840942d8d019d1873afcea2edc88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Chilliet?= Date: Thu, 16 Jul 2026 12:01:44 +0200 Subject: [PATCH 10/10] perf: Delete composer autoloader for shipped apps to rely on the new one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Côme Chilliet --- apps/admin_audit/composer/autoload.php | 22 ------------------- apps/appstore/composer/autoload.php | 22 ------------------- .../composer/autoload.php | 22 ------------------- apps/comments/composer/autoload.php | 22 ------------------- .../contactsinteraction/composer/autoload.php | 22 ------------------- apps/dashboard/composer/autoload.php | 22 ------------------- apps/dav/composer/autoload.php | 22 ------------------- apps/encryption/composer/autoload.php | 22 ------------------- .../composer/autoload.php | 22 ------------------- apps/federation/composer/autoload.php | 22 ------------------- apps/files/composer/autoload.php | 22 ------------------- apps/files_external/composer/autoload.php | 22 ------------------- apps/files_reminders/composer/autoload.php | 22 ------------------- apps/files_sharing/composer/autoload.php | 22 ------------------- apps/files_trashbin/composer/autoload.php | 22 ------------------- apps/files_versions/composer/autoload.php | 22 ------------------- .../composer/autoload.php | 22 ------------------- apps/oauth2/composer/autoload.php | 22 ------------------- apps/profile/composer/autoload.php | 22 ------------------- apps/provisioning_api/composer/autoload.php | 22 ------------------- apps/settings/composer/autoload.php | 22 ------------------- apps/sharebymail/composer/autoload.php | 22 ------------------- apps/systemtags/composer/autoload.php | 22 ------------------- apps/testing/composer/autoload.php | 22 ------------------- apps/theming/composer/autoload.php | 22 ------------------- .../composer/autoload.php | 22 ------------------- apps/updatenotification/composer/autoload.php | 22 ------------------- apps/user_ldap/composer/autoload.php | 22 ------------------- apps/user_status/composer/autoload.php | 22 ------------------- apps/weather_status/composer/autoload.php | 22 ------------------- apps/webhook_listeners/composer/autoload.php | 22 ------------------- apps/workflowengine/composer/autoload.php | 22 ------------------- 32 files changed, 704 deletions(-) delete mode 100644 apps/admin_audit/composer/autoload.php delete mode 100644 apps/appstore/composer/autoload.php delete mode 100644 apps/cloud_federation_api/composer/autoload.php delete mode 100644 apps/comments/composer/autoload.php delete mode 100644 apps/contactsinteraction/composer/autoload.php delete mode 100644 apps/dashboard/composer/autoload.php delete mode 100644 apps/dav/composer/autoload.php delete mode 100644 apps/encryption/composer/autoload.php delete mode 100644 apps/federatedfilesharing/composer/autoload.php delete mode 100644 apps/federation/composer/autoload.php delete mode 100644 apps/files/composer/autoload.php delete mode 100644 apps/files_external/composer/autoload.php delete mode 100644 apps/files_reminders/composer/autoload.php delete mode 100644 apps/files_sharing/composer/autoload.php delete mode 100644 apps/files_trashbin/composer/autoload.php delete mode 100644 apps/files_versions/composer/autoload.php delete mode 100644 apps/lookup_server_connector/composer/autoload.php delete mode 100644 apps/oauth2/composer/autoload.php delete mode 100644 apps/profile/composer/autoload.php delete mode 100644 apps/provisioning_api/composer/autoload.php delete mode 100644 apps/settings/composer/autoload.php delete mode 100644 apps/sharebymail/composer/autoload.php delete mode 100644 apps/systemtags/composer/autoload.php delete mode 100644 apps/testing/composer/autoload.php delete mode 100644 apps/theming/composer/autoload.php delete mode 100644 apps/twofactor_backupcodes/composer/autoload.php delete mode 100644 apps/updatenotification/composer/autoload.php delete mode 100644 apps/user_ldap/composer/autoload.php delete mode 100644 apps/user_status/composer/autoload.php delete mode 100644 apps/weather_status/composer/autoload.php delete mode 100644 apps/webhook_listeners/composer/autoload.php delete mode 100644 apps/workflowengine/composer/autoload.php diff --git a/apps/admin_audit/composer/autoload.php b/apps/admin_audit/composer/autoload.php deleted file mode 100644 index 7480b576ba0e7..0000000000000 --- a/apps/admin_audit/composer/autoload.php +++ /dev/null @@ -1,22 +0,0 @@ -