Passed
Push — master ( 08d0fa...a886df )
by Fabio
06:03
created

TException::__construct()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 16
c 1
b 0
f 0
nc 12
nop 3
dl 0
loc 23
rs 8.8333
ccs 12
cts 12
cp 1
crap 7
1
<?php
2
/**
3
 * Exception classes file
4
 *
5
 * @author Qiang Xue <[email protected]>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Exceptions;
11
12
use Prado\Prado;
13
use Prado\TPropertyValue;
14
15
use Throwable;
16
17
/**
18
 * TException class
19
 *
20
 * TException is the base class for all PRADO exceptions.
21
 *
22
 * TException provides the functionality of translating an error code
23
 * into a descriptive error message in a language that is preferred
24
 * by user browser. Additional parameters may be passed together with
25
 * the error code so that the translated message contains more detailed
26
 * information.
27
 *
28
 * Old style TException only have an error Message Code as follows:
29
 * <code>
30
 *   throw new TException('component_error_message_code'[, $param1, $param2, ..., $chainedException]);
31
 * <code>
32
 * The parameters and $chainedException are optional. $chainedException
33
 * may be entirely left out.
34
 *
35
 * To include an actual integer error Code, new style PRADO Exceptions
36
 * should be used.  The new style is as follows:
37
 * <code>
38
 *   throw new TException($errorCode, 'component_error_message_code'[, $param1, $param2, ..., $chainedException]);
39
 * <code>
40
 *
41
 * Please note that the Error Code and Error Message/Message-Code is swapped
42
 * to the Normal PHP Exceptions (where the $message is the first parameter and $code
43
 * is the second).  This done to support Error Message Parameters.
44
 *
45
 * By default, TException looks for a message file by calling
46
 * {@link getErrorMessageFile()} method, which uses the "message-xx.txt"
47
 * file located under "Prado\Exceptions" folder, where "xx" is the
48
 * code of the user preferred language. If such a file is not found,
49 113
 * "message.txt" will be used instead.
50
 *
51 113
 * @author Qiang Xue <[email protected]>
52 113
 * @since 3.0
53 113
 */
54 113
class TException extends \Exception
55 113
{
56 113
	private $_errorCode = '';
57 113
	private static $_messagefiles = [];
58 71
	protected static $_messageCache = [];
59
60 113
	/**
61 113
	 * Constructor.
62
	 * @param int|string $errorCode The Optional PHP Exception Error Code.  In old style
63
	 *   Exceptions this is a string error Message Code.  If this is a string, then the
64
	 *   $errorCode acts as and $errorMessage becomes a parameter in $args.
65
	 * @param string $errorMessage The error message code when $errorCode is an integer.
66
	 *   This can be a string that is listed in the message file. If so, the message in
67
	 *   the preferred language will be used as the error message.
68 113
	 * @param array $args These are used to replace placeholders ({0}, {1}, {2}, etc.)
69
	 *   in the message except the last argument.  If the last argument is a Throwable
70 113
	 *   it is treated as the $previous Exception for exception chaining.
71
	 */
72
	public function __construct($errorCode, $errorMessage = null, ...$args)
73 113
	{
74 2
		if(!is_int($errorCode)) {
75 2
			//assume old code
76 2
			if ($errorMessage !== null || !empty($args)) {
77 2
				array_unshift($args, $errorMessage);
78
			}
79
			$errorMessage = $errorCode;
80
			$errorCode = 0;
81 113
		}
82
		$this->_errorCode = $errorMessage;
83
		$errorMessage = $this->translateErrorMessage($errorMessage);
84
		$n = count($args);
85
		$previous = null;
86
		if($n > 0 && ($args[$n - 1] instanceof Throwable)) {
87 108
			$previous = array_pop($args);
88
			$n--;
89 108
		}
90 108
		$tokens = [];
91 108
		for ($i = 0; $i < $n; ++$i) {
92 107
			$tokens['{' . $i . '}'] = TPropertyValue::ensureString($args[$i]);
93
		}
94 108
		parent::__construct(strtr($errorMessage, $tokens), $errorCode, $previous);
95
	}
96
97
	/**
98
	 * Adds to the various files to read when rendering an error
99
	 * @param string $file the extra message file
100
	 * @since 4.2.0
101
	 */
102
	public static function addMessageFile($file)
103
	{
104
		if (preg_match('/^(.*)(-.{2.4})?\.(.{2,4})$/', $file, $matching)) {
105
			$lang = Prado::getPreferredLanguage();
106
			$msgFile = $matching[1] . '-' . $lang . '.' . $matching[2];
107
			if (is_file($msgFile)) {
108 5
				$file = $msgFile;
109
			}
110 5
		}
111 5
		TException::$_messagefiles[] = $file;
112
	}
113
114
	/**
115
	 * Translates an error code into an error message.
116
	 * @param string $key error code that is passed in the exception constructor.
117
	 * @return string the translated error message
118
	 */
119
	protected function translateErrorMessage($key)
120
	{
121
		$msgFiles = TException::$_messagefiles;
122
		$msgFiles[] = $this->getErrorMessageFile();
123
		$value = $key;
124
125
		// Cache messages
126
		foreach ($msgFiles as $msgFile) {
127
			if (!isset(self::$_messageCache[$msgFile])) {
128
				if (($entries = @file($msgFile)) !== false) {
129
					foreach ($entries as $entry) {
130
						[$code, $message] = array_merge(explode('=', $entry, 2), ['']);
131
						self::$_messageCache[$msgFile][trim($code)] = trim($message);
132
					}
133
				}
134
			}
135
			$value = self::$_messageCache[$msgFile][$key] ?? $value;
136
		}
137
		return $value;
138
	}
139
140
	/**
141
	 * @return string path to the error message file
142
	 */
143
	protected function getErrorMessageFile()
144
	{
145
		$lang = Prado::getPreferredLanguage();
146
		$msgFile = Prado::getFrameworkPath() . '/Exceptions/messages/messages-' . $lang . '.txt';
147
		if (!is_file($msgFile)) {
148
			$msgFile = Prado::getFrameworkPath() . '/Exceptions/messages/messages.txt';
149
		}
150
		return $msgFile;
151
	}
152
153
	/**
154
	 * @return string error code
155
	 */
156
	public function getErrorCode()
157
	{
158
		return $this->_errorCode;
159
	}
160
161
	/**
162
	 * @param string $code error code
163
	 */
164
	public function setErrorCode($code)
165
	{
166
		$this->_errorCode = $code;
167
	}
168
169
	/**
170
	 * @return string error message
171
	 */
172
	public function getErrorMessage()
173
	{
174
		return $this->getMessage();
175
	}
176
177
	/**
178
	 * @param string $message error message
179
	 */
180
	protected function setErrorMessage($message)
181
	{
182
		$this->message = $message;
183
	}
184
}
185