class TypedConfigManager

Same name in other branches
  1. 9 core/lib/Drupal/Core/Config/TypedConfigManager.php \Drupal\Core\Config\TypedConfigManager
  2. 10 core/lib/Drupal/Core/Config/TypedConfigManager.php \Drupal\Core\Config\TypedConfigManager
  3. 11.x core/lib/Drupal/Core/Config/TypedConfigManager.php \Drupal\Core\Config\TypedConfigManager

Manages config schema type plugins.

Hierarchy

Expanded class hierarchy of TypedConfigManager

1 file declares its use of TypedConfigManager
TestViewsTest.php in core/modules/views/tests/src/Kernel/TestViewsTest.php
1 string reference to 'TypedConfigManager'
core.services.yml in core/core.services.yml
core/core.services.yml
1 service uses TypedConfigManager
config.typed in core/core.services.yml
Drupal\Core\Config\TypedConfigManager

File

core/lib/Drupal/Core/Config/TypedConfigManager.php, line 17

Namespace

Drupal\Core\Config
View source
class TypedConfigManager extends TypedDataManager implements TypedConfigManagerInterface {
    
    /**
     * A storage instance for reading configuration data.
     *
     * @var \Drupal\Core\Config\StorageInterface
     */
    protected $configStorage;
    
    /**
     * A storage instance for reading configuration schema data.
     *
     * @var \Drupal\Core\Config\StorageInterface
     */
    protected $schemaStorage;
    
    /**
     * The array of plugin definitions, keyed by plugin id.
     *
     * @var array
     */
    protected $definitions;
    
    /**
     * Creates a new typed configuration manager.
     *
     * @param \Drupal\Core\Config\StorageInterface $configStorage
     *   The storage object to use for reading schema data
     * @param \Drupal\Core\Config\StorageInterface $schemaStorage
     *   The storage object to use for reading schema data
     * @param \Drupal\Core\Cache\CacheBackendInterface $cache
     *   The cache backend to use for caching the definitions.
     * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
     *   The module handler.
     * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
     *   (optional) The class resolver.
     */
    public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, CacheBackendInterface $cache, ModuleHandlerInterface $module_handler, ClassResolverInterface $class_resolver = NULL) {
        $this->configStorage = $configStorage;
        $this->schemaStorage = $schemaStorage;
        $this->setCacheBackend($cache, 'typed_config_definitions');
        $this->alterInfo('config_schema_info');
        $this->moduleHandler = $module_handler;
        $this->classResolver = $class_resolver ?: \Drupal::service('class_resolver');
    }
    
    /**
     * {@inheritdoc}
     */
    protected function getDiscovery() {
        if (!isset($this->discovery)) {
            $this->discovery = new ConfigSchemaDiscovery($this->schemaStorage);
        }
        return $this->discovery;
    }
    
    /**
     * {@inheritdoc}
     */
    public function get($name) {
        $data = $this->configStorage
            ->read($name);
        return $this->createFromNameAndData($name, $data);
    }
    
    /**
     * {@inheritdoc}
     */
    public function buildDataDefinition(array $definition, $value, $name = NULL, $parent = NULL) {
        // Add default values for data type and replace variables.
        $definition += [
            'type' => 'undefined',
        ];
        $replace = [];
        $type = $definition['type'];
        if (strpos($type, ']')) {
            // Replace variable names in definition.
            $replace = is_array($value) ? $value : [];
            if (isset($parent)) {
                $replace['%parent'] = $parent;
            }
            if (isset($name)) {
                $replace['%key'] = $name;
            }
            $type = $this->replaceName($type, $replace);
            // Remove the type from the definition so that it is replaced with the
            // concrete type from schema definitions.
            unset($definition['type']);
        }
        // Add default values from type definition.
        $definition += $this->getDefinitionWithReplacements($type, $replace);
        $data_definition = $this->createDataDefinition($definition['type']);
        // Pass remaining values from definition array to data definition.
        foreach ($definition as $key => $value) {
            if (!isset($data_definition[$key])) {
                $data_definition[$key] = $value;
            }
        }
        return $data_definition;
    }
    
    /**
     * Determines the typed config type for a plugin ID.
     *
     * @param string $base_plugin_id
     *   The plugin ID.
     * @param array $definitions
     *   An array of typed config definitions.
     *
     * @return string
     *   The typed config type for the given plugin ID.
     */
    protected function determineType($base_plugin_id, array $definitions) {
        if (isset($definitions[$base_plugin_id])) {
            $type = $base_plugin_id;
        }
        elseif (strpos($base_plugin_id, '.') && ($name = $this->getFallbackName($base_plugin_id))) {
            // Found a generic name, replacing the last element by '*'.
            $type = $name;
        }
        else {
            // If we don't have definition, return the 'undefined' element.
            $type = 'undefined';
        }
        return $type;
    }
    
    /**
     * Gets a schema definition with replacements for dynamic names.
     *
     * @param string $base_plugin_id
     *   A plugin ID.
     * @param array $replacements
     *   An array of replacements for dynamic type names.
     * @param bool $exception_on_invalid
     *   (optional) This parameter is passed along to self::getDefinition().
     *   However, self::getDefinition() does not respect this parameter, so it is
     *   effectively useless in this context.
     *
     * @return array
     *   A schema definition array.
     */
    protected function getDefinitionWithReplacements($base_plugin_id, array $replacements, $exception_on_invalid = TRUE) {
        $definitions = $this->getDefinitions();
        $type = $this->determineType($base_plugin_id, $definitions);
        $definition = $definitions[$type];
        // Check whether this type is an extension of another one and compile it.
        if (isset($definition['type'])) {
            $merge = $this->getDefinition($definition['type'], $exception_on_invalid);
            // Preserve integer keys on merge, so sequence item types can override
            // parent settings as opposed to adding unused second, third, etc. items.
            $definition = NestedArray::mergeDeepArray([
                $merge,
                $definition,
            ], TRUE);
            // Replace dynamic portions of the definition type.
            if (!empty($replacements) && strpos($definition['type'], ']')) {
                $sub_type = $this->determineType($this->replaceName($definition['type'], $replacements), $definitions);
                $sub_definition = $definitions[$sub_type];
                if (isset($definitions[$sub_type]['type'])) {
                    $sub_merge = $this->getDefinition($definitions[$sub_type]['type'], $exception_on_invalid);
                    $sub_definition = NestedArray::mergeDeepArray([
                        $sub_merge,
                        $definitions[$sub_type],
                    ], TRUE);
                }
                // Merge the newly determined subtype definition with the original
                // definition.
                $definition = NestedArray::mergeDeepArray([
                    $sub_definition,
                    $definition,
                ], TRUE);
                $type = "{$type}||{$sub_type}";
            }
            // Unset type so we try the merge only once per type.
            unset($definition['type']);
            $this->definitions[$type] = $definition;
        }
        // Add type and default definition class.
        $definition += [
            'definition_class' => '\\Drupal\\Core\\TypedData\\DataDefinition',
            'type' => $type,
            'unwrap_for_canonical_representation' => TRUE,
        ];
        return $definition;
    }
    
    /**
     * {@inheritdoc}
     */
    public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) {
        return $this->getDefinitionWithReplacements($base_plugin_id, [], $exception_on_invalid);
    }
    
    /**
     * {@inheritdoc}
     */
    public function clearCachedDefinitions() {
        $this->schemaStorage
            ->reset();
        parent::clearCachedDefinitions();
    }
    
    /**
     * Gets fallback configuration schema name.
     *
     * @param string $name
     *   Configuration name or key.
     *
     * @return null|string
     *   The resolved schema name for the given configuration name or key. Returns
     *   null if there is no schema name to fallback to. For example,
     *   breakpoint.breakpoint.module.toolbar.narrow will check for definitions in
     *   the following order:
     *     breakpoint.breakpoint.module.toolbar.*
     *     breakpoint.breakpoint.module.*.*
     *     breakpoint.breakpoint.module.*
     *     breakpoint.breakpoint.*.*.*
     *     breakpoint.breakpoint.*
     *     breakpoint.*.*.*.*
     *     breakpoint.*
     *   Colons are also used, for example,
     *   block.settings.system_menu_block:footer will check for definitions in the
     *   following order:
     *     block.settings.system_menu_block:*
     *     block.settings.*:*
     *     block.settings.*
     *     block.*.*:*
     *     block.*
     */
    protected function getFallbackName($name) {
        // Check for definition of $name with filesystem marker.
        $replaced = preg_replace('/([^\\.:]+)([\\.:\\*]*)$/', '*\\2', $name);
        if ($replaced != $name) {
            if (isset($this->definitions[$replaced])) {
                return $replaced;
            }
            else {
                // No definition for this level. Collapse multiple wildcards to a single
                // wildcard to see if there is a greedy match. For example,
                // breakpoint.breakpoint.*.* becomes
                // breakpoint.breakpoint.*
                $one_star = preg_replace('/\\.([:\\.\\*]*)$/', '.*', $replaced);
                if ($one_star != $replaced && isset($this->definitions[$one_star])) {
                    return $one_star;
                }
                // Check for next level. For example, if breakpoint.breakpoint.* has
                // been checked and no match found then check breakpoint.*.*
                return $this->getFallbackName($replaced);
            }
        }
    }
    
    /**
     * Replaces variables in configuration name.
     *
     * The configuration name may contain one or more variables to be replaced,
     * enclosed in square brackets like '[name]' and will follow the replacement
     * rules defined by the replaceVariable() method.
     *
     * @param string $name
     *   Configuration name with variables in square brackets.
     * @param mixed $data
     *   Configuration data for the element.
     *
     * @return string
     *   Configuration name with variables replaced.
     */
    protected function replaceName($name, $data) {
        if (preg_match_all("/\\[(.*)\\]/U", $name, $matches)) {
            // Build our list of '[value]' => replacement.
            $replace = [];
            foreach (array_combine($matches[0], $matches[1]) as $key => $value) {
                $replace[$key] = $this->replaceVariable($value, $data);
            }
            return strtr($name, $replace);
        }
        else {
            return $name;
        }
    }
    
    /**
     * Replaces variable values in included names with configuration data.
     *
     * Variable values are nested configuration keys that will be replaced by
     * their value or some of these special strings:
     * - '%key', will be replaced by the element's key.
     * - '%parent', to reference the parent element.
     * - '%type', to reference the schema definition type. Can only be used in
     *   combination with %parent.
     *
     * There may be nested configuration keys separated by dots or more complex
     * patterns like '%parent.name' which references the 'name' value of the
     * parent element.
     *
     * Example patterns:
     * - 'name.subkey', indicates a nested value of the current element.
     * - '%parent.name', will be replaced by the 'name' value of the parent.
     * - '%parent.%key', will be replaced by the parent element's key.
     * - '%parent.%type', will be replaced by the schema type of the parent.
     * - '%parent.%parent.%type', will be replaced by the schema type of the
     *   parent's parent.
     *
     * @param string $value
     *   Variable value to be replaced.
     * @param mixed $data
     *   Configuration data for the element.
     *
     * @return string
     *   The replaced value if a replacement found or the original value if not.
     */
    protected function replaceVariable($value, $data) {
        $parts = explode('.', $value);
        // Process each value part, one at a time.
        while ($name = array_shift($parts)) {
            if (!is_array($data) || !isset($data[$name])) {
                // Key not found, return original value
                return $value;
            }
            elseif (!$parts) {
                $value = $data[$name];
                if (is_bool($value)) {
                    $value = (int) $value;
                }
                // If no more parts left, this is the final property.
                return (string) $value;
            }
            else {
                // Get nested value and continue processing.
                if ($name == '%parent') {
                    
                    /** @var \Drupal\Core\Config\Schema\ArrayElement $parent */
                    // Switch replacement values with values from the parent.
                    $parent = $data['%parent'];
                    $data = $parent->getValue();
                    $data['%type'] = $parent->getDataDefinition()
                        ->getDataType();
                    // The special %parent and %key values now need to point one level up.
                    if ($new_parent = $parent->getParent()) {
                        $data['%parent'] = $new_parent;
                        $data['%key'] = $new_parent->getName();
                    }
                }
                else {
                    $data = $data[$name];
                }
            }
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function hasConfigSchema($name) {
        // The schema system falls back on the Undefined class for unknown types.
        $definition = $this->getDefinition($name);
        return is_array($definition) && $definition['class'] != Undefined::class;
    }
    
    /**
     * {@inheritdoc}
     */
    protected function alterDefinitions(&$definitions) {
        $discovered_schema = array_keys($definitions);
        parent::alterDefinitions($definitions);
        $altered_schema = array_keys($definitions);
        if ($discovered_schema != $altered_schema) {
            $added_keys = implode(',', array_diff($altered_schema, $discovered_schema));
            $removed_keys = implode(',', array_diff($discovered_schema, $altered_schema));
            if (!empty($added_keys) && !empty($removed_keys)) {
                $message = "Invoking hook_config_schema_info_alter() has added ({$added_keys}) and removed ({$removed_keys}) schema definitions";
            }
            elseif (!empty($added_keys)) {
                $message = "Invoking hook_config_schema_info_alter() has added ({$added_keys}) schema definitions";
            }
            else {
                $message = "Invoking hook_config_schema_info_alter() has removed ({$removed_keys}) schema definitions";
            }
            throw new ConfigSchemaAlterException($message);
        }
    }
    
    /**
     * {@inheritdoc}
     */
    public function createFromNameAndData($config_name, array $config_data) {
        $definition = $this->getDefinition($config_name);
        $data_definition = $this->buildDataDefinition($definition, $config_data);
        return $this->create($data_definition, $config_data);
    }

}

Members

Title Sort descending Modifiers Object type Summary Overriden Title Overrides
DefaultPluginManager::$additionalAnnotationNamespaces protected property Additional namespaces the annotation discovery mechanism should scan for
annotation definitions.
DefaultPluginManager::$alterHook protected property Name of the alter hook if one should be invoked.
DefaultPluginManager::$cacheKey protected property The cache key.
DefaultPluginManager::$cacheTags protected property An array of cache tags to use for the cached definitions.
DefaultPluginManager::$defaults protected property A set of defaults to be referenced by $this->processDefinition() if
additional processing of plugins is necessary or helpful for development
purposes.
9
DefaultPluginManager::$moduleHandler protected property The module handler to invoke the alter hook. 1
DefaultPluginManager::$namespaces protected property An object that implements \Traversable which contains the root paths
keyed by the corresponding namespace to look for plugin implementations.
DefaultPluginManager::$pluginDefinitionAnnotationName protected property The name of the annotation that contains the plugin definition.
DefaultPluginManager::$pluginInterface protected property The interface each plugin should implement. 1
DefaultPluginManager::$subdir protected property The subdirectory within a namespace to look for plugins, or FALSE if the
plugins are in the top level of the namespace.
DefaultPluginManager::alterInfo protected function Sets the alter hook name.
DefaultPluginManager::extractProviderFromDefinition protected function Extracts the provider from a plugin definition.
DefaultPluginManager::findDefinitions protected function Finds plugin definitions. 7
DefaultPluginManager::fixContextAwareDefinitions private function Fix the definitions of context-aware plugins.
DefaultPluginManager::getCacheContexts public function The cache contexts associated with this object. Overrides CacheableDependencyInterface::getCacheContexts
DefaultPluginManager::getCachedDefinitions protected function Returns the cached plugin definitions of the decorated discovery class.
DefaultPluginManager::getCacheMaxAge public function The maximum age for which this object may be cached. Overrides CacheableDependencyInterface::getCacheMaxAge
DefaultPluginManager::getCacheTags public function The cache tags associated with this object. Overrides CacheableDependencyInterface::getCacheTags
DefaultPluginManager::getDefinitions public function Gets the definition of all plugins for this type. Overrides DiscoveryTrait::getDefinitions 2
DefaultPluginManager::getFactory protected function Gets the plugin factory. Overrides PluginManagerBase::getFactory
DefaultPluginManager::processDefinition public function Performs extra processing on plugin definitions. 13
DefaultPluginManager::providerExists protected function Determines if the provider of a definition exists. 3
DefaultPluginManager::setCacheBackend public function Initialize the cache backend.
DefaultPluginManager::setCachedDefinitions protected function Sets a cache of plugin definitions for the decorated discovery class.
DefaultPluginManager::useCaches public function Disable the use of caches. Overrides CachedDiscoveryInterface::useCaches 1
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DiscoveryTrait::doGetDefinition protected function Gets a specific plugin definition.
DiscoveryTrait::hasDefinition public function
PluginManagerBase::$discovery protected property The object that discovers plugins managed by this manager.
PluginManagerBase::$factory protected property The object that instantiates plugins managed by this manager.
PluginManagerBase::$mapper protected property The object that returns the preconfigured plugin instance appropriate for a particular runtime condition.
PluginManagerBase::handlePluginNotFound protected function Allows plugin managers to specify custom behavior if a plugin is not found. 1
TypedConfigManager::$configStorage protected property A storage instance for reading configuration data.
TypedConfigManager::$definitions protected property The array of plugin definitions, keyed by plugin id. Overrides DiscoveryCachedTrait::$definitions
TypedConfigManager::$schemaStorage protected property A storage instance for reading configuration schema data.
TypedConfigManager::alterDefinitions protected function Invokes the hook to alter the definitions if the alter hook is set. Overrides DefaultPluginManager::alterDefinitions
TypedConfigManager::buildDataDefinition public function Creates a new data definition object from a type definition array and
actual configuration data. Since type definitions may contain variables
to be replaced, we need the configuration value to create it.
Overrides TypedConfigManagerInterface::buildDataDefinition
TypedConfigManager::clearCachedDefinitions public function Clears static and persistent plugin definition caches. Overrides TypedDataManager::clearCachedDefinitions
TypedConfigManager::createFromNameAndData public function Gets typed data for a given configuration name and its values. Overrides TypedConfigManagerInterface::createFromNameAndData
TypedConfigManager::determineType protected function Determines the typed config type for a plugin ID.
TypedConfigManager::get public function Gets typed configuration data. Overrides TypedConfigManagerInterface::get
TypedConfigManager::getDefinition public function Gets a specific plugin definition. Overrides DiscoveryCachedTrait::getDefinition
TypedConfigManager::getDefinitionWithReplacements protected function Gets a schema definition with replacements for dynamic names.
TypedConfigManager::getDiscovery protected function Gets the plugin discovery. Overrides DefaultPluginManager::getDiscovery
TypedConfigManager::getFallbackName protected function Gets fallback configuration schema name.
TypedConfigManager::hasConfigSchema public function Checks if the configuration schema with the given config name exists. Overrides TypedConfigManagerInterface::hasConfigSchema
TypedConfigManager::replaceName protected function Replaces variables in configuration name.
TypedConfigManager::replaceVariable protected function Replaces variable values in included names with configuration data.
TypedConfigManager::__construct public function Creates a new typed configuration manager. Overrides TypedDataManager::__construct
TypedDataManager::$classResolver protected property The class resolver.
TypedDataManager::$constraintManager protected property The validation constraint manager to use for instantiating constraints.
TypedDataManager::$prototypes protected property An array of typed data property prototypes.
TypedDataManager::$validator protected property The validator used for validating typed data.
TypedDataManager::create public function Creates a new typed data object instance. Overrides TypedDataManagerInterface::create
TypedDataManager::createDataDefinition public function Creates a new data definition object. Overrides TypedDataManagerInterface::createDataDefinition
TypedDataManager::createInstance public function Instantiates a typed data object. Overrides PluginManagerBase::createInstance
TypedDataManager::createListDataDefinition public function Creates a new list data definition for items of the given data type. Overrides TypedDataManagerInterface::createListDataDefinition
TypedDataManager::getCanonicalRepresentation public function Gets the canonical representation of a TypedData object. Overrides TypedDataManagerInterface::getCanonicalRepresentation
TypedDataManager::getDefaultConstraints public function Gets default constraints for the given data definition. Overrides TypedDataManagerInterface::getDefaultConstraints
TypedDataManager::getInstance public function Overrides PluginManagerBase::getInstance
TypedDataManager::getPropertyInstance public function Get a typed data instance for a property of a given typed data object. Overrides TypedDataManagerInterface::getPropertyInstance
TypedDataManager::getValidationConstraintManager public function Gets the validation constraint manager. Overrides TypedDataManagerInterface::getValidationConstraintManager
TypedDataManager::getValidator public function Gets the validator for validating typed data. Overrides TypedDataManagerInterface::getValidator
TypedDataManager::setValidationConstraintManager public function Sets the validation constraint manager. Overrides TypedDataManagerInterface::setValidationConstraintManager
TypedDataManager::setValidator public function Sets the validator for validating typed data. Overrides TypedDataManagerInterface::setValidator
UseCacheBackendTrait::$cacheBackend protected property Cache backend instance.
UseCacheBackendTrait::$useCaches protected property Flag whether caches should be used or skipped.
UseCacheBackendTrait::cacheGet protected function Fetches from the cache backend, respecting the use caches flag. 1
UseCacheBackendTrait::cacheSet protected function Stores data in the persistent cache, respecting the use caches flag.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.