Completed
Pull Request — 8.x-2.x (#5)
by Frédéric G.
02:46
created

modules/mongodb_watchdog/mongodb_watchdog.admin.inc::mongodb_watchdog_event()   F

Complexity

Conditions 10
Paths 288

Size

Total Lines 101
Code Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 80
c 2
b 0
f 0
nc 288
nop 1
dl 0
loc 101
rs 3.1304

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @file
4
 *   Administrative page callbacks for the Database Logging module.
5
 */
6
7
use Drupal\Core\Logger\RfcLogLevel;
8
9
/**
10
 * Display watchdogs entry details in MongoDB.
11
 *
12
 * @param array $dblog
13
 */
14
function mongodb_watchdog_event($dblog) {
15
  $severity = watchdog_severity_levels();
16
  $rows = array(
17
    array(
18
      array('data' => t('Type'), 'header' => TRUE),
19
      t($dblog['type']),
20
    ),
21
    array(
22
      array('data' => t('Severity'), 'header' => TRUE),
23
      $severity[$dblog['severity']],
24
    ),
25
    array(
26
      array('data' => t('Function'), 'header' => TRUE),
27
      isset($dblog['function']) ? $dblog['function'] : '',
28
    ),
29
    array(
30
      array('data' => t('File'), 'header' => TRUE),
31
      isset($dblog['file']) ? $dblog['file'] : '',
32
    ),
33
    array(
34
      array('data' => t('Line'), 'header' => TRUE),
35
      isset($dblog['line']) ? $dblog['line'] : '',
36
    ),
37
    array(
38
      array('data' => t('Count'), 'header' => TRUE),
39
      isset($dblog['count']) ? $dblog['count'] : '',
40
    ),
41
  );
42
  $build['reports'] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$build was never initialized. Although not strictly required by PHP, it is generally a good practice to add $build = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
43
    '#type' => 'markup',
44
    '#markup' => l(t('Return to log report'), 'admin/reports/mongodb'),
45
  );
46
  $build['mongodb_watchdog_event_table']['header'] = array(
47
    '#theme' => 'table',
48
    '#rows' => $rows,
49
    '#attributes' => array('class' => array('dblog-event')),
50
  );
51
  // @todo: the count is unreliable, so just get the actual number of entries.
52
//$total = min($dblog['count'], variable_get('mongodb_watchdog_items', 10000));
53
  $collection = mongodb_collection(variable_get('mongodb_watchdog', 'watchdog'));
54
  $collection = $collection->db->selectCollection('watchdog_event_' . $dblog['_id']);
55
  $total = $collection->count();
56
  $limit = 20;
57
  $pagenumber = mongodb_watchdog_pager_init(0, $limit, $total);
58
  $result = $collection
59
    ->find()
60
    ->skip($pagenumber * $limit)
61
    ->limit($limit)
62
    ->sort(array('$natural' => -1));
63
  $severity = watchdog_severity_levels();
0 ignored issues
show
Unused Code introduced by
$severity is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
64
  $rows = array();
65
  $header = array(
66
    array('data' => t('Date'), 'header' => TRUE),
67
    array('data' => t('User'), 'header' => TRUE),
68
    array('data' => t('Location'), 'header' => TRUE),
69
    array('data' => t('Referrer'), 'header' => TRUE),
70
    array('data' => t('Hostname'), 'header' => TRUE),
71
    array('data' => t('Message'), 'header' => TRUE),
72
    array('data' => t('Operations'), 'header' => TRUE),
73
  );
74
  foreach ($result as $event) {
75
    if (isset($event['wd-user'])) {
76
      $account = $event['wd-user'];
77
      unset($event['wd-user']);
78
      $ip = $dblog['ip'];
79
      $request_uri = $dblog['request_uri'];
80
      $referer = $dblog['referer'];
81
      $link = $dblog['link'];
82
      $dblog['variables'] = $event;
83
    }
84
    else {
85
      $account = $event['user'];
86
      $ip = $event['ip'];
87
      $request_uri = $event['request_uri'];
88
      $referer = $event['referer'];
89
      $link = $event['link'];
90
      $dblog['variables'] = $event['variables'];
91
    }
92
    $rows[] = array(
93
      format_date($event['timestamp'], 'short'),
94
      l($account['name'], 'user/' . $account['uid']),
95
      $request_uri ? l(truncate_utf8(basename(($request_uri)), 20), $request_uri) : '',
96
      $referer ? l(truncate_utf8(basename(($referer)), 20), $referer) : '',
97
      check_plain($ip),
98
      _mongodb_watchdog_format_message($dblog),
99
      $link,
100
    );
101
  }
102
  $build['mongodb_watchdog_event_table']['messages'] = array(
103
    '#theme' => 'table',
104
    '#header' => $header,
105
    '#rows' => $rows,
106
  );
107
  if ($total > $limit) {
108
    $build['mongodb_watchdog_event_table']['pager'] = array(
109
      '#theme' => 'pager',
110
    );
111
112
  }
113
  return $build;
114
}
115
116
/**
117
 * Initialize the global pager variables for use in a mongodb_watchdog event list.
118
 *
119
 * @param int $element
120
 * @param int $limit
121
 * @param int $total
122
 * 
123
 * @return int
124
 */
