scanner-8.x-1.0-rc3/src/Plugin/Scanner/Entity.php

src/Plugin/Scanner/Entity.php
<?php

namespace Drupal\scanner\Plugin\Scanner;

use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\scanner\Plugin\ScannerPluginBase;

/**
 * A generic Scanner plugin for handling entities.
 *
 * @Scanner(
 *   id = "scanner_entity",
 *   type = "entity",
 * )
 */
class Entity extends ScannerPluginBase {

  use LoggerChannelTrait;
  use MessengerTrait;

  /**
   * The scanner regular expression.
   *
   * @var string
   */
  protected string $scannerRegexChars = '.\/+*?[^]$() {}=!<>|:';

  /**
   * Performs the search operation for the given string/expression.
   *
   * @param string $field
   *   The field with the matching string (formatted as type:bundle:field).
   * @param array $values
   *   An array containing the $form_state values.
   *
   * @return array
   *   An array containing the titles of the entity and a snippet of the
   *   matching text.
   */
  public function search(string $field, array $values): array {
    $data = [];
    [$entityType] = explode(':', $field);

    // Attempt to load the matching plugin for the matching entity.
    try {
      $plugin = $this->scannerManager->createInstance("scanner_$entityType");
      if (empty($plugin)) {
        throw new PluginException('Unable to load entity type ' . $entityType . '.');
      }
      // Perform the search on the current field.
      $results = $plugin->search($field, $values);
      if (!empty($results)) {
        $data = $results;
      }
    }
    catch (PluginException $e) {
      // The instance could not be found so fail gracefully and let the user
      // know.
      $this->getLogger('scanner')->error($e->getMessage());
      $this->messenger()->addError($this->t('An error occurred @e:', ['@e' => $e->getMessage()]));
    }

    return $data;
  }

  /**
   * Performs the replace operation for the given string/expression.
   *
   * @param string $field
   *   The field with the matching string (formatted as type:bundle:field).
   * @param array $values
   *   An array containing the $form_state values.
   * @param array $undo_data
   *   An array containing the data.
   *
   * @return array
   *   An array containing the revision ids of the affected entities.
   */
  public function replace(string $field, array $values, array $undo_data): array {
    $data = [];
    [$entityType] = explode(':', $field);

    try {
      $plugin = $this->scannerManager->createInstance("scanner_$entityType");
      // Perform the replacement on the current field and save results.
      $results = $plugin->replace($field, $values, $undo_data);
      if (!empty($results)) {
        $data = $results;
      }
    }
    catch (PluginException $e) {
      // The instance could not be found so fail gracefully and let the user
      // know.
      $this->getLogger('scanner')->error($e->getMessage());
      $this->messenger()->addError('An error occurred: ' . $e->getMessage());
    }

    return $data;
  }

  /**
   * Undo the replace operation by reverting entities to a previous revision.
   *
   * @param array $data
   *   An array containing the revision ids needed to undo the previous replace
   *   operation.
   */
  public function undo(array $data): void {
    foreach ($data as $key => $value) {
      [$entityType] = explode(':', $key);
      // Attempt to load the matching plugin for the matching entity.
      try {
        $plugin = $this->scannerManager->createInstance("scanner_$entityType");
        $plugin->undo($value);
      }
      catch (PluginException $e) {
        $this->getLogger('scanner')->error($e->getMessage());
        $this->messenger()->addError('An error occurred: ' . $e->getMessage());
      }
    }
  }

