Completed
Push — master ( 740d41...a3b506 )
by Michal
07:10 queued 16s
created

IncidentsTable::getStackHash()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 9
cts 9
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 4
nop 1
crap 4
1
<?php
2
/* vim: set noexpandtab sw=2 ts=2 sts=2: */
3
namespace App\Model\Table;
4
5
use App\Model\AppModel;
6
use Cake\ORM\Table;
7
use Cake\ORM\TableRegistry;
8
use Cake\Log\Log;
9
use Cake\Model\Model;
10
/**
11
 * An incident a representing a single incident of a submited bug
12
 *
13
 * phpMyAdmin Error reporting server
14
 * Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
15
 *
16
 * Licensed under The MIT License
17
 * For full copyright and license information, please see the LICENSE.txt
18
 * Redistributions of files must retain the above copyright notice.
19
 *
20
 * @copyright     Copyright (c) phpMyAdmin project (https://www.phpmyadmin.net/)
21
 * @package       Server.Model
22
 * @link          https://www.phpmyadmin.net/
23
 * @license       https://opensource.org/licenses/mit-license.php MIT License
24
 */
25
26
/**
27
 * An incident a representing a single incident of a submited bug
28
 *
29
 * @package       Server.Model
30
 */
31
class IncidentsTable extends Table {
32
33
/**
34
 * @var Array
35
 * @link 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
 * @link http://book.cakephp.org/2.0/en/models/model-attributes.html#validate
43
 * @link http://book.cakephp.org/2.0/en/models/data-validation.html
44
 * @see Model::$validate
45
 */
46
	public $validate = array(
47
		'pma_version' => array(
48
			'rule' => 'notEmpty',
49
			'required' => true,
50
		),
51
		'php_version' => array(
52
			'rule' => 'notEmpty',
53
			'required' => true,
54
		),
55
		'full_report' => array(
56
			'rule' => 'notEmpty',
57
			'required' => true,
58
		),
59
		'stacktrace' => array(
60
			'rule' => 'notEmpty',
61
			'required' => true,
62
		),
63
		'browser' => array(
64
			'rule' => 'notEmpty',
65
			'required' => true,
66
		),
67
		'stackhash' => array(
68
			'rule' => 'notEmpty',
69
			'required'	 => true,
70
		),
71
		'user_os' => array(
72
			'rule' => 'notEmpty',
73
			'required' => true,
74
		),
75
		'locale' => array(
76
			'rule' => 'notEmpty',
77
			'required' => true,
78
		),
79
		'script_name' => array(
80
			'rule' => 'notEmpty',
81
			'required' => true,
82
		),
83
		'server_software' => array(
84
			'rule' => 'notEmpty',
85
			'required' => true,
86
		),
87
		'configuration_storage' => array(
88
			'rule' => 'notEmpty',
89
			'required' => true,
90
		),
91
	);
92
93
/**
94
 * @var Array
95
 * @link http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#belongsto
96
 * @see Model::$belongsTo
97
 */
98
99
/**
100
 * The fields which are summarized in the report page with charts and are also
101
 * used in the overall stats and charts for the website
102
 * @var Array
103
 */
104
	public $summarizableFields = array('browser', 'pma_version', 'php_version',
105
			'locale', 'server_software', 'user_os', 'script_name',
106
			'configuration_storage');
107
108 17
	public function __construct($id = false, $table = null, $ds = null) {
109 17
		parent::__construct($id, $table, $ds);
0 ignored issues
show
Unused Code introduced by
The call to Table::__construct() has too many arguments starting with $table.

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.

Loading history...
110
111 17
		$this->filterTimes = array(
0 ignored issues
show
Bug introduced by
The property filterTimes does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
112
			'all_time' => array(
113
				'label' => 'All Time',
114
				'limit' => null,
115
				'group' => "DATE_FORMAT(Incidents.created, '%m %Y')",
116 17
			),
117
			'day' => array(
118 17
				'label' => 'Last Day',
119 17
				'limit' => date('Y-m-d', strtotime('-1 day')),
120
				'group' =>
121 17
						"DATE_FORMAT(Incidents.created, '%a %b %d %Y %H')",
122
			),
123
			'week' => array(
124 17
				'label' => 'Last Week',
125 17
				'limit' => date('Y-m-d', strtotime('-1 week')),
126
				'group' =>
127 17
						"DATE_FORMAT(Incidents.created, '%a %b %d %Y')",
128
			),
129
			'month' => array(
130 17
				'label' => 'Last Month',
131 17
				'limit' => date('Y-m-d', strtotime('-1 month')),
132
				'group' =>
133 17
						"DATE_FORMAT(Incidents.created, '%a %b %d %Y')",
134
			),
135
			'year' => array(
136 17
				'label' => 'Last Year',
137 17
				'limit' => date('Y-m-d', strtotime('-1 year')),
138 17
				'group' => "DATE_FORMAT(Incidents.created, '%b %u %Y')",
139
			),
140
		);
141 17
	}
142
143
/**
144
 * creates an incident/report record given a raw bug report object
145
 *
146
 * This gets a decoded bug report from the submitted json body. This has not
147
 * yet been santized. It either adds it as an incident to another report or
148
 * creates a new report if nothing matches.
149
 *
150
 * @param Array $bugReport the bug report being submitted
151
 * @return Array of inserted incident ids. If the report/incident was not
152
 *			correctly saved, false is put in it place.
153
 */
154 2
	public function createIncidentFromBugReport($bugReport) {
155 2
		if ($bugReport == null) {
156 1
			return array(false);
157
		}
158 2
		$incident_ids = array();	// array to hold ids of all the inserted incidents
159 2
		$schematizedIncidents = $this->_getSchematizedIncidents($bugReport);
160 2
        $incidentsTable = TableRegistry::get('Incidents');
161 2
        $reportsTable = TableRegistry::get('Reports');
162 2
		foreach($schematizedIncidents as $index => $si){
163
			//$tmpIncident = new IncidentsTable();
164
			// find closest report. If not found, create a new report.
165 2
			$closestReport = $this->_getClosestReport($bugReport, $index);
166 2
			if($closestReport) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $closestReport of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
167 2
				$si["report_id"] = $closestReport["id"];
168 2
                $si = $incidentsTable->newEntity($si);
169 2
                $si->created = date('Y-m-d H:i:s', time());
0 ignored issues
show
Bug introduced by
Accessing created on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
170 2
                $si->modified = date('Y-m-d H:i:s', time());
0 ignored issues
show
Bug introduced by
Accessing modified on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
171 2
				$isSaved = $incidentsTable->save($si);
172
			} else {
173
				//no close report. Create a new report.
174 2
				$report = $this->_getReportDetails($bugReport,$index);
175 2
                $report = $reportsTable->newEntity($report);
176 2
                $report->created = date('Y-m-d H:i:s', time());
0 ignored issues
show
Bug introduced by
Accessing created on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
177 2
                $report->modified = date('Y-m-d H:i:s', time());
0 ignored issues
show
Bug introduced by
Accessing modified on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
178 2
                $reportsTable->save($report);
179 2
                $si["report_id"] = $report->id;
0 ignored issues
show
Bug introduced by
Accessing id on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
180 2
                $si = $incidentsTable->newEntity($si);
181 2
                $si->created = date('Y-m-d H:i:s', time());
0 ignored issues
show
Bug introduced by
Accessing created on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
182 2
                $si->modified = date('Y-m-d H:i:s', time());
0 ignored issues
show
Bug introduced by
Accessing modified on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
183 2
                $isSaved = $incidentsTable->save($si);
184
				/*$data = array(
185
					'Incident' => $si,
186
					'Report' => $report
187
				);
188
                $tmpIncident->bindModel(
189
                    array('belongsTo' => array(
190
                            'Report'
191
                        )
192
                    )
193
                );*/
194
				//$isSaved = $tmpIncident->saveAssociated($data);
195
			}
196
197 2
			if($isSaved) {
198 2
				array_push($incident_ids,$si->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
199 2
				if (!$closestReport) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $closestReport of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
200
					// add notifications entry
201 2
					$tmpIncident = $incidentsTable->findById($si->id)->all()->first();
0 ignored issues
show
Bug introduced by
Accessing id on the interface Cake\Datasource\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
202 2
					if (!TableRegistry::get('Notifications')->addNotifications(intval($tmpIncident['report_id']))) {
203
						Log::write(
204
							'error',
205
							'ERRORED: Notification::addNotifications() failed on Report#'
206
								. $tmpIncident['report_id'],
207 2
							'alert'
208
						);
209
					}
210
				}
211
			} else {
212 2
				array_push($incident_ids,false);
213
			}
214
		}
215
216 2
		return $incident_ids;
217
	}
