Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Config / Schema / SchemaCheckTrait.php
diff --git a/web/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php b/web/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
new file mode 100644 (file)
index 0000000..9230281
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+namespace Drupal\Core\Config\Schema;
+
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\Type\BooleanInterface;
+use Drupal\Core\TypedData\Type\StringInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
+
+/**
+ * Provides a trait for checking configuration schema.
+ */
+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;
+    }
+    $definition = $typed_config->getDefinition($config_name);
+    $data_definition = $typed_config->buildDataDefinition($definition, $config_data);
+    $this->schema = $typed_config->create($data_definition, $config_data);
+    $errors = [];
+    foreach ($config_data as $key => $value) {
+      $errors = array_merge($errors, $this->checkValue($key, $value));
+    }
+    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;
+    $element = $this->schema->get($key);
+    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) ||
+          // Allow integer values in a float field.
+          (($type == 'double' || $type == 'integer') && $element instanceof FloatInterface) ||
+          ($type == 'boolean' && $element instanceof BooleanInterface) ||
+          ($type == 'string' && $element instanceof StringInterface) ||
+          // Null values are allowed for all primitive types.
+          ($value === NULL);
+      }
+      // Array elements can also opt-in for allowing a NULL value.
+      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;
+      }
+      // Recurse into any nested keys.
+      foreach ($value as $nested_value_key => $nested_value) {
+        $errors = array_merge($errors, $this->checkValue($key . '.' . $nested_value_key, $nested_value));
+      }
+      return $errors;
+    }
+    // No errors found.
+    return [];
+  }
+
+}