1
0
mirror of https://github.com/freescout-helpdesk/freescout.git synced 2024-11-23 19:02:46 +01:00

PHP 8.2 compatibility

This commit is contained in:
FreeScout 2022-12-09 23:58:10 -08:00
parent e107c41ce9
commit 23c8107463
9 changed files with 1504 additions and 39 deletions

View File

@ -68,7 +68,7 @@ Mobile apps support the same functionality and modules as the web version of you
FreeScout is a pure PHP/MySQL application, so it can be easily deployed even on a shared hosting.
* Nginx / Apache / IIS
* PHP 7.1 - 8.1
* PHP 7.1 - 8.2
* MySQL 5.0+ / MariaDB 5.0+ / PostgreSQL
## Installation
@ -103,7 +103,7 @@ Images & one-click installs:
Don't miss news, updates and new modules!
[Email Newsletter](https://freescout.net/subscribe/) | [Facebook](https://www.facebook.com/profile.php?id=100088012965853) | [Twitter](https://freescout.net/twitter/) | [YouTube](https://freescout.net/youtube/) | [RSS](https://freescout.net/feed/)
[Email Newsletter](https://freescout.net/subscribe/) | [Facebook](https://freescout.net/facebook/) | [Twitter](https://freescout.net/twitter/) | [YouTube](https://freescout.net/youtube/) | [RSS](https://freescout.net/feed/)
## Contributing

View File

@ -1195,45 +1195,46 @@ class Helper
}
/**
* It looks like this is not used anywhere.
* Json encode to avoid "Unable to JSON encode payload. Error code: 5"
*/
public static function jsonEncodeSafe($value, $options = 0, $depth = 512, $utfErrorFlag = false)
{
$encoded = json_encode($value, $options, $depth);
switch (json_last_error()) {
case JSON_ERROR_NONE:
return $encoded;
// case JSON_ERROR_DEPTH:
// return 'Maximum stack depth exceeded'; // or trigger_error() or throw new Exception()
// case JSON_ERROR_STATE_MISMATCH:
// return 'Underflow or the modes mismatch'; // or trigger_error() or throw new Exception()
// case JSON_ERROR_CTRL_CHAR:
// return 'Unexpected control character found';
// case JSON_ERROR_SYNTAX:
// return 'Syntax error, malformed JSON'; // or trigger_error() or throw new Exception()
case JSON_ERROR_UTF8:
$clean = self::utf8ize($value);
if ($utfErrorFlag) {
//return 'UTF8 encoding error'; // or trigger_error() or throw new Exception()
}
return self::jsonEncodeSafe($clean, $options, $depth, true);
// default:
// return 'Unknown error'; // or trigger_error() or throw new Exception()
// public static function jsonEncodeSafe($value, $options = 0, $depth = 512, $utfErrorFlag = false)
// {
// $encoded = json_encode($value, $options, $depth);
// switch (json_last_error()) {
// case JSON_ERROR_NONE:
// return $encoded;
// // case JSON_ERROR_DEPTH:
// // return 'Maximum stack depth exceeded'; // or trigger_error() or throw new Exception()
// // case JSON_ERROR_STATE_MISMATCH:
// // return 'Underflow or the modes mismatch'; // or trigger_error() or throw new Exception()
// // case JSON_ERROR_CTRL_CHAR:
// // return 'Unexpected control character found';
// // case JSON_ERROR_SYNTAX:
// // return 'Syntax error, malformed JSON'; // or trigger_error() or throw new Exception()
// case JSON_ERROR_UTF8:
// $clean = self::utf8ize($value);
// if ($utfErrorFlag) {
// //return 'UTF8 encoding error'; // or trigger_error() or throw new Exception()
// }
// return self::jsonEncodeSafe($clean, $options, $depth, true);
// // default:
// // return 'Unknown error'; // or trigger_error() or throw new Exception()
}
}
// }
// }
public static function utf8ize($mixed)
{
if (is_array($mixed)) {
foreach ($mixed as $key => $value) {
$mixed[$key] = self::utf8ize($value);
}
} else if (is_string ($mixed)) {
return utf8_encode($mixed);
}
return $mixed;
}
// public static function utf8ize($mixed)
// {
// if (is_array($mixed)) {
// foreach ($mixed as $key => $value) {
// $mixed[$key] = self::utf8ize($value);
// }
// } else if (is_string ($mixed)) {
// return utf8_encode($mixed);
// }
// return $mixed;
// }
/**
* Check if host is available on the port specified.

View File

@ -125,11 +125,13 @@
"Symfony\\Component\\Finder\\": "overrides/symfony/finder/",
"Symfony\\Component\\Console\\Helper\\": "overrides/symfony/console/Helper/",
"DebugBar\\": "overrides/maximebf/debugbar/src/DebugBar/",
"DebugBar\\DataFormatter\\": "overrides/maximebf/debugbar/src/DataFormatter/DataFormatter/",
"Illuminate\\Cache\\Console\\": "overrides/laravel/framework/src/Illuminate/Cache/Console/",
"Dotenv\\": "overrides/vlucas/phpdotenv/src/",
"Illuminate\\View\\": "overrides/laravel/framework/src/Illuminate/View/",
"Symfony\\Component\\Routing\\": "overrides/symfony/routing/",
"Symfony\\Component\\VarDumper\\Cloner\\": "overrides/symfony/var-dumper/Cloner/",
"Symfony\\Component\\VarDumper\\Dumper\\": "overrides/symfony/var-dumper/Dumper/",
"Devfactory\\Minify\\Providers\\": "overrides/devfactory/minify/src/Providers/",
"Barryvdh\\Debugbar\\": "overrides/barryvdh/laravel-debugbar/src/",
"Barryvdh\\Debugbar\\DataFormatter\\": "overrides/barryvdh/laravel-debugbar/src/DataFormatter/",
@ -138,6 +140,7 @@
"Spatie\\String\\": "overrides/spatie/string/src/",
"GuzzleHttp\\": "overrides/guzzlehttp/guzzle/src/",
"GuzzleHttp\\Cookie\\": "overrides/guzzlehttp/guzzle/src/Cookie/",
"GuzzleHttp\\Psr7\\": "overrides/guzzlehttp/psr7/src/",
"Ramsey\\Uuid\\": "overrides/ramsey/uuid/src/",
"Rap2hpoutre\\LaravelLogViewer\\": "overrides/rap2hpoutre/laravel-log-viewer/src/Rap2hpoutre/LaravelLogViewer/",
"Symfony\\Component\\Console\\Descriptor\\": "overrides/symfony/console/Descriptor",
@ -224,6 +227,7 @@
"vendor/symfony/finder/Iterator/DateRangeFilterIterator.php",
"vendor/symfony/console/Helper/HelperSet.php",
"vendor/maximebf/debugbar/src/DebugBar/DebugBar.php",
"vendor/maximebf/debugbar/src/DebugBar/DataFormatter/DataFormatter.php",
"vendor/laravel/framework/src/Illuminate/Cache/Console/ClearCommand.php",
"vendor/vlucas/phpdotenv/src/Loader.php",
"vendor/laravel/framework/src/Illuminate/View/View.php",
@ -231,6 +235,7 @@
"vendor/symfony/routing/CompiledRoute.php",
"vendor/symfony/var-dumper/Cloner/Data.php",
"vendor/symfony/var-dumper/Cloner/Stub.php",
"vendor/symfony/var-dumper/Dumper/HtmlDumper.php",
"vendor/devfactory/minify/src/Providers/BaseProvider.php",
"vendor/barryvdh/laravel-debugbar/src/DataFormatter/QueryFormatter.php",
"vendor/symfony/process/Process.php",
@ -239,13 +244,16 @@
"vendor/spatie/string/src/Str.php",
"vendor/guzzlehttp/guzzle/src/Client.php",
"vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php",
"vendor/guzzlehttp/guzzle/src/psr7/src/LazyOpenStream.php",
"vendor/ramsey/uuid/src/Uuid.php",
"vendor/laravel/framework/src/Illuminate/Routing/Router.php",
"vendor/rap2hpoutre/laravel-log-viewer/src/Rap2hpoutre/LaravelLogViewer/LaravelLogViewer.php",
"vendor/symfony/console/Descriptor/TextDescriptor.php",
"vendor/symfony/console/Helper/Helper.php",
"vendor/symfony/finder/Iterator/SortableIterator.php",
"vendor/symfony/css-selector/XPath/Extension/NodeExtension.php"
"vendor/symfony/css-selector/XPath/Extension/NodeExtension.php",
"vendor/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/NameSync.php",
"vendor/ezyang/htmlpurifier/library/HTMLPurifier/Lexer.php"
]
},
"autoload-dev": {

View File

@ -0,0 +1,42 @@
<?php
/**
* Post-transform that performs validation to the name attribute; if
* it is present with an equivalent id attribute, it is passed through;
* otherwise validation is performed.
*/
class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
{
public $idDef;
public function __construct()
{
$this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
}
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (!isset($attr['name'])) {
return $attr;
}
$name = $attr['name'];
if (isset($attr['id']) && $attr['id'] === $name) {
return $attr;
}
$result = $this->idDef->validate($name, $config, $context);
if ($result === false) {
unset($attr['name']);
} else {
$attr['name'] = $result;
}
return $attr;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,383 @@
<?php
/**
* Forgivingly lexes HTML (SGML-style) markup into tokens.
*
* A lexer parses a string of SGML-style markup and converts them into
* corresponding tokens. It doesn't check for well-formedness, although its
* internal mechanism may make this automatic (such as the case of
* HTMLPurifier_Lexer_DOMLex). There are several implementations to choose
* from.
*
* A lexer is HTML-oriented: it might work with XML, but it's not
* recommended, as we adhere to a subset of the specification for optimization
* reasons. This might change in the future. Also, most tokenizers are not
* expected to handle DTDs or PIs.
*
* This class should not be directly instantiated, but you may use create() to
* retrieve a default copy of the lexer. Being a supertype, this class
* does not actually define any implementation, but offers commonly used
* convenience functions for subclasses.
*
* @note The unit tests will instantiate this class for testing purposes, as
* many of the utility functions require a class to be instantiated.
* This means that, even though this class is not runnable, it will
* not be declared abstract.
*
* @par
*
* @note
* We use tokens rather than create a DOM representation because DOM would:
*
* @par
* -# Require more processing and memory to create,
* -# Is not streamable, and
* -# Has the entire document structure (html and body not needed).
*
* @par
* However, DOM is helpful in that it makes it easy to move around nodes
* without a lot of lookaheads to see when a tag is closed. This is a
* limitation of the token system and some workarounds would be nice.
*/
class HTMLPurifier_Lexer
{
public $_entity_parser;
/**
* Whether or not this lexer implements line-number/column-number tracking.
* If it does, set to true.
*/
public $tracksLineNumbers = false;
// -- STATIC ----------------------------------------------------------
/**
* Retrieves or sets the default Lexer as a Prototype Factory.
*
* By default HTMLPurifier_Lexer_DOMLex will be returned. There are
* a few exceptions involving special features that only DirectLex
* implements.
*
* @note The behavior of this class has changed, rather than accepting
* a prototype object, it now accepts a configuration object.
* To specify your own prototype, set %Core.LexerImpl to it.
* This change in behavior de-singletonizes the lexer object.
*
* @param HTMLPurifier_Config $config
* @return HTMLPurifier_Lexer
* @throws HTMLPurifier_Exception
*/
public static function create($config)
{
if (!($config instanceof HTMLPurifier_Config)) {
$lexer = $config;
trigger_error(
"Passing a prototype to
HTMLPurifier_Lexer::create() is deprecated, please instead
use %Core.LexerImpl",
E_USER_WARNING
);
} else {
$lexer = $config->get('Core.LexerImpl');
}
$needs_tracking =
$config->get('Core.MaintainLineNumbers') ||
$config->get('Core.CollectErrors');
$inst = null;
if (is_object($lexer)) {
$inst = $lexer;
} else {
if (is_null($lexer)) {
do {
// auto-detection algorithm
if ($needs_tracking) {
$lexer = 'DirectLex';
break;
}
if (class_exists('DOMDocument', false) &&
method_exists('DOMDocument', 'loadHTML') &&
!extension_loaded('domxml')
) {
// check for DOM support, because while it's part of the
// core, it can be disabled compile time. Also, the PECL
// domxml extension overrides the default DOM, and is evil
// and nasty and we shan't bother to support it
$lexer = 'DOMLex';
} else {
$lexer = 'DirectLex';
}
} while (0);
} // do..while so we can break
// instantiate recognized string names
switch ($lexer) {
case 'DOMLex':
$inst = new HTMLPurifier_Lexer_DOMLex();
break;
case 'DirectLex':
$inst = new HTMLPurifier_Lexer_DirectLex();
break;
case 'PH5P':
$inst = new HTMLPurifier_Lexer_PH5P();
break;
default:
throw new HTMLPurifier_Exception(
"Cannot instantiate unrecognized Lexer type " .
htmlspecialchars($lexer)
);
}
}
if (!$inst) {
throw new HTMLPurifier_Exception('No lexer was instantiated');
}
// once PHP DOM implements native line numbers, or we
// hack out something using XSLT, remove this stipulation
if ($needs_tracking && !$inst->tracksLineNumbers) {
throw new HTMLPurifier_Exception(
'Cannot use lexer that does not support line numbers with ' .
'Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'
);
}
return $inst;
}
// -- CONVENIENCE MEMBERS ---------------------------------------------
public function __construct()
{
$this->_entity_parser = new HTMLPurifier_EntityParser();
}
/**
* Most common entity to raw value conversion table for special entities.
* @type array
*/
protected $_special_entity2str =
array(
'&quot;' => '"',
'&amp;' => '&',
'&lt;' => '<',
'&gt;' => '>',
'&#39;' => "'",
'&#039;' => "'",
'&#x27;' => "'"
);
public function parseText($string, $config) {
return $this->parseData($string, false, $config);
}
public function parseAttr($string, $config) {
return $this->parseData($string, true, $config);
}
/**
* Parses special entities into the proper characters.
*
* This string will translate escaped versions of the special characters
* into the correct ones.
*
* @param string $string String character data to be parsed.
* @return string Parsed character data.
*/
public function parseData($string, $is_attr, $config)
{
// following functions require at least one character
if ($string === '') {
return '';
}
// subtracts amps that cannot possibly be escaped
$num_amp = substr_count($string, '&') - substr_count($string, '& ') -
($string[strlen($string) - 1] === '&' ? 1 : 0);
if (!$num_amp) {
return $string;
} // abort if no entities
$num_esc_amp = substr_count($string, '&amp;');
$string = strtr($string, $this->_special_entity2str);
// code duplication for sake of optimization, see above
$num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') -
($string[strlen($string) - 1] === '&' ? 1 : 0);
if ($num_amp_2 <= $num_esc_amp) {
return $string;
}
// hmm... now we have some uncommon entities. Use the callback.
if ($config->get('Core.LegacyEntityDecoder')) {
$string = $this->_entity_parser->substituteSpecialEntities($string);
} else {
if ($is_attr) {
$string = $this->_entity_parser->substituteAttrEntities($string);
} else {
$string = $this->_entity_parser->substituteTextEntities($string);
}
}
return $string;
}
/**
* Lexes an HTML string into tokens.
* @param $string String HTML.
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return HTMLPurifier_Token[] array representation of HTML.
*/
public function tokenizeHTML($string, $config, $context)
{
trigger_error('Call to abstract class', E_USER_ERROR);
}
/**
* Translates CDATA sections into regular sections (through escaping).
* @param string $string HTML string to process.
* @return string HTML with CDATA sections escaped.
*/
protected static function escapeCDATA($string)
{
return preg_replace_callback(
'/<!\[CDATA\[(.+?)\]\]>/s',
array('HTMLPurifier_Lexer', 'CDATACallback'),
$string
);
}
/**
* Special CDATA case that is especially convoluted for <script>
* @param string $string HTML string to process.
* @return string HTML with CDATA sections escaped.
*/
protected static function escapeCommentedCDATA($string)
{
return preg_replace_callback(
'#<!--//--><!\[CDATA\[//><!--(.+?)//--><!\]\]>#s',
array('HTMLPurifier_Lexer', 'CDATACallback'),
$string
);
}
/**
* Special Internet Explorer conditional comments should be removed.
* @param string $string HTML string to process.
* @return string HTML with conditional comments removed.
*/
protected static function removeIEConditional($string)
{
return preg_replace(
'#<!--\[if [^>]+\]>.*?<!\[endif\]-->#si', // probably should generalize for all strings
'',
$string
);
}
/**
* Callback function for escapeCDATA() that does the work.
*
* @warning Though this is public in order to let the callback happen,
* calling it directly is not recommended.
* @param array $matches PCRE matches array, with index 0 the entire match
* and 1 the inside of the CDATA section.
* @return string Escaped internals of the CDATA section.
*/
protected static function CDATACallback($matches)
{
// not exactly sure why the character set is needed, but whatever
return htmlspecialchars($matches[1], ENT_COMPAT, 'UTF-8');
}
/**
* Takes a piece of HTML and normalizes it by converting entities, fixing
* encoding, extracting bits, and other good stuff.
* @param string $html HTML.
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return string
* @todo Consider making protected
*/
public function normalize($html, $config, $context)
{
// normalize newlines to \n
if ($config->get('Core.NormalizeNewlines')) {
$html = str_replace("\r\n", "\n", $html);
$html = str_replace("\r", "\n", $html);
}
if ($config->get('HTML.Trusted')) {
// escape convoluted CDATA
$html = $this->escapeCommentedCDATA($html);
}
// escape CDATA
$html = $this->escapeCDATA($html);
$html = $this->removeIEConditional($html);
// extract body from document if applicable
if ($config->get('Core.ConvertDocumentToFragment')) {
$e = false;
if ($config->get('Core.CollectErrors')) {
$e =& $context->get('ErrorCollector');
}
$new_html = $this->extractBody($html);
if ($e && $new_html != $html) {
$e->send(E_WARNING, 'Lexer: Extracted body');
}
$html = $new_html;
}
// expand entities that aren't the big five
if ($config->get('Core.LegacyEntityDecoder')) {
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
}
// clean into wellformed UTF-8 string for an SGML context: this has
// to be done after entity expansion because the entities sometimes
// represent non-SGML characters (horror, horror!)
$html = HTMLPurifier_Encoder::cleanUTF8($html);
// if processing instructions are to removed, remove them now
if ($config->get('Core.RemoveProcessingInstructions')) {
$html = preg_replace('#<\?.+?\?>#s', '', $html);
}
$hidden_elements = $config->get('Core.HiddenElements');
if ($config->get('Core.AggressivelyRemoveScript') &&
!($config->get('HTML.Trusted') || !$config->get('Core.RemoveScriptContents')
|| empty($hidden_elements["script"]))) {
$html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
}
return $html;
}
/**
* Takes a string of HTML (fragment or document) and returns the content
* @todo Consider making protected
*/
public function extractBody($html)
{
$matches = array();
$result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, $matches);
if ($result) {
// Make sure it's not in a comment
$comment_start = strrpos($matches[1], '<!--');
$comment_end = strrpos($matches[1], '-->');
if ($comment_start === false ||
($comment_end !== false && $comment_end > $comment_start)) {
return $matches[2];
}
}
return $html;
}
}
// vim: et sw=4 sts=4

View File

@ -0,0 +1,40 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
/**
* Lazily reads or writes to a file that is opened only after an IO operation
* take place on the stream.
*/
#[\AllowDynamicProperties]
class LazyOpenStream implements StreamInterface
{
use StreamDecoratorTrait;
/** @var string File to open */
private $filename;
/** @var string $mode */
private $mode;
/**
* @param string $filename File to lazily open
* @param string $mode fopen mode to use when opening the stream
*/
public function __construct($filename, $mode)
{
$this->filename = $filename;
$this->mode = $mode;
}
/**
* Creates the underlying stream lazily when required.
*
* @return StreamInterface
*/
protected function createStream()
{
return stream_for(try_fopen($this->filename, $this->mode));
}
}

View File

@ -0,0 +1,84 @@
<?php
/*
* This file is part of the DebugBar package.
*
* (c) 2013 Maxime Bouroumeau-Fuseau
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace DebugBar\DataFormatter;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use Symfony\Component\VarDumper\Dumper\CliDumper;
class DataFormatter implements DataFormatterInterface
{
public $cloner;
public $dumper;
/**
* DataFormatter constructor.
*/
public function __construct()
{
$this->cloner = new VarCloner();
$this->dumper = new CliDumper();
}
/**
* @param $data
* @return string
*/
public function formatVar($data)
{
$output = '';
$this->dumper->dump(
$this->cloner->cloneVar($data),
function ($line, $depth) use (&$output) {
// A negative depth means "end of dump"
if ($depth >= 0) {
// Adds a two spaces indentation to the line
$output .= str_repeat(' ', $depth).$line."\n";
}
}
);
return trim($output);
}
/**
* @param float $seconds
* @return string
*/
public function formatDuration($seconds)
{
if ($seconds < 0.001) {
return round($seconds * 1000000) . 'μs';
} elseif ($seconds < 1) {
return round($seconds * 1000, 2) . 'ms';
}
return round($seconds, 2) . 's';
}
/**
* @param string $size
* @param int $precision
* @return string
*/
public function formatBytes($size, $precision = 2)
{
if ($size === 0 || $size === null) {
return "0B";
}
$sign = $size < 0 ? '-' : '';
$size = abs($size);
$base = log($size) / log(1024);
$suffixes = array('B', 'KB', 'MB', 'GB', 'TB');
return $sign . round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
}
}

