Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

Logger   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Test Coverage

Coverage 83.75%

Importance

Changes 0
Metric Value
dl 0
loc 289
ccs 67
cts 80
cp 0.8375
rs 10
c 0
b 0
f 0
wmc 29

14 Methods

Rating   Name   Duplication   Size   Complexity  
A disable() 0 2 1
B setLevel() 0 24 5
A setPrinter() 0 2 1
B log() 0 29 6
A notice() 0 2 1
A setHooks() 0 2 1
A error() 0 2 1
A warn() 0 2 1
A dump() 0 2 1
A getLevel() 0 2 1
B __construct() 0 25 6
A enable() 0 2 1
A process() 0 13 2
A info() 0 2 1
1
<?php
2
namespace Elgg;
3
4
use Elgg\Printer\ErrorLogPrinter;
5
6
/**
7
 * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
8
 *
9
 * Use the elgg_* versions instead.
10
 *
11
 * @access private
12
 *
13
 * @package    Elgg.Core
14
 * @subpackage Logging
15
 * @since      1.9.0
16
 */
17
class Logger {
18
19
	const OFF = 0;
20
	const ERROR = 400;
21
	const WARNING = 300;
22
	const NOTICE = 250;
23
	const INFO = 200;
24
25
	protected static $levels = [
26
		0 => 'OFF',
27
		200 => 'INFO',
28
		250 => 'NOTICE',
29
		300 => 'WARNING',
30
		400 => 'ERROR',
31
	];
32
33
	/**
34
	 * @var int
35
	 */
36
	public static $verbosity;
37
38
	/**
39
	 * @var int The logging level
40
	 */
41
	protected $level = self::ERROR;
42
43
	/**
44
	 * @var PluginHooksService
45
	 */
46
	protected $hooks;
47
48
	/**
49
	 * @var Context
50
	 */
51
	private $context;
52
53
	/**
54
	 * @var array
55
	 */
56
	private $disabled_stack;
57
58
	/**
59
	 * @var Printer
60
	 */
61
	private $printer;
62
63
	/**
64
	 * @var Config
65
	 */
66
	private $config;
67
68
	/**
69
	 * Constructor
70
	 *
71
	 * @param PluginHooksService $hooks   Hooks service
72
	 * @param Context            $context Context service
73
	 * @param Config             $config  Config
74
	 * @param Printer            $printer Printer
75
	 */
76 4417
	public function __construct(PluginHooksService $hooks, Context $context, Config $config, Printer $printer = null) {
77 4417
		$this->hooks = $hooks;
78 4417
		$this->context = $context;
79 4417
		if (!isset($printer)) {
80 60
			$printer = new ErrorLogPrinter();
81
		}
82 4417
		$this->printer = $printer;
83 4417
		$this->config = $config;
84
		
85 4417
		$php_error_level = error_reporting();
86
87
		// value is in settings.php, use until boot values are available
88 4417
		if ($this->config->hasInitialValue('debug')) {
89 4417
			$this->setLevel($this->config->debug);
0 ignored issues
show
Bug introduced by
$this->config->debug of type string is incompatible with the type integer expected by parameter $level of Elgg\Logger::setLevel(). ( Ignorable by Annotation )

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

89
			$this->setLevel(/** @scrutinizer ignore-type */ $this->config->debug);
Loading history...
90 4417
			return;
91
		}
92
93 9
		$this->level = self::OFF;
94
95 9
		if (($php_error_level & E_NOTICE) == E_NOTICE) {
96 9
			$this->level = self::NOTICE;
97
		} elseif (($php_error_level & E_WARNING) == E_WARNING) {
98
			$this->level = self::WARNING;
99
		} elseif (($php_error_level & E_ERROR) == E_ERROR) {
100
			$this->level = self::ERROR;
101
		}
102 9
	}
103
104
	/**
105
	 * Set the logging level
106
	 *
107
	 * @param int $level The logging level
108
	 * @return void
109
	 */
110 4777
	public function setLevel($level) {
111 4777
		if (!$level) {
112
			// 0 or empty string
113 6
			$this->level = self::OFF;
114 6
			return;
115
		}
116
117
		// @todo Elgg has used string constants for logging levels
118 4777
		if (is_string($level)) {
119 4777
			$level = strtoupper($level);
120 4777
			$level = array_search($level, self::$levels);
121
122 4777
			if ($level !== false) {
123 4777
				$this->level = $level;
124
			} else {
125
				$this->warn(__METHOD__ .": invalid level ignored.");
126
			}
127 4777
			return;
128
		}
129
130 1
		if (isset(self::$levels[$level])) {
131 1
			$this->level = $level;
132
		} else {
133
			$this->warn(__METHOD__ .": invalid level ignored.");
134
		}
135 1
	}
136
137
	/**
138
	 * Get the current logging level
139
	 *
140
	 * @return int
141
	 */
142 38
	public function getLevel() {
143 38
		return $this->level;
144
	}
145
146
	/**
147
	 * Set custom printer
148
	 *
149
	 * @param Printer $printer Printer
150
	 * @return void
151
	 */
152
	public function setPrinter(Printer $printer) {
153
		$this->printer = $printer;
154
	}
155
156
	/**
157
	 * Add a message to the log
158
	 *
159
	 * @param string $message The message to log
160
	 * @param int    $level   The logging level
161
	 * @return bool Whether the messages was logged
162
	 */
163 5379
	public function log($message, $level = self::NOTICE) {
164 5379
		if ($this->disabled_stack) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->disabled_stack of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
165
			// capture to top of stack
166 213
			end($this->disabled_stack);
167 213
			$key = key($this->disabled_stack);
168 213
			$this->disabled_stack[$key][] = [
169 213
				'message' => $message,
170 213
				'level' => $level,
171
			];
172
		}
173
174 5379
		if ($this->level == self::OFF || $level < $this->level) {
175 5379
			return false;
176
		}
177
178 1089
		if (!array_key_exists($level, self::$levels)) {
179
			return false;
180
		}
181
182
		// when capturing, still use consistent return value
183 1089
		if ($this->disabled_stack) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->disabled_stack of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
184 108
			return true;
185
		}
