Compare commits

...

17 Commits

Author SHA1 Message Date
Robin Appelman 4bdc0751c0 fix: use array_key_exists instead of isset in di container
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:06 +02:00
Robin Appelman bb632f0cfa chore: rebuild autoloader
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:06 +02:00
Robin Appelman 38673662d8 fix: handle aliases in SimpleContainer::has
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:05 +02:00
Robin Appelman e6931e11e5 fix: don't delegate to app container if the server container has it
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:05 +02:00
Robin Appelman f9dc997403 fix: allow setting appName in container
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:04 +02:00
Robin Appelman 4e56211a6c DIContainer: don't go trough server getter in query
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:04 +02:00
Robin Appelman 5dcdafc616 fix SimpleConainer::isset with aliases
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:03 +02:00
Robin Appelman 03c90f66ae fix phpdoc template
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:02 +02:00
Robin Appelman 274313f8ca optimize delegation from app to server container a bit
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:01 +02:00
Robin Appelman b8ff2ffe8a cache di container namespace
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:12:00 +02:00
Robin Appelman b70a3e6f34 going nuclear: ripping out pimple
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 16:11:56 +02:00
Robin Appelman c3a96b9790 re-setup aliases when an alias'd service is overwritten
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 14:13:31 +02:00
Robin Appelman 3f487633d5 remove duplicate OCA query handling and add earlier short cirtcuit for when instances are already created
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 14:13:09 +02:00
Robin Appelman 8f4a19ccab minor optimization for SimpleContainer::sanitizeName
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 14:12:11 +02:00
Robin Appelman 83d74782df dont register aliases as factories as that block some optimizations
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 14:12:10 +02:00
Robin Appelman d9351b8fd0 remove unneeded isset when registering a service
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 14:12:07 +02:00
Robin Appelman 1b6be5f081 register resolved class as raw instead of closure
Signed-off-by: Robin Appelman <robin@icewind.nl>
2025-06-24 14:11:06 +02:00
7 changed files with 124 additions and 65 deletions
@@ -1045,6 +1045,7 @@ return array(
'OC\\AppFramework\\Services\\InitialState' => $baseDir . '/lib/private/AppFramework/Services/InitialState.php',
'OC\\AppFramework\\Utility\\ControllerMethodReflector' => $baseDir . '/lib/private/AppFramework/Utility/ControllerMethodReflector.php',
'OC\\AppFramework\\Utility\\QueryNotFoundException' => $baseDir . '/lib/private/AppFramework/Utility/QueryNotFoundException.php',
'OC\\AppFramework\\Utility\\ServiceFactory' => $baseDir . '/lib/private/AppFramework/Utility/ServiceFactory.php',
'OC\\AppFramework\\Utility\\SimpleContainer' => $baseDir . '/lib/private/AppFramework/Utility/SimpleContainer.php',
'OC\\AppFramework\\Utility\\TimeFactory' => $baseDir . '/lib/private/AppFramework/Utility/TimeFactory.php',
'OC\\AppScriptDependency' => $baseDir . '/lib/private/AppScriptDependency.php',
@@ -1086,6 +1086,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\AppFramework\\Services\\InitialState' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/InitialState.php',
'OC\\AppFramework\\Utility\\ControllerMethodReflector' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/ControllerMethodReflector.php',
'OC\\AppFramework\\Utility\\QueryNotFoundException' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/QueryNotFoundException.php',
'OC\\AppFramework\\Utility\\ServiceFactory' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/ServiceFactory.php',
'OC\\AppFramework\\Utility\\SimpleContainer' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/SimpleContainer.php',
'OC\\AppFramework\\Utility\\TimeFactory' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/TimeFactory.php',
'OC\\AppScriptDependency' => __DIR__ . '/../../..' . '/lib/private/AppScriptDependency.php',
+2 -2
View File
@@ -89,7 +89,7 @@ class App {
* @param string $controllerName the name of the controller under which it is
* stored in the DI container
* @param string $methodName the method that you want to call
* @param DIContainer $container an instance of a pimple container.
* @param DIContainer $container an instance of a container.
* @param array $urlParams list of URL parameters (optional)
* @throws HintException
*/
@@ -231,7 +231,7 @@ class App {
* stored in the DI container
* @param string $methodName the method that you want to call
* @param array $urlParams an array with variables extracted from the routes
* @param DIContainer $container an instance of a pimple container.
* @param DIContainer $container an instance of container.
*/
public static function part(string $controllerName, string $methodName, array $urlParams,
DIContainer $container) {
@@ -7,6 +7,7 @@
namespace OC\AppFramework\DependencyInjection;
use OC;
use OC\AppFramework\App;
use OC\AppFramework\Http;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Output;
@@ -57,6 +58,7 @@ use Psr\Log\LoggerInterface;
*/
class DIContainer extends SimpleContainer implements IAppContainer {
private string $appName;
private string $nameSpace;
/**
* @var array
@@ -73,8 +75,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
* @param ServerContainer|null $server
*/
public function __construct(string $appName, array $urlParams = [], ?ServerContainer $server = null) {
parent::__construct();
$this->appName = $appName;
$this->nameSpace = App::buildAppNamespace($this->appName);
$this['appName'] = $appName;
$this['urlParams'] = $urlParams;
@@ -398,21 +400,30 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return false;
}
public function offsetSet($offset, $value): void {
if ($offset === 'AppName' || $offset === 'appName') {
$this->appName = $value;
}
$this->items[$offset] = $value;
}
public function query(string $name, bool $autoload = true) {
if ($name === 'AppName' || $name === 'appName') {
return $this->appName;
}
$name = $this->sanitizeName($name);
$name = $this->resolveAlias($name);
$isServerClass = str_starts_with($name, 'OCP\\') || str_starts_with($name, 'OC\\');
if ($isServerClass && !$this->has($name)) {
return $this->getServer()->query($name, $autoload);
return $this->server->queryNoApps($name, $autoload);
}
try {
return $this->queryNoFallback($name);
} catch (QueryException $firstException) {
try {
return $this->getServer()->query($name, $autoload);
return $this->server->query($name, $autoload);
} catch (QueryException $secondException) {
if ($firstException->getCode() === 1) {
throw $secondException;
@@ -427,20 +438,22 @@ class DIContainer extends SimpleContainer implements IAppContainer {
* @return mixed
* @throws QueryException if the query could not be resolved
*/
public function queryNoFallback($name) {
$name = $this->sanitizeName($name);
public function queryNoFallback(string $name) {
if ($this->offsetExists($name)) {
return parent::query($name);
} elseif ($this->appName === 'settings' && str_starts_with($name, 'OC\\Settings\\')) {
return parent::query($name);
} elseif ($this->appName === 'core' && str_starts_with($name, 'OC\\Core\\')) {
return parent::query($name);
} elseif (str_starts_with($name, \OC\AppFramework\App::buildAppNamespace($this->appName) . '\\')) {
} elseif (str_starts_with($name, $this->nameSpace)) {
return parent::query($name);
}
throw new QueryException('Could not resolve ' . $name . '!' .
' Class can not be instantiated', 1);
}
protected function resolveAlias(string $name): string {
return parent::resolveAlias($this->server->resolveAlias($name));
}
}
@@ -0,0 +1,19 @@
<?php
namespace OC\AppFramework\Utility;
use Psr\Container\ContainerInterface;
class ServiceFactory {
private $factory;
private ContainerInterface $container;
public function __construct(ContainerInterface $container, callable $factory) {
$this->container = $container;
$this->factory = $factory;
}
public function get() {
return ($this->factory)($this->container);
}
}
@@ -11,7 +11,6 @@ use ArrayAccess;
use Closure;
use OCP\AppFramework\QueryException;
use OCP\IContainer;
use Pimple\Container;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use ReflectionClass;
@@ -21,16 +20,12 @@ use ReflectionParameter;
use function class_exists;
/**
* SimpleContainer is a simple implementation of a container on basis of Pimple
* SimpleContainer is a simple implementation of a container
*/
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
public static bool $useLazyObjects = false;
private Container $container;
public function __construct() {
$this->container = new Container();
}
protected array $items = [];
protected array $aliases = [];
/**
* @template T
@@ -46,12 +41,13 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
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);
return array_key_exists($id, $this->items) || array_key_exists($id, $this->aliases) || class_exists($id);
}
/**
* @param ReflectionClass $class the class to instantiate
* @return object the created class
* @template T of object
* @param ReflectionClass<T> $class the class to instantiate
* @return T the created class
* @suppress PhanUndeclaredClassInstanceof
*/
private function buildClass(ReflectionClass $class): object {
@@ -79,7 +75,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
$resolveName = $parameter->getName();
// try to find out if it is a class or a simple parameter
if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
if (($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
$resolveName = $parameterType->getName();
}
@@ -93,7 +89,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
return $parameter->getDefaultValue();
}
if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
if (($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
$resolveName = $parameter->getName();
try {
return $this->query($resolveName);
@@ -131,19 +127,25 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
public function query(string $name, bool $autoload = true) {
$name = $this->sanitizeName($name);
if (isset($this->container[$name])) {
return $this->container[$name];
$name = $this->resolveAlias($name);
if (array_key_exists($name, $this->items)) {
$item = $this->items[$name];
if ($item instanceof ServiceFactory) {
return $item->get();
} elseif (is_callable($item)) {
$this->items[$name] = $item($this);
}
return $this->items[$name];
}
if ($autoload) {
$object = $this->resolve($name);
$this->registerService($name, function () use ($object) {
return $object;
});
$this->items[$name] = $object;
return $object;
}
throw new QueryNotFoundException('Could not resolve ' . $name . '!');
throw new QueryNotFoundException('Could not resolve ' . $name . '!' . get_class($this));
}
/**
@@ -151,7 +153,8 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
* @param mixed $value
*/
public function registerParameter($name, $value) {
$this[$name] = $value;
$this->items[$name] = $value;
unset($this->aliases[$name]);
}
/**
@@ -164,17 +167,12 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
* @param bool $shared
*/
public function registerService($name, Closure $closure, $shared = true) {
$wrapped = function () use ($closure) {
return $closure($this);
};
$name = $this->sanitizeName($name);
if (isset($this->container[$name])) {
unset($this->container[$name]);
}
unset($this->aliases[$name]);
if ($shared) {
$this->container[$name] = $wrapped;
$this->items[$name] = $closure;
} else {
$this->container[$name] = $this->container->factory($wrapped);
$this->items[$name] = new ServiceFactory($this, $closure);
}
}
@@ -186,9 +184,13 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
* @param string $target the target that should be resolved instead
*/
public function registerAlias($alias, $target) {
$this->registerService($alias, function (ContainerInterface $container) use ($target) {
return $container->get($target);
}, false);
$alias = $this->sanitizeName($alias);
$target = $this->sanitizeName($target);
if ($alias === $target) {
throw new QueryNotFoundException('Can\'t alias to self');
}
unset($this->items[$alias]);
$this->aliases[$alias] = $target;
}
/**
@@ -196,17 +198,14 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
* @return string
*/
protected function sanitizeName($name) {
if (isset($name[0]) && $name[0] === '\\') {
return ltrim($name, '\\');
}
return $name;
return ltrim($name, '\\');
}
/**
* @deprecated 20.0.0 use \Psr\Container\ContainerInterface::has
*/
public function offsetExists($id): bool {
return $this->container->offsetExists($id);
return array_key_exists($id, $this->items) || array_key_exists($id, $this->aliases);
}
/**
@@ -215,20 +214,43 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
*/
#[\ReturnTypeWillChange]
public function offsetGet($id) {
return $this->container->offsetGet($id);
return $this->query($id);
}
/**
* @deprecated 20.0.0 use \OCP\IContainer::registerService
*/
public function offsetSet($offset, $value): void {
$this->container->offsetSet($offset, $value);
$this->items[$offset] = $value;
}
/**
* @deprecated 20.0.0
*/
public function offsetUnset($offset): void {
$this->container->offsetUnset($offset);
unset($this->items[$offset]);
unset($this->aliases[$offset]);
}
/**
* Check if we already have a resolved instance of $name
*/
public function isResolved($name): bool {
if (!array_key_exists($name, $this->items)) {
return false;
}
$item = $this->items[$name];
if ($item instanceof ServiceFactory) {
return false;
} else {
return !is_callable($item);
}
}
protected function resolveAlias(string $name): string {
while (array_key_exists($name, $this->aliases)) {
$name = $this->aliases[$name];
}
return $name;
}
}
+20 -17
View File
@@ -34,7 +34,6 @@ class ServerContainer extends SimpleContainer {
* ServerContainer constructor.
*/
public function __construct() {
parent::__construct();
$this->appContainers = [];
$this->namespaces = [];
$this->hasNoAppContainer = [];
@@ -119,26 +118,23 @@ class ServerContainer extends SimpleContainer {
* @deprecated 20.0.0 use \Psr\Container\ContainerInterface::get
*/
public function query(string $name, bool $autoload = true) {
// if we have a resolved instance ourselves, there is no need to try and delegate
// we check this before doing any sanitization, because usually the name already is sanitized
if ($this->isResolved($name)) {
return $this->items[$name];
}
$name = $this->sanitizeName($name);
if (str_starts_with($name, 'OCA\\')) {
// Skip server container query for app namespace classes
// In case the service starts with OCA\ we try to find the service in
// the apps container first.
if (!array_key_exists($name, $this->items) && !array_key_exists($name, $this->aliases) && ($appContainer = $this->getAppContainerForService($name)) !== null) {
try {
return parent::query($name, false);
return $appContainer->queryNoFallback($name);
} catch (QueryException $e) {
// Continue with general autoloading then
}
// In case the service starts with OCA\ we try to find the service in
// the apps container first.
if (($appContainer = $this->getAppContainerForService($name)) !== null) {
try {
return $appContainer->queryNoFallback($name);
} catch (QueryException $e) {
// Didn't find the service or the respective app container
// In this case the service won't be part of the core container,
// so we can throw directly
throw $e;
}
// Didn't find the service or the respective app container
// In this case the service won't be part of the core container,
// so we can throw directly
throw $e;
}
} elseif (str_starts_with($name, 'OC\\Settings\\') && substr_count($name, '\\') >= 3) {
$segments = explode('\\', $name);
@@ -154,6 +150,13 @@ class ServerContainer extends SimpleContainer {
return parent::query($name, $autoload);
}
public function queryNoApps(string $name, bool $autoload = true) {
if ($this->isResolved($name)) {
return $this->items[$name];
}
return parent::query($name, $autoload);
}
/**
* @internal
* @param string $id