aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/AppFramework
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/AppFramework')
-rw-r--r--lib/private/AppFramework/App.php4
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php23
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php2
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php245
-rw-r--r--lib/private/AppFramework/Http.php4
-rw-r--r--lib/private/AppFramework/Http/Dispatcher.php6
-rw-r--r--lib/private/AppFramework/Http/Output.php11
-rw-r--r--lib/private/AppFramework/Http/Request.php22
-rw-r--r--lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php4
-rw-r--r--lib/private/AppFramework/Middleware/NotModifiedMiddleware.php2
-rw-r--r--lib/private/AppFramework/Middleware/OCSMiddleware.php1
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php1
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/CORSMiddleware.php12
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php5
-rw-r--r--lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php4
-rw-r--r--lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php17
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php4
-rw-r--r--lib/private/AppFramework/OCS/BaseResponse.php7
-rw-r--r--lib/private/AppFramework/OCS/V1Response.php1
-rw-r--r--lib/private/AppFramework/OCS/V2Response.php1
-rw-r--r--lib/private/AppFramework/Routing/RouteConfig.php279
-rw-r--r--lib/private/AppFramework/Routing/RouteParser.php4
-rw-r--r--lib/private/AppFramework/Services/AppConfig.php4
-rw-r--r--lib/private/AppFramework/Utility/SimpleContainer.php65
27 files changed, 212 insertions, 519 deletions
diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php
index c5f61d7e938..e719ea19f90 100644
--- a/lib/private/AppFramework/App.php
+++ b/lib/private/AppFramework/App.php
@@ -101,7 +101,7 @@ class App {
$profiler->setEnabled($profiler->isEnabled() && !is_null($urlParams) && isset($urlParams['_route']) && !str_starts_with($urlParams['_route'], 'profiler.'));
if ($profiler->isEnabled()) {
\OC::$server->get(IEventLogger::class)->activate();
- $profiler->add(new RoutingDataCollector($container['AppName'], $controllerName, $methodName));
+ $profiler->add(new RoutingDataCollector($container['appName'], $controllerName, $methodName));
}
$eventLogger->start('app:controller:params', 'Gather controller parameters');
@@ -115,7 +115,7 @@ class App {
$request = $container->get(IRequest::class);
$request->setUrlParameters($container['urlParams']);
}
- $appName = $container['AppName'];
+ $appName = $container['appName'];
$eventLogger->end('app:controller:params');
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index 2b04d291730..64e3dbfd928 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -20,6 +20,7 @@ use OCP\Dashboard\IManager;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IServerContainer;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use function class_exists;
@@ -69,26 +70,32 @@ class Coordinator {
*/
try {
$path = $this->appManager->getAppPath($appId);
+ OC_App::registerAutoloading($appId, $path);
} catch (AppPathNotFoundException) {
// Ignore
continue;
}
- OC_App::registerAutoloading($appId, $path);
$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
*/
- $appNameSpace = App::buildAppNamespace($appId);
+ if ($appId === 'core') {
+ $appNameSpace = 'OC\\Core';
+ } else {
+ $appNameSpace = App::buildAppNamespace($appId);
+ }
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
+
try {
- if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) {
+ if (class_exists($applicationClassName) && is_a($applicationClassName, IBootstrap::class, true)) {
$this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId");
try {
- /** @var IBootstrap|App $application */
- $apps[$appId] = $application = $this->serverContainer->query($applicationClassName);
- } catch (QueryException $e) {
+ /** @var IBootstrap&App $application */
+ $application = $this->serverContainer->query($applicationClassName);
+ $apps[$appId] = $application;
+ } catch (ContainerExceptionInterface $e) {
// Weird, but ok
$this->eventLogger->end("bootstrap:register_app:$appId");
continue;
@@ -171,7 +178,7 @@ class Coordinator {
public function isBootable(string $appId) {
$appNameSpace = App::buildAppNamespace($appId);
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
- return class_exists($applicationClassName) &&
- in_array(IBootstrap::class, class_implements($applicationClassName), true);
+ return class_exists($applicationClassName)
+ && in_array(IBootstrap::class, class_implements($applicationClassName), true);
}
}
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index c3b829825c2..95ad129c466 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -157,7 +157,7 @@ class RegistrationContext {
/** @var ServiceRegistration<\OCP\Files\Conversion\IConversionProvider>[] */
private array $fileConversionProviders = [];
-
+
/** @var ServiceRegistration<IMailProvider>[] */
private $mailProviders = [];
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index b6e2df4ce7b..5ccc1b7d348 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -1,20 +1,33 @@
<?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\AppFramework\DependencyInjection;
-use OC;
use OC\AppFramework\Http;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Output;
+use OC\AppFramework\Middleware\AdditionalScriptsMiddleware;
+use OC\AppFramework\Middleware\CompressionMiddleware;
use OC\AppFramework\Middleware\FlowV2EphemeralSessionsMiddleware;
use OC\AppFramework\Middleware\MiddlewareDispatcher;
+use OC\AppFramework\Middleware\NotModifiedMiddleware;
use OC\AppFramework\Middleware\OCSMiddleware;
+use OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware;
+use OC\AppFramework\Middleware\Security\BruteForceMiddleware;
use OC\AppFramework\Middleware\Security\CORSMiddleware;
+use OC\AppFramework\Middleware\Security\CSPMiddleware;
+use OC\AppFramework\Middleware\Security\FeaturePolicyMiddleware;
+use OC\AppFramework\Middleware\Security\PasswordConfirmationMiddleware;
use OC\AppFramework\Middleware\Security\RateLimitingMiddleware;
+use OC\AppFramework\Middleware\Security\ReloadExecutionMiddleware;
+use OC\AppFramework\Middleware\Security\SameSiteCookieMiddleware;
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\ScopedPsrLogger;
@@ -24,7 +37,6 @@ use OC\Diagnostics\EventLogger;
use OC\Log\PsrLoggerAdapter;
use OC\ServerContainer;
use OC\Settings\AuthorizedGroupMapper;
-use OC\User\Manager as UserManager;
use OCA\WorkflowEngine\Manager;
use OCP\AppFramework\Http\IOutput;
use OCP\AppFramework\IAppContainer;
@@ -32,14 +44,13 @@ use OCP\AppFramework\QueryException;
use OCP\AppFramework\Services\IAppConfig;
use OCP\AppFramework\Services\IInitialState;
use OCP\AppFramework\Utility\IControllerMethodReflector;
-use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Files\AppData\IAppDataFactory;
use OCP\Files\Folder;
use OCP\Files\IAppData;
use OCP\Group\ISubAdmin;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
-use OCP\IInitialStateService;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IRequest;
@@ -47,40 +58,24 @@ use OCP\IServerContainer;
use OCP\ISession;
use OCP\IURLGenerator;
use OCP\IUserSession;
-use OCP\Security\Bruteforce\IThrottler;
use OCP\Security\Ip\IRemoteAddress;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
-/**
- * @deprecated 20.0.0
- */
class DIContainer extends SimpleContainer implements IAppContainer {
private string $appName;
+ private array $middleWares = [];
+ private ServerContainer $server;
- /**
- * @var array
- */
- private $middleWares = [];
-
- /** @var ServerContainer */
- private $server;
-
- /**
- * Put your class dependencies in here
- * @param string $appName the name of the app
- * @param array $urlParams
- * @param ServerContainer|null $server
- */
public function __construct(string $appName, array $urlParams = [], ?ServerContainer $server = null) {
parent::__construct();
$this->appName = $appName;
- $this['appName'] = $appName;
- $this['urlParams'] = $urlParams;
+ $this->registerParameter('appName', $appName);
+ $this->registerParameter('urlParams', $urlParams);
- $this->registerAlias('Request', IRequest::class);
+ /** @deprecated 32.0.0 */
+ $this->registerDeprecatedAlias('Request', IRequest::class);
- /** @var \OC\ServerContainer $server */
if ($server === null) {
$server = \OC::$server;
}
@@ -89,74 +84,73 @@ class DIContainer extends SimpleContainer implements IAppContainer {
// aliases
/** @deprecated 26.0.0 inject $appName */
- $this->registerAlias('AppName', 'appName');
+ $this->registerDeprecatedAlias('AppName', 'appName');
/** @deprecated 26.0.0 inject $webRoot*/
- $this->registerAlias('WebRoot', 'webRoot');
+ $this->registerDeprecatedAlias('WebRoot', 'webRoot');
/** @deprecated 26.0.0 inject $userId */
- $this->registerAlias('UserId', 'userId');
+ $this->registerDeprecatedAlias('UserId', 'userId');
/**
* Core services
*/
- $this->registerService(IOutput::class, function () {
- return new Output($this->getServer()->getWebRoot());
- });
+ /* Cannot be an alias because Output is not in OCA */
+ $this->registerService(IOutput::class, fn (ContainerInterface $c): IOutput => new Output($c->get('webRoot')));
$this->registerService(Folder::class, function () {
return $this->getServer()->getUserFolder();
});
- $this->registerService(IAppData::class, function (ContainerInterface $c) {
- return $this->getServer()->getAppDataDir($c->get('AppName'));
+ $this->registerService(IAppData::class, function (ContainerInterface $c): IAppData {
+ return $c->get(IAppDataFactory::class)->get($c->get('appName'));
});
$this->registerService(IL10N::class, function (ContainerInterface $c) {
- return $this->getServer()->getL10N($c->get('AppName'));
+ return $this->getServer()->getL10N($c->get('appName'));
});
// Log wrappers
$this->registerService(LoggerInterface::class, function (ContainerInterface $c) {
+ /* Cannot be an alias because it uses LoggerInterface so it would infinite loop */
return new ScopedPsrLogger(
$c->get(PsrLoggerAdapter::class),
- $c->get('AppName')
+ $c->get('appName')
);
});
$this->registerService(IServerContainer::class, function () {
return $this->getServer();
});
- $this->registerAlias('ServerContainer', IServerContainer::class);
+ /** @deprecated 32.0.0 */
+ $this->registerDeprecatedAlias('ServerContainer', IServerContainer::class);
- $this->registerService(\OCP\WorkflowEngine\IManager::class, function (ContainerInterface $c) {
- return $c->get(Manager::class);
- });
+ $this->registerAlias(\OCP\WorkflowEngine\IManager::class, Manager::class);
- $this->registerService(ContainerInterface::class, function (ContainerInterface $c) {
- return $c;
- });
- $this->registerAlias(IAppContainer::class, ContainerInterface::class);
+ $this->registerService(ContainerInterface::class, fn (ContainerInterface $c) => $c);
+ $this->registerDeprecatedAlias(IAppContainer::class, ContainerInterface::class);
// commonly used attributes
- $this->registerService('userId', function (ContainerInterface $c) {
- return $c->get(IUserSession::class)->getSession()->get('user_id');
+ $this->registerService('userId', function (ContainerInterface $c): ?string {
+ return $c->get(ISession::class)->get('user_id');
});
- $this->registerService('webRoot', function (ContainerInterface $c) {
+ $this->registerService('webRoot', function (ContainerInterface $c): string {
return $c->get(IServerContainer::class)->getWebRoot();
});
- $this->registerService('OC_Defaults', function (ContainerInterface $c) {
+ $this->registerService('OC_Defaults', function (ContainerInterface $c): object {
return $c->get(IServerContainer::class)->get('ThemingDefaults');
});
- $this->registerService('Protocol', function (ContainerInterface $c) {
- /** @var \OC\Server $server */
- $server = $c->get(IServerContainer::class);
- $protocol = $server->getRequest()->getHttpProtocol();
+ /** @deprecated 32.0.0 */
+ $this->registerDeprecatedAlias('Protocol', Http::class);
+ $this->registerService(Http::class, function (ContainerInterface $c) {
+ $protocol = $c->get(IRequest::class)->getHttpProtocol();
return new Http($_SERVER, $protocol);
});
- $this->registerService('Dispatcher', function (ContainerInterface $c) {
+ /** @deprecated 32.0.0 */
+ $this->registerDeprecatedAlias('Dispatcher', Dispatcher::class);
+ $this->registerService(Dispatcher::class, function (ContainerInterface $c) {
return new Dispatcher(
$c->get('Protocol'),
$c->get(MiddlewareDispatcher::class),
@@ -180,42 +174,19 @@ class DIContainer extends SimpleContainer implements IAppContainer {
/**
* Middleware
*/
- $this->registerAlias('MiddlewareDispatcher', MiddlewareDispatcher::class);
+ /** @deprecated 32.0.0 */
+ $this->registerDeprecatedAlias('MiddlewareDispatcher', MiddlewareDispatcher::class);
$this->registerService(MiddlewareDispatcher::class, function (ContainerInterface $c) {
$server = $this->getServer();
$dispatcher = new MiddlewareDispatcher();
- $dispatcher->registerMiddleware(
- $c->get(OC\AppFramework\Middleware\CompressionMiddleware::class)
- );
-
- $dispatcher->registerMiddleware($c->get(OC\AppFramework\Middleware\NotModifiedMiddleware::class));
-
- $dispatcher->registerMiddleware(
- $c->get(OC\AppFramework\Middleware\Security\ReloadExecutionMiddleware::class)
- );
-
- $dispatcher->registerMiddleware(
- new OC\AppFramework\Middleware\Security\SameSiteCookieMiddleware(
- $c->get(IRequest::class),
- $c->get(IControllerMethodReflector::class)
- )
- );
- $dispatcher->registerMiddleware(
- new CORSMiddleware(
- $c->get(IRequest::class),
- $c->get(IControllerMethodReflector::class),
- $c->get(IUserSession::class),
- $c->get(IThrottler::class),
- $c->get(LoggerInterface::class)
- )
- );
- $dispatcher->registerMiddleware(
- new OCSMiddleware(
- $c->get(IRequest::class)
- )
- );
+ $dispatcher->registerMiddleware($c->get(CompressionMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(NotModifiedMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(ReloadExecutionMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(SameSiteCookieMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(CORSMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(OCSMiddleware::class));
$dispatcher->registerMiddleware($c->get(FlowV2EphemeralSessionsMiddleware::class));
@@ -224,75 +195,31 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$c->get(IControllerMethodReflector::class),
$c->get(INavigationManager::class),
$c->get(IURLGenerator::class),
- $server->get(LoggerInterface::class),
- $c->get('AppName'),
+ $c->get(LoggerInterface::class),
+ $c->get('appName'),
$server->getUserSession()->isLoggedIn(),
$c->get(IGroupManager::class),
$c->get(ISubAdmin::class),
$server->getAppManager(),
$server->getL10N('lib'),
$c->get(AuthorizedGroupMapper::class),
- $server->get(IUserSession::class),
+ $c->get(IUserSession::class),
$c->get(IRemoteAddress::class),
);
$dispatcher->registerMiddleware($securityMiddleware);
- $dispatcher->registerMiddleware(
- new OC\AppFramework\Middleware\Security\CSPMiddleware(
- $server->query(OC\Security\CSP\ContentSecurityPolicyManager::class),
- $server->query(OC\Security\CSP\ContentSecurityPolicyNonceManager::class),
- )
- );
- $dispatcher->registerMiddleware(
- $server->query(OC\AppFramework\Middleware\Security\FeaturePolicyMiddleware::class)
- );
- $dispatcher->registerMiddleware(
- new OC\AppFramework\Middleware\Security\PasswordConfirmationMiddleware(
- $c->get(IControllerMethodReflector::class),
- $c->get(ISession::class),
- $c->get(IUserSession::class),
- $c->get(ITimeFactory::class),
- $c->get(\OC\Authentication\Token\IProvider::class),
- $c->get(LoggerInterface::class),
- $c->get(IRequest::class),
- $c->get(UserManager::class),
- )
- );
- $dispatcher->registerMiddleware(
- new TwoFactorMiddleware(
- $c->get(OC\Authentication\TwoFactorAuth\Manager::class),
- $c->get(IUserSession::class),
- $c->get(ISession::class),
- $c->get(IURLGenerator::class),
- $c->get(IControllerMethodReflector::class),
- $c->get(IRequest::class)
- )
- );
- $dispatcher->registerMiddleware(
- new OC\AppFramework\Middleware\Security\BruteForceMiddleware(
- $c->get(IControllerMethodReflector::class),
- $c->get(IThrottler::class),
- $c->get(IRequest::class),
- $c->get(LoggerInterface::class)
- )
- );
+ $dispatcher->registerMiddleware($c->get(CSPMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(FeaturePolicyMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(PasswordConfirmationMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(TwoFactorMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(BruteForceMiddleware::class));
$dispatcher->registerMiddleware($c->get(RateLimitingMiddleware::class));
- $dispatcher->registerMiddleware(
- new OC\AppFramework\Middleware\PublicShare\PublicShareMiddleware(
- $c->get(IRequest::class),
- $c->get(ISession::class),
- $c->get(IConfig::class),
- $c->get(IThrottler::class)
- )
- );
- $dispatcher->registerMiddleware(
- $c->get(\OC\AppFramework\Middleware\AdditionalScriptsMiddleware::class)
- );
+ $dispatcher->registerMiddleware($c->get(PublicShareMiddleware::class));
+ $dispatcher->registerMiddleware($c->get(AdditionalScriptsMiddleware::class));
- /** @var \OC\AppFramework\Bootstrap\Coordinator $coordinator */
$coordinator = $c->get(\OC\AppFramework\Bootstrap\Coordinator::class);
$registrationContext = $coordinator->getRegistrationContext();
if ($registrationContext !== null) {
- $appId = $this->getAppName();
+ $appId = $this->get('appName');
foreach ($registrationContext->getMiddlewareRegistrations() as $middlewareRegistration) {
if ($middlewareRegistration->getAppId() === $appId
|| $middlewareRegistration->isGlobal()) {
@@ -304,28 +231,12 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$dispatcher->registerMiddleware($c->get($middleWare));
}
- $dispatcher->registerMiddleware(
- new SessionMiddleware(
- $c->get(IControllerMethodReflector::class),
- $c->get(ISession::class)
- )
- );
+ $dispatcher->registerMiddleware($c->get(SessionMiddleware::class));
return $dispatcher;
});
- $this->registerService(IAppConfig::class, function (ContainerInterface $c) {
- return new OC\AppFramework\Services\AppConfig(
- $c->get(IConfig::class),
- $c->get(\OCP\IAppConfig::class),
- $c->get('AppName')
- );
- });
- $this->registerService(IInitialState::class, function (ContainerInterface $c) {
- return new OC\AppFramework\Services\InitialState(
- $c->get(IInitialStateService::class),
- $c->get('AppName')
- );
- });
+ $this->registerAlias(IAppConfig::class, \OC\AppFramework\Services\AppConfig::class);
+ $this->registerAlias(IInitialState::class, \OC\AppFramework\Services\InitialState::class);
}
/**
@@ -337,13 +248,13 @@ class DIContainer extends SimpleContainer implements IAppContainer {
/**
* @param string $middleWare
- * @return boolean|null
*/
- public function registerMiddleWare($middleWare) {
+ public function registerMiddleWare($middleWare): bool {
if (in_array($middleWare, $this->middleWares, true) !== false) {
return false;
}
$this->middleWares[] = $middleWare;
+ return true;
}
/**
@@ -351,7 +262,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
* @return string the name of your application
*/
public function getAppName() {
- return $this->query('AppName');
+ return $this->query('appName');
}
/**
@@ -371,7 +282,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return \OC_User::isAdminUser($uid);
}
- private function getUserId() {
+ private function getUserId(): string {
return $this->getServer()->getSession()->get('user_id');
}
@@ -438,9 +349,15 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return parent::query($name);
} elseif (str_starts_with($name, \OC\AppFramework\App::buildAppNamespace($this->appName) . '\\')) {
return parent::query($name);
+ } elseif (
+ str_starts_with($name, 'OC\\AppFramework\\Services\\')
+ || str_starts_with($name, 'OC\\AppFramework\\Middleware\\')
+ ) {
+ /* AppFramework services are scoped to the application */
+ return parent::query($name);
}
- throw new QueryException('Could not resolve ' . $name . '!' .
- ' Class can not be instantiated', 1);
+ throw new QueryException('Could not resolve ' . $name . '!'
+ . ' Class can not be instantiated', 1);
}
}
diff --git a/lib/private/AppFramework/Http.php b/lib/private/AppFramework/Http.php
index 3c3e19692bf..08d6259c2a2 100644
--- a/lib/private/AppFramework/Http.php
+++ b/lib/private/AppFramework/Http.php
@@ -102,7 +102,7 @@ class Http extends BaseHttp {
$status = self::STATUS_FOUND;
}
- return $this->protocolVersion . ' ' . $status . ' ' .
- $this->headers[$status];
+ return $this->protocolVersion . ' ' . $status . ' '
+ . $this->headers[$status];
}
}
diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php
index d129a7d770b..8d91ddf7502 100644
--- a/lib/private/AppFramework/Http/Dispatcher.php
+++ b/lib/private/AppFramework/Http/Dispatcher.php
@@ -64,7 +64,8 @@ class Dispatcher {
* @param LoggerInterface $logger
* @param IEventLogger $eventLogger
*/
- public function __construct(Http $protocol,
+ public function __construct(
+ Http $protocol,
MiddlewareDispatcher $middlewareDispatcher,
ControllerMethodReflector $reflector,
IRequest $request,
@@ -72,7 +73,8 @@ class Dispatcher {
ConnectionAdapter $connection,
LoggerInterface $logger,
IEventLogger $eventLogger,
- ContainerInterface $appContainer) {
+ ContainerInterface $appContainer,
+ ) {
$this->protocol = $protocol;
$this->middlewareDispatcher = $middlewareDispatcher;
$this->reflector = $reflector;
diff --git a/lib/private/AppFramework/Http/Output.php b/lib/private/AppFramework/Http/Output.php
index ff0ef1c15ca..b4a8672fdc7 100644
--- a/lib/private/AppFramework/Http/Output.php
+++ b/lib/private/AppFramework/Http/Output.php
@@ -13,14 +13,9 @@ use OCP\AppFramework\Http\IOutput;
* Very thin wrapper class to make output testable
*/
class Output implements IOutput {
- /** @var string */
- private $webRoot;
-
- /**
- * @param $webRoot
- */
- public function __construct($webRoot) {
- $this->webRoot = $webRoot;
+ public function __construct(
+ private string $webRoot,
+ ) {
}
/**
diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php
index d177221556c..e662cb8679a 100644
--- a/lib/private/AppFramework/Http/Request.php
+++ b/lib/private/AppFramework/Http/Request.php
@@ -45,7 +45,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
protected string $inputStream;
- protected $content;
+ private bool $isPutStreamContentAlreadySent = false;
protected array $items = [];
protected array $allowedKeys = [
'get',
@@ -64,6 +64,7 @@ class Request implements \ArrayAccess, \Countable, IRequest {
protected ?CsrfTokenManager $csrfTokenManager;
protected bool $contentDecoded = false;
+ private ?\JsonException $decodingException = null;
/**
* @param array $vars An associative array with the following optional values:
@@ -356,13 +357,13 @@ class Request implements \ArrayAccess, \Countable, IRequest {
protected function getContent() {
// If the content can't be parsed into an array then return a stream resource.
if ($this->isPutStreamContent()) {
- if ($this->content === false) {
+ if ($this->isPutStreamContentAlreadySent) {
throw new \LogicException(
'"put" can only be accessed once if not '
. 'application/x-www-form-urlencoded or application/json.'
);
}
- $this->content = false;
+ $this->isPutStreamContentAlreadySent = true;
return fopen($this->inputStream, 'rb');
} else {
$this->decodeContent();
@@ -389,7 +390,14 @@ class Request implements \ArrayAccess, \Countable, IRequest {
// 'application/json' and other JSON-related content types must be decoded manually.
if (preg_match(self::JSON_CONTENT_TYPE_REGEX, $this->getHeader('Content-Type')) === 1) {
- $params = json_decode(file_get_contents($this->inputStream), true);
+ $content = file_get_contents($this->inputStream);
+ if ($content !== '') {
+ try {
+ $params = json_decode($content, true, flags:JSON_THROW_ON_ERROR);
+ } catch (\JsonException $e) {
+ $this->decodingException = $e;
+ }
+ }
if (\is_array($params) && \count($params) > 0) {
$this->items['params'] = $params;
if ($this->method === 'POST') {
@@ -413,6 +421,12 @@ class Request implements \ArrayAccess, \Countable, IRequest {
$this->contentDecoded = true;
}
+ public function throwDecodingExceptionIfAny(): void {
+ if ($this->decodingException !== null) {
+ throw $this->decodingException;
+ }
+ }
+
/**
* Checks if the CSRF check was correct
diff --git a/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php b/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php
index c30855a0e98..e4571dfc50e 100644
--- a/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php
+++ b/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php
@@ -33,8 +33,8 @@ class FlowV2EphemeralSessionsMiddleware extends Middleware {
}
if (
- $controller instanceof ClientFlowLoginV2Controller &&
- ($methodName === 'grantPage' || $methodName === 'generateAppPassword')
+ $controller instanceof ClientFlowLoginV2Controller
+ && ($methodName === 'grantPage' || $methodName === 'generateAppPassword')
) {
return;
}
diff --git a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
index 17b423164f6..08b30092155 100644
--- a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
+++ b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
@@ -29,7 +29,7 @@ class NotModifiedMiddleware extends Middleware {
}
$modifiedSinceHeader = $this->request->getHeader('IF_MODIFIED_SINCE');
- if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC2822)) {
+ if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC7231)) {
$response->setStatus(Http::STATUS_NOT_MODIFIED);
return $response;
}
diff --git a/lib/private/AppFramework/Middleware/OCSMiddleware.php b/lib/private/AppFramework/Middleware/OCSMiddleware.php
index 46612bf0d29..64f4b0054de 100644
--- a/lib/private/AppFramework/Middleware/OCSMiddleware.php
+++ b/lib/private/AppFramework/Middleware/OCSMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
index c80d06c90ae..5df4009b094 100644
--- a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
+++ b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
index b3040673d0f..83e799e3d3b 100644
--- a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
+++ b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
index 40af67739d6..4453f5a7d4b 100644
--- a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
@@ -68,8 +68,8 @@ class CORSMiddleware extends Middleware {
// ensure that @CORS annotated API routes are not used in conjunction
// with session authentication since this enables CSRF attack vectors
- if ($this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class) &&
- (!$this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class) || $this->session->isLoggedIn())) {
+ if ($this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class)
+ && (!$this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class) || $this->session->isLoggedIn())) {
$user = array_key_exists('PHP_AUTH_USER', $this->request->server) ? $this->request->server['PHP_AUTH_USER'] : null;
$pass = array_key_exists('PHP_AUTH_PW', $this->request->server) ? $this->request->server['PHP_AUTH_PW'] : null;
@@ -134,10 +134,10 @@ class CORSMiddleware extends Middleware {
// allow credentials headers must not be true or CSRF is possible
// otherwise
foreach ($response->getHeaders() as $header => $value) {
- if (strtolower($header) === 'access-control-allow-credentials' &&
- strtolower(trim($value)) === 'true') {
- $msg = 'Access-Control-Allow-Credentials must not be ' .
- 'set to true in order to prevent CSRF';
+ if (strtolower($header) === 'access-control-allow-credentials'
+ && strtolower(trim($value)) === 'true') {
+ $msg = 'Access-Control-Allow-Credentials must not be '
+ . 'set to true in order to prevent CSRF';
throw new SecurityException($msg);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php
index 646a5240bfc..53fbaaf5ed2 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php
index 91f1dba874d..0380c6781aa 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
index 7e950f2c976..ca30f736fbc 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -14,7 +15,7 @@ use OCP\AppFramework\Http;
* @package OC\AppFramework\Middleware\Security\Exceptions
*/
class NotConfirmedException extends SecurityException {
- public function __construct() {
- parent::__construct('Password confirmation is required', Http::STATUS_FORBIDDEN);
+ public function __construct(string $message = 'Password confirmation is required') {
+ parent::__construct($message, Http::STATUS_FORBIDDEN);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
index d00840084a3..0facbffe504 100644
--- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -79,6 +80,9 @@ class PasswordConfirmationMiddleware extends Middleware {
if ($this->isPasswordConfirmationStrict($reflectionMethod)) {
$authHeader = $this->request->getHeader('Authorization');
+ if (!str_starts_with(strtolower($authHeader), 'basic ')) {
+ throw new NotConfirmedException('Required authorization header missing');
+ }
[, $password] = explode(':', base64_decode(substr($authHeader, 6)), 2);
$loginName = $this->session->get('loginname');
$loginResult = $this->userManager->checkPassword($loginName, $password);
diff --git a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
index efe56e0b124..097ed1b2b8f 100644
--- a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -13,16 +14,10 @@ use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Middleware;
class SameSiteCookieMiddleware extends Middleware {
- /** @var Request */
- private $request;
-
- /** @var ControllerMethodReflector */
- private $reflector;
-
- public function __construct(Request $request,
- ControllerMethodReflector $reflector) {
- $this->request = $request;
- $this->reflector = $reflector;
+ public function __construct(
+ private Request $request,
+ private ControllerMethodReflector $reflector,
+ ) {
}
public function beforeController($controller, $methodName) {
@@ -58,7 +53,7 @@ class SameSiteCookieMiddleware extends Middleware {
throw $exception;
}
- protected function setSameSiteCookie() {
+ protected function setSameSiteCookie(): void {
$cookieParams = $this->request->getCookieParams();
$secureCookie = ($cookieParams['secure'] === true) ? 'secure; ' : '';
$policies = [
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index 6b054b03f44..e3a293e0fd9 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -184,8 +184,8 @@ class SecurityMiddleware extends Middleware {
}
// Check for strict cookie requirement
- if ($this->hasAnnotationOrAttribute($reflectionMethod, 'StrictCookieRequired', StrictCookiesRequired::class) ||
- !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
+ if ($this->hasAnnotationOrAttribute($reflectionMethod, 'StrictCookieRequired', StrictCookiesRequired::class)
+ || !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
if (!$this->request->passesStrictCookieCheck()) {
throw new StrictCookieMissingException();
}
diff --git a/lib/private/AppFramework/OCS/BaseResponse.php b/lib/private/AppFramework/OCS/BaseResponse.php
index 5929a3993ec..05ce133db24 100644
--- a/lib/private/AppFramework/OCS/BaseResponse.php
+++ b/lib/private/AppFramework/OCS/BaseResponse.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -83,9 +84,9 @@ abstract class BaseResponse extends Response {
*/
protected function renderResult(array $meta): string {
$status = $this->getStatus();
- if ($status === Http::STATUS_NO_CONTENT ||
- $status === Http::STATUS_NOT_MODIFIED ||
- ($status >= 100 && $status <= 199)) {
+ if ($status === Http::STATUS_NO_CONTENT
+ || $status === Http::STATUS_NOT_MODIFIED
+ || ($status >= 100 && $status <= 199)) {
// Those status codes are not supposed to have a body:
// https://stackoverflow.com/q/8628725
return '';
diff --git a/lib/private/AppFramework/OCS/V1Response.php b/lib/private/AppFramework/OCS/V1Response.php
index 131ca22ff24..1c2c25f5cb0 100644
--- a/lib/private/AppFramework/OCS/V1Response.php
+++ b/lib/private/AppFramework/OCS/V1Response.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/OCS/V2Response.php b/lib/private/AppFramework/OCS/V2Response.php
index 47cf0f60200..efc9348eb37 100644
--- a/lib/private/AppFramework/OCS/V2Response.php
+++ b/lib/private/AppFramework/OCS/V2Response.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Routing/RouteConfig.php b/lib/private/AppFramework/Routing/RouteConfig.php
deleted file mode 100644
index 2b7f21a8ba5..00000000000
--- a/lib/private/AppFramework/Routing/RouteConfig.php
+++ /dev/null
@@ -1,279 +0,0 @@
-<?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\AppFramework\Routing;
-
-use OC\AppFramework\DependencyInjection\DIContainer;
-use OC\Route\Router;
-
-/**
- * Class RouteConfig
- * @package OC\AppFramework\routing
- */
-class RouteConfig {
- /** @var DIContainer */
- private $container;
-
- /** @var Router */
- private $router;
-
- /** @var array */
- private $routes;
-
- /** @var string */
- private $appName;
-
- /** @var string[] */
- private $controllerNameCache = [];
-
- protected $rootUrlApps = [
- 'cloud_federation_api',
- 'core',
- 'files_sharing',
- 'files',
- 'profile',
- 'settings',
- 'spreed',
- ];
-
- /**
- * @param \OC\AppFramework\DependencyInjection\DIContainer $container
- * @param \OC\Route\Router $router
- * @param array $routes
- * @internal param $appName
- */
- public function __construct(DIContainer $container, Router $router, $routes) {
- $this->routes = $routes;
- $this->container = $container;
- $this->router = $router;
- $this->appName = $container['AppName'];
- }
-
- /**
- * The routes and resource will be registered to the \OCP\Route\IRouter
- */
- public function register() {
- // parse simple
- $this->processIndexRoutes($this->routes);
-
- // parse resources
- $this->processIndexResources($this->routes);
-
- /*
- * OCS routes go into a different collection
- */
- $oldCollection = $this->router->getCurrentCollection();
- $this->router->useCollection($oldCollection . '.ocs');
-
- // parse ocs simple routes
- $this->processOCS($this->routes);
-
- // parse ocs simple routes
- $this->processOCSResources($this->routes);
-
- $this->router->useCollection($oldCollection);
- }
-
- private function processOCS(array $routes): void {
- $ocsRoutes = $routes['ocs'] ?? [];
- foreach ($ocsRoutes as $ocsRoute) {
- $this->processRoute($ocsRoute, 'ocs.');
- }
- }
-
- /**
- * Creates one route base on the give configuration
- * @param array $routes
- * @throws \UnexpectedValueException
- */
- private function processIndexRoutes(array $routes): void {
- $simpleRoutes = $routes['routes'] ?? [];
- foreach ($simpleRoutes as $simpleRoute) {
- $this->processRoute($simpleRoute);
- }
- }
-
- protected function processRoute(array $route, string $routeNamePrefix = ''): void {
- $name = $route['name'];
- $postfix = $route['postfix'] ?? '';
- $root = $this->buildRootPrefix($route, $routeNamePrefix);
-
- $url = $root . '/' . ltrim($route['url'], '/');
- $verb = strtoupper($route['verb'] ?? 'GET');
-
- $split = explode('#', $name, 2);
- if (count($split) !== 2) {
- throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
- }
- [$controller, $action] = $split;
-
- $controllerName = $this->buildControllerName($controller);
- $actionName = $this->buildActionName($action);
-
- /*
- * The route name has to be lowercase, for symfony to match it correctly.
- * This is required because smyfony allows mixed casing for controller names in the routes.
- * To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
- * This is also safe on the PHP side because class and method names collide regardless of the casing.
- */
- $routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix);
-
- $router = $this->router->create($routeName, $url)
- ->method($verb);
-
- // optionally register requirements for route. This is used to
- // tell the route parser how url parameters should be matched
- if (array_key_exists('requirements', $route)) {
- $router->requirements($route['requirements']);
- }
-
- // optionally register defaults for route. This is used to
- // tell the route parser how url parameters should be default valued
- $defaults = [];
- if (array_key_exists('defaults', $route)) {
- $defaults = $route['defaults'];
- }
-
- $defaults['caller'] = [$this->appName, $controllerName, $actionName];
- $router->defaults($defaults);
- }
-
- /**
- * For a given name and url restful OCS routes are created:
- * - index
- * - show
- * - create
- * - update
- * - destroy
- *
- * @param array $routes
- */
- private function processOCSResources(array $routes): void {
- $this->processResources($routes['ocs-resources'] ?? [], 'ocs.');
- }
-
- /**
- * For a given name and url restful routes are created:
- * - index
- * - show
- * - create
- * - update
- * - destroy
- *
- * @param array $routes
- */
- private function processIndexResources(array $routes): void {
- $this->processResources($routes['resources'] ?? []);
- }
-
- /**
- * For a given name and url restful routes are created:
- * - index
- * - show
- * - create
- * - update
- * - destroy
- *
- * @param array $resources
- * @param string $routeNamePrefix
- */
- protected function processResources(array $resources, string $routeNamePrefix = ''): void {
- // declaration of all restful actions
- $actions = [
- ['name' => 'index', 'verb' => 'GET', 'on-collection' => true],
- ['name' => 'show', 'verb' => 'GET'],
- ['name' => 'create', 'verb' => 'POST', 'on-collection' => true],
- ['name' => 'update', 'verb' => 'PUT'],
- ['name' => 'destroy', 'verb' => 'DELETE'],
- ];
-
- foreach ($resources as $resource => $config) {
- $root = $this->buildRootPrefix($config, $routeNamePrefix);
-
- // the url parameter used as id to the resource
- foreach ($actions as $action) {
- $url = $root . '/' . ltrim($config['url'], '/');
- $method = $action['name'];
-
- $verb = strtoupper($action['verb'] ?? 'GET');
- $collectionAction = $action['on-collection'] ?? false;
- if (!$collectionAction) {
- $url .= '/{id}';
- }
- if (isset($action['url-postfix'])) {
- $url .= '/' . $action['url-postfix'];
- }
-
- $controller = $resource;
-
- $controllerName = $this->buildControllerName($controller);
- $actionName = $this->buildActionName($method);
-
- $routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method;
-
- $route = $this->router->create($routeName, $url)
- ->method($verb);
-
- $route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]);
- }
- }
- }
-
- private function buildRootPrefix(array $route, string $routeNamePrefix): string {
- $defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName;
- $root = $route['root'] ?? $defaultRoot;
-
- if ($routeNamePrefix !== '') {
- // In OCS all apps are whitelisted
- return $root;
- }
-
- if (!\in_array($this->appName, $this->rootUrlApps, true)) {
- // Only allow root URLS for some apps
- return $defaultRoot;
- }
-
- return $root;
- }
-
- /**
- * Based on a given route name the controller name is generated
- * @param string $controller
- * @return string
- */
- private function buildControllerName(string $controller): string {
- if (!isset($this->controllerNameCache[$controller])) {
- $this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller';
- }
- return $this->controllerNameCache[$controller];
- }
-
- /**
- * Based on the action part of the route name the controller method name is generated
- * @param string $action
- * @return string
- */
- private function buildActionName(string $action): string {
- return $this->underScoreToCamelCase($action);
- }
-
- /**
- * Underscored strings are converted to camel case strings
- * @param string $str
- * @return string
- */
- private function underScoreToCamelCase(string $str): string {
- $pattern = '/_[a-z]?/';
- return preg_replace_callback(
- $pattern,
- function ($matches) {
- return strtoupper(ltrim($matches[0], '_'));
- },
- $str);
- }
-}
diff --git a/lib/private/AppFramework/Routing/RouteParser.php b/lib/private/AppFramework/Routing/RouteParser.php
index 894a74c727b..55e58234673 100644
--- a/lib/private/AppFramework/Routing/RouteParser.php
+++ b/lib/private/AppFramework/Routing/RouteParser.php
@@ -76,7 +76,7 @@ class RouteParser {
$url = $root . '/' . ltrim($route['url'], '/');
$verb = strtoupper($route['verb'] ?? 'GET');
- $split = explode('#', $name, 2);
+ $split = explode('#', $name, 3);
if (count($split) !== 2) {
throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar');
}
@@ -87,7 +87,7 @@ class RouteParser {
/*
* The route name has to be lowercase, for symfony to match it correctly.
- * This is required because smyfony allows mixed casing for controller names in the routes.
+ * This is required because symfony allows mixed casing for controller names in the routes.
* To avoid breaking all the existing route names, registering and matching will only use the lowercase names.
* This is also safe on the PHP side because class and method names collide regardless of the casing.
*/
diff --git a/lib/private/AppFramework/Services/AppConfig.php b/lib/private/AppFramework/Services/AppConfig.php
index 77c5ea4de0c..04d97738483 100644
--- a/lib/private/AppFramework/Services/AppConfig.php
+++ b/lib/private/AppFramework/Services/AppConfig.php
@@ -343,7 +343,7 @@ class AppConfig implements IAppConfig {
*
* @return array<string, string>
*/
- public function getAppInstalledVersions(): array {
- return $this->appConfig->getAppInstalledVersions();
+ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
+ return $this->appConfig->getAppInstalledVersions($onlyEnabled);
}
}
diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php
index 24918992ea3..ed26e75ec89 100644
--- a/lib/private/AppFramework/Utility/SimpleContainer.php
+++ b/lib/private/AppFramework/Utility/SimpleContainer.php
@@ -12,7 +12,9 @@ use Closure;
use OCP\AppFramework\QueryException;
use OCP\IContainer;
use Pimple\Container;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
+use Psr\Log\LoggerInterface;
use ReflectionClass;
use ReflectionException;
use ReflectionNamedType;
@@ -23,8 +25,9 @@ use function class_exists;
* SimpleContainer is a simple implementation of a container on basis of Pimple
*/
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
- /** @var Container */
- private $container;
+ public static bool $useLazyObjects = false;
+
+ private Container $container;
public function __construct() {
$this->container = new Container();
@@ -49,16 +52,29 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
/**
* @param ReflectionClass $class the class to instantiate
- * @return \stdClass the created class
+ * @return object the created class
* @suppress PhanUndeclaredClassInstanceof
*/
- private function buildClass(ReflectionClass $class) {
+ private function buildClass(ReflectionClass $class): object {
$constructor = $class->getConstructor();
if ($constructor === null) {
+ /* No constructor, return a instance directly */
return $class->newInstance();
}
+ if (PHP_VERSION_ID >= 80400 && self::$useLazyObjects) {
+ /* For PHP>=8.4, use a lazy ghost to delay constructor and dependency resolving */
+ /** @psalm-suppress UndefinedMethod */
+ return $class->newLazyGhost(function (object $object) use ($constructor): void {
+ /** @psalm-suppress DirectConstructorCall For lazy ghosts we have to call the constructor directly */
+ $object->__construct(...$this->buildClassConstructorParameters($constructor));
+ });
+ } else {
+ return $class->newInstanceArgs($this->buildClassConstructorParameters($constructor));
+ }
+ }
- return $class->newInstanceArgs(array_map(function (ReflectionParameter $parameter) {
+ private function buildClassConstructorParameters(\ReflectionMethod $constructor): array {
+ return array_map(function (ReflectionParameter $parameter) {
$parameterType = $parameter->getType();
$resolveName = $parameter->getName();
@@ -69,10 +85,10 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
}
try {
- $builtIn = $parameter->hasType() && ($parameter->getType() instanceof ReflectionNamedType)
- && $parameter->getType()->isBuiltin();
+ $builtIn = $parameterType !== null && ($parameterType instanceof ReflectionNamedType)
+ && $parameterType->isBuiltin();
return $this->query($resolveName, !$builtIn);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
// Service not found, use the default value when available
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
@@ -82,7 +98,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
$resolveName = $parameter->getName();
try {
return $this->query($resolveName);
- } catch (QueryException $e2) {
+ } catch (ContainerExceptionInterface $e2) {
// Pass null if typed and nullable
if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
return null;
@@ -95,7 +111,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
throw $e;
}
- }, $constructor->getParameters()));
+ }, $constructor->getParameters());
}
public function resolve($name) {
@@ -105,8 +121,8 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
if ($class->isInstantiable()) {
return $this->buildClass($class);
} else {
- throw new QueryException($baseMsg .
- ' Class can not be instantiated');
+ throw new QueryException($baseMsg
+ . ' Class can not be instantiated');
}
} catch (ReflectionException $e) {
// Class does not exist
@@ -153,13 +169,13 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
return $closure($this);
};
$name = $this->sanitizeName($name);
- if (isset($this[$name])) {
- unset($this[$name]);
+ if (isset($this->container[$name])) {
+ unset($this->container[$name]);
}
if ($shared) {
- $this[$name] = $wrapped;
+ $this->container[$name] = $wrapped;
} else {
- $this[$name] = $this->container->factory($wrapped);
+ $this->container[$name] = $this->container->factory($wrapped);
}
}
@@ -170,8 +186,21 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
* @param string $alias the alias that should be registered
* @param string $target the target that should be resolved instead
*/
- public function registerAlias($alias, $target) {
- $this->registerService($alias, function (ContainerInterface $container) use ($target) {
+ public function registerAlias($alias, $target): void {
+ $this->registerService($alias, function (ContainerInterface $container) use ($target): mixed {
+ return $container->get($target);
+ }, false);
+ }
+
+ protected function registerDeprecatedAlias(string $alias, string $target): void {
+ $this->registerService($alias, function (ContainerInterface $container) use ($target, $alias): mixed {
+ try {
+ $logger = $container->get(LoggerInterface::class);
+ $logger->debug('The requested alias "' . $alias . '" is deprecated. Please request "' . $target . '" directly. This alias will be removed in a future Nextcloud version.', ['app' => 'serverDI']);
+ } catch (ContainerExceptionInterface $e) {
+ // Could not get logger. Continue
+ }
+
return $container->get($target);
}, false);
}