database = $database; $this->configFactory = $config_factory; } /** * {@inheritdoc} */ public function logRequest($path, $langcode) { if (mb_strlen($path) > static::MAX_PATH_LENGTH) { // Don't attempt to log paths that would result in an exception. There is // no point in logging truncated paths, as they cannot be used to build a // new redirect. return; } // Ignore invalid UTF-8, which can't be logged. if (!Unicode::validateUtf8($path)) { return; } // If the request is not new, update its count and timestamp. $this->database->merge('redirect_404') ->key('path', $path) ->key('langcode', $langcode) ->expression('count', 'count + 1') ->fields([ 'timestamp' => REQUEST_TIME, 'count' => 1, 'resolved' => 0, ]) ->execute(); } /** * {@inheritdoc} */ public function resolveLogRequest($path, $langcode) { $this->database->update('redirect_404') ->fields(['resolved' => 1]) ->condition('path', $path) ->condition('langcode', $langcode) ->execute(); } /** * {@inheritdoc} */ public function purgeOldRequests() { $row_limit = $this->configFactory->get('redirect_404.settings')->get('row_limit'); // In admin form 0 used as value for 'All' label. if ($row_limit == 0) { return; } $query = $this->database->select('redirect_404', 'r404'); $query->fields('r404', ['timestamp']); // On databases known to support log(), use it to calculate a logarithmic // scale of the count, to delete records with count of 1-9 first, then // 10-99 and so on. if ($this->database->driver() == 'mysql' || $this->database->driver() == 'pgsql') { $query->addExpression('floor(log(10, count))', 'count_log'); $query->orderBy('count_log', 'DESC'); } $query->orderBy('timestamp', 'DESC'); $cutoff = $query ->range($row_limit, 1) ->execute() ->fetchAssoc(); if (!empty($cutoff)) { // Delete records having older timestamp and less visits (on a logarithmic // scale) than cutoff. $delete_query = $this->database->delete('redirect_404'); if ($this->database->driver() == 'mysql' || $this->database->driver() == 'pgsql') { // Delete rows with same count_log AND older timestamp than cutoff. $and_condition = $delete_query->andConditionGroup() ->where('floor(log(10, count)) = :count_log2', [':count_log2' => $cutoff['count_log']]) ->condition('timestamp', $cutoff['timestamp'], '<='); // And delete all the rows with count_log less than the cutoff. $condition = $delete_query->orConditionGroup() ->where('floor(log(10, count)) < :count_log1', [':count_log1' => $cutoff['count_log']]) ->condition($and_condition); $delete_query->condition($condition); } else { $delete_query->condition('timestamp', $cutoff['timestamp'], '<='); } $delete_query->execute(); } } /** * {@inheritdoc} */ public function listRequests(array $header = [], $search = NULL) { $query = $this->database ->select('redirect_404', 'r404') ->extend('Drupal\Core\Database\Query\TableSortExtender') ->orderByHeader($header) ->extend('Drupal\Core\Database\Query\PagerSelectExtender') ->limit(25) ->fields('r404'); if ($search) { // Replace wildcards with PDO wildcards. // @todo Find a way to write a nicer pattern. $wildcard = '%' . trim(preg_replace('!\*+!', '%', $this->database->escapeLike($search)), '%') . '%'; $query->condition('path', $wildcard, 'LIKE'); } $results = $query->condition('resolved', 0, '=')->execute()->fetchAll(); return $results; } }