Completed
Push — 7.x-1.x ( 2f9e3c...1070be )
by Frédéric G.
01:36
created

Size   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 167
rs 10
c 0
b 0
f 0
wmc 18
lcom 2
cbo 3

6 Methods

Rating   Name   Duplication   Size   Complexity  
B checkBin() 0 26 3
B checkBinContents() 0 19 5
A init() 0 5 1
A isSchemaCache() 0 14 1
A getAllBins() 0 12 3
B run() 0 52 5
1
<?php
2
3
/**
4
 * @file
5
 * OSInet QA Plugin for cache size.
6
 *
7
 * @copyright Copyright (C) 2011-2018 Frederic G. MARAND for Ouest Systèmes Informatiques (OSInet)
8
 *
9
 * @since DRUPAL-6
10
 *
11
 * @license Licensed under the disjunction of the CeCILL, version 2 and General Public License version 2 and later
12
 */
13
14
namespace Drupal\qa\Plugin\Qa\Control\Cache;
15
16
use Drupal\qa\Plugin\Qa\Control\BaseControl;
17
18
class Size extends BaseControl {
19
  const DATA_SIZE_LIMIT = 524288; // Memcache default entry limit: 1024*1024 * 0.5 for safety
20
  const DATA_SUMMARY_LENGTH = 1024;
21
22
  /**
23
   * @param string $bin_name
24
   *
25
   * @return array
26
   *   - name: the name of the checked bin
27
   *   - status: 0 for KO, 1 for OK
28
   *   - result: information in case of failed check.
29
   */
30
  function checkBin($bin_name) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
31
    $ret = array(
32
      'name' => $bin_name,
33
    );
34
    $ret['status'] = FALSE;
35
    $arg = array('@name' => $bin_name);
36
37
    if (!db_table_exists($bin_name)) {
38
      $ret['result'] = t('Bin @name is missing in the database.', $arg);
39
      return $ret;
40
    }
41
42
    $sql = "SELECT cid, data, expire, created, serialized FROM {$bin_name} ORDER BY cid";
43
    $q = db_query($sql);
44
    if (!$q) {
45
      $ret['result'] = t('Failed fetching database data for bin @name.', $arg);
46
      return $ret;
47
    }
48
49
    list($status, $result) = $this->checkBinContents($q);
50
51
    $ret['status'] = $status;
52
    $ret['result'] = $result;
53
54
    return $ret;
55
  }
56
57
  /**
58
   * Check the contents of an existing and accessible bin.
59
   *
60
   * @param $q
61
   *   The DBTNG query object for the bin contents, already queried.
62
   *
63
   * @return array
64
   *   - 0 : status bool
65
   *   - 1 : result array
66
   */
67
  protected function checkBinContents($q) {
68
    $status = TRUE;
69
    $result = array();
70
    foreach ($q->fetchAll() as $row) {
71
      // Cache drivers will need to serialize anyway.
72
      $data = $row->serialized ? $row->data : serialize($row->data);
73
      $len = strlen($data);
74
      if ($len == 0 || $len >= static::DATA_SIZE_LIMIT) {
75
        $status = FALSE;
76
        $result[] = array(
77
          $row->cid,
78
          number_format($len, 0, ',', ''),
79
          check_plain(drupal_substr($data, 0, static::DATA_SUMMARY_LENGTH)) . '&hellip;',
80
        );
81
      }
82
    }
83
84
    return array($status, $result);
85
  }
86
87
  /**
88
   * {@inheritdoc]
89
   */
90
  public function init() {
91
    $this->package_name = __NAMESPACE__;
92
    $this->title = t('Suspicious cache content');
93
    $this->description = t('Look for empty or extra-long (>= 1 MB) cache content.');
94
  }
95
96
  /**
97
   * Does the passed schema match the expected cache schema structure ?
98
   *
99
   * @param array $schema
100
   *
101
   * @return bool
102
   */
103
  public static function isSchemaCache(array $schema) {
104
    $reference_schema_keys = array(
105
      'cid',
106
      'created',
107
      'data',
108
      'expire',
109
      'serialized'
110
    );
111
    $keys = array_keys($schema['fields']);
112
    sort($keys);
113
    $ret = $keys == $reference_schema_keys;
114
115
    return $ret;
116
  }
117
118
  public static function getAllBins($rebuild = FALSE) {
119
    $schema = drupal_get_complete_schema($rebuild);
120
    $ret = array();
121
    foreach ($schema as $name => $info) {
122
      if (static::isSchemaCache($info)) {
123
        $ret[] = $name;
124
      }
125
    }
126
    sort($ret);
127
128
    return $ret;
129
  }
130
131
  function run() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
132
    $pass = parent::run();
133
    $bins = self::getAllBins(TRUE);
134
    foreach ($bins as $bin_name) {
135
      $pass->record($this->checkBin($bin_name));
136
    }
137
    $pass->life->end();
138
139
    if ($pass->status) {
140
      $pass->result = format_plural(count($bins), '1 bin checked, not containing suspicious values',
141
        '@count bins checked, none containing suspicious values', array());
142
    }
143
    else {
144
      $info = format_plural(count($bins), '1 view checked and containing suspicious values',
145
        '@count bins checked, @bins containing suspicious values', array(
146
          '@bins' => count($pass->result),
147
        ));
148
149
      // Prepare for theming
150
      $result = array();
151
      // @XXX May be inconsistent with non-BMP strings ?
152
      uksort($pass->result, 'strcasecmp');
153
      foreach ($pass->result as $bin_name => $bin_report) {
154
        foreach ($bin_report as $entry) {
155
          array_unshift($entry, $bin_name);
156
          $result[] = $entry;
157
        }
158
      }
159
      $header = array(
160
        t('Bin'),
161
        t('CID'),
162
        t('Length'),
163
        t('Beginning of data'),
164
      );
165
166
      $build = array(
167
        'info' => array(
168
          '#markup' => $info,
169
        ),
170
        'list' => array(
171
          '#markup' => '<p>' . t('Checked: @checked', array('@checked' => implode(', ', $bins))) . "</p>\n",
172
        ),
173
        'table' => array(
174
          '#theme' => 'table',
175
          '#header' => $header,
176
          '#rows' => $result,
177
        )
178
      );
179
      $pass->result = drupal_render($build);
180
    }
181
    return $pass;
182
  }
183
184
}
185