Skip to content

Commit ec8479d

Browse files
mficzelBernhard Schmitt
andcommitted
Adjust Neos.Flow.ObjectManagement subcontext
Co-authored-by: Bernhard Schmitt <schmitt@sitegeist.de>
1 parent 33e8ef2 commit ec8479d

15 files changed

Lines changed: 249 additions & 113 deletions

Neos.Flow/Classes/ObjectManagement/CompileTimeObjectManager.php

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
use Neos\Flow\ObjectManagement\Exception\InvalidObjectConfigurationException;
2525
use Neos\Flow\ObjectManagement\Exception\UnknownObjectException;
2626
use Neos\Flow\ObjectManagement\Exception\WrongScopeException;
27+
use Neos\Flow\Package;
2728
use Neos\Flow\Package\FlowPackageInterface;
2829
use Neos\Flow\Package\PackageInterface;
30+
use Neos\Flow\Package\PackageKeyAwareInterface;
2931
use Neos\Flow\Reflection\ReflectionService;
3032
use Psr\Log\LoggerInterface;
3133

@@ -34,6 +36,17 @@
3436
* singleton scoped objects. This Object Manager is used during compile time when the proxy
3537
* class based DI mechanism is not yet available.
3638
*
39+
* @template ObjectRecordInstance of object
40+
* @extends ObjectManager<ObjectRecordInstance>
41+
* @phpstan-type CompileTimeObjectRecordShape array{
42+
* l: string,
43+
* s: int,
44+
* p: string,
45+
* c?: class-string,
46+
* ca?: array<int, null|array{t:int, v:mixed, wm:int}>,
47+
* f?: array{string, string},
48+
* fa?: array<int, null|array{t:int, v:mixed}>
49+
* }
3750
* @Flow\Scope("singleton")
3851
* @Flow\Proxy(false)
3952
*/
@@ -60,24 +73,24 @@ class CompileTimeObjectManager extends ObjectManager
6073
protected ?LoggerInterface $logger;
6174

6275
/**
63-
* @var array
76+
* @var array<class-string,Configuration>
6477
*/
6578
protected array $objectConfigurations = [];
6679

6780
/**
6881
* A list of all class names known to the Object Manager
6982
*
70-
* @var array
83+
* @var array<string,array<int,class-string>>
7184
*/
7285
protected array $registeredClassNames = [];
7386

7487
/**
75-
* @var array
88+
* @var array<string>
7689
*/
7790
protected array $objectNameBuildStack = [];
7891

7992
/**
80-
* @var array
93+
* @var array<int,array<int,class-string>>
8194
*/
8295
protected array $cachedClassNamesByScope = [];
8396

@@ -124,18 +137,47 @@ public function injectLogger(LoggerInterface $logger): void
124137
/**
125138
* Initializes the object configurations and some other parts of this Object Manager.
126139
*
127-
* @param PackageInterface[] $packages An array of active packages to consider
140+
* @param array<string, PackageInterface&PackageKeyAwareInterface> $packages An array of active packages to consider
128141
* @return void
129142
* @throws InvalidConfigurationTypeException
130143
* @throws InvalidObjectConfigurationException
131144
* @throws CacheException
132145
*/
133146
public function initialize(array $packages): void
134147
{
148+
if ($this->reflectionService === null || $this->configurationManager === null || $this->logger === null) {
149+
$missingDependencies = [];
150+
if ($this->reflectionService === null) {
151+
$missingDependencies[] = 'reflectionService';
152+
}
153+
if ($this->configurationManager === null) {
154+
$missingDependencies[] = 'configurationManager';
155+
}
156+
if ($this->logger === null) {
157+
$missingDependencies[] = 'logger';
158+
}
159+
throw new \Exception(sprintf('%s is missing dependencies: %s', self::class, implode(', ', $missingDependencies)), 1784221618);
160+
}
135161
$this->registeredClassNames = $this->registerClassFiles($packages);
136162
$this->reflectionService->buildReflectionData($this->registeredClassNames);
137163

138-
$rawCustomObjectConfigurations = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_OBJECTS);
164+
/**
165+
* @var array<string,array<string,array{
166+
* className?: class-string,
167+
* scope?: string,
168+
* factoryObjectName?: class-string,
169+
* factoryMethodName?: string,
170+
* arguments?: array<mixed>,
171+
* properties?: array<string,array{
172+
* object?: class-string|array{
173+
* factoryObjectName: class-string,
174+
* factoryMethodName?: string,
175+
* arguments?: array<mixed>
176+
* }
177+
* }>,
178+
* }>> $rawCustomObjectConfigurations
179+
*/
180+
$rawCustomObjectConfigurations = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_OBJECTS) ?: [];
139181

