Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 0 additions & 44 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3450,53 +3450,9 @@
</MoreSpecificImplementedParamType>
</file>
<file src="lib/private/AppFramework/Utility/SimpleContainer.php">
<LessSpecificReturnStatement>
<code><![CDATA[$class->newInstance()]]></code>
<code><![CDATA[$class->newInstanceArgs(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 instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
$resolveName = $parameterType->getName();
}

try {
$builtIn = $parameter->hasType() && ($parameter->getType() instanceof ReflectionNamedType)
&& $parameter->getType()->isBuiltin();
return $this->query($resolveName, !$builtIn);
} catch (QueryException $e) {
// Service not found, use the default value when available
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
}

if ($parameterType !== null && ($parameterType instanceof ReflectionNamedType) && !$parameterType->isBuiltin()) {
$resolveName = $parameter->getName();
try {
return $this->query($resolveName);
} catch (QueryException $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($e->getMessage(), (int)$e->getCode(), $e);
}
}

throw $e;
}
}, $constructor->getParameters()))]]></code>
</LessSpecificReturnStatement>
<MissingTemplateParam>
<code><![CDATA[ArrayAccess]]></code>
</MissingTemplateParam>
<MoreSpecificReturnType>
<code><![CDATA[\stdClass]]></code>
</MoreSpecificReturnType>
<RedundantCast>
<code><![CDATA[(int)$e->getCode()]]></code>
</RedundantCast>
Expand Down
8 changes: 8 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -2743,4 +2743,12 @@
* Defaults to true.
*/
'files.trash.delete' => true,

/**
* Enable lazy objects feature from PHP 8.4 to be used in the Dependency Injection.
* Should improve performances by avoiding buiding unused objects.
*
* Defaults to true.
*/
'enable_lazy_objects' => true,
];
3 changes: 3 additions & 0 deletions lib/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,9 @@ public static function init(): void {
}
$loaderEnd = microtime(true);

// Enable lazy loading if activated
\OC\AppFramework\Utility\SimpleContainer::$useLazyObjects = (bool)self::$config->getValue('enable_lazy_objects', true);

// setup the basic server
self::$server = new \OC\Server(\OC::$WEBROOT, self::$config);
self::$server->boot();
Expand Down
35 changes: 25 additions & 10 deletions lib/private/AppFramework/Utility/SimpleContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use OCP\AppFramework\QueryException;
use OCP\IContainer;
use Pimple\Container;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionException;
Expand All @@ -23,8 +24,9 @@
* 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();
Expand All @@ -49,16 +51,29 @@ public function has(string $id): bool {

/**
* @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();
Expand All @@ -69,10 +84,10 @@ private function buildClass(ReflectionClass $class) {
}

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();
Expand All @@ -82,7 +97,7 @@ private function buildClass(ReflectionClass $class) {
$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;
Expand All @@ -95,7 +110,7 @@ private function buildClass(ReflectionClass $class) {

throw $e;
}
}, $constructor->getParameters()));
}, $constructor->getParameters());
}

public function resolve($name) {
Expand Down
6 changes: 6 additions & 0 deletions lib/public/AppFramework/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ public function __construct(string $appName, array $urlParams = []) {
$step['args'][1] === $classNameParts[1]) {
$setUpViaQuery = true;
break;
} elseif (isset($step['class'], $step['function'], $step['args'][0]) &&
$step['class'] === \ReflectionClass::class &&
$step['function'] === 'initializeLazyObject' &&
$step['args'][0] === $this) {
$setUpViaQuery = true;
break;
}
}

Expand Down
10 changes: 9 additions & 1 deletion tests/lib/AppFramework/Utility/SimpleContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ public function __construct(ClassSimpleConstructor $class, $test) {
}

class ClassNullableUntypedConstructorArg {
public $class;
public function __construct($class) {
$this->class = $class;
}
}
class ClassNullableTypedConstructorArg {
Expand Down Expand Up @@ -217,6 +219,8 @@ public function testConstructorComplexNoTestParameterFound(): void {
$object = $this->container->query(
'Test\AppFramework\Utility\ClassComplexConstructor'
);
/* Use the object to trigger DI on PHP >= 8.4 */
get_object_vars($object);
}

public function testRegisterFactory(): void {
Expand All @@ -243,7 +247,11 @@ public function testRegisterAliasFactory(): void {
public function testQueryUntypedNullable(): void {
$this->expectException(\OCP\AppFramework\QueryException::class);

$this->container->query(ClassNullableUntypedConstructorArg::class);
$object = $this->container->query(
ClassNullableUntypedConstructorArg::class
);
/* Use the object to trigger DI on PHP >= 8.4 */
get_object_vars($object);
}

public function testQueryTypedNullable(): void {
Expand Down
Loading