Passed
Push — master ( a42385...9b46a7 )
by Paul
04:14
created

Console::info()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use DateTime;
6
use GeminiLabs\SiteReviews\Application;
7
use ReflectionClass;
8
9
class Console
10
{
11
	const ALERT = 'alert';
12
	const CRITICAL = 'critical';
13
	const DEBUG = 'debug';
14
	const EMERGENCY = 'emergency';
15
	const ERROR = 'error';
16
	const INFO = 'info';
17
	const NOTICE = 'notice';
18
	const WARNING = 'warning';
19
20
	protected $file;
21
	protected $log;
22
23 6
	public function __construct( Application $app )
24
	{
25 6
		$this->file = $app->path( 'console.log' );
26 6
		$this->log = file_exists( $this->file )
27 6
			? file_get_contents( $this->file )
28
			: '';
29 6
		$this->reset();
30 6
	}
31
32
	/**
33
	 * @return string
34
	 */
35
	public function __toString()
36
	{
37
		return $this->get();
38
	}
39
40
	/**
41
	 * Action must be taken immediately
42
	 * Example: Entire website down, database unavailable, etc. This should trigger the SMS alerts and wake you up
43
	 * @param mixed $message
44
	 * @param array $context
45
	 * @return static
46
	 */
47
	public function alert( $message, array $context = [] )
48
	{
49
		return $this->log( static::ALERT, $message, $context );
50
	}
51
52
	/**
53
	 * @return void
54
	 */
55
	public function clear()
56
	{
57
		$this->log = '';
58
		file_put_contents( $this->file, $this->log );
59
	}
60
61
	/**
62
	 * Critical conditions
63
	 * Example: Application component unavailable, unexpected exception
64
	 * @param mixed $message
65
	 * @param array $context
66
	 * @return static
67
	 */
68
	public function critical( $message, array $context = [] )
69
	{
70
		return $this->log( static::CRITICAL, $message, $context );
71
	}
72
73
	/**
74
	 * Detailed debug information
75
	 * @param mixed $message
76
	 * @param array $context
77
	 * @return static
78
	 */
79
	public function debug( $message, array $context = [] )
80
	{
81
		return $this->log( static::DEBUG, $message, $context );
82
	}
83
84
	/**
85
	 * System is unusable
86
	 * @param mixed $message
87
	 * @param array $context
88
	 * @return static
89
	 */
90
	public function emergency( $message, array $context = [] )
91
	{
92
		return $this->log( static::EMERGENCY, $message, $context );
93
	}
94
95
	/**
96
	 * Runtime errors that do not require immediate action but should typically be logged and monitored
97
	 * @param mixed $message
98
	 * @param array $context
99
	 * @return static
100
	 */
101
	public function error( $message, array $context = [] )
102
	{
103
		return $this->log( static::ERROR, $message, $context );
104
	}
105
106
	/**
107
	 * @return string
108
	 */
109
	public function get()
110
	{
111
		return empty( $this->log )
112
			? __( 'Console is empty', 'site-reviews' )
113
			: $this->log;
114
	}
115
116
	/**
117
	 * @param null|string $valueIfEmpty
118
	 * @return string
119
	 */
120
	public function humanSize( $valueIfEmpty = null )
121
	{
122
		$bytes = $this->size();
123
		if( empty( $bytes ) && is_string( $valueIfEmpty )) {
124
			return $valueIfEmpty;
125
		}
126
		$exponent = floor( log( max( $bytes, 1 ), 1024 ));
127
		return round( $bytes / pow( 1024, $exponent ), 2 ).' '.['bytes','KB','MB','GB'][$exponent];
128
	}
129
130
	/**
131
	 * Interesting events
132
	 * Example: User logs in, SQL logs
133
	 * @param mixed $message
134
	 * @param array $context
135
	 * @return static
136
	 */
137 6
	public function info( $message, array $context = [] )
138
	{
139 6
		return $this->log( static::INFO, $message, $context );
140
	}
141
142
	/**
143
	 * @param mixed $level
144
	 * @param mixed $message
145
	 * @return static
146
	 */
147 6
	public function log( $level, $message, array $context = [] )
148
	{
149 6
		$constants = (new ReflectionClass( __CLASS__ ))->getConstants();
150 6
		if( in_array( $level, $constants, true )) {
151 6
			$entry = $this->buildLogEntry( $level, $message, $context );
152 6
			file_put_contents( $this->file, $entry, FILE_APPEND|LOCK_EX );
153 6
			$this->reset();
154
		}
155 6
		return $this;
156
	}
157
158
	/**
159
	 * Normal but significant events
160
	 * @param mixed $message
161
	 * @param array $context
162
	 * @return static
163
	 */
164
	public function notice( $message, array $context = [] )
165
	{
166
		return $this->log( static::NOTICE, $message, $context );
167
	}
168
169
	/**
170
	 * @return int
171
	 */
172 6
	public function size()
173
	{
174 6
		return file_exists( $this->file )
175 6
			? filesize( $this->file )
176 6
			: 0;
177
	}
178
179
	/**
180
	 * Exceptional occurrences that are not errors
181
	 * Example: Use of deprecated APIs, poor use of an API, undesirable things that are not necessarily wrong
182
	 * @param mixed $message
183
	 * @param array $context
184
	 * @return static
185
	 */
186
	public function warning( $message, array $context = [] )
187
	{
188
		return $this->log( static::WARNING, $message, $context );
189
	}
190
191
	/**
192
	 * @param string $level
193
	 * @param mixed $message
194
	 * @return string
195
	 */
196 6
	protected function buildLogEntry( $level, $message, array $context = [] )
197
	{
198 6
		return sprintf( '[%s|%s] %s: %s'.PHP_EOL,
199 6
			current_time( 'mysql' ),
200 6
			$this->getBacktrace(),
201 6
			strtoupper( $level ),
202 6
			$this->interpolate( $message, $context )
203
		);
204
	}
205
206
	/**
207
	 * @return void|string
208
	 */
209 6
	protected function getBacktrace()
210
	{
211 6
		$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 4 );
212 6
		$entry = array_pop( $backtrace );
213 6
		$path = str_replace( [glsr()->path( 'plugin/' ), glsr()->path()], '', $entry['file'] );
214 6
		return $path.':'.$entry['line'];
215
	}
216
217
	/**
218
	 * Interpolates context values into the message placeholders
219
	 * @param mixed $message
220
	 * @param array $context
221
	 * @return string
222
	 */
223 6
	protected function interpolate( $message, $context = [] )
224
	{
225 6
		if( $this->isObjectOrArray( $message ) || !is_array( $context )) {
226
			return print_r( $message, true );
227
		}
228 6
		$replace = [];
229 6
		foreach( $context as $key => $value ) {
230
			$replace['{'.$key.'}'] = $this->normalizeValue( $value );
231
		}
232 6
		return strtr( $message, $replace );
233
	}
234
235
	/**
236
	 * @param mixed $value
237
	 * @return bool
238
	 */
239 6
	protected function isObjectOrArray( $value )
240
	{
241 6
		return is_object( $value ) || is_array( $value );
242
	}
243
244
	/**
245
	 * @param mixed $value
246
	 * @return string
247
	 */
248
	protected function normalizeValue( $value )
249
	{
250
		if( $value instanceof DateTime ) {
251
			$value = $value->format( 'Y-m-d H:i:s' );
252
		}
253
		else if( $this->isObjectOrArray( $value )) {
254
			$value = json_encode( $value );
255
		}
256
		return (string)$value;
257
	}
258
259
	/**
260
	 * @return void
261
	 */
262 6
	protected function reset()
263
	{
264 6
		if( $this->size() > pow( 1024, 2 ) / 8 ) {
265
			$this->clear();
266
			file_put_contents(
267
				$this->file,
268
				$this->buildLogEntry( 'info', __( 'Console was automatically cleared (128 KB maximum size)', 'site-reviews' ))
269
			);
270
		}
271 6
	}
272
}
273