218
219
/**
220
 * retrieves the closest report to a given bug report
221
 *
222
 * it checks for another report with the same line number, filename and
223
 * pma_version
224
 *
225
 * @param Array $bugReport the bug report being checked
226
 *			Integer $index: for php exception type.
227
 * @return Array the first similar report or null
228
 */
229 3
	protected function _getClosestReport($bugReport, $index=0) {
230 3
		if(isset($bugReport['exception_type'])
231 3
			&& $bugReport['exception_type'] == 'php'
232
		) {
233 1
			$location = $bugReport['errors'][$index]['file'];
234 1
			$linenumber = $bugReport['errors'][$index]['lineNum'];
235
		} else {
236
			List($location, $linenumber) =
237 3
					$this->_getIdentifyingLocation($bugReport['exception']['stack']);
238
		}
239 3
		$report = TableRegistry::get('Reports')->findByLocationAndLinenumberAndPmaVersion(
240 3
					$location, $linenumber, $bugReport['pma_version'])->all()->first();
241 3
		return $report;
242
	}
243
244
/**
245
 * creates the report data from an incident that has no related report.
246
 *
247
 * @param Array $bugReport the bug report the report record is being created for
248
 *			Integer $index: for php exception type.
249
 * @return Array an array with the report fields can be used with Report->save
250
 */
