1
|
|
|
<?php |
2
|
|
|
/* vim: set expandtab sw=4 ts=4 sts=4: */ |
3
|
|
|
|
4
|
|
|
/** |
5
|
|
|
* An incident a representing a single incident of a submited bug. |
6
|
|
|
* |
7
|
|
|
* phpMyAdmin Error reporting server |
8
|
|
|
* Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/) |
9
|
|
|
* |
10
|
|
|
* Licensed under The MIT License |
11
|
|
|
* For full copyright and license information, please see the LICENSE.txt |
12
|
|
|
* Redistributions of files must retain the above copyright notice. |
13
|
|
|
* |
14
|
|
|
* @copyright Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/) |
15
|
|
|
* @license https://opensource.org/licenses/mit-license.php MIT License |
16
|
|
|
* |
17
|
|
|
* @see https://www.phpmyadmin.net/ |
18
|
|
|
*/ |
19
|
|
|
|
20
|
|
|
namespace App\Model\Table; |
21
|
|
|
|
22
|
|
|
use Cake\Log\Log; |
23
|
|
|
use Cake\Model\Model; |
24
|
|
|
use Cake\ORM\Table; |
25
|
|
|
use Cake\ORM\TableRegistry; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* An incident a representing a single incident of a submited bug. |
29
|
|
|
*/ |
30
|
|
|
class IncidentsTable extends Table |
31
|
|
|
{ |
32
|
|
|
/** |
33
|
|
|
* @var array |
34
|
|
|
* |
35
|
|
|
* @see http://book.cakephp.org/2.0/en/models/behaviors.html#using-behaviors |
36
|
|
|
* @see Model::$actsAs |
37
|
|
|
*/ |
38
|
|
|
public $actsAs = array('Summarizable'); |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var array |
42
|
|
|
* |
43
|
|
|
* @see http://book.cakephp.org/2.0/en/models/model-attributes.html#validate |
44
|
|
|
* @see http://book.cakephp.org/2.0/en/models/data-validation.html |
45
|
|
|
* @see Model::$validate |
46
|
|
|
*/ |
47
|
|
|
public $validate = array( |
48
|
|
|
'pma_version' => array( |
49
|
|
|
'rule' => 'notEmpty', |
50
|
|
|
'required' => true, |
51
|
|
|
), |
52
|
|
|
'php_version' => array( |
53
|
|
|
'rule' => 'notEmpty', |
54
|
|
|
'required' => true, |
55
|
|
|
), |
56
|
|
|
'full_report' => array( |
57
|
|
|
'rule' => 'notEmpty', |
58
|
|
|
'required' => true, |
59
|
|
|
), |
60
|
|
|
'stacktrace' => array( |
61
|
|
|
'rule' => 'notEmpty', |
62
|
|
|
'required' => true, |
63
|
|
|
), |
64
|
|
|
'browser' => array( |
65
|
|
|
'rule' => 'notEmpty', |
66
|
|
|
'required' => true, |
67
|
|
|
), |
68
|
|
|
'stackhash' => array( |
69
|
|
|
'rule' => 'notEmpty', |
70
|
|
|
'required' => true, |
71
|
|
|
), |
72
|
|
|
'user_os' => array( |
73
|
|
|
'rule' => 'notEmpty', |
74
|
|
|
'required' => true, |
75
|
|
|
), |
76
|
|
|
'locale' => array( |
77
|
|
|
'rule' => 'notEmpty', |
78
|
|
|
'required' => true, |
79
|
|
|
), |
80
|
|
|
'script_name' => array( |
81
|
|
|
'rule' => 'notEmpty', |
82
|
|
|
'required' => true, |
83
|
|
|
), |
84
|
|
|
'server_software' => array( |
85
|
|
|
'rule' => 'notEmpty', |
86
|
|
|
'required' => true, |
87
|
|
|
), |
88
|
|
|
'configuration_storage' => array( |
89
|
|
|
'rule' => 'notEmpty', |
90
|
|
|
'required' => true, |
91
|
|
|
), |
92
|
|
|
); |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @var array |
96
|
|
|
* |
97
|
|
|
* @see http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#belongsto |
98
|
|
|
* @see Model::$belongsTo |
99
|
|
|
*/ |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* The fields which are summarized in the report page with charts and are also |
103
|
|
|
* used in the overall stats and charts for the website. |
104
|
|
|
* |
105
|
|
|
* @var array |
106
|
|
|
*/ |
107
|
|
|
public $summarizableFields = array( |
108
|
|
|
'browser', 'pma_version', 'php_version', |
109
|
|
|
'locale', 'server_software', 'user_os', 'script_name', |
110
|
|
|
'configuration_storage', |
111
|
|
|
); |
112
|
|
|
|
113
|
23 |
|
public function __construct($id = false, $table = null, $ds = null) |
114
|
|
|
{ |
115
|
23 |
|
parent::__construct($id, $table, $ds); |
|
|
|
|
116
|
|
|
|
117
|
23 |
|
$this->filterTimes = array( |
|
|
|
|
118
|
23 |
|
'all_time' => array( |
119
|
|
|
'label' => 'All Time', |
120
|
|
|
'limit' => null, |
121
|
|
|
'group' => "DATE_FORMAT(Incidents.created, '%m %Y')", |
122
|
|
|
), |
123
|
|
|
'day' => array( |
124
|
23 |
|
'label' => 'Last Day', |
125
|
23 |
|
'limit' => date('Y-m-d', strtotime('-1 day')), |
126
|
23 |
|
'group' => "DATE_FORMAT(Incidents.created, '%a %b %d %Y %H')", |
127
|
|
|
), |
128
|
|
|
'week' => array( |
129
|
23 |
|
'label' => 'Last Week', |
130
|
23 |
|
'limit' => date('Y-m-d', strtotime('-1 week')), |
131
|
23 |
|
'group' => "DATE_FORMAT(Incidents.created, '%a %b %d %Y')", |
132
|
|
|
), |
133
|
|
|
'month' => array( |
134
|
23 |
|
'label' => 'Last Month', |
135
|
23 |
|
'limit' => date('Y-m-d', strtotime('-1 month')), |
136
|
23 |
|
'group' => "DATE_FORMAT(Incidents.created, '%a %b %d %Y')", |
137
|
|
|
), |
138
|
|
|
'year' => array( |
139
|
23 |
|
'label' => 'Last Year', |
140
|
23 |
|
'limit' => date('Y-m-d', strtotime('-1 year')), |
141
|
23 |
|
'group' => "DATE_FORMAT(Incidents.created, '%b %u %Y')", |
142
|
|
|
), |
143
|
|
|
); |
144
|
23 |
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* creates an incident/report record given a raw bug report object. |
148
|
|
|
* |
149
|
|
|
* This gets a decoded bug report from the submitted json body. This has not |
150
|
|
|
* yet been santized. It either adds it as an incident to another report or |
151
|
|
|
* creates a new report if nothing matches. |
152
|
|
|
* |
153
|
|
|
* @param array $bugReport the bug report being submitted |
154
|
|
|
* |
155
|
|
|
* @return array of: |
156
|
|
|
* 1. array of inserted incident ids. If the report/incident was not |
157
|
|
|
* correctly saved, false is put in it place. |
158
|
|
|
* 2. array of newly created report ids. If no new report was created, |
159
|
|
|
* an empty array is returned |
160
|
|
|
*/ |
161
|
1 |
|
public function createIncidentFromBugReport($bugReport) |
162
|
|
|
{ |
163
|
1 |
|
if ($bugReport == null) { |
164
|
|
|
return array(false); |
165
|
|
|
} |
166
|
1 |
|
$incident_ids = array(); // array to hold ids of all the inserted incidents |
167
|
1 |
|
$new_report_ids = array(); // array to hold ids of all newly created reports |
168
|
|
|
|
169
|
|
|
// Also sanitizes the bug report |
170
|
1 |
|
$schematizedIncidents = $this->_getSchematizedIncidents($bugReport); |
171
|
1 |
|
$incidentsTable = TableRegistry::get('Incidents'); |
172
|
1 |
|
$reportsTable = TableRegistry::get('Reports'); |
173
|
1 |
|
foreach ($schematizedIncidents as $index => $si) { |
174
|
|
|
|
175
|
|
|
// find closest report. If not found, create a new report. |
176
|
1 |
|
$closestReport = $this->_getClosestReport($bugReport, $index); |
177
|
1 |
|
if ($closestReport) { |
|
|
|
|
178
|
1 |
|
$si['report_id'] = $closestReport['id']; |
179
|
1 |
|
$si = $incidentsTable->newEntity($si); |
180
|
1 |
|
$si->created = date('Y-m-d H:i:s', time()); |
|
|
|
|
181
|
1 |
|
$si->modified = date('Y-m-d H:i:s', time()); |
|
|
|
|
182
|
|
|
|
183
|
1 |
|
$this->_logLongIncidentSubmissions($si, $incident_ids); |
184
|
1 |
|
if (in_array(false, $incident_ids)) { |
185
|
1 |
|
break; |
186
|
|
|
} |
187
|
|
|
} else { |
188
|
|
|
// no close report. Create a new report. |
189
|
1 |
|
$report = $this->_getReportDetails($bugReport, $index); |
190
|
|
|
|
191
|
1 |
|
$this->_logLongIncidentSubmissions($si, $incident_ids); |
192
|
1 |
|
if (in_array(false, $incident_ids)) { |
193
|
|
|
break; |
194
|
|
|
} |
195
|
|
|
|
196
|
1 |
|
$report = $reportsTable->newEntity($report); |
197
|
1 |
|
$report->created = date('Y-m-d H:i:s', time()); |
|
|
|
|
198
|
1 |
|
$report->modified = date('Y-m-d H:i:s', time()); |
|
|
|
|
199
|
1 |
|
$reportsTable->save($report); |
200
|
|
|
|
201
|
1 |
|
$si['report_id'] = $report->id; |
|
|
|
|
202
|
1 |
|
$new_report_ids[] = $report->id; |
|
|
|
|
203
|
1 |
|
$si = $incidentsTable->newEntity($si); |
204
|
1 |
|
$si->created = date('Y-m-d H:i:s', time()); |
|
|
|
|
205
|
1 |
|
$si->modified = date('Y-m-d H:i:s', time()); |
|
|
|
|
206
|
|
|
} |
207
|
|
|
|
208
|
1 |
|
$isSaved = $incidentsTable->save($si); |
209
|
1 |
|
if ($isSaved) { |
210
|
1 |
|
array_push($incident_ids, $si->id); |
|
|
|
|
211
|
1 |
|
if (!$closestReport) { |
|
|
|
|
212
|
|
|
// add notifications entry |
213
|
1 |
|
$tmpIncident = $incidentsTable->findById($si->id)->all()->first(); |
|
|
|
|
214
|
1 |
|
if (!TableRegistry::get('Notifications')->addNotifications(intval($tmpIncident['report_id']))) { |
215
|
|
|
Log::write( |
216
|
|
|
'error', |
217
|
|
|
'ERRORED: Notification::addNotifications() failed on Report#' |
218
|
|
|
. $tmpIncident['report_id'], |
219
|
1 |
|
'alert' |
220
|
|
|
); |
221
|
|
|
} |
222
|
|
|
} |
223
|
|
|
} else { |
224
|
1 |
|
array_push($incident_ids, false); |
225
|
|
|
} |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
return array( |
229
|
1 |
|
'incidents' => $incident_ids, |
230
|
1 |
|
'reports' => $new_report_ids |
231
|
|
|
); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* retrieves the closest report to a given bug report. |
236
|
|
|
* |
237
|
|
|
* it checks for another report with the same line number, filename and |
238
|
|
|
* pma_version |
239
|
|
|
* |
240
|
|
|
* @param array $bugReport the bug report being checked |
241
|
|
|
* Integer $index: for php exception type |
242
|
|
|
* @param mixed $index |
243
|
|
|
* |
244
|
|
|
* @return array the first similar report or null |
245
|
|
|
*/ |
246
|
2 |
|
protected function _getClosestReport($bugReport, $index = 0) |
247
|
|
|
{ |
248
|
2 |
|
if (isset($bugReport['exception_type']) |
249
|
2 |
|
&& $bugReport['exception_type'] == 'php' |
250
|
|
|
) { |
251
|
1 |
|
$location = $bugReport['errors'][$index]['file']; |
252
|
1 |
|
$linenumber = $bugReport['errors'][$index]['lineNum']; |
253
|
|
|
} else { |
254
|
|
|
list($location, $linenumber) = |
255
|
2 |
|
$this->_getIdentifyingLocation($bugReport['exception']['stack']); |
256
|
|
|
} |
257
|
2 |
|
$report = TableRegistry::get('Reports')->findByLocationAndLinenumberAndPmaVersion( |
258
|
|
|
$location, $linenumber, |
259
|
2 |
|
$this->getStrippedPmaVersion($bugReport['pma_version']) |
260
|
2 |
|
)->all()->first(); |
261
|
|
|
|
262
|
2 |
|
return $report; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* creates the report data from an incident that has no related report. |
267
|
|
|
* |
268
|
|
|
* @param array $bugReport the bug report the report record is being created for |
269
|
|
|
* Integer $index: for php exception type |
270
|
|
|
* @param mixed $index |
271
|
|
|
* |
272
|
|
|
* @return array an array with the report fields can be used with Report->save |
273
|
|
|
*/ |
274
|
2 |
|
protected function _getReportDetails($bugReport, $index = 0) |
275
|
|
|
{ |
276
|
2 |
|
if (isset($bugReport['exception_type']) |
277
|
2 |
|
&& $bugReport['exception_type'] == 'php' |
278
|
|
|
) { |
279
|
2 |
|
$location = $bugReport['errors'][$index]['file']; |
280
|
2 |
|
$linenumber = $bugReport['errors'][$index]['lineNum']; |
281
|
|
|
$reportDetails = array( |
282
|
2 |
|
'error_message' => $bugReport['errors'][$index]['msg'], |
283
|
2 |
|
'error_name' => $bugReport['errors'][$index]['type'], |
284
|
|
|
); |
285
|
2 |
|
$exception_type = 1; |
286
|
|
|
} else { |
287
|
|
|
list($location, $linenumber) = |
288
|
2 |
|
$this->_getIdentifyingLocation($bugReport['exception']['stack']); |
289
|
|
|
|
290
|
|
|
$reportDetails = array( |
291
|
2 |
|
'error_message' => $bugReport['exception']['message'], |
292
|
2 |
|
'error_name' => $bugReport['exception']['name'], |
293
|
|
|
); |
294
|
2 |
|
$exception_type = 0; |
295
|
|
|
} |
296
|
|
|
|
297
|
2 |
|
$reportDetails = array_merge( |
298
|
|
|
$reportDetails, |
299
|
|
|
array( |
300
|
2 |
|
'status' => 'new', |
301
|
2 |
|
'location' => $location, |
302
|
2 |
|
'linenumber' => $linenumber, |
303
|
2 |
|
'pma_version' => $bugReport['pma_version'], |
304
|
2 |
|
'exception_type' => $exception_type, |
305
|
|
|
) |
306
|
|
|
); |
307
|
|
|
|
308
|
2 |
|
return $reportDetails; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* creates the incident data from the submitted bug report. |
313
|
|
|
* |
314
|
|
|
* @param array $bugReport the bug report the report record is being created for |
315
|
|
|
* |
316
|
|
|
* @return array an array of schematized incident. |
317
|
|
|
* Can be used with Incident->save |
318
|
|
|
*/ |
319
|
2 |
|
protected function _getSchematizedIncidents($bugReport) |
320
|
|
|
{ |
321
|
|
|
//$bugReport = Sanitize::clean($bugReport, array('escape' => false)); |
322
|
2 |
|
$schematizedReports = array(); |
323
|
|
|
$schematizedCommonReport = array( |
324
|
2 |
|
'pma_version' => $this->getStrippedPmaVersion($bugReport['pma_version']), |
325
|
2 |
|
'php_version' => $this->_getSimpleVersion($bugReport['php_version'], 2), |
326
|
2 |
|
'browser' => $bugReport['browser_name'] . ' ' |
327
|
2 |
|
. $this->_getSimpleVersion($bugReport['browser_version'], 1), |
328
|
2 |
|
'user_os' => $bugReport['user_os'], |
329
|
2 |
|
'locale' => $bugReport['locale'], |
330
|
2 |
|
'configuration_storage' => $bugReport['configuration_storage'], |
331
|
2 |
|
'server_software' => $this->_getServer($bugReport['server_software']), |
332
|
2 |
|
'full_report' => json_encode($bugReport), |
333
|
|
|
); |
334
|
|
|
|
335
|
2 |
|
if (isset($bugReport['exception_type']) |
336
|
2 |
|
&& $bugReport['exception_type'] == 'php' |
337
|
|
|
) { |
338
|
|
|
// for each "errors" |
339
|
2 |
|
foreach ($bugReport['errors'] as $error) { |
340
|
2 |
|
$tmpReport = array_merge( |
341
|
|
|
$schematizedCommonReport, |
342
|
|
|
array( |
343
|
2 |
|
'error_name' => $error['type'], |
344
|
2 |
|
'error_message' => $error['msg'], |
345
|
2 |
|
'script_name' => $error['file'], |
346
|
2 |
|
'stacktrace' => json_encode($error['stackTrace']), |
347
|
2 |
|
'stackhash' => $error['stackhash'], |
348
|
2 |
|
'exception_type' => 1, // 'php' |
349
|
|
|
) |
350
|
|
|
); |
351
|
2 |
|
array_push($schematizedReports, $tmpReport); |
352
|
|
|
} |
353
|
|
|
} else { |
354
|
2 |
|
$tmpReport = array_merge( |
355
|
|
|
$schematizedCommonReport, |
356
|
|
|
array( |
357
|
2 |
|
'error_name' => $bugReport['exception']['name'], |
358
|
2 |
|
'error_message' => $bugReport['exception']['message'], |
359
|
2 |
|
'script_name' => $bugReport['script_name'], |
360
|
2 |
|
'stacktrace' => json_encode($bugReport['exception']['stack']), |
361
|
2 |
|
'stackhash' => $this->getStackHash($bugReport['exception']['stack']), |
362
|
2 |
|
'exception_type' => 0, //'js' |
363
|
|
|
) |
364
|
|
|
); |
365
|
|
|
|
366
|
2 |
|
if (isset($bugReport['steps'])) { |
367
|
2 |
|
$tmpReport['steps'] = $bugReport['steps']; |
368
|
|
|
} |
369
|
2 |
|
array_push($schematizedReports, $tmpReport); |
370
|
|
|
} |
371
|
|
|
|
372
|
2 |
|
return $schematizedReports; |
373
|
|
|
} |
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* Gets the identifiying location info from a stacktrace. |
377
|
|
|
* |
378
|
|
|
* This is used to skip stacktrace levels that are within the error reporting js |
379
|
|
|
* files that sometimes appear in the stacktrace but are not related to the bug |
380
|
|
|
* report |
381
|
|
|
* |
382
|
|
|
* returns two things in an array: |
383
|
|
|
* - the first element is the filename/scriptname of the error |
384
|
|
|
* - the second element is the linenumber of the error |
385
|
|
|
* |
386
|
|
|
* @param array $stacktrace the stacktrace being examined |
387
|
|
|
* |
388
|
|
|
* @return array an array with the filename/scriptname and linenumber of the |
389
|
|
|
* error |
390
|
|
|
*/ |
391
|
4 |
|
protected function _getIdentifyingLocation($stacktrace) |
392
|
|
|
{ |
393
|
4 |
|
$fallback = 'UNKNOWN'; |
394
|
4 |
|
foreach ($stacktrace as $level) { |
395
|
4 |
|
if (isset($level['filename'])) { |
396
|
|
|
// ignore unrelated files that sometimes appear in the error report |
397
|
4 |
|
if ($level['filename'] === 'tracekit/tracekit.js') { |
398
|
1 |
|
continue; |
399
|
4 |
|
} elseif ($level['filename'] === 'error_report.js') { |
400
|
|
|
// in case the error really is in the error_report.js file save it for |
401
|
|
|
// later |
402
|
1 |
|
if ($fallback == 'UNKNOWN') { |
403
|
1 |
|
$fallback = array($level['filename'], $level['line']); |
404
|
|
|
} |
405
|
1 |
|
continue; |
406
|
|
|
} |
407
|
|
|
|
408
|
4 |
|
return array($level['filename'], $level['line']); |
409
|
1 |
|
} elseif (isset($level['scriptname'])) { |
410
|
1 |
|
return array($level['scriptname'], $level['line']); |
411
|
|
|
} |
412
|
1 |
|
continue; |
413
|
|
|
} |
414
|
|
|
|
415
|
1 |
|
return $fallback; |
416
|
|
|
} |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* Gets a part of a version string according to the specified version Length. |
420
|
|
|
* |
421
|
|
|
* @param string $versionString the version string |
422
|
|
|
* @param string $versionLength the number of version components to return. eg |
423
|
|
|
* 1 for major version only and 2 for major and |
424
|
|
|
* minor version |
425
|
|
|
* |
426
|
|
|
* @return string the major and minor version part |
427
|
|
|
*/ |
428
|
3 |
|
protected function _getSimpleVersion($versionString, $versionLength) |
429
|
|
|
{ |
430
|
3 |
|
$versionLength = (int) $versionLength; |
431
|
3 |
|
if ($versionLength < 1) { |
432
|
1 |
|
$versionLength = 1; |
433
|
|
|
} |
434
|
|
|
/* modify the re to accept a variable number of version components. I |
435
|
|
|
* atleast take one component and optionally get more components if need be. |
436
|
|
|
* previous code makes sure that the $versionLength variable is a positive |
437
|
|
|
* int |
438
|
|
|
*/ |
439
|
3 |
|
$result = preg_match( |
440
|
3 |
|
"/^(\d+\.){" . ($versionLength - 1) . "}\d+/", |
441
|
|
|
$versionString, |
442
|
3 |
|
$matches |
443
|
|
|
); |
444
|
3 |
|
if ($result) { |
445
|
3 |
|
$simpleVersion = $matches[0]; |
446
|
|
|
|
447
|
3 |
|
return $simpleVersion; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
return $versionString; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* Returns the version string stripped of |
455
|
|
|
* 'deb', 'ubuntu' and other suffixes |
456
|
|
|
* |
457
|
|
|
* @param string $versionString phpMyAdmin version |
458
|
|
|
* |
459
|
|
|
* @return string stripped phpMyAdmin version |
460
|
|
|
*/ |
461
|
9 |
|
public function getStrippedPmaVersion($versionString) |
462
|
|
|
{ |
463
|
9 |
|
$allowedRegexp = '/^(\d+)(\.\d+){0,3}(\-.*)?/'; |
464
|
9 |
|
$matches = array(); |
465
|
|
|
|
466
|
|
|
// Check if $versionString matches the regexp |
467
|
|
|
// and store the matched strings |
468
|
9 |
|
if (preg_match($allowedRegexp, $versionString, $matches)) { |
469
|
9 |
|
return $matches[0]; |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
// If $versionString does not match the regexp at all, |
473
|
|
|
// leave it as it is |
474
|
|
|
return $versionString; |
475
|
|
|
} |
476
|
|
|
|
477
|
|
|
/** |
478
|
|
|
* Gets the server name and version from the server signature. |
479
|
|
|
* |
480
|
|
|
* @param string $signature the server signature |
481
|
|
|
* |
482
|
|
|
* @return string the server name and version or UNKNOWN |
483
|
|
|
*/ |
484
|
3 |
|
protected function _getServer($signature) |
485
|
|
|
{ |
486
|
3 |
|
if (preg_match("/(apache\/\d+\.\d+)|(nginx\/\d+\.\d+)|(iis\/\d+\.\d+)" |
487
|
3 |
|
. "|(lighttpd\/\d+\.\d+)/i", |
488
|
3 |
|
$signature, $matches)) { |
489
|
3 |
|
return $matches[0]; |
490
|
|
|
} |
491
|
|
|
|
492
|
1 |
|
return 'UNKNOWN'; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* returns the hash pertaining to a stacktrace. |
497
|
|
|
* |
498
|
|
|
* @param array $stacktrace the stacktrace in question |
499
|
|
|
* |
500
|
|
|
* @return string the hash string of the stacktrace |
501
|
|
|
*/ |
502
|
3 |
|
public function getStackHash($stacktrace) |
503
|
|
|
{ |
504
|
3 |
|
$handle = hash_init('md5'); |
505
|
3 |
|
foreach ($stacktrace as $level) { |
506
|
3 |
|
$elements = array('filename', 'scriptname', 'line', 'func', 'column'); |
507
|
3 |
|
foreach ($elements as $element) { |
508
|
3 |
|
if (!isset($level[$element])) { |
509
|
3 |
|
continue; |
510
|
|
|
} |
511
|
3 |
|
hash_update($handle, $level[$element]); |
512
|
|
|
} |
513
|
|
|
} |
514
|
|
|
|
515
|
3 |
|
return hash_final($handle); |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* Checks the length of stacktrace and full_report |
520
|
|
|
* and logs if it is greater than what it can hold |
521
|
|
|
* |
522
|
|
|
* @param array $si submitted incident |
523
|
|
|
* @param array $incident_ids incident IDs |
524
|
|
|
* |
525
|
|
|
* @return array $incident_ids |
526
|
|
|
*/ |
527
|
1 |
|
private function _logLongIncidentSubmissions($si, &$incident_ids) { |
528
|
|
|
|
529
|
1 |
|
$stacktraceLength = mb_strlen($si['stacktrace']); |
530
|
1 |
|
$fullReportLength = mb_strlen($si['full_report']); |
531
|
|
|
|
532
|
1 |
|
if ($stacktraceLength > 65535 || $fullReportLength > 65535) { |
533
|
|
|
// If length of report is longer than |
534
|
|
|
// what can fit in the table field, |
535
|
|
|
// we log it and don't save it in the database |
536
|
1 |
|
Log::error( |
537
|
|
|
'Too long data submitted in the incident. The length of stacktrace: ' |
538
|
1 |
|
. $stacktraceLength . ', while length of bug report: ' |
539
|
1 |
|
. $fullReportLength . '. The full incident reported was as follows: ' |
540
|
1 |
|
. json_encode($si) |
541
|
|
|
); |
542
|
|
|
|
543
|
|
|
// add a 'false' to the return array |
544
|
1 |
|
array_push($incident_ids, false); |
545
|
|
|
} |
546
|
1 |
|
} |
547
|
|
|
} |
548
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.