Completed
Push — master ( 5eeda5...3dac5b )
by John
35:13 queued 14s
created
lib/private/AppFramework/App.php 1 patch
Indentation   +197 added lines, -197 removed lines patch added patch discarded remove patch
@@ -29,206 +29,206 @@
 block discarded – undo
29 29
  * Handles all the dependency injection, controllers and output flow
30 30
  */
31 31
 class App {
32
-	/** @var string[] */
33
-	private static $nameSpaceCache = [];
34
-
35
-	/**
36
-	 * Turns an app id into a namespace by either reading the appinfo.xml's
37
-	 * namespace tag or uppercasing the appid's first letter
38
-	 * @param string $appId the app id
39
-	 * @param string $topNamespace the namespace which should be prepended to
40
-	 *                             the transformed app id, defaults to OCA\
41
-	 * @return string the starting namespace for the app
42
-	 */
43
-	public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
44
-		// Hit the cache!
45
-		if (isset(self::$nameSpaceCache[$appId])) {
46
-			return $topNamespace . self::$nameSpaceCache[$appId];
47
-		}
48
-
49
-		$appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId);
50
-		if (isset($appInfo['namespace'])) {
51
-			self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
52
-		} else {
53
-			// if the tag is not found, fall back to uppercasing the first letter
54
-			self::$nameSpaceCache[$appId] = ucfirst($appId);
55
-		}
56
-
57
-		return $topNamespace . self::$nameSpaceCache[$appId];
58
-	}
59
-
60
-	public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
61
-		if (!str_starts_with($className, $topNamespace)) {
62
-			return null;
63
-		}
64
-
65
-		foreach (self::$nameSpaceCache as $appId => $namespace) {
66
-			if (str_starts_with($className, $topNamespace . $namespace . '\\')) {
67
-				return $appId;
68
-			}
69
-		}
70
-
71
-		return null;
72
-	}
73
-
74
-
75
-	/**
76
-	 * Shortcut for calling a controller method and printing the result
77
-	 *
78
-	 * @param string $controllerName the name of the controller under which it is
79
-	 *                               stored in the DI container
80
-	 * @param string $methodName the method that you want to call
81
-	 * @param DIContainer $container an instance of a pimple container.
82
-	 * @param array $urlParams list of URL parameters (optional)
83
-	 * @throws HintException
84
-	 */
85
-	public static function main(string $controllerName, string $methodName, DIContainer $container, ?array $urlParams = null) {
86
-		/** @var IProfiler $profiler */
87
-		$profiler = $container->get(IProfiler::class);
88
-		$eventLogger = $container->get(IEventLogger::class);
89
-		// Disable profiler on the profiler UI
90
-		$profiler->setEnabled($profiler->isEnabled() && !is_null($urlParams) && isset($urlParams['_route']) && !str_starts_with($urlParams['_route'], 'profiler.'));
91
-		if ($profiler->isEnabled()) {
92
-			\OC::$server->get(IEventLogger::class)->activate();
93
-			$profiler->add(new RoutingDataCollector($container['appName'], $controllerName, $methodName));
94
-		}
95
-
96
-		$eventLogger->start('app:controller:params', 'Gather controller parameters');
97
-
98
-		if (!is_null($urlParams)) {
99
-			/** @var Request $request */
100
-			$request = $container->get(IRequest::class);
101
-			$request->setUrlParameters($urlParams);
102
-		} elseif (isset($container['urlParams']) && !is_null($container['urlParams'])) {
103
-			/** @var Request $request */
104
-			$request = $container->get(IRequest::class);
105
-			$request->setUrlParameters($container['urlParams']);
106
-		}
107
-		$appName = $container['appName'];
108
-
109
-		$eventLogger->end('app:controller:params');
110
-
111
-		$eventLogger->start('app:controller:load', 'Load app controller');
112
-
113
-		// first try $controllerName then go for \OCA\AppName\Controller\$controllerName
114
-		try {
115
-			$controller = $container->get($controllerName);
116
-		} catch (QueryException $e) {
117
-			if (str_contains($controllerName, '\\Controller\\')) {
118
-				// This is from a global registered app route that is not enabled.
119
-				[/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
120
-				throw new HintException('App ' . strtolower($app) . ' is not enabled');
121
-			}
122
-
123
-			if ($appName === 'core') {
124
-				$appNameSpace = 'OC\\Core';
125
-			} else {
126
-				$appNameSpace = self::buildAppNamespace($appName);
127
-			}
128
-			$controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
129
-			$controller = $container->query($controllerName);
130
-		}
131
-
132
-		$eventLogger->end('app:controller:load');
133
-
134
-		$eventLogger->start('app:controller:dispatcher', 'Initialize dispatcher and pre-middleware');
135
-
136
-		// initialize the dispatcher and run all the middleware before the controller
137
-		$dispatcher = $container->get(Dispatcher::class);
138
-
139
-		$eventLogger->end('app:controller:dispatcher');
140
-
141
-		$eventLogger->start('app:controller:run', 'Run app controller');
142
-
143
-		[
144
-			$httpHeaders,
145
-			$responseHeaders,
146
-			$responseCookies,
147
-			$output,
148
-			$response
149
-		] = $dispatcher->dispatch($controller, $methodName);
150
-
151
-		$eventLogger->end('app:controller:run');
152
-
153
-		$io = $container[IOutput::class];
154
-
155
-		if ($profiler->isEnabled()) {
156
-			$eventLogger->end('runtime');
157
-			$profile = $profiler->collect($container->get(IRequest::class), $response);
158
-			$profiler->saveProfile($profile);
159
-			$io->setHeader('X-Debug-Token:' . $profile->getToken());
160
-			$io->setHeader('Server-Timing: token;desc="' . $profile->getToken() . '"');
161
-		}
162
-
163
-		if (!is_null($httpHeaders)) {
164
-			$io->setHeader($httpHeaders);
165
-		}
166
-
167
-		foreach ($responseHeaders as $name => $value) {
168
-			$io->setHeader($name . ': ' . $value);
169
-		}
170
-
171
-		foreach ($responseCookies as $name => $value) {
172
-			$expireDate = null;
173
-			if ($value['expireDate'] instanceof \DateTime) {
174
-				$expireDate = $value['expireDate']->getTimestamp();
175
-			}
176
-			$sameSite = $value['sameSite'] ?? 'Lax';
177
-
178
-			$io->setCookie(
179
-				$name,
180
-				$value['value'],
181
-				$expireDate,
182
-				$container->getServer()->getWebRoot(),
183
-				null,
184
-				$container->getServer()->getRequest()->getServerProtocol() === 'https',
185
-				true,
186
-				$sameSite
187
-			);
188
-		}
189
-
190
-		/*
32
+    /** @var string[] */
33
+    private static $nameSpaceCache = [];
34
+
35
+    /**
36
+     * Turns an app id into a namespace by either reading the appinfo.xml's
37
+     * namespace tag or uppercasing the appid's first letter
38
+     * @param string $appId the app id
39
+     * @param string $topNamespace the namespace which should be prepended to
40
+     *                             the transformed app id, defaults to OCA\
41
+     * @return string the starting namespace for the app
42
+     */
43
+    public static function buildAppNamespace(string $appId, string $topNamespace = 'OCA\\'): string {
44
+        // Hit the cache!
45
+        if (isset(self::$nameSpaceCache[$appId])) {
46
+            return $topNamespace . self::$nameSpaceCache[$appId];
47
+        }
48
+
49
+        $appInfo = \OCP\Server::get(IAppManager::class)->getAppInfo($appId);
50
+        if (isset($appInfo['namespace'])) {
51
+            self::$nameSpaceCache[$appId] = trim($appInfo['namespace']);
52
+        } else {
53
+            // if the tag is not found, fall back to uppercasing the first letter
54
+            self::$nameSpaceCache[$appId] = ucfirst($appId);
55
+        }
56
+
57
+        return $topNamespace . self::$nameSpaceCache[$appId];
58
+    }
59
+
60
+    public static function getAppIdForClass(string $className, string $topNamespace = 'OCA\\'): ?string {
61
+        if (!str_starts_with($className, $topNamespace)) {
62
+            return null;
63
+        }
64
+
65
+        foreach (self::$nameSpaceCache as $appId => $namespace) {
66
+            if (str_starts_with($className, $topNamespace . $namespace . '\\')) {
67
+                return $appId;
68
+            }
69
+        }
70
+
71
+        return null;
72
+    }
73
+
74
+
75
+    /**
76
+     * Shortcut for calling a controller method and printing the result
77
+     *
78
+     * @param string $controllerName the name of the controller under which it is
79
+     *                               stored in the DI container
80
+     * @param string $methodName the method that you want to call
81
+     * @param DIContainer $container an instance of a pimple container.
82
+     * @param array $urlParams list of URL parameters (optional)
83
+     * @throws HintException
84
+     */
85
+    public static function main(string $controllerName, string $methodName, DIContainer $container, ?array $urlParams = null) {
86
+        /** @var IProfiler $profiler */
87
+        $profiler = $container->get(IProfiler::class);
88
+        $eventLogger = $container->get(IEventLogger::class);
89
+        // Disable profiler on the profiler UI
90
+        $profiler->setEnabled($profiler->isEnabled() && !is_null($urlParams) && isset($urlParams['_route']) && !str_starts_with($urlParams['_route'], 'profiler.'));
91
+        if ($profiler->isEnabled()) {
92
+            \OC::$server->get(IEventLogger::class)->activate();
93
+            $profiler->add(new RoutingDataCollector($container['appName'], $controllerName, $methodName));
94
+        }
95
+
96
+        $eventLogger->start('app:controller:params', 'Gather controller parameters');
97
+
98
+        if (!is_null($urlParams)) {
99
+            /** @var Request $request */
100
+            $request = $container->get(IRequest::class);
101
+            $request->setUrlParameters($urlParams);
102
+        } elseif (isset($container['urlParams']) && !is_null($container['urlParams'])) {
103
+            /** @var Request $request */
104
+            $request = $container->get(IRequest::class);
105
+            $request->setUrlParameters($container['urlParams']);
106
+        }
107
+        $appName = $container['appName'];
108
+
109
+        $eventLogger->end('app:controller:params');
110
+
111
+        $eventLogger->start('app:controller:load', 'Load app controller');
112
+
113
+        // first try $controllerName then go for \OCA\AppName\Controller\$controllerName
114
+        try {
115
+            $controller = $container->get($controllerName);
116
+        } catch (QueryException $e) {
117
+            if (str_contains($controllerName, '\\Controller\\')) {
118
+                // This is from a global registered app route that is not enabled.
119
+                [/*OC(A)*/, $app, /* Controller/Name*/] = explode('\\', $controllerName, 3);
120
+                throw new HintException('App ' . strtolower($app) . ' is not enabled');
121
+            }
122
+
123
+            if ($appName === 'core') {
124
+                $appNameSpace = 'OC\\Core';
125
+            } else {
126
+                $appNameSpace = self::buildAppNamespace($appName);
127
+            }
128
+            $controllerName = $appNameSpace . '\\Controller\\' . $controllerName;
129
+            $controller = $container->query($controllerName);
130
+        }
131
+
132
+        $eventLogger->end('app:controller:load');
133
+
134
+        $eventLogger->start('app:controller:dispatcher', 'Initialize dispatcher and pre-middleware');
135
+
136
+        // initialize the dispatcher and run all the middleware before the controller
137
+        $dispatcher = $container->get(Dispatcher::class);
138
+
139
+        $eventLogger->end('app:controller:dispatcher');
140
+
141
+        $eventLogger->start('app:controller:run', 'Run app controller');
142
+
143
+        [
144
+            $httpHeaders,
145
+            $responseHeaders,
146
+            $responseCookies,
147
+            $output,
148
+            $response
149
+        ] = $dispatcher->dispatch($controller, $methodName);
150
+
151
+        $eventLogger->end('app:controller:run');
152
+
153
+        $io = $container[IOutput::class];
154
+
155
+        if ($profiler->isEnabled()) {
156
+            $eventLogger->end('runtime');
157
+            $profile = $profiler->collect($container->get(IRequest::class), $response);
158
+            $profiler->saveProfile($profile);
159
+            $io->setHeader('X-Debug-Token:' . $profile->getToken());
160
+            $io->setHeader('Server-Timing: token;desc="' . $profile->getToken() . '"');
161
+        }
162
+
163
+        if (!is_null($httpHeaders)) {
164
+            $io->setHeader($httpHeaders);
165
+        }
166
+
167
+        foreach ($responseHeaders as $name => $value) {
168
+            $io->setHeader($name . ': ' . $value);
169
+        }
170
+
171
+        foreach ($responseCookies as $name => $value) {
172
+            $expireDate = null;
173
+            if ($value['expireDate'] instanceof \DateTime) {
174
+                $expireDate = $value['expireDate']->getTimestamp();
175
+            }
176
+            $sameSite = $value['sameSite'] ?? 'Lax';
177
+
178
+            $io->setCookie(
179
+                $name,
180
+                $value['value'],
181
+                $expireDate,
182
+                $container->getServer()->getWebRoot(),
183
+                null,
184
+                $container->getServer()->getRequest()->getServerProtocol() === 'https',
185
+                true,
186
+                $sameSite
187
+            );
188
+        }
189
+
190
+        /*
191 191
 		 * Status 204 does not have a body and no Content Length
192 192
 		 * Status 304 does not have a body and does not need a Content Length
193 193
 		 * https://tools.ietf.org/html/rfc7230#section-3.3
194 194
 		 * https://tools.ietf.org/html/rfc7230#section-3.3.2
195 195
 		 */
196
-		$emptyResponse = false;
197
-		if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
198
-			$status = (int)$matches[1];
199
-			if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
200
-				$emptyResponse = true;
201
-			}
202
-		}
203
-
204
-		if (!$emptyResponse) {
205
-			if ($response instanceof ICallbackResponse) {
206
-				$response->callback($io);
207
-			} elseif (!is_null($output)) {
208
-				$io->setHeader('Content-Length: ' . strlen($output));
209
-				$io->setOutput($output);
210
-			}
211
-		}
212
-	}
213
-
214
-	/**
215
-	 * Shortcut for calling a controller method and printing the result.
216
-	 * Similar to App:main except that no headers will be sent.
217
-	 *
218
-	 * @param string $controllerName the name of the controller under which it is
219
-	 *                               stored in the DI container
220
-	 * @param string $methodName the method that you want to call
221
-	 * @param array $urlParams an array with variables extracted from the routes
222
-	 * @param DIContainer $container an instance of a pimple container.
223
-	 */
224
-	public static function part(string $controllerName, string $methodName, array $urlParams,
225
-		DIContainer $container) {
226
-		$container['urlParams'] = $urlParams;
227
-		$controller = $container[$controllerName];
228
-
229
-		$dispatcher = $container['Dispatcher'];
230
-
231
-		[, , $output] = $dispatcher->dispatch($controller, $methodName);
232
-		return $output;
233
-	}
196
+        $emptyResponse = false;
197
+        if (preg_match('/^HTTP\/\d\.\d (\d{3}) .*$/', $httpHeaders, $matches)) {
198
+            $status = (int)$matches[1];
199
+            if ($status === Http::STATUS_NO_CONTENT || $status === Http::STATUS_NOT_MODIFIED) {
200
+                $emptyResponse = true;
201
+            }
202
+        }
203
+
204
+        if (!$emptyResponse) {
205
+            if ($response instanceof ICallbackResponse) {
206
+                $response->callback($io);
207
+            } elseif (!is_null($output)) {
208
+                $io->setHeader('Content-Length: ' . strlen($output));
209
+                $io->setOutput($output);
210
+            }
211
+        }
212
+    }
213
+
214
+    /**
215
+     * Shortcut for calling a controller method and printing the result.
216
+     * Similar to App:main except that no headers will be sent.
217
+     *
218
+     * @param string $controllerName the name of the controller under which it is
219
+     *                               stored in the DI container
220
+     * @param string $methodName the method that you want to call
221
+     * @param array $urlParams an array with variables extracted from the routes
222
+     * @param DIContainer $container an instance of a pimple container.
223
+     */
224
+    public static function part(string $controllerName, string $methodName, array $urlParams,
225
+        DIContainer $container) {
226
+        $container['urlParams'] = $urlParams;
227
+        $controller = $container[$controllerName];
228
+
229
+        $dispatcher = $container['Dispatcher'];
230
+
231
+        [, , $output] = $dispatcher->dispatch($controller, $methodName);
232
+        return $output;
233
+    }
234 234
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/DependencyInjection/DIContainer.php 1 patch
Indentation   +297 added lines, -297 removed lines patch added patch discarded remove patch
@@ -63,301 +63,301 @@
 block discarded – undo
