JBDump Increment / "' . $counterName . '" = ' . $count . ''; } } // JBDump trace incriment output if (!empty(self::$_counters['trace'])) { uasort(self::$_counters['trace'], function ($a, $b) { if ($a['count'] == $b['count']) { return 0; } return ($a['count'] < $b['count']) ? 1 : -1; }); foreach (self::$_counters['trace'] as $counterHash => $traceInfo) { self::i()->dump($traceInfo['trace'], $traceInfo['label'] . ' = ' . $traceInfo['count']); } } // JBDump pairs profiler if (!empty(self::$_profilerPairs)) { foreach (self::$_profilerPairs as $label => $pairs) { $timeDelta = $memDelta = $count = 0; $memDiffs = $timeDiffs = array(); foreach ($pairs as $key => $pair) { if (!isset($pair['stop']) || !isset($pair['start'])) { continue; } $count++; $tD = $pair['stop'][0] - $pair['start'][0]; $mD = $pair['stop'][1] - $pair['start'][1]; $timeDiffs[] = $tD; $memDiffs[] = $mD; $timeDelta += $tD; $memDelta += $mD; } if ($count > 0) { $timeAvg = array_sum($timeDiffs) / $count; $memoAvg = array_sum($memDiffs) / $count; $timeStd = $memoStd = ''; if ($count > 1) { $timeStdValue = $this->_stdDev($timeDiffs); $memoStdValue = $this->_stdDev($memDiffs); $timeStd = ' (±' . self::_profilerFormatTime($timeStdValue, true, 2) . ')'; $memoStd = ' (±' . self::_profilerFormatMemory($memoStdValue, true) . ')'; } $output = array( '
JBDump ProfilerPairs / "' . $label . '"',
'Count = ' . $count,
'Time = ' . implode(";\t\t", array(
'ave: ' . self::_profilerFormatTime($timeAvg, true, 2) . $timeStd,
'sum: ' . self::_profilerFormatTime(array_sum($timeDiffs), true, 2),
'min(' . (array_search(min($timeDiffs), $timeDiffs) + 1) . '):' . self::_profilerFormatTime(min($timeDiffs), true, 2),
'max(' . (array_search(max($timeDiffs), $timeDiffs) + 1) . '): ' . self::_profilerFormatTime(max($timeDiffs), true, 2),
)),
'Memory = ' . implode(";\t\t", array(
'ave: ' . self::_profilerFormatMemory($memoAvg, true) . $memoStd,
'sum: ' . self::_profilerFormatMemory(array_sum($memDiffs), true),
'min(' . (array_search(min($memDiffs), $memDiffs) + 1) . '): ' . self::_profilerFormatMemory(min($memDiffs), true),
'max(' . (array_search(max($memDiffs), $memDiffs) + 1) . '): ' . self::_profilerFormatMemory(max($memDiffs), true),
)),
''
);
} else {
$output = array(
'JBDump ProfilerPairs / "' . $label . '"',
'Count = ' . $count,
''
);
}
echo implode(PHP_EOL, $output);
}
}
}
/**
* Returns the global JBDump object, only creating it
* if it doesn't already exist
* @param array $options Initialization parameters
* @return JBDump
*/
public static function i($options = array())
{
static $instance;
if (!isset($instance)) {
$instance = new self($options);
if (self::$_config['profiler']['showStart']) {
self::mark('jbdump::start');
}
}
return $instance;
}
/**
* Include css and js files in document
* @param bool $force
* @return void
*/
protected function _initAssets($force = true)
{
static $loaded;
if (!isset($loaded) || $force) {
$loaded = true;
echo
'
';
}
}
/**
* Check permissions for show all debug messages
* - check ip, it if set in config
* - check requestParam, if it set in config
* - else return self::$enabled
* @return bool
*/
public static function isDebug()
{
$result = self::$enabled;
if ($result) {
if (self::$_config['personal']['ip']) {
if (is_array(self::$_config['personal']['ip'])) {
$result = in_array(self::getClientIP(), self::$_config['personal']['ip']);
} else {
$result = self::getClientIP() == self::$_config['personal']['ip'];
}
}
if (self::$_config['personal']['requestParam'] && $result) {
if (isset($_REQUEST[self::$_config['personal']['requestParam']])
&&
$_REQUEST[self::$_config['personal']['requestParam']] == self::$_config['personal']['requestValue']
) {
$result = true;
} else {
$result = false;
}
}
}
return $result;
}
/**
* Force show PHP error messages
* @param $reportLevel error_reporting level
* @return bool
*/
public static function showErrors($reportLevel = -1)
{
if (!self::isDebug()) {
return false;
}
if ($reportLevel === null || $reportLevel === false) {
return false;
}
if ($reportLevel != 0) {
error_reporting($reportLevel);
ini_set('error_reporting', $reportLevel);
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
} else {
error_reporting(0);
ini_set('error_reporting', 0);
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
}
return true;
}
/**
* Set max execution time
* @param integer $time Time limit in seconds
* @return JBDump
*/
public static function maxTime($time = 600)
{
if (!self::isDebug()) {
return false;
}
ini_set('max_execution_time', $time);
set_time_limit($time);
return self::i();
}
/**
* Enable debug
* @return JBDump
*/
public static function on()
{
self::$enabled = true;
return self::i();
}
/**
* Disable debug
* @return JBDump
*/
public static function off()
{
self::$enabled = false;
return self::i();
}
/**
* Set debug parameters
* @param array $data Params for debug, see self::$_config vars
* @param string $section
* @return JBDump
*/
public function setParams($data, $section = null)
{
if ($section) {
$newData = array($section => $data);
$data = $newData;
unset($newData);
}
if (isset($data['errors']['reporting'])) {
$this->showErrors($data['errors']['reporting']);
}
// set root directory
if (!isset($data['root']) && !self::$_config['root']) {
$data['root'] = $_SERVER['DOCUMENT_ROOT'];
}
// set log path
if (isset($data['log']['path']) && $data['log']['path']) {
$this->_logPath = $data['log']['path'];
} elseif (!self::$_config['log']['path'] || !$this->_logPath) {
$this->_logPath = dirname(__FILE__) . self::DS . 'logs';
}
// set log filename
$logFile = 'jbdump';
if (isset($data['log']['file']) && $data['log']['file']) {
$logFile = $data['log']['file'];
} elseif (!self::$_config['log']['file'] || !$this->_logfile) {
$logFile = 'jbdump';
}
$this->_logfile = $this->_logPath . self::DS . $logFile . '_' . date('Y.m.d') . '.log.php';
// merge new params with of config
foreach ($data as $key => $value) {
if (is_array($value)) {
foreach ($value as $keyInner => $valueInner) {
if (!isset(self::$_config[$key])) {
self::$_config[$key] = array();
}
self::$_config[$key][$keyInner] = $valueInner;
}
} else {
self::$_config[$key] = $value;
}
}
return $this;
}
/**
* Show client IP
* @return JBDump
*/
public static function ip()
{
if (!self::isDebug()) {
return false;
}
$ip = self::getClientIP();
$data = array(
'ip' => $ip,
'host' => gethostbyaddr($ip),
'source' => '$_SERVER["' . self::getClientIP(true) . '"]',
'inet_pton' => inet_pton($ip),
'ip2long' => ip2long($ip),
);
return self::i()->dump($data, '! my IP = ' . $ip . ' !');
}
/**
* Show $_GET array
* @return JBDump
*/
public static function get()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($_GET, '! $_GET !');
}
/**
* Add message to log file
* @param mixed $entry Text to log file
* @param string $markName Name of log record
* @param array $params Additional params
* @return JBDump
*/
public static function log($entry, $markName = '...', $params = array())
{
if (!self::isDebug()) {
return false;
}
// emulate normal class
$_this = self::i();
// check var type
if (is_bool($entry)) {
$entry = ($entry) ? 'TRUE' : 'FALSE';
} elseif (is_null($entry)) {
$entry = 'NULL';
} elseif (is_resource($entry)) {
$entry = 'resource of "' . get_resource_type($entry) . '"';
}
// serialize type
if (self::$_config['log']['serialize'] == 'formats') {
// don't change log entry
} elseif (self::$_config['log']['serialize'] == 'none') {
$entry = array('jbdump_message' => $entry);
} elseif (self::$_config['log']['serialize'] == 'json') {
$entry = array('jbdump_message' => @json_encode($entry));
} elseif (self::$_config['log']['serialize'] == 'serialize') {
$entry = array('jbdump_message' => serialize($entry));
} elseif (self::$_config['log']['serialize'] == 'print_r') {
$entry = array('jbdump_message' => print_r($entry, true));
} elseif (self::$_config['log']['serialize'] == 'php_array') {
$markName = (empty($markName) || $markName == '...') ? 'dumpVar' : $markName;
$entry = array('jbdump_message' => JBDump_array2php::toString($entry, $markName));
} elseif (self::$_config['log']['serialize'] == 'var_dump') {
ob_start();
var_dump($entry);
$entry = ob_get_clean();
$entry = array('jbdump_message' => var_dump($entry, true));
}
if (isset($params['trace'])) {
$_this->_trace = $params['trace'];
} else {
$_this->_trace = debug_backtrace();
}
$entry['name'] = $markName;
$entry['datetime'] = date(self::DATE_FORMAT);
$entry['client_ip'] = self::getClientIP();
$entry['file'] = $_this->_getSourcePath($_this->_trace, true);
$entry = array_change_key_case($entry, CASE_UPPER);
$fields = array();
$format = isset($params['format']) ? $params['format'] : self::$_config['log']['format'];
preg_match_all("/{(.*?)}/i", $format, $fields);
// Fill in the field data
$line = $format;
for ($i = 0; $i < count($fields[0]); $i++) {
$line = str_replace($fields[0][$i], (isset ($entry[$fields[1][$i]])) ? $entry[$fields[1][$i]] : "-", $line);
}
// Write the log entry line
if ($_this->_openLog()) {
error_log($line . PHP_EOL, 3, $_this->_logfile);
}
return $_this;
}
/**
* Open log file
* @return bool
*/
function _openLog()
{
if (!@file_exists($this->_logfile)) {
if (!is_dir($this->_logPath) && $this->_logPath) {
mkdir($this->_logPath, 0777, true);
}
$header[] = "#";
$header[] = "#Date: " . date(DATE_RFC822, time());
$header[] = "#Software: JBDump v" . self::VERSION . ' by Joomla-book.ru';
$fields = str_replace("{", "", self::$_config['log']['format']);
$fields = str_replace("}", "", $fields);
$fields = strtolower($fields);
$header[] = '#' . str_replace("\t", "\t", $fields);
$head = implode(PHP_EOL, $header);
} else {
$head = false;
}
if ($head) {
error_log($head . PHP_EOL, 3, $this->_logfile);
}
return true;
}
/**
* Show $_FILES array
* @return JBDump
*/
public static function files()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($_FILES, '! $_FILES !');
}
/**
* Show current usage memory in filesize format
* @return JBDump
*/
public static function memory($formated = true)
{
if (!self::isDebug()) {
return false;
}
$memory = self::i()->_getMemory();
if ($formated) {
$memory = self::i()->_formatSize($memory);
}
return self::i()->dump($memory, '! memory !');
}
/**
* Show declared interfaces
* @return JBDump
*/
public static function interfaces()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump(get_declared_interfaces(), '! interfaces !');
}
/**
* Parse url
* @param string $url URL string
* @param string $varname URL name
* @return JBDump
*/
public static function url($url, $varname = '...')
{
if (!self::isDebug()) {
return false;
}
$parsed = parse_url($url);
if (isset($parsed['query'])) {
parse_str($parsed['query'], $parsed['query_parsed']);
}
return self::i()->dump($parsed, $varname);
}
/**
* Show included files
* @return JBDump
*/
public static function includes()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump(get_included_files(), '! includes files !');
}
/**
* Show defined functions
* @param bool $showInternal Get only internal functions
* @return JBDump
*/
public static function functions($showInternal = false)
{
if (!self::isDebug()) {
return false;
}
$functions = get_defined_functions();
if ($showInternal) {
$functions = $functions['internal'];
$type = 'internal';
} else {
$functions = $functions['user'];
$type = 'user';
}
return self::i()->dump($functions, '! functions (' . $type . ') !');
}
/**
* Show defined constants
* @param bool $showAll Get only user defined functions
* @return bool|JBDump
*/
public static function defines($showAll = false)
{
if (!self::isDebug()) {
return false;
}
$defines = get_defined_constants(true);
if (!$showAll) {
$defines = (isset($defines['user'])) ? $defines['user'] : array();
}
return self::i()->dump($defines, '! defines !');
}
/**
* Show loaded PHP extensions
* @param bool $zend Get only Zend extensions
* @return JBDump
*/
public static function extensions($zend = false)
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump(get_loaded_extensions($zend), '! extensions ' . ($zend ? '(Zend)' : '') . ' !');
}
/**
* Show HTTP headers
* @return JBDump
*/
public static function headers()
{
if (!self::isDebug()) {
return false;
}
if (function_exists('apache_request_headers')) {
$data = array(
'Request' => apache_request_headers(),
'Response' => apache_response_headers(),
'List' => headers_list()
);
} else {
$data = array(
'List' => headers_list()
);
}
if (headers_sent($filename, $linenum)) {
$data['Sent'] = 'Headers already sent in ' . self::i()->_getRalativePath($filename) . ':' . $linenum;
} else {
$data['Sent'] = false;
}
return self::i()->dump($data, '! headers !');
}
/**
* Show php.ini content (open php.ini file)
* @return JBDump
*/
public static function phpini()
{
if (!self::isDebug()) {
return false;
}
$data = get_cfg_var('cfg_file_path');
if (!@file($data)) {
return false;
}
$ini = parse_ini_file($data, true);
return self::i()->dump($ini, '! php.ini !');
}
/**
* Show php.ini content (PHP API)
* @param string $extension Extension name
* @param bool $details Retrieve details settings or only the current value for each setting
* @return bool|JBDump
*/
public static function conf($extension = '', $details = true)
{
if (!self::isDebug()) {
return false;
}
if ($extension == '') {
$label = '';
$data = ini_get_all();
} else {
$label = ' (' . $extension . ') ';
$data = ini_get_all($extension, $details);
}
return self::i()->dump($data, '! configuration settings' . $label . ' !');
}
/**
* Show included and system paths
* @return JBDump
*/
public static function path()
{
if (!self::isDebug()) {
return false;
}
$result = array(
'get_include_path' => explode(PATH_SEPARATOR, trim(get_include_path(), PATH_SEPARATOR)),
'$_SERVER[PATH]' => explode(PATH_SEPARATOR, trim($_SERVER['PATH'], PATH_SEPARATOR))
);
return self::i()->dump($result, '! paths !');
}
/**
* Show $_REQUEST array or dump $_GET, $_POST, $_COOKIE
* @param bool $notReal Get real $_REQUEST array
* @return bool|JBDump
*/
public static function request($notReal = false)
{
if (!self::isDebug()) {
return false;
}
if ($notReal) {
self::get();
self::post();
self::cookie();
return self::files();
} else {
return self::i()->dump($_REQUEST, '! $_REQUEST !');
}
}
/**
* Show $_POST array
* @return JBDump
*/
public static function post()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($_POST, '! $_POST !');
}
/**
* Show $_SERVER array
* @return JBDump
*/
public static function server()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($_SERVER, '! $_SERVER !');
}
/**
* Show $_COOKIE array
* @return JBDump
*/
public static function cookie()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($_COOKIE, '! $_COOKIE !');
}
/**
* Convert JSON format to human readability
* @param $json
* @param string $name
* @return bool|JBDump
*/
public static function json($json, $name = '...')
{
if (!self::isDebug()) {
return false;
}
$jsonData = json_decode($json);
$result = self::i()->_jsonEncode($jsonData);
return self::i()->dump($result, $name);
}
/**
* Show $_ENV array
* @return JBDump
*/
public static function env()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($_ENV, '! $_ENV !');
}
/**
* Show $_SESSION array
* @return JBDump
*/
public static function session()
{
$sessionId = session_id();
if (!$sessionId) {
$_SESSION = 'PHP session don\'t start';
$sessionId = '';
} else {
$sessionId = ' (' . $sessionId . ') ';
}
return self::i()->dump($_SESSION, '! $_SESSION ' . $sessionId . ' !');
}
/**
* Show $GLOBALS array
* @return JBDump
*/
public static function globals()
{
if (!self::isDebug()) {
return false;
}
return self::i()->dump($GLOBALS, '! $GLOBALS !');
}
/**
* Convert timestamp to normal date, in DATE_RFC822 format
* @param null|integer $timestamp Time in Unix timestamp format
* @return bool|JBDump
*/
public static function timestamp($timestamp = null)
{
if (!self::isDebug()) {
return false;
}
$date = date(DATE_RFC822, $timestamp);
return self::i()->dump($date, $timestamp . ' sec = ');
}
/**
* @param string $url
* @param array $data
* @param string $method
* @param array $params
* @return JBDump
*/
public static function loadUrl($url, $data = array(), $method = 'get', $params = array())
{
$result = array(
'lib' => '',
'code' => 0,
'headers' => array(),
'body' => null,
'error' => null,
'info' => null,
);
$method = trim(strtolower($method));
$queryData = http_build_query((array)$data, null, '&');
if ($method == 'get') {
$url = $url . (strpos($url, '?') === false ? '?' : '&') . $queryData;
}
if (function_exists('curl_init') && is_callable('curl_init')) {
$result['lib'] = 'cUrl';
$options = array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true, // return web page
CURLOPT_HEADER => true, // return headers
CURLOPT_ENCODING => "", // handle all encodings
CURLOPT_USERAGENT => "JBDump", // who am i
CURLOPT_AUTOREFERER => true, // set referer on redirect
CURLOPT_CONNECTTIMEOUT => 120, // timeout on connect
CURLOPT_TIMEOUT => 120, // timeout on response
CURLOPT_MAXREDIRS => 20, // stop after 10 redirects
// Disabled SSL Cert checks
CURLOPT_SSL_VERIFYPEER => isset($params['ssl']) ? $params['ssl'] : true,
CURLOPT_HTTPHEADER => array(
'Expect:', // http://the-stickman.com/web-development/php-and-curl-disabling-100-continue-header/
'Content-Type:application/x-www-form-urlencoded; charset=utf-8',
),
);
if (isset($params['cert'])) {
$options[CURLOPT_CAINFO] = __DIR__ . '/jbdump.pem';
}
if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
$options[CURLOPT_FOLLOWLOCATION] = true;
}
if ($method == 'post') {
$options[CURLOPT_POSTFIELDS] = $queryData;
$options[CURLOPT_POST] = true;
}
$ch = curl_init($url);
curl_setopt_array($ch, $options);
$result['full'] = curl_exec($ch);
if (curl_errno($ch) || curl_error($ch)) {
$result['error'] = '#' . curl_errno($ch) . ' - "' . curl_error($ch) . '"';
}
$info = curl_getinfo($ch);
curl_close($ch);
// parse response
$redirects = isset($info['redirect_count']) ? $info['redirect_count'] : 0;
$response = explode("\r\n\r\n", $result['full'], 2 + $redirects);
$result['body'] = array_pop($response);
$headers = explode("\r\n", array_pop($response));
// code
preg_match('/[0-9]{3}/', array_shift($headers), $matches);
$result['code'] = count($matches) ? $matches[0] : null;
// parse headers
$resHeaders = array();
foreach ($headers as $header) {
$pos = strpos($header, ':');
$name = trim(substr($header, 0, $pos));
$value = trim(substr($header, ($pos + 1)));
$resHeaders[$name] = $value;
}
$result['info'] = $info;
$result['headers'] = $resHeaders;
} else {
$result['lib'] = 'file_get_contents';
$context = null;
if ($method == 'post') {
$context = stream_context_create(array('http' => array(
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => $queryData
)));
}
$result['full'] = file_get_contents($url, false, $context);
}
return self::i()->dump($result, 'Load URL');
}
/**
* Find all locale in system
* list - only for linux like systems
* @return JBDump
*/
public static function locale()
{
if (!self::isDebug()) {
return false;
}
ob_start();
@system('locale -a');
$locale = explode(PHP_EOL, trim(ob_get_contents()));
ob_end_clean();
$result = array(
'list' => $locale,
'conv' => @localeconv()
);
return self::i()->dump($result, '! locale info !');
}
/**
* Show date default timezone
* @return JBDump
*/
public static function timezone()
{
if (!self::isDebug()) {
return false;
}
$data = date_default_timezone_get();
return self::i()->dump($data, '! timezone !');
}
/**
* Wrapper for PHP print_r function
* @param mixed $var The variable to dump
* @param string $varname Label to prepend to output
* @param array $params Echo output if true
* @return bool|JBDump
*/
public static function print_r($var, $varname = '...', $params = array())
{
if (!self::isDebug()) {
return false;
}
$output = print_r($var, true);
$_this = self::i();
$_this->_dumpRenderHtml($output, $varname, $params);
return $_this;
}
/**
* Render variable as phpArray
* @param mixed $var
* @param string $name
* @param bool $isReturn
* @return mixed
*/
public static function phpArray($var, $varname = 'varName', $isReturn = false)
{
if (!self::isDebug()) {
return false;
}
$output = JBDump_array2php::toString($var, $varname);
if ($isReturn) {
return $output;
}
$_this = self::i();
$_this->_dumpRenderHtml($output, $varname, $params);
return $_this;
}
/**
* Wrapper for PHP var_dump function
* @param mixed $var The variable to dump
* @param string $varname Echo output if true
* @param array $params Additionls params
* @return bool|JBDump
*/
public static function var_dump($var, $varname = '...', $params = array())
{
if (!self::isDebug()) {
return false;
}
// var_dump the variable into a buffer and keep the output
ob_start();
var_dump($var);
$output = ob_get_clean();
$_this = self::i();
// neaten the newlines and indents
$output = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $output);
//if (!extension_loaded('xdebug')) {
$output = $_this->_htmlChars($output);
//}
$_this->_dumpRenderHtml($output, $varname . '::html', $params);
return $_this;
}
/**
* Get system backtrace in formated view
* @param bool $trace Custom php backtrace
* @param bool $addObject Show objects in result
* @return JBDump
*/
public static function trace($trace = null, $addObject = false)
{
if (!self::isDebug()) {
return false;
}
$_this = self::i();
$trace = $trace ? $trace : debug_backtrace($addObject);
unset($trace[0]);
$result = $_this->convertTrace($trace, $addObject);
return $_this->dump($result, '! backtrace !');
}
/**
* Show declared classes
* @param bool $sort
* @return bool|JBDump
*/
public static function classes($sort = false)
{
if (!self::isDebug()) {
return false;
}
$classes = get_declared_classes();
if ((bool)$sort) {
sort($classes);
}
return self::i()->dump($classes, '! classes !');
}
/**
* Show declared classes
* @param $object
* @return bool|JBDump
*/
public static function methods($object)
{
if (!self::isDebug()) {
return false;
}
$methodst = self::i()->_getMethods($object);
if (is_string($object)) {
$className = $object;
} else {
$className = get_class($object);
}
return self::i()->dump($methodst, '<! methods of "' . $className . '" !>');
}
/**
* Dump info about class (object)
* @param string|object $data Object or class name
* @return JBDump
*/
public static function classInfo($data)
{
$result = self::_getClass($data);
if ($result) {
$data = $result['name'];
}
return self::i()->dump($result, '! class (' . $data . ') !');
}
/**
* Dump all info about extension
* @param string $extensionName Extension name
* @return JBDump
*/
public static function extInfo($extensionName)
{
$result = self::_getExtension($extensionName);
if ($result) {
$extensionName = $result['name'];
}
return self::i()->dump($result, '! extension (' . $extensionName . ') !');
}
/**
* Dump all file info
* @param string $file path to file
* @return JBDump
*/
public static function pathInfo($file)
{
$result = self::_pathInfo($file);
return self::i()->dump($result, '! pathInfo (' . $file . ') !');
}
/**
* Dump all info about function
* @param string|Closure $functionName Closure or function name
* @return JBDump
*/
public static function funcInfo($functionName)
{
$result = self::_getFunction($functionName);
if ($result) {
$functionName = $result['name'];
}
return self::i()->dump($result, '! function (' . $functionName . ') !');
}
/**
* Show current microtime
* @return JBDump
*/
public static function microtime()
{
$_this = self::i();
if (!$_this->isDebug()) {
return false;
}
$data = $_this->_microtime();
return $_this->dump($data, '! current microtime !');
}
/**
* @param string $label
* @return bool
* @return JBDump
*/
public static function markStart($label = 'default')
{
$time = self::_microtime();
$memory = self::_getMemory();
$_this = self::i();
if (!$_this->isDebug()) {
return false;
}
if (!isset(self::$_profilerPairs[$label])) {
self::$_profilerPairs[$label] = array();
}
$length = count(self::$_profilerPairs[$label]);
if (isset(self::$_profilerPairs[$label][$length]['start'])) {
$length++;
}
self::$_profilerPairs[$label][$length] = array('start' => array($time, $memory));
return $_this;
}
/**
* @param int $outputMode
* 0 - on destructor (PHP Die)
* 1 - immediately
* @param string $name
*/
public static function inc($name = null, $outputMode = 0)
{
$_this = self::i();
if (!$_this->isDebug()) {
return false;
}
if (!$name) {
$trace = debug_backtrace();
$traceInfo = $_this->_getOneTrace($trace[1]);
$line = isset($trace[0]['line']) ? $trace[0]['line'] : 0;
$name = $traceInfo['func'] . ', line #' . $line;
}
if (is_string($outputMode)) {
$name = $outputMode;
$outputMode = 0;
}
if (!isset(self::$_counters['mode_' . $outputMode][$name])) {
self::$_counters['mode_' . $outputMode][$name] = 0;
}
self::$_counters['mode_' . $outputMode][$name]++;
if ($outputMode == 1) {
echo '' . $name . ' = ' . self::$_counters['mode_' . $outputMode][$name] . ''; } return self::$_counters['mode_' . $outputMode][$name]; } /** * @param string $label * @return bool * @return int */ public static function incTrace($label = null) { $_this = self::i(); if (!$_this->isDebug()) { return false; } $trace = debug_backtrace(); if (!$label) { $traceInfo = $_this->_getOneTrace($trace[1]); $line = isset($trace[0]['line']) ? $trace[0]['line'] : 0; $label = $traceInfo['func'] . ', line #' . $line; } unset($trace[0]); unset($trace[1]); $trace = array_slice($trace, 0, self::$_config['profiler']['traceLimit']); $traceInfo = array(); foreach ($trace as $oneTrace) { $traceData = $_this->_getOneTrace($oneTrace); $line = isset($oneTrace['line']) ? $oneTrace['line'] : 0; $traceInfo[] = $traceData['func'] . ', line #' . $line; } $hash = md5(serialize($traceInfo)); if (!isset(self::$_counters['trace'][$hash])) { self::$_counters['trace'][$hash] = array( 'count' => 0, 'label' => $label, 'trace' => $traceInfo, ); } self::$_counters['trace'][$hash]['count']++; return self::$_counters['trace'][$hash]['count']; } /** * @param string $label * @return JBDump */ public static function markStop($label = 'default') { $time = self::_microtime(); $memory = self::_getMemory(); $_this = self::i(); if (!$_this->isDebug()) { return false; } if (!isset(self::$_profilerPairs[$label])) { self::$_profilerPairs[$label] = array(); } $length = count(self::$_profilerPairs[$label]); if ($length > 0) { $length--; } self::$_profilerPairs[$label][$length]['stop'] = array($time, $memory); return $_this; } /** * Output a time mark * The mark is returned as text current profiler status * @param string $label A label for the time mark * @return JBDump */ public static function mark($label = '') { $_this = self::i(); if (!$_this->isDebug()) { return false; } $current = $_this->_microtime() - $_this->_start; $memory = self::_getMemory(); $trace = debug_backtrace(); $markInfo = array( 'time' => $current, 'timeDiff' => $current - $_this->_prevTime, 'memory' => $memory, 'memoryDiff' => $memory - $_this->_prevMemory, 'trace' => $_this->_getSourcePath($trace, true), 'label' => $label, ); $_this->_bufferInfo[] = $markInfo; if ((int)self::$_config['profiler']['render'] & self::PROFILER_RENDER_FILE) { $_this->log(self::_profilerFormatMark($markInfo), 'mark #'); } $_this->_prevTime = $current; $_this->_prevMemory = $memory; return $_this; } /** * Show profiler result * @param int $mode Render mode * @return JBDump */ public function profiler($mode = 1) { if ($this->isDebug() && count($this->_bufferInfo) > 2 && $mode) { $mode = (int)$mode; if ($mode && self::isAjax()) { if ($mode & self::PROFILER_RENDER_TOTAL) { $this->_profilerRenderTotal(); } } else { if ($mode & self::PROFILER_RENDER_TABLE) { $this->_profilerRenderTable(); } if ($mode & self::PROFILER_RENDER_CHART) { $this->_profilerRenderChart(); } if ($mode & self::PROFILER_RENDER_TOTAL) { $this->_profilerRenderTotal(); } if ($mode & self::PROFILER_RENDER_ECHO) { $this->_profilerRenderEcho(); } } } } /** * @param string $data * @return string */ protected function _htmlChars($data) { /* * // experimental if (function_exists('mb_detect_encoding')) { $encoding = mb_detect_encoding($data); if ($encoding == 'ASCII') { $encoding = 'cp1251'; } } */ $encoding = 'UTF-8'; if (version_compare(PHP_VERSION, '5.4', '>=')) { $flags = ENT_QUOTES | ENT_XML1 | ENT_SUBSTITUTE; } else { $flags = ENT_QUOTES; } $data = (string)$data; // $data = iconv('WINDOWS-1251', 'UTF-8//TRANSLIT', $data); return htmlspecialchars($data, $flags, $encoding, true); } /** * Convert profiler memory value to usability view * @param int $memoryBytes * @param bool $addMeasure * @return float */ protected static function _profilerFormatMemory($memoryBytes, $addMeasure = false) { $bytes = round($memoryBytes / 1024 / 1024, 3); if ($addMeasure) { $bytes .= ' MB'; } return $bytes; } /** * Convert profiler time value to usability view * @param $time * @param bool $addMeasure * @return float */ protected static function _profilerFormatTime($time, $addMeasure = false, $round = 0) { $time = round($time * 1000, $round); if ($addMeasure) { $time .= ' ms'; } return $time; } /** * Convert profiler mark to string * @param array $mark * @return string */ protected static function _profilerFormatMark(array $mark) { return sprintf("%0.3f sec (+%.3f); %0.3f MB (%s%0.3f) - %s", (float)$mark['time'], (float)$mark['timeDiff'], ($mark['memory'] / 1024 / 1024), ($mark['memoryDiff'] / 1024 / 1024 >= 0) ? '+' : '', ($mark['memoryDiff'] / 1024 / 1024), $mark['label'] ); } /** * Profiler render - total info */ protected function _profilerRenderTotal() { reset($this->_bufferInfo); $first = current($this->_bufferInfo); $last = end($this->_bufferInfo); $memoryPeak = memory_get_peak_usage(true); $memoryDeltas = $timeDeltas = array(); foreach ($this->_bufferInfo as $oneMark) { $memoryDeltas[] = $oneMark['memoryDiff']; $timeDeltas[] = $oneMark['timeDiff']; } $totalInfo = array(); $totalInfo[] = '- Points: ' . count($this->_bufferInfo); $totalInfo[] = '-------- Time (ms)'; $totalInfo[] = '- Max delta, msec: ' . self::_profilerFormatTime(max($timeDeltas)); $totalInfo[] = '- Min delta, msec: ' . self::_profilerFormatTime(min($timeDeltas)); $totalInfo[] = '- Total delta, msec: ' . self::_profilerFormatTime(($last['time'] - $first['time'])); $totalInfo[] = '- Limit, sec: ' . ini_get('max_execution_time'); $totalInfo[] = '-------- Memory (MB)'; $totalInfo[] = '- Max delta: ' . self::_profilerFormatMemory(max($memoryDeltas)); $totalInfo[] = '- Min delta: ' . self::_profilerFormatMemory(min($memoryDeltas)); $totalInfo[] = '- Usage on peak: ' . $this->_formatSize($memoryPeak) . ' (' . $memoryPeak . ')'; $totalInfo[] = '- Total delta: ' . self::_profilerFormatMemory($last['memory'] - $first['memory']); $totalInfo[] = '- Limit: ' . ini_get('memory_limit'); if (self::isAjax()) { $this->_dumpRenderLog($totalInfo, 'Profiler total'); } else { $totalInfo = PHP_EOL . "\t" . implode(PHP_EOL . "\t", $totalInfo) . PHP_EOL; $this->_dumpRenderLite($totalInfo, '! profiler total info !'); } } /** * Profile render - to log file */ protected function _profilerRenderFile() { $this->log('-------------------------------------------------------', 'Profiler start'); foreach ($this->_bufferInfo as $key => $mark) { $this->log(self::_profilerFormatMark($mark)); } $this->log('-------------------------------------------------------', 'Profiler end'); } /** * Profile render - echo lite */ protected function _profilerRenderEcho() { $output = PHP_EOL; foreach ($this->_bufferInfo as $key => $mark) { $output .= "\t" . self::_profilerFormatMark($mark) . PHP_EOL; } $this->_dumpRenderLite($output, '! profiler !'); } /** * Profiler render - table */ protected function _profilerRenderTable() { $this->_initAssets(); ?> _dumpRenderLite($data, $varname, $params); } elseif (self::$_config['dump']['render'] == 'lite') { $_this->_dumpRenderLite($data, $varname, $params); } elseif (self::$_config['dump']['render'] == 'html') { $_this->_dumpRenderHtml($data, $varname, $params); } elseif (self::$_config['dump']['render'] == 'log') { $_this->_dumpRenderLog($data, $varname, $params); } elseif (self::$_config['dump']['render'] == 'mail') { $_this->_dumpRenderMail($data, $varname, $params); } elseif (self::$_config['dump']['render'] == 'print_r') { $_this->_dumpRenderPrintr($data, $varname, $params); } elseif (self::$_config['dump']['render'] == 'var_dump') { $_this->_dumpRenderVardump($data, $varname, $params); } if (self::$_config['dump']['die']) { die('JBDump_die'); } return $_this; } /** * Dump render - HTML * @param mixed $data * @param string $varname * @param array $params */ protected function _dumpRenderHtml($data, $varname = '...', $params = array()) { $this->_currentDepth = 0; $this->_initAssets(); if (isset($params['trace'])) { $this->_trace = $params['trace']; } else { $this->_trace = debug_backtrace(); } $text = $this->_getSourceFunction($this->_trace); $path = $this->_getSourcePath($this->_trace); ?>
------------------------------' . PHP_EOL;
}
$output[] = $varname . ' = ';
$output[] = rtrim($printrOut, PHP_EOL);
if (!self::isCli()) {
$output[] = PHP_EOL . '------------------------------' . PHP_EOL;
} else {
$output[] = PHP_EOL;
}
if (!self::isAjax()) {
echo '' . implode('', $output) . '' . PHP_EOL;
} else {
echo implode('', $output);
}
}
/**
* Dump render - to logfile
* @param mixed $data
* @param string $varname
* @param array $params
*/
protected function _dumpRenderLog($data, $varname = '...', $params = array())
{
$this->log($data, $varname, $params);
}
/**
* Dump render - send to email
* @param mixed $data
* @param string $varname
* @param array $params
*/
protected function _dumpRenderMail($data, $varname = '...', $params = array())
{
$this->mail(array(
'varname' => $varname,
'data' => $data
));
}
/**
* Dump render - php print_r
* @param mixed $data
* @param string $varname
* @param array $params
*/
protected function _dumpRenderPrintr($data, $varname = '...', $params = array())
{
$this->print_r($data, $varname, $params);
}
/**
* Dump render - php var_dump
* @param mixed $data
* @param string $varname
* @param array $params
*/
protected function _dumpRenderVardump($data, $varname = '...', $params = array())
{
$this->var_dump($data, $varname, $params);
}
/**
* Get all available hash from data
* @param string $data Data from get hash
* @return JBDump
*/
public static function hash($data)
{
$result = array();
foreach (hash_algos() as $algoritm) {
$result[$algoritm] = hash($algoritm, $data, false);
}
return self::i()->dump($result, '! hash !');
}
/**
* Get current usage memory
* @return int
*/
protected static function _getMemory()
{
if (function_exists('memory_get_usage')) {
return memory_get_usage();
} else {
$output = array();
$pid = getmypid();
if (substr(PHP_OS, 0, 3) == 'WIN') {
@exec('tasklist /FI "PID eq ' . $pid . '" /FO LIST', $output);
if (!isset($output[5])) {
$output[5] = null;
}
return (int)substr($output[5], strpos($output[5], ':') + 1);
} else {
@exec("ps -o rss -p $pid", $output);
return $output[1] * 1024;
}
}
}
/**
* Get current microtime
* @return float
*/
public static function _microtime()
{
return microtime(true);
}
/**
* Check is current level is expanded
*/
protected function _isExpandedLevel()
{
return $this->_currentDepth <= self::$_config['dump']['expandLevel'];
}
/**
* Maps type variable to a function
* @param mixed $data Mixed data for dump
* @param string $name Variable name
* @return JBDump
*/
protected function _dump($data, $name = '...')
{
$varType = strtolower(getType($data));
$advType = false;
if ($varType == 'string' && preg_match('#(.*)::(.*)#', $name, $matches)) {
$matches[2] = trim(strToLower($matches[2]));
if ($this->_strlen($matches[2]) > 0) {
$advType = $matches[2];
}
$name = $matches[1];
}
if ($this->_strlen($name) > 80) {
$name = substr($name, 0, 80) . '...';
}
if ($varType == 'null') {
$this->_null($name);
} elseif ($varType == 'boolean') {
$this->_boolean($data, $name, $advType);
} elseif ($varType == 'integer') {
$this->_integer($data, $name, $advType);
} elseif ($varType == 'double') {
$this->_float($data, $name, $advType);
} elseif ($varType == 'string') {
$this->_string($data, $name, $advType);
} elseif ($varType == 'array') {
if ($this->_currentDepth <= self::$_config['dump']['maxDepth']) {
$this->_currentDepth++;
$this->_array($data, $name, $advType);
$this->_currentDepth--;
} else {
$this->_maxDepth($data, $name, $advType);
}
} elseif ($varType == 'object') {
if ($this->_currentDepth <= self::$_config['dump']['maxDepth']) {
$this->_currentDepth++;
if (get_class($data) == 'Closure') {
$this->_closure($data, $name, $advType);
} else {
$this->_object($data, $name, $advType);
}
$this->_currentDepth--;
} else {
$this->_maxDepth($data, $name);
}
} elseif ($varType == 'resource') {
$this->_resource($data, $name, $advType);
} else {
$this->_undefined($data, $name, $advType);
}
return $this;
}
/**
* Render HTML for object and array
* @param array|object $data Variablevalue
* @param bool $isExpanded Flag is current block expanded
* @return void
*/
protected function _vars($data, $isExpanded = false)
{
$_is_object = is_object($data);
?>
' . $data . ''; } elseif ($advType == 'source') { $data = trim($data); if ($data && strpos($data, '') !== 0) { $_ = 'PHP Code'; $_extra = true; $data = "' . highlight_string($data, true) . ''; } else { $_ = '// code not found'; $data = null; } } else { $_ = $data; if (!( strpos($data, "\r") === false && strpos($data, "\n") === false && strpos($data, " ") === false && strpos($data, "\t") === false ) ) { $_extra = true; } else { $_extra = false; } if ($this->_strlen($data)) { if ($this->_strlen($data) > self::$_config['dump']['stringLength']) { if (function_exists('mb_substr')) { $_ = mb_substr($data, 0, self::$_config['dump']['stringLength'] - 3) . '...'; } else { $_ = substr($data, 0, self::$_config['dump']['stringLength'] - 3) . '...'; } $_extra = true; } $_ = $this->_htmlChars($_); $data = ''; } } ?>
JBDump mail from ' . '' . $_SERVER['HTTP_HOST'] . '' . '
'; $message[] = 'Date: ' . date(DATE_RFC822, time()) . '
'; $message[] = 'IP: ' . self::getClientIP() . '
'; $message[] = 'Debug message:' . print_r($text, true) . ''; $message[] = ''; $message = wordwrap(implode(PHP_EOL, $message), 70); // To send HTML mail, the Content-type header must be set $headers = array(); $headers[] = 'MIME-Version: 1.0'; $headers[] = 'Content-type: text/html; charset=utf-8'; $headers[] = 'To: ' . $to; $headers[] = 'From: JBDump debug
tags
public static $use_pre = true;
// This flag tells us if SqlFormatted has been initialized
protected static $init;
// Regular expressions for tokenizing
protected static $regex_boundaries;
protected static $regex_reserved;
protected static $regex_reserved_newline;
protected static $regex_reserved_toplevel;
protected static $regex_function;
// Cache variables
// Only tokens shorter than this size will be cached. Somewhere between 10 and 20 seems to work well for most cases.
public static $max_cachekey_size = 15;
protected static $token_cache = array();
protected static $cache_hits = 0;
protected static $cache_misses = 0;
/**
* Get stats about the token cache
* @return Array An array containing the keys 'hits', 'misses', 'entries', and 'size' in bytes
*/
public static function getCacheStats()
{
return array(
'hits' => self::$cache_hits,
'misses' => self::$cache_misses,
'entries' => count(self::$token_cache),
'size' => strlen(serialize(self::$token_cache))
);
}
/**
* Stuff that only needs to be done once. Builds regular expressions and sorts the reserved words.
*/
protected static function init()
{
if (self::$init) {
return;
}
// Sort reserved word list from longest word to shortest, 3x faster than usort
$reservedMap = array_combine(self::$reserved, array_map('strlen', self::$reserved));
arsort($reservedMap);
self::$reserved = array_keys($reservedMap);
// Set up regular expressions
self::$regex_boundaries = '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$boundaries)) . ')';
self::$regex_reserved = '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$reserved)) . ')';
self::$regex_reserved_toplevel = str_replace(' ', '\\s+', '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$reserved_toplevel)) . ')');
self::$regex_reserved_newline = str_replace(' ', '\\s+', '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$reserved_newline)) . ')');
self::$regex_function = '(' . implode('|', array_map(array(__CLASS__, 'quote_regex'), self::$functions)) . ')';
self::$init = true;
}
/**
* Return the next token and token type in a SQL string.
* Quoted strings, comments, reserved words, whitespace, and punctuation are all their own tokens.
* @param String $string The SQL string
* @param array $previous The result of the previous getNextToken() call
* @return Array An associative array containing the type and value of the token.
*/
protected static function getNextToken($string, $previous = null)
{
// Whitespace
if (preg_match('/^\s+/', $string, $matches)) {
return array(
self::TOKEN_VALUE => $matches[0],
self::TOKEN_TYPE => self::TOKEN_TYPE_WHITESPACE
);
}
// Comment
if ($string[0] === '#' || (isset($string[1]) && ($string[0] === '-' && $string[1] === '-') || ($string[0] === '/' && $string[1] === '*'))) {
// Comment until end of line
if ($string[0] === '-' || $string[0] === '#') {
$last = strpos($string, PHP_EOL);
$type = self::TOKEN_TYPE_COMMENT;
} else { // Comment until closing comment tag
$last = strpos($string, "*/", 2) + 2;
$type = self::TOKEN_TYPE_BLOCK_COMMENT;
}
if ($last === false) {
$last = strlen($string);
}
return array(
self::TOKEN_VALUE => substr($string, 0, $last),
self::TOKEN_TYPE => $type
);
}
// Quoted String
if ($string[0] === '"' || $string[0] === '\'' || $string[0] === '`' || $string[0] === '[') {
$return = array(
self::TOKEN_TYPE => (($string[0] === '`' || $string[0] === '[') ? self::TOKEN_TYPE_BACKTICK_QUOTE : self::TOKEN_TYPE_QUOTE),
self::TOKEN_VALUE => self::getQuotedString($string)
);
return $return;
}
// User-defined Variable
if ($string[0] === '@' && isset($string[1])) {
$ret = array(
self::TOKEN_VALUE => null,
self::TOKEN_TYPE => self::TOKEN_TYPE_VARIABLE
);
// If the variable name is quoted
if ($string[1] === '"' || $string[1] === '\'' || $string[1] === '`') {
$ret[self::TOKEN_VALUE] = '@' . self::getQuotedString(substr($string, 1));
} // Non-quoted variable name
else {
preg_match('/^(@[a-zA-Z0-9\._\$]+)/', $string, $matches);
if ($matches) {
$ret[self::TOKEN_VALUE] = $matches[1];
}
}
if ($ret[self::TOKEN_VALUE] !== null) {
return $ret;
}
}
// Number (decimal, binary, or hex)
if (preg_match('/^([0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)($|\s|"\'`|' . self::$regex_boundaries . ')/', $string, $matches)) {
return array(
self::TOKEN_VALUE => $matches[1],
self::TOKEN_TYPE => self::TOKEN_TYPE_NUMBER
);
}
// Boundary Character (punctuation and symbols)
if (preg_match('/^(' . self::$regex_boundaries . ')/', $string, $matches)) {
return array(
self::TOKEN_VALUE => $matches[1],
self::TOKEN_TYPE => self::TOKEN_TYPE_BOUNDARY
);
}
// A reserved word cannot be preceded by a '.'
// this makes it so in "mytable.from", "from" is not considered a reserved word
if (!$previous || !isset($previous[self::TOKEN_VALUE]) || $previous[self::TOKEN_VALUE] !== '.') {
$upper = strtoupper($string);
// Top Level Reserved Word
if (preg_match('/^(' . self::$regex_reserved_toplevel . ')($|\s|' . self::$regex_boundaries . ')/', $upper, $matches)) {
return array(
self::TOKEN_TYPE => self::TOKEN_TYPE_RESERVED_TOPLEVEL,
self::TOKEN_VALUE => substr($string, 0, strlen($matches[1]))
);
}
// Newline Reserved Word
if (preg_match('/^(' . self::$regex_reserved_newline . ')($|\s|' . self::$regex_boundaries . ')/', $upper, $matches)) {
return array(
self::TOKEN_TYPE => self::TOKEN_TYPE_RESERVED_NEWLINE,
self::TOKEN_VALUE => substr($string, 0, strlen($matches[1]))
);
}
// Other Reserved Word
if (preg_match('/^(' . self::$regex_reserved . ')($|\s|' . self::$regex_boundaries . ')/', $upper, $matches)) {
return array(
self::TOKEN_TYPE => self::TOKEN_TYPE_RESERVED,
self::TOKEN_VALUE => substr($string, 0, strlen($matches[1]))
);
}
}
// A function must be suceeded by '('
// this makes it so "count(" is considered a function, but "count" alone is not
$upper = strtoupper($string);
// function
if (preg_match('/^(' . self::$regex_function . '[(]|\s|[)])/', $upper, $matches)) {
return array(
self::TOKEN_TYPE => self::TOKEN_TYPE_RESERVED,
self::TOKEN_VALUE => substr($string, 0, strlen($matches[1]) - 1)
);
}
// Non reserved word
preg_match('/^(.*?)($|\s|["\'`]|' . self::$regex_boundaries . ')/', $string, $matches);
return array(
self::TOKEN_VALUE => $matches[1],
self::TOKEN_TYPE => self::TOKEN_TYPE_WORD
);
}
protected static function getQuotedString($string)
{
$ret = null;
// This checks for the following patterns:
// 1. backtick quoted string using `` to escape
// 2. square bracket quoted string (SQL Server) using ]] to escape
// 3. double quoted string using "" or \" to escape
// 4. single quoted string using '' or \' to escape
if (preg_match('/^(((`[^`]*($|`))+)|((\[[^\]]*($|\]))(\][^\]]*($|\]))*)|(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)|((\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*(\'|$))+))/s', $string, $matches)) {
$ret = $matches[1];
}
return $ret;
}
/**
* Takes a SQL string and breaks it into tokens.
* Each token is an associative array with type and value.
* @param String $string The SQL string
* @return Array An array of tokens.
*/
protected static function tokenize($string)
{
self::init();
$tokens = array();
// Used for debugging if there is an error while tokenizing the string
$original_length = strlen($string);
// Used to make sure the string keeps shrinking on each iteration
$old_string_len = strlen($string) + 1;
$token = null;
$current_length = strlen($string);
// Keep processing the string until it is empty
while ($current_length) {
// If the string stopped shrinking, there was a problem
if ($old_string_len <= $current_length) {
$tokens[] = array(
self::TOKEN_VALUE => $string,
self::TOKEN_TYPE => self::TOKEN_TYPE_ERROR
);
return $tokens;
}
$old_string_len = $current_length;
// Determine if we can use caching
if ($current_length >= self::$max_cachekey_size) {
$cacheKey = substr($string, 0, self::$max_cachekey_size);
} else {
$cacheKey = false;
}
// See if the token is already cached
if ($cacheKey && isset(self::$token_cache[$cacheKey])) {
// Retrieve from cache
$token = self::$token_cache[$cacheKey];
$token_length = strlen($token[self::TOKEN_VALUE]);
self::$cache_hits++;
} else {
// Get the next token and the token type
$token = self::getNextToken($string, $token);
$token_length = strlen($token[self::TOKEN_VALUE]);
self::$cache_misses++;
// If the token is shorter than the max length, store it in cache
if ($cacheKey && $token_length < self::$max_cachekey_size) {
self::$token_cache[$cacheKey] = $token;
}
}
$tokens[] = $token;
// Advance the string
$string = substr($string, $token_length);
$current_length -= $token_length;
}
return $tokens;
}
/**
* Format the whitespace in a SQL string to make it easier to read.
* @param String $string The SQL string
* @param boolean $highlight If true, syntax highlighting will also be performed
* @return String The SQL string with HTML styles and formatting wrapped in a tag
*/
public static function format($string, $highlight = true)
{
// This variable will be populated with formatted html
$return = '';
// Use an actual tab while formatting and then switch out with self::$tab at the end
$tab = "\t";
$indent_level = 0;
$newline = false;
$inline_parentheses = false;
$increase_special_indent = false;
$increase_block_indent = false;
$indent_types = array();
$added_newline = false;
$inline_count = 0;
$inline_indented = false;
$clause_limit = false;
// Tokenize String
$original_tokens = self::tokenize($string);
// Remove existing whitespace
$tokens = array();
foreach ($original_tokens as $i => $token) {
if ($token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
$token['i'] = $i;
$tokens[] = $token;
}
}
// Format token by token
foreach ($tokens as $i => $token) {
// Get highlighted token if doing syntax highlighting
if ($highlight) {
$highlighted = self::highlightToken($token);
} else { // If returning raw text
$highlighted = $token[self::TOKEN_VALUE];
}
// If we are increasing the special indent level now
if ($increase_special_indent) {
$indent_level++;
$increase_special_indent = false;
array_unshift($indent_types, 'special');
}
// If we are increasing the block indent level now
if ($increase_block_indent) {
$indent_level++;
$increase_block_indent = false;
array_unshift($indent_types, 'block');
}
// If we need a new line before the token
if ($newline) {
$return .= PHP_EOL . str_repeat($tab, $indent_level);
$newline = false;
$added_newline = true;
} else {
$added_newline = false;
}
// Display comments directly where they appear in the source
if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
$indent = str_repeat($tab, $indent_level);
$return .= PHP_EOL . $indent;
$highlighted = str_replace(PHP_EOL, PHP_EOL . $indent, $highlighted);
}
$return .= $highlighted;
$newline = true;
continue;
}
if ($inline_parentheses) {
// End of inline parentheses
if ($token[self::TOKEN_VALUE] === ')') {
$return = rtrim($return, ' ');
if ($inline_indented) {
array_shift($indent_types);
$indent_level--;
$return .= PHP_EOL . str_repeat($tab, $indent_level);
}
$inline_parentheses = false;
$return .= $highlighted . ' ';
continue;
}
if ($token[self::TOKEN_VALUE] === ',') {
if ($inline_count >= 30) {
$inline_count = 0;
$newline = true;
}
}
$inline_count += strlen($token[self::TOKEN_VALUE]);
}
// Opening parentheses increase the block indent level and start a new line
if ($token[self::TOKEN_VALUE] === '(') {
// First check if this should be an inline parentheses block
// Examples are "NOW()", "COUNT(*)", "int(10)", key(`somecolumn`), DECIMAL(7,2)
// Allow up to 3 non-whitespace tokens inside inline parentheses
$length = 0;
for ($j = 1; $j <= 250; $j++) {
// Reached end of string
if (!isset($tokens[$i + $j])) {
break;
}
$next = $tokens[$i + $j];
// Reached closing parentheses, able to inline it
if ($next[self::TOKEN_VALUE] === ')') {
$inline_parentheses = true;
$inline_count = 0;
$inline_indented = false;
break;
}
// Reached an invalid token for inline parentheses
if ($next[self::TOKEN_VALUE] === ';' || $next[self::TOKEN_VALUE] === '(') {
break;
}
// Reached an invalid token type for inline parentheses
if ($next[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL || $next[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE || $next[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $next[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
break;
}
$length += strlen($next[self::TOKEN_VALUE]);
}
if ($inline_parentheses && $length > 30) {
$increase_block_indent = true;
$inline_indented = true;
$newline = true;
}
// Take out the preceding space unless there was whitespace there in the original query
if (isset($original_tokens[$token['i'] - 1]) && $original_tokens[$token['i'] - 1][self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
$return = rtrim($return, ' ');
}
if (!$inline_parentheses) {
$increase_block_indent = true;
// Add a newline after the parentheses
$newline = true;
}
} // Closing parentheses decrease the block indent level
elseif ($token[self::TOKEN_VALUE] === ')') {
// Remove whitespace before the closing parentheses
$return = rtrim($return, ' ');
$indent_level--;
// Reset indent level
while ($j = array_shift($indent_types)) {
if ($j === 'special') {
$indent_level--;
} else {
break;
}
}
if ($indent_level < 0) {
// This is an error
$indent_level = 0;
if ($highlight) {
$return .= PHP_EOL . self::highlightError($token[self::TOKEN_VALUE]);
continue;
}
}
// Add a newline before the closing parentheses (if not already added)
if (!$added_newline) {
$return .= PHP_EOL . str_repeat($tab, $indent_level);
}
} // Top level reserved words start a new line and increase the special indent level
elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
$increase_special_indent = true;
// If the last indent type was 'special', decrease the special indent for this round
reset($indent_types);
if (current($indent_types) === 'special') {
$indent_level--;
array_shift($indent_types);
}
// Add a newline after the top level reserved word
$newline = true;
// Add a newline before the top level reserved word (if not already added)
if (!$added_newline) {
$return .= PHP_EOL . str_repeat($tab, $indent_level);
} // If we already added a newline, redo the indentation since it may be different now
else {
$return = rtrim($return, $tab) . str_repeat($tab, $indent_level);
}
// If the token may have extra whitespace
if (strpos($token[self::TOKEN_VALUE], ' ') !== false || strpos($token[self::TOKEN_VALUE], PHP_EOL) !== false || strpos($token[self::TOKEN_VALUE], "\t") !== false) {
$highlighted = preg_replace('/\s+/', ' ', $highlighted);
}
//if SQL 'LIMIT' clause, start variable to reset newline
if ($token[self::TOKEN_VALUE] === 'LIMIT' && !$inline_parentheses) {
$clause_limit = true;
}
} // Checks if we are out of the limit clause
elseif ($clause_limit && $token[self::TOKEN_VALUE] !== "," && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_NUMBER && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
$clause_limit = false;
} // Commas start a new line (unless within inline parentheses or SQL 'LIMIT' clause)
elseif ($token[self::TOKEN_VALUE] === ',' && !$inline_parentheses) {
//If the previous TOKEN_VALUE is 'LIMIT', resets new line
if ($clause_limit === true) {
$newline = false;
$clause_limit = false;
} // All other cases of commas
else {
$newline = true;
}
} // Newline reserved words start a new line
elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE) {
// Add a newline before the reserved word (if not already added)
if (!$added_newline) {
$return .= PHP_EOL . str_repeat($tab, $indent_level);
}
// If the token may have extra whitespace
if (strpos($token[self::TOKEN_VALUE], ' ') !== false || strpos($token[self::TOKEN_VALUE], PHP_EOL) !== false || strpos($token[self::TOKEN_VALUE], "\t") !== false) {
$highlighted = preg_replace('/\s+/', ' ', $highlighted);
}
} // Multiple boundary characters in a row should not have spaces between them (not including parentheses)
elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BOUNDARY) {
if (isset($tokens[$i - 1]) && $tokens[$i - 1][self::TOKEN_TYPE] === self::TOKEN_TYPE_BOUNDARY) {
if (isset($original_tokens[$token['i'] - 1]) && $original_tokens[$token['i'] - 1][self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE) {
$return = rtrim($return, ' ');
}
}
}
// If the token shouldn't have a space before it
if ($token[self::TOKEN_VALUE] === '.' || $token[self::TOKEN_VALUE] === ',' || $token[self::TOKEN_VALUE] === ';') {
$return = rtrim($return, ' ');
}
$return .= $highlighted . ' ';
// If the token shouldn't have a space after it
if ($token[self::TOKEN_VALUE] === '(' || $token[self::TOKEN_VALUE] === '.') {
$return = rtrim($return, ' ');
}
// If this is the "-" of a negative number, it shouldn't have a space after it
if ($token[self::TOKEN_VALUE] === '-' && isset($tokens[$i + 1]) && $tokens[$i + 1][self::TOKEN_TYPE] === self::TOKEN_TYPE_NUMBER && isset($tokens[$i - 1])) {
$prev = $tokens[$i - 1][self::TOKEN_TYPE];
if ($prev !== self::TOKEN_TYPE_QUOTE && $prev !== self::TOKEN_TYPE_BACKTICK_QUOTE && $prev !== self::TOKEN_TYPE_WORD && $prev !== self::TOKEN_TYPE_NUMBER) {
$return = rtrim($return, ' ');
}
}
}
// If there are unmatched parentheses
if ($highlight && array_search('block', $indent_types) !== false) {
$return .= PHP_EOL . self::highlightError("WARNING: unclosed parentheses or section");
}
// Replace tab characters with the configuration tab character
$return = trim(str_replace("\t", self::$tab, $return));
if ($highlight) {
$return = self::output($return);
}
return $return;
}
/**
* Add syntax highlighting to a SQL string
* @param String $string The SQL string
* @return String The SQL string with HTML styles applied
*/
public static function highlight($string)
{
$tokens = self::tokenize($string);
$return = '';
foreach ($tokens as $token) {
$return .= self::highlightToken($token);
}
return self::output($return);
}
/**
* Split a SQL string into multiple queries.
* Uses ";" as a query delimiter.
* @param String $string The SQL string
* @return Array An array of individual query strings without trailing semicolons
*/
public static function splitQuery($string)
{
$queries = array();
$current_query = '';
$empty = true;
$tokens = self::tokenize($string);
foreach ($tokens as $token) {
// If this is a query separator
if ($token[self::TOKEN_VALUE] === ';') {
if (!$empty) {
$queries[] = $current_query . ';';
}
$current_query = '';
$empty = true;
continue;
}
// If this is a non-empty character
if ($token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_WHITESPACE && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_COMMENT && $token[self::TOKEN_TYPE] !== self::TOKEN_TYPE_BLOCK_COMMENT) {
$empty = false;
}
$current_query .= $token[self::TOKEN_VALUE];
}
if (!$empty) {
$queries[] = trim($current_query);
}
return $queries;
}
/**
* Remove all comments from a SQL string
* @param String $string The SQL string
* @return String The SQL string without comments
*/
public static function removeComments($string)
{
$result = '';
$tokens = self::tokenize($string);
foreach ($tokens as $token) {
// Skip comment tokens
if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
continue;
}
$result .= $token[self::TOKEN_VALUE];
}
$result = self::format($result, false);
return $result;
}
/**
* Compress a query by collapsing white space and removing comments
* @param String $string The SQL string
* @return String The SQL string without comments
*/
public static function compress($string)
{
$result = '';
$tokens = self::tokenize($string);
$whitespace = true;
foreach ($tokens as $token) {
// Skip comment tokens
if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_COMMENT || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_BLOCK_COMMENT) {
continue;
} // Remove extra whitespace in reserved words (e.g "OUTER JOIN" becomes "OUTER JOIN")
elseif ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_NEWLINE || $token[self::TOKEN_TYPE] === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
$token[self::TOKEN_VALUE] = preg_replace('/\s+/', ' ', $token[self::TOKEN_VALUE]);
}
if ($token[self::TOKEN_TYPE] === self::TOKEN_TYPE_WHITESPACE) {
// If the last token was whitespace, don't add another one
if ($whitespace) {
continue;
} else {
$whitespace = true;
// Convert all whitespace to a single space
$token[self::TOKEN_VALUE] = ' ';
}
} else {
$whitespace = false;
}
$result .= $token[self::TOKEN_VALUE];
}
return rtrim($result);
}
/**
* Highlights a token depending on its type.
* @param Array $token An associative array containing type and value.
* @return String HTML code of the highlighted token.
*/
protected static function highlightToken($token)
{
$type = $token[self::TOKEN_TYPE];
if (self::is_cli()) {
$token = $token[self::TOKEN_VALUE];
} else {
if (defined('ENT_IGNORE')) {
$token = htmlentities($token[self::TOKEN_VALUE], ENT_COMPAT | ENT_IGNORE, 'UTF-8');
} else {
$token = htmlentities($token[self::TOKEN_VALUE], ENT_COMPAT, 'UTF-8');
}
}
if ($type === self::TOKEN_TYPE_BOUNDARY) {
return self::highlightBoundary($token);
} elseif ($type === self::TOKEN_TYPE_WORD) {
return self::highlightWord($token);
} elseif ($type === self::TOKEN_TYPE_BACKTICK_QUOTE) {
return self::highlightBacktickQuote($token);
} elseif ($type === self::TOKEN_TYPE_QUOTE) {
return self::highlightQuote($token);
} elseif ($type === self::TOKEN_TYPE_RESERVED) {
return self::highlightReservedWord($token);
} elseif ($type === self::TOKEN_TYPE_RESERVED_TOPLEVEL) {
return self::highlightReservedWord($token);
} elseif ($type === self::TOKEN_TYPE_RESERVED_NEWLINE) {
return self::highlightReservedWord($token);
} elseif ($type === self::TOKEN_TYPE_NUMBER) {
return self::highlightNumber($token);
} elseif ($type === self::TOKEN_TYPE_VARIABLE) {
return self::highlightVariable($token);
} elseif ($type === self::TOKEN_TYPE_COMMENT || $type === self::TOKEN_TYPE_BLOCK_COMMENT) {
return self::highlightComment($token);
}
return $token;
}
/**
* Highlights a quoted string
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightQuote($value)
{
if (self::is_cli()) {
return self::$cli_quote . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a backtick quoted string
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightBacktickQuote($value)
{
if (self::is_cli()) {
return self::$cli_backtick_quote . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a reserved word
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightReservedWord($value)
{
if (self::is_cli()) {
return self::$cli_reserved . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a boundary token
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightBoundary($value)
{
if ($value === '(' || $value === ')') {
return $value;
}
if (self::is_cli()) {
return self::$cli_boundary . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a number
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightNumber($value)
{
if (self::is_cli()) {
return self::$cli_number . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights an error
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightError($value)
{
if (self::is_cli()) {
return self::$cli_error . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a comment
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightComment($value)
{
if (self::is_cli()) {
return self::$cli_comment . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a word token
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightWord($value)
{
if (self::is_cli()) {
return self::$cli_word . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Highlights a variable token
* @param String $value The token's value
* @return String HTML code of the highlighted token.
*/
protected static function highlightVariable($value)
{
if (self::is_cli()) {
return self::$cli_variable . $value . "\x1b[0m";
} else {
return '' . $value . '';
}
}
/**
* Helper function for building regular expressions for reserved words and boundary characters
* @param String $a The string to be quoted
* @return String The quoted string
*/
private static function quote_regex($a)
{
return preg_quote($a, '/');
}
/**
* Helper function for building string output
* @param String $string The string to be quoted
* @return String The quoted string
*/
private static function output($string)
{
if (self::is_cli()) {
return $string . PHP_EOL;
} else {
$string = trim($string);
if (!self::$use_pre) {
return $string;
}
return $string;
}
}
private static function is_cli()
{
if (isset(self::$cli)) {
return self::$cli;
} else {
return php_sapi_name() === 'cli';
}
}
}
/**
* Class array2php
*/
class JBDump_array2php
{
const LE = PHP_EOL;
const TAB = " ";
/**
* @param $array
* @param null $varName
* @return string
*/
public static function toString($array, $varName = null, $shift = 0)
{
$self = new self();
$rendered = $self->_render($array, 0);
if ($shift > 0) {
$rendered = explode(self::LE, $rendered);
foreach ($rendered as $key => $line) {
$rendered[$key] = $self->_getIndent($shift) . $line;
}
$rendered[0] = ltrim($rendered[0]);
$rendered = implode(self::LE, $rendered);
}
if ($varName) {
return PHP_EOL . $self->_getIndent($shift) . "\$" . $varName . ' = ' . $rendered . ";" . PHP_EOL . " " . self::TAB;
}
return $rendered;
}
/**
* @param $array
* @param int $depth
* @return string
*/
protected function _render($array, $depth = 0)
{
$isObject = false;
if ($depth >= 10) {
return 'null /* MAX DEEP REACHED! */';
}
if (is_object($array)) {
$isObject = get_class($array);
$array = (array)$array;
}
if (!is_array($array)) {
return 'null /* undefined var */';
}
if (empty($array)) {
return $isObject ? '(object)array( /* Object: "' . $isObject . '" */)' : 'array()';
}
$string = 'array( ' . self::LE;
if ($isObject) {
$string = '(object)array( ' . self::LE . $this->_getIndent($depth + 1) . '/* Object: "' . $isObject . '" */ ' . self::LE;
}
$depth++;
foreach ($array as $key => $val) {
$string .= $this->_getIndent($depth) . $this->_quoteWrap($key) . ' => ';
if (is_array($val) || is_object($val)) {
$string .= $this->_render($val, $depth) . ',' . self::LE;
} else {
$string .= $this->_quoteWrap($val) . ',' . self::LE;
}
}
$depth--;
$string .= $this->_getIndent($depth) . ')';
return $string;
}
/**
* @param $depth
* @return string
*/
protected function _getIndent($depth)
{
return str_repeat(self::TAB, $depth);
}
/**
* @param $var
* @return string
*/
protected function _quoteWrap($var)
{
$type = strtolower(gettype($var));
switch ($type) {
case 'string':
return "'" . str_replace("'", "\\'", $var) . "'";
case 'null':
return "null";
case 'boolean':
return $var ? 'TRUE' : 'FALSE';
case 'object':
return '"{ Object: ' . get_class($var) . ' }"';
//TODO: handle other variable types.. ( objects? )
case 'integer':
case 'double':
default :
return $var;
}
}
}
/**
* Alias for JBDump::i()->dump($var) with additions params
* @param mixed $var Variable
* @param string $name Variable name
* @param bool $isDie Die after dump
* @return JBDump
*/
function jbdump($var = 'JBDump::variable is no set', $isDie = true, $name = '...')
{
$_this = JBDump::i();
if ($var != 'JBDump::variable is no set') {
if ($_this->isDebug()) {
$_this->dump($var, $name);
$isDie && die('JBDump_auto_die');
}
}
return $_this;
}