[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <[email protected]> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12 namespace Symfony\Component\VarDumper\Dumper; 13 14 use Symfony\Component\VarDumper\Cloner\Cursor; 15 use Symfony\Component\VarDumper\Cloner\Stub; 16 17 /** 18 * CliDumper dumps variables for command line output. 19 * 20 * @author Nicolas Grekas <[email protected]> 21 */ 22 class CliDumper extends AbstractDumper 23 { 24 public static $defaultColors; 25 public static $defaultOutput = 'php://stdout'; 26 27 protected $colors; 28 protected $maxStringWidth = 0; 29 protected $styles = [ 30 // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics 31 'default' => '0;38;5;208', 32 'num' => '1;38;5;38', 33 'const' => '1;38;5;208', 34 'str' => '1;38;5;113', 35 'note' => '38;5;38', 36 'ref' => '38;5;247', 37 'public' => '', 38 'protected' => '', 39 'private' => '', 40 'meta' => '38;5;170', 41 'key' => '38;5;113', 42 'index' => '38;5;38', 43 ]; 44 45 protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/'; 46 protected static $controlCharsMap = [ 47 "\t" => '\t', 48 "\n" => '\n', 49 "\v" => '\v', 50 "\f" => '\f', 51 "\r" => '\r', 52 "\033" => '\e', 53 ]; 54 55 protected $collapseNextHash = false; 56 protected $expandNextHash = false; 57 58 private $displayOptions = [ 59 'fileLinkFormat' => null, 60 ]; 61 62 private $handlesHrefGracefully; 63 64 /** 65 * {@inheritdoc} 66 */ 67 public function __construct($output = null, string $charset = null, int $flags = 0) 68 { 69 parent::__construct($output, $charset, $flags); 70 71 if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { 72 // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI 73 $this->setStyles([ 74 'default' => '31', 75 'num' => '1;34', 76 'const' => '1;31', 77 'str' => '1;32', 78 'note' => '34', 79 'ref' => '1;30', 80 'meta' => '35', 81 'key' => '32', 82 'index' => '34', 83 ]); 84 } 85 86 $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; 87 } 88 89 /** 90 * Enables/disables colored output. 91 */ 92 public function setColors(bool $colors) 93 { 94 $this->colors = $colors; 95 } 96 97 /** 98 * Sets the maximum number of characters per line for dumped strings. 99 */ 100 public function setMaxStringWidth(int $maxStringWidth) 101 { 102 $this->maxStringWidth = $maxStringWidth; 103 } 104 105 /** 106 * Configures styles. 107 * 108 * @param array $styles A map of style names to style definitions 109 */ 110 public function setStyles(array $styles) 111 { 112 $this->styles = $styles + $this->styles; 113 } 114 115 /** 116 * Configures display options. 117 * 118 * @param array $displayOptions A map of display options to customize the behavior 119 */ 120 public function setDisplayOptions(array $displayOptions) 121 { 122 $this->displayOptions = $displayOptions + $this->displayOptions; 123 } 124 125 /** 126 * {@inheritdoc} 127 */ 128 public function dumpScalar(Cursor $cursor, string $type, $value) 129 { 130 $this->dumpKey($cursor); 131 132 $style = 'const'; 133 $attr = $cursor->attr; 134 135 switch ($type) { 136 case 'default': 137 $style = 'default'; 138 break; 139 140 case 'integer': 141 $style = 'num'; 142 143 if (isset($this->styles['integer'])) { 144 $style = 'integer'; 145 } 146 147 break; 148 149 case 'double': 150 $style = 'num'; 151 152 if (isset($this->styles['float'])) { 153 $style = 'float'; 154 } 155 156 switch (true) { 157 case \INF === $value: $value = 'INF'; break; 158 case -\INF === $value: $value = '-INF'; break; 159 case is_nan($value): $value = 'NAN'; break; 160 default: 161 $value = (string) $value; 162 if (!str_contains($value, $this->decimalPoint)) { 163 $value .= $this->decimalPoint.'0'; 164 } 165 break; 166 } 167 break; 168 169 case 'NULL': 170 $value = 'null'; 171 break; 172 173 case 'boolean': 174 $value = $value ? 'true' : 'false'; 175 break; 176 177 default: 178 $attr += ['value' => $this->utf8Encode($value)]; 179 $value = $this->utf8Encode($type); 180 break; 181 } 182 183 $this->line .= $this->style($style, $value, $attr); 184 185 $this->endValue($cursor); 186 } 187 188 /** 189 * {@inheritdoc} 190 */ 191 public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) 192 { 193 $this->dumpKey($cursor); 194 $attr = $cursor->attr; 195 196 if ($bin) { 197 $str = $this->utf8Encode($str); 198 } 199 if ('' === $str) { 200 $this->line .= '""'; 201 $this->endValue($cursor); 202 } else { 203 $attr += [ 204 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, 205 'binary' => $bin, 206 ]; 207 $str = $bin && false !== strpos($str, "\0") ? [$str] : explode("\n", $str); 208 if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { 209 unset($str[1]); 210 $str[0] .= "\n"; 211 } 212 $m = \count($str) - 1; 213 $i = $lineCut = 0; 214 215 if (self::DUMP_STRING_LENGTH & $this->flags) { 216 $this->line .= '('.$attr['length'].') '; 217 } 218 if ($bin) { 219 $this->line .= 'b'; 220 } 221 222 if ($m) { 223 $this->line .= '"""'; 224 $this->dumpLine($cursor->depth); 225 } else { 226 $this->line .= '"'; 227 } 228 229 foreach ($str as $str) { 230 if ($i < $m) { 231 $str .= "\n"; 232 } 233 if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { 234 $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); 235 $lineCut = $len - $this->maxStringWidth; 236 } 237 if ($m && 0 < $cursor->depth) { 238 $this->line .= $this->indentPad; 239 } 240 if ('' !== $str) { 241 $this->line .= $this->style('str', $str, $attr); 242 } 243 if ($i++ == $m) { 244 if ($m) { 245 if ('' !== $str) { 246 $this->dumpLine($cursor->depth); 247 if (0 < $cursor->depth) { 248 $this->line .= $this->indentPad; 249 } 250 } 251 $this->line .= '"""'; 252 } else { 253 $this->line .= '"'; 254 } 255 if ($cut < 0) { 256 $this->line .= '…'; 257 $lineCut = 0; 258 } elseif ($cut) { 259 $lineCut += $cut; 260 } 261 } 262 if ($lineCut) { 263 $this->line .= '…'.$lineCut; 264 $lineCut = 0; 265 } 266 267 if ($i > $m) { 268 $this->endValue($cursor); 269 } else { 270 $this->dumpLine($cursor->depth); 271 } 272 } 273 } 274 } 275 276 /** 277 * {@inheritdoc} 278 */ 279 public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild) 280 { 281 if (null === $this->colors) { 282 $this->colors = $this->supportsColors(); 283 } 284 285 $this->dumpKey($cursor); 286 $attr = $cursor->attr; 287 288 if ($this->collapseNextHash) { 289 $cursor->skipChildren = true; 290 $this->collapseNextHash = $hasChild = false; 291 } 292 293 $class = $this->utf8Encode($class); 294 if (Cursor::HASH_OBJECT === $type) { 295 $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; 296 } elseif (Cursor::HASH_RESOURCE === $type) { 297 $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); 298 } else { 299 $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '['; 300 } 301 302 if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { 303 $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); 304 } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { 305 $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); 306 } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { 307 $prefix = substr($prefix, 0, -1); 308 } 309 310 $this->line .= $prefix; 311 312 if ($hasChild) { 313 $this->dumpLine($cursor->depth); 314 } 315 } 316 317 /** 318 * {@inheritdoc} 319 */ 320 public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut) 321 { 322 if (empty($cursor->attr['cut_hash'])) { 323 $this->dumpEllipsis($cursor, $hasChild, $cut); 324 $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); 325 } 326 327 $this->endValue($cursor); 328 } 329 330 /** 331 * Dumps an ellipsis for cut children. 332 * 333 * @param bool $hasChild When the dump of the hash has child item 334 * @param int $cut The number of items the hash has been cut by 335 */ 336 protected function dumpEllipsis(Cursor $cursor, bool $hasChild, int $cut) 337 { 338 if ($cut) { 339 $this->line .= ' …'; 340 if (0 < $cut) { 341 $this->line .= $cut; 342 } 343 if ($hasChild) { 344 $this->dumpLine($cursor->depth + 1); 345 } 346 } 347 } 348 349 /** 350 * Dumps a key in a hash structure. 351 */ 352 protected function dumpKey(Cursor $cursor) 353 { 354 if (null !== $key = $cursor->hashKey) { 355 if ($cursor->hashKeyIsBinary) { 356 $key = $this->utf8Encode($key); 357 } 358 $attr = ['binary' => $cursor->hashKeyIsBinary]; 359 $bin = $cursor->hashKeyIsBinary ? 'b' : ''; 360 $style = 'key'; 361 switch ($cursor->hashType) { 362 default: 363 case Cursor::HASH_INDEXED: 364 if (self::DUMP_LIGHT_ARRAY & $this->flags) { 365 break; 366 } 367 $style = 'index'; 368 // no break 369 case Cursor::HASH_ASSOC: 370 if (\is_int($key)) { 371 $this->line .= $this->style($style, $key).' => '; 372 } else { 373 $this->line .= $bin.'"'.$this->style($style, $key).'" => '; 374 } 375 break; 376 377 case Cursor::HASH_RESOURCE: 378 $key = "\0~\0".$key; 379 // no break 380 case Cursor::HASH_OBJECT: 381 if (!isset($key[0]) || "\0" !== $key[0]) { 382 $this->line .= '+'.$bin.$this->style('public', $key).': '; 383 } elseif (0 < strpos($key, "\0", 1)) { 384 $key = explode("\0", substr($key, 1), 2); 385 386 switch ($key[0][0]) { 387 case '+': // User inserted keys 388 $attr['dynamic'] = true; 389 $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; 390 break 2; 391 case '~': 392 $style = 'meta'; 393 if (isset($key[0][1])) { 394 parse_str(substr($key[0], 1), $attr); 395 $attr += ['binary' => $cursor->hashKeyIsBinary]; 396 } 397 break; 398 case '*': 399 $style = 'protected'; 400 $bin = '#'.$bin; 401 break; 402 default: 403 $attr['class'] = $key[0]; 404 $style = 'private'; 405 $bin = '-'.$bin; 406 break; 407 } 408 409 if (isset($attr['collapse'])) { 410 if ($attr['collapse']) { 411 $this->collapseNextHash = true; 412 } else { 413 $this->expandNextHash = true; 414 } 415 } 416 417 $this->line .= $bin.$this->style($style, $key[1], $attr).($attr['separator'] ?? ': '); 418 } else { 419 // This case should not happen 420 $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; 421 } 422 break; 423 } 424 425 if ($cursor->hardRefTo) { 426 $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; 427 } 428 } 429 } 430 431 /** 432 * Decorates a value with some style. 433 * 434 * @param string $style The type of style being applied 435 * @param string $value The value being styled 436 * @param array $attr Optional context information 437 * 438 * @return string 439 */ 440 protected function style(string $style, string $value, array $attr = []) 441 { 442 if (null === $this->colors) { 443 $this->colors = $this->supportsColors(); 444 } 445 446 if (null === $this->handlesHrefGracefully) { 447 $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') 448 && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); 449 } 450 451 if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { 452 $prefix = substr($value, 0, -$attr['ellipsis']); 453 if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && str_starts_with($prefix, $_SERVER[$pwd])) { 454 $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); 455 } 456 if (!empty($attr['ellipsis-tail'])) { 457 $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); 458 $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); 459 } else { 460 $value = substr($value, -$attr['ellipsis']); 461 } 462 463 $value = $this->style('default', $prefix).$this->style($style, $value); 464 465 goto href; 466 } 467 468 $map = static::$controlCharsMap; 469 $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; 470 $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; 471 $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { 472 $s = $startCchr; 473 $c = $c[$i = 0]; 474 do { 475 $s .= $map[$c[$i]] ?? sprintf('\x%02X', \ord($c[$i])); 476 } while (isset($c[++$i])); 477 478 return $s.$endCchr; 479 }, $value, -1, $cchrCount); 480 481 if ($this->colors) { 482 if ($cchrCount && "\033" === $value[0]) { 483 $value = substr($value, \strlen($startCchr)); 484 } else { 485 $value = "\033[{$this->styles[$style]}m".$value; 486 } 487 if ($cchrCount && str_ends_with($value, $endCchr)) { 488 $value = substr($value, 0, -\strlen($endCchr)); 489 } else { 490 $value .= "\033[{$this->styles['default']}m"; 491 } 492 } 493 494 href: 495 if ($this->colors && $this->handlesHrefGracefully) { 496 if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], $attr['line'] ?? 0)) { 497 if ('note' === $style) { 498 $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; 499 } else { 500 $attr['href'] = $href; 501 } 502 } 503 if (isset($attr['href'])) { 504 $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; 505 } 506 } elseif ($attr['if_links'] ?? false) { 507 return ''; 508 } 509 510 return $value; 511 } 512 513 /** 514 * @return bool 515 */ 516 protected function supportsColors() 517 { 518 if ($this->outputStream !== static::$defaultOutput) { 519 return $this->hasColorSupport($this->outputStream); 520 } 521 if (null !== static::$defaultColors) { 522 return static::$defaultColors; 523 } 524 if (isset($_SERVER['argv'][1])) { 525 $colors = $_SERVER['argv']; 526 $i = \count($colors); 527 while (--$i > 0) { 528 if (isset($colors[$i][5])) { 529 switch ($colors[$i]) { 530 case '--ansi': 531 case '--color': 532 case '--color=yes': 533 case '--color=force': 534 case '--color=always': 535 case '--colors=always': 536 return static::$defaultColors = true; 537 538 case '--no-ansi': 539 case '--color=no': 540 case '--color=none': 541 case '--color=never': 542 case '--colors=never': 543 return static::$defaultColors = false; 544 } 545 } 546 } 547 } 548 549 $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; 550 $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'w') : $this->outputStream; 551 552 return static::$defaultColors = $this->hasColorSupport($h); 553 } 554 555 /** 556 * {@inheritdoc} 557 */ 558 protected function dumpLine(int $depth, bool $endOfValue = false) 559 { 560 if ($this->colors) { 561 $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); 562 } 563 parent::dumpLine($depth); 564 } 565 566 protected function endValue(Cursor $cursor) 567 { 568 if (-1 === $cursor->hashType) { 569 return; 570 } 571 572 if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { 573 if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { 574 $this->line .= ','; 575 } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { 576 $this->line .= ','; 577 } 578 } 579 580 $this->dumpLine($cursor->depth, true); 581 } 582 583 /** 584 * Returns true if the stream supports colorization. 585 * 586 * Reference: Composer\XdebugHandler\Process::supportsColor 587 * https://github.com/composer/xdebug-handler 588 * 589 * @param mixed $stream A CLI output stream 590 */ 591 private function hasColorSupport($stream): bool 592 { 593 if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { 594 return false; 595 } 596 597 // Follow https://no-color.org/ 598 if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { 599 return false; 600 } 601 602 if ('Hyper' === getenv('TERM_PROGRAM')) { 603 return true; 604 } 605 606 if (\DIRECTORY_SEPARATOR === '\\') { 607 return (\function_exists('sapi_windows_vt100_support') 608 && @sapi_windows_vt100_support($stream)) 609 || false !== getenv('ANSICON') 610 || 'ON' === getenv('ConEmuANSI') 611 || 'xterm' === getenv('TERM'); 612 } 613 614 return stream_isatty($stream); 615 } 616 617 /** 618 * Returns true if the Windows terminal supports true color. 619 * 620 * Note that this does not check an output stream, but relies on environment 621 * variables from known implementations, or a PHP and Windows version that 622 * supports true color. 623 */ 624 private function isWindowsTrueColor(): bool 625 { 626 $result = 183 <= getenv('ANSICON_VER') 627 || 'ON' === getenv('ConEmuANSI') 628 || 'xterm' === getenv('TERM') 629 || 'Hyper' === getenv('TERM_PROGRAM'); 630 631 if (!$result) { 632 $version = sprintf( 633 '%s.%s.%s', 634 PHP_WINDOWS_VERSION_MAJOR, 635 PHP_WINDOWS_VERSION_MINOR, 636 PHP_WINDOWS_VERSION_BUILD 637 ); 638 $result = $version >= '10.0.15063'; 639 } 640 641 return $result; 642 } 643 644 private function getSourceLink(string $file, int $line) 645 { 646 if ($fmt = $this->displayOptions['fileLinkFormat']) { 647 return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); 648 } 649 650 return false; 651 } 652 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |