Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12345 anikendra 1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * Redistributions of files must retain the above copyright notice.
8
 *
9
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
10
 * @link          http://cakephp.org CakePHP(tm) Project
11
 * @since         DebugKit 0.1
12
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
13
 */
14
 
15
App::uses('Debugger', 'Utility');
16
 
17
if (!function_exists('firecake')) {
18
 
19
/**
20
 * Procedural version of FireCake::log()
21
 *
22
 * @param $message
23
 * @param null $label
24
 */
25
	function firecake($message, $label = null) {
26
		FireCake::fb($message, $label, 'log');
27
	}
28
 
29
}
30
 
31
/**
32
 * FirePHP Class for CakePHP
33
 *
34
 * Provides most of the functionality offered by FirePHPCore
35
 * Interoperates with FirePHP extension for Firefox
36
 *
37
 * For more information see: http://www.firephp.org/
38
 *
39
 */
40
class FireCake {
41
 
42
/**
43
 * Options for FireCake.
44
 *
45
 * @see _defaultOptions and setOptions();
46
 * @var string
47
 */
48
	public $options = array();
49
 
50
/**
51
 * Default Options used in CakeFirePhp
52
 *
53
 * @var string
54
 */
55
	protected $_defaultOptions = array(
56
		'maxObjectDepth' => 10,
57
		'maxArrayDepth' => 20,
58
		'useNativeJsonEncode' => true,
59
		'includeLineNumbers' => true,
60
	);
61
 
62
/**
63
 * Message Levels for messages sent via FirePHP
64
 *
65
 * @var array
66
 */
67
	protected $_levels = array(
68
		'log' => 'LOG',
69
		'info' => 'INFO',
70
		'warn' => 'WARN',
71
		'error' => 'ERROR',
72
		'dump' => 'DUMP',
73
		'trace' => 'TRACE',
74
		'exception' => 'EXCEPTION',
75
		'table' => 'TABLE',
76
		'groupStart' => 'GROUP_START',
77
		'groupEnd' => 'GROUP_END',
78
	);
79
 
80
/**
81
 * Version number for X-Wf-1-Plugin-1 HTML header
82
 *
83
 * @var string
84
 */
85
	protected $_version = '0.2.1';
86
 
87
/**
88
 * internal messageIndex counter
89
 *
90
 * @var integer
91
 */
92
	protected $_messageIndex = 1;
93
 
94
/**
95
 * stack of objects encoded by stringEncode()
96
 *
97
 * @var array
98
 */
99
	protected $_encodedObjects = array();
100
 
101
/**
102
 * methodIndex to include in tracebacks when using includeLineNumbers
103
 *
104
 * @var array
105
 */
106
	protected $_methodIndex = array('info', 'log', 'warn', 'error', 'table', 'trace');
107
 
108
/**
109
 * FireCake output status
110
 *
111
 * @var boolean
112
 */
113
	protected $_enabled = true;
114
 
115
/**
116
 * get Instance of the singleton
117
 *
118
 * @param string $class Class instance to store in the singleton. Used with subclasses and Tests.
119
 * @return FireCake
120
 */
121
	public static function getInstance($class = null) {
122
		static $instance = array();
123
		if (!empty($class)) {
124
			if (!$instance || strtolower($class) !== strtolower(get_class($instance[0]))) {
125
				$instance[0] = new $class();
126
				$instance[0]->setOptions();
127
			}
128
		}
129
		if (!isset($instance[0]) || !$instance[0]) {
130
			$instance[0] = new FireCake();
131
			$instance[0]->setOptions();
132
		}
133
		return $instance[0];
134
	}
135
 
136
/**
137
 * setOptions
138
 *
139
 * @param array $options Array of options to set.
140
 * @return void
141
 */
142
	public static function setOptions($options = array()) {
143
		$_this = FireCake::getInstance();
144
		if (empty($_this->options)) {
145
			$_this->options = array_merge($_this->_defaultOptions, $options);
146
		} else {
147
			$_this->options = array_merge($_this->options, $options);
148
		}
149
	}
150
 
151
/**
152
 * Return boolean based on presence of FirePHP extension
153
 *
154
 * @return boolean
155
 */
156
	public static function detectClientExtension() {
157
		$ua = FireCake::getUserAgent();
158
		if (preg_match('/\sFirePHP\/([\.|\d]*)\s?/si', $ua, $match) && version_compare($match[1], '0.0.6', '>=')) {
159
			return true;
160
		}
161
		if (env('HTTP_X_FIREPHP_VERSION') && version_compare(env('HTTP_X_FIREPHP_VERSION'), '0.6', '>=')) {
162
			return true;
163
		}
164
		return false;
165
	}
166
 
167
/**
168
 * Get the Current UserAgent
169
 *
170
 * @return string UserAgent string of active client connection
171
 */
172
	public static function getUserAgent() {
173
		return env('HTTP_USER_AGENT');
174
	}
175
 
176
/**
177
 * Disable FireCake output
178
 * All subsequent output calls will not be run.
179
 *
180
 * @return void
181
 */
182
	public static function disable() {
183
		$_this = FireCake::getInstance();
184
		$_this->_enabled = false;
185
	}
186
 
187
/**
188
 * Enable FireCake output
189
 *
190
 * @return void
191
 */
192
	public static function enable() {
193
		$_this = FireCake::getInstance();
194
		$_this->_enabled = true;
195
	}
196
 
197
/**
198
 * Convenience wrapper for LOG messages
199
 *
200
 * @param string $message Message to log
201
 * @param string $label Label for message (optional)
202
 * @return void
203
 */
204
	public static function log($message, $label = null) {
205
		FireCake::fb($message, $label, 'log');
206
	}
207
 
208
/**
209
 * Convenience wrapper for WARN messages
210
 *
211
 * @param string $message Message to log
212
 * @param string $label Label for message (optional)
213
 * @return void
214
 */
215
	public static function warn($message, $label = null) {
216
		FireCake::fb($message, $label, 'warn');
217
	}
218
 
219
/**
220
 * Convenience wrapper for INFO messages
221
 *
222
 * @param string $message Message to log
223
 * @param string $label Label for message (optional)
224
 * @return void
225
 */
226
	public static function info($message, $label = null) {
227
		FireCake::fb($message, $label, 'info');
228
	}
229
 
230
/**
231
 * Convenience wrapper for ERROR messages
232
 *
233
 * @param string $message Message to log
234
 * @param string $label Label for message (optional)
235
 * @return void
236
 */
237
	public static function error($message, $label = null) {
238
		FireCake::fb($message, $label, 'error');
239
	}
240
 
241
/**
242
 * Convenience wrapper for TABLE messages
243
 *
244
 * @param string $label Label for message (optional)
245
 * @param string $message Message to log
246
 * @return void
247
 */
248
	public static function table($label, $message) {
249
		FireCake::fb($message, $label, 'table');
250
	}
251
 
252
/**
253
 * Convenience wrapper for DUMP messages
254
 *
255
 * @param string $label Unique label for message
256
 * @param string $message Message to log
257
 * @return void
258
 */
259
	public static function dump($label, $message) {
260
		FireCake::fb($message, $label, 'dump');
261
	}
262
 
263
/**
264
 * Convenience wrapper for TRACE messages
265
 *
266
 * @param string $label Label for message (optional)
267
 * @return void
268
 */
269
	public static function trace($label) {
270
		FireCake::fb($label, 'trace');
271
	}
272
 
273
/**
274
 * Convenience wrapper for GROUP messages
275
 * Messages following the group call will be nested in a group block
276
 *
277
 * @param string $label Label for group (optional)
278
 * @return void
279
 */
280
	public static function group($label) {
281
		FireCake::fb(null, $label, 'groupStart');
282
	}
283
 
284
/**
285
 * Convenience wrapper for GROUPEND messages
286
 * Closes a group block
287
 *
288
 * @internal param string $label Label for group (optional)
289
 * @return void
290
 */
291
	public static function groupEnd() {
292
		FireCake::fb(null, null, 'groupEnd');
293
	}
294
 
295
/**
296
 * fb - Send messages with FireCake to FirePHP
297
 *
298
 * Much like FirePHP's fb() this method can be called with various parameter counts
299
 * fb($message) - Just send a message defaults to LOG type
300
 * fb($message, $type) - Send a message with a specific type
301
 * fb($message, $label, $type) - Send a message with a custom label and type.
302
 *
303
 * @param mixed $message Message to output. For other parameters see usage above.
304
 * @return boolean Success
305
 */
306
	public static function fb($message) {
307
		$_this = FireCake::getInstance();
308
 
309
		if (headers_sent($filename, $linenum)) {
310
			trigger_error(__d('debug_kit', 'Headers already sent in %s on line %s. Cannot send log data to FirePHP.', $filename, $linenum), E_USER_WARNING);
311
			return false;
312
		}
313
		if (!$_this->_enabled || !$_this->detectClientExtension()) {
314
			return false;
315
		}
316
 
317
		$args = func_get_args();
318
		$type = $label = null;
319
		switch (count($args)) {
320
			case 1:
321
				$type = $_this->_levels['log'];
322
				break;
323
			case 2:
324
				$type = $args[1];
325
				break;
326
			case 3:
327
				$type = $args[2];
328
				$label = $args[1];
329
				break;
330
			default:
331
				trigger_error(__d('debug_kit', 'Incorrect parameter count for FireCake::fb()'), E_USER_WARNING);
332
				return false;
333
		}
334
		if (isset($_this->_levels[$type])) {
335
			$type = $_this->_levels[$type];
336
		} else {
337
			$type = $_this->_levels['log'];
338
		}
339
 
340
		$meta = array();
341
		$skipFinalObjectEncode = false;
342
		if ($type == $_this->_levels['trace']) {
343
			$trace = debug_backtrace();
344
			if (!$trace) {
345
				return false;
346
			}
347
			$message = FireCake::_parseTrace($trace, $args[0]);
348
			$skipFinalObjectEncode = true;
349
		}
350
 
351
		if ($_this->options['includeLineNumbers']) {
352
			if (!isset($meta['file']) || !isset($meta['line'])) {
353
				$trace = debug_backtrace();
354
				for ($i = 0, $len = count($trace); $i < $len; $i++) {
355
					$keySet = (isset($trace[$i]['class']) && isset($trace[$i]['function']));
356
					$selfCall = ($keySet &&
357
						strtolower($trace[$i]['class']) === 'firecake' &&
358
						in_array($trace[$i]['function'], $_this->_methodIndex)
359
					);
360
					if ($selfCall) {
361
						$meta['File'] = isset($trace[$i]['file']) ? Debugger::trimPath($trace[$i]['file']) : '';
362
						$meta['Line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
363
						break;
364
					}
365
				}
366
			}
367
		}
368
 
369
		$structureIndex = 1;
370
		if ($type == $_this->_levels['dump']) {
371
			$structureIndex = 2;
372
			$_this->_sendHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
373
		} else {
374
			$_this->_sendHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
375
		}
376
 
377
		$_this->_sendHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
378
		$_this->_sendHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . $_this->_version);
379
		if ($type == $_this->_levels['groupStart']) {
380
			$meta['Collapsed'] = 'true';
381
		}
382
		if ($type == $_this->_levels['dump']) {
383
			$dump = FireCake::jsonEncode($message);
384
			$msg = '{"' . $label . '":' . $dump . '}';
385
		} else {
386
			$meta['Type'] = $type;
387
			if ($label !== null) {
388
				$meta['Label'] = $label;
389
			}
390
			$msg = '[' . $_this->jsonEncode($meta) . ',' . $_this->jsonEncode($message, $skipFinalObjectEncode) . ']';
391
		}
392
 
393
		$lines = explode("\n", chunk_split($msg, 5000, "\n"));
394
 
395
		foreach ($lines as $i => $line) {
396
			if (empty($line)) {
397
				continue;
398
			}
399
			$header = 'X-Wf-1-' . $structureIndex . '-1-' . $_this->_messageIndex;
400
			if (count($lines) > 2) {
401
				$first = ($i == 0) ? strlen($msg) : '';
402
				$end = ($i < count($lines) - 2) ? '\\' : '';
403
				$message = $first . '|' . $line . '|' . $end;
404
				$_this->_sendHeader($header, $message);
405
			} else {
406
				$_this->_sendHeader($header, strlen($line) . '|' . $line . '|');
407
			}
408
			$_this->_messageIndex++;
409
			if ($_this->_messageIndex > 99999) {
410
				trigger_error(__d('debug_kit', 'Maximum number (99,999) of messages reached!'), E_USER_WARNING);
411
			}
412
		}
413
		$_this->_sendHeader('X-Wf-1-Index', $_this->_messageIndex - 1);
414
		return true;
415
	}
416
 
417
/**
418
 * Parse a debug backtrace
419
 *
420
 * @param array $trace Debug backtrace output
421
 * @param $messageName
422
 * @return array
423
 */
424
	protected static function _parseTrace($trace, $messageName) {
425
		$message = array();
426
		for ($i = 0, $len = count($trace); $i < $len; $i++) {
427
			$keySet = (isset($trace[$i]['class']) && isset($trace[$i]['function']));
428
			$selfCall = ($keySet && $trace[$i]['class'] === 'FireCake');
429
			if (!$selfCall) {
430
				$message = array(
431
					'Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
432
					'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
433
					'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
434
					'Message' => $messageName,
435
					'File' => isset($trace[$i]['file']) ? Debugger::trimPath($trace[$i]['file']) : '',
436
					'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
437
					'Args' => isset($trace[$i]['args']) ? FireCake::stringEncode($trace[$i]['args']) : '',
438
					'Trace' => FireCake::_escapeTrace(array_splice($trace, $i + 1))
439
				);
440
				break;
441
			}
442
		}
443
		return $message;
444
	}
445
 
446
/**
447
 * Fix a trace for use in output
448
 *
449
 * @param mixed $trace Trace to fix
450
 * @return string
451
 */
452
	protected static function _escapeTrace($trace) {
453
		for ($i = 0, $len = count($trace); $i < $len; $i++) {
454
			if (isset($trace[$i]['file'])) {
455
				$trace[$i]['file'] = Debugger::trimPath($trace[$i]['file']);
456
			}
457
			if (isset($trace[$i]['args'])) {
458
				$trace[$i]['args'] = FireCake::stringEncode($trace[$i]['args']);
459
			}
460
		}
461
		return $trace;
462
	}
463
 
464
/**
465
 * Encode non string objects to string.
466
 * Filter out recursion, so no errors are raised by json_encode or $javascript->object()
467
 *
468
 * @param mixed $object Object or variable to encode to string.
469
 * @param integer $objectDepth Current Depth in object chains.
470
 * @param integer $arrayDepth Current Depth in array chains.
471
 * @return string|Object
472
 */
473
	public static function stringEncode($object, $objectDepth = 1, $arrayDepth = 1) {
474
		$_this = FireCake::getInstance();
475
		$return = array();
476
		if (is_resource($object)) {
477
			return '** ' . (string)$object . '**';
478
		}
479
		if (is_object($object)) {
480
			if ($objectDepth == $_this->options['maxObjectDepth']) {
481
				return '** Max Object Depth (' . $_this->options['maxObjectDepth'] . ') **';
482
			}
483
			foreach ($_this->_encodedObjects as $encoded) {
484
				if ($encoded === $object) {
485
					return '** Recursion (' . get_class($object) . ') **';
486
				}
487
			}
488
			$_this->_encodedObjects[] = $object;
489
 
490
			$return['__className'] = $class = get_class($object);
491
			$properties = get_object_vars($object);
492
			foreach ($properties as $name => $property) {
493
				$return[$name] = FireCake::stringEncode($property, 1, $objectDepth + 1);
494
			}
495
			array_pop($_this->_encodedObjects);
496
		}
497
		if (is_array($object)) {
498
			if ($arrayDepth == $_this->options['maxArrayDepth']) {
499
				return '** Max Array Depth (' . $_this->options['maxArrayDepth'] . ') **';
500
			}
501
			foreach ($object as $key => $value) {
502
				$return[$key] = FireCake::stringEncode($value, 1, $arrayDepth + 1);
503
			}
504
		}
505
		if (is_string($object) || is_numeric($object) || is_bool($object) || $object === null) {
506
			return $object;
507
		}
508
		return $return;
509
	}
510
 
511
/**
512
 * Encode an object into JSON
513
 *
514
 * @param mixed $object Object or array to json encode
515
 * @param boolean $skipEncode
516
 * @internal param bool $doIt
517
 * @static
518
 * @return string
519
 */
520
	public static function jsonEncode($object, $skipEncode = false) {
521
		$_this = FireCake::getInstance();
522
		if (!$skipEncode) {
523
			$object = FireCake::stringEncode($object);
524
		}
525
		return json_encode($object);
526
	}
527
 
528
/**
529
 * Send Headers - write headers.
530
 *
531
 * @param $name
532
 * @param $value
533
 * @return void
534
 */
535
	protected function _sendHeader($name, $value) {
536
		header($name . ': ' . $value);
537
	}
538
}