Commit | Line | Data |
---|---|---|
4c393c5a TD |
1 | <?php |
2 | namespace wcf\util; | |
727efac4 | 3 | use wcf\system\exception\SystemException; |
4c393c5a TD |
4 | use wcf\system\Regex; |
5 | ||
6 | /** | |
c3362304 | 7 | * Contains helper functions to process the Exception log. |
4c393c5a TD |
8 | * |
9 | * @author Tim Duesterhus | |
10 | * @copyright 2001-2019 WoltLab GmbH | |
11 | * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> | |
12 | * @package WoltLabSuite\Core\Util | |
13 | * @since 5.2 | |
14 | */ | |
15 | final class ExceptionLogUtil { | |
16 | /** | |
17 | * Splits the given string of Exceptions into an array. | |
18 | * | |
19 | * @param string $contents | |
20 | * @return string[] | |
21 | */ | |
22 | public static function splitLog($contents) { | |
23 | // unify newlines | |
24 | $contents = StringUtil::unifyNewlines($contents); | |
25 | ||
26 | // split contents | |
27 | $split = new Regex('(?:^|\n<<<<\n\n)(?:<<<<<<<<([a-f0-9]{40})<<<<\n|$)'); | |
28 | $contents = $split->split($contents, Regex::SPLIT_NON_EMPTY_ONLY | Regex::CAPTURE_SPLIT_DELIMITER); | |
29 | ||
8cd2830d TD |
30 | if (empty($contents)) { |
31 | return []; | |
32 | } | |
33 | ||
4c393c5a TD |
34 | // even items become keys, odd items become values |
35 | return array_merge(...array_map( | |
36 | function($v) { | |
37 | return [$v[0] => $v[1]]; | |
38 | }, | |
39 | array_chunk($contents, 2) | |
40 | )); | |
41 | } | |
42 | ||
43 | /** | |
44 | * Parses the given log entry. | |
45 | * | |
46 | * @param string $entry | |
47 | * @return mixed[] | |
48 | */ | |
49 | public static function parseException($entry) { | |
50 | static $regex = null; | |
51 | static $chainRegex = null; | |
b6b94adf | 52 | if ($regex === null || $chainRegex === null) { |
4c393c5a TD |
53 | $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". |
54 | "Message: (?P<message>.*?)\s*\n". | |
55 | "PHP version: (?P<phpVersion>.*?)\s*\n". | |
56 | "WoltLab Suite version: (?P<wcfVersion>.*?)\s*\n". | |
57 | "Request URI: (?P<requestURI>.*?)\s*\n". | |
58 | "Referrer: (?P<referrer>.*?)\s*\n". | |
59 | "User Agent: (?P<userAgent>.*?)\s*\n". | |
60 | "Peak Memory Usage: (?<peakMemory>\d+)/(?<maxMemory>(?:\d+|-1))\s*\n". | |
61 | "(?<chain>======\n". | |
62 | ".*)", Regex::DOT_ALL); | |
63 | $chainRegex = new Regex("======\n". | |
64 | "Error Class: (?P<class>.*?)\s*\n". | |
65 | "Error Message: (?P<message>.*?)\s*\n". | |
a9fa9b54 | 66 | "Error Code: (?P<code>[a-zA-Z0-9]+)\s*\n". |
4c393c5a TD |
67 | "File: (?P<file>.*?) \((?P<line>\d+)\)\s*\n". |
68 | "Extra Information: (?P<information>(?:-|[a-zA-Z0-9+/]+={0,2}))\s*\n". | |
69 | "Stack Trace: (?P<stack>\[[^\n]+\])", Regex::DOT_ALL); | |
70 | } | |
71 | ||
72 | if (!$regex->match($entry)) { | |
727efac4 | 73 | throw new \InvalidArgumentException('The given entry is malformed.'); |
4c393c5a TD |
74 | } |
75 | $matches = $regex->getMatches(); | |
76 | $chainRegex->match($matches['chain'], true, Regex::ORDER_MATCH_BY_SET); | |
77 | ||
78 | $chainMatches = array_map(function ($item) { | |
79 | if ($item['information'] === '-') { | |
80 | $item['information'] = null; | |
81 | } | |
82 | else { | |
727efac4 TD |
83 | try { |
84 | $item['information'] = unserialize(base64_decode($item['information']), ['allowed_classes' => false]); | |
85 | } | |
86 | catch (SystemException $e) { | |
87 | throw new \InvalidArgumentException('The additional information section of the given entry is malformed.', 0, $e); | |
88 | } | |
4c393c5a TD |
89 | } |
90 | ||
727efac4 TD |
91 | try { |
92 | $item['stack'] = JSON::decode($item['stack']); | |
93 | } | |
94 | catch (SystemException $e) { | |
95 | throw new \InvalidArgumentException('The stack trace of the given entry is malformed.', 0, $e); | |
96 | } | |
4c393c5a TD |
97 | |
98 | return $item; | |
99 | }, $chainRegex->getMatches()); | |
100 | ||
c3362304 TD |
101 | $matches['stackHash'] = sha1(implode("\0", array_map(function ($item) { |
102 | $result = ""; | |
103 | foreach ($item['stack'] as $stack) { | |
104 | $result .= $stack['file']."\t".$stack['line']."\t".$stack['class'].$stack['type'].$stack['function']."\n"; | |
105 | } | |
106 | return $result; | |
107 | }, $chainMatches))); | |
108 | ||
4c393c5a TD |
109 | $matches['date'] = strtotime($matches['date']); |
110 | $matches['chain'] = $chainMatches; | |
111 | ||
112 | return $matches; | |
113 | } | |
114 | ||
115 | /** | |
116 | * Forbid creation of ExceptionLogUtil objects. | |
117 | */ | |
118 | private function __construct() { | |
119 | // does nothing | |
120 | } | |
121 | } |