deprecation_status-8.x-3.x-dev/src/Form/ErrorsForm.php

src/Form/ErrorsForm.php
<?php

namespace Drupal\deprecation_status\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\Core\Utility\TableSort;
use Drupal\deprecation_status\DataSource;
use Symfony\Component\HttpFoundation\RedirectResponse;

class ErrorsForm extends FormBase {

  /**
   * Stored sorting criteria.
   *
   * @type int
   */
  protected $column;

  /**
   * Stored sorting criteria ('asc' or 'desc').
   *
   * @type string
   */
  protected $sort;

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'deprecation_status_errors_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $form['#cache']['tags'][] = 'deprecation_status';
    $form['#attached']['library'][] = 'deprecation_status/deprecation_status.lists';

    $form['filters'] = [
      '#type' => 'details',
      '#title' => $this->t('Filter errors'),
      '#attributes' => ['class' => ['container-inline']],
      '#open' => TRUE,
    ];

    $form['filters']['error'] = [
      '#title' => 'Error',
      '#type' => 'textfield',
      '#default_value' => @$_GET['error'],
      '#attributes' => ['placeholder' => "Use *, eg. '*Entity*'"],
      '#size' => 20,
    ];
    $form['filters']['names'] = [
      '#title' => 'Names',
      '#type' => 'textfield',
      '#default_value' => @$_GET['names'],
      '#attributes' => ['placeholder' => "Use *, eg. 'commerce*'"],
      '#size' => 20,
    ];
    $form['filters']['category'] = [
      '#title' => 'Category',
      '#type' => 'select',
      '#options' => [
        'Info.yml or composer.json' => 'Info.yml or composer.json',
        'Drupal API, rector covered' => 'Drupal API, rector covered',
        'Drupal API, not rector covered' => 'Drupal API, not rector covered',
        'Symfony API' => 'Symfony API',
        'Twig API' => 'Twig API',
        'PHPUnit API' => 'PHPUnit API',
        'Guzzle API' => 'Guzzle API',
        'Frontend API' => 'Frontend API',
        'Other problem' => 'Other problem',
        'Parse error' => 'Parse error'
      ],
      '#default_value' => @$_GET['category'],
      '#required' => FALSE,
      '#empty_option' => '- No filter -',
    ];
    $form['filters']['topx'] = [
      '#title' => 'Top X by usage',
      '#type' => 'select',
      '#options' => [],
      '#default_value' => @$_GET['topx'],
      '#required' => FALSE,
      '#empty_option' => '- No filter -',
    ];
    foreach([50, 100, 200, 500, 1000] as $number) {
      $form['filters']['topx']['#options'][$number] = '<= ' . $number;
    }
    $form['filters']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
    ];

    $common_script = <<<SCRIPTEND
