function ComponentValidator::validateProps

Same name in this branch
  1. 11.x core/modules/sdc/src/Component/ComponentValidator.php \Drupal\sdc\Component\ComponentValidator::validateProps()
Same name and namespace in other branches
  1. 10 core/modules/sdc/src/Component/ComponentValidator.php \Drupal\sdc\Component\ComponentValidator::validateProps()
  2. 10 core/lib/Drupal/Core/Theme/Component/ComponentValidator.php \Drupal\Core\Theme\Component\ComponentValidator::validateProps()

Validates that the props provided to the component.

Valid props are compliant with the schema definition in the component metadata file.

Parameters

array $context: The Twig context that contains the prop data.

\Drupal\Core\Plugin\Component $component: The component to validate the props against.

Return value

bool TRUE if the props adhere to the component definition.

Throws

\Drupal\Core\Render\Component\Exception\InvalidComponentException

File

core/lib/Drupal/Core/Theme/Component/ComponentValidator.php, line 163

Class

ComponentValidator
Validates a component based on its definition and the component schema.

Namespace

Drupal\Core\Theme\Component

Code

public function validateProps(array $context, Component $component) : bool {
  // If the validator isn't set, then the validation library is not installed.
  if (!$this->validator) {
    return TRUE;
  }
  $component_id = $component->getPluginId();
  $schema = $component->metadata->schema;
  if (!$schema) {
    if ($component->metadata->mandatorySchemas) {
      throw new InvalidComponentException(sprintf('The component "%s" does not provide schema information. Schema definitions are mandatory for components declared in modules. For components declared in themes, schema definitions are only mandatory if the "enforce_prop_schemas" key is set to "true" in the theme info file.', $component_id));
    }
    return TRUE;
  }
  if (empty($schema['properties'])) {
    // If there are no properties in the schema there is nothing to validate.
    return TRUE;
  }
  $prop_names = array_keys($schema['properties']);
  $props_raw = array_intersect_key($context, array_flip($prop_names));
  // Validator::arrayToObjectRecursive stringifies the props using the JSON
  // encoder. Before that happens, we want to validate classes. Once the
  // classes are validated, we remove them as potential problems for the JSON
  // Schema validation.
  [
    $schema,
    $props_raw,
  ] = $this->validateClassProps($schema, $props_raw, $component_id);
  $schema = Validator::arrayToObjectRecursive($schema);
  $props = Validator::arrayToObjectRecursive($props_raw);
  $this->validator
    ->reset();
  $this->validator
    ->validate($props, $schema, Constraint::CHECK_MODE_TYPE_CAST);
  $this->validator
    ->getErrors();
  if ($this->validator
    ->isValid()) {
    return TRUE;
  }
  // Dismiss type errors if the prop received a render array.
  $errors = array_filter($this->validator
    ->getErrors(), function (array $error) use ($context) : bool {
    // Support 5.0 ($error['constraint']) and 6.0
    // ($error['constraint']['name']) at the same time.
    if (($error['constraint']['name'] ?? $error['constraint'] ?? '') !== 'type') {
      return TRUE;
    }
    return !Element::isRenderArray($context[$error['property']] ?? NULL);
  });
  if (empty($errors)) {
    return TRUE;
  }
  $message_parts = array_map(static function (array $error) use ($component_id, $context) : string {
    // We check the error message instead of values and definitions here
    // because it's hard to access both given the possible complexity of a
    // schema. Since this is a small non critical DX improvement error
    // message checking should be sufficient.
    if (str_contains($error['message'], 'NULL value found, but a ')) {
      $error['message'] .= '. This may be because the property is empty instead of having data present. If possible fix the source data, use the |default() twig filter, or update the schema to allow multiple types.';
    }
    // If the property value has been set, print it out for easier
    // debugging.
    if (isset($context[$error['property']]) && \is_scalar($context[$error['property']])) {
      $error['message'] .= \sprintf('. The provided value is: "%s"', $context[$error['property']]);
    }
    return sprintf('[%s/%s] %s.', $component_id, $error['property'], $error['message']);
  }, $errors);
  $message = implode("\n", $message_parts);
  throw new InvalidComponentException($message);
}

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