125
function mongodb_watchdog_pager_init($element, $limit, $total) {
126
  global $pager_page_array, $pager_total, $pager_total_items;
127
128
  // Initialize pager, see pager.inc.
129
  $page = isset($_GET['page']) ? $_GET['page'] : '';
130
  $pager_page_array = explode(',', $page);
131
  if (!isset($pager_page_array[$element])) {
132
    $pager_page_array[$element] = 0;
133
  }
134
  $pager_total_items[$element] = $total;
135
  $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
136
  $pager_page_array[$element] = max(0, min((int) $pager_page_array[$element], ((int) $pager_total[$element]) - 1));
137
  return isset($pager_page_array[$element]) ? $pager_page_array[$element] : 0;
138
}
139
140
/**
141
 * Formats a log message for display.
142
 *
143
 * @param $dblog
144
 *   An object with at least the message and variables properties
145
 *
146
 * @return string
147
 */
148
function _mongodb_watchdog_format_message($dblog) {
149
  // Legacy messages and user specified text
150
  if (!isset($dblog['variables'])) {
151
    return $dblog['message'];
152
  }
153
  // Message to translate with injected variables
154
  return t($dblog['message'], $dblog['variables']);
155
}
156
157
/**
158
 * Creates a list of database log administration filters that can be applied.
159
 *
160
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
161
 *   Associative array of filters. The top-level keys are used as the form
162
 *   element names for the filters, and the values are arrays with the following
163
 *   elements:
164
 *   - title: Title of the filter.
165
 *   - where: The filter condition.
166
 *   - options: Array of options for the select list for the filter.
167
 */
168
function mongodb_watchdog_filters() {
169
  $filters = array();
170
171
  foreach (_dblog_get_message_types() as $type) {
172
    $types[$type] = t($type);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$types was never initialized. Although not strictly required by PHP, it is generally a good practice to add $types = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
173
  }
174
175
  if (!empty($types)) {
176
    $filters['type'] = array(
177
      'title' => t('Type'),
178
      'where' => "w.type = ?",
179
      'options' => $types,
180
    );
181
  }
182
183
  $filters['severity'] = array(
184
    'title' => t('Severity'),
185
    'where' => 'w.severity = ?',
186
    'options' => RfcLogLevel::getLevels(),
187
  );
188
189
  return $filters;
190
}
191
192
/**
193
 * Build the filter form.
194
 *
195
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
196
 *   A form array
197
 */
198
function mongodb_watchdog_filter_form($form) {
199
  $filters = mongodb_watchdog_filters();
200
201
  $form['filters'] = array(
202
    '#type' => 'fieldset',
203
    '#title' => t('Filter log messages'),
204
    '#collapsible' => TRUE,
205
    '#collapsed' => empty($_SESSION),
206
    '#attached' => array(
207
      'css' => array(
208
        drupal_get_path('module', 'mongodb_watchdog') . '/mongodb_watchdog.css',
209
    )),
210
  );
211
212 View Code Duplication
  foreach ($filters as $key => $filter) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
213
    $form['filters']['status'][$key] = array(
214
      '#title' => check_plain($filter['title']),
215
      '#type' => 'select',
216
      '#multiple' => TRUE,
217
      '#size' => 8,
218
      '#options' => $filter['options'],
219
    );
220
    if (!empty($_SESSION['mongodb_watchdog_overview_filter'][$key])) {
221
      $form['filters']['status'][$key]['#default_value'] = $_SESSION['mongodb_watchdog_overview_filter'][$key];
222
    }
223
  }
224
225
  $form['filters']['buttons']['submit'] = array(
226
    '#type' => 'submit',
227
    '#value' => t('Filter'),
228
  );
229
  if (!empty($_SESSION['mongodb_watchdog_overview_filter'])) {
230
    $form['filters']['buttons']['reset'] = array(
231
      '#type' => 'submit',
232
      '#value' => t('Reset')
233
    );
234
  }
235
236
  return $form;
237
}
238
239
/**
240
 * Validate result from mongodb_watchdog administration filter form.
241
 */
242
function mongodb_watchdog_filter_form_validate($form, &$form_state) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
243
  if ($form_state['values']['op'] == t('Filter') && empty($form_state['values']['type']) && empty($form_state['values']['severity'])) {
244
    form_set_error('type', t('You must select something to filter by.'));
245
  }
246
}
247
248
/**
249
 * Process result from mongodb_watchdog administration filter form.
250
 */
