diff options
Diffstat (limited to 'lib/private/AppFramework/Utility/SimpleContainer.php')
-rw-r--r-- | lib/private/AppFramework/Utility/SimpleContainer.php | 136 |
1 files changed, 81 insertions, 55 deletions
diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php index 2aa5da116e6..0db3bfc1c77 100644 --- a/lib/private/AppFramework/Utility/SimpleContainer.php +++ b/lib/private/AppFramework/Utility/SimpleContainer.php @@ -1,32 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\AppFramework\Utility; use ArrayAccess; @@ -34,9 +12,12 @@ 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; use ReflectionParameter; use function class_exists; @@ -44,66 +25,93 @@ use function class_exists; * SimpleContainer is a simple implementation of a container on basis of Pimple */ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { + public static bool $useLazyObjects = false; - /** @var Container */ - private $container; + private Container $container; public function __construct() { $this->container = new Container(); } - public function get($id) { + /** + * @template T + * @param class-string<T>|string $id + * @return T|mixed + * @psalm-template S as class-string<T>|string + * @psalm-param S $id + * @psalm-return (S is class-string<T> ? T : mixed) + */ + public function get(string $id): mixed { return $this->query($id); } - public function has($id): bool { + public function has(string $id): bool { // If a service is no registered but is an existing class, we can probably load it return isset($this->container[$id]) || class_exists($id); } /** * @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(); // try to find out if it is a class or a simple parameter - if ($parameterType !== null && !$parameterType->isBuiltin()) { + if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) { $resolveName = $parameterType->getName(); } try { - $builtIn = $parameter->hasType() && $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(); } - if ($parameterType !== null && !$parameterType->isBuiltin()) { + if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) { $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; + } + // don't lose the error we got while trying to query by type - throw new QueryException($e2->getMessage(), (int) $e2->getCode(), $e); + throw new QueryException($e->getMessage(), (int)$e->getCode(), $e); } } throw $e; } - }, $constructor->getParameters())); + }, $constructor->getParameters()); } public function resolve($name) { @@ -113,11 +121,12 @@ 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) { - throw new QueryException($baseMsg . ' ' . $e->getMessage()); + // Class does not exist + throw new QueryNotFoundException($baseMsg . ' ' . $e->getMessage()); } } @@ -135,7 +144,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { return $object; } - throw new QueryException('Could not resolve ' . $name . '!'); + throw new QueryNotFoundException('Could not resolve ' . $name . '!'); } /** @@ -160,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); } } @@ -177,13 +186,28 @@ 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' => $this->appName ?? 'serverDI', + ]); + } catch (ContainerExceptionInterface $e) { + // Could not get logger. Continue + } + + return $container->get($target); + }, false); + } + + /** * @param string $name * @return string */ @@ -197,13 +221,15 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { /** * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::has */ - public function offsetExists($id) { + public function offsetExists($id): bool { return $this->container->offsetExists($id); } /** * @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get + * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($id) { return $this->container->offsetGet($id); } @@ -211,14 +237,14 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { /** * @deprecated 20.0.0 use \OCP\IContainer::registerService */ - public function offsetSet($id, $service) { - $this->container->offsetSet($id, $service); + public function offsetSet($offset, $value): void { + $this->container->offsetSet($offset, $value); } /** * @deprecated 20.0.0 */ - public function offsetUnset($offset) { + public function offsetUnset($offset): void { $this->container->offsetUnset($offset); } } |