* * 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:99999; word-break: normal',
'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;
$this->line = mb_convert_encoding($this->line, 'HTML-ENTITIES', 'UTF-8');
if (-1 === $depth) {
AbstractDumper::dumpLine(0);
}
AbstractDumper::dumpLine($depth);
}
}