PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/app/Plugin/DebugKit/Lib/FireCake.php

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