class DebugDump

Drupal's extension for printing dump() output results.

@internal

Hierarchy

  • class \Drupal\TestTools\Extension\Dump\DebugDump extends \PHPUnit\Runner\Extension\Extension

Expanded class hierarchy of DebugDump

6 files declare their use of DebugDump
BrowserTestBase.php in core/tests/Drupal/Tests/BrowserTestBase.php
BrowserTestBaseTest.php in core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
KernelTestBase.php in core/tests/Drupal/KernelTests/KernelTestBase.php
KernelTestBaseTest.php in core/tests/Drupal/KernelTests/KernelTestBaseTest.php
UnitTestCase.php in core/tests/Drupal/Tests/UnitTestCase.php

... See full list

File

core/tests/Drupal/TestTools/Extension/Dump/DebugDump.php, line 21

Namespace

Drupal\TestTools\Extension\Dump
View source
final class DebugDump implements Extension {
  
  /**
   * The path to the dump staging file.
   */
  private static string $stagingFilePath;
  
  /**
   * Whether colors should be used for printing.
   */
  private static bool $colors = FALSE;
  
  /**
   * Whether the caller of dump should be included in the report.
   */
  private static bool $printCaller = FALSE;
  
  /**
   * {@inheritdoc}
   */
  public function bootstrap(Configuration $configuration, Facade $facade, ParameterCollection $parameters) : void {
    // Determine staging file path.
    self::$stagingFilePath = tempnam(sys_get_temp_dir(), 'dpd');
    // Determine color output.
    $colors = $parameters->has('colors') ? $parameters->get('colors') : FALSE;
    self::$colors = filter_var($colors, \FILTER_VALIDATE_BOOLEAN);
    // Print caller.
    $printCaller = $parameters->has('printCaller') ? $parameters->get('printCaller') : FALSE;
    self::$printCaller = filter_var($printCaller, \FILTER_VALIDATE_BOOLEAN);
    // Set the environment variable with the configuration.
    $config = json_encode([
      'stagingFilePath' => self::$stagingFilePath,
      'colors' => self::$colors,
      'printCaller' => self::$printCaller,
    ]);
    putenv('DRUPAL_PHPUNIT_DUMPER_CONFIG=' . $config);
    $facade->registerSubscriber(new TestRunnerFinishedSubscriber($this));
  }
  
  /**
   * Determines if the extension is enabled.
   *
   * @return bool
   *   TRUE if enabled, FALSE if disabled.
   */
  public static function isEnabled() : bool {
    return getenv('DRUPAL_PHPUNIT_DUMPER_CONFIG') !== FALSE;
  }
  
  /**
   * A CLI handler for \Symfony\Component\VarDumper\VarDumper.
   *
   * @param mixed $var
   *   The variable to be dumped.
   */
  public static function cliHandler(mixed $var) : void {
    if (!self::isEnabled()) {
      return;
    }
    $config = (array) json_decode(getenv('DRUPAL_PHPUNIT_DUMPER_CONFIG'));
    $caller = self::getCaller();
    $cloner = new VarCloner();
    $dumper = new CliDumper();
    $dumper->setColors($config['colors']);
    $dump = [];
    $dumper->dump($cloner->cloneVar($var), function ($line, $depth, $indent_pad) use (&$dump) {
      // A negative depth means "end of dump".
      if ($depth >= 0) {
        // Adds a two spaces indentation to the line.
        $dump[] = str_repeat($indent_pad, $depth) . $line;
      }
    });
    file_put_contents($config['stagingFilePath'], self::encodeDump($caller['test']->id(), $caller['file'], $caller['line'], $dump) . "\n", FILE_APPEND);
  }
  
  /**
   * Encodes the dump for storing.
   *
   * @param string $testId
   *   The id of the test from where the dump was called.
   * @param string|null $file
   *   The path of the file from where the dump was called.
   * @param int|null $line
   *   The line number from where the dump was called.
   * @param array $dump
   *   The dump as an array of lines.
   *
   * @return string
   *   An encoded string.
   */
  private static function encodeDump(string $testId, ?string $file, ?int $line, array $dump) : string {
    $data = [
      'test' => $testId,
      'file' => $file,
      'line' => $line,
      'dump' => $dump,
    ];
    $jsonData = json_encode($data);
    return base64_encode($jsonData);
  }
  