251 3
	protected function _getReportDetails($bugReport, $index=0) {
252 3
		if(isset($bugReport['exception_type'])
253 3
			&& $bugReport['exception_type'] == 'php'
254
		) {
255 2
			$location = $bugReport['errors'][$index]['file'];
256 2
			$linenumber = $bugReport['errors'][$index]['lineNum'];
257
			$reportDetails = array(
258 2
					'error_message' => $bugReport['errors'][$index]['msg'],
259 2
					'error_name' => $bugReport['errors'][$index]['type'],
260
					);
261 2
			$exception_type = 1;
262
		} else {
263
			List($location, $linenumber) =
264 3
				$this->_getIdentifyingLocation($bugReport["exception"]["stack"]);
265
266
			$reportDetails = array(
267 3
					'error_message' => $bugReport['exception']['message'],
268 3
					'error_name' => $bugReport['exception']['name'],
269
					);
270 3
			$exception_type = 0;
271
		}
272
273 3
		$reportDetails = array_merge(
274
			$reportDetails,
275
			array(
276 3
				'status' => 'new',
277 3
				'location' => $location,
278 3
				'linenumber' => $linenumber,
279 3
				'pma_version' => $bugReport['pma_version'],
280 3
				'exception_type' => $exception_type
281
			)
282
		);
283 3
		return $reportDetails;
284
	}
285
286
/**
287
 * creates the incident data from the submitted bug report.
288
 *
289
 * @param Array $bugReport the bug report the report record is being created for
290
 * @return Array an array of schematized incident. 
291
 *				Can be used with ِIncident->save
292
 */
293 3
	protected function _getSchematizedIncidents($bugReport) {
294
		//$bugReport = Sanitize::clean($bugReport, array('escape' => false));
295 3
		$schematizedReports = array();
296
		$schematizedCommonReport = array(
297 3
			'pma_version' => $bugReport['pma_version'],
298 3
			'php_version' => $this->_getSimpleVersion($bugReport['php_version'], 2),
299 3
			'browser' => $bugReport['browser_name'] . " "
300 3
					. $this->_getSimpleVersion($bugReport['browser_version'], 1),
301 3
			'user_os' => $bugReport['user_os'],
302 3
			'locale' => $bugReport['locale'],
303 3
			'configuration_storage' => $bugReport['configuration_storage'],
304 3
			'server_software' => $this->_getServer($bugReport['server_software']),
305 3
			'full_report' => json_encode($bugReport)
306
		);
307
308 3
		if(isset($bugReport['exception_type'])
309 3
			&& $bugReport['exception_type'] == 'php'
310
		) {
311
			// for each "errors"
312 2
			foreach($bugReport['errors'] as $error){
313 2
				 $tmpReport = array_merge(
314
					$schematizedCommonReport,
315
					array(
316 2
						'error_name' => $error['type'],
317 2
						'error_message' => $error['msg'],
318 2
						'script_name' => $error['file'],
319 2
						'stacktrace' => json_encode($error['stackTrace']),
320 2
						'stackhash' => $error['stackhash'],
321 2
						'exception_type' => 1 		// 'php'
322
					)
323
				);
324 2
				array_push($schematizedReports,$tmpReport);
325
			}
326
		} else {
327 3
			$tmpReport = array_merge(
328
				$schematizedCommonReport,
329
				array(
330 3
					'error_name' => $bugReport['exception']['name'],
331 3
					'error_message' => $bugReport['exception']['message'],
332 3
					'script_name' => $bugReport['script_name'],
333 3
					'stacktrace' => json_encode($bugReport['exception']['stack']),
334 3
					'stackhash' => $this->getStackHash($bugReport['exception']['stack']),
335 3
					'exception_type' => 0 	//'js'
336
				)
337
			);
338
339 3
			if (isset($bugReport['steps'])) {
340 3
				$tmpReport["steps"] = $bugReport['steps'];
341
			}
342 3
			array_push($schematizedReports,$tmpReport);
343
		}
344
345 3
		return $schematizedReports;
346
	}