View File

@ -919,8 +919,11 @@ class Carbon extends DateTime implements JsonSerializable
*
* @return void
*/
private static function setLastErrors(array $lastErrors)
private static function setLastErrors($lastErrors)
{
if (!is_array($lastErrors)) {
return;
}
static::$lastErrors = $lastErrors;
}

View File

@ -0,0 +1,904 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <p@tchwork.com>
*/
class HtmlDumper extends CliDumper
{
public static $defaultOutput = 'php://output';
protected $dumpHeader;
protected $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">';
protected $dumpSuffix = '</pre><script>Sfdump(%s)</script>';
protected $dumpId = 'sf-dump';
protected $colors = true;
protected $headerIsDumped = false;
protected $lastDepth = -1;
protected $styles = [
'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: break-all',
'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',
'ellipsis' => 'color:#FF8400',
];
private $displayOptions = [
'maxDepth' => 1,
'maxStringLength' => 160,
'fileLinkFormat' => null,
];
private $extraDisplayOptions = [];
/**
* {@inheritdoc}
*/
public function __construct($output = null, $charset = null, $flags = 0)
{
AbstractDumper::__construct($output, $charset, $flags);
$this->dumpId = 'sf-dump-'.mt_rand();
$this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
}
/**
* {@inheritdoc}
*/
public function setStyles(array $styles)
{
$this->headerIsDumped = false;
$this->styles = $styles + $this->styles;
}
/**
* Configures display options.
*
* @param array $displayOptions A map of display options to customize the behavior
*/
public function setDisplayOptions(array $displayOptions)
{
$this->headerIsDumped = false;
$this->displayOptions = $displayOptions + $this->displayOptions;
}
/**
* 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, array $extraDisplayOptions = [])
{
$this->extraDisplayOptions = $extraDisplayOptions;
$result = parent::dump($data, $output);
$this->dumpId = 'sf-dump-'.mt_rand();
return $result;
}
/**
* Dumps the HTML header.
*/
protected function getDumpHeader()
{
$this->headerIsDumped = null !== $this->outputStream ? $this->outputStream : $this->lineDumper;
if (null !== $this->dumpHeader) {
return $this->dumpHeader;
}
$line = str_replace('{$options}', json_encode($this->displayOptions, \JSON_FORCE_OBJECT), <<<'EOHTML'
<script>
Sfdump = window.Sfdump || (function (doc) {
var refStyle = doc.createElement('style'),
rxEsc = /([.*+?^${}()|\[\]\/\\])/g,
idRx = /\bsf-dump-\d+-ref[012]\w+\b/,
keyHint = 0 <= navigator.platform.toUpperCase().indexOf('MAC') ? 'Cmd' : 'Ctrl',
addEventListener = function (e, n, cb) {
e.addEventListener(n, cb, false);
};
(doc.documentElement.firstElementChild || doc.documentElement.children[0]).appendChild(refStyle);
if (!doc.addEventListener) {
addEventListener = function (element, eventName, callback) {
element.attachEvent('on' + eventName, function (e) {
e.preventDefault = function () {e.returnValue = false;};
e.target = e.srcElement;
callback(e);
});
};
}
function toggle(a, recursive) {
var s = a.nextSibling || {}, oldClass = s.className, arrow, newClass;
if (/\bsf-dump-compact\b/.test(oldClass)) {
arrow = '▼';
newClass = 'sf-dump-expanded';
} else if (/\bsf-dump-expanded\b/.test(oldClass)) {
arrow = '▶';
newClass = 'sf-dump-compact';
} else {
return false;
}
if (doc.createEvent && s.dispatchEvent) {
var event = doc.createEvent('Event');
event.initEvent('sf-dump-expanded' === newClass ? 'sfbeforedumpexpand' : 'sfbeforedumpcollapse', true, false);
s.dispatchEvent(event);
}
a.lastChild.innerHTML = arrow;
s.className = s.className.replace(/\bsf-dump-(compact|expanded)\b/, newClass);
if (recursive) {
try {
a = s.querySelectorAll('.'+oldClass);
for (s = 0; s < a.length; ++s) {
if (-1 == a[s].className.indexOf(newClass)) {
a[s].className = newClass;
a[s].previousSibling.lastChild.innerHTML = arrow;
}
}
} catch (e) {
}
}
return true;
};
function collapse(a, recursive) {
var s = a.nextSibling || {}, oldClass = s.className;
if (/\bsf-dump-expanded\b/.test(oldClass)) {
toggle(a, recursive);
return true;
}
return false;
};
function expand(a, recursive) {
var s = a.nextSibling || {}, oldClass = s.className;
if (/\bsf-dump-compact\b/.test(oldClass)) {
toggle(a, recursive);
return true;
}
return false;
};
function collapseAll(root) {
var a = root.querySelector('a.sf-dump-toggle');
if (a) {
collapse(a, true);
expand(a);
return true;
}
return false;
}
function reveal(node) {
var previous, parents = [];
while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) {
parents.push(previous);
}
if (0 !== parents.length) {
parents.forEach(function (parent) {
expand(parent);
});
return true;
}
return false;
}
function highlight(root, activeNode, nodes) {
resetHighlightedNodes(root);
Array.from(nodes||[]).forEach(function (node) {
if (!/\bsf-dump-highlight\b/.test(node.className)) {
node.className = node.className + ' sf-dump-highlight';
}
});
if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) {
activeNode.className = activeNode.className + ' sf-dump-highlight-active';
}
}
function resetHighlightedNodes(root) {
Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) {
strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, '');
strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, '');
});
}
return function (root, x) {
root = doc.getElementById(root);
var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'),
options = {$options},
elt = root.getElementsByTagName('A'),
len = elt.length,
i = 0, s, h,
t = [];
while (i < len) t.push(elt[i++]);
for (i in x) {
options[i] = x[i];
}
function a(e, f) {
addEventListener(root, e, function (e) {
if ('A' == e.target.tagName) {
f(e.target, e);
} else if ('A' == e.target.parentNode.tagName) {
f(e.target.parentNode, e);
} else if (e.target.nextElementSibling && 'A' == e.target.nextElementSibling.tagName) {
f(e.target.nextElementSibling, e, true);
}
});
};
function isCtrlKey(e) {
return e.ctrlKey || e.metaKey;
}
function xpathString(str) {
var parts = str.match(/[^'"]+|['"]/g).map(function (part) {
if ("'" == part) {
return '"\'"';
}
if ('"' == part) {
return "'\"'";
}
return "'" + part + "'";
});
return "concat(" + parts.join(",") + ", '')";
}
function xpathHasClass(className) {
return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')";
}
addEventListener(root, 'mouseover', function (e) {
if ('' != refStyle.innerHTML) {
refStyle.innerHTML = '';
}
});
a('mouseover', function (a, e, c) {
if (c) {
e.target.style.cursor = "pointer";
} else if (a = idRx.exec(a.className)) {
try {
refStyle.innerHTML = 'pre.sf-dump .'+a[0]+'{background-color: #B729D9; color: #FFF !important; border-radius: 2px}';
} catch (e) {
}
}
});
a('click', function (a, e, c) {
if (/\bsf-dump-toggle\b/.test(a.className)) {
e.preventDefault();
if (!toggle(a, isCtrlKey(e))) {
var r = doc.getElementById(a.getAttribute('href').substr(1)),
s = r.previousSibling,
f = r.parentNode,
t = a.parentNode;
t.replaceChild(r, a);
f.replaceChild(a, s);
t.insertBefore(s, r);
f = f.firstChild.nodeValue.match(indentRx);
t = t.firstChild.nodeValue.match(indentRx);
if (f && t && f[0] !== t[0]) {
r.innerHTML = r.innerHTML.replace(new RegExp('^'+f[0].replace(rxEsc, '\\$1'), 'mg'), t[0]);
}
if (/\bsf-dump-compact\b/.test(r.className)) {
toggle(s, isCtrlKey(e));
}
}
if (c) {
} else if (doc.getSelection) {
try {
doc.getSelection().removeAllRanges();
} catch (e) {
doc.getSelection().empty();
}
} else {
doc.selection.empty();
}
} else if (/\bsf-dump-str-toggle\b/.test(a.className)) {
e.preventDefault();
e = a.parentNode.parentNode;
e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className);
}
});
elt = root.getElementsByTagName('SAMP');
len = elt.length;
i = 0;
while (i < len) t.push(elt[i++]);
len = t.length;
for (i = 0; i < len; ++i) {
elt = t[i];
if ('SAMP' == elt.tagName) {
a = elt.previousSibling || {};
if ('A' != a.tagName) {
a = doc.createElement('A');
a.className = 'sf-dump-ref';
elt.parentNode.insertBefore(a, elt);
} else {
a.innerHTML += ' ';
}
a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children';
a.innerHTML += '<span>▼</span>';
a.className += ' sf-dump-toggle';
x = 1;
if ('sf-dump' != elt.parentNode.className) {
x += elt.parentNode.getAttribute('data-depth')/1;
}
elt.setAttribute('data-depth', x);
var className = elt.className;
elt.className = 'sf-dump-expanded';
if (className ? 'sf-dump-expanded' !== className : (x > options.maxDepth)) {
toggle(a);
}
} else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
a = a.substr(1);
elt.className += ' '+a;
if (/[\[{]$/.test(elt.previousSibling.nodeValue)) {
a = a != elt.nextSibling.id && doc.getElementById(a);
try {
s = a.nextSibling;
elt.appendChild(a);
s.parentNode.insertBefore(a, s);
if (/^[@#]/.test(elt.innerHTML)) {
elt.innerHTML += ' <span>▶</span>';
} else {
elt.innerHTML = '<span>▶</span>';
elt.className = 'sf-dump-ref';
}
elt.className += ' sf-dump-toggle';
} catch (e) {
if ('&' == elt.innerHTML.charAt(0)) {
elt.innerHTML = '…';
elt.className = 'sf-dump-ref';
}
}
}
}
}
if (doc.evaluate && Array.from && root.children.length > 1) {
root.setAttribute('tabindex', 0);
SearchState = function () {
this.nodes = [];
this.idx = 0;
};
SearchState.prototype = {
next: function () {
if (this.isEmpty()) {
return this.current();
}
this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0;
return this.current();
},
previous: function () {
if (this.isEmpty()) {
return this.current();
}
this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1);
return this.current();
},
isEmpty: function () {
return 0 === this.count();
},
current: function () {
if (this.isEmpty()) {
return null;
}
return this.nodes[this.idx];
},
reset: function () {
this.nodes = [];
this.idx = 0;
},
count: function () {
return this.nodes.length;
},
};
function showCurrent(state)
{
var currentNode = state.current();
if (currentNode) {
reveal(currentNode);
highlight(root, currentNode, state.nodes);
}
counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count();
}
var search = doc.createElement('div');
search.className = 'sf-dump-search-wrapper sf-dump-search-hidden';
search.innerHTML = '
<input type="text" class="sf-dump-search-input">
<span class="sf-dump-search-count">0 of 0<\/span>
<button type="button" class="sf-dump-search-input-previous" tabindex="-1">
<svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 1331l-166 165q-19 19-45 19t-45-19L896 965l-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/><\/svg>
<\/button>
<button type="button" class="sf-dump-search-input-next" tabindex="-1">
<svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1683 808l-742 741q-19 19-45 19t-45-19L109 808q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/><\/svg>
<\/button>
';
root.insertBefore(search, root.firstChild);
var state = new SearchState();
var searchInput = search.querySelector('.sf-dump-search-input');
var counter = search.querySelector('.sf-dump-search-count');
var searchInputTimer = 0;
var previousSearchQuery = '';
addEventListener(searchInput, 'keyup', function (e) {
var searchQuery = e.target.value;
/* Don't perform anything if the pressed key didn't change the query */
if (searchQuery === previousSearchQuery) {
return;
}
previousSearchQuery = searchQuery;
clearTimeout(searchInputTimer);
searchInputTimer = setTimeout(function () {
state.reset();
collapseAll(root);
resetHighlightedNodes(root);
if ('' === searchQuery) {
counter.textContent = '0 of 0';
return;
}
var classMatches = [
"sf-dump-str",
"sf-dump-key",
"sf-dump-public",
"sf-dump-protected",
"sf-dump-private",
].map(xpathHasClass).join(' or ');
var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
while (node = xpathResult.iterateNext()) state.nodes.push(node);
showCurrent(state);
}, 400);
});
Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) {
addEventListener(btn, 'click', function (e) {
e.preventDefault();
-1 !== e.target.className.indexOf('next') ? state.next() : state.previous();
searchInput.focus();
collapseAll(root);
showCurrent(state);
})
});
addEventListener(root, 'keydown', function (e) {
var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className);
if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) {
/* F3 or CMD/CTRL + F */
e.preventDefault();
search.className = search.className.replace(/\bsf-dump-search-hidden\b/, '');
searchInput.focus();
} else if (isSearchActive) {
if (27 === e.keyCode) {
/* ESC key */
search.className += ' sf-dump-search-hidden';
e.preventDefault();
resetHighlightedNodes(root);
searchInput.value = '';
} else if (
(isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */
|| 13 === e.keyCode /* Enter */
|| 114 === e.keyCode /* F3 */
) {
e.preventDefault();
e.shiftKey ? state.previous() : state.next();
collapseAll(root);
showCurrent(state);
}
}
});
}
if (0 >= options.maxStringLength) {
return;
}
try {
elt = root.querySelectorAll('.sf-dump-str');
len = elt.length;
i = 0;
t = [];
while (i < len) t.push(elt[i++]);
len = t.length;
for (i = 0; i < len; ++i) {
elt = t[i];
s = elt.innerText || elt.textContent;
x = s.length - options.maxStringLength;
if (0 < x) {
h = elt.innerHTML;
elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength);
elt.className += ' sf-dump-str-collapse';
elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+
'<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>';
}
}
} catch (e) {
}
};
})(document);
</script><style>
pre.sf-dump {
display: block;
white-space: pre;
padding: 5px;
}
pre.sf-dump:after {
content: "";
visibility: hidden;
display: block;
height: 0;
clear: both;
}
pre.sf-dump span {
display: inline;
}
pre.sf-dump .sf-dump-compact {
display: none;
}
pre.sf-dump abbr {
text-decoration: none;
border: none;
cursor: help;
}
pre.sf-dump a {
text-decoration: none;
cursor: pointer;
border: 0;
outline: none;
color: inherit;
}
pre.sf-dump .sf-dump-ellipsis {
display: inline-block;
overflow: visible;
text-overflow: ellipsis;
max-width: 5em;
white-space: nowrap;
overflow: hidden;
vertical-align: top;
}
pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis {
max-width: none;
}
pre.sf-dump code {
display:inline;
padding:0;
background:none;
}
.sf-dump-str-collapse .sf-dump-str-collapse {
display: none;
}
.sf-dump-str-expand .sf-dump-str-expand {
display: none;
}
.sf-dump-public.sf-dump-highlight,
.sf-dump-protected.sf-dump-highlight,
.sf-dump-private.sf-dump-highlight,
.sf-dump-str.sf-dump-highlight,
.sf-dump-key.sf-dump-highlight {
background: rgba(111, 172, 204, 0.3);
border: 1px solid #7DA0B1;
border-radius: 3px;
}
.sf-dump-public.sf-dump-highlight-active,
.sf-dump-protected.sf-dump-highlight-active,
.sf-dump-private.sf-dump-highlight-active,
.sf-dump-str.sf-dump-highlight-active,
.sf-dump-key.sf-dump-highlight-active {
background: rgba(253, 175, 0, 0.4);
border: 1px solid #ffa500;
border-radius: 3px;
}
pre.sf-dump .sf-dump-search-hidden {
display: none;
}
pre.sf-dump .sf-dump-search-wrapper {
float: right;
font-size: 0;
white-space: nowrap;
max-width: 100%;
text-align: right;
}
pre.sf-dump .sf-dump-search-wrapper > * {
vertical-align: top;
box-sizing: border-box;
height: 21px;
font-weight: normal;
border-radius: 0;
background: #FFF;
color: #757575;
border: 1px solid #BBB;
}
pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input {
padding: 3px;
height: 21px;
font-size: 12px;
border-right: none;
width: 140px;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
color: #000;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next,
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous {
background: #F2F2F2;
outline: none;
border-left: none;
font-size: 0;
line-height: 0;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg,
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg {
pointer-events: none;
width: 12px;
height: 12px;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count {
display: inline-block;
padding: 0 5px;
margin: 0;
border-left: none;
line-height: 21px;
font-size: 12px;
}
EOHTML
);
foreach ($this->styles as $class => $style) {
$line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}';
}
return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader;
}
/**
* {@inheritdoc}
*/
public function enterHash(Cursor $cursor, $type, $class, $hasChild)
{
parent::enterHash($cursor, $type, $class, false);
if ($cursor->skipChildren) {
$cursor->skipChildren = false;
$eol = ' class=sf-dump-compact>';
} elseif ($this->expandNextHash) {
$this->expandNextHash = false;
$eol = ' class=sf-dump-expanded>';
} else {
$eol = '>';
}
if ($hasChild) {
$this->line .= '<samp';
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(' id=%s-ref%s', $this->dumpId, $r);
}
$this->line .= $eol;
$this->dumpLine($cursor->depth);
}
}
/**
* {@inheritdoc}
*/
public function leaveHash(Cursor $cursor, $type, $class, $hasChild, $cut)
{
$this->dumpEllipsis($cursor, $hasChild, $cut);
if ($hasChild) {
$this->line .= '</samp>';
}
parent::leaveHash($cursor, $type, $class, $hasChild, 0);
}
/**
* {@inheritdoc}
*/
protected function style($style, $value, $attr = [])
{
if ('' === $value) {
return '';
}
$v = esc($value);
if ('ref' === $style) {
if (empty($attr['count'])) {
return sprintf('<a class=sf-dump-ref>%s</a>', $v);
}
$r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1);
return sprintf('<a class=sf-dump-ref href=#%s-ref%s title="%d occurrences">%s</a>', $this->dumpId, $r, 1 + $attr['count'], $v);
}
if ('const' === $style && isset($attr['value'])) {
$style .= sprintf(' title="%s"', esc(is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value'])));
} 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="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : '');
} elseif ('note' === $style && false !== $c = strrpos($v, '\\')) {
return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>', $v, $style, substr($v, $c + 1));
} elseif ('protected' === $style) {
$style .= ' title="Protected property"';
} elseif ('meta' === $style && isset($attr['title'])) {
$style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title'])));
} elseif ('private' === $style) {
$style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($this->utf8Encode($attr['class'])));
}
$map = static::$controlCharsMap;
if (isset($attr['ellipsis'])) {
$class = 'sf-dump-ellipsis';
if (isset($attr['ellipsis-type'])) {
$class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']);
}
$label = esc(substr($value, -$attr['ellipsis']));
$style = str_replace(' title="', " title=\"$v\n", $style);
$v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label)));
if (!empty($attr['ellipsis-tail'])) {
$tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail'])));
$v .= sprintf('<span class=sf-dump-ellipsis>%s</span>%s', substr($label, 0, $tail), substr($label, $tail));
} else {
$v .= $label;
}
}
$v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) {
$s = '<span class=sf-dump-default>';
$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.'</span>';
}, $v).'</span>';
if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
$attr['href'] = $href;
}
if (isset($attr['href'])) {
$target = isset($attr['file']) ? '' : ' target="_blank"';
$v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v);
}
if (isset($attr['lang'])) {
$v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $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 !== (null !== $this->outputStream ? $this->outputStream : $this->lineDumper)) {
$this->line = $this->getDumpHeader().$this->line;
}
if (-1 === $depth) {
$args = ['"'.$this->dumpId.'"'];
if ($this->extraDisplayOptions) {
$args[] = json_encode($this->extraDisplayOptions, \JSON_FORCE_OBJECT);
}
// Replace is for BC
$this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args));
}
$this->lastDepth = $depth;
$this->line = htmlentities($this->line);
if (-1 === $depth) {
AbstractDumper::dumpLine(0);
}
AbstractDumper::dumpLine($depth);
}
private function getSourceLink($file, $line)
{
$options = $this->extraDisplayOptions + $this->displayOptions;
if ($fmt = $options['fileLinkFormat']) {
return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line);
}
return false;
}
}
function esc($str)
{
return htmlspecialchars($str, \ENT_QUOTES, 'UTF-8');
}