These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace Drupal\mongodb_watchdog\Controller; |
||
| 4 | |||
| 5 | use Drupal\Core\Config\ImmutableConfig; |
||
| 6 | use Drupal\mongodb_watchdog\Logger; |
||
| 7 | use MongoDB\BSON\Javascript; |
||
| 8 | use MongoDB\Collection; |
||
| 9 | use MongoDB\Database; |
||
| 10 | use Psr\Log\LoggerInterface; |
||
| 11 | use Symfony\Component\DependencyInjection\ContainerInterface; |
||
| 12 | use Symfony\Component\HttpFoundation\Request; |
||
| 13 | |||
| 14 | /** |
||
| 15 | * The Top403/Top404 controllers. |
||
| 16 | */ |
||
| 17 | class TopController extends ControllerBase { |
||
| 18 | const TYPES = [ |
||
| 19 | 'page not found', |
||
| 20 | 'access denied', |
||
| 21 | ]; |
||
| 22 | |||
| 23 | /** |
||
| 24 | * The database holding the logger collections. |
||
| 25 | * |
||
| 26 | * @var \MongoDB\Database |
||
| 27 | */ |
||
| 28 | protected $database; |
||
| 29 | |||
| 30 | /** |
||
| 31 | * TopController constructor. |
||
| 32 | * |
||
| 33 | * @param \Psr\Log\LoggerInterface $logger |
||
| 34 | * The logger service, to log intervening events. |
||
| 35 | * @param \Drupal\mongodb_watchdog\Logger $watchdog |
||
| 36 | * The MongoDB logger, to load stored events. |
||
| 37 | * @param \Drupal\Core\Config\ImmutableConfig $config |
||
| 38 | * The module configuration. |
||
| 39 | * @param \MongoDB\Database $database |
||
| 40 | * Needed because there is no group() command in phplib yet. |
||
| 41 | * |
||
| 42 | * @see https://jira.mongodb.org/browse/PHPLIB-177 |
||
| 43 | */ |
||
| 44 | public function __construct( |
||
| 45 | LoggerInterface $logger, |
||
| 46 | Logger $watchdog, |
||
| 47 | ImmutableConfig $config, |
||
| 48 | Database $database) { |
||
| 49 | parent::__construct($logger, $watchdog, $config); |
||
| 50 | |||
| 51 | $this->database = $database; |
||
| 52 | } |
||
| 53 | |||
| 54 | /** |
||
| 55 | * Controller. |
||
| 56 | * |
||
| 57 | * @param \Symfony\Component\HttpFoundation\Request $request |
||
| 58 | * The current request. |
||
| 59 | * @param string $type |
||
| 60 | * The type of top report to produce. |
||
| 61 | * |
||
| 62 | * @return array |
||
|
0 ignored issues
–
show
|
|||
| 63 | * A render array. |
||
| 64 | */ |
||
| 65 | View Code Duplication | public function build(Request $request, $type) { |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 66 | $top = $this->getTop(); |
||
| 67 | |||
| 68 | $rows = $this->getRowData($request, $type); |
||
| 69 | $main = empty($rows) |
||
| 70 | ? [ |
||
| 71 | '#markup' => t('No "%type" message found', ['%type' => $type]), |
||
| 72 | '#prefix' => '<div class="mongodb_watchdog__message">', |
||
| 73 | '#suffix' => '</div>', |
||
| 74 | ] |
||
| 75 | : $this->buildMainTable($rows); |
||
| 76 | |||
| 77 | $ret = $this->buildDefaults($main, $top); |
||
| 78 | return $ret; |
||
| 79 | } |
||
| 80 | |||
| 81 | /** |
||
| 82 | * Build the main table. |
||
| 83 | * |
||
| 84 | * @param array $rows |
||
| 85 | * The event data. |
||
| 86 | * |
||
| 87 | * @return array<string,string|array> |
||
|
0 ignored issues
–
show
|
|||
| 88 | * A render array for the main table. |
||
| 89 | */ |
||
| 90 | View Code Duplication | protected function buildMainTable(array $rows) { |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 91 | $ret = [ |
||
| 92 | '#header' => $this->buildMainTableHeader(), |
||
| 93 | '#rows' => $this->buildMainTableRows($rows), |
||
| 94 | '#type' => 'table', |
||
| 95 | ]; |
||
| 96 | return $ret; |
||
| 97 | } |
||
| 98 | |||
| 99 | /** |
||
| 100 | * Build the main table header. |
||
| 101 | * |
||
| 102 | * @return \Drupal\Core\StringTranslation\TranslatableMarkup[] |
||
| 103 | * A table header array. |
||
| 104 | */ |
||
| 105 | protected function buildMainTableHeader() { |
||
| 106 | $header = [ |
||
| 107 | t('#'), |
||
| 108 | t('Paths'), |
||
| 109 | ]; |
||
| 110 | |||
| 111 | return $header; |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Build the main table rows. |
||
| 116 | * |
||
| 117 | * @param array[] $counts |
||
| 118 | * The array of counts per 403/404 page. |
||
| 119 | * |
||
| 120 | * @return array<string,array|string> |
||
| 121 | * A render array for a table. |
||
| 122 | */ |
||
| 123 | protected function buildMainTableRows($counts) { |
||
|
0 ignored issues
–
show
|
|||
| 124 | $rows = []; |
||
| 125 | foreach ($counts as $count) { |
||
| 126 | $row = [ |
||
| 127 | $count['count'], |
||
| 128 | $count['variables.@uri'], |
||
| 129 | ]; |
||
| 130 | $rows[] = $row; |
||
| 131 | } |
||
| 132 | |||
| 133 | return $rows; |
||
| 134 | } |
||
| 135 | |||
| 136 | /** |
||
| 137 | * {@inheritdoc} |
||
| 138 | */ |
||
| 139 | View Code Duplication | public static function create(ContainerInterface $container) { |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 140 | /** @var \Psr\Log\LoggerInterface $logger */ |
||
| 141 | $logger = $container->get('logger.channel.mongodb_watchdog'); |
||
| 142 | |||
| 143 | /** @var \Drupal\mongodb_watchdog\Logger $watchdog */ |
||
| 144 | $watchdog = $container->get('mongodb.logger'); |
||
| 145 | |||
| 146 | /** @var \Drupal\Core\Config\ImmutableConfig $config */ |
||
| 147 | $config = $container->get('config.factory')->get('mongodb_watchdog.settings'); |
||
| 148 | |||
| 149 | /** @var \MongoDB\Database $database */ |
||
| 150 | $database = $container->get('mongodb.watchdog_storage'); |
||
| 151 | |||
| 152 | return new static($logger, $watchdog, $config, $database); |
||
| 153 | } |
||
| 154 | |||
| 155 | /** |
||
| 156 | * Obtain the data from the logger. |
||
| 157 | * |
||
| 158 | * @param \Symfony\Component\HttpFoundation\Request $request |
||
| 159 | * The current request. Needed for paging. |
||
| 160 | * @param string $type |
||
| 161 | * The type of top list to retrieve. |
||
| 162 | * |
||
| 163 | * @return array |
||
| 164 | * The data array. |
||
| 165 | */ |
||
| 166 | protected function getRowData(Request $request, $type) { |
||
| 167 | // Find _id for the error type. |
||
| 168 | $templateCollection = $this->watchdog->templateCollection(); |
||
| 169 | $template = $templateCollection->findOne(['type' => $type], ['_id']); |
||
| 170 | if (empty($template)) { |
||
| 171 | return []; |
||
| 172 | } |
||
| 173 | |||
| 174 | // Find occurrences of error type. |
||
| 175 | $collectionName = $template['_id']; |
||
| 176 | $eventCollection = $this->watchdog->eventCollection($collectionName); |
||
| 177 | |||
| 178 | $key = ['variables.@uri' => 1]; |
||
| 179 | $cond = []; |
||
| 180 | $reducer = <<<JAVASCRIPT |
||
| 181 | function reducer(doc, accumulator) { |
||
| 182 | accumulator.count++; |
||
| 183 | } |
||
| 184 | JAVASCRIPT; |
||
| 185 | |||
| 186 | $initial = ['count' => 0]; |
||
| 187 | $counts = $this->group($eventCollection, $key, $cond, $reducer, $initial); |
||
| 188 | if (empty($counts['ok'])) { |
||
| 189 | return []; |
||
| 190 | } |
||
| 191 | |||
| 192 | $counts = $counts['retval']; |
||
| 193 | usort($counts, [$this, 'topSort']); |
||
| 194 | |||
| 195 | $page = $this->setupPager($request, count($counts)); |
||
| 196 | $skip = $page * $this->itemsPerPage; |
||
| 197 | $counts = array_slice($counts, $skip, $this->itemsPerPage); |
||
| 198 | |||
| 199 | return $counts; |
||
| 200 | } |
||
| 201 | |||
| 202 | /** |
||
| 203 | * Command wrapper for missing MongoDB group() implementation in PHPlib. |
||
| 204 | * |
||
| 205 | * @param \MongoDB\Collection $collection |
||
| 206 | * The collection on which to perform the command. |
||
| 207 | * @param object $key |
||
| 208 | * The grouping key. |
||
| 209 | * @param object $cond |
||
| 210 | * The condition. |
||
| 211 | * @param string $reduce |
||
| 212 | * The reducer function: must be valid JavaScript code. |
||
| 213 | * @param object $initial |
||
| 214 | * The initial document. |
||
| 215 | * |
||
| 216 | * @return array|void |
||
| 217 | * Void in case of error, otherwise an array with the following keys: |
||
| 218 | * - waitedMS: time spent waiting |
||
| 219 | * - retval: an array of command results, containing at least the key |
||
| 220 | * - count: the total number of documents matched |
||
| 221 | * - keys: the number of different keys, normally matching count(retval) |
||
| 222 | * - ok: 1.0 in case of success. |
||
| 223 | */ |
||
| 224 | public function group(Collection $collection, $key, $cond, $reduce, $initial) { |
||
| 225 | $cursor = $this->database->command([ |
||
| 226 | 'group' => [ |
||
| 227 | 'ns' => $collection->getCollectionName(), |
||
| 228 | 'key' => $key, |
||
| 229 | 'cond' => $cond, |
||
| 230 | 'initial' => $initial, |
||
| 231 | '$reduce' => new Javascript($reduce), |
||
| 232 | ], |
||
| 233 | ], Logger::LEGACY_TYPE_MAP); |
||
| 234 | |||
| 235 | $ret = $cursor->toArray(); |
||
| 236 | $ret = reset($ret); |
||
| 237 | return $ret; |
||
| 238 | } |
||
| 239 | |||
| 240 | /** |
||
| 241 | * Callback for usort() to sort top entries returned from a group query. |
||
| 242 | * |
||
| 243 | * @param array $first |
||
| 244 | * The first value to compare. |
||
| 245 | * @param array $second |
||
| 246 | * The second value to compare. |
||
| 247 | * |
||
| 248 | * @return int |
||
| 249 | * The comparison result. |
||
| 250 | * |
||
| 251 | * @see \Drupal\mongodb_watchdog\Controller\TopController::build() |
||
| 252 | */ |
||
| 253 | protected function topSort(array $first, array $second) { |
||
| 254 | return $second['count'] <=> $first['count']; |
||
| 255 | } |
||
| 256 | |||
| 257 | } |
||
| 258 |
This check looks for the generic type
arrayas a return type and suggests a more specific type. This type is inferred from the actual code.