Passed
Push — development ( e3a728...c7cb34 )
by Spuds
01:05 queued 20s
created

Exception   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Test Coverage

Coverage 75%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 32
dl 0
loc 139
ccs 24
cts 32
cp 0.75
rs 10
c 1
b 0
f 0
wmc 20

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 2
B logMessage() 0 21 8
A loadMessage() 0 27 6
A fatalLangError() 0 3 1
A parseMessage() 0 16 3
1
<?php
2
3
/**
4
 * General exception handler. Has support for throwing errors that
5
 * are specifically targeted at users and even placing multiple messages
6
 * in one exception.
7
 *
8
 * @package   ElkArte Forum
9
 * @copyright ElkArte Forum contributors
10
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
11
 *
12
 * @version 2.0 Beta 1
13
 *
14
 */
15
16
namespace ElkArte\Exceptions;
17
18
use ElkArte\Errors\Errors;
0 ignored issues
show
Bug introduced by
The type ElkArte\Errors\Errors was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use ElkArte\Languages\Txt;
20
use ElkArte\User;
21
22
/**
23
 * General exception handler aimed at extending the default functionality of PHP exceptions.
24
 * This class introduces additional features to format and log error messages, load language files,
25
 * handle custom error messages, and integrate with the ElkArte system.
26
 */
27
class Exception extends \Exception
28
{
29
	/** @var string[] Values to use in vsprintf. */
30
	protected $sprintf = [];
31
32
	/**
33
	 * \ElkArte\Exceptions\Exception constructor.
34
	 * Extended exception rules because we need more stuff
35
	 *
36
	 * @param string|string[] $message index of $txt or message
37
	 *  - If an array is used, then it can specify a custom language template to load.
38
	 * @param bool|string $log type of error, defines under which "log" is shown.
39
	 *  - If false is used, the error is not logged.
40
	 * @param string[] $sprintf optional array of values to use in vsprintf with the $txt
41
	 * @param int $code
42
	 * @param Exception|null $previous
43
	 */
44
	public function __construct($message, protected $log = false, $sprintf = [], $code = 0, Exception $previous = null)
45
	{
46
		$this->sprintf = is_array($sprintf) ? $sprintf : (array) $sprintf;
0 ignored issues
show
introduced by
The condition is_array($sprintf) is always true.
Loading history...
47
48
		// Make sure everything is assigned properly, this will route through the exception_handler
49
		// in the Errors class.
50
		parent::__construct($this->loadMessage($message), $code, $previous);
51
	}
52 34
53
	/**
54 34
	 * Loads the language file specified in \ElkArte\Exceptions\Exception::parseMessage()
55 34
	 * and replaces the index received in the constructor.
56
	 *
57
	 * @param string|string[] $message
58 34
	 *
59 34
	 * @return string The index or the message.
60
	 */
61
	protected function loadMessage($message): string
62
	{
63
		global $txt;
64
65
		if (empty($message))
66
		{
67
			Errors::instance()->display_minimal_error('');
68
		}
69 34
70
		$msg = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $msg is dead and can be removed.
Loading history...
71 34
		$lang = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $lang is dead and can be removed.
Loading history...
72
		try
73
		{
74
			[$msg, $lang] = $this->parseMessage($message);
75 34
			if ($lang !== false)
76 34
			{
77
				Txt::load($lang);
78 34
			}
79
		}
80
		catch (\Exception)
81
		{
82
			Errors::instance()->display_minimal_error($message);
83
		}
84
85
		$this->logMessage($message, $lang);
0 ignored issues
show
Bug introduced by
It seems like $message can also be of type string[]; however, parameter $msg of ElkArte\Exceptions\Exception::logMessage() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

85
		$this->logMessage(/** @scrutinizer ignore-type */ $message, $lang);
Loading history...
86 34
87
		return isset($txt[$msg]) ? (empty($this->sprintf) ? $txt[$msg] : vsprintf($txt[$msg], $this->sprintf)) : ($msg);
88 34
	}
89
90
	/**
91
	 * Cleans up the message param passed to the constructor.
92
	 *
93
	 * @param string|string[] $message Can be several different things:
94
	 * - The index of $txt string
95
	 * - A plain text message
96
	 * - An array with the following structure:
97
	 *   array(
98
	 *       0 => language to load (use \ElkArte\Languages\Txt::load)
99
	 *       1 => index of $txt
100
	 *   )
101
	 * - A namespaced index in the form:
102
	 *     - lexicon.index
103
	 *     - a txt language file followed by a "dot" followed by the "index"
104
	 *     - e.g., Post.attach_timeout
105
	 * - "lexicon" is loaded by \ElkArte\Languages\Txt::load.
106
	 *
107
	 * @return array
108
	 */
109
	protected function parseMessage($message): array
110
	{
111 34
		// Load message with language support
112
		if (is_array($message))
113
		{
114 34
			return [$message[1], $message[0]];
115
		}
116
117
		if (preg_match('/^(\w+)\.(.+)$/', $message, $matches) !== 0)
118
		{
119 34
			// language.messageIndex
120
			return [$matches[2], $matches[1]];
121
		}
122
123
		// Simple Error message
124
		return [$message, 'Errors'];
125
	}
126
127
	/**
128 34
	 * Loads the language file specified in \ElkArte\Exceptions\Exception::parseMessage()
129 34
	 * and replaces the index received in the constructor.  Logs the message if needed.
130
	 *
131
	 * @param string $msg the message to log
132 34
	 * @param string $lexicon the language file to load
133
	 */
134
	protected function logMessage($msg, $lexicon): void
135
	{
136
		global $language, $txt;
137
138
		if (empty($msg))
139
		{
140
			$this->log = false;
141
			return;
142
		}
143 34
144
		// Don't need to reload the language file if both the user and
145 34
		// the forum share the same language.
146
		if (!isset($language) || (isset(User::$info->language) && $language !== User::$info->language))
0 ignored issues
show
Bug Best Practice introduced by
The property language does not exist on ElkArte\Helper\ValuesContainer. Since you implemented __get, consider adding a @property annotation.
Loading history...
147
		{
148
			Txt::load($lexicon, $language);
149 34
		}
150
151 28
		if ($this->log !== false)
152
		{
153
			$msg = isset($txt[$msg]) ? (empty($this->sprintf) ? $txt[$msg] : vsprintf($txt[$msg], $this->sprintf)) : ($msg);
154 34
			Errors::instance()->log_error($msg, $this->log, $this->getFile(), $this->getLine());
155
		}
156
	}
157
158
	/**
159 34
	 * Calls fatal_lang_error and ends the execution of the script.
160
	 *
161
	 * @deprecated since 2.0, use Errors::instance()->fatal_lang_error directly
162
	 */
163
	public function fatalLangError(): never
164
	{
165
		Errors::instance()->fatal_lang_error($this->message, $this->log, $this->sprintf);
166
	}
167
}
168