63 63
 use Psr\Log\LoggerInterface;
64 64
 
65 65
 class DIContainer extends SimpleContainer implements IAppContainer {
66
-	protected string $appName;
67
-	private array $middleWares = [];
68
-	private ServerContainer $server;
69
-
70
-	public function __construct(string $appName, array $urlParams = [], ?ServerContainer $server = null) {
71
-		parent::__construct();
72
-		$this->appName = $appName;
73
-		$this->registerParameter('appName', $appName);
74
-		$this->registerParameter('urlParams', $urlParams);
75
-
76
-		/** @deprecated 32.0.0 */
77
-		$this->registerDeprecatedAlias('Request', IRequest::class);
78
-
79
-		if ($server === null) {
80
-			$server = \OC::$server;
81
-		}
82
-		$this->server = $server;
83
-		$this->server->registerAppContainer($appName, $this);
84
-
85
-		// aliases
86
-		/** @deprecated 26.0.0 inject $appName */
87
-		$this->registerDeprecatedAlias('AppName', 'appName');
88
-		/** @deprecated 26.0.0 inject $webRoot*/
89
-		$this->registerDeprecatedAlias('WebRoot', 'webRoot');
90
-		/** @deprecated 26.0.0 inject $userId */
91
-		$this->registerDeprecatedAlias('UserId', 'userId');
92
-
93
-		/**
94
-		 * Core services
95
-		 */
96
-		/* Cannot be an alias because Output is not in OCA */
97
-		$this->registerService(IOutput::class, fn (ContainerInterface $c): IOutput => new Output($c->get('webRoot')));
98
-
99
-		$this->registerService(Folder::class, function () {
100
-			return $this->getServer()->getUserFolder();
101
-		});
102
-
103
-		$this->registerService(IAppData::class, function (ContainerInterface $c): IAppData {
104
-			return $c->get(IAppDataFactory::class)->get($c->get('appName'));
105
-		});
106
-
107
-		$this->registerService(IL10N::class, function (ContainerInterface $c) {
108
-			return $this->getServer()->getL10N($c->get('appName'));
109
-		});
110
-
111
-		// Log wrappers
112
-		$this->registerService(LoggerInterface::class, function (ContainerInterface $c) {
113
-			/* Cannot be an alias because it uses LoggerInterface so it would infinite loop */
114
-			return new ScopedPsrLogger(
115
-				$c->get(PsrLoggerAdapter::class),
116
-				$c->get('appName')
117
-			);
118
-		});
119
-
120
-		$this->registerService(IServerContainer::class, function () {
121
-			return $this->getServer();
122
-		});
123
-		/** @deprecated 32.0.0 */
124
-		$this->registerDeprecatedAlias('ServerContainer', IServerContainer::class);
125
-
126
-		$this->registerAlias(\OCP\WorkflowEngine\IManager::class, Manager::class);
127
-
128
-		$this->registerService(ContainerInterface::class, fn (ContainerInterface $c) => $c);
129
-		$this->registerDeprecatedAlias(IAppContainer::class, ContainerInterface::class);
130
-
131
-		// commonly used attributes
132
-		$this->registerService('userId', function (ContainerInterface $c): ?string {
133
-			return $c->get(ISession::class)->get('user_id');
134
-		});
135
-
136
-		$this->registerService('webRoot', function (ContainerInterface $c): string {
137
-			return $c->get(IServerContainer::class)->getWebRoot();
138
-		});
139
-
140
-		$this->registerService('OC_Defaults', function (ContainerInterface $c): object {
141
-			return $c->get(IServerContainer::class)->get('ThemingDefaults');
142
-		});
143
-
144
-		/** @deprecated 32.0.0 */
145
-		$this->registerDeprecatedAlias('Protocol', Http::class);
146
-		$this->registerService(Http::class, function (ContainerInterface $c) {
147
-			$protocol = $c->get(IRequest::class)->getHttpProtocol();
148
-			return new Http($_SERVER, $protocol);
149
-		});
150
-
151
-		/** @deprecated 32.0.0 */
152
-		$this->registerDeprecatedAlias('Dispatcher', Dispatcher::class);
153
-		$this->registerService(Dispatcher::class, function (ContainerInterface $c) {
154
-			return new Dispatcher(
155
-				$c->get(Http::class),
156
-				$c->get(MiddlewareDispatcher::class),
157
-				$c->get(IControllerMethodReflector::class),
158
-				$c->get(IRequest::class),
159
-				$c->get(IConfig::class),
160
-				$c->get(IDBConnection::class),
161
-				$c->get(LoggerInterface::class),
162
-				$c->get(EventLogger::class),
163
-				$c,
164
-			);
165
-		});
166
-
167
-		/**
168
-		 * App Framework default arguments
169
-		 */
170
-		$this->registerParameter('corsMethods', 'PUT, POST, GET, DELETE, PATCH');
171
-		$this->registerParameter('corsAllowedHeaders', 'Authorization, Content-Type, Accept');
172
-		$this->registerParameter('corsMaxAge', 1728000);
173
-
174
-		/**
175
-		 * Middleware
176
-		 */
177
-		/** @deprecated 32.0.0 */
178
-		$this->registerDeprecatedAlias('MiddlewareDispatcher', MiddlewareDispatcher::class);
179
-		$this->registerService(MiddlewareDispatcher::class, function (ContainerInterface $c) {
180
-			$server = $this->getServer();
181
-
182
-			$dispatcher = new MiddlewareDispatcher();
183
-
184
-			$dispatcher->registerMiddleware($c->get(CompressionMiddleware::class));
185
-			$dispatcher->registerMiddleware($c->get(NotModifiedMiddleware::class));
186
-			$dispatcher->registerMiddleware($c->get(ReloadExecutionMiddleware::class));
187
-			$dispatcher->registerMiddleware($c->get(SameSiteCookieMiddleware::class));
188
-			$dispatcher->registerMiddleware($c->get(CORSMiddleware::class));
189
-			$dispatcher->registerMiddleware($c->get(OCSMiddleware::class));
190
-
191
-			$dispatcher->registerMiddleware($c->get(FlowV2EphemeralSessionsMiddleware::class));
192
-
193
-			$securityMiddleware = new SecurityMiddleware(
194
-				$c->get(IRequest::class),
195
-				$c->get(IControllerMethodReflector::class),
196
-				$c->get(INavigationManager::class),
197
-				$c->get(IURLGenerator::class),
198
-				$c->get(LoggerInterface::class),
199
-				$c->get('appName'),
200
-				$server->getUserSession()->isLoggedIn(),
201
-				$c->get(IGroupManager::class),
202
-				$c->get(ISubAdmin::class),
203
-				$server->getAppManager(),
204
-				$server->getL10N('lib'),
205
-				$c->get(AuthorizedGroupMapper::class),
206
-				$c->get(IUserSession::class),
207
-				$c->get(IRemoteAddress::class),
208
-			);
209
-			$dispatcher->registerMiddleware($securityMiddleware);
210
-			$dispatcher->registerMiddleware($c->get(CSPMiddleware::class));
211
-			$dispatcher->registerMiddleware($c->get(FeaturePolicyMiddleware::class));
212
-			$dispatcher->registerMiddleware($c->get(PasswordConfirmationMiddleware::class));
213
-			$dispatcher->registerMiddleware($c->get(TwoFactorMiddleware::class));
214
-			$dispatcher->registerMiddleware($c->get(BruteForceMiddleware::class));
215
-			$dispatcher->registerMiddleware($c->get(RateLimitingMiddleware::class));
216
-			$dispatcher->registerMiddleware($c->get(PublicShareMiddleware::class));
217
-			$dispatcher->registerMiddleware($c->get(AdditionalScriptsMiddleware::class));
218
-
219
-			$coordinator = $c->get(\OC\AppFramework\Bootstrap\Coordinator::class);
220
-			$registrationContext = $coordinator->getRegistrationContext();
221
-			if ($registrationContext !== null) {
222
-				$appId = $this->get('appName');
223
-				foreach ($registrationContext->getMiddlewareRegistrations() as $middlewareRegistration) {
224
-					if ($middlewareRegistration->getAppId() === $appId
225
-						|| $middlewareRegistration->isGlobal()) {
226
-						$dispatcher->registerMiddleware($c->get($middlewareRegistration->getService()));
227
-					}
228
-				}
229
-			}
230
-			foreach ($this->middleWares as $middleWare) {
231
-				$dispatcher->registerMiddleware($c->get($middleWare));
232
-			}
233
-
234
-			$dispatcher->registerMiddleware($c->get(SessionMiddleware::class));
235
-			return $dispatcher;
236
-		});
237
-
238
-		$this->registerAlias(IAppConfig::class, \OC\AppFramework\Services\AppConfig::class);
239
-		$this->registerAlias(IInitialState::class, \OC\AppFramework\Services\InitialState::class);
240
-	}
241
-
242
-	/**
243
-	 * @return \OCP\IServerContainer
244
-	 */
245
-	public function getServer() {
246
-		return $this->server;
247
-	}
248
-
249
-	/**
250
-	 * @param string $middleWare
251
-	 */
252
-	public function registerMiddleWare($middleWare): bool {
253
-		if (in_array($middleWare, $this->middleWares, true) !== false) {
254
-			return false;
255
-		}
256
-		$this->middleWares[] = $middleWare;
257
-		return true;
258
-	}
259
-
260
-	/**
261
-	 * used to return the appname of the set application
262
-	 * @return string the name of your application
263
-	 */
264
-	public function getAppName() {
265
-		return $this->query('appName');
266
-	}
267
-
268
-	/**
269
-	 * @deprecated 12.0.0 use IUserSession->isLoggedIn()
270
-	 * @return boolean
271
-	 */
272
-	public function isLoggedIn() {
273
-		return \OC::$server->getUserSession()->isLoggedIn();
274
-	}
275
-
276
-	/**
277
-	 * @deprecated 12.0.0 use IGroupManager->isAdmin($userId)
278
-	 * @return boolean
279
-	 */
280
-	public function isAdminUser() {
281
-		$uid = $this->getUserId();
282
-		return \OC_User::isAdminUser($uid);
283
-	}
284
-
285
-	private function getUserId(): string {
286
-		return $this->getServer()->getSession()->get('user_id');
287
-	}
288
-
289
-	/**
290
-	 * Register a capability
291
-	 *
292
-	 * @param string $serviceName e.g. 'OCA\Files\Capabilities'
293
-	 */
294
-	public function registerCapability($serviceName) {
295
-		$this->query('OC\CapabilitiesManager')->registerCapability(function () use ($serviceName) {
296
-			return $this->query($serviceName);
297
-		});
298
-	}
299
-
300
-	public function has($id): bool {
301
-		if (parent::has($id)) {
302
-			return true;
303
-		}
304
-
305
-		if ($this->server->has($id, true)) {
306
-			return true;
307
-		}
308
-
309
-		return false;
310
-	}
311
-
312
-	public function query(string $name, bool $autoload = true) {
313
-		if ($name === 'AppName' || $name === 'appName') {
314
-			return $this->appName;
315
-		}
316
-
317
-		$isServerClass = str_starts_with($name, 'OCP\\') || str_starts_with($name, 'OC\\');
318
-		if ($isServerClass && !$this->has($name)) {
319
-			return $this->getServer()->query($name, $autoload);
320
-		}
321
-
322
-		try {
323
-			return $this->queryNoFallback($name);
324
-		} catch (QueryException $firstException) {
325
-			try {
326
-				return $this->getServer()->query($name, $autoload);
327
-			} catch (QueryException $secondException) {
328
-				if ($firstException->getCode() === 1) {
329
-					throw $secondException;
330
-				}
331
-				throw $firstException;
332
-			}
333
-		}
334
-	}
335
-
336
-	/**
337
-	 * @param string $name
338
-	 * @return mixed
339
-	 * @throws QueryException if the query could not be resolved
340
-	 */
341
-	public function queryNoFallback($name) {
342
-		$name = $this->sanitizeName($name);
343
-
344
-		if ($this->offsetExists($name)) {
345
-			return parent::query($name);
346
-		} elseif ($this->appName === 'settings' && str_starts_with($name, 'OC\\Settings\\')) {
347
-			return parent::query($name);
348
-		} elseif ($this->appName === 'core' && str_starts_with($name, 'OC\\Core\\')) {
349
-			return parent::query($name);
350
-		} elseif (str_starts_with($name, \OC\AppFramework\App::buildAppNamespace($this->appName) . '\\')) {
351
-			return parent::query($name);
352
-		} elseif (
353
-			str_starts_with($name, 'OC\\AppFramework\\Services\\')
354
-			|| str_starts_with($name, 'OC\\AppFramework\\Middleware\\')
355
-		) {
356
-			/* AppFramework services are scoped to the application */
357
-			return parent::query($name);
358
-		}
359
-
360
-		throw new QueryException('Could not resolve ' . $name . '!'
361
-			. ' Class can not be instantiated', 1);
362
-	}
66
+    protected string $appName;
67
+    private array $middleWares = [];
68
+    private ServerContainer $server;
69
+
70
+    public function __construct(string $appName, array $urlParams = [], ?ServerContainer $server = null) {
71
+        parent::__construct();
72
+        $this->appName = $appName;
73
+        $this->registerParameter('appName', $appName);
74
+        $this->registerParameter('urlParams', $urlParams);
75
+
76
+        /** @deprecated 32.0.0 */
77
+        $this->registerDeprecatedAlias('Request', IRequest::class);
78
+
79
+        if ($server === null) {
80
+            $server = \OC::$server;
81
+        }
82
+        $this->server = $server;
83
+        $this->server->registerAppContainer($appName, $this);
84
+
85
+        // aliases
86
+        /** @deprecated 26.0.0 inject $appName */
87
+        $this->registerDeprecatedAlias('AppName', 'appName');
88
+        /** @deprecated 26.0.0 inject $webRoot*/
89
+        $this->registerDeprecatedAlias('WebRoot', 'webRoot');
90
+        /** @deprecated 26.0.0 inject $userId */
91
+        $this->registerDeprecatedAlias('UserId', 'userId');
92
+
93
+        /**
94
+         * Core services
95
+         */
96
+        /* Cannot be an alias because Output is not in OCA */
97
+        $this->registerService(IOutput::class, fn (ContainerInterface $c): IOutput => new Output($c->get('webRoot')));
98
+
99
+        $this->registerService(Folder::class, function () {
100
+            return $this->getServer()->getUserFolder();
101
+        });
102
+
103
+        $this->registerService(IAppData::class, function (ContainerInterface $c): IAppData {
104
+            return $c->get(IAppDataFactory::class)->get($c->get('appName'));
105
+        });
106
+
107
+        $this->registerService(IL10N::class, function (ContainerInterface $c) {
108
+            return $this->getServer()->getL10N($c->get('appName'));
109
+        });
110
+
111
+        // Log wrappers
112
+        $this->registerService(LoggerInterface::class, function (ContainerInterface $c) {
113
+            /* Cannot be an alias because it uses LoggerInterface so it would infinite loop */
114
+            return new ScopedPsrLogger(
115
+                $c->get(PsrLoggerAdapter::class),
116
+                $c->get('appName')
117
+            );
118
+        });
119
+
120
+        $this->registerService(IServerContainer::class, function () {
121
+            return $this->getServer();
122
+        });
123
+        /** @deprecated 32.0.0 */
124
+        $this->registerDeprecatedAlias('ServerContainer', IServerContainer::class);
125
+
126
+        $this->registerAlias(\OCP\WorkflowEngine\IManager::class, Manager::class);
127
+
128
+        $this->registerService(ContainerInterface::class, fn (ContainerInterface $c) => $c);
129
+        $this->registerDeprecatedAlias(IAppContainer::class, ContainerInterface::class);
130
+
131
+        // commonly used attributes
132
+        $this->registerService('userId', function (ContainerInterface $c): ?string {
133
+            return $c->get(ISession::class)->get('user_id');
134
+        });
135
+
136
+        $this->registerService('webRoot', function (ContainerInterface $c): string {
137
+            return $c->get(IServerContainer::class)->getWebRoot();
138
+        });
139
+
140
+        $this->registerService('OC_Defaults', function (ContainerInterface $c): object {
141
+            return $c->get(IServerContainer::class)->get('ThemingDefaults');
142
+        });
143
+
144
+        /** @deprecated 32.0.0 */
145
+        $this->registerDeprecatedAlias('Protocol', Http::class);
146
+        $this->registerService(Http::class, function (ContainerInterface $c) {
147
+            $protocol = $c->get(IRequest::class)->getHttpProtocol();
148
+            return new Http($_SERVER, $protocol);
149
+        });
150
+
151
+        /** @deprecated 32.0.0 */
152
+        $this->registerDeprecatedAlias('Dispatcher', Dispatcher::class);
153
+        $this->registerService(Dispatcher::class, function (ContainerInterface $c) {
154
+            return new Dispatcher(
155
+                $c->get(Http::class),
156
+                $c->get(MiddlewareDispatcher::class),
157
+                $c->get(IControllerMethodReflector::class),
158
+                $c->get(IRequest::class),
159
+                $c->get(IConfig::class),
160
+                $c->get(IDBConnection::class),
161
+                $c->get(LoggerInterface::class),
162
+                $c->get(EventLogger::class),
163
+                $c,
164
+            );
165
+        });
166
+
167
+        /**
168
+         * App Framework default arguments
169
+         */
170
+        $this->registerParameter('corsMethods', 'PUT, POST, GET, DELETE, PATCH');
171
+        $this->registerParameter('corsAllowedHeaders', 'Authorization, Content-Type, Accept');
172
+        $this->registerParameter('corsMaxAge', 1728000);
173
+
174
+        /**
175
+         * Middleware
176
+         */
177
+        /** @deprecated 32.0.0 */
178
+        $this->registerDeprecatedAlias('MiddlewareDispatcher', MiddlewareDispatcher::class);
179
+        $this->registerService(MiddlewareDispatcher::class, function (ContainerInterface $c) {
180
+            $server = $this->getServer();
181
+
182
+            $dispatcher = new MiddlewareDispatcher();
183
+
184
+            $dispatcher->registerMiddleware($c->get(CompressionMiddleware::class));
185
+            $dispatcher->registerMiddleware($c->get(NotModifiedMiddleware::class));
186
+            $dispatcher->registerMiddleware($c->get(ReloadExecutionMiddleware::class));
187
+            $dispatcher->registerMiddleware($c->get(SameSiteCookieMiddleware::class));
188
+            $dispatcher->registerMiddleware($c->get(CORSMiddleware::class));
189
+            $dispatcher->registerMiddleware($c->get(OCSMiddleware::class));
190
+
191
+            $dispatcher->registerMiddleware($c->get(FlowV2EphemeralSessionsMiddleware::class));
192
+
193
+            $securityMiddleware = new SecurityMiddleware(
194
+                $c->get(IRequest::class),
195
+                $c->get(IControllerMethodReflector::class),
196
+                $c->get(INavigationManager::class),
197
+                $c->get(IURLGenerator::class),
198
+                $c->get(LoggerInterface::class),
199
+                $c->get('appName'),
200
+                $server->getUserSession()->isLoggedIn(),
201
+                $c->get(IGroupManager::class),
202
+                $c->get(ISubAdmin::class),
203
+                $server->getAppManager(),
204
+                $server->getL10N('lib'),
205
+                $c->get(AuthorizedGroupMapper::class),
206
+                $c->get(IUserSession::class),
207
+                $c->get(IRemoteAddress::class),
208
+            );
209
+            $dispatcher->registerMiddleware($securityMiddleware);
210
+            $dispatcher->registerMiddleware($c->get(CSPMiddleware::class));
211
+            $dispatcher->registerMiddleware($c->get(FeaturePolicyMiddleware::class));
212
+            $dispatcher->registerMiddleware($c->get(PasswordConfirmationMiddleware::class));
213
+            $dispatcher->registerMiddleware($c->get(TwoFactorMiddleware::class));
214
+            $dispatcher->registerMiddleware($c->get(BruteForceMiddleware::class));
215
+            $dispatcher->registerMiddleware($c->get(RateLimitingMiddleware::class));
216
+            $dispatcher->registerMiddleware($c->get(PublicShareMiddleware::class));
217
+            $dispatcher->registerMiddleware($c->get(AdditionalScriptsMiddleware::class));
218
+
219
+            $coordinator = $c->get(\OC\AppFramework\Bootstrap\Coordinator::class);
220
+            $registrationContext = $coordinator->getRegistrationContext();
221
+            if ($registrationContext !== null) {
222
+                $appId = $this->get('appName');
223
+                foreach ($registrationContext->getMiddlewareRegistrations() as $middlewareRegistration) {
224
+                    if ($middlewareRegistration->getAppId() === $appId
225
+                        || $middlewareRegistration->isGlobal()) {
226
+                        $dispatcher->registerMiddleware($c->get($middlewareRegistration->getService()));
227
+                    }
228
+                }
229
+            }
230
+            foreach ($this->middleWares as $middleWare) {
231
+                $dispatcher->registerMiddleware($c->get($middleWare));
232
+            }
233
+
234
+            $dispatcher->registerMiddleware($c->get(SessionMiddleware::class));
235
+            return $dispatcher;
236
+        });
237
+
238
+        $this->registerAlias(IAppConfig::class, \OC\AppFramework\Services\AppConfig::class);
239
+        $this->registerAlias(IInitialState::class, \OC\AppFramework\Services\InitialState::class);
240
+    }
241
+
242
+    /**
243
+     * @return \OCP\IServerContainer
244
+     */
245
+    public function getServer() {
246
+        return $this->server;
247
+    }
248
+
249
+    /**
250
+     * @param string $middleWare
251
+     */
252
+    public function registerMiddleWare($middleWare): bool {
253
+        if (in_array($middleWare, $this->middleWares, true) !== false) {
254
+            return false;
255
+        }
256
+        $this->middleWares[] = $middleWare;
257
+        return true;
258
+    }
259
+
260
+    /**
261
+     * used to return the appname of the set application
262
+     * @return string the name of your application
263
+     */
264
+    public function getAppName() {
265
+        return $this->query('appName');
266
+    }
267
+
268
+    /**
269
+     * @deprecated 12.0.0 use IUserSession->isLoggedIn()
270
+     * @return boolean
271
+     */
272
+    public function isLoggedIn() {
273
+        return \OC::$server->getUserSession()->isLoggedIn();
274
+    }
275
+
276
+    /**
277
+     * @deprecated 12.0.0 use IGroupManager->isAdmin($userId)
278
+     * @return boolean
279
+     */
280
+    public function isAdminUser() {
281
+        $uid = $this->getUserId();
282
+        return \OC_User::isAdminUser($uid);
283
+    }
284
+
285
+    private function getUserId(): string {
286
+        return $this->getServer()->getSession()->get('user_id');
287
+    }
288
+
289
+    /**
290
+     * Register a capability
291
+     *
292
+     * @param string $serviceName e.g. 'OCA\Files\Capabilities'
293
+     */
294
+    public function registerCapability($serviceName) {
295
+        $this->query('OC\CapabilitiesManager')->registerCapability(function () use ($serviceName) {
296
+            return $this->query($serviceName);
297
+        });
298
+    }
299
+
300
+    public function has($id): bool {
301
+        if (parent::has($id)) {
302
+            return true;
303
+        }
304
+
305
+        if ($this->server->has($id, true)) {
306
+            return true;
307
+        }
308
+
309
+        return false;
310
+    }
311
+
312
+    public function query(string $name, bool $autoload = true) {
313
+        if ($name === 'AppName' || $name === 'appName') {
314
+            return $this->appName;
315
+        }
316
+
317
+        $isServerClass = str_starts_with($name, 'OCP\\') || str_starts_with($name, 'OC\\');
318
+        if ($isServerClass && !$this->has($name)) {
319
+            return $this->getServer()->query($name, $autoload);
320
+        }
321
+
322
+        try {
323
+            return $this->queryNoFallback($name);
324
+        } catch (QueryException $firstException) {
325
+            try {
326
+                return $this->getServer()->query($name, $autoload);
327
+            } catch (QueryException $secondException) {
328
+                if ($firstException->getCode() === 1) {
329
+                    throw $secondException;
330
+                }
331
+                throw $firstException;
332
+            }
333
+        }
334
+    }
335
+
336
+    /**
337
+     * @param string $name
338
+     * @return mixed
339
+     * @throws QueryException if the query could not be resolved
340
+     */
341
+    public function queryNoFallback($name) {
342
+        $name = $this->sanitizeName($name);
343
+
344
+        if ($this->offsetExists($name)) {
345
+            return parent::query($name);
346
+        } elseif ($this->appName === 'settings' && str_starts_with($name, 'OC\\Settings\\')) {
347
+            return parent::query($name);
348
+        } elseif ($this->appName === 'core' && str_starts_with($name, 'OC\\Core\\')) {
349
+            return parent::query($name);
350
+        } elseif (str_starts_with($name, \OC\AppFramework\App::buildAppNamespace($this->appName) . '\\')) {
351
+            return parent::query($name);
352
+        } elseif (
353
+            str_starts_with($name, 'OC\\AppFramework\\Services\\')
354
+            || str_starts_with($name, 'OC\\AppFramework\\Middleware\\')
355
+        ) {
356
+            /* AppFramework services are scoped to the application */
357
+            return parent::query($name);
358
+        }
359
+
360
+        throw new QueryException('Could not resolve ' . $name . '!'
361
+            . ' Class can not be instantiated', 1);
362
+    }
363 363
 }