140182
$configurationBuilder = new ConfigurationBuilder(
141183
$this->reflectionService,
@@ -174,7 +216,7 @@ public function setInstance($objectName, $instance): void
174216
/**
175217
* Returns a list of all class names, grouped by package key, which were registered by registerClassFiles()
176218
*
177-
* @return array
219+
* @return array<string,array<int,class-string>>
178220
*/
179221
public function getRegisteredClassNames(): array
180222
{
@@ -185,7 +227,7 @@ public function getRegisteredClassNames(): array
185227
* Returns a list of class names, which are configured with the given scope
186228
*
187229
* @param integer $scope One of the ObjectConfiguration::SCOPE_ constants
188-
* @return array An array of class names configured with the given scope
230+
* @return array<int,class-string> An array of class names configured with the given scope
189231
*/
190232
public function getClassNamesByScope(int $scope): array
191233
{
@@ -194,7 +236,7 @@ public function getClassNamesByScope(int $scope): array
194236
if ($information[self::KEY_SCOPE] === $scope) {
195237
if (isset($information[self::KEY_CLASS_NAME])) {
196238
$this->cachedClassNamesByScope[$scope][] = $information[self::KEY_CLASS_NAME];
197-
} else {
239+
} elseif (is_string($objectName) && class_exists($objectName)) {
198240
$this->cachedClassNamesByScope[$scope][] = $objectName;
199241
}
200242
}
@@ -210,14 +252,14 @@ public function getClassNamesByScope(int $scope): array
210252
*
211253
* For performance reasons this function ignores classes whose name ends with "Exception".
212254
*
213-
* @param array $packages A list of packages to consider
214-
* @return array A list of class names which were discovered in the given packages
255+
* @param array<string, PackageInterface&PackageKeyAwareInterface> $packages A list of packages to consider
256+
* @return array<string, array<class-string>> A list of class names which were discovered in the given packages
215257
*
216258
* @throws InvalidConfigurationTypeException
217259
*/
218260
protected function registerClassFiles(array $packages): array
219261
{
220-
$allSettings = $this->configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS);
262+
$allSettings = $this->configurationManager?->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS) ?? [];
221263
$includeClassesConfiguration = [];
222264
if (isset($allSettings['Neos']['Flow']['object']['includeClasses'])) {
223265
if (!is_array($allSettings['Neos']['Flow']['object']['includeClasses'])) {
@@ -246,7 +288,7 @@ protected function registerClassFiles(array $packages): array
246288
}
247289
}
248290
}
249-
if (isset($availableClassNames[$packageKey]) && is_array($availableClassNames[$packageKey])) {
291+
if (isset($availableClassNames[$packageKey])) {
250292
$availableClassNames[$packageKey] = array_unique($availableClassNames[$packageKey]);
251293
}
252294
}
@@ -258,9 +300,9 @@ protected function registerClassFiles(array $packages): array
258300
* Given an array of class names by package key this filters out classes that
259301
* have been configured to be included by object management.
260302
*
261-
* @param array $classNames 2-level array - key of first level is package key, value of second level is classname (FQN)
262-
* @param array $includeClassesConfiguration array of includeClasses configurations
263-
* @return array The input array with all configured to be included in object management added in
303+
* @param array<string, array<class-string>> $classNames 2-level array - key of first level is package key, value of second level is classname (FQN)
304+
* @param array<string, array<string>> $includeClassesConfiguration array of includeClasses configurations
305+
* @return array<string, array<class-string>> The input array with all configured to be included in object management added in
264306
* @throws InvalidConfigurationTypeException
265307
*/
266308
protected function filterClassNamesFromConfiguration(array $classNames, array $includeClassesConfiguration): array
@@ -271,16 +313,16 @@ protected function filterClassNamesFromConfiguration(array $classNames, array $i
271313
/**
272314
* Filters the classnames available for object management by filter expressions that includes classes.
273315
*
274-
* @param array $classNames All classnames per package
275-
* @param array $filterConfiguration The filter configuration to apply
276-
* @return array the remaining class
316+
* @param array<string, array<class-string>> $classNames All classnames per package
317+
* @param array<string, array<string>> $filterConfiguration The filter configuration to apply
318+
* @return array<string, array<class-string>> the remaining class
277319
* @throws InvalidConfigurationTypeException
278320
*/
279321
protected function applyClassFilterConfiguration(array $classNames, array $filterConfiguration): array
280322
{
281323
foreach ($filterConfiguration as $packageKey => $filterExpressions) {
282324
if (!array_key_exists($packageKey, $classNames)) {
283-
$this->logger->debug('The package "' . $packageKey . '" specified in the setting "Neos.Flow.object.includeClasses" was either excluded or is not loaded.');
325+
$this->logger?->debug('The package "' . $packageKey . '" specified in the setting "Neos.Flow.object.includeClasses" was either excluded or is not loaded.');
284326
continue;
285327
}
286328
if (!is_array($filterExpressions)) {
@@ -314,7 +356,7 @@ static function ($className) use ($filterExpression) {
314356
* Builds the objects array which contains information about the registered objects,
315357
* their scope, class, built method etc.
316358
*
317-
* @return array
359+
* @return array<string, CompileTimeObjectRecordShape>
318360
* @throws CacheException
319361
*/
320362
protected function buildObjectsArray(): array
@@ -341,10 +383,15 @@ protected function buildObjectsArray(): array
341383
$factoryMethodArguments = $objectConfiguration->getFactoryArguments();
342384
if (count($factoryMethodArguments) > 0) {
343385
foreach ($factoryMethodArguments as $index => $argument) {
344-
$objects[$objectName][self::KEY_FACTORY_ARGUMENTS][$index] = [
345-
self::KEY_ARGUMENT_TYPE => $argument->getType(),
346-
self::KEY_ARGUMENT_VALUE => $argument->getValue()
347-
];
386+
if ($argument === null) {
387+
$objects[$objectName][self::KEY_FACTORY_ARGUMENTS][$index] = null;
388+
continue;
389+
} else {
390+
$objects[$objectName][self::KEY_FACTORY_ARGUMENTS][$index] = [
391+
self::KEY_ARGUMENT_TYPE => $argument->getType(),
392+
self::KEY_ARGUMENT_VALUE => $argument->getValue()
393+
];
394+
}
348395
}
349396
}
350397
}
@@ -363,14 +410,14 @@ protected function buildObjectsArray(): array
363410
}
364411
}
365412
}
366-
$this->configurationCache->set('objects', $objects);
413+
$this->configurationCache?->set('objects', $objects);
367414
return $objects;
368415
}
369416

370417
/**
371418
* Returns object configurations which were previously built by the ConfigurationBuilder.
372419
*
373-
* @return array
420+
* @return array<class-string, Configuration>
374421
*/
375422
public function getObjectConfigurations(): array
376423
{
@@ -422,7 +469,7 @@ public function get($objectName, ...$constructorArguments): object
422469
break;
423470
case Property::PROPERTY_TYPES_CONFIGURATION:
424471
$propertyValue = $property->getValue();
425-
$value = $this->configurationManager->getConfiguration($propertyValue['type'], $propertyValue['path']);
472+
$value = $this->configurationManager?->getConfiguration($propertyValue['type'], $propertyValue['path']);
426473
break;
427474
case Property::PROPERTY_TYPES_OBJECT:
428475
$propertyObjectName = $property->getValue();

Neos.Flow/Classes/ObjectManagement/Configuration/Configuration.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public function getPackageKey(): string
190190
/**
191191
* Sets the class name of a factory which is in charge of instantiating this object
192192
*
193-
* @param class-string $objectName Valid object name of a factory
193+
* @param string $objectName Valid object name of a factory
194194
* @return void
195195
*/
196196
public function setFactoryObjectName(string $objectName): void
@@ -206,7 +206,7 @@ public function setFactoryObjectName(string $objectName): void
206206
/**
207207
* Returns the class name of the factory for this object, if any
208208
*
209-
* @return class-string The factory class name
209+
* @return string The factory class name
210210
*/
211211
public function getFactoryObjectName(): string
212212
{
@@ -370,15 +370,17 @@ public function setProperty(ConfigurationProperty $property): void
370370
* Setter function for injection constructor arguments. If an empty array is passed to this
371371
* method, all (possibly) defined constructor arguments are removed from the configuration.
372372
*
373-
* @param array<ConfigurationArgument> $arguments
373+
* @param array<?ConfigurationArgument> $arguments
374374
* @throws InvalidConfigurationException
375375
* @return void
376376
*/
377377
public function setArguments(array $arguments): void
378378
{
379379
$this->arguments = [];
380380
foreach ($arguments as $argument) {
381-
$this->setArgument($argument);
381+
if ($argument instanceof ConfigurationArgument) {
382+
$this->setArgument($argument);
383+
}
382384
}
383385
}
384386

@@ -396,11 +398,11 @@ public function setArgument(ConfigurationArgument $argument): void
396398
/**
397399
* Returns a sorted array of constructor arguments indexed by position (starting with "1")
398400
*
399-
* @return array<ConfigurationArgument> A sorted array of ConfigurationArgument objects with the argument position as index
401+
* @return array<int, ?ConfigurationArgument> A sorted array of ConfigurationArgument objects with the argument position as index
400402
*/
401403
public function getArguments(): array
402404
{
403-
if (count($this->arguments) < 1) {
405+
if (empty($this->arguments)) {
404406
return [];
405407
}
406408

@@ -428,7 +430,7 @@ public function setFactoryArgument(ConfigurationArgument $argument): void
428430
/**
429431
* Returns a sorted array of factory method arguments indexed by position (starting with "1")
430432
*
431-
* @return array<ConfigurationArgument> A sorted array of ConfigurationArgument objects with the argument position as index
433+
* @return array<int, ?ConfigurationArgument> A sorted array of ConfigurationArgument objects with the argument position as index
432434
*/
433435
public function getFactoryArguments(): array
434436
{

Neos.Flow/Classes/ObjectManagement/Configuration/ConfigurationBuilder.php

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,21 @@ public function __construct(
5757
* into the overall configuration. Finally autowires dependencies of arguments and properties
5858
* which can be resolved automatically.
5959
*
60-
* @param array<string, array<string>> $availableClassAndInterfaceNamesByPackage An array of available class names, grouped by package key
61-
* @param array $rawObjectConfigurationsByPackages An array of package keys and their raw (ie. unparsed) object configurations
60+
* @param array<string,array<int,class-string>> $availableClassAndInterfaceNamesByPackage An array of available class names, grouped by package key
61+
* @param array<string,array<string,array{
62+
* className?: class-string,
63+
* scope?: string,
64+
* factoryObjectName?: class-string,
65+
* factoryMethodName?: string,
66+
* arguments?: array<mixed>,
67+
* properties?: array<string,array{
68+
* object?: class-string|array{
69+
* factoryObjectName: class-string,
70+
* factoryMethodName?: string,
71+
* arguments?: array<mixed>
72+
* }
73+
* }>,
74+
* }|mixed>> $rawObjectConfigurationsByPackages An array of package keys and their raw (ie. unparsed) object configurations
6275
* @return array<Configuration> Object configurations
6376
* @throws ClassLoadingForReflectionFailedException
6477
* @throws InvalidClassException
@@ -173,9 +186,9 @@ public function buildObjectConfigurations(array $availableClassAndInterfaceNames
173186
* Builds a raw configuration array by parsing possible scope and autowiring
174187
* annotations from the given class or interface.
175188
*
176-
* @param string $className
177-
* @param array $rawObjectConfiguration
178-
* @return array
189+
* @param class-string $className
190+
* @param array<string, mixed> $rawObjectConfiguration
191+
* @return array<string, mixed>
179192
*/
180193
protected function enhanceRawConfigurationWithAnnotationOptions($className, array $rawObjectConfiguration): array
181194
{
@@ -219,6 +232,9 @@ protected function wireFactoryArguments(array $objectConfigurations): array
219232
{
220233
foreach ($objectConfigurations as $objectConfiguration) {
221234
foreach ($objectConfiguration->getFactoryArguments() as $index => $argument) {
235+
if ($argument === null) {
236+
continue;
237+
}
222238
if ($argument->getType() !== ConfigurationArgument::ARGUMENT_TYPES_OBJECT) {
223239
continue;
224240
}
@@ -253,9 +269,12 @@ protected function autowireArguments(array $objectConfigurations): array
253269
{
254270
foreach ($objectConfigurations as $objectConfiguration) {
255271
$className = $objectConfiguration->getClassName();
272+
273+
/** @phpstan-ignore identical.alwaysFalse (sometimes this is an empty string) */
256274
if ($className === '') {
257275
continue;
258276
}
277+
259278
if ($objectConfiguration->getAutowiring() === Configuration::AUTOWIRING_MODE_OFF) {
260279
continue;
261280
}
@@ -304,7 +323,7 @@ protected function autowireArguments(array $objectConfigurations): array
304323
$arguments[$index] = new ConfigurationArgument($index, $parameterInformation['class'], ConfigurationArgument::ARGUMENT_TYPES_OBJECT);
305324
} elseif ($parameterInformation['allowsNull'] === true) {
306325
$arguments[$index] = new ConfigurationArgument($index, null, ConfigurationArgument::ARGUMENT_TYPES_STRAIGHTVALUE, Configuration::AUTOWIRING_MODE_OFF);
307-
} elseif (interface_exists($parameterInformation['class'])) {
326+
} elseif (is_string($parameterInformation['class']) && interface_exists($parameterInformation['class'])) {
308327
$debuggingHint = sprintf('No default implementation for the required interface %s was configured, therefore no specific class name could be used for this dependency. ', $parameterInformation['class']);
309328
}
310329
}
@@ -337,6 +356,7 @@ protected function autowireProperties(array $objectConfigurations): array
337356
$className = $objectConfiguration->getClassName();
338357
$properties = $objectConfiguration->getProperties();
339358

359+
/** @phpstan-ignore identical.alwaysFalse (sometimes this is an empty string) */
340360
if ($className === '') {
341361
continue;
342362
}

0 commit comments

Comments
 (0)