186
187 992
		$levelString = self::$levels[$level];
188
189 992
		$this->process("$levelString: $message", $level);
190
191 992
		return true;
192
	}
193
194
	/**
195
	 * Log message at the ERROR level
196
	 *
197
	 * @param string $message The message to log
198
	 * @return bool
199
	 */
200 41
	public function error($message) {
201 41
		return $this->log($message, self::ERROR);
202
	}
203
204
	/**
205
	 * Log message at the WARNING level
206
	 *
207
	 * @param string $message The message to log
208
	 * @return bool
209
	 */
210 664
	public function warn($message) {
211 664
		return $this->log($message, self::WARNING);
212
	}
213
214
	/**
215
	 * Log message at the NOTICE level
216
	 *
217
	 * @param string $message The message to log
218
	 * @return bool
219
	 */
220 553
	public function notice($message) {
221 553
		return $this->log($message, self::NOTICE);
222
	}
223
224
	/**
225
	 * Log message at the INFO level
226
	 *
227
	 * @param string $message The message to log
228
	 * @return bool
229
	 */
230 5379
	public function info($message) {
231 5379
		return $this->log($message, self::INFO);
232
	}
233
234
	/**
235
	 * Dump data to log
236
	 *
237
	 * @param mixed $data The data to log
238
	 * @return void
239
	 */
240
	public function dump($data) {
241
		$this->process($data, self::ERROR);
242
	}
243
244
	/**
245
	 * Process logging data
246
	 *
247
	 * @param mixed $data  The data to process
248
	 * @param int   $level The logging level for this data
249
	 * @return void
250
	 */
251 992
	protected function process($data, $level) {
252
		
253
		// plugin can return false to stop the default logging method
254
		$params = [
255 992
			'level' => $level,
256 992
			'msg' => $data,
257
		];
258
259 992
		if (!$this->hooks->trigger('debug', 'log', $params, true)) {
260 1
			return;
261
		}
262
263 991
		$this->printer->write($data, $level);
264 991
	}
265
266
	/**
267
	 * Temporarily disable logging and capture logs (before tests)
268
	 *
269
	 * Call disable() before your tests and enable() after. enable() will return a list of
270
	 * calls to log() (and helper methods) that were not acted upon.
271
	 *
272
	 * @note This behaves like a stack. You must call enable() for each disable() call.
273
	 *
274
	 * @return void
275
	 * @see enable()
276
	 * @access private
277
	 * @internal
278
	 */
279 366
	public function disable() {
280 366
		$this->disabled_stack[] = [];
281 366
	}
282
283
	/**
284
	 * Restore logging and get record of log calls (after tests)
285
	 *
286
	 * @return array
287
	 * @see disable()
288
	 * @access private
289
	 * @internal
290
	 */
291 366
	public function enable() {
292 366
		return array_pop($this->disabled_stack);
293
	}
294
295
	/**
296
	 * Reset the hooks service for this instance (testing)
297
	 *
298
	 * @param PluginHooksService $hooks the plugin hooks service
299
	 *
300
	 * @return void
301
	 * @access private
302
	 * @internal
303
	 */
304 1
	public function setHooks(PluginHooksService $hooks) {
305 1
		$this->hooks = $hooks;
306 1
	}
307
}
308