Passed
Pull Request — 8.x-1.x (#18)
by Frédéric G.
05:46
created

Size::checkBin()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 23
rs 9.7666
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
namespace Drupal\qa\Plugin\Qa\Control\Cache;
4
5
use Drupal\qa\Plugin\Qa\Control\BaseControl;
6
7
class Size extends BaseControl {
8
9
  const DATA_SIZE_LIMIT = 524288; // Memcache default entry limit: 1024*1024 * 0.5 for safety
10
11
  const DATA_SUMMARY_LENGTH = 1024;
12
13
  /**
14
   * @param string $bin_name
15
   *
16
   * @return array
17
   *   - name: the name of the checked bin
18
   *   - status: 0 for KO, 1 for OK
19
   *   - result: information in case of failed check.
20
   */
21
  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...
22
    $ret = ['name' => $bin_name,];
23
    $ret['status'] = FALSE;
24
    $arg = ['@name' => $bin_name];
25
26
    if (!db_table_exists($bin_name)) {
0 ignored issues
show
Bug introduced by
The function db_table_exists was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

26
    if (!/** @scrutinizer ignore-call */ db_table_exists($bin_name)) {
Loading history...
27
      $ret['result'] = t('Bin @name is missing in the database.', $arg);
28
      return $ret;
29
    }
30
31
    $sql = "SELECT cid, data, expire, created, serialized FROM {$bin_name} ORDER BY cid";
32
    $q = db_query($sql);
0 ignored issues
show
Bug introduced by
The function db_query was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

32
    $q = /** @scrutinizer ignore-call */ db_query($sql);
Loading history...
33
    if (!$q) {
34
      $ret['result'] = t('Failed fetching database data for bin @name.', $arg);
35
      return $ret;
36
    }
37
38
    [$status, $result] = $this->checkBinContents($q);
39
40
    $ret['status'] = $status;
41
    $ret['result'] = $result;
42
43
    return $ret;
44
  }
45
46
  /**
47
   * Check the contents of an existing and accessible bin.
48
   *
49
   * @param $q
50
   *   The DBTNG query object for the bin contents, already queried.
51
   *
52
   * @return array
53
   *   - 0 : status bool
54
   *   - 1 : result array
55
   */
56
  protected function checkBinContents($q) {
57
    $status = TRUE;
58
    $result = [];
59
    foreach ($q->fetchAll() as $row) {
60
      // Cache drivers will need to serialize anyway.
61
      $data = $row->serialized ? $row->data : serialize($row->data);
62
      $len = strlen($data);
63
      if ($len == 0 || $len >= static::DATA_SIZE_LIMIT) {
64
        $status = FALSE;
65
        $result[] = [
66
          $row->cid,
67
          number_format($len, 0, ',', ''),
68
          check_plain(drupal_substr($data, 0,
0 ignored issues
show
Bug introduced by
The function check_plain was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

68
          /** @scrutinizer ignore-call */ 
69
          check_plain(drupal_substr($data, 0,
Loading history...
Bug introduced by
The function drupal_substr was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

68
          check_plain(/** @scrutinizer ignore-call */ drupal_substr($data, 0,
Loading history...
69
            static::DATA_SUMMARY_LENGTH)) . '&hellip;',
70
        ];
71
      }
72
    }
73
74
    return [$status, $result];
75
  }
76
77
  /**
78
   * {@inheritdoc]
79
   */
80
  public function init() {
81
    $this->package_name = __NAMESPACE__;
82
    $this->title = t('Suspicious cache content');
83
    $this->description = t('Look for empty or extra-long (>= 1 MB) cache content.');
84
  }
85
86
  /**
87
   * Does the passed schema match the expected cache schema structure ?
88
   *
89
   * @param array $schema
90
   *
91
   * @return bool
92
   */
93
  public static function isSchemaCache(array $schema) {
94
    $reference_schema_keys = [
95
      'cid',
96
      'created',
97
      'data',
98
      'expire',
99
      'serialized',
100
    ];
101
    $keys = array_keys($schema['fields']);
102
    sort($keys);
103
    $ret = $keys == $reference_schema_keys;
104
105
    return $ret;
106
  }
107
108
  public static function getAllBins($rebuild = FALSE) {
109
    $schema = drupal_get_complete_schema($rebuild);
0 ignored issues
show
Bug introduced by
The function drupal_get_complete_schema was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

109
    $schema = /** @scrutinizer ignore-call */ drupal_get_complete_schema($rebuild);
Loading history...
110
    $ret = [];
111
    foreach ($schema as $name => $info) {
112
      if (static::isSchemaCache($info)) {
113
        $ret[] = $name;
114
      }
115
    }
116
    sort($ret);
117
118
    return $ret;
119
  }
120
121
  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...
122
    $pass = parent::run();
123
    $bins = self::getAllBins(TRUE);
124
    foreach ($bins as $bin_name) {
125
      $pass->record($this->checkBin($bin_name));
0 ignored issues
show
Bug introduced by
$this->checkBin($bin_name) of type array is incompatible with the type Drupal\qa\Result|null expected by parameter $checkResult of Drupal\qa\Pass::record(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

125
      $pass->record(/** @scrutinizer ignore-type */ $this->checkBin($bin_name));
Loading history...
126
    }
127
    $pass->life->end();
128
129
    if ($pass->status) {
0 ignored issues
show
Bug introduced by
The property status does not seem to exist on Drupal\qa\Pass.
Loading history...
130
      $pass->result = format_plural(count($bins),
0 ignored issues
show
Bug introduced by
The function format_plural was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

130
      $pass->result = /** @scrutinizer ignore-call */ format_plural(count($bins),
Loading history...
131
        '1 bin checked, not containing suspicious values',
132
        '@count bins checked, none containing suspicious values', []);
133
    }
134
    else {
135
      $info = format_plural(count($bins),
136
        '1 view checked and containing suspicious values',
137
        '@count bins checked, @bins containing suspicious values', [
138
          '@bins' => count($pass->result),
139
        ]);
140
141
      // Prepare for theming
142
      $result = [];
143
      // @XXX May be inconsistent with non-BMP strings ?
144
      uksort($pass->result, 'strcasecmp');
145
      foreach ($pass->result as $bin_name => $bin_report) {
146
        foreach ($bin_report as $entry) {
147
          array_unshift($entry, $bin_name);
148
          $result[] = $entry;
149
        }
150
      }
151
      $header = [
152
        t('Bin'),
153
        t('CID'),
154
        t('Length'),
155
        t('Beginning of data'),
156
      ];
157
158
      $build = [
159
        'info' => [
160
          '#markup' => $info,
161
        ],
162
        'list' => [
163
          '#markup' => '<p>' . t('Checked: @checked',
164
              ['@checked' => implode(', ', $bins)]) . "</p>\n",
165
        ],
166
        'table' => [
167
          '#theme' => 'table',
168
          '#header' => $header,
169
          '#rows' => $result,
170
        ],
171
      ];
172
      $pass->result = drupal_render($build);
0 ignored issues
show
Bug introduced by
The function drupal_render was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

172
      $pass->result = /** @scrutinizer ignore-call */ drupal_render($build);
Loading history...
173
    }
174
    return $pass;
175
  }
176
177
}
178