347
348
/**
349
 * Gets the identifiying location info from a stacktrace
350
 *
351
 * This is used to skip stacktrace levels that are within the error reporting js
352
 * files that sometimes appear in the stacktrace but are not related to the bug
353
 * report
354
 *
355
 * returns two things in an array:
356
 * - the first element is the filename/scriptname of the error
357
 * - the second element is the linenumber of the error
358
 *
359
 * @param Array $stacktrace the stacktrace being examined
360
 * @return Array an array with the filename/scriptname and linenumber of the
361
 *	 error
362
 */
363 5
	protected function _getIdentifyingLocation($stacktrace) {
364 5
		$fallback = 'UNKNOWN';
365 5
		foreach ($stacktrace as $level) {
366 5
			if (isset($level["filename"])) {
367
				// ignore unrelated files that sometimes appear in the error report
368 5
				if ($level["filename"] === "tracekit.js") {
369 1
					continue;
370 5
				} elseif($level["filename"] === "error_report.js") {
371
					// in case the error really is in the error_report.js file save it for
372
					// later
373 1
					if($fallback == 'UNKNOWN') {
374 1
						$fallback = array($level["filename"], $level["line"]);
375
					}
376 1
					continue;
377
				} else {
378 5
					return array($level["filename"], $level["line"]);
379
				}
380 1
			} elseif (isset($level["scriptname"])) {
381 1
				return array($level["scriptname"], $level["line"]);
382
			} else {
383 1
				continue;
384
			}
385
		}
386 1
		return $fallback;
387
	}
388
389
390
/**
391
 * Gets a part of a version string according to the specified version Length
392
 *
393
 * @param  String $versionString the version string
394
 * @param  String $versionLength the number of version components to return. eg
395
 *                               1 for major version only and 2 for major and
396
 *                               minor version
397
 * @return String the major and minor version part
398
 */
399 4
	protected function _getSimpleVersion($versionString, $versionLength) {
400 4
		$versionLength = (int) $versionLength;
401 4
		if ($versionLength < 1) {
402 1
			$versionLength = 1;
403
		}
404
		/* modify the re to accept a variable number of version components. I
405
		 * atleast take one component and optionally get more components if need be.
406
		 * previous code makes sure that the $versionLength variable is a positive
407
		 * int
408
		 */
409 4
		$result = preg_match(
410 4
			"/^(\d+\.){" . ($versionLength - 1) . "}\d+/",
411
			$versionString,
412
			$matches
413
		);
414 4
		if ($result) {
415 4
			$simpleVersion = $matches[0];
416 4
			return $simpleVersion;
417
		} else {
418
			return $versionString;
419
		}
420
	}
421
422
/**
423
 * Gets the server name and version from the server signature
424
 *
425
 * @param String $signature the server signature
426
 * @return String the server name and version or UNKNOWN
427
 */
428 4
	protected function _getServer($signature) {
429 4
		if (preg_match("/(apache\/\d+\.\d+)|(nginx\/\d+\.\d+)|(iis\/\d+\.\d+)"
430 4
				. "|(lighttpd\/\d+\.\d+)/i",
431
				$signature, $matches)) {
432 4
			return $matches[0];
433
		} else {
434 1
			return "UNKNOWN";
435
		}
436
	}
437
438
/**
439
 * returns the hash pertaining to a stacktrace
440
 *
441
 * @param Array $stacktrace the stacktrace in question
442
 * @return String the hash string of the stacktrace
443
 */
444 4
	public function getStackHash($stacktrace) {
445 4
		$handle = hash_init("md5");
446 4
		foreach ($stacktrace as $level) {
447 4
			$elements = array("filename", "scriptname", "line", "func", "column");
448 4
			foreach ($elements as $element) {
449 4
				if (!isset($level[$element])) {
450 4
					continue;
451
				}
452 4
				hash_update($handle, $level[$element]);
453
			}
454
		}
455 4
		return hash_final($handle);
456
	}
457
}
458