Verified Commit 51ffd666 authored by Dave Long's avatar Dave Long
Browse files

fix: #3611957 Improve InvalidComponentException by including the parent/calling Twig template path

By: anybody
By: thomas.frobieter
By: catch
By: grevil
(cherry picked from commit 96240ad3)
parent 2069a869
Loading
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
use Drupal\Core\Theme\Component\ComponentValidator;
use Drupal\Core\Theme\ComponentPluginManager;
use Twig\Extension\AbstractExtension;
use Twig\Template;
use Twig\TwigFunction;

/**
@@ -129,6 +130,31 @@ protected function doValidateProps(array $context, string $component_id): bool {
    catch (ComponentNotFoundException $e) {
      throw new InvalidComponentException($e->getMessage(), $e->getCode(), $e);
    }
    catch (InvalidComponentException $e) {
      // Inspect the backtrace to find the template that embedded the component.
      $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 20);
      $calling_template = 'Unknown Template';

      foreach ($backtrace as $trace) {
        if (isset($trace['object']) && $trace['object'] instanceof Template) {
          $template_name = $trace['object']->getTemplateName();

          // Skip the component's own template to find its parent container.
          if ($template_name === $component_id) {
            continue;
          }

          $calling_template = $template_name;
          break;
        }
      }

      throw new InvalidComponentException(
        sprintf('Component validation failed in Twig file [%s] for component [%s]: %s', $calling_template, $component_id, $e->getMessage()),
        $e->getCode(),
        $e
      );
    }
  }

}
+36 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\Component\Exception\InvalidComponentDataException;
use Drupal\Core\Render\Component\Exception\InvalidComponentException;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Theme\ComponentPluginManager;
@@ -158,6 +159,41 @@ public function testRenderPropValidation(): void {
    }
  }

  /**
   * Ensures nested invalid props report the calling parent template.
   */
  public function testRenderNestedPropValidationIncludesCallingTemplate(): void {
    // Omit ctaText so my-banner still validates, but nested my-cta does not.
    $build = [
      '#type' => 'component',
      '#component' => 'sdc_test:my-banner',
      '#props' => [
        'heading' => 'I am a banner',
        'ctaHref' => 'https://www.example.org',
        'ctaTarget' => '',
      ],
      '#slots' => [
        'banner_body' => [
          '#plain_text' => 'Banner body',
        ],
      ],
    ];
    try {
      $this->renderComponentRenderArray($build);
      $this->fail('Invalid nested prop did not cause an exception');
    }
    catch (\Throwable $e) {
      // Find the "InvalidComponentException":
      while ($e && !$e instanceof InvalidComponentException) {
        $e = $e->getPrevious();
      }
      $this->assertInstanceOf(InvalidComponentException::class, $e);
      // Check, that the error message contains the error calling template, as
      // well as the component, whose props actually fail validation:
      $this->assertStringContainsString('Component validation failed in Twig file [sdc_test:my-banner] for component [sdc_test:my-cta]:', $e->getMessage());
    }
  }

  /**
   * Ensure fuzzy coercing of arrays and objects works properly.
   */