SCRIPTEND;
    $form['chart'] = [
      '#children' => <<<SCRIPTEND
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.6.2/dist/chart.min.js"></script>
<div id="charts">
<canvas id="total8vs9"></canvas>
<canvas id="totalDeprecations"></canvas>
</div>
<script type="text/javascript">
var ctx = document.getElementById('totalDeprecations').getContext('2d');
var chart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: [@totalDeprecationsLabels],
    datasets: [{
      data: [@totalDeprecationsData],
      backgroundColor: ['#e9d8a6', '#fdae61', '#f46d43', '#d73027', '#a50026', '#e0f3f8', '#abd9e9', '#74add1', '#4575b4', '#313695']
    }]
  },
  options: {
    aspectRatio: window.innerWidth < 800 ? 1.5 : 2,
    responsive: false,
    plugins: {
      title: {
        display: true,
        text: '@deprecatedcount occurences of errors found',
        position: 'bottom'
      },
      legend: {
        display: true,
        position: window.innerWidth < 800 ? 'top' : 'right',
        onClick: null
      }
    }
  }
});
var ctx = document.getElementById('total8vs9').getContext('2d');
var chart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: [@8vs9Labels],
    datasets: [{
      data: [@8vs9DeprecationsData],
      backgroundColor: ["#4393c3", "#053061"]
    }]
  },
  options: {
    aspectRatio: window.innerWidth < 800 ? 1.5 : 2,
    responsive: false,
    plugins: {
      title: {
        display: true,
        text: '@8vs9dcount total Drupal deprecation occurences',
        position: 'bottom'
      },
      legend: {
        display: true,
        position: window.innerWidth < 800 ? 'top' : 'right',
        onClick: null
      }
    }
  }
});
</script>
SCRIPTEND
    ];

    // Read all errors into one big array.
    $errors = [];
    $file = fopen(DataSource::getFullPath('errors_detail.csv'), 'r');
    $i = 0;
    while ($line = fgetcsv($file, 0, ";")) {
      // Skip the header row.
      if ($i > 0) {
        $errors[] = $line;
      }
      $i++;
    }
    fclose($file);

    // Set up columns for the table.
    $columns = [
      'total' => 'All occurences',
      'projects' => 'Affected projects',
      'top' => 'Occurs in a top X project by usage',
      'category' => 'Category',
      'message' => 'Message',
    ];
    if (!empty($_GET['names']) || !empty($_GET['topx'])) {
      $columns['filtered_total'] = 'Filtered occurrences';
      $columns['filtered_projects'] = 'Filtered affected projects';
    }
    // Add sorting information to columns.
    $i = 0;
    foreach ($columns as $name => &$column) {
      $column = ['data' => $column, 'field' => $i];
      $i++;
    }
    // Sort by total descending initially.
    $columns['total']['sort'] = 'desc';

    $errors = $this->tableFilter($errors);
    if (count($errors) == 0) {
      unset($form['chart']);
    }
    else {
      $summary = $this->dataSummary($errors);
      $deprecation_labels = "'" . join("','", array_keys($summary['errors'])) . "'";
      $deprecation_data = join(',', array_values($summary['errors']));
      $deprecation_count = $summary['total'];
      $drupal89_labels = "'" . join("','", array_keys($summary['drupal'])) . "'";
      $drupal89_data = join(',', array_values($summary['drupal']));
      $drupal89_count = $summary['total_drupal'];
      $form['chart']['#children'] = str_replace(
        [
         '@deprecatedcount', '@totalDeprecationsLabels', '@totalDeprecationsData',
         '@8vs9dcount', '@8vs9Labels', '@8vs9DeprecationsData'
        ],
        [
          $deprecation_count, $deprecation_labels, $deprecation_data,
          $drupal89_count, $drupal89_labels, $drupal89_data
        ],
        $form['chart']['#children']
      );
    }

    // Sort the data array based on query tablesort information.
    $order = TableSort::getOrder($columns, \Drupal::request());
    $this->column = $order['sql'] + ($order['sql'] > 4 ? 2 : 0);
    $this->sort = TableSort::getSort($columns, \Drupal::request());
    uasort($errors, [$this, 'tableSort']);

    // Display the resulting data.
    $form['table'] = [
      '#type' => 'table',
      '#header' => $columns,
      '#empty' => t('No matching errors found.'),
    ];

    // Page the results based on query information.
    $page = \Drupal::service('pager.parameters')->findPage();
    $num_per_page = 30;
    \Drupal::service('pager.manager')->createPager(count($errors), $num_per_page);
    $offset = $num_per_page * $page;
    $errors_page = array_slice($errors, $offset, $num_per_page);

    // Key results based on header key names.
    $keys = array_keys($columns);
    foreach ($errors_page as $error) {
      $error_link = [
        'data' => [
          '#type' => 'link',
          '#title' => $error[1],
          '#url' => Url::fromRoute('deprecation_status.error', ['error_index' => $error[6]]),
        ]
      ];
      $error_class = 'warning';
      if ($error[3] == 'Drupal API, rector covered') {
        $error_class = 'passed';
      }
      elseif ($error[3] == 'Parse error') {
        $error_class = 'error';
      }
      elseif ($error[3] == 'Other problem') {
        $error_class = 'unknown';
      }

      $row = [
        '#attributes' => ['class' => ['item-' . $error_class]],
        'total' => ['#type' => 'markup', '#markup' => $error[0]],
        'projects' => $error_link,
        'top' => ['#type' => 'markup', '#markup' => $error[2]],
        'category' => ['#type' => 'markup', '#markup' =>  '<span class="category">' . $error[3] . '</span>'],
        'message' => ['#type' => 'markup', '#markup' => DataSource::formatError($error[4])],
      ];
      if (!empty($_GET['names']) || !empty($_GET['topx'])) {
        $row['filtered_total'] = ['#type' => 'markup', '#markup' => $error[7]];
        $query = [];
        if (!empty($_GET['names'])) {
          $query['names'] = $_GET['names'];
        }
        if (!empty($_GET['topx'])) {
          $query['topx'] = $_GET['topx'];
        }
        $row['filtered_projects'] = [
          'data' => [
            '#type' => 'link',
            '#title' => $error[8],
            '#url' => Url::fromRoute('deprecation_status.error', ['error_index' => $error[6]], ['query' => $query]),
          ]
        ];
      }
      $form['table'][] = $row;
    }

    $form['pager'] = array(
      '#type' => 'pager'
    );

    $form['data_info'] = DataSource::getDataInfo();
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $keys = ['error', 'names', 'category', 'topx'];
    $submitted = array_filter($form_state->getValues());
    $query = [];
    foreach ($keys as $key) {
      if (!empty($submitted[$key])) {
        $query[$key] = $submitted[$key];
      }
    }
    $form_state->setRedirect('deprecation_status.errors_form', [], ['query' => $query]);
  }

  /**
   * Custom table sorting based on stored sorting criteria.
   */
  protected function tableSort($a, $b) {
    if ($a[$this->column] == $b[$this->column]) {
      return 0;
    }
    $cmp = ($a[$this->column] < $b[$this->column]) ? -1 : 1;
    return ($this->sort == 'desc') ? $cmp * -1 : $cmp;
  }

  /**
   * Custom table filter based on form values.
   */
  protected function tableFilter($errors) {
    $keys = ['error', 'names', 'category', 'topx'];
    foreach ($keys as $key) {
      $$key = @$_GET[$key];
    }

    // Process project name matching for preg_match() later.
    $name_list = [];
    if (!empty($names)) {
      $all_names = explode(',', $names);
      foreach($all_names as $one_name) {
        $parts = explode('*', trim($one_name));
        $processed_parts = [];
        foreach ($parts as $part) {
          $processed_parts[] = preg_quote($part, '!');
        }
        $name_list[] = join('.*', $processed_parts);
      }
    }

    // Process error message matching for preg_match() later.
    $message = '';
    if (!empty($error)) {
      $parts = explode('*', trim($error));
      $processed_parts = [];
      foreach ($parts as $part) {
        $processed_parts[] = preg_quote($part, '!');
      }
      $message = join('.*', $processed_parts);
    }

    $filtered = [];
    foreach ($errors as $index => $error) {
      // Assume this row is a match.
      $match = TRUE;
      $counts = [];

      // Go through each filter and if the row is still a match, see if this
      // filter will invalidate it.
      if (!empty($message) && $match) {
        $match = preg_match("!^$message$!", $error[4]);
      }
      if (!empty($category) && $match) {
        $match = ($error[3] == $category);
      }
      if (!empty($topx) && $match) {
        if ($error[2] <= $topx) {
          // If the group was a match for the topmost group, extract affected projects and counts.
          $projects = explode(', ', $error[5]);
          foreach ($projects as $item) {
            if ($topx == 50 && preg_match('!\[50\] \((\d+)\)$!', $item, $found)) {
              $counts[$item] = $found[1];
            }
            if ($topx == 100 && preg_match('!\[(50|100)\] \((\d+)\)$!', $item, $found)) {
              $counts[$item] = $found[2];
            }
            if ($topx == 200 && preg_match('!\[(50|100|200)\] \((\d+)\)$!', $item, $found)) {
              $counts[$item] = $found[2];
            }
            if ($topx == 500 && preg_match('!\[(50|100|200|500)\] \((\d+)\)$!', $item, $found)) {
              $counts[$item] = $found[2];
            }
            if ($topx == 1000 && preg_match('!\[(50|100|200|500|1000)\] \((\d+)\)$!', $item, $found)) {
              $counts[$item] = $found[2];
            }
          }
        }
        else {
          // The group was not a match.
          $match = FALSE;
        }
      }
      if (!empty($name_list) && $match) {
        $error_projects = explode(', ', $error[5]);
        // Assume match will become false and only set to TRUE if explicit.
        $match = FALSE;
        foreach ($error_projects as $item) {
          list($error_project, $error_data) = explode(' [', $item);
          foreach($name_list as $name) {
            if (preg_match("!^$name$!", $error_project)) {
              list($nil, $error_number1) = explode('(', $error_data);
              list($error_number) = explode(')', $error_number1);
              $counts[$item] = $error_number;
              $match = TRUE;
              break;
            }
          }
        }
      }

      // If the row was still a match, continue.
      if ($match) {
        $error[] = $index;
        if (!empty($names) || !empty($topx)) {
          $error[] = (int) array_sum($counts);
          $error[] = (int) count($counts);
        }
        $filtered[] = $error;
      }
    }

    return $filtered;
  }

  /**
   * Summarize results from $projects for charts.
   *
   * @param array $projects
   *   Project data array
   *
   * @return array
   *   Summary array with project, total and errors / next step breakdown.
   */
  protected function dataSummary($errors) {
    $summary = [
      'total' => 0,
      'errors' => [
        'Info.yml or composer.json' => 0,
        'Drupal API, rector covered' => 0,
        'Drupal API, not rector covered' => 0,
        'Symfony API' => 0,
        'Twig API' => 0,
        'PHPUnit API' => 0,
        'Guzzle API' => 0,
        'Frontend API' => 0,
        'Other problem' => 0,
        'Parse error' => 0,
      ],
      'total_drupal' => 0,
      'drupal' => [
        'Drupal 8 deprecated API' => 0,
        'Drupal 9 deprecated API' => 0,
      ]
    ];
    foreach($errors as $error) {
      //$this_count = !empty($error[6]) ? $error[6] : $error[0];
      $summary['total'] += $error[0];
      $summary['errors'][$error[3]] += $error[0];
      if (preg_match('!(in|iin|as of) [Dd]rupal[ :]([89]).[0-9x]!', $error[4], $major_deprecated_version)) {
        $summary['total_drupal'] += $error[0];
        $summary['drupal']['Drupal ' . $major_deprecated_version[2] . ' deprecated API'] += $error[0];
      }
    }

    // Update error results with percentages in labels.
    $errors = [];
    foreach ($summary['errors'] as $key => $result) {
      $percentage = !empty($summary['total']) ? round($result / ($summary['total'] / 100), 2) : 0;
      $errors[$key . ' (' . $percentage . '%)'] = $result;
    }
    $summary['errors'] = $errors;

    // Update Drupal results with percentages in labels.
    if ($summary['total_drupal'] > 0) {
      $errors = [];
      foreach ($summary['drupal'] as $key => $result) {
        $percentage = round($result / ($summary['total_drupal'] / 100), 2);
        $errors[$key . ' (' . $percentage . '%)'] = $result;
      }
      $summary['drupal'] = $errors;
    }

    return $summary;
  }

}

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

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