DiffOpOutputBuilder.php

Same filename and directory in other branches
  1. 10 core/lib/Drupal/Component/Diff/DiffOpOutputBuilder.php

Namespace

Drupal\Component\Diff

File

core/lib/Drupal/Component/Diff/DiffOpOutputBuilder.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Component\Diff;

use Drupal\Component\Diff\Engine\DiffOp;
use Drupal\Component\Diff\Engine\DiffOpAdd;
use Drupal\Component\Diff\Engine\DiffOpChange;
use Drupal\Component\Diff\Engine\DiffOpCopy;
use Drupal\Component\Diff\Engine\DiffOpDelete;
use SebastianBergmann\Diff\Differ;
use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface;

/**
 * Returns a diff as an array of DiffOp operations.
 */
final class DiffOpOutputBuilder implements DiffOutputBuilderInterface {
  
  /**
   * A constant to manage removal+addition as a single operation.
   */
  private const CHANGED = 999;
  
  /**
   * {@inheritdoc}
   */
  public function getDiff(array $diff) : string {
    return serialize($this->toOpsArray($diff));
  }
  
  /**
   * Converts the output of Differ to an array of DiffOp* value objects.
   *
   * @param array $diff
   *   The array output of Differ::diffToArray().
   *
   * @return \Drupal\Component\Diff\Engine\DiffOp[]
   *   An array of DiffOp* value objects.
   */
  public function toOpsArray(array $diff) : array {
    $ops = [];
    $hunkMode = NULL;
    $hunkSource = [];
    $hunkTarget = [];
    for ($i = 0; $i < count($diff); $i++) {
      // Handle a sequence of removals + additions as a sequence of changes, and
      // manages the tail if required.
      if ($diff[$i][1] === Differ::REMOVED) {
        if ($hunkMode !== NULL) {
          $ops[] = $this->hunkOp($hunkMode, $hunkSource, $hunkTarget);
          $hunkSource = [];
          $hunkTarget = [];
        }
        for ($n = $i; $n < count($diff) && $diff[$n][1] === Differ::REMOVED; $n++) {
          $hunkSource[] = $diff[$n][0];
        }
        for (; $n < count($diff) && $diff[$n][1] === Differ::ADDED; $n++) {
          $hunkTarget[] = $diff[$n][0];
        }
        if (count($hunkTarget) === 0) {
          $ops[] = $this->hunkOp(Differ::REMOVED, $hunkSource, $hunkTarget);
        }
        else {
          $ops[] = $this->hunkOp(self::CHANGED, $hunkSource, $hunkTarget);
        }
        $hunkMode = NULL;
        $hunkSource = [];
        $hunkTarget = [];
        $i = $n - 1;
        continue;
      }
      // When here, we are adding or copying the item. Removing or changing is
      // managed above.
      if ($hunkMode === NULL) {
        $hunkMode = $diff[$i][1];
      }
      elseif ($hunkMode !== $diff[$i][1]) {
        $ops[] = $this->hunkOp($hunkMode, $hunkSource, $hunkTarget);
        $hunkMode = $diff[$i][1];
        $hunkSource = [];
        $hunkTarget = [];
      }
      $hunkSource[] = $diff[$i][0];
    }
    if ($hunkMode !== NULL) {
      $ops[] = $this->hunkOp($hunkMode, $hunkSource, $hunkTarget);
    }
    return $ops;
  }
  
  /**
   * Returns the proper DiffOp object based on the hunk mode.
   *
   * @param int $mode
   *   A Differ constant or self::CHANGED.
   * @param string[] $source
   *   An array of strings to be changed/added/removed/copied.
   * @param string[] $source
   *   The array of strings to be changed to when self::CHANGED is specified.
   *
   * @return \Drupal\Component\Diff\Engine\DiffOp
   *   A DiffOp* value object.
   *
   * @throw \InvalidArgumentException
   *   When $mode is not valid.
   */
  private function hunkOp(int $mode, array $source, array $target) : DiffOp {
    switch ($mode) {
      case Differ::OLD:
        return new DiffOpCopy($source);
      case self::CHANGED:
        return new DiffOpChange($source, $target);
      case Differ::ADDED:
        return new DiffOpAdd($source);
      case Differ::REMOVED:
        return new DiffOpDelete($source);
    }
    throw new \InvalidArgumentException("Invalid \$mode {$mode} specified");
  }

}

Classes

Title Deprecated Summary
DiffOpOutputBuilder Returns a diff as an array of DiffOp operations.

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