/app/Plugin/DebugKit/Lib/FireCake.php
PHP | 538 lines | 327 code | 37 blank | 174 comment | 39 complexity | 7d896522efb6c48e830dd5722a2b5e84 MD5 | raw file
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
- *
- * Licensed under The MIT License
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
- * @link http://cakephp.org CakePHP(tm) Project
- * @since DebugKit 0.1
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- */
- App::uses('Debugger', 'Utility');
- if (!function_exists('firecake')) {
- /**
- * Procedural version of FireCake::log()
- *
- * @param $message
- * @param null $label
- */
- function firecake($message, $label = null) {
- FireCake::fb($message, $label, 'log');
- }
- }
- /**
- * FirePHP Class for CakePHP
- *
- * Provides most of the functionality offered by FirePHPCore
- * Interoperates with FirePHP extension for Firefox
- *
- * For more information see: http://www.firephp.org/
- *
- */
- class FireCake {
- /**
- * Options for FireCake.
- *
- * @see _defaultOptions and setOptions();
- * @var string
- */
- public $options = array();
- /**
- * Default Options used in CakeFirePhp
- *
- * @var string
- */
- protected $_defaultOptions = array(
- 'maxObjectDepth' => 10,
- 'maxArrayDepth' => 20,
- 'useNativeJsonEncode' => true,
- 'includeLineNumbers' => true,
- );
- /**
- * Message Levels for messages sent via FirePHP
- *
- * @var array
- */
- protected $_levels = array(
- 'log' => 'LOG',
- 'info' => 'INFO',
- 'warn' => 'WARN',
- 'error' => 'ERROR',
- 'dump' => 'DUMP',
- 'trace' => 'TRACE',
- 'exception' => 'EXCEPTION',
- 'table' => 'TABLE',
- 'groupStart' => 'GROUP_START',
- 'groupEnd' => 'GROUP_END',
- );
- /**
- * Version number for X-Wf-1-Plugin-1 HTML header
- *
- * @var string
- */
- protected $_version = '0.2.1';
- /**
- * internal messageIndex counter
- *
- * @var integer
- */
- protected $_messageIndex = 1;
- /**
- * stack of objects encoded by stringEncode()
- *
- * @var array
- */
- protected $_encodedObjects = array();
- /**
- * methodIndex to include in tracebacks when using includeLineNumbers
- *
- * @var array
- */
- protected $_methodIndex = array('info', 'log', 'warn', 'error', 'table', 'trace');
- /**
- * FireCake output status
- *
- * @var boolean
- */
- protected $_enabled = true;
- /**
- * get Instance of the singleton
- *
- * @param string $class Class instance to store in the singleton. Used with subclasses and Tests.
- * @return FireCake
- */
- public static function getInstance($class = null) {
- static $instance = array();
- if (!empty($class)) {
- if (!$instance || strtolower($class) !== strtolower(get_class($instance[0]))) {
- $instance[0] = new $class();
- $instance[0]->setOptions();
- }
- }
- if (!isset($instance[0]) || !$instance[0]) {
- $instance[0] = new FireCake();
- $instance[0]->setOptions();
- }
- return $instance[0];
- }
- /**
- * setOptions
- *
- * @param array $options Array of options to set.
- * @return void
- */
- public static function setOptions($options = array()) {
- $_this = FireCake::getInstance();
- if (empty($_this->options)) {
- $_this->options = array_merge($_this->_defaultOptions, $options);
- } else {
- $_this->options = array_merge($_this->options, $options);
- }
- }
- /**
- * Return boolean based on presence of FirePHP extension
- *
- * @return boolean
- */
- public static function detectClientExtension() {
- $ua = FireCake::getUserAgent();
- if (preg_match('/\sFirePHP\/([\.|\d]*)\s?/si', $ua, $match) && version_compare($match[1], '0.0.6', '>=')) {
- return true;
- }
- if (env('HTTP_X_FIREPHP_VERSION') && version_compare(env('HTTP_X_FIREPHP_VERSION'), '0.6', '>=')) {
- return true;
- }
- return false;
- }
- /**
- * Get the Current UserAgent
- *
- * @return string UserAgent string of active client connection
- */
- public static function getUserAgent() {
- return env('HTTP_USER_AGENT');
- }
- /**
- * Disable FireCake output
- * All subsequent output calls will not be run.
- *
- * @return void
- */
- public static function disable() {
- $_this = FireCake::getInstance();
- $_this->_enabled = false;
- }
- /**
- * Enable FireCake output
- *
- * @return void
- */
- public static function enable() {
- $_this = FireCake::getInstance();
- $_this->_enabled = true;
- }
- /**
- * Convenience wrapper for LOG messages
- *
- * @param string $message Message to log
- * @param string $label Label for message (optional)
- * @return void
- */
- public static function log($message, $label = null) {
- FireCake::fb($message, $label, 'log');
- }
- /**
- * Convenience wrapper for WARN messages
- *
- * @param string $message Message to log
- * @param string $label Label for message (optional)
- * @return void
- */
- public static function warn($message, $label = null) {
- FireCake::fb($message, $label, 'warn');
- }
- /**
- * Convenience wrapper for INFO messages
- *
- * @param string $message Message to log
- * @param string $label Label for message (optional)
- * @return void
- */
- public static function info($message, $label = null) {
- FireCake::fb($message, $label, 'info');
- }
- /**
- * Convenience wrapper for ERROR messages
- *
- * @param string $message Message to log
- * @param string $label Label for message (optional)
- * @return void
- */
- public static function error($message, $label = null) {
- FireCake::fb($message, $label, 'error');
- }
- /**
- * Convenience wrapper for TABLE messages
- *
- * @param string $label Label for message (optional)
- * @param string $message Message to log
- * @return void
- */
- public static function table($label, $message) {
- FireCake::fb($message, $label, 'table');
- }
- /**
- * Convenience wrapper for DUMP messages
- *
- * @param string $label Unique label for message
- * @param string $message Message to log
- * @return void
- */
- public static function dump($label, $message) {
- FireCake::fb($message, $label, 'dump');
- }
- /**
- * Convenience wrapper for TRACE messages
- *
- * @param string $label Label for message (optional)
- * @return void
- */
- public static function trace($label) {
- FireCake::fb($label, 'trace');
- }
- /**
- * Convenience wrapper for GROUP messages
- * Messages following the group call will be nested in a group block
- *
- * @param string $label Label for group (optional)
- * @return void
- */
- public static function group($label) {
- FireCake::fb(null, $label, 'groupStart');
- }
- /**
- * Convenience wrapper for GROUPEND messages
- * Closes a group block
- *
- * @internal param string $label Label for group (optional)
- * @return void
- */
- public static function groupEnd() {
- FireCake::fb(null, null, 'groupEnd');
- }
- /**
- * fb - Send messages with FireCake to FirePHP
- *
- * Much like FirePHP's fb() this method can be called with various parameter counts
- * fb($message) - Just send a message defaults to LOG type
- * fb($message, $type) - Send a message with a specific type
- * fb($message, $label, $type) - Send a message with a custom label and type.
- *
- * @param mixed $message Message to output. For other parameters see usage above.
- * @return boolean Success
- */
- public static function fb($message) {
- $_this = FireCake::getInstance();
- if (headers_sent($filename, $linenum)) {
- trigger_error(__d('debug_kit', 'Headers already sent in %s on line %s. Cannot send log data to FirePHP.', $filename, $linenum), E_USER_WARNING);
- return false;
- }
- if (!$_this->_enabled || !$_this->detectClientExtension()) {
- return false;
- }
- $args = func_get_args();
- $type = $label = null;
- switch (count($args)) {
- case 1:
- $type = $_this->_levels['log'];
- break;
- case 2:
- $type = $args[1];
- break;
- case 3:
- $type = $args[2];
- $label = $args[1];
- break;
- default:
- trigger_error(__d('debug_kit', 'Incorrect parameter count for FireCake::fb()'), E_USER_WARNING);
- return false;
- }
- if (isset($_this->_levels[$type])) {
- $type = $_this->_levels[$type];
- } else {
- $type = $_this->_levels['log'];
- }
- $meta = array();
- $skipFinalObjectEncode = false;
- if ($type == $_this->_levels['trace']) {
- $trace = debug_backtrace();
- if (!$trace) {
- return false;
- }
- $message = FireCake::_parseTrace($trace, $args[0]);
- $skipFinalObjectEncode = true;
- }
- if ($_this->options['includeLineNumbers']) {
- if (!isset($meta['file']) || !isset($meta['line'])) {
- $trace = debug_backtrace();
- for ($i = 0, $len = count($trace); $i < $len; $i++) {
- $keySet = (isset($trace[$i]['class']) && isset($trace[$i]['function']));
- $selfCall = ($keySet &&
- strtolower($trace[$i]['class']) === 'firecake' &&
- in_array($trace[$i]['function'], $_this->_methodIndex)
- );
- if ($selfCall) {
- $meta['File'] = isset($trace[$i]['file']) ? Debugger::trimPath($trace[$i]['file']) : '';
- $meta['Line'] = isset($trace[$i]['line']) ? $trace[$i]['line'] : '';
- break;
- }
- }
- }
- }
- $structureIndex = 1;
- if ($type == $_this->_levels['dump']) {
- $structureIndex = 2;
- $_this->_sendHeader('X-Wf-1-Structure-2', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');
- } else {
- $_this->_sendHeader('X-Wf-1-Structure-1', 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
- }
- $_this->_sendHeader('X-Wf-Protocol-1', 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
- $_this->_sendHeader('X-Wf-1-Plugin-1', 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/' . $_this->_version);
- if ($type == $_this->_levels['groupStart']) {
- $meta['Collapsed'] = 'true';
- }
- if ($type == $_this->_levels['dump']) {
- $dump = FireCake::jsonEncode($message);
- $msg = '{"' . $label . '":' . $dump . '}';
- } else {
- $meta['Type'] = $type;
- if ($label !== null) {
- $meta['Label'] = $label;
- }
- $msg = '[' . $_this->jsonEncode($meta) . ',' . $_this->jsonEncode($message, $skipFinalObjectEncode) . ']';
- }
- $lines = explode("\n", chunk_split($msg, 5000, "\n"));
- foreach ($lines as $i => $line) {
- if (empty($line)) {
- continue;
- }
- $header = 'X-Wf-1-' . $structureIndex . '-1-' . $_this->_messageIndex;
- if (count($lines) > 2) {
- $first = ($i == 0) ? strlen($msg) : '';
- $end = ($i < count($lines) - 2) ? '\\' : '';
- $message = $first . '|' . $line . '|' . $end;
- $_this->_sendHeader($header, $message);
- } else {
- $_this->_sendHeader($header, strlen($line) . '|' . $line . '|');
- }
- $_this->_messageIndex++;
- if ($_this->_messageIndex > 99999) {
- trigger_error(__d('debug_kit', 'Maximum number (99,999) of messages reached!'), E_USER_WARNING);
- }
- }
- $_this->_sendHeader('X-Wf-1-Index', $_this->_messageIndex - 1);
- return true;
- }
- /**
- * Parse a debug backtrace
- *
- * @param array $trace Debug backtrace output
- * @param $messageName
- * @return array
- */
- protected static function _parseTrace($trace, $messageName) {
- $message = array();
- for ($i = 0, $len = count($trace); $i < $len; $i++) {
- $keySet = (isset($trace[$i]['class']) && isset($trace[$i]['function']));
- $selfCall = ($keySet && $trace[$i]['class'] === 'FireCake');
- if (!$selfCall) {
- $message = array(
- 'Class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : '',
- 'Type' => isset($trace[$i]['type']) ? $trace[$i]['type'] : '',
- 'Function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : '',
- 'Message' => $messageName,
- 'File' => isset($trace[$i]['file']) ? Debugger::trimPath($trace[$i]['file']) : '',
- 'Line' => isset($trace[$i]['line']) ? $trace[$i]['line'] : '',
- 'Args' => isset($trace[$i]['args']) ? FireCake::stringEncode($trace[$i]['args']) : '',
- 'Trace' => FireCake::_escapeTrace(array_splice($trace, $i + 1))
- );
- break;
- }
- }
- return $message;
- }
- /**
- * Fix a trace for use in output
- *
- * @param mixed $trace Trace to fix
- * @return string
- */
- protected static function _escapeTrace($trace) {
- for ($i = 0, $len = count($trace); $i < $len; $i++) {
- if (isset($trace[$i]['file'])) {
- $trace[$i]['file'] = Debugger::trimPath($trace[$i]['file']);
- }
- if (isset($trace[$i]['args'])) {
- $trace[$i]['args'] = FireCake::stringEncode($trace[$i]['args']);
- }
- }
- return $trace;
- }
- /**
- * Encode non string objects to string.
- * Filter out recursion, so no errors are raised by json_encode or $javascript->object()
- *
- * @param mixed $object Object or variable to encode to string.
- * @param integer $objectDepth Current Depth in object chains.
- * @param integer $arrayDepth Current Depth in array chains.
- * @return string|Object
- */
- public static function stringEncode($object, $objectDepth = 1, $arrayDepth = 1) {
- $_this = FireCake::getInstance();
- $return = array();
- if (is_resource($object)) {
- return '** ' . (string)$object . '**';
- }
- if (is_object($object)) {
- if ($objectDepth == $_this->options['maxObjectDepth']) {
- return '** Max Object Depth (' . $_this->options['maxObjectDepth'] . ') **';
- }
- foreach ($_this->_encodedObjects as $encoded) {
- if ($encoded === $object) {
- return '** Recursion (' . get_class($object) . ') **';
- }
- }
- $_this->_encodedObjects[] = $object;
- $return['__className'] = $class = get_class($object);
- $properties = get_object_vars($object);
- foreach ($properties as $name => $property) {
- $return[$name] = FireCake::stringEncode($property, 1, $objectDepth + 1);
- }
- array_pop($_this->_encodedObjects);
- }
- if (is_array($object)) {
- if ($arrayDepth == $_this->options['maxArrayDepth']) {
- return '** Max Array Depth (' . $_this->options['maxArrayDepth'] . ') **';
- }
- foreach ($object as $key => $value) {
- $return[$key] = FireCake::stringEncode($value, 1, $arrayDepth + 1);
- }
- }
- if (is_string($object) || is_numeric($object) || is_bool($object) || $object === null) {
- return $object;
- }
- return $return;
- }
- /**
- * Encode an object into JSON
- *
- * @param mixed $object Object or array to json encode
- * @param boolean $skipEncode
- * @internal param bool $doIt
- * @static
- * @return string
- */
- public static function jsonEncode($object, $skipEncode = false) {
- $_this = FireCake::getInstance();
- if (!$skipEncode) {
- $object = FireCake::stringEncode($object);
- }
- return json_encode($object);
- }
- /**
- * Send Headers - write headers.
- *
- * @param $name
- * @param $value
- * @return void
- */
- protected function _sendHeader($name, $value) {
- header($name . ': ' . $value);
- }
- }