migrate_plus-8.x-5.x-dev/src/Plugin/migrate/process/DomStrReplace.php

src/Plugin/migrate/process/DomStrReplace.php
<?php

declare(strict_types=1);

namespace Drupal\migrate_plus\Plugin\migrate\process;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;

/**
 * String replacements on a source dom.
 *
 * Analogous to str_replace process plugin, but based on a \DOMDocument instead
 * of a string.
 * Meant to be used after dom process plugin.
 *
 * Available configuration keys:
 * - mode: What to modify. Possible values:
 *   - attribute: One element attribute.
 *   - element: An element name.
 *   - text: The element text content.
 * - xpath: XPath query expression that will produce the \DOMNodeList to walk.
 * - attribute_options: A map of options related to the attribute mode. Required
 *   when mode is attribute. The keys can be:
 *   - name: Name of the attribute to match and modify.
 * - search: pattern to match.
 * - replace: value to replace the searched pattern with.
 * - regex: Use regular expression replacement.
 * - case_insensitive: Case insensitive search. Only valid when regex is false.
 *
 * Examples:
 *
 * @code
 * process:
 *   'body/value':
 *     -
 *       plugin: dom
 *       method: import
 *       source: 'body/0/value'
 *     -
 *       plugin: dom_str_replace
 *       mode: attribute
 *       xpath: '//a'
 *       attribute_options:
 *         name: href
 *       search: 'foo'
 *       replace: 'bar'
 *     -
 *       plugin: dom_str_replace
 *       mode: attribute
 *       xpath: '//a'
 *       attribute_options:
 *         name: href
 *       regex: true
 *       search: '/foo/'
 *       replace: 'bar'
 *     -
 *       plugin: dom_str_replace
 *       mode: element
 *       xpath: '//b'
 *       search: 'b'
 *       replace: 'strong'
 *     -
 *       plugin: dom_str_replace
 *       mode: attribute
 *       xpath: //a
 *       attribute_options:
 *         name: href
 *       regex: true
 *       search: '/foo-(\d+)/'
 *       replace: 'bar-$1'
 *     -
 *       plugin: dom_str_replace
 *       mode: text
 *       xpath: '//a'
 *       search: 'Find more information here'
 *       replace: 'More information'
 *     -
 *       plugin: dom
 *       method: export
 * @endcode
 *
 * @MigrateProcessPlugin(
 *   id = "dom_str_replace"
 * )
 */
class DomStrReplace extends DomProcessBase {