Please login to merge, or discard this patch.
lib/private/AppFramework/Utility/SimpleContainer.php 1 patch
Indentation   +222 added lines, -222 removed lines patch added patch discarded remove patch
@@ -25,226 +25,226 @@
 block discarded – undo
25 25
  * SimpleContainer is a simple implementation of a container on basis of Pimple
26 26
  */
27 27
 class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
28
-	public static bool $useLazyObjects = false;
29
-
30
-	private Container $container;
31
-
32
-	public function __construct() {
33
-		$this->container = new Container();
34
-	}
35
-
36
-	/**
37
-	 * @template T
38
-	 * @param class-string<T>|string $id
39
-	 * @return T|mixed
40
-	 * @psalm-template S as class-string<T>|string
41
-	 * @psalm-param S $id
42
-	 * @psalm-return (S is class-string<T> ? T : mixed)
43
-	 */
44
-	public function get(string $id): mixed {
45
-		return $this->query($id);
46
-	}
47
-
48
-	public function has(string $id): bool {
49
-		// If a service is no registered but is an existing class, we can probably load it
50
-		return isset($this->container[$id]) || class_exists($id);
51
-	}
52
-
53
-	/**
54
-	 * @param ReflectionClass $class the class to instantiate
55
-	 * @return object the created class
56
-	 * @suppress PhanUndeclaredClassInstanceof
57
-	 */
58
-	private function buildClass(ReflectionClass $class): object {
59
-		$constructor = $class->getConstructor();
60
-		if ($constructor === null) {
61
-			/* No constructor, return a instance directly */
62
-			return $class->newInstance();
63
-		}
64
-		if (PHP_VERSION_ID >= 80400 && self::$useLazyObjects) {
65
-			/* For PHP>=8.4, use a lazy ghost to delay constructor and dependency resolving */
66
-			/** @psalm-suppress UndefinedMethod */
67
-			return $class->newLazyGhost(function (object $object) use ($constructor): void {
68
-				/** @psalm-suppress DirectConstructorCall For lazy ghosts we have to call the constructor directly */
69
-				$object->__construct(...$this->buildClassConstructorParameters($constructor));
70
-			});
71
-		} else {
72
-			return $class->newInstanceArgs($this->buildClassConstructorParameters($constructor));
73
-		}
74
-	}
75
-
76
-	private function buildClassConstructorParameters(\ReflectionMethod $constructor): array {
77
-		return array_map(function (ReflectionParameter $parameter) {
78
-			$parameterType = $parameter->getType();
79
-
80
-			$resolveName = $parameter->getName();
81
-
82
-			// try to find out if it is a class or a simple parameter
83
-			if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
84
-				$resolveName = $parameterType->getName();
85
-			}
86
-
87
-			try {
88
-				$builtIn = $parameterType !== null && ($parameterType instanceof ReflectionNamedType)
89
-							&& $parameterType->isBuiltin();
90
-				return $this->query($resolveName, !$builtIn);
91
-			} catch (ContainerExceptionInterface $e) {
92
-				// Service not found, use the default value when available
93
-				if ($parameter->isDefaultValueAvailable()) {
94
-					return $parameter->getDefaultValue();
95
-				}
96
-
97
-				if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
98
-					$resolveName = $parameter->getName();
99
-					try {
100
-						return $this->query($resolveName);
101
-					} catch (ContainerExceptionInterface $e2) {
102
-						// Pass null if typed and nullable
103
-						if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
104
-							return null;
105
-						}
106
-
107
-						// don't lose the error we got while trying to query by type
108
-						throw new QueryException($e->getMessage(), (int)$e->getCode(), $e);
109
-					}
110
-				}
111
-
112
-				throw $e;
113
-			}
114
-		}, $constructor->getParameters());
115
-	}
116
-
117
-	public function resolve($name) {
118
-		$baseMsg = 'Could not resolve ' . $name . '!';
119
-		try {
120
-			$class = new ReflectionClass($name);
121
-			if ($class->isInstantiable()) {
122
-				return $this->buildClass($class);
123
-			} else {
124
-				throw new QueryException($baseMsg
125
-					. ' Class can not be instantiated');
126
-			}
127
-		} catch (ReflectionException $e) {
128
-			// Class does not exist
129
-			throw new QueryNotFoundException($baseMsg . ' ' . $e->getMessage());
130
-		}
131
-	}
132
-
133
-	public function query(string $name, bool $autoload = true) {
134
-		$name = $this->sanitizeName($name);
135
-		if (isset($this->container[$name])) {
136
-			return $this->container[$name];
137
-		}
138
-
139
-		if ($autoload) {
140
-			$object = $this->resolve($name);
141
-			$this->registerService($name, function () use ($object) {
142
-				return $object;
143
-			});
144
-			return $object;
145
-		}
146
-
147
-		throw new QueryNotFoundException('Could not resolve ' . $name . '!');
148
-	}
149
-
150
-	/**
151
-	 * @param string $name
152
-	 * @param mixed $value
153
-	 */
154
-	public function registerParameter($name, $value) {
155
-		$this[$name] = $value;
156
-	}
157
-
158
-	/**
159
-	 * The given closure is call the first time the given service is queried.
160
-	 * The closure has to return the instance for the given service.
161
-	 * Created instance will be cached in case $shared is true.
162
-	 *
163
-	 * @param string $name name of the service to register another backend for
164
-	 * @param Closure $closure the closure to be called on service creation
165
-	 * @param bool $shared
166
-	 */
167
-	public function registerService($name, Closure $closure, $shared = true) {
168
-		$wrapped = function () use ($closure) {
169
-			return $closure($this);
170
-		};
171
-		$name = $this->sanitizeName($name);
172
-		if (isset($this->container[$name])) {
173
-			unset($this->container[$name]);
174
-		}
175
-		if ($shared) {
176
-			$this->container[$name] = $wrapped;
177
-		} else {
178
-			$this->container[$name] = $this->container->factory($wrapped);
179
-		}
180
-	}
181
-
182
-	/**
183
-	 * Shortcut for returning a service from a service under a different key,
184
-	 * e.g. to tell the container to return a class when queried for an
185
-	 * interface
186
-	 * @param string $alias the alias that should be registered
187
-	 * @param string $target the target that should be resolved instead
188
-	 */
189
-	public function registerAlias($alias, $target): void {
190
-		$this->registerService($alias, function (ContainerInterface $container) use ($target): mixed {
191
-			return $container->get($target);
192
-		}, false);
193
-	}
194
-
195
-	protected function registerDeprecatedAlias(string $alias, string $target): void {
196
-		$this->registerService($alias, function (ContainerInterface $container) use ($target, $alias): mixed {
197
-			try {
198
-				$logger = $container->get(LoggerInterface::class);
199
-				$logger->debug('The requested alias "' . $alias . '" is deprecated. Please request "' . $target . '" directly. This alias will be removed in a future Nextcloud version.', [
200
-					'app' => $this->appName ?? 'serverDI',
201
-				]);
202
-			} catch (ContainerExceptionInterface $e) {
203
-				// Could not get logger. Continue
204
-			}
205
-
206
-			return $container->get($target);
207
-		}, false);
208
-	}
209
-
210
-	/**
211
-	 * @param string $name
212
-	 * @return string
213
-	 */
214
-	protected function sanitizeName($name) {
215
-		if (isset($name[0]) && $name[0] === '\\') {
216
-			return ltrim($name, '\\');
217
-		}
218
-		return $name;
219
-	}
220
-
221
-	/**
222
-	 * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::has
223
-	 */
224
-	public function offsetExists($id): bool {
225
-		return $this->container->offsetExists($id);
226
-	}
227
-
228
-	/**
229
-	 * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get
230
-	 * @return mixed
231
-	 */
232
-	#[\ReturnTypeWillChange]
233
-	public function offsetGet($id) {
234
-		return $this->container->offsetGet($id);
235
-	}
236
-
237
-	/**
238
-	 * @deprecated 20.0.0 use \OCP\IContainer::registerService
239
-	 */
240
-	public function offsetSet($offset, $value): void {
241
-		$this->container->offsetSet($offset, $value);
242
-	}
243
-
244
-	/**
245
-	 * @deprecated 20.0.0
246
-	 */
247
-	public function offsetUnset($offset): void {
248
-		$this->container->offsetUnset($offset);
249
-	}
28
+    public static bool $useLazyObjects = false;
29
+
30
+    private Container $container;
31
+
32
+    public function __construct() {
33
+        $this->container = new Container();
34
+    }
35
+
36
+    /**
37
+     * @template T
38
+     * @param class-string<T>|string $id
39
+     * @return T|mixed
40
+     * @psalm-template S as class-string<T>|string
41
+     * @psalm-param S $id
42
+     * @psalm-return (S is class-string<T> ? T : mixed)
43
+     */
44
+    public function get(string $id): mixed {
45
+        return $this->query($id);
46
+    }
47
+
48
+    public function has(string $id): bool {
49
+        // If a service is no registered but is an existing class, we can probably load it
50
+        return isset($this->container[$id]) || class_exists($id);
51
+    }
52
+
53
+    /**
54
+     * @param ReflectionClass $class the class to instantiate
55
+     * @return object the created class
56
+     * @suppress PhanUndeclaredClassInstanceof
57
+     */
58
+    private function buildClass(ReflectionClass $class): object {
59
+        $constructor = $class->getConstructor();
60
+        if ($constructor === null) {
61
+            /* No constructor, return a instance directly */
62
+            return $class->newInstance();
63
+        }
64
+        if (PHP_VERSION_ID >= 80400 && self::$useLazyObjects) {
65
+            /* For PHP>=8.4, use a lazy ghost to delay constructor and dependency resolving */
66
+            /** @psalm-suppress UndefinedMethod */
67
+            return $class->newLazyGhost(function (object $object) use ($constructor): void {
68
+                /** @psalm-suppress DirectConstructorCall For lazy ghosts we have to call the constructor directly */
69
+                $object->__construct(...$this->buildClassConstructorParameters($constructor));
70
+            });
71
+        } else {
72
+            return $class->newInstanceArgs($this->buildClassConstructorParameters($constructor));
73
+        }
74
+    }
75
+
76
+    private function buildClassConstructorParameters(\ReflectionMethod $constructor): array {
77
+        return array_map(function (ReflectionParameter $parameter) {
78
+            $parameterType = $parameter->getType();
79
+
80
+            $resolveName = $parameter->getName();
81
+
82
+            // try to find out if it is a class or a simple parameter
83
+            if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
84
+                $resolveName = $parameterType->getName();
85
+            }
86
+
87
+            try {
88
+                $builtIn = $parameterType !== null && ($parameterType instanceof ReflectionNamedType)
89
+                            && $parameterType->isBuiltin();
90
+                return $this->query($resolveName, !$builtIn);
91
+            } catch (ContainerExceptionInterface $e) {
92
+                // Service not found, use the default value when available
93
+                if ($parameter->isDefaultValueAvailable()) {
94
+                    return $parameter->getDefaultValue();
95
+                }
96
+
97
+                if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
98
+                    $resolveName = $parameter->getName();
99
+                    try {
100
+                        return $this->query($resolveName);
101
+                    } catch (ContainerExceptionInterface $e2) {
102
+                        // Pass null if typed and nullable
103
+                        if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
104
+                            return null;
105
+                        }
106
+
107
+                        // don't lose the error we got while trying to query by type
108
+                        throw new QueryException($e->getMessage(), (int)$e->getCode(), $e);
109
+                    }
110
+                }
111
+
112
+                throw $e;
113
+            }
114
+        }, $constructor->getParameters());
115
+    }
116
+
117
+    public function resolve($name) {
118
+        $baseMsg = 'Could not resolve ' . $name . '!';
119
+        try {
120
+            $class = new ReflectionClass($name);
121
+            if ($class->isInstantiable()) {
122
+                return $this->buildClass($class);
123
+            } else {
124
+                throw new QueryException($baseMsg
125
+                    . ' Class can not be instantiated');
126
+            }
127
+        } catch (ReflectionException $e) {
128
+            // Class does not exist
129
+            throw new QueryNotFoundException($baseMsg . ' ' . $e->getMessage());
130
+        }
131
+    }
132
+
133
+    public function query(string $name, bool $autoload = true) {
134
+        $name = $this->sanitizeName($name);
135
+        if (isset($this->container[$name])) {
136
+            return $this->container[$name];
137
+        }
138
+
139
+        if ($autoload) {
140
+            $object = $this->resolve($name);
141
+            $this->registerService($name, function () use ($object) {
142
+                return $object;
143
+            });
144
+            return $object;
145
+        }
146
+
147
+        throw new QueryNotFoundException('Could not resolve ' . $name . '!');
148
+    }
149
+
150
+    /**
151
+     * @param string $name
152
+     * @param mixed $value
153
+     */
154
+    public function registerParameter($name, $value) {
155
+        $this[$name] = $value;
156
+    }
157
+
158
+    /**
159
+     * The given closure is call the first time the given service is queried.
160
+     * The closure has to return the instance for the given service.
161
+     * Created instance will be cached in case $shared is true.
162
+     *
163
+     * @param string $name name of the service to register another backend for
164
+     * @param Closure $closure the closure to be called on service creation
165
+     * @param bool $shared
166
+     */
167
+    public function registerService($name, Closure $closure, $shared = true) {
168
+        $wrapped = function () use ($closure) {
169
+            return $closure($this);
170
+        };
171
+        $name = $this->sanitizeName($name);
172
+        if (isset($this->container[$name])) {
173
+            unset($this->container[$name]);
174
+        }
175
+        if ($shared) {
176
+            $this->container[$name] = $wrapped;
177
+        } else {
178
+            $this->container[$name] = $this->container->factory($wrapped);
179
+        }
180
+    }
181
+
182
+    /**
183
+     * Shortcut for returning a service from a service under a different key,
184
+     * e.g. to tell the container to return a class when queried for an
185
+     * interface
186
+     * @param string $alias the alias that should be registered
187
+     * @param string $target the target that should be resolved instead
188
+     */
189
+    public function registerAlias($alias, $target): void {
190
+        $this->registerService($alias, function (ContainerInterface $container) use ($target): mixed {
191
+            return $container->get($target);
192
+        }, false);
193
+    }
194
+
195
+    protected function registerDeprecatedAlias(string $alias, string $target): void {
196
+        $this->registerService($alias, function (ContainerInterface $container) use ($target, $alias): mixed {
197
+            try {
198
+                $logger = $container->get(LoggerInterface::class);
199
+                $logger->debug('The requested alias "' . $alias . '" is deprecated. Please request "' . $target . '" directly. This alias will be removed in a future Nextcloud version.', [
200
+                    'app' => $this->appName ?? 'serverDI',
201
+                ]);
202
+            } catch (ContainerExceptionInterface $e) {
203
+                // Could not get logger. Continue
204
+            }
205
+
206
+            return $container->get($target);
207
+        }, false);
208
+    }
209
+
210
+    /**
211
+     * @param string $name
212
+     * @return string
213
+     */
214
+    protected function sanitizeName($name) {
215
+        if (isset($name[0]) && $name[0] === '\\') {
216
+            return ltrim($name, '\\');
217
+        }
218
+        return $name;
219
+    }
220
+
221
+    /**
222
+     * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::has
223
+     */
224
+    public function offsetExists($id): bool {
225
+        return $this->container->offsetExists($id);
226
+    }
227
+
228
+    /**
229
+     * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get
230
+     * @return mixed
231
+     */
232
+    #[\ReturnTypeWillChange]
233
+    public function offsetGet($id) {
234
+        return $this->container->offsetGet($id);
235
+    }
236
+
237
+    /**
238
+     * @deprecated 20.0.0 use \OCP\IContainer::registerService
239
+     */
240
+    public function offsetSet($offset, $value): void {
241
+        $this->container->offsetSet($offset, $value);
242
+    }
243
+
244
+    /**
245
+     * @deprecated 20.0.0
246
+     */
247
+    public function offsetUnset($offset): void {
248
+        $this->container->offsetUnset($offset);
249
+    }
250 250
 }
Please login to merge, or discard this patch.