  /**
   * Decodes a dump retrieved from storage.
   *
   * @param string $encodedData
   *   An encoded string.
   *
   * @return array{test: string, file: string|null, line: int|null, dump: string[]}
   *   An encoded string.
   */
  private static function decodeDump(string $encodedData) : array {
    $jsonData = base64_decode($encodedData);
    return (array) json_decode($jsonData);
  }
  
  /**
   * Returns information about the caller of dump().
   *
   * @return array{test: \PHPUnit\Framework\Event\Code\TestMethod, file: string|null, line: int|null}
   *   Caller information.
   */
  private static function getCaller() : array {
    $backtrace = debug_backtrace();
    while (!isset($backtrace[0]['function']) || $backtrace[0]['function'] !== 'dump') {
      array_shift($backtrace);
    }
    $call['file'] = $backtrace[1]['file'] ?? NULL;
    $call['line'] = $backtrace[1]['line'] ?? NULL;
    while (!isset($backtrace[0]['object']) || !$backtrace[0]['object'] instanceof TestCase) {
      array_shift($backtrace);
    }
    $call['test'] = $backtrace[0]['object']->valueObjectForEvents();
    return $call;
  }
  
  /**
   * Retrieves dumps from storage.
   *
   * @return array{string, array{file: string|null, line: int|null, dump: string[]}}
   *   Caller information.
   */
  public static function getDumps() : array {
    if (!self::isEnabled()) {
      return [];
    }
    $config = (array) json_decode(getenv('DRUPAL_PHPUNIT_DUMPER_CONFIG'));
    $contents = rtrim(file_get_contents($config['stagingFilePath']));
    if (empty($contents)) {
      return [];
    }
    $encodedDumps = explode("\n", $contents);
    $dumps = [];
    foreach ($encodedDumps as $encodedDump) {
      $dump = self::decodeDump($encodedDump);
      $test = $dump['test'];
      unset($dump['test']);
      $dumps[$test][] = $dump;
    }
    return $dumps;
  }
  
  /**
   * Prints the dumps generated during the test.
   */
  public function testRunnerFinished(TestRunnerFinished $event) : void {
    $dumps = self::getDumps();
    // Cleanup.
    unlink(self::$stagingFilePath);
    putenv('DRUPAL_PHPUNIT_DUMPER_CONFIG');
    if ($dumps === []) {
      return;
    }
    print "\n\n";
    print "dump() output\n";
    print "-------------\n\n";
    foreach ($dumps as $testId => $testDumps) {
      if (self::$printCaller) {
        print $testId . "\n";
      }
      foreach ($testDumps as $dump) {
        if (self::$printCaller) {
          print "in " . $dump['file'] . ", line " . $dump['line'] . ":\n";
        }
        foreach ($dump['dump'] as $line) {
          print $line . "\n";
        }
        if (self::$printCaller) {
          print "\n";
        }
      }
      if (self::$printCaller) {
        print "\n";
      }
    }
  }

}

Members

Title Sort descending Modifiers Object type Summary
DebugDump::$colors private static property Whether colors should be used for printing.
DebugDump::$printCaller private static property Whether the caller of dump should be included in the report.
DebugDump::$stagingFilePath private static property The path to the dump staging file.
DebugDump::bootstrap public function
DebugDump::cliHandler public static function A CLI handler for \Symfony\Component\VarDumper\VarDumper.
DebugDump::decodeDump private static function Decodes a dump retrieved from storage.
DebugDump::encodeDump private static function Encodes the dump for storing.
DebugDump::getCaller private static function Returns information about the caller of dump().
DebugDump::getDumps public static function Retrieves dumps from storage.
DebugDump::isEnabled public static function Determines if the extension is enabled.
DebugDump::testRunnerFinished public function Prints the dumps generated during the test.

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