  /**
   * {@inheritdoc}
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->configuration += [
      'case_insensitive' => FALSE,
      'regex' => FALSE,
    ];
    $options_validation = [
      'xpath' => NULL,
      'mode' => [
        'attribute' => [
          'attribute_options' => NULL,
        ],
        'element' => [],
        'text' => [],
      ],
      'search' => NULL,
      'replace' => NULL,
    ];
    foreach ($options_validation as $option_name => $possible_values) {
      if (empty($this->configuration[$option_name])) {
        if ($option_name === 'replace' && isset($this->configuration[$option_name])) {
          // Allow empty string for replace.
          continue;
        }
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          "Configuration option '$option_name' is required."
        );
      }
      if (!empty($possible_values) && !array_key_exists($this->configuration[$option_name], $possible_values)) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          sprintf(
            'Configuration option "%s" only accepts the following values: %s.',
            $option_name,
            implode(', ', array_keys($possible_values))
          )
        );
      }
    }
    $mode = $this->configuration['mode'];
    $mode_validation = $options_validation['mode'][$mode];
    foreach ($mode_validation as $option_name => $possible_values) {
      if (empty($this->configuration[$option_name])) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          "Configuration option '$option_name' is required for mode '$mode'."
        );
      }
      if (!is_null($possible_values) && !in_array($this->configuration[$option_name], $possible_values)) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          sprintf(
            'Configuration option "%s" only accepts the following values: %s.',
            $option_name,
            implode(', ', $possible_values)
          )
        );
      }
    }
    $mode_validation = $options_validation['mode'][$this->configuration['mode']];
    foreach ($mode_validation as $option_name => $possible_values) {
      if (empty($this->configuration[$option_name])) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          "Configuration option '$option_name' is required for mode '$mode'."
        );
      }
      if (!is_null($possible_values) && !in_array($this->configuration[$option_name], $possible_values)) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          sprintf(
            'Configuration option "%s" only accepts the following values: %s.',
            $option_name,
            implode(', ', $possible_values)
          )
        );
      }
    }
    $mode_validation = $options_validation['mode'][$this->configuration['mode']];
    foreach ($mode_validation as $option_name => $possible_values) {
      if (empty($this->configuration[$option_name])) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          "Configuration option '$option_name' is required for mode '$mode'."
        );
      }
      if (!is_null($possible_values) && !in_array($this->configuration[$option_name], $possible_values)) {
        throw new InvalidPluginDefinitionException(
          $this->getPluginId(),
          sprintf(
            'Configuration option "%s" only accepts the following values: %s.',
            $option_name,
            implode(', ', $possible_values)
          )
        );
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
    $this->init($value, $destination_property);

    foreach ($this->xpath->query($this->configuration['xpath']) as $html_node) {
      $subject = $this->getSubject($html_node);
      if (empty($subject)) {
        // Could not find subject, skip processing.
        continue;
      }
      $search = $this->getSearch();
      $replace = $this->getReplace();
      $this->doReplace($html_node, $search, $replace, $subject);
    }

    return $this->document;
  }

  /**
   * Retrieves the right subject string.
   *
   * @param \DOMElement $node
   *   The current element from iteration.
   *
   * @return string
   *   The string to use a subject on search.
   */
  protected function getSubject(\DOMElement $node): string {
    return match ($this->configuration['mode']) {
      'attribute' => $node->getAttribute($this->configuration['attribute_options']['name']),
      'element' => $node->nodeName,
      'text' => $node->textContent,
      default => '',
    };

  }

  /**
   * Retrieves the right search string based on configuration.
   *
   * @return string
   *   The value to be searched.
   */
  protected function getSearch(): string {
    return match ($this->configuration['mode']) {
      'attribute', 'element', 'text' => $this->configuration['search'],
      default => '',
    };

  }

  /**
   * Retrieves the right replace string based on configuration.
   *
   * @return string
   *   The value to use for replacement.
   */
  protected function getReplace(): string {
    return match ($this->configuration['mode']) {
      'attribute', 'element', 'text' => $this->configuration['replace'],
      default => '',
    };

  }

  /**
   * Retrieves the right replace string based on configuration.
   *
   * @param \DOMElement $html_node
   *   The current element from iteration.
   * @param string $search
   *   The search string or pattern.
   * @param string $replace
   *   The replacement string.
   * @param string $subject
   *   The string on which to perform the substitution.
   */
  protected function doReplace(\DOMElement $html_node, string $search, string $replace, string $subject): void {
    if ($this->configuration['regex']) {
      $function = 'preg_replace';
    }
    elseif ($this->configuration['case_insensitive']) {
      $function = 'str_ireplace';
    }
    else {
      $function = 'str_replace';
    }
    $new_subject = $function($search, $replace, $subject);
    $this->postReplace($html_node, $new_subject);
  }

  /**
   * Performs post-replace actions.
   *
   * @param \DOMElement $html_node
   *   The current element from iteration.
   * @param string $new_subject
   *   The new value to use.
   */
  protected function postReplace(\DOMElement $html_node, string $new_subject): void {
    switch ($this->configuration['mode']) {
      case 'attribute':
        $html_node->setAttribute($this->configuration['attribute_options']['name'], $new_subject);
        break;

      case 'element':
        $new_node = $this->document->createElement($new_subject);
        foreach ($html_node->childNodes as $child) {
          $new_node->appendChild($child->cloneNode(TRUE));
        }
        foreach ($html_node->attributes as $attribute) {
          $new_node->setAttribute($attribute->name, $attribute->value);
        }
        $html_node->parentNode->replaceChild($new_node, $html_node);
        break;

      case 'text':
        $html_node->textContent = $new_subject;
        break;
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc