From 6aa68f42c548af3facdd5546233160e34c05c6eb Mon Sep 17 00:00:00 2001 From: digitalnature Date: Sun, 15 Nov 2015 14:47:49 +0200 Subject: [PATCH] basic cli support, validHtml option --- README.md | 4 +- ref.css | 60 +++++++++++------ ref.js | 49 +++++++------- ref.php | 196 +++++++++++++++++++++++++++++++++++++++++------------- 4 files changed, 214 insertions(+), 95 deletions(-) diff --git a/README.md b/README.md index aa31a03..48393f1 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Currently available options and their default values: | `'scriptPath'` | `'{:dir}/ref.js'` | Local path to a custom javascript (HTML only); `FALSE` means no javascript (tooltips / toggle / kbd shortcuts require JS) | `'showUrls'` | `FALSE` | Gets information about URLs. Setting to false can improve performance (requires showStringMatches to be TRUE) | `'timeout'` | `10` | Stop execution after this amount of seconds, forcing an incomplete listing. Applies to all calls - +| `'validHtml'` | `FALSE` | For HTML mode only. Whether to produce W3C-valid HTML (larger code output) or unintelligible, potentially browser-incompatible but much smaller code output ## Similar projects @@ -94,7 +94,7 @@ Currently available options and their default values: - [dump_r](https://github.com/leeoniya/dump_r.php) - [Krumo](http://sourceforge.net/projects/krumo/) - [dBug](http://dbug.ospinto.com/) -- [symfony-vardumper] (http://www.sitepoint.com/var_dump-introducing-symfony-vardumper/) +- [symfony-vardumper](http://www.sitepoint.com/var_dump-introducing-symfony-vardumper/) ## TODOs diff --git a/ref.css b/ref.css index a83a45a..155a820 100644 --- a/ref.css +++ b/ref.css @@ -6,6 +6,7 @@ /* reset default styles for these elements */ .ref i, .ref span, +.ref r, .ref a{ font-style: inherit; font-weight: inherit; @@ -19,17 +20,20 @@ } /* meta content (used to generate tooltips) */ -.ref > div{ +.ref > div, +.ref > t{ display: none; } /* show help cursor when mouse is over an entity with a tooltip */ -.ref [data-tip]{ +.ref [data-tip], +.ref [h]{ cursor: help; } /* pointer if inside a link */ -.ref a > [data-tip]{ +.ref a > [data-tip], +.ref a > [h]{ cursor: pointer; } @@ -70,7 +74,8 @@ margin: 20px 0 0 25px; } -#rTip [data-cell]{ +#rTip [data-cell], +#rTip [c]{ padding: 2px 7px; } @@ -85,7 +90,8 @@ color: #777; } -#rTip [data-cell][data-varType]{ +#rTip [data-cell][data-varType], +#rTip [c][data-varType]{ padding: 10px; background: #333; box-shadow: inset -1px 0 0 #444; @@ -94,7 +100,8 @@ border-bottom-left-radius: 4px; } -#rTip [data-cell][data-sub]{ +#rTip [data-cell][data-sub], +#rTip [c][data-sub]{ padding: 8px 10px 10px 10px; background: #333; box-shadow: inset 0 1px 0 #444; @@ -103,12 +110,14 @@ border-bottom-left-radius: 4px; } -#rTip [data-table] [data-cell]:first-child{ +#rTip [data-table] [data-cell]:first-child, +#rTip [t] [c]:first-child{ font: bold 11px Helvetica, Arial; color: #888; } -#rTip [data-table] [data-cell]:nth-child(2){ +#rTip [data-table] [data-cell]:nth-child(2), +#rTip [t] [c]:nth-child(2){ color: #edd078; } @@ -116,7 +125,7 @@ /* base entity - can be nested */ -.ref span{ +.ref span, .ref r{ white-space: pre; display: inline; } @@ -178,16 +187,19 @@ transform: rotate(90deg); } -.ref [data-group]{ +.ref [data-group], +.ref [g]{ display: none; } -.ref [data-toggle][data-exp] ~ [data-group]{ +.ref [data-toggle][data-exp] ~ [data-group], +.ref [data-toggle][data-exp] ~ [g]{ display: block; } /* group sections */ -.ref [data-table]{ +.ref [data-table], +.ref [t]{ display: table; } @@ -202,16 +214,20 @@ /* emulate a table for displaying array & object members */ /* section row */ -.ref [data-row]{ +.ref [data-row], +.ref [r]{ display: table-row; } /* zebra-like rows */ .ref [data-output] [data-row]:nth-child(odd){background: #f4f4f4;} .ref [data-output] [data-row]:nth-child(even){background: #f9f9f9;} +.ref [data-output] [r]:nth-child(odd){background: #f4f4f4;} +.ref [data-output] [r]:nth-child(even){background: #f9f9f9;} /* section cells */ -.ref [data-cell]{ +.ref [data-cell], +.ref [c]{ display: table-cell; width: auto; vertical-align: top; @@ -220,7 +236,9 @@ /* last cell of a row (forces table to adjust width like we want to) */ .ref [data-output] [data-table], -.ref [data-output] [data-cell]:last-child{ +.ref [data-output] [t], +.ref [data-output] [data-cell]:last-child, +.ref [data-output] [c]:last-child{ width: 100%; } @@ -232,7 +250,8 @@ .ref [data-null], .ref [data-unknown], .ref [data-resource], -.ref [data-match]{ +.ref [data-match], +.ref [m]{ font: bold 11px Helvetica, Arial; color: #fff; padding: 1px 3px; @@ -246,7 +265,8 @@ } /* string matches */ -.ref [data-match]{ +.ref [data-match], +.ref [m]{ background-color: #d78035; } @@ -356,7 +376,8 @@ } /* group info prefix */ -.ref [data-gLabel]{ +.ref [data-gLabel], +.ref [gl]{ font: bold 11px Helvetica, Arial; padding: 0 3px; color: #333; @@ -373,7 +394,8 @@ color: #444; } -.ref [data-mod] span{ +.ref [data-mod] span, +.ref [data-mod] r{ display: inline-block; margin: 0 2px; width: 14px; diff --git a/ref.js b/ref.js index b2893c7..d1ac898 100644 --- a/ref.js +++ b/ref.js @@ -45,32 +45,29 @@ window.addEventListener('load', function(){ tip.id = 'rTip'; document.body.appendChild(tip); -}); -window.addEventListener('keydown', function(e){ - - var tt = e.target.tagName.toLowerCase(); - if((e.keyCode != 88) || (tt == 'input') || (tt == 'textarea') || (tt == 'select')) - return; - - var kbds = document.querySelectorAll('.ref [data-toggle]'), - partlyExp = document.querySelectorAll('.ref [data-toggle][data-exp]').length !== kbds.length, - _ref = document.querySelectorAll('.ref'); - - - e.preventDefault(); - - if( e.ctrlKey && e.keyCode == 88 ){ - for(var i = 0, m = _ref.length; i < m; i++){ - if( _ref[i].style.display == 'none' ){ - _ref[i].style.display = 'block'; - }else{ - _ref[i].style.display = 'none'; - } - } - }else{ - for(var i = 0, m = kbds.length; i < m; i++) + window.addEventListener('keydown', function(e){ + if((e.keyCode != 88) || (['input', 'textarea', 'select'].indexOf(e.target.tagName.toLowerCase()) > -1)) + return; + + e.preventDefault(); + + if(e.ctrlKey && e.keyCode == 88){ + var d = refs[0].style.display !== 'none' ? 'none' : 'block'; + for(var i = 0, n = refs.length; i < n; i++) + refs[i].style.display = d; + + return; + } + + var kbds = document.querySelectorAll('.ref [data-toggle]'), + m = kbds.length, + partlyExp = document.querySelectorAll('.ref [data-toggle][data-exp]').length !== m; + + for(var i = 0; i < m; i++) partlyExp ? (kbds[i].dataset.exp = 1) : (delete kbds[i].dataset.exp); - } - + + }); + }); + diff --git a/ref.php b/ref.php index 74f2c3c..19bece7 100644 --- a/ref.php +++ b/ref.php @@ -15,8 +15,6 @@ function r(){ // this variable gets passed as reference to getInputExpressions(), which will store the operators in it $options = array(); - $ref = new ref('html'); - // names of the arguments that were passed to this function $expressions = ref::getInputExpressions($options); $capture = in_array('@', $options, true); @@ -26,10 +24,15 @@ function r(){ if(func_num_args() !== count($expressions)) $expressions = null; + // use HTML formatter only if we're not in CLI mode, or if return was requested + $format = (php_sapi_name() !== 'cli') || $capture ? 'html' : 'cliText'; + // IE goes funky if there's no doctype - if(!$capture && !headers_sent() && (!ob_get_level() || ini_get('output_buffering'))) + if(!$capture && ($format === 'html') && !headers_sent() && (!ob_get_level() || ini_get('output_buffering'))) print 'REF'; + $ref = new ref($format); + if($capture) ob_start(); @@ -41,7 +44,7 @@ function r(){ return ob_get_clean(); // stop the script if this function was called with the bitwise not operator - if(in_array('~', $options, true)){ + if(in_array('~', $options, true) && ($format === 'html')){ print ''; exit(0); } @@ -60,8 +63,8 @@ function rt(){ $options = array(); $output = ''; $expressions = ref::getInputExpressions($options); - $ref = new ref('text'); $capture = in_array('@', $options, true); + $ref = new ref((php_sapi_name() !== 'cli') || $capture ? 'text' : 'cliText'); if(func_num_args() !== count($expressions)) $expressions = null; @@ -160,6 +163,10 @@ class ref{ // stop evaluation after this amount of time (seconds) 'timeout' => 10, + + // whether to produce W3c-valid HTML, + // or unintelligible, but optimized markup that takes less space + 'validHtml' => false, ), /** @@ -175,7 +182,14 @@ class ref{ * * @var bool */ - $timeout = -1; + $timeout = -1, + + $debug = array( + 'cacheHits' => 0, + 'objects' => 0, + 'arrays' => 0, + 'scalars' => 0, + ); protected @@ -946,9 +960,12 @@ protected function fromReflector(\Reflector $reflector, $single = '', \Reflector // @todo: test this $hash = var_export(func_get_args(), true); + //$hash = $reflector->getName() . ';' . $single . ';' . ($context ? $context->getName() : ''); - if($this->fmt->didCache($hash)) + if($this->fmt->didCache($hash)){ + static::$debug['cacheHits']++; return; + } $items = array($reflector); @@ -1125,6 +1142,11 @@ public static function getTimeoutPoint(){ } + public static function getDebugInfo(){ + return static::$debug; + } + + protected function hasInstanceTimedOut(){ @@ -1601,8 +1623,10 @@ protected function evaluate(&$subject, $specialStr = false){ } // check cache at this point - if(!$recursion && $this->fmt->didCache($hash)) + if(!$recursion && $this->fmt->didCache($hash)){ + static::$debug['cacheHits']++; return; + } $reflector = new \ReflectionObject($subject); $this->fmt->startContain('class'); @@ -1675,6 +1699,7 @@ protected function evaluate(&$subject, $specialStr = false){ $this->fmt->sep('=>'); $this->fmt->colDiv(); $this->evaluate($value); + //$this->evaluate($value instanceof \Traversable ? ((count($value) > 0) ? $value : (string)$value) : $value); $this->fmt->endRow(); } } @@ -2244,14 +2269,14 @@ class RHtmlFormatter extends RFormatter{ * * @var string */ - $out = '', + $out = '', /** * Tracks current nesting level * * @var int */ - $level = 0, + $level = 0, /** * Stores tooltip content for all entries @@ -2262,7 +2287,7 @@ class RHtmlFormatter extends RFormatter{ * * @var array */ - $tips = array(), + $tips = array(), /** * Used to cache output to speed up processing. @@ -2272,7 +2297,14 @@ class RHtmlFormatter extends RFormatter{ * * @var array */ - $cache = array(); + $cache = array(), + + /** + * Map of used HTML tag and attributes + * + * @var string + */ + $def = array(); @@ -2283,7 +2315,7 @@ class RHtmlFormatter extends RFormatter{ * * @var int */ - $counter = 0, + $counter = 0, /** * Tracks style/jscript inclusion state @@ -2293,6 +2325,42 @@ class RHtmlFormatter extends RFormatter{ $didAssets = false; + public function __construct(){ + + if(ref::config('validHtml')){ + + $this->def = array( + 'base' => 'span', + 'tip' => 'div', + 'cell' => 'data-cell', + 'table' => 'data-table', + 'row' => 'data-row', + 'group' => 'data-group', + 'gLabel' => 'data-gLabel', + 'match' => 'data-match', + 'tipRef' => 'data-tip', + ); + + + }else{ + + $this->def = array( + 'base' => 'r', + 'tip' => 't', + 'cell' => 'c', + 'table' => 't', + 'row' => 'r', + 'group' => 'g', + 'gLabel' => 'gl', + 'match' => 'm', + 'tipRef' => 'h', + ); + + } + + } + + public function flush(){ print $this->out; @@ -2306,7 +2374,7 @@ public function didCache($id){ if(!isset($this->cache[$id])){ $this->cache[$id] = array(); - $this->cache[$id][] = strlen($this->out); + $this->cache[$id][] = strlen($this->out); return false; } @@ -2315,7 +2383,7 @@ public function didCache($id){ return false; } - $this->out .= substr($this->out, $this->cache[$id][0], $this->cache[$id][1]); + $this->out .= substr($this->out, $this->cache[$id][0], $this->cache[$id][1]); return true; } @@ -2355,20 +2423,20 @@ public function text($type, $text = null, $meta = null, $uri = null){ if($tipIdx === false) $tipIdx = array_push($this->tips, $meta) - 1; - $tip = ' data-tip="' . $tipIdx . '"'; + $tip = " {$this->def['tipRef']}=\"{$tipIdx}\""; + //$tip = sprintf('%s="%d"', $this->def['tipRef'], $tipIdx); } // wrap text in a link? if($uri !== null) $text = '' . $text . ''; - //$this->out .= ($type !== 'name') ? "{$text}" : "{$text}"; - $typeStr = ''; foreach($type as $part) $typeStr .= " data-{$part}"; - $this->out .= "{$text}"; + $this->out .= "<{$this->def['base']}{$typeStr}{$tip}>{$text}def['base']}>"; + //$this->out .= sprintf('<%1$s%2$s %3$s>%4$s', $this->def['base'], $typeStr, $tip, $text); } public function startContain($type, $label = false){ @@ -2383,20 +2451,20 @@ public function startContain($type, $label = false){ foreach($type as $part) $typeStr .= " data-{$part}"; - $this->out .= ""; + $this->out .= "<{$this->def['base']}{$typeStr}>"; if($label) - $this->out .= "{$type[0]}"; + $this->out .= "<{$this->def['base']} {$this->def['match']}>{$type[0]}def['base']}>"; } public function endContain(){ - $this->out .= ''; + $this->out .= "def['base']}>"; } public function emptyGroup($prefix = ''){ if($prefix !== '') - $prefix = '' . static::escape($prefix) . ''; + $prefix = "<{$this->def['base']} {$this->def['gLabel']}>" . static::escape($prefix) . "def['base']}>"; $this->out .= "({$prefix})"; } @@ -2417,32 +2485,32 @@ public function startGroup($prefix = ''){ $exp = ($expLvl < 0) || (($expLvl > 0) && ($this->level <= $expLvl)) ? ' data-exp' : ''; if($prefix !== '') - $prefix = '' . static::escape($prefix) . ''; + $prefix = "<{$this->def['base']} {$this->def['gLabel']}>" . static::escape($prefix) . "def['base']}>"; - $this->out .= "({$prefix}"; + $this->out .= "({$prefix}<{$this->def['base']} data-toggle{$exp}>def['base']}><{$this->def['base']} {$this->def['group']}><{$this->def['base']} {$this->def['table']}>"; return true; } public function endGroup(){ - $this->out .= ')'; + $this->out .= "def['base']}>def['base']}>)"; $this->level--; } public function sectionTitle($title){ - $this->out .= "{$title}"; + $this->out .= "def['base']}><{$this->def['base']} data-tHead>{$title}def['base']}><{$this->def['base']} {$this->def['table']}>"; } public function startRow(){ - $this->out .= ''; + $this->out .= "<{$this->def['base']} {$this->def['row']}><{$this->def['base']} {$this->def['cell']}>"; } public function endRow(){ - $this->out .= ''; + $this->out .= "def['base']}>def['base']}>"; } public function colDiv($padLen = null){ - $this->out .= ''; + $this->out .= "def['base']}><{$this->def['base']} {$this->def['cell']}>"; } public function bubbles(array $items){ @@ -2450,23 +2518,23 @@ public function bubbles(array $items){ if(!$items) return; - $this->out .= ''; + $this->out .= "<{$this->def['base']} data-mod>"; foreach($items as $info) $this->out .= $this->text('mod-' . strtolower($info[1]), $info[0], $info[1]); - $this->out .= ''; + $this->out .= "def['base']}>"; } public function startExp(){ - $this->out .= ''; + $this->out .= "<{$this->def['base']} data-input>"; } public function endExp(){ if(ref::config('showBacktrace') && ($trace = ref::getBacktrace())) - $this->out .= '' . $trace['file'] . ':' . $trace['line'] . ''; + $this->out .= "<{$this->def['base']} data-backtrace>{$trace['file']}:{$trace['line']}def['base']}>"; - $this->out .= ''; + $this->out .= "def['base']}><{$this->def['base']} data-output>"; } public function startRoot(){ @@ -2474,7 +2542,7 @@ public function startRoot(){ } public function endRoot(){ - $this->out .= ''; + $this->out .= "def['base']}>"; // process tooltips $tipHtml = ''; @@ -2496,10 +2564,10 @@ public function endRoot(){ $cols = array(); if($meta['left']) - $cols[] = "{$meta['left']}"; + $cols[] = "<{$this->def['base']} {$this->def['cell']} data-varType>{$meta['left']}def['base']}>"; - $title = $meta['title'] ? "{$meta['title']}" : ''; - $desc = $meta['description'] ? "{$meta['description']}" : ''; + $title = $meta['title'] ? "<{$this->def['base']} data-title>{$meta['title']}def['base']}>" : ''; + $desc = $meta['description'] ? "<{$this->def['base']} data-desc>{$meta['description']}def['base']}>" : ''; $tags = ''; foreach($meta['tags'] as $tag => $values){ @@ -2509,33 +2577,33 @@ public function endRoot(){ unset($value[1]); } - $value = is_array($value) ? implode('', $value) : $value; - $tags .= "@{$tag}{$value}"; + $value = is_array($value) ? implode("def['base']}><{$this->def['base']} {$this->def['cell']}>", $value) : $value; + $tags .= "<{$this->def['base']} {$this->def['row']}><{$this->def['base']} {$this->def['cell']}>@{$tag}def['base']}><{$this->def['base']} {$this->def['cell']}>{$value}def['base']}>def['base']}>"; } } if($tags) - $tags = "{$tags}"; + $tags = "<{$this->def['base']} {$this->def['table']}>{$tags}def['base']}>"; if($title || $desc || $tags) - $cols[] = "{$title}{$desc}{$tags}"; + $cols[] = "<{$this->def['base']} {$this->def['cell']}>{$title}{$desc}{$tags}def['base']}>"; if($cols) - $tip = '' . implode('', $cols) . ''; + $tip = "<{$this->def['base']} {$this->def['row']}>" . implode('', $cols) . "def['base']}>"; $sub = ''; foreach($meta['sub'] as $line) - $sub .= '' . implode('', $line) . ''; + $sub .= "<{$this->def['base']} {$this->def['row']}><{$this->def['base']} {$this->def['cell']}>" . implode("def['base']}><{$this->def['base']} {$this->def['cell']}>", $line) . "def['base']}>def['base']}>"; if($sub) - $tip .= "{$sub}"; + $tip .= "<{$this->def['base']} {$this->def['row']}><{$this->def['base']} {$this->def['cell']} data-sub><{$this->def['base']} {$this->def['table']}>{$sub}def['base']}>def['base']}>def['base']}>"; if($tip) - $this->out .= "
{$tip}
"; + $this->out .= "<{$this->def['tip']}>{$tip}def['tip']}>"; } if(($timeout = ref::getTimeoutPoint()) > 0) - $this->out .= sprintf('Listing incomplete. Timed-out after %4.2fs', $timeout); + $this->out .= sprintf("<{$this->def['base']} data-error>Listing incomplete. Timed-out after %4.2fsdef['base']}>", $timeout); $this->out .= ''; } @@ -2769,3 +2837,35 @@ public function endRoot(){ } } + + + +/** + * Text formatter with color support for CLI -- unfinished + * + */ +class RCliTextFormatter extends RTextFormatter{ + + public function sectionTitle($title){ + $pad = str_repeat(' ', $this->indent + 2); + $this->out .= sprintf("\n\n%s\x1b[4;97m%s\x1b[0m", $pad, $title); + } + + public function startExp(){ + $this->out .= "\x1b[1;44;96m "; + } + + public function endExp(){ + if(ref::config('showBacktrace') && ($trace = ref::getBacktrace())) + $this->out .= "\x1b[0m\x1b[44;36m " . $trace['file'] . ':' . $trace['line']; + + $this->out .= " \x1b[0m\n"; + } + + public function endRoot(){ + $this->out .= "\n"; + if(($timeout = ref::getTimeoutPoint()) > 0) + $this->out .= sprintf("\n\x1b[3;91m-- Listing incomplete. Timed-out after %4.2fs --\x1b[0m\n", $timeout); + } + +}