1
|
|
|
<?php |
2
|
|
|
namespace Elgg; |
3
|
|
|
|
4
|
|
|
/** |
5
|
|
|
* WARNING: API IN FLUX. DO NOT USE DIRECTLY. |
6
|
|
|
* |
7
|
|
|
* Use the elgg_* versions instead. |
8
|
|
|
* |
9
|
|
|
* @access private |
10
|
|
|
* |
11
|
|
|
* @package Elgg.Core |
12
|
|
|
* @subpackage Logging |
13
|
|
|
* @since 1.9.0 |
14
|
|
|
*/ |
15
|
|
|
class Logger { |
16
|
|
|
|
17
|
|
|
const OFF = 0; |
18
|
|
|
const ERROR = 400; |
19
|
|
|
const WARNING = 300; |
20
|
|
|
const NOTICE = 250; |
21
|
|
|
const INFO = 200; |
22
|
|
|
|
23
|
|
|
protected static $levels = [ |
24
|
|
|
0 => 'OFF', |
25
|
|
|
200 => 'INFO', |
26
|
|
|
250 => 'NOTICE', |
27
|
|
|
300 => 'WARNING', |
28
|
|
|
400 => 'ERROR', |
29
|
|
|
]; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @var int The logging level |
33
|
|
|
*/ |
34
|
|
|
protected $level = self::ERROR; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @var bool Display to user? |
38
|
|
|
*/ |
39
|
|
|
protected $display = false; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @var PluginHooksService |
43
|
|
|
*/ |
44
|
|
|
protected $hooks; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @var Context |
48
|
|
|
*/ |
49
|
|
|
private $context; |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @var array |
53
|
|
|
*/ |
54
|
|
|
private $disabled_stack; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var callable |
58
|
|
|
*/ |
59
|
|
|
private $printer; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Constructor |
63
|
|
|
* |
64
|
|
|
* @param PluginHooksService $hooks Hooks service |
65
|
|
|
* @param Context $context Context service |
66
|
|
|
*/ |
67
|
257 |
|
public function __construct(PluginHooksService $hooks, Context $context) { |
68
|
257 |
|
$this->hooks = $hooks; |
69
|
257 |
|
$this->context = $context; |
70
|
257 |
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Set the logging level |
74
|
|
|
* |
75
|
|
|
* @param int $level The logging level |
76
|
|
|
* @return void |
77
|
|
|
*/ |
78
|
200 |
|
public function setLevel($level) { |
79
|
200 |
|
if (!$level) { |
80
|
|
|
// 0 or empty string |
81
|
1 |
|
$this->level = self::OFF; |
82
|
1 |
|
return; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
// @todo Elgg has used string constants for logging levels |
86
|
199 |
|
if (is_string($level)) { |
87
|
198 |
|
$level = strtoupper($level); |
88
|
198 |
|
$level = array_search($level, self::$levels); |
89
|
|
|
|
90
|
198 |
|
if ($level !== false) { |
91
|
198 |
|
$this->level = $level; |
|
|
|
|
92
|
|
|
} else { |
93
|
|
|
$this->warn(__METHOD__ .": invalid level ignored."); |
94
|
|
|
} |
95
|
198 |
|
return; |
96
|
|
|
} |
97
|
|
|
|
98
|
1 |
|
if (isset(self::$levels[$level])) { |
99
|
1 |
|
$this->level = $level; |
100
|
|
|
} else { |
101
|
|
|
$this->warn(__METHOD__ .": invalid level ignored."); |
102
|
|
|
} |
103
|
1 |
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Get the current logging level |
107
|
|
|
* |
108
|
|
|
* @return int |
109
|
|
|
*/ |
110
|
36 |
|
public function getLevel() { |
111
|
36 |
|
return $this->level; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* Set whether the logging should be displayed to the user |
116
|
|
|
* |
117
|
|
|
* Whether data is actually displayed to the user depends on this setting |
118
|
|
|
* and other factors such as whether we are generating a JavaScript or CSS |
119
|
|
|
* file. |
120
|
|
|
* |
121
|
|
|
* @param bool $display Whether to display logging |
122
|
|
|
* @return void |
123
|
|
|
*/ |
124
|
|
|
public function setDisplay($display) { |
125
|
|
|
$this->display = $display; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
/** |
129
|
|
|
* Set custom printer |
130
|
|
|
* |
131
|
|
|
* @param callable $printer Printer |
132
|
|
|
* @return void |
133
|
|
|
*/ |
134
|
|
|
public function setPrinter(callable $printer) { |
135
|
|
|
if (is_callable($printer)) { |
136
|
|
|
$this->printer = $printer; |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Add a message to the log |
142
|
|
|
* |
143
|
|
|
* @param string $message The message to log |
144
|
|
|
* @param int $level The logging level |
145
|
|
|
* @return bool Whether the messages was logged |
146
|
|
|
*/ |
147
|
384 |
|
public function log($message, $level = self::NOTICE) { |
148
|
384 |
|
if ($this->disabled_stack) { |
|
|
|
|
149
|
|
|
// capture to top of stack |
150
|
224 |
|
end($this->disabled_stack); |
151
|
224 |
|
$key = key($this->disabled_stack); |
152
|
224 |
|
$this->disabled_stack[$key][] = [ |
153
|
224 |
|
'message' => $message, |
154
|
224 |
|
'level' => $level, |
155
|
|
|
]; |
156
|
|
|
} |
157
|
|
|
|
158
|
384 |
|
if ($this->level == self::OFF || $level < $this->level) { |
159
|
287 |
|
return false; |
160
|
|
|
} |
161
|
|
|
|
162
|
112 |
|
if (!array_key_exists($level, self::$levels)) { |
163
|
|
|
return false; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// when capturing, still use consistent return value |
167
|
112 |
|
if ($this->disabled_stack) { |
|
|
|
|
168
|
106 |
|
return true; |
169
|
|
|
} |
170
|
|
|
|
171
|
7 |
|
$levelString = self::$levels[$level]; |
172
|
|
|
|
173
|
|
|
// notices and below never displayed to user |
174
|
7 |
|
$display = $this->display && $level > self::NOTICE; |
175
|
|
|
|
176
|
7 |
|
$this->process("$levelString: $message", $display, $level); |
177
|
|
|
|
178
|
7 |
|
return true; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Log message at the ERROR level |
183
|
|
|
* |
184
|
|
|
* @param string $message The message to log |
185
|
|
|
* @return bool |
186
|
|
|
*/ |
187
|
30 |
|
public function error($message) { |
188
|
30 |
|
return $this->log($message, self::ERROR); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
/** |
192
|
|
|
* Log message at the WARNING level |
193
|
|
|
* |
194
|
|
|
* @param string $message The message to log |
195
|
|
|
* @return bool |
196
|
|
|
*/ |
197
|
13 |
|
public function warn($message) { |
198
|
13 |
|
return $this->log($message, self::WARNING); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Log message at the NOTICE level |
203
|
|
|
* |
204
|
|
|
* @param string $message The message to log |
205
|
|
|
* @return bool |
206
|
|
|
*/ |
207
|
88 |
|
public function notice($message) { |
208
|
88 |
|
return $this->log($message, self::NOTICE); |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* Log message at the INFO level |
213
|
|
|
* |
214
|
|
|
* @param string $message The message to log |
215
|
|
|
* @return bool |
216
|
|
|
*/ |
217
|
284 |
|
public function info($message) { |
218
|
284 |
|
return $this->log($message, self::INFO); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Dump data to log or screen |
223
|
|
|
* |
224
|
|
|
* @param mixed $data The data to log |
225
|
|
|
* @param bool $display Whether to include this in the HTML page |
226
|
|
|
* @return void |
227
|
|
|
*/ |
228
|
|
|
public function dump($data, $display = true) { |
229
|
|
|
$this->process($data, $display, self::ERROR); |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Process logging data |
234
|
|
|
* |
235
|
|
|
* @param mixed $data The data to process |
236
|
|
|
* @param bool $display Whether to display the data to the user. Otherwise log it. |
237
|
|
|
* @param int $level The logging level for this data |
238
|
|
|
* @return void |
239
|
|
|
*/ |
240
|
7 |
|
protected function process($data, $display, $level) { |
241
|
|
|
|
242
|
|
|
// plugin can return false to stop the default logging method |
243
|
|
|
$params = [ |
244
|
7 |
|
'level' => $level, |
245
|
7 |
|
'msg' => $data, |
246
|
7 |
|
'display' => $display, |
247
|
7 |
|
'to_screen' => $display, |
248
|
|
|
]; |
249
|
|
|
|
250
|
7 |
|
if (!$this->hooks->trigger('debug', 'log', $params, true)) { |
251
|
1 |
|
return; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
// Do not want to write to JS or CSS pages |
255
|
6 |
|
if ($this->context->contains('js') || $this->context->contains('css')) { |
256
|
|
|
$display = false; |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
// don't display in simplecache requests |
260
|
6 |
|
$path = substr(current_page_url(), strlen(elgg_get_site_url())); |
261
|
6 |
|
if (preg_match('~^(cache|action|serve-file)/~', $path)) { |
262
|
|
|
$display = false; |
263
|
|
|
} |
264
|
|
|
|
265
|
6 |
|
if ($display == true) { |
|
|
|
|
266
|
|
|
if ($this->printer) { |
267
|
|
|
call_user_func($this->printer, $data, $level); |
268
|
|
|
} else { |
269
|
|
|
echo '<pre class="elgg-logger-data">'; |
270
|
|
|
echo htmlspecialchars(print_r($data, true), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); |
271
|
|
|
echo '</pre>'; |
272
|
|
|
} |
273
|
|
|
} |
274
|
|
|
|
275
|
6 |
|
error_log(print_r($data, true)); |
276
|
6 |
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Temporarily disable logging and capture logs (before tests) |
280
|
|
|
* |
281
|
|
|
* Call disable() before your tests and enable() after. enable() will return a list of |
282
|
|
|
* calls to log() (and helper methods) that were not acted upon. |
283
|
|
|
* |
284
|
|
|
* @note This behaves like a stack. You must call enable() for each disable() call. |
285
|
|
|
* |
286
|
|
|
* @return void |
287
|
|
|
* @see enable |
288
|
|
|
* @access private |
289
|
|
|
* @internal |
290
|
|
|
*/ |
291
|
281 |
|
public function disable() { |
292
|
281 |
|
$this->disabled_stack[] = []; |
293
|
281 |
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Restore logging and get record of log calls (after tests) |
297
|
|
|
* |
298
|
|
|
* @return array |
299
|
|
|
* @see disable |
300
|
|
|
* @access private |
301
|
|
|
* @internal |
302
|
|
|
*/ |
303
|
262 |
|
public function enable() { |
304
|
262 |
|
return array_pop($this->disabled_stack); |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Reset the hooks service for this instance (testing) |
309
|
|
|
* |
310
|
|
|
* @return void |
311
|
|
|
* @access private |
312
|
|
|
* @internal |
313
|
|
|
*/ |
314
|
1 |
|
public function setHooks(PluginHooksService $hooks) { |
315
|
1 |
|
$this->hooks = $hooks; |
316
|
1 |
|
} |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.