mirror of
https://github.com/nextcloud/server.git
synced 2026-06-08 16:26:59 -04:00
Merge pull request #52538 from nextcloud/feat/use-php84-lazy-objects
Use PHP 8.4 lazy ghosts for Dependency injection
This commit is contained in:
commit
231916d403
6 changed files with 51 additions and 55 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -2744,4 +2744,12 @@ $CONFIG = [
|
|||
* 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,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -618,6 +618,9 @@ class OC {
|
|||
}
|
||||
$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();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use Closure;
|
|||
use OCP\AppFramework\QueryException;
|
||||
use OCP\IContainer;
|
||||
use Pimple\Container;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
|
|
@ -23,8 +24,9 @@ use function class_exists;
|
|||
* SimpleContainer is a simple implementation of a container on basis of Pimple
|
||||
*/
|
||||
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
|
||||
/** @var Container */
|
||||
private $container;
|
||||
public static bool $useLazyObjects = false;
|
||||
|
||||
private Container $container;
|
||||
|
||||
public function __construct() {
|
||||
$this->container = new Container();
|
||||
|
|
@ -49,16 +51,29 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
|
|||
|
||||
/**
|
||||
* @param ReflectionClass $class the class to instantiate
|
||||
* @return \stdClass the created class
|
||||
* @return object the created class
|
||||
* @suppress PhanUndeclaredClassInstanceof
|
||||
*/
|
||||
private function buildClass(ReflectionClass $class) {
|
||||
private function buildClass(ReflectionClass $class): object {
|
||||
$constructor = $class->getConstructor();
|
||||
if ($constructor === null) {
|
||||
/* No constructor, return a instance directly */
|
||||
return $class->newInstance();
|
||||
}
|
||||
if (PHP_VERSION_ID >= 80400 && self::$useLazyObjects) {
|
||||
/* For PHP>=8.4, use a lazy ghost to delay constructor and dependency resolving */
|
||||
/** @psalm-suppress UndefinedMethod */
|
||||
return $class->newLazyGhost(function (object $object) use ($constructor): void {
|
||||
/** @psalm-suppress DirectConstructorCall For lazy ghosts we have to call the constructor directly */
|
||||
$object->__construct(...$this->buildClassConstructorParameters($constructor));
|
||||
});
|
||||
} else {
|
||||
return $class->newInstanceArgs($this->buildClassConstructorParameters($constructor));
|
||||
}
|
||||
}
|
||||
|
||||
return $class->newInstanceArgs(array_map(function (ReflectionParameter $parameter) {
|
||||
private function buildClassConstructorParameters(\ReflectionMethod $constructor): array {
|
||||
return array_map(function (ReflectionParameter $parameter) {
|
||||
$parameterType = $parameter->getType();
|
||||
|
||||
$resolveName = $parameter->getName();
|
||||
|
|
@ -69,10 +84,10 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
|
|||
}
|
||||
|
||||
try {
|
||||
$builtIn = $parameter->hasType() && ($parameter->getType() instanceof ReflectionNamedType)
|
||||
&& $parameter->getType()->isBuiltin();
|
||||
$builtIn = $parameterType !== null && ($parameterType instanceof ReflectionNamedType)
|
||||
&& $parameterType->isBuiltin();
|
||||
return $this->query($resolveName, !$builtIn);
|
||||
} catch (QueryException $e) {
|
||||
} catch (ContainerExceptionInterface $e) {
|
||||
// Service not found, use the default value when available
|
||||
if ($parameter->isDefaultValueAvailable()) {
|
||||
return $parameter->getDefaultValue();
|
||||
|
|
@ -82,7 +97,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
|
|||
$resolveName = $parameter->getName();
|
||||
try {
|
||||
return $this->query($resolveName);
|
||||
} catch (QueryException $e2) {
|
||||
} catch (ContainerExceptionInterface $e2) {
|
||||
// Pass null if typed and nullable
|
||||
if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
|
||||
return null;
|
||||
|
|
@ -95,7 +110,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
|
|||
|
||||
throw $e;
|
||||
}
|
||||
}, $constructor->getParameters()));
|
||||
}, $constructor->getParameters());
|
||||
}
|
||||
|
||||
public function resolve($name) {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,12 @@ class App {
|
|||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ class ClassComplexConstructor {
|
|||
}
|
||||
|
||||
class ClassNullableUntypedConstructorArg {
|
||||
public $class;
|
||||
public function __construct($class) {
|
||||
$this->class = $class;
|
||||
}
|
||||
}
|
||||
class ClassNullableTypedConstructorArg {
|
||||
|
|
@ -217,6 +219,8 @@ class SimpleContainerTest extends \Test\TestCase {
|
|||
$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 {
|
||||
|
|
@ -243,7 +247,11 @@ class SimpleContainerTest extends \Test\TestCase {
|
|||
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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue