Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12345 anikendra 1
<?php
2
/**
3
 * DebugKit DebugToolbar Component
4
 *
5
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
6
 *
7
 * Licensed under The MIT License
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @since         DebugKit 0.1
13
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
14
 */
15
 
16
App::uses('CakeLog', 'Log');
17
App::uses('CakeLogInterface', 'Log');
18
App::uses('DebugTimer', 'DebugKit.Lib');
19
App::uses('DebugMemory', 'DebugKit.Lib');
20
App::uses('HelperCollection', 'View');
21
App::uses('CakeEventManager', 'Event');
22
App::uses('CakeEventListener', 'Event');
23
 
24
/**
25
 * Class ToolbarComponent
26
 *
27
 * @since         DebugKit 0.1
28
 */
29
class ToolbarComponent extends Component implements CakeEventListener {
30
 
31
/**
32
 * Settings for the Component
33
 *
34
 * - forceEnable - Force the toolbar to display even if debug == 0. Default = false
35
 * - autoRun - Automatically display the toolbar. If set to false, toolbar display can be triggered by adding
36
 *    `?debug=true` to your URL.
37
 *
38
 * @var array
39
 */
40
	public $settings = array(
41
		'forceEnable' => false,
42
		'autoRun' => true
43
	);
44
 
45
/**
46
 * Controller instance reference
47
 *
48
 * @var object
49
 */
50
	public $controller;
51
 
52
/**
53
 * Components used by DebugToolbar
54
 *
55
 * @var array
56
 */
57
	public $components = array('RequestHandler', 'Session');
58
 
59
/**
60
 * The default panels the toolbar uses.
61
 * which panels are used can be configured when attaching the component
62
 *
63
 * @var array
64
 */
65
	protected $_defaultPanels = array(
66
		'DebugKit.History',
67
		'DebugKit.Session',
68
		'DebugKit.Request',
69
		'DebugKit.SqlLog',
70
		'DebugKit.Timer',
71
		'DebugKit.Log',
72
		'DebugKit.Variables',
73
		'DebugKit.Environment',
74
		'DebugKit.Include'
75
	);
76
 
77
/**
78
 * Loaded panel objects.
79
 *
80
 * @var array
81
 */
82
	public $panels = array();
83
 
84
/**
85
 * javascript files component will be using
86
 *
87
 * @var array
88
 */
89
	public $javascript = array(
90
		'libs' => 'DebugKit./js/js_debug_toolbar'
91
	);
92
 
93
/**
94
 * CSS files component will be using
95
 *
96
 * @var array
97
 */
98
	public $css = array('DebugKit./css/debug_toolbar.css');
99
 
100
/**
101
 * CacheKey used for the cache file.
102
 *
103
 * @var string
104
 */
105
	public $cacheKey = 'toolbar_cache';
106
 
107
/**
108
 * Duration of the debug kit history cache
109
 *
110
 * @var string
111
 */
112
	public $cacheDuration = '+4 hours';
113
 
114
/**
115
 * Status whether component is enable or disable
116
 *
117
 * @var boolean
118
 */
119
	public $enabled = true;
120
 
121
/**
122
 * Constructor
123
 *
124
 * If debug is off the component will be disabled and not do any further time tracking
125
 * or load the toolbar helper.
126
 *
127
 * @param ComponentCollection $collection
128
 * @param array $settings
129
 * @return \ToolbarComponent
130
 */
131
	public function __construct(ComponentCollection $collection, $settings = array()) {
132
		$settings = array_merge((array)Configure::read('DebugKit'), $settings);
133
		$panels = $this->_defaultPanels;
134
		if (isset($settings['panels'])) {
135
			$panels = $this->_makePanelList($settings['panels']);
136
			unset($settings['panels']);
137
		}
138
		$this->controller = $collection->getController();
139
 
140
		parent::__construct($collection, array_merge($this->settings, (array)$settings));
141
 
142
		if (
143
			!Configure::read('debug') &&
144
			empty($this->settings['forceEnable'])
145
		) {
146
			$this->enabled = false;
147
			return false;
148
		}
149
		if (
150
			$this->settings['autoRun'] === false &&
151
			!isset($this->controller->request->query['debug'])
152
		) {
153
			$this->enabled = false;
154
			return false;
155
		}
156
 
157
		$this->controller->getEventManager()->attach($this);
158
 
159
		DebugMemory::record(__d('debug_kit', 'Component initialization'));
160
 
161
		$this->cacheKey .= $this->Session->read('Config.userAgent');
162
		if (
163
			in_array('DebugKit.History', $panels) ||
164
			(isset($settings['history']) && $settings['history'] !== false)
165
		) {
166
			$this->_createCacheConfig();
167
		}
168
 
169
		$this->_loadPanels($panels, $settings);
170
		return false;
171
	}
172
 
173
/**
174
 * Register all the timing handlers for core events.
175
 *
176
 * @return array
177
 */
178
	public function implementedEvents() {
179
		$before = function ($name) {
180
			return function () use ($name) {
181
				DebugTimer::start($name, __d('debug_kit', $name));
182
			};
183
		};
184
		$after = function ($name) {
185
			return function () use ($name) {
186
				DebugTimer::stop($name);
187
			};
188
		};
189
 
190
		return array(
191
			'Controller.initialize' => array(
192
				array('priority' => 0, 'callable' => $before('Event: Controller.initialize')),
193
				array('priority' => 999, 'callable' => $after('Event: Controller.initialize'))
194
			),
195
			'Controller.startup' => array(
196
				array('priority' => 0, 'callable' => $before('Event: Controller.startup')),
197
				array('priority' => 999, 'callable' => $after('Event: Controller.startup'))
198
			),
199
			'Controller.beforeRender' => array(
200
				array('priority' => 0, 'callable' => $before('Event: Controller.beforeRender')),
201
				array('priority' => 999, 'callable' => $after('Event: Controller.beforeRender'))
202
			),
203
			'Controller.shutdown' => array(
204
				array('priority' => 0, 'callable' => $before('Event: Controller.shutdown')),
205
				array('priority' => 999, 'callable' => $after('Event: Controller.shutdown'))
206
			),
207
			'View.beforeRender' => array(
208
				array('priority' => 0, 'callable' => $before('Event: View.beforeRender')),
209
				array('priority' => 999, 'callable' => $after('Event: View.beforeRender'))
210
			),
211
			'View.afterRender' => array(
212
				array('priority' => 0, 'callable' => $before('Event: View.afterRender')),
213
				array('priority' => 999, 'callable' => $after('Event: View.afterRender'))
214
			),
215
			'View.beforeLayout' => array(
216
				array('priority' => 0, 'callable' => $before('Event: View.beforeLayout')),
217
				array('priority' => 999, 'callable' => $after('Event: View.beforeLayout'))
218
			),
219
			'View.afterLayout' => array(
220
				array('priority' => 0, 'callable' => $before('Event: View.afterLayout')),
221
				array('priority' => 999, 'callable' => $after('Event: View.afterLayout'))
222
			),
223
		);
224
	}
225
 
226
/**
227
 * Initialize callback.
228
 * If automatically disabled, tell component collection about the state.
229
 *
230
 * @param Controller $controller
231
 * @return boolean
232
 */
233
	public function initialize(Controller $controller) {
234
		if (!$this->enabled) {
235
			$this->_Collection->disable('Toolbar');
236
		}
237
	}
238
 
239
/**
240
 * Go through user panels and remove default panels as indicated.
241
 *
242
 * @param array $userPanels The list of panels ther user has added removed.
243
 * @return array Array of panels to use.
244
 */
245
	protected function _makePanelList($userPanels) {
246
		$panels = $this->_defaultPanels;
247
		foreach ($userPanels as $key => $value) {
248
			if (is_numeric($key)) {
249
				$panels[] = $value;
250
			}
251
			if (is_string($key) && $value === false) {
252
				$index = array_search($key, $panels);
253
				if ($index !== false) {
254
					unset($panels[$index]);
255
				}
256
				// Compatibility for when panels were not
257
				// required to have a plugin prefix.
258
				$alternate = 'DebugKit.' . ucfirst($key);
259
				$index = array_search($alternate, $panels);
260
				if ($index !== false) {
261
					unset($panels[$index]);
262
				}
263
			}
264
		}
265
		return $panels;
266
	}
267
 
268
/**
269
 * Component Startup
270
 *
271
 * @param Controller $controller
272
 * @return boolean
273
 */
274
	public function startup(Controller $controller) {
275
		$panels = array_keys($this->panels);
276
		foreach ($panels as $panelName) {
277
			$this->panels[$panelName]->startup($controller);
278
		}
279
		DebugTimer::start(
280
			'controllerAction',
281
			__d('debug_kit', 'Controller action')
282
		);
283
		DebugMemory::record(
284
			__d('debug_kit', 'Controller action start')
285
		);
286
	}
287
 
288
/**
289
 * beforeRedirect callback
290
 *
291
 * @param Controller $controller
292
 * @param $url
293
 * @param null $status
294
 * @param boolean $exit
295
 * @return void
296
 */
297
	public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true) {
298
		if (!class_exists('DebugTimer')) {
299
			return null;
300
		}
301
		DebugTimer::stop('controllerAction');
302
		DebugTimer::start(
303
			'processToolbar',
304
			__d('debug_kit', 'Processing toolbar state')
305
		);
306
		$vars = $this->_gatherVars($controller);
307
		$this->_saveState($controller, $vars);
308
		DebugTimer::stop('processToolbar');
309
	}
310
 
311
/**
312
 * beforeRender callback
313
 *
314
 * Calls beforeRender on all the panels and set the aggregate to the controller.
315
 *
316
 * @param Controller $controller
317
 * @return void
318
 */
319
	public function beforeRender(Controller $controller) {
320
		if (!class_exists('DebugTimer')) {
321
			return null;
322
		}
323
		DebugTimer::stop('controllerAction');
324
 
325
		DebugTimer::start(
326
			'processToolbar',
327
			__d('debug_kit', 'Processing toolbar data')
328
		);
329
		$vars = $this->_gatherVars($controller);
330
		$this->_saveState($controller, $vars);
331
 
332
		$this->javascript = array_unique(array_merge($this->javascript, $vars['javascript']));
333
		$this->css = array_unique(array_merge($this->css, $vars['css']));
334
		unset($vars['javascript'], $vars['css']);
335
 
336
		$controller->set(array(
337
			'debugToolbarPanels' => $vars,
338
			'debugToolbarJavascript' => $this->javascript,
339
			'debugToolbarCss' => $this->css
340
		));
341
 
342
		$isHtml = (
343
			!isset($controller->request->params['ext']) ||
344
			$controller->request->params['ext'] === 'html'
345
		);
346
 
347
		if (!$controller->request->is('ajax') && $isHtml) {
348
			$format = 'Html';
349
		} else {
350
			$format = 'FirePhp';
351
		}
352
 
353
		$controller->helpers[] = 'DebugKit.DebugTimer';
354
		$controller->helpers['DebugKit.Toolbar'] = array(
355
			'output' => sprintf('DebugKit.%sToolbar', $format),
356
			'cacheKey' => $this->cacheKey,
357
			'cacheConfig' => 'debug_kit',
358
			'forceEnable' => $this->settings['forceEnable'],
359
		);
360
 
361
		DebugTimer::stop('processToolbar');
362
		DebugMemory::record(__d('debug_kit', 'Controller render start'));
363
	}
364
 
365
/**
366
 * Load a toolbar state from cache
367
 *
368
 * @param integer $key
369
 * @return array
370
 */
371
	public function loadState($key) {
372
		$history = Cache::read($this->cacheKey, 'debug_kit');
373
		if (isset($history[$key])) {
374
			return $history[$key];
375
		}
376
		return array();
377
	}
378
 
379
/**
380
 * Create the cache config for the history
381
 *
382
 * @return void
383
 */
384
	protected function _createCacheConfig() {
385
		if (Configure::read('Cache.disable') === true || Cache::config('debug_kit')) {
386
			return;
387
		}
388
		$cache = array(
389
		    'duration' => $this->cacheDuration,
390
		    'engine' => 'File',
391
		    'path' => CACHE
392
		);
393
		if (isset($this->settings['cache'])) {
394
			$cache = array_merge($cache, $this->settings['cache']);
395
		}
396
		Cache::config('debug_kit', $cache);
397
	}
398
 
399
/**
400
 * collects the panel contents
401
 *
402
 * @param Controller $controller
403
 * @return array Array of all panel beforeRender()
404
 */
405
	protected function _gatherVars(Controller $controller) {
406
		$vars = array('javascript' => array(), 'css' => array());
407
		$panels = array_keys($this->panels);
408
 
409
		foreach ($panels as $panelName) {
410
			$panel = $this->panels[$panelName];
411
			$panelName = Inflector::underscore($panelName);
412
			$vars[$panelName]['content'] = $panel->beforeRender($controller);
413
			$elementName = Inflector::underscore($panelName) . '_panel';
414
			if (isset($panel->elementName)) {
415
				$elementName = $panel->elementName;
416
			}
417
			$vars[$panelName]['elementName'] = $elementName;
418
			$vars[$panelName]['plugin'] = $panel->plugin;
419
			$vars[$panelName]['title'] = $panel->title;
420
			$vars[$panelName]['disableTimer'] = true;
421
 
422
			if (!empty($panel->javascript)) {
423
				$vars['javascript'] = array_merge($vars['javascript'], (array)$panel->javascript);
424
			}
425
			if (!empty($panel->css)) {
426
				$vars['css'] = array_merge($vars['css'], (array)$panel->css);
427
			}
428
		}
429
		return $vars;
430
	}
431
 
432
/**
433
 * Load Panels used in the debug toolbar
434
 *
435
 * @param $panels
436
 * @param $settings
437
 * @return void
438
 */
439
	protected function _loadPanels($panels, $settings) {
440
		foreach ($panels as $panel) {
441
			$className = ucfirst($panel) . 'Panel';
442
			list($plugin, $className) = pluginSplit($className, true);
443
 
444
			App::uses($className, $plugin . 'Panel');
445
			if (!class_exists($className)) {
446
				trigger_error(__d('debug_kit', 'Could not load DebugToolbar panel %s', $panel), E_USER_WARNING);
447
				continue;
448
			}
449
			$panelObj = new $className($settings);
450
			if ($panelObj instanceof DebugPanel) {
451
				list(, $panel) = pluginSplit($panel);
452
				$this->panels[Inflector::underscore($panel)] = $panelObj;
453
			}
454
		}
455
	}
456
 
457
/**
458
 * Save the current state of the toolbar varibles to the cache file.
459
 *
460
 * @param \Controller|object $controller Controller instance
461
 * @param array $vars Vars to save.
462
 * @return void
463
 */
464
	protected function _saveState(Controller $controller, $vars) {
465
		$config = Cache::config('debug_kit');
466
		if (empty($config) || !isset($this->panels['history'])) {
467
			return;
468
		}
469
		$history = Cache::read($this->cacheKey, 'debug_kit');
470
		if (empty($history)) {
471
			$history = array();
472
		}
473
		if (count($history) == $this->panels['history']->history) {
474
			array_pop($history);
475
		}
476
 
477
		if (isset($vars['variables']['content'])) {
478
			// Remove unserializable native objects.
479
			array_walk_recursive($vars['variables']['content'], function (&$item) {
480
				if (
481
					$item instanceof Closure ||
482
					$item instanceof PDO ||
483
					$item instanceof SimpleXmlElement
484
				) {
485
					$item = 'Unserializable object - ' . get_class($item);
486
				} elseif ($item instanceof Exception) {
487
					$item = sprintf(
488
						'Unserializable object - %s. Error: %s in %s, line %s',
489
						get_class($item),
490
						$item,
491
						$item->getMessage(),
492
						$item->getFile(),
493
						$item->getLine()
494
					);
495
				}
496
				return $item;
497
			});
498
		}
499
		unset($vars['history']);
500
		array_unshift($history, $vars);
501
		Cache::write($this->cacheKey, $history, 'debug_kit');
502
	}
503
 
504
}