  /**
   * Helper function to "build" the proper query condition.
   *
   * @param string $search
   *   The string that is to be searched for.
   * @param bool $mode
   *   The boolean that indicated whether the search should be case-sensitive.
   * @param bool $wholeword
   *   The boolean that indicates whether the search should be word-bounded.
   * @param bool $regex
   *   The boolean that indicates whether the search term is a regular
   *   expression.
   * @param string $preceded
   *   The string for preceded expression.
   * @param string $followed
   *   The string for the succeeding expression.
   *
   * @return array
   *   Returns an associative array keyed by:
   *   - condition: The target ICU regular expression (for database use);
   *   - phpRegex: The target perl-compatible regular expression (for PHP);
   *   - operator: The operator to use in a database condition; usually one of
   *     'REGEXP', 'LIKE', 'REGEXP_LIKE'.
   */
  protected function buildCondition(string $search, bool $mode, bool $wholeword, bool $regex, string $preceded, string $followed): array {
    $preceded_php = '';
    if (!empty($preceded)) {
      if (!$regex) {
        $preceded = addcslashes($preceded, $this->scannerRegexChars);
      }
      $preceded_php = '(?<=' . $preceded . ')';
    }
    $followed_php = '';
    if (!empty($followed)) {
      if (!$regex) {
        $followed = addcslashes($followed, $this->scannerRegexChars);
      }
      $followed_php = '(?=' . $followed . ')';
    }

    $word_boundaries_helper = $this->scannerHelper;
    if ($word_boundaries_helper->whichToUse() === 'icu') {
      $word_boundary_prefix = $word_boundary_suffix = "\\b";
    }
    else {
      // Control which word boundaries are used, used for compatibility with
      // different releases of MySQL.
      // @see https://dev.mysql.com/doc/refman/8.0/en/regexp.html#regexp-compatibility
      // @see https://mariadb.com/kb/en/regular-expressions-overview/#word-boundaries
      $word_boundary_prefix = '[[:<:]]';
      $word_boundary_suffix = '[[:>:]]';
    }

    // Case 1.
    if ($wholeword && $regex) {
      $value = $word_boundary_prefix . $preceded . $search . $followed . $word_boundary_suffix;
      $operator = 'REGEXP';
      $phpRegex = '/\b' . $preceded_php . $search . $followed_php . '\b/';
    }
    // Case 2.
    elseif ($wholeword && !$regex) {
      $value = $word_boundary_prefix . $preceded . addcslashes($search, $this->scannerRegexChars) . $followed . $word_boundary_suffix;
      $operator = 'REGEXP';
      $phpRegex = '/\b' . $preceded_php . addcslashes($search, $this->scannerRegexChars) . $followed . '\b/';
    }
    // Case 3.
    elseif (!$wholeword && $regex) {
      $value = $preceded . $search . $followed;
      $operator = 'REGEXP';
      $phpRegex = '/' . $preceded_php . $search . $followed_php . '/';
    }
    // Case 4.
    else {
      $value = '%' . $preceded . addcslashes($search, $this->scannerRegexChars) . $followed . '%';
      $operator = 'LIKE';
      $phpRegex = '/' . $preceded . addcslashes($search, $this->scannerRegexChars) . $followed . '/';
    }

    if ($mode) {
      if ($operator === 'REGEXP' && $word_boundaries_helper->supportsInlinePcreFlags()) {
        // Check if the database engine supports the inline PCRE regex. If it
        // does, then we'll use that to specify the case-sensitivity rather than
        // the BINARY flag.
        // https://mariadb.com/kb/en/pcre/#option-setting
        $value = "(?-i)$value";
      }
      elseif ($operator === 'REGEXP' && $word_boundaries_helper->shouldUseRegexpLike()) {
        // Check if it supports the "REGEXP BINARY" syntax. If it does not, then
        // we'll use REGEXP_LIKE instead.
        $operator = 'REGEXP_LIKE';
      }
      else {
        $operator .= ' BINARY';
      }
    }
    else {
      $phpRegex .= 'i';
    }

    return [
      'condition' => $value,
      'phpRegex' => $phpRegex,
      'operator' => $operator,
    ];
  }

  /**
   * Ensures the query is limited by the specified search conditions.
   *
   * @param \Drupal\Core\Entity\Query\QueryInterface $query
   *   The query to modify.
   * @param array $condition
   *   An array of condition values as returned by ::buildCondition().
   * @param string $fieldname
   *   The name of the entity field having a condition added.
   * @param bool $mode
   *   TRUE for a case-sensitive search; FALSE otherwise.
   * @param string $language
   *   The language code or 'all' for all languages.
   */
  protected function addQueryCondition(QueryInterface $query, array $condition, string $fieldname, bool $mode, string $language): void {
    if ($language !== 'all') {
      $query->condition('langcode', $language, '=');
    }

    $langcode = $language === 'all' ? NULL : $language;
    if ($condition['operator'] === 'REGEXP_LIKE') {
      // The REGEXP_LIKE() function can't be added directly to the entity query,
      // so we'll add a tag and add the actual condition later.
      /* @see \scanner_query_scanner_search_regexp_like_alter() */
      /* @see \Drupal\scanner\Plugin\Scanner\Entity::addRegexpLikeCondition() */
      $query->addTag('scanner_search_regexp_like')
        ->addMetaData('scanner_search_regexp_like', [
          'entity_type_id' => $query->getEntityTypeId(),
          'fieldname' => $fieldname,
          'langcode' => $langcode,
          'mode' => $mode,
          'pattern' => $condition['condition'],
        ]);
    }
    else {
      $query->condition($fieldname, $condition['condition'], $condition['operator'], $langcode);
    }
  }

}

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

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