You are here

class TypedConfigManager in Drupal 10

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Config/TypedConfigManager.php \Drupal\Core\Config\TypedConfigManager
  2. 9 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

Namesort descending Modifiers Type Description 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::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
DependencySerializationTrait::$_serviceIds protected property
DependencySerializationTrait::__sleep public function 2
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 Gets a preconfigured instance of a plugin. 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.
UseCacheBackendTrait::cacheSet protected function Stores data in the persistent cache, respecting the use caches flag.