public function process_db_object($id, wpdb $db)
 {
     global $EZSQL_ERROR;
     $rows = array();
     $types = array();
     $total_time = 0;
     $has_result = false;
     $has_trace = false;
     $i = 0;
     foreach ((array) $db->queries as $query) {
         # @TODO: decide what I want to do with this:
         if (false !== strpos($query[2], 'wp_admin_bar') and !isset($_REQUEST['qm_display_admin_bar'])) {
             continue;
         }
         $sql = $query[0];
         $ltime = $query[1];
         $stack = $query[2];
         $has_trace = isset($query['trace']);
         $has_result = isset($query['result']);
         if (isset($query['result'])) {
             $result = $query['result'];
         } else {
             $result = null;
         }
         $total_time += $ltime;
         if (isset($query['trace'])) {
             $trace = $query['trace'];
             $component = $query['trace']->get_component();
             $caller = $query['trace']->get_caller();
             $caller_name = $caller['id'];
             $caller = $caller['display'];
         } else {
             $trace = null;
             $component = null;
             $callers = explode(',', $stack);
             $caller = trim(end($callers));
             if (false !== strpos($caller, '(')) {
                 $caller_name = substr($caller, 0, strpos($caller, '(')) . '()';
             } else {
                 $caller_name = $caller;
             }
         }
         $sql = trim($sql);
         $type = QM_Util::get_query_type($sql);
         $this->log_type($type);
         $this->log_caller($caller_name, $ltime, $type);
         $this->maybe_log_dupe($sql, $i);
         if ($component) {
             $this->log_component($component, $ltime, $type);
         }
         if (!isset($types[$type]['total'])) {
             $types[$type]['total'] = 1;
         } else {
             $types[$type]['total']++;
         }
         if (!isset($types[$type]['callers'][$caller])) {
             $types[$type]['callers'][$caller] = 1;
         } else {
             $types[$type]['callers'][$caller]++;
         }
         $row = compact('caller', 'caller_name', 'stack', 'sql', 'ltime', 'result', 'type', 'component', 'trace');
         if (is_wp_error($result)) {
             $this->data['errors'][] = $row;
         }
         if (self::is_expensive($row)) {
             $this->data['expensive'][] = $row;
         }
         $rows[$i] = $row;
         $i++;
     }
     if ('$wpdb' === $id && !$has_result && !empty($EZSQL_ERROR) && is_array($EZSQL_ERROR)) {
         // Fallback for displaying database errors when wp-content/db.php isn't in place
         foreach ($EZSQL_ERROR as $error) {
             $row = array('caller' => 'Unknown', 'caller_name' => 'Unknown', 'stack' => '', 'sql' => $error['query'], 'result' => new WP_Error('qmdb', $error['error_str']), 'type' => '', 'component' => false, 'trace' => null);
             $this->data['errors'][] = $row;
         }
     }
     $total_qs = count($rows);
     $this->data['total_qs'] += $total_qs;
     $this->data['total_time'] += $total_time;
     # @TODO put errors in here too:
     # @TODO proper class instead of (object)
     $this->data['dbs'][$id] = (object) compact('rows', 'types', 'has_result', 'has_trace', 'total_time', 'total_qs');
 }
 public function output()
 {
     $data = $this->collector->get_data();
     if (empty($data['dupes'])) {
         return;
     }
     $colspan = empty($data['dupe_components']) ? 4 : 5;
     echo '<div class="qm" id="' . esc_attr($this->collector->id()) . '">';
     echo '<table cellspacing="0">';
     echo '<thead>';
     echo '<tr>';
     echo '<th colspan="' . absint($colspan) . '">' . esc_html($this->collector->name()) . '</th>';
     echo '</tr>';
     echo '<tr>';
     echo '<th>' . esc_html__('Query', 'query-monitor') . '</th>';
     echo '<th class="qm-num">' . esc_html__('Count', 'query-monitor') . '</th>';
     echo '<th>' . esc_html__('Callers', 'query-monitor') . '</th>';
     if (!empty($data['dupe_components'])) {
         echo '<th>' . esc_html__('Components', 'query-monitor') . '</th>';
     }
     echo '<th>' . esc_html__('Potential Troublemakers', 'query-monitor') . '</th>';
     echo '</tr>';
     echo '</thead>';
     echo '<tbody>';
     /* translators: %s: Number of calls to a PHP function */
     $call_text = _n_noop('%s call', '%s calls', 'query-monitor');
     foreach ($data['dupes'] as $sql => $queries) {
         // This should probably happen in the collector's processor
         $type = QM_Util::get_query_type($sql);
         $sql_out = self::format_sql($sql);
         if ('SELECT' !== $type) {
             $sql_out = "<span class='qm-nonselectsql'>{$sql_out}</span>";
         }
         echo '<tr>';
         echo '<td class="qm-row-sql qm-ltr qm-wrap">';
         echo $sql_out;
         // WPCS: XSS ok;
         echo '</td>';
         echo '<td class="qm-num">';
         echo esc_html(number_format_i18n(count($queries), 0));
         echo '</td>';
         echo '<td class="qm-row-caller qm-nowrap qm-ltr">';
         foreach ($data['dupe_callers'][$sql] as $caller => $calls) {
             printf('<a href="#" class="qm-filter-trigger" data-qm-target="db_queries-wpdb" data-qm-filter="caller" data-qm-value="%s">%s</a><br><span class="qm-info">&nbsp;%s</span><br>', esc_attr($caller), esc_html($caller), esc_html(sprintf(translate_nooped_plural($call_text, $calls, 'query-monitor'), number_format_i18n($calls))));
         }
         echo '</td>';
         if (isset($data['dupe_components'][$sql])) {
             echo '<td class="qm-row-component qm-nowrap">';
             foreach ($data['dupe_components'][$sql] as $component => $calls) {
                 printf('%s<br><span class="qm-info">&nbsp;%s</span><br>', esc_html($component), esc_html(sprintf(translate_nooped_plural($call_text, $calls, 'query-monitor'), number_format_i18n($calls))));
             }
             echo '</td>';
         }
         echo '<td class="qm-row-caller qm-nowrap qm-ltr">';
         foreach ($data['dupe_sources'][$sql] as $source => $calls) {
             printf('%s<br><span class="qm-info">&nbsp;%s</span><br>', esc_html($source), esc_html(sprintf(translate_nooped_plural($call_text, $calls, 'query-monitor'), number_format_i18n($calls))));
         }
         echo '</td>';
         echo '</tr>';
     }
     echo '</tbody>';
     echo '</table>';
     echo '</div>';
 }