/**
* Traverses through the given class and interface names and builds a base object configuration
* for all of them. Then parses the provided extra configuration and merges the result
* into the overall configuration. Finally autowires dependencies of arguments and properties
* which can be resolved automatically.
*
* @param array $availableClassAndInterfaceNamesByPackage An array of available class names, grouped by package key
* @param array $rawObjectConfigurationsByPackages An array of package keys and their raw (ie. unparsed) object configurations
* @return array<Configuration> Object configurations
* @throws InvalidObjectConfigurationException
*/
public function buildObjectConfigurations(array $availableClassAndInterfaceNamesByPackage, array $rawObjectConfigurationsByPackages)
{
$objectConfigurations = [];
$interfaceNames = [];
foreach ($availableClassAndInterfaceNamesByPackage as $packageKey => $classAndInterfaceNames) {
foreach ($classAndInterfaceNames as $classOrInterfaceName) {
$objectName = $classOrInterfaceName;
if ($this->reflectionService->isClassUnconfigurable($classOrInterfaceName)) {
continue;
}
if (interface_exists($classOrInterfaceName)) {
$interfaceName = $classOrInterfaceName;
$implementationClassName = $this->reflectionService->getDefaultImplementationClassNameForInterface($interfaceName);
if (!isset($rawObjectConfigurationsByPackages[$packageKey][$interfaceName]) && $implementationClassName === false) {
continue;
}
if ($this->reflectionService->isClassAnnotatedWith($interfaceName, Flow\Scope::class)) {
throw new InvalidObjectConfigurationException(sprintf('Scope annotations in interfaces don\'t have any effect, therefore you better remove it from %s in order to avoid confusion.', $interfaceName), 1299095595);
}
$interfaceNames[$interfaceName] = true;
} else {
$implementationClassName = $classOrInterfaceName;
}
$rawObjectConfiguration = ['className' => $implementationClassName];
$rawObjectConfiguration = $this->enhanceRawConfigurationWithAnnotationOptions($classOrInterfaceName, $rawObjectConfiguration);
$objectConfigurations[$objectName] = $this->parseConfigurationArray($objectName, $rawObjectConfiguration, 'automatically registered class');
$objectConfigurations[$objectName]->setPackageKey($packageKey);
}
}
foreach ($rawObjectConfigurationsByPackages as $packageKey => $rawObjectConfigurations) {
foreach ($rawObjectConfigurations as $objectName => $rawObjectConfiguration) {
$objectName = str_replace('_', '\\', $objectName);
if (!is_array($rawObjectConfiguration)) {
throw new InvalidObjectConfigurationException('Configuration of object "' . $objectName . '" in package "' . $packageKey . '" is not an array, please check your Objects.yaml for syntax errors.', 1295954338);
}
$existingObjectConfiguration = isset($objectConfigurations[$objectName]) ? $objectConfigurations[$objectName] : null;
if (isset($rawObjectConfiguration['className'])) {
$rawObjectConfiguration = $this->enhanceRawConfigurationWithAnnotationOptions($rawObjectConfiguration['className'], $rawObjectConfiguration);
}
$newObjectConfiguration = $this->parseConfigurationArray($objectName, $rawObjectConfiguration, 'configuration of package ' . $packageKey . ', definition for object "' . $objectName . '"', $existingObjectConfiguration);
if (!isset($objectConfigurations[$objectName]) && !interface_exists($objectName, true) && !class_exists($objectName, false)) {
throw new InvalidObjectConfigurationException('Tried to configure unknown object "' . $objectName . '" in package "' . $packageKey . '". Please check your Objects.yaml.', 1184926175);
}
if ($objectName !== $newObjectConfiguration->getClassName() && !interface_exists($objectName, true)) {
throw new InvalidObjectConfigurationException('Tried to set a differing class name for class "' . $objectName . '" in the object configuration of package "' . $packageKey . '". Setting "className" is only allowed for interfaces, please check your Objects.yaml."', 1295954589);
}
if (empty($newObjectConfiguration->getClassName()) && empty($newObjectConfiguration->getFactoryObjectName())) {
$count = count($this->reflectionService->getAllImplementationClassNamesForInterface($objectName));
$hint = $count ? 'It seems like there is no class which implements that interface, maybe the object configuration is obsolete?' : sprintf('There are %s classes implementing that interface, therefore you must specify a specific class in your object configuration.', $count);
throw new InvalidObjectConfigurationException('The object configuration for "' . $objectName . '" in the object configuration of package "' . $packageKey . '" lacks a "className" entry. ' . $hint, 1422566751);
}
$objectConfigurations[$objectName] = $newObjectConfiguration;
if ($objectConfigurations[$objectName]->getPackageKey() === null) {
$objectConfigurations[$objectName]->setPackageKey($packageKey);
}
}
}
// If an implementation class could be determined for an interface object configuration, set the scope for the
// interface object configuration to the scope found in the implementation class configuration, but
// only if the interface doesn't have a specifically configured scope (i.e. is prototype so far)
foreach (array_keys($interfaceNames) as $interfaceName) {
$implementationClassName = $objectConfigurations[$interfaceName]->getClassName();
if ($implementationClassName !== '' && isset($objectConfigurations[$implementationClassName]) && $objectConfigurations[$interfaceName]->getScope() === Configuration::SCOPE_PROTOTYPE) {
$objectConfigurations[$interfaceName]->setScope($objectConfigurations[$implementationClassName]->getScope());
}
}
$this->autowireArguments($objectConfigurations);
$this->autowireProperties($objectConfigurations);
return $objectConfigurations;
}