251
function mongodb_watchdog_filter_form_submit($form, &$form_state) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
252
  $op = $form_state['values']['op'];
253
  $filters = mongodb_watchdog_filters();
254
  switch ($op) {
255
    case t('Filter'):
256
      foreach ($filters as $name => $filter) {
257
        if (isset($form_state['values'][$name])) {
258
          $_SESSION['mongodb_watchdog_overview_filter'][$name] = $form_state['values'][$name];
259
        }
260
      }
261
      break;
262
263
    case t('Reset'):
264
      $_SESSION['mongodb_watchdog_overview_filter'] = array();
265
      break;
266
  }
267
  return 'admin/reports/mongodb';
268
}
269
270
/**
271
 * Gets all available filter types.
272
 *
273
 * @return array
274
 *   An array of message type names.
275
 */
276
function _mongodb_watchdog_get_message_types() {
277
  // As of version 1.0.1, the PHP driver doesn't expose the 'distinct' command.
278
  $collection = mongodb_collection(variable_get('mongodb_watchdog', 'watchdog'));
279
  $result = $collection->db->command(array('distinct' => $collection->getName(), 'key' => 'type'));
0 ignored issues
show
Bug introduced by
The method getName does only exist in MongoCollection, but not in MongoDebugCollection and MongoDummy.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
280
  return $result['values'];
281
}
282
283
/**
284
 * Return form for mongodb_watchdog clear button.
285
 *
286
 * @ingroup forms
287
 * @see dblog_clear_log_submit()
288
 *
289
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<string,array>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
290
 *   A form array.
291
 */
292
function mongodb_watchdog_clear_log_form($form) {
293
  $form['mongodb_watchdog_clear'] = array(
294
    '#type' => 'fieldset',
295
    '#title' => t('Clear log messages'),
296
    '#description' => t('This will permanently remove the log messages from the database.'),
297
    '#collapsible' => TRUE,
298
    '#collapsed' => TRUE,
299
  );
300
301
  $form['mongodb_watchdog_clear']['clear'] = array(
302
    '#type' => 'submit',
303
    '#value' => t('Clear log messages'),
304
    '#submit' => array('mongodb_watchdog_clear_log_submit'),
305
  );
306
307
  return $form;
308
}
309
310
/**
311
 * Submit callback: clear database with log messages.
312
 */
313
function mongodb_watchdog_clear_log_submit() {
314
  try {
315
    // Drop the watchdog collection.
316
    $collection = mongodb_collection(variable_get('mongodb_watchdog', 'watchdog'));
317
    $collection->db->dropCollection($collection->getName());
0 ignored issues
show
Bug introduced by
The method getName does only exist in MongoCollection, but not in MongoDebugCollection and MongoDummy.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
318
319
    // Recreate the indexes.
320
    module_load_include('install', 'mongodb_watchdog');
321
    mongodb_watchdog_ensure_indexes();
322
323
    // Drop the event collections.
324
    foreach ($collection->db->listCollections() as $table) {
325
      $parts = explode('.', $table);
326
      if (substr($parts[1], 0, 15) == 'watchdog_event_') {
327
        $collection->db->dropCollection($table);
328
      }
329
    }
330
331
    drupal_set_message(t('MongoDB log cleared.'));
332
  }
333
  catch (Exception $e) {
334
    drupal_set_message(t('An error occured while clearing the MongoDB log.'), 'error');
335
  }
336
}
337
338
/**
339
 * Build a MongoDB query based on the selected filters.
340
 *
341
 * Refer to the @link https://jira.mongodb.org/browse/PHP-1051 Mongo Issue regarding the $in value @endlink
342
 * Refer to the @link https://jira.mongodb.org/browse/PHP-104 Mongo Issue regarding numeric keys on objects @endlink
343
 * @return array
344
 *   An array to build a MongoDB query.
345
 */
346
function mongodb_watchdog_build_filter_query() {
347
  if (empty($_SESSION['mongodb_watchdog_overview_filter'])) {
348
    return array();
349
  }
350
351
  // Build query.
352
  $where = $args = array();
0 ignored issues
show
Unused Code introduced by
$args is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code introduced by
$where is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
353
  $types = $_SESSION['mongodb_watchdog_overview_filter']['type'] ? $_SESSION['mongodb_watchdog_overview_filter']['type'] : NULL;
354
  $severities = $_SESSION['mongodb_watchdog_overview_filter']['severity'] ? $_SESSION['mongodb_watchdog_overview_filter']['severity'] : NULL;
355
356
  $find = array();
357
  if ($types) {
358
    $find['type'] = array('$in' => array_values($types));
359
  }
360
  if ($severities) {
361
    // MongoDB is picky about types, ensure the severities are all integers.
362
    $find['severity'] = array('$in' => array_values(array_map('intval', $severities)));
363
  }
364
  return $find;
365
}
366