* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\VarDumper\Dumper; use Symfony\Component\VarDumper\Cloner\Cursor; use Symfony\Component\VarDumper\Cloner\Data; /** * HtmlDumper dumps variables as HTML. * * @author Nicolas Grekas */ class HtmlDumper extends CliDumper { public static $defaultOutput = 'php://output'; protected $dumpHeader; protected $dumpPrefix = '
';
    protected $dumpSuffix = '
'; protected $dumpId = 'sf-dump'; protected $colors = true; protected $headerIsDumped = false; protected $lastDepth = -1; protected $styles = array( 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:100000', 'num' => 'font-weight:bold; color:#1299DA', 'const' => 'font-weight:bold', 'str' => 'font-weight:bold; color:#56DB3A', 'note' => 'color:#1299DA', 'ref' => 'color:#A0A0A0', 'public' => 'color:#FFFFFF', 'protected' => 'color:#FFFFFF', 'private' => 'color:#FFFFFF', 'meta' => 'color:#B729D9', 'key' => 'color:#56DB3A', 'index' => 'color:#1299DA', ); /** * {@inheritdoc} */ public function __construct($output = null, $charset = null) { AbstractDumper::__construct($output, $charset); $this->dumpId = 'sf-dump-'.mt_rand(); } /** * {@inheritdoc} */ public function setOutput($output) { if ($output !== $prev = parent::setOutput($output)) { $this->headerIsDumped = false; } return $prev; } /** * {@inheritdoc} */ public function setStyles(array $styles) { $this->headerIsDumped = false; $this->styles = $styles + $this->styles; } /** * Sets an HTML header that will be dumped once in the output stream. * * @param string $header An HTML string. */ public function setDumpHeader($header) { $this->dumpHeader = $header; } /** * Sets an HTML prefix and suffix that will encapse every single dump. * * @param string $prefix The prepended HTML string. * @param string $suffix The appended HTML string. */ public function setDumpBoundaries($prefix, $suffix) { $this->dumpPrefix = $prefix; $this->dumpSuffix = $suffix; } /** * {@inheritdoc} */ public function dump(Data $data, $output = null) { parent::dump($data, $output); $this->dumpId = 'sf-dump-'.mt_rand(); } /** * Dumps the HTML header. */ protected function getDumpHeader() { $this->headerIsDumped = true; if (null !== $this->dumpHeader) { return $this->dumpHeader; } $line = <<<'EOHTML' '.$this->dumpHeader; } /** * {@inheritdoc} */ public function enterHash(Cursor $cursor, $type, $class, $hasChild) { parent::enterHash($cursor, $type, $class, false); if ($hasChild) { if ($cursor->refIndex) { $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2; $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex; $this->line .= sprintf('', $this->dumpId, $r); } else { $this->line .= ''; } $this->dumpLine($cursor->depth); } } /** * {@inheritdoc} */ public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut) { $this->dumpEllipsis($cursor, $hasChild, $cut); if ($hasChild) { $this->line .= ''; } parent::leaveHash($cursor, $type, $class, $hasChild, 0); } /** * {@inheritdoc} */ protected function style($style, $value, $attr = array()) { if ('' === $value) { return ''; } $v = htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); if ('ref' === $style) { if (empty($attr['count'])) { return sprintf('%s', $v); } $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); return sprintf('%s', $this->dumpId, $r, 1 + $attr['count'], $v); } if ('const' === $style && array_key_exists('value', $attr)) { $style .= sprintf(' title="%s"', htmlspecialchars(json_encode($attr['value']), ENT_QUOTES, 'UTF-8')); } elseif ('public' === $style) { $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'); } elseif ('str' === $style && 1 < $attr['length']) { $style .= sprintf(' title="%s%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); } elseif ('note' === $style && false !== $c = strrpos($v, '\\')) { return sprintf('%s', $v, $style, substr($v, $c + 1)); } elseif ('protected' === $style) { $style .= ' title="Protected property"'; } elseif ('private' === $style) { $style .= sprintf(' title="Private property defined in class: `%s`"', $attr['class']); } $map = static::$controlCharsMap; $style = ""; $v = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $style) { $s = ''; $c = $c[$i = 0]; do { $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', ord($c[$i])); } while (isset($c[++$i])); return $s.$style; }, $v, -1, $cchrCount); if ($cchrCount && '<' === $v[0]) { $v = substr($v, 7); } else { $v = $style.$v; } if ($cchrCount && '>' === substr($v, -1)) { $v = substr($v, 0, -strlen($style)); } else { $v .= ''; } return $v; } /** * {@inheritdoc} */ protected function dumpLine($depth, $endOfValue = false) { if (-1 === $this->lastDepth) { $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; } if (!$this->headerIsDumped) { $this->line = $this->getDumpHeader().$this->line; } if (-1 === $depth) { $this->line .= sprintf($this->dumpSuffix, $this->dumpId); } $this->lastDepth = $depth; // Replaces non-ASCII UTF-8 chars by numeric HTML entities $this->line = preg_replace_callback( '/[\x80-\xFF]+/', function ($m) { $m = unpack('C*', $m[0]); $i = 1; $entities = ''; while (isset($m[$i])) { if (0xF0 <= $m[$i]) { $c = (($m[$i++] - 0xF0) << 18) + (($m[$i++] - 0x80) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; } elseif (0xE0 <= $m[$i]) { $c = (($m[$i++] - 0xE0) << 12) + (($m[$i++] - 0x80) << 6) + $m[$i++] - 0x80; } else { $c = (($m[$i++] - 0xC0) << 6) + $m[$i++] - 0x80; } $entities .= '&#'.$c.';'; } return $entities; }, $this->line ); if (-1 === $depth) { AbstractDumper::dumpLine(0); } AbstractDumper::dumpLine($depth); } }