You are here

trait SchemaCheckTrait in Drupal 10

Same name and namespace in other branches
  1. 8 core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php \Drupal\Core\Config\Schema\SchemaCheckTrait
  2. 9 core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php \Drupal\Core\Config\Schema\SchemaCheckTrait

Provides a trait for checking configuration schema.

Hierarchy

5 files declare their use of SchemaCheckTrait
CKEditor5.php in core/modules/ckeditor5/src/Plugin/Editor/CKEditor5.php
CKEditor5PluginDefinition.php in core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
ConfigSchemaChecker.php in core/lib/Drupal/Core/Config/Development/ConfigSchemaChecker.php
SchemaCheckTestTrait.php in core/tests/Drupal/Tests/SchemaCheckTestTrait.php
SchemaCheckTraitTest.php in core/tests/Drupal/KernelTests/Core/Config/SchemaCheckTraitTest.php

File

core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php, line 16

Namespace

Drupal\Core\Config\Schema
View source
trait SchemaCheckTrait {

  /**
   * The config schema wrapper object for the configuration object under test.
   *
   * @var \Drupal\Core\Config\Schema\Element
   */
  protected $schema;

  /**
   * The configuration object name under test.
   *
   * @var string
   */
  protected $configName;

  /**
   * Checks the TypedConfigManager has a valid schema for the configuration.
   *
   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
   *   The TypedConfigManager.
   * @param string $config_name
   *   The configuration name.
   * @param array $config_data
   *   The configuration data, assumed to be data for a top-level config object.
   *
   * @return array|bool
   *   FALSE if no schema found. List of errors if any found. TRUE if fully
   *   valid.
   */
  public function checkConfigSchema(TypedConfigManagerInterface $typed_config, $config_name, $config_data) {

    // We'd like to verify that the top-level type is either config_base,
    // config_entity, or a derivative. The only thing we can really test though
    // is that the schema supports having langcode in it. So add 'langcode' to
    // the data if it doesn't already exist.
    if (!isset($config_data['langcode'])) {
      $config_data['langcode'] = 'en';
    }
    $this->configName = $config_name;
    if (!$typed_config
      ->hasConfigSchema($config_name)) {
      return FALSE;
    }
    $this->schema = $typed_config
      ->createFromNameAndData($config_name, $config_data);
    $errors = [];
    foreach ($config_data as $key => $value) {
      $errors[] = $this
        ->checkValue($key, $value);
    }
    $errors = array_merge([], ...$errors);
    if (empty($errors)) {
      return TRUE;
    }
    return $errors;
  }

  /**
   * Helper method to check data type.
   *
   * @param string $key
   *   A string of configuration key.
   * @param mixed $value
   *   Value of given key.
   *
   * @return array
   *   List of errors found while checking with the corresponding schema.
   */
  protected function checkValue($key, $value) {
    $error_key = $this->configName . ':' . $key;

    /** @var \Drupal\Core\TypedData\TypedDataInterface $element */
    $element = $this->schema
      ->get($key);

    // Check if this type has been deprecated.
    $data_definition = $element
      ->getDataDefinition();
    if (!empty($data_definition['deprecated'])) {
      @trigger_error($data_definition['deprecated'], E_USER_DEPRECATED);
    }
    if ($element instanceof Undefined) {
      return [
        $error_key => 'missing schema',
      ];
    }

    // Do not check value if it is defined to be ignored.
    if ($element && $element instanceof Ignore) {
      return [];
    }
    if ($element && is_scalar($value) || $value === NULL) {
      $success = FALSE;
      $type = gettype($value);
      if ($element instanceof PrimitiveInterface) {
        $success = $type == 'integer' && $element instanceof IntegerInterface || ($type == 'double' || $type == 'integer') && $element instanceof FloatInterface || $type == 'boolean' && $element instanceof BooleanInterface || $type == 'string' && $element instanceof StringInterface || $value === NULL;
      }
      elseif ($element instanceof ArrayElement && $element
        ->isNullable() && $value === NULL) {
        $success = TRUE;
      }
      $class = get_class($element);
      if (!$success) {
        return [
          $error_key => "variable type is {$type} but applied schema class is {$class}",
        ];
      }
    }
    else {
      $errors = [];
      if (!$element instanceof TraversableTypedDataInterface) {
        $errors[$error_key] = 'non-scalar value but not defined as an array (such as mapping or sequence)';
      }

      // Go on processing so we can get errors on all levels. Any non-scalar
      // value must be an array so cast to an array.
      if (!is_array($value)) {
        $value = (array) $value;
      }
      $nested_errors = [];

      // Recurse into any nested keys.
      foreach ($value as $nested_value_key => $nested_value) {
        $nested_errors[] = $this
          ->checkValue($key . '.' . $nested_value_key, $nested_value);
      }
      return array_merge($errors, ...$nested_errors);
    }

    // No errors found.
    return [];
  }

}

Members

Namesort descending Modifiers Type Description Overrides
SchemaCheckTrait::$configName protected property The configuration object name under test.
SchemaCheckTrait::$schema protected property The config schema wrapper object for the configuration object under test.
SchemaCheckTrait::checkConfigSchema public function Checks the TypedConfigManager has a valid schema for the configuration.
SchemaCheckTrait::checkValue protected function Helper method to check data type.