Merge branch '3.1' into 5.2
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / util / ExceptionLogUtil.class.php
1 <?php
2 namespace wcf\util;
3 use wcf\system\Regex;
4
5 /**
6 * Contains header-related functions.
7 *
8 * @author Tim Duesterhus
9 * @copyright 2001-2019 WoltLab GmbH
10 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
11 * @package WoltLabSuite\Core\Util
12 * @since 5.2
13 */
14 final class ExceptionLogUtil {
15 /**
16 * Splits the given string of Exceptions into an array.
17 *
18 * @param string $contents
19 * @return string[]
20 */
21 public static function splitLog($contents) {
22 // unify newlines
23 $contents = StringUtil::unifyNewlines($contents);
24
25 // split contents
26 $split = new Regex('(?:^|\n<<<<\n\n)(?:<<<<<<<<([a-f0-9]{40})<<<<\n|$)');
27 $contents = $split->split($contents, Regex::SPLIT_NON_EMPTY_ONLY | Regex::CAPTURE_SPLIT_DELIMITER);
28
29 // even items become keys, odd items become values
30 return array_merge(...array_map(
31 function($v) {
32 return [$v[0] => $v[1]];
33 },
34 array_chunk($contents, 2)
35 ));
36 }
37
38 /**
39 * Parses the given log entry.
40 *
41 * @param string $entry
42 * @return mixed[]
43 */
44 public static function parseException($entry) {
45 static $regex = null;
46 static $chainRegex = null;
47 if ($regex === null || $chainRegex === null) {
48 $regex = new Regex("(?P<date>[MTWFS][a-z]{2}, \d{1,2} [JFMASOND][a-z]{2} \d{4} \d{2}:\d{2}:\d{2} [+-]\d{4})\s*\n".
49 "Message: (?P<message>.*?)\s*\n".
50 "PHP version: (?P<phpVersion>.*?)\s*\n".
51 "WoltLab Suite version: (?P<wcfVersion>.*?)\s*\n".
52 "Request URI: (?P<requestURI>.*?)\s*\n".
53 "Referrer: (?P<referrer>.*?)\s*\n".
54 "User Agent: (?P<userAgent>.*?)\s*\n".
55 "Peak Memory Usage: (?<peakMemory>\d+)/(?<maxMemory>(?:\d+|-1))\s*\n".
56 "(?<chain>======\n".
57 ".*)", Regex::DOT_ALL);
58 $chainRegex = new Regex("======\n".
59 "Error Class: (?P<class>.*?)\s*\n".
60 "Error Message: (?P<message>.*?)\s*\n".
61 "Error Code: (?P<code>[a-zA-Z0-9]+)\s*\n".
62 "File: (?P<file>.*?) \((?P<line>\d+)\)\s*\n".
63 "Extra Information: (?P<information>(?:-|[a-zA-Z0-9+/]+={0,2}))\s*\n".
64 "Stack Trace: (?P<stack>\[[^\n]+\])", Regex::DOT_ALL);
65 }
66
67 if (!$regex->match($entry)) {
68 throw new \InvalidArgumentException('The given entry is malformed');
69 }
70 $matches = $regex->getMatches();
71 $chainRegex->match($matches['chain'], true, Regex::ORDER_MATCH_BY_SET);
72
73 $chainMatches = array_map(function ($item) {
74 if ($item['information'] === '-') {
75 $item['information'] = null;
76 }
77 else {
78 $item['information'] = unserialize(base64_decode($item['information']), ['allowed_classes' => false]);
79 }
80
81 $item['stack'] = JSON::decode($item['stack']);
82
83 return $item;
84 }, $chainRegex->getMatches());
85
86 $matches['date'] = strtotime($matches['date']);
87 $matches['chain'] = $chainMatches;
88
89 return $matches;
90 }
91
92 /**
93 * Forbid creation of ExceptionLogUtil objects.
94 */
95 private function __construct() {
96 // does nothing
97 }
98 }