Merge branch '5.2' into 5.3
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / util / ExceptionLogUtil.class.php
CommitLineData
4c393c5a
TD
1<?php
2namespace wcf\util;
727efac4 3use wcf\system\exception\SystemException;
4c393c5a
TD
4use 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 */
15final 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}