first commit

This commit is contained in:
CHIEFSOFT\ameye
2025-11-22 09:54:51 -05:00
commit 07a07ab49f
722 changed files with 125787 additions and 0 deletions
+397
View File
@@ -0,0 +1,397 @@
<?php
declare(strict_types=1);
namespace Laminas\Escaper;
use function assert;
use function bin2hex;
use function ctype_digit;
use function hexdec;
use function htmlspecialchars;
use function in_array;
use function is_string;
use function mb_convert_encoding;
use function ord;
use function preg_match;
use function preg_replace_callback;
use function rawurlencode;
use function sprintf;
use function strlen;
use function strtolower;
use function strtoupper;
use function substr;
use const ENT_QUOTES;
use const ENT_SUBSTITUTE;
/**
* Context specific methods for use in secure output escaping
*
* @final
*/
class Escaper implements EscaperInterface
{
/**
* Entity Map mapping Unicode codepoints to any available named HTML entities.
*
* While HTML supports far more named entities, the lowest common denominator
* has become HTML5's XML Serialisation which is restricted to the those named
* entities that XML supports. Using HTML entities would result in this error:
* XML Parsing Error: undefined entity
*
* @var array<int, string>
*/
protected static $htmlNamedEntityMap = [
34 => 'quot', // quotation mark
38 => 'amp', // ampersand
60 => 'lt', // less-than sign
62 => 'gt', // greater-than sign
];
/**
* Current encoding for escaping. If not UTF-8, we convert strings from this encoding
* pre-escaping and back to this encoding post-escaping.
*
* @var non-empty-string
*/
protected $encoding = 'utf-8';
/**
* Holds the value of the special flags passed as second parameter to
* htmlspecialchars().
*
* @var int
*/
protected $htmlSpecialCharsFlags;
/**
* Static Matcher which escapes characters for HTML Attribute contexts
*
* @var callable
* @psalm-var callable(array<array-key, string>):string
*/
protected $htmlAttrMatcher;
/**
* Static Matcher which escapes characters for Javascript contexts
*
* @var callable
* @psalm-var callable(array<array-key, string>):string
*/
protected $jsMatcher;
/**
* Static Matcher which escapes characters for CSS Attribute contexts
*
* @var callable
* @psalm-var callable(array<array-key, string>):string
*/
protected $cssMatcher;
/**
* List of all encoding supported by this class
*
* @var list<non-empty-string>
*/
protected $supportedEncodings = [
'iso-8859-1',
'iso8859-1',
'iso-8859-5',
'iso8859-5',
'iso-8859-15',
'iso8859-15',
'utf-8',
'cp866',
'ibm866',
'866',
'cp1251',
'windows-1251',
'win-1251',
'1251',
'cp1252',
'windows-1252',
'1252',
'koi8-r',
'koi8-ru',
'koi8r',
'big5',
'950',
'gb2312',
'936',
'big5-hkscs',
'shift_jis',
'sjis',
'sjis-win',
'cp932',
'932',
'euc-jp',
'eucjp',
'eucjp-win',
'macroman',
];
/**
* Constructor: Single parameter allows setting of global encoding for use by
* the current object.
*
* @param non-empty-string|null $encoding
* @throws Exception\InvalidArgumentException
*/
public function __construct(?string $encoding = null)
{
if ($encoding !== null) {
if ($encoding === '') {
throw new Exception\InvalidArgumentException(
static::class . ' constructor parameter does not allow a blank value'
);
}
$encoding = strtolower($encoding);
if (! in_array($encoding, $this->supportedEncodings)) {
throw new Exception\InvalidArgumentException(
'Value of \'' . $encoding . '\' passed to ' . static::class
. ' constructor parameter is invalid. Provide an encoding supported by htmlspecialchars()'
);
}
$this->encoding = $encoding;
}
// We take advantage of ENT_SUBSTITUTE flag to correctly deal with invalid UTF-8 sequences.
$this->htmlSpecialCharsFlags = ENT_QUOTES | ENT_SUBSTITUTE;
// set matcher callbacks
$this->htmlAttrMatcher =
/** @param array<array-key, string> $matches */
fn(array $matches): string => $this->htmlAttrMatcher($matches);
$this->jsMatcher =
/** @param array<array-key, string> $matches */
fn(array $matches): string => $this->jsMatcher($matches);
$this->cssMatcher =
/** @param array<array-key, string> $matches */
fn(array $matches): string => $this->cssMatcher($matches);
}
/**
* Return the encoding that all output/input is expected to be encoded in.
*
* @return non-empty-string
*/
public function getEncoding()
{
return $this->encoding;
}
/** @inheritDoc */
public function escapeHtml(string $string)
{
return htmlspecialchars($string, $this->htmlSpecialCharsFlags, $this->encoding);
}
/** @inheritDoc */
public function escapeHtmlAttr(string $string)
{
$string = $this->toUtf8($string);
if ($string === '' || ctype_digit($string)) {
return $string;
}
$result = preg_replace_callback('/[^a-z0-9,\.\-_]/iSu', $this->htmlAttrMatcher, $string);
assert(is_string($result));
return $this->fromUtf8($result);
}
/** @inheritDoc */
public function escapeJs(string $string)
{
$string = $this->toUtf8($string);
if ($string === '' || ctype_digit($string)) {
return $string;
}
$result = preg_replace_callback('/[^a-z0-9,\._]/iSu', $this->jsMatcher, $string);
assert(is_string($result));
return $this->fromUtf8($result);
}
/** @inheritDoc */
public function escapeUrl(string $string)
{
return rawurlencode($string);
}
/** @inheritDoc */
public function escapeCss(string $string)
{
$string = $this->toUtf8($string);
if ($string === '' || ctype_digit($string)) {
return $string;
}
$result = preg_replace_callback('/[^a-z0-9]/iSu', $this->cssMatcher, $string);
assert(is_string($result));
return $this->fromUtf8($result);
}
/**
* Callback function for preg_replace_callback that applies HTML Attribute
* escaping to all matches.
*
* @param array<array-key, string> $matches
* @return string
*/
protected function htmlAttrMatcher($matches)
{
$chr = $matches[0];
$ord = ord($chr);
/**
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
if (
($ord <= 0x1f && $chr !== "\t" && $chr !== "\n" && $chr !== "\r")
|| ($ord >= 0x7f && $ord <= 0x9f)
) {
return '&#xFFFD;';
}
/**
* Check if the current character to escape has a name entity we should
* replace it with while grabbing the integer value of the character.
*/
if (strlen($chr) > 1) {
$chr = $this->convertEncoding($chr, 'UTF-32BE', 'UTF-8');
}
$hex = bin2hex($chr);
$ord = hexdec($hex);
if (isset(static::$htmlNamedEntityMap[$ord])) {
return '&' . static::$htmlNamedEntityMap[$ord] . ';';
}
/**
* Per OWASP recommendations, we'll use upper hex entities
* for any other characters where a named entity does not exist.
*/
if ($ord > 255) {
return sprintf('&#x%04X;', $ord);
}
return sprintf('&#x%02X;', $ord);
}
/**
* Callback function for preg_replace_callback that applies Javascript
* escaping to all matches.
*
* @param array<array-key, string> $matches
* @return string
*/
protected function jsMatcher($matches)
{
$chr = $matches[0];
if (strlen($chr) === 1) {
return sprintf('\\x%02X', ord($chr));
}
$chr = $this->convertEncoding($chr, 'UTF-16BE', 'UTF-8');
$hex = strtoupper(bin2hex($chr));
if (strlen($hex) <= 4) {
return sprintf('\\u%04s', $hex);
}
$highSurrogate = substr($hex, 0, 4);
$lowSurrogate = substr($hex, 4, 4);
return sprintf('\\u%04s\\u%04s', $highSurrogate, $lowSurrogate);
}
/**
* Callback function for preg_replace_callback that applies CSS
* escaping to all matches.
*
* @param array<array-key, string> $matches
* @return string
*/
protected function cssMatcher($matches)
{
$chr = $matches[0];
if (strlen($chr) === 1) {
$ord = ord($chr);
} else {
$chr = $this->convertEncoding($chr, 'UTF-32BE', 'UTF-8');
$ord = hexdec(bin2hex($chr));
}
return sprintf('\\%X ', $ord);
}
/**
* Converts a string to UTF-8 from the base encoding. The base encoding is set via this
*
* @param string $string
* @throws Exception\RuntimeException
* @return string
*/
protected function toUtf8($string)
{
if ($this->getEncoding() === 'utf-8') {
$result = $string;
} else {
$result = $this->convertEncoding($string, 'UTF-8', $this->getEncoding());
}
if (! $this->isUtf8($result)) {
throw new Exception\RuntimeException(
sprintf('String to be escaped was not valid UTF-8 or could not be converted: %s', $result)
);
}
return $result;
}
/**
* Converts a string from UTF-8 to the base encoding. The base encoding is set via this
*
* @param string $string
* @return string
*/
protected function fromUtf8($string)
{
if ($this->getEncoding() === 'utf-8') {
return $string;
}
return $this->convertEncoding($string, $this->getEncoding(), 'UTF-8');
}
/**
* Checks if a given string appears to be valid UTF-8 or not.
*
* @param string $string
* @return bool
*/
protected function isUtf8($string)
{
return $string === '' || preg_match('/^./su', $string);
}
/**
* Encoding conversion helper which wraps mb_convert_encoding
*
* @param string $string
* @param string $to
* @param array|string $from
* @return string
*/
protected function convertEncoding($string, $to, $from)
{
$result = mb_convert_encoding($string, $to, $from);
if ($result === false) {
return ''; // return non-fatal blank string on encoding errors from users
}
return $result;
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Laminas\Escaper;
/**
* Interface for context specific methods for use in secure output escaping
*/
interface EscaperInterface
{
/**
* Escape a string for the HTML Body context where there are very few characters
* of special meaning. Internally this will use htmlspecialchars().
*
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeHtml(string $string);
/**
* Escape a string for the HTML Attribute context. We use an extended set of characters
* to escape that are not covered by htmlspecialchars() to cover cases where an attribute
* might be unquoted or quoted illegally (e.g. backticks are valid quotes for IE).
*
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeHtmlAttr(string $string);
/**
* Escape a string for the Javascript context. This does not use json_encode(). An extended
* set of characters are escaped beyond ECMAScript's rules for Javascript literal string
* escaping in order to prevent misinterpretation of Javascript as HTML leading to the
* injection of special characters and entities. The escaping used should be tolerant
* of cases where HTML escaping was not applied on top of Javascript escaping correctly.
* Backslash escaping is not used as it still leaves the escaped character as-is and so
* is not useful in a HTML context.
*
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeJs(string $string);
/**
* Escape a string for the URI or Parameter contexts. This should not be used to escape
* an entire URI - only a subcomponent being inserted. The function is a simple proxy
* to rawurlencode() which now implements RFC 3986 since PHP 5.3 completely.
*
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeUrl(string $string);
/**
* Escape a string for the CSS context. CSS escaping can be applied to any string being
* inserted into CSS and escapes everything except alphanumerics.
*
* @return ($string is non-empty-string ? non-empty-string : string)
*/
public function escapeCss(string $string);
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Laminas\Escaper\Exception;
use Throwable;
interface ExceptionInterface extends Throwable
{
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Laminas\Escaper\Exception;
/**
* Invalid argument exception
*/
class InvalidArgumentException extends \InvalidArgumentException implements
ExceptionInterface
{
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Laminas\Escaper\Exception;
/**
* Invalid argument exception
*/
class RuntimeException extends \RuntimeException implements
ExceptionInterface
{
}
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2020 Laminas Project a Series of LF Projects, LLC.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- Neither the name of Laminas Foundation nor the names of its contributors may
be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+659
View File
@@ -0,0 +1,659 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint;
/**
* @psalm-type PhpTokenArray = array{int, string, int}
* @psalm-type PhpToken = string|PhpTokenArray
* @psalm-type CallParameter = array{
* name: string,
* path: string,
* expression: bool,
* literal: bool,
* new_without_parens: bool,
* }
*/
class CallFinder
{
private static array $ignore = [
T_CLOSE_TAG => true,
T_COMMENT => true,
T_DOC_COMMENT => true,
T_INLINE_HTML => true,
T_OPEN_TAG => true,
T_OPEN_TAG_WITH_ECHO => true,
T_WHITESPACE => true,
];
/**
* Things we need to do specially for operator tokens:
* - Refuse to strip spaces around them
* - Wrap the access path in parentheses if there
* are any of these in the final short parameter.
*/
private static array $operator = [
T_AND_EQUAL => true,
T_BOOLEAN_AND => true,
T_BOOLEAN_OR => true,
T_ARRAY_CAST => true,
T_BOOL_CAST => true,
T_CLONE => true,
T_CONCAT_EQUAL => true,
T_DEC => true,
T_DIV_EQUAL => true,
T_DOUBLE_CAST => true,
T_FUNCTION => true,
T_INC => true,
T_INCLUDE => true,
T_INCLUDE_ONCE => true,
T_INSTANCEOF => true,
T_INT_CAST => true,
T_IS_EQUAL => true,
T_IS_GREATER_OR_EQUAL => true,
T_IS_IDENTICAL => true,
T_IS_NOT_EQUAL => true,
T_IS_NOT_IDENTICAL => true,
T_IS_SMALLER_OR_EQUAL => true,
T_LOGICAL_AND => true,
T_LOGICAL_OR => true,
T_LOGICAL_XOR => true,
T_MINUS_EQUAL => true,
T_MOD_EQUAL => true,
T_MUL_EQUAL => true,
T_OBJECT_CAST => true,
T_OR_EQUAL => true,
T_PLUS_EQUAL => true,
T_REQUIRE => true,
T_REQUIRE_ONCE => true,
T_SL => true,
T_SL_EQUAL => true,
T_SR => true,
T_SR_EQUAL => true,
T_STRING_CAST => true,
T_UNSET_CAST => true,
T_XOR_EQUAL => true,
T_POW => true,
T_POW_EQUAL => true,
T_SPACESHIP => true,
T_DOUBLE_ARROW => true,
T_FN => true,
T_COALESCE_EQUAL => true,
'!' => true,
'%' => true,
'&' => true,
'*' => true,
'+' => true,
'-' => true,
'.' => true,
'/' => true,
':' => true,
'<' => true,
'=' => true,
'>' => true,
'?' => true,
'^' => true,
'|' => true,
'~' => true,
];
private static array $preserve_spaces = [
T_CLASS => true,
T_NEW => true,
];
private static array $strip = [
'(' => true,
')' => true,
'[' => true,
']' => true,
'{' => true,
'}' => true,
T_OBJECT_OPERATOR => true,
T_DOUBLE_COLON => true,
T_NS_SEPARATOR => true,
];
private static array $classcalls = [
T_DOUBLE_COLON => true,
T_OBJECT_OPERATOR => true,
];
private static array $namespace = [
T_STRING => true,
];
/**
* @psalm-param callable-array|callable-string $function
*
* @psalm-return list<array{parameters: list<CallParameter>, modifiers: list<PhpToken>}>
*
* @return array List of matching calls on the relevant line
*/
public static function getFunctionCalls(string $source, int $line, $function): array
{
static $up = [
'(' => true,
'[' => true,
'{' => true,
T_CURLY_OPEN => true,
T_DOLLAR_OPEN_CURLY_BRACES => true,
];
static $down = [
')' => true,
']' => true,
'}' => true,
];
static $modifiers = [
'!' => true,
'@' => true,
'~' => true,
'+' => true,
'-' => true,
];
static $identifier = [
T_DOUBLE_COLON => true,
T_STRING => true,
T_NS_SEPARATOR => true,
];
if (KINT_PHP80) {
$up[T_ATTRIBUTE] = true;
self::$operator[T_MATCH] = true;
self::$strip[T_NULLSAFE_OBJECT_OPERATOR] = true;
self::$classcalls[T_NULLSAFE_OBJECT_OPERATOR] = true;
self::$namespace[T_NAME_FULLY_QUALIFIED] = true;
self::$namespace[T_NAME_QUALIFIED] = true;
self::$namespace[T_NAME_RELATIVE] = true;
$identifier[T_NAME_FULLY_QUALIFIED] = true;
$identifier[T_NAME_QUALIFIED] = true;
$identifier[T_NAME_RELATIVE] = true;
}
if (!KINT_PHP84) {
self::$operator[T_NEW] = true; // @codeCoverageIgnore
}
/** @psalm-var list<PhpToken> */
$tokens = \token_get_all($source);
$function_calls = [];
// Performance optimization preventing backwards loops
/** @psalm-var array<PhpToken|null> */
$prev_tokens = [null, null, null];
if (\is_array($function)) {
$class = \explode('\\', $function[0]);
$class = \strtolower(\end($class));
$function = \strtolower($function[1]);
} else {
$class = null;
/**
* @psalm-suppress RedundantFunctionCallGivenDocblockType
* Psalm bug #11075
*/
$function = \strtolower($function);
}
// Loop through tokens
foreach ($tokens as $index => $token) {
if (!\is_array($token)) {
continue;
}
if ($token[2] > $line) {
break;
}
// Store the last real tokens for later
if (isset(self::$ignore[$token[0]])) {
continue;
}
$prev_tokens = [$prev_tokens[1], $prev_tokens[2], $token];
// The logic for 7.3 through 8.1 is far more complicated.
// This should speed things up without making a lot more work for us
if (KINT_PHP82 && $line !== $token[2]) {
continue;
}
// Check if it's the right type to be the function we're looking for
if (!isset(self::$namespace[$token[0]])) {
continue;
}
$ns = \explode('\\', \strtolower($token[1]));
if (\end($ns) !== $function) {
continue;
}
// Check if it's a function call
$nextReal = self::realTokenIndex($tokens, $index);
if ('(' !== ($tokens[$nextReal] ?? null)) {
continue;
}
// Check if it matches the signature
if (null === $class) {
if (null !== $prev_tokens[1] && isset(self::$classcalls[$prev_tokens[1][0]])) {
continue;
}
} else {
if (null === $prev_tokens[1] || T_DOUBLE_COLON !== $prev_tokens[1][0]) {
continue;
}
if (null === $prev_tokens[0] || !isset(self::$namespace[$prev_tokens[0][0]])) {
continue;
}
// All self::$namespace tokens are T_ constants
/**
* @psalm-var PhpTokenArray $prev_tokens[0]
* Psalm bug #746 (wontfix)
*/
$ns = \explode('\\', \strtolower($prev_tokens[0][1]));
if (\end($ns) !== $class) {
continue;
}
}
$last_line = $token[2];
$depth = 1; // The depth respective to the function call
$offset = $nextReal + 1; // The start of the function call
$instring = false; // Whether we're in a string or not
$realtokens = false; // Whether the current scope contains anything meaningful or not
$paramrealtokens = false; // Whether the current parameter contains anything meaningful
$params = []; // All our collected parameters
$shortparam = []; // The short version of the parameter
$param_start = $offset; // The distance to the start of the parameter
// Loop through the following tokens until the function call ends
while (isset($tokens[$offset])) {
$token = $tokens[$offset];
if (\is_array($token)) {
$last_line = $token[2];
}
if (!isset(self::$ignore[$token[0]]) && !isset($down[$token[0]])) {
$paramrealtokens = $realtokens = true;
}
// If it's a token that makes us to up a level, increase the depth
if (isset($up[$token[0]])) {
if (1 === $depth) {
$shortparam[] = $token;
$realtokens = false;
}
++$depth;
} elseif (isset($down[$token[0]])) {
--$depth;
// If this brings us down to the parameter level, and we've had
// real tokens since going up, fill the $shortparam with an ellipsis
if (1 === $depth) {
if ($realtokens) {
$shortparam[] = '...';
}
$shortparam[] = $token;
}
} elseif ('"' === $token || 'b"' === $token) {
// Strings use the same symbol for up and down, but we can
// only ever be inside one string, so just use a bool for that
if ($instring) {
--$depth;
if (1 === $depth) {
$shortparam[] = '...';
}
} else {
++$depth;
}
$instring = !$instring;
$shortparam[] = $token;
} elseif (1 === $depth) {
if (',' === $token[0]) {
$params[] = [
'full' => \array_slice($tokens, $param_start, $offset - $param_start),
'short' => $shortparam,
];
$shortparam = [];
$paramrealtokens = false;
$param_start = $offset + 1;
} elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
$quote = $token[1][0];
if ('b' === $quote) {
$quote = $token[1][1];
if (\strlen($token[1]) > 3) {
$token[1] = 'b'.$quote.'...'.$quote;
}
} else {
if (\strlen($token[1]) > 2) {
$token[1] = $quote.'...'.$quote;
}
}
$shortparam[] = $token;
} else {
$shortparam[] = $token;
}
}
// Depth has dropped to 0 (So we've hit the closing paren)
if ($depth <= 0) {
if ($paramrealtokens) {
$params[] = [
'full' => \array_slice($tokens, $param_start, $offset - $param_start),
'short' => $shortparam,
];
}
break;
}
++$offset;
}
// If we're not passed (or at) the line at the end
// of the function call, we're too early so skip it
// Only applies to < 8.2 since we check line explicitly above that
if (!KINT_PHP82 && $last_line < $line) {
continue; // @codeCoverageIgnore
}
$formatted_parameters = [];
// Format the final output parameters
foreach ($params as $param) {
$name = self::tokensFormatted($param['short']);
$path = self::tokensToString(self::tokensTrim($param['full']));
$expression = false;
$literal = false;
$new_without_parens = false;
foreach ($name as $token) {
if (self::tokenIsOperator($token)) {
$expression = true;
break;
}
}
// As of 8.4 new is only an expression when parentheses are
// omitted. In that case we can cheat and add them ourselves.
//
// > PHP interprets the first expression after new as a class name
// per https://wiki.php.net/rfc/new_without_parentheses
if (KINT_PHP84 && !$expression && T_NEW === $name[0][0]) {
$had_name_token = false;
$new_without_parens = true;
foreach ($name as $token) {
if (T_NEW === $token[0]) {
continue;
}
if (isset(self::$ignore[$token[0]])) {
continue;
}
if (T_CLASS === $token[0]) {
$new_without_parens = false;
break;
}
if ('(' === $token && $had_name_token) {
$new_without_parens = false;
break;
}
$had_name_token = true;
}
}
if (!$expression && 1 === \count($name)) {
switch ($name[0][0]) {
case T_CONSTANT_ENCAPSED_STRING:
case T_LNUMBER:
case T_DNUMBER:
$literal = true;
break;
case T_STRING:
switch (\strtolower($name[0][1])) {
case 'null':
case 'true':
case 'false':
$literal = true;
}
}
$name = self::tokensToString($name);
} else {
$name = self::tokensToString($name);
if (!$expression) {
switch (\strtolower($name)) {
case 'array()':
case '[]':
$literal = true;
break;
}
}
}
$formatted_parameters[] = [
'name' => $name,
'path' => $path,
'expression' => $expression,
'literal' => $literal,
'new_without_parens' => $new_without_parens,
];
}
// Skip first-class callables
if (KINT_PHP81 && 1 === \count($formatted_parameters) && '...' === \reset($formatted_parameters)['path']) {
continue;
}
// Get the modifiers
--$index;
while (isset($tokens[$index])) {
if (!isset(self::$ignore[$tokens[$index][0]]) && !isset($identifier[$tokens[$index][0]])) {
break;
}
--$index;
}
$mods = [];
while (isset($tokens[$index])) {
if (isset(self::$ignore[$tokens[$index][0]])) {
--$index;
continue;
}
if (isset($modifiers[$tokens[$index][0]])) {
$mods[] = $tokens[$index];
--$index;
continue;
}
break;
}
$function_calls[] = [
'parameters' => $formatted_parameters,
'modifiers' => $mods,
];
}
return $function_calls;
}
private static function realTokenIndex(array $tokens, int $index): ?int
{
++$index;
while (isset($tokens[$index])) {
if (!isset(self::$ignore[$tokens[$index][0]])) {
return $index;
}
++$index;
}
return null;
}
/**
* We need a separate method to check if tokens are operators because we
* occasionally add "..." to short parameter versions. If we simply check
* for `$token[0]` then "..." will incorrectly match the "." operator.
*
* @psalm-param PhpToken $token The token to check
*/
private static function tokenIsOperator($token): bool
{
return '...' !== $token && isset(self::$operator[$token[0]]);
}
/**
* @psalm-param PhpToken $token The token to check
*/
private static function tokenPreserveWhitespace($token): bool
{
return self::tokenIsOperator($token) || isset(self::$preserve_spaces[$token[0]]);
}
private static function tokensToString(array $tokens): string
{
$out = '';
foreach ($tokens as $token) {
if (\is_string($token)) {
$out .= $token;
} else {
$out .= $token[1];
}
}
return $out;
}
private static function tokensTrim(array $tokens): array
{
foreach ($tokens as $index => $token) {
if (isset(self::$ignore[$token[0]])) {
unset($tokens[$index]);
} else {
break;
}
}
$tokens = \array_reverse($tokens);
foreach ($tokens as $index => $token) {
if (isset(self::$ignore[$token[0]])) {
unset($tokens[$index]);
} else {
break;
}
}
return \array_reverse($tokens);
}
private static function tokensFormatted(array $tokens): array
{
$tokens = self::tokensTrim($tokens);
$space = false;
$attribute = false;
// Keep space between "strip" symbols for different behavior for matches or closures
// Normally we want to strip spaces between strip tokens: $x{...}[...]
// However with closures and matches we don't: function (...) {...}
$ignorestrip = false;
$output = [];
$last = null;
if (T_FUNCTION === $tokens[0][0] ||
T_FN === $tokens[0][0] ||
(KINT_PHP80 && T_MATCH === $tokens[0][0])
) {
$ignorestrip = true;
}
foreach ($tokens as $index => $token) {
if (isset(self::$ignore[$token[0]])) {
if ($space) {
continue;
}
$next = self::realTokenIndex($tokens, $index);
if (null === $next) {
// This should be impossible, since we always call tokensTrim first
break; // @codeCoverageIgnore
}
$next = $tokens[$next];
/**
* @psalm-var PhpToken $last
* Since we call tokensTrim we know we can't be here without a $last
*/
if ($attribute && ']' === $last[0]) {
$attribute = false;
} elseif (!$ignorestrip && isset(self::$strip[$last[0]]) && !self::tokenPreserveWhitespace($next)) {
continue;
}
if (!$ignorestrip && isset(self::$strip[$next[0]]) && !self::tokenPreserveWhitespace($last)) {
continue;
}
$token[1] = ' ';
$space = true;
} else {
if (KINT_PHP80 && null !== $last && T_ATTRIBUTE === $last[0]) {
$attribute = true;
}
$space = false;
$last = $token;
}
$output[] = $token;
}
return $output;
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint;
use Kint\Parser\Parser;
use Kint\Renderer\RendererInterface;
use Kint\Value\Context\ContextInterface;
interface FacadeInterface
{
public function __construct(Parser $p, RendererInterface $r);
public function setStatesFromStatics(array $statics): void;
public function setStatesFromCallInfo(array $info): void;
/**
* Renders a list of vars including the pre and post renders.
*
* @param array $vars Data to dump
* @param ContextInterface[] $base The base contexts
*/
public function dumpAll(array $vars, array $base): string;
}
+672
View File
@@ -0,0 +1,672 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint;
use InvalidArgumentException;
use Kint\Parser\ConstructablePluginInterface;
use Kint\Parser\Parser;
use Kint\Parser\PluginInterface;
use Kint\Renderer\ConstructableRendererInterface;
use Kint\Renderer\RendererInterface;
use Kint\Renderer\TextRenderer;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\UninitializedValue;
/**
* @psalm-consistent-constructor
* Psalm bug #8523
*
* @psalm-import-type CallParameter from CallFinder
*
* @psalm-type KintMode = Kint::MODE_*|bool
*
* @psalm-api
*/
class Kint implements FacadeInterface
{
public const MODE_RICH = 'r';
public const MODE_TEXT = 't';
public const MODE_CLI = 'c';
public const MODE_PLAIN = 'p';
/**
* @var mixed Kint mode
*
* false: Disabled
* true: Enabled, default mode selection
* other: Manual mode selection
*
* @psalm-var KintMode
*/
public static $enabled_mode = true;
/**
* Default mode.
*
* @psalm-var KintMode
*/
public static $mode_default = self::MODE_RICH;
/**
* Default mode in CLI with cli_detection on.
*
* @psalm-var KintMode
*/
public static $mode_default_cli = self::MODE_CLI;
/**
* @var bool enable detection when Kint is command line.
*
* Formats output with whitespace only; does not HTML-escape it
*/
public static bool $cli_detection = true;
/**
* @var bool Return output instead of echoing
*/
public static bool $return = false;
/**
* @var int depth limit for array/object traversal. 0 for no limit
*/
public static int $depth_limit = 7;
/**
* @var bool expand all trees by default for rich view
*/
public static bool $expanded = false;
/**
* @var bool whether to display where kint was called from
*/
public static bool $display_called_from = true;
/**
* @var array Kint aliases. Add debug functions in Kint wrappers here to fix modifiers and backtraces
*/
public static array $aliases = [
[self::class, 'dump'],
[self::class, 'trace'],
[self::class, 'dumpAll'],
];
/**
* @psalm-var array<RendererInterface|class-string<ConstructableRendererInterface>>
*
* Array of modes to renderer class names
*/
public static array $renderers = [
self::MODE_RICH => Renderer\RichRenderer::class,
self::MODE_PLAIN => Renderer\PlainRenderer::class,
self::MODE_TEXT => TextRenderer::class,
self::MODE_CLI => Renderer\CliRenderer::class,
];
/**
* @psalm-var array<PluginInterface|class-string<ConstructablePluginInterface>>
*/
public static array $plugins = [
\Kint\Parser\ArrayLimitPlugin::class,
\Kint\Parser\ArrayObjectPlugin::class,
\Kint\Parser\Base64Plugin::class,
\Kint\Parser\BinaryPlugin::class,
\Kint\Parser\BlacklistPlugin::class,
\Kint\Parser\ClassHooksPlugin::class,
\Kint\Parser\ClassMethodsPlugin::class,
\Kint\Parser\ClassStaticsPlugin::class,
\Kint\Parser\ClassStringsPlugin::class,
\Kint\Parser\ClosurePlugin::class,
\Kint\Parser\ColorPlugin::class,
\Kint\Parser\DateTimePlugin::class,
\Kint\Parser\DomPlugin::class,
\Kint\Parser\EnumPlugin::class,
\Kint\Parser\FsPathPlugin::class,
\Kint\Parser\HtmlPlugin::class,
\Kint\Parser\IteratorPlugin::class,
\Kint\Parser\JsonPlugin::class,
\Kint\Parser\MicrotimePlugin::class,
\Kint\Parser\MysqliPlugin::class,
// \Kint\Parser\SerializePlugin::class,
\Kint\Parser\SimpleXMLElementPlugin::class,
\Kint\Parser\SplFileInfoPlugin::class,
\Kint\Parser\StreamPlugin::class,
\Kint\Parser\TablePlugin::class,
\Kint\Parser\ThrowablePlugin::class,
\Kint\Parser\TimestampPlugin::class,
\Kint\Parser\ToStringPlugin::class,
\Kint\Parser\TracePlugin::class,
\Kint\Parser\XmlPlugin::class,
];
protected Parser $parser;
protected RendererInterface $renderer;
public function __construct(Parser $p, RendererInterface $r)
{
$this->parser = $p;
$this->renderer = $r;
}
public function setParser(Parser $p): void
{
$this->parser = $p;
}
public function getParser(): Parser
{
return $this->parser;
}
public function setRenderer(RendererInterface $r): void
{
$this->renderer = $r;
}
public function getRenderer(): RendererInterface
{
return $this->renderer;
}
public function setStatesFromStatics(array $statics): void
{
$this->renderer->setStatics($statics);
$this->parser->setDepthLimit($statics['depth_limit'] ?? 0);
$this->parser->clearPlugins();
if (!isset($statics['plugins'])) {
return;
}
$plugins = [];
foreach ($statics['plugins'] as $plugin) {
if ($plugin instanceof PluginInterface) {
$plugins[] = $plugin;
} elseif (\is_string($plugin) && \is_a($plugin, ConstructablePluginInterface::class, true)) {
$plugins[] = new $plugin($this->parser);
}
}
$plugins = $this->renderer->filterParserPlugins($plugins);
foreach ($plugins as $plugin) {
try {
$this->parser->addPlugin($plugin);
} catch (InvalidArgumentException $e) {
\trigger_error(
'Plugin '.Utils::errorSanitizeString(\get_class($plugin)).' could not be added to a Kint parser: '.Utils::errorSanitizeString($e->getMessage()),
E_USER_WARNING
);
}
}
}
public function setStatesFromCallInfo(array $info): void
{
$this->renderer->setCallInfo($info);
if (isset($info['modifiers']) && \is_array($info['modifiers']) && \in_array('+', $info['modifiers'], true)) {
$this->parser->setDepthLimit(0);
}
$this->parser->setCallerClass($info['caller']['class'] ?? null);
}
public function dumpAll(array $vars, array $base): string
{
if (\array_keys($vars) !== \array_keys($base)) {
throw new InvalidArgumentException('Kint::dumpAll requires arrays of identical size and keys as arguments');
}
if ([] === $vars) {
return $this->dumpNothing();
}
$output = $this->renderer->preRender();
foreach ($vars as $key => $_) {
if (!$base[$key] instanceof ContextInterface) {
throw new InvalidArgumentException('Kint::dumpAll requires all elements of the second argument to be ContextInterface instances');
}
$output .= $this->dumpVar($vars[$key], $base[$key]);
}
$output .= $this->renderer->postRender();
return $output;
}
protected function dumpNothing(): string
{
$output = $this->renderer->preRender();
$output .= $this->renderer->render(new UninitializedValue(new BaseContext('No argument')));
$output .= $this->renderer->postRender();
return $output;
}
/**
* Dumps and renders a var.
*
* @param mixed &$var Data to dump
*/
protected function dumpVar(&$var, ContextInterface $c): string
{
return $this->renderer->render(
$this->parser->parse($var, $c)
);
}
/**
* Gets all static settings at once.
*
* @return array Current static settings
*/
public static function getStatics(): array
{
return [
'aliases' => static::$aliases,
'cli_detection' => static::$cli_detection,
'depth_limit' => static::$depth_limit,
'display_called_from' => static::$display_called_from,
'enabled_mode' => static::$enabled_mode,
'expanded' => static::$expanded,
'mode_default' => static::$mode_default,
'mode_default_cli' => static::$mode_default_cli,
'plugins' => static::$plugins,
'renderers' => static::$renderers,
'return' => static::$return,
];
}
/**
* Creates a Kint instance based on static settings.
*
* @param array $statics array of statics as returned by getStatics
*/
public static function createFromStatics(array $statics): ?FacadeInterface
{
$mode = false;
if (isset($statics['enabled_mode'])) {
$mode = $statics['enabled_mode'];
if (true === $mode && isset($statics['mode_default'])) {
$mode = $statics['mode_default'];
if (PHP_SAPI === 'cli' && !empty($statics['cli_detection']) && isset($statics['mode_default_cli'])) {
$mode = $statics['mode_default_cli'];
}
}
}
if (false === $mode) {
return null;
}
$renderer = null;
if (isset($statics['renderers'][$mode])) {
if ($statics['renderers'][$mode] instanceof RendererInterface) {
$renderer = $statics['renderers'][$mode];
}
if (\is_a($statics['renderers'][$mode], ConstructableRendererInterface::class, true)) {
$renderer = new $statics['renderers'][$mode]();
}
}
$renderer ??= new TextRenderer();
return new static(new Parser(), $renderer);
}
/**
* Creates base contexts given parameter info.
*
* @psalm-param list<CallParameter> $params
*
* @return BaseContext[] Base contexts for the arguments
*/
public static function getBasesFromParamInfo(array $params, int $argc): array
{
$bases = [];
for ($i = 0; $i < $argc; ++$i) {
$param = $params[$i] ?? null;
if (!empty($param['literal'])) {
$name = 'literal';
} else {
$name = $param['name'] ?? '$'.$i;
}
if (isset($param['path'])) {
$access_path = $param['path'];
if ($param['expression']) {
$access_path = '('.$access_path.')';
} elseif ($param['new_without_parens']) {
$access_path .= '()';
}
} else {
$access_path = '$'.$i;
}
$base = new BaseContext($name);
$base->access_path = $access_path;
$bases[] = $base;
}
return $bases;
}
/**
* Gets call info from the backtrace, alias, and argument count.
*
* Aliases must be normalized beforehand (Utils::normalizeAliases)
*
* @param array $aliases Call aliases as found in Kint::$aliases
* @param array[] $trace Backtrace
* @param array $args Arguments
*
* @return array Call info
*
* @psalm-param list<non-empty-array> $trace
*/
public static function getCallInfo(array $aliases, array $trace, array $args): array
{
$found = false;
$callee = null;
$caller = null;
$miniTrace = [];
foreach ($trace as $frame) {
if (Utils::traceFrameIsListed($frame, $aliases)) {
$found = true;
$miniTrace = [];
}
if (!Utils::traceFrameIsListed($frame, ['spl_autoload_call'])) {
$miniTrace[] = $frame;
}
}
if ($found) {
$callee = \reset($miniTrace) ?: null;
$caller = \next($miniTrace) ?: null;
}
foreach ($miniTrace as $index => $frame) {
if ((0 === $index && $callee === $frame) || isset($frame['file'], $frame['line'])) {
unset($frame['object'], $frame['args']);
$miniTrace[$index] = $frame;
} else {
unset($miniTrace[$index]);
}
}
$miniTrace = \array_values($miniTrace);
$call = static::getSingleCall($callee ?: [], $args);
$ret = [
'params' => null,
'modifiers' => [],
'callee' => $callee,
'caller' => $caller,
'trace' => $miniTrace,
];
if (null !== $call) {
$ret['params'] = $call['parameters'];
$ret['modifiers'] = $call['modifiers'];
}
return $ret;
}
/**
* Dumps a backtrace.
*
* Functionally equivalent to Kint::dump(1) or Kint::dump(debug_backtrace(true))
*
* @return int|string
*/
public static function trace()
{
if (false === static::$enabled_mode) {
return 0;
}
static::$aliases = Utils::normalizeAliases(static::$aliases);
$call_info = static::getCallInfo(static::$aliases, \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), []);
$statics = static::getStatics();
if (\in_array('~', $call_info['modifiers'], true)) {
$statics['enabled_mode'] = static::MODE_TEXT;
}
$kintstance = static::createFromStatics($statics);
if (!$kintstance) {
return 0;
}
if (\in_array('-', $call_info['modifiers'], true)) {
while (\ob_get_level()) {
\ob_end_clean();
}
}
$kintstance->setStatesFromStatics($statics);
$kintstance->setStatesFromCallInfo($call_info);
$trimmed_trace = [];
$trace = \debug_backtrace();
foreach ($trace as $frame) {
if (Utils::traceFrameIsListed($frame, static::$aliases)) {
$trimmed_trace = [];
}
$trimmed_trace[] = $frame;
}
\array_shift($trimmed_trace);
$base = new BaseContext('Kint\\Kint::trace()');
$base->access_path = 'debug_backtrace()';
$output = $kintstance->dumpAll([$trimmed_trace], [$base]);
if (static::$return || \in_array('@', $call_info['modifiers'], true)) {
return $output;
}
echo $output;
if (\in_array('-', $call_info['modifiers'], true)) {
\flush(); // @codeCoverageIgnore
}
return 0;
}
/**
* Dumps some data.
*
* Functionally equivalent to Kint::dump(1) or Kint::dump(debug_backtrace())
*
* @psalm-param mixed ...$args
*
* @return int|string
*/
public static function dump(...$args)
{
if (false === static::$enabled_mode) {
return 0;
}
static::$aliases = Utils::normalizeAliases(static::$aliases);
$call_info = static::getCallInfo(static::$aliases, \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), $args);
$statics = static::getStatics();
if (\in_array('~', $call_info['modifiers'], true)) {
$statics['enabled_mode'] = static::MODE_TEXT;
}
$kintstance = static::createFromStatics($statics);
if (!$kintstance) {
return 0;
}
if (\in_array('-', $call_info['modifiers'], true)) {
while (\ob_get_level()) {
\ob_end_clean();
}
}
$kintstance->setStatesFromStatics($statics);
$kintstance->setStatesFromCallInfo($call_info);
$bases = static::getBasesFromParamInfo($call_info['params'] ?? [], \count($args));
$output = $kintstance->dumpAll(\array_values($args), $bases);
if (static::$return || \in_array('@', $call_info['modifiers'], true)) {
return $output;
}
echo $output;
if (\in_array('-', $call_info['modifiers'], true)) {
\flush(); // @codeCoverageIgnore
}
return 0;
}
/**
* Returns specific function call info from a stack trace frame, or null if no match could be found.
*
* @param array $frame The stack trace frame in question
* @param array $args The arguments
*
* @return ?array params and modifiers, or null if a specific call could not be determined
*/
protected static function getSingleCall(array $frame, array $args): ?array
{
if (
!isset($frame['file'], $frame['line'], $frame['function']) ||
!\is_readable($frame['file']) ||
false === ($source = \file_get_contents($frame['file']))
) {
return null;
}
if (empty($frame['class'])) {
$callfunc = $frame['function'];
} else {
$callfunc = [$frame['class'], $frame['function']];
}
$calls = CallFinder::getFunctionCalls($source, $frame['line'], $callfunc);
$argc = \count($args);
$return = null;
foreach ($calls as $call) {
$is_unpack = false;
// Handle argument unpacking as a last resort
foreach ($call['parameters'] as $i => &$param) {
if (0 === \strpos($param['name'], '...')) {
$is_unpack = true;
// If we're on the last param
if ($i < $argc && $i === \count($call['parameters']) - 1) {
unset($call['parameters'][$i]);
if (Utils::isAssoc($args)) {
// Associated unpacked arrays can be accessed by key
$keys = \array_slice(\array_keys($args), $i);
foreach ($keys as $key) {
$call['parameters'][] = [
'name' => \substr($param['name'], 3).'['.\var_export($key, true).']',
'path' => \substr($param['path'], 3).'['.\var_export($key, true).']',
'expression' => false,
'literal' => false,
'new_without_parens' => false,
];
}
} else {
// Numeric unpacked arrays have their order blown away like a pass
// through array_values so we can't access them directly at all
for ($j = 0; $j + $i < $argc; ++$j) {
$call['parameters'][] = [
'name' => 'array_values('.\substr($param['name'], 3).')['.$j.']',
'path' => 'array_values('.\substr($param['path'], 3).')['.$j.']',
'expression' => false,
'literal' => false,
'new_without_parens' => false,
];
}
}
$call['parameters'] = \array_values($call['parameters']);
} else {
$call['parameters'] = \array_slice($call['parameters'], 0, $i);
}
break;
}
if ($i >= $argc) {
continue 2;
}
}
if ($is_unpack || \count($call['parameters']) === $argc) {
if (null === $return) {
$return = $call;
} else {
// If we have multiple calls on the same line with the same amount of arguments,
// we can't be sure which it is so just return null and let them figure it out
return null;
}
}
}
return $return;
}
}
+20
View File
@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+48
View File
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
abstract class AbstractPlugin implements ConstructablePluginInterface
{
private Parser $parser;
public function __construct(Parser $parser)
{
$this->parser = $parser;
}
public function setParser(Parser $p): void
{
$this->parser = $p;
}
protected function getParser(): Parser
{
return $this->parser;
}
}
+169
View File
@@ -0,0 +1,169 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use InvalidArgumentException;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\ProfileRepresentation;
use Kint\Value\Representation\ValueRepresentation;
class ArrayLimitPlugin extends AbstractPlugin implements PluginBeginInterface
{
/**
* Maximum size of arrays before limiting.
*/
public static int $trigger = 1000;
/**
* Maximum amount of items to show in a limited array.
*/
public static int $limit = 50;
/**
* Don't limit arrays with string keys.
*/
public static bool $numeric_only = true;
public function __construct(Parser $p)
{
if (self::$limit < 0) {
throw new InvalidArgumentException('ArrayLimitPlugin::$limit can not be lower than 0');
}
if (self::$limit >= self::$trigger) {
throw new InvalidArgumentException('ArrayLimitPlugin::$limit can not be lower than ArrayLimitPlugin::$trigger');
}
parent::__construct($p);
}
public function getTypes(): array
{
return ['array'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_BEGIN;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
$parser = $this->getParser();
$pdepth = $parser->getDepthLimit();
if (!$pdepth) {
return null;
}
$cdepth = $c->getDepth();
if ($cdepth >= $pdepth - 1) {
return null;
}
if (\count($var) < self::$trigger) {
return null;
}
if (self::$numeric_only && Utils::isAssoc($var)) {
return null;
}
$slice = \array_slice($var, 0, self::$limit, true);
$array = $parser->parse($slice, $c);
if (!$array instanceof ArrayValue) {
return null;
}
$base = new BaseContext($c->getName());
$base->depth = $pdepth - 1;
$base->access_path = $c->getAccessPath();
$slice = \array_slice($var, self::$limit, null, true);
$slice = $parser->parse($slice, $base);
if (!$slice instanceof ArrayValue) {
return null;
}
foreach ($slice->getContents() as $child) {
$this->replaceDepthLimit($child, $cdepth + 1);
}
$out = new ArrayValue($c, \count($var), \array_merge($array->getContents(), $slice->getContents()));
$out->flags = $array->flags;
// Explicitly copy over profile plugin
$arrayp = $array->getRepresentation('profiling');
$slicep = $slice->getRepresentation('profiling');
if ($arrayp instanceof ProfileRepresentation && $slicep instanceof ProfileRepresentation) {
$out->addRepresentation(new ProfileRepresentation($arrayp->complexity + $slicep->complexity));
}
// Add contents. Check is in case some bad plugin empties both $slice and $array
if ($contents = $out->getContents()) {
$out->addRepresentation(new ContainerRepresentation('Contents', $contents, null, true));
}
return $out;
}
protected function replaceDepthLimit(AbstractValue $v, int $depth): void
{
$c = $v->getContext();
if ($c instanceof BaseContext) {
$c->depth = $depth;
}
$pdepth = $this->getParser()->getDepthLimit();
if (($v->flags & AbstractValue::FLAG_DEPTH_LIMIT) && $pdepth && $depth < $pdepth) {
$v->flags = $v->flags & ~AbstractValue::FLAG_DEPTH_LIMIT | AbstractValue::FLAG_ARRAY_LIMIT;
}
$reps = $v->getRepresentations();
foreach ($reps as $rep) {
if ($rep instanceof ContainerRepresentation) {
foreach ($rep->getContents() as $child) {
$this->replaceDepthLimit($child, $depth + 1);
}
} elseif ($rep instanceof ValueRepresentation) {
$this->replaceDepthLimit($rep->getValue(), $depth + 1);
}
}
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use ArrayObject;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ContextInterface;
class ArrayObjectPlugin extends AbstractPlugin implements PluginBeginInterface
{
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_BEGIN;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
if (!$var instanceof ArrayObject) {
return null;
}
$flags = $var->getFlags();
if (ArrayObject::STD_PROP_LIST === $flags) {
return null;
}
$parser = $this->getParser();
$var->setFlags(ArrayObject::STD_PROP_LIST);
$v = $parser->parse($var, $c);
$var->setFlags($flags);
return $v;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Representation\ValueRepresentation;
use Kint\Value\StringValue;
class Base64Plugin extends AbstractPlugin implements PluginCompleteInterface
{
/**
* The minimum length before a string will be considered for base64 decoding.
*/
public static int $min_length_hard = 16;
/**
* The minimum length before the base64 decoding will take precedence.
*/
public static int $min_length_soft = 50;
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (\strlen($var) < self::$min_length_hard || \strlen($var) % 4) {
return $v;
}
if (\preg_match('/^[A-Fa-f0-9]+$/', $var)) {
return $v;
}
if (!\preg_match('/^[A-Za-z0-9+\\/=]+$/', $var)) {
return $v;
}
$data = \base64_decode($var, true);
if (false === $data) {
return $v;
}
$c = $v->getContext();
$base = new BaseContext('base64_decode('.$c->getName().')');
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'base64_decode('.$ap.')';
}
$data = $this->getParser()->parse($data, $base);
$data->flags |= AbstractValue::FLAG_GENERATED;
if (!$data instanceof StringValue || false === $data->getEncoding()) {
return $v;
}
$r = new ValueRepresentation('Base64', $data);
if (\strlen($var) > self::$min_length_soft) {
$v->addRepresentation($r, 0);
} else {
$v->addRepresentation($r);
}
return $v;
}
}
+54
View File
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\BinaryRepresentation;
use Kint\Value\StringValue;
class BinaryPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if ($v instanceof StringValue && false === $v->getEncoding()) {
$v->addRepresentation(new BinaryRepresentation($v->getValue(), true), 0);
}
return $v;
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ContextInterface;
use Kint\Value\InstanceValue;
use Psr\Container\ContainerInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
class BlacklistPlugin extends AbstractPlugin implements PluginBeginInterface
{
/**
* List of classes and interfaces to blacklist.
*
* @var class-string[]
*/
public static array $blacklist = [];
/**
* List of classes and interfaces to blacklist except when dumped directly.
*
* @var class-string[]
*/
public static array $shallow_blacklist = [
ContainerInterface::class,
EventDispatcherInterface::class,
];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_BEGIN;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
foreach (self::$blacklist as $class) {
if ($var instanceof $class) {
return $this->blacklistValue($var, $c);
}
}
if ($c->getDepth() <= 0) {
return null;
}
foreach (self::$shallow_blacklist as $class) {
if ($var instanceof $class) {
return $this->blacklistValue($var, $c);
}
}
return null;
}
/**
* @param object &$var
*/
protected function blacklistValue(&$var, ContextInterface $c): InstanceValue
{
$object = new InstanceValue($c, \get_class($var), \spl_object_hash($var), \spl_object_id($var));
$object->flags |= AbstractValue::FLAG_BLACKLIST;
return $object;
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\MethodContext;
use Kint\Value\Context\PropertyContext;
use Kint\Value\DeclaredCallableBag;
use Kint\Value\InstanceValue;
use Kint\Value\MethodValue;
use Kint\Value\Representation\ContainerRepresentation;
use ReflectionProperty;
class ClassHooksPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static bool $verbose = false;
/** @psalm-var array<class-string, array<string, MethodValue[]>> */
private array $cache = [];
/** @psalm-var array<class-string, array<string, MethodValue[]>> */
private array $cache_verbose = [];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
if (!KINT_PHP84) {
return Parser::TRIGGER_NONE; // @codeCoverageIgnore
}
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$v instanceof InstanceValue) {
return $v;
}
$props = $v->getRepresentation('properties');
if (!$props instanceof ContainerRepresentation) {
return $v;
}
foreach ($props->getContents() as $prop) {
$c = $prop->getContext();
if (!$c instanceof PropertyContext || PropertyContext::HOOK_NONE === $c->hooks) {
continue;
}
$cname = $c->getName();
$cowner = $c->owner_class;
if (!isset($this->cache_verbose[$cowner][$cname])) {
$ref = new ReflectionProperty($cowner, $cname);
$hooks = $ref->getHooks();
foreach ($hooks as $hook) {
if (!self::$verbose && false === $hook->getDocComment()) {
continue;
}
$m = new MethodValue(
new MethodContext($hook),
new DeclaredCallableBag($hook)
);
$this->cache_verbose[$cowner][$cname][] = $m;
if (false !== $hook->getDocComment()) {
$this->cache[$cowner][$cname][] = $m;
}
}
$this->cache[$cowner][$cname] ??= [];
if (self::$verbose) {
$this->cache_verbose[$cowner][$cname] ??= [];
}
}
$cache = self::$verbose ? $this->cache_verbose : $this->cache;
$cache = $cache[$cowner][$cname] ?? [];
if (\count($cache)) {
$prop->addRepresentation(new ContainerRepresentation('Hooks', $cache, 'propertyhooks'));
}
}
return $v;
}
}
+225
View File
@@ -0,0 +1,225 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\MethodContext;
use Kint\Value\DeclaredCallableBag;
use Kint\Value\InstanceValue;
use Kint\Value\MethodValue;
use Kint\Value\Representation\ContainerRepresentation;
use ReflectionClass;
use ReflectionMethod;
class ClassMethodsPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static bool $show_access_path = true;
/**
* Whether to go out of the way to show constructor paths
* when the instance isn't accessible.
*
* Disabling this improves performance.
*/
public static bool $show_constructor_path = false;
/** @psalm-var array<class-string, MethodValue[]> */
private array $instance_cache = [];
/** @psalm-var array<class-string, MethodValue[]> */
private array $static_cache = [];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
/**
* @psalm-template T of AbstractValue
*
* @psalm-param mixed $var
* @psalm-param T $v
*
* @psalm-return T
*/
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$v instanceof InstanceValue) {
return $v;
}
$class = $v->getClassName();
$scope = $this->getParser()->getCallerClass();
if ($contents = $this->getCachedMethods($class)) {
if (self::$show_access_path) {
if (null !== $v->getContext()->getAccessPath()) {
// If we have an access path we can generate them for the children
foreach ($contents as $key => $val) {
if ($val->getContext()->isAccessible($scope)) {
$val = clone $val;
$val->getContext()->setAccessPathFromParent($v);
$contents[$key] = $val;
}
}
} elseif (self::$show_constructor_path && isset($contents['__construct'])) {
// __construct is the only exception: The only non-static method
// that can be called without access to the parent instance.
// Technically I guess it really is a static method but so long
// as PHP continues to refer to it as a normal one so will we.
$val = $contents['__construct'];
if ($val->getContext()->isAccessible($scope)) {
$val = clone $val;
$val->getContext()->setAccessPathFromParent($v);
$contents['__construct'] = $val;
}
}
}
$v->addRepresentation(new ContainerRepresentation('Methods', $contents));
}
if ($contents = $this->getCachedStaticMethods($class)) {
$v->addRepresentation(new ContainerRepresentation('Static methods', $contents));
}
return $v;
}
/**
* @psalm-param class-string $class
*
* @psalm-return MethodValue[]
*/
private function getCachedMethods(string $class): array
{
if (!isset($this->instance_cache[$class])) {
$methods = [];
$r = new ReflectionClass($class);
$parent_methods = [];
if ($parent = \get_parent_class($class)) {
$parent_methods = $this->getCachedMethods($parent);
}
foreach ($r->getMethods() as $mr) {
if ($mr->isStatic()) {
continue;
}
$canon_name = \strtolower($mr->name);
if ($mr->isPrivate() && '__construct' !== $canon_name) {
$canon_name = \strtolower($mr->getDeclaringClass()->name).'::'.$canon_name;
}
if ($mr->getDeclaringClass()->name === $class) {
$method = new MethodValue(new MethodContext($mr), new DeclaredCallableBag($mr));
$methods[$canon_name] = $method;
unset($parent_methods[$canon_name]);
} elseif (isset($parent_methods[$canon_name])) {
$method = $parent_methods[$canon_name];
unset($parent_methods[$canon_name]);
if (!$method->getContext()->inherited) {
$method = clone $method;
$method->getContext()->inherited = true;
}
$methods[$canon_name] = $method;
} elseif ($mr->getDeclaringClass()->isInterface()) {
$c = new MethodContext($mr);
$c->inherited = true;
$methods[$canon_name] = new MethodValue($c, new DeclaredCallableBag($mr));
}
}
foreach ($parent_methods as $name => $method) {
if (!$method->getContext()->inherited) {
$method = clone $method;
$method->getContext()->inherited = true;
}
if ('__construct' === $name) {
$methods['__construct'] = $method;
} else {
$methods[] = $method;
}
}
$this->instance_cache[$class] = $methods;
}
return $this->instance_cache[$class];
}
/**
* @psalm-param class-string $class
*
* @psalm-return MethodValue[]
*/
private function getCachedStaticMethods(string $class): array
{
if (!isset($this->static_cache[$class])) {
$methods = [];
$r = new ReflectionClass($class);
$parent_methods = [];
if ($parent = \get_parent_class($class)) {
$parent_methods = $this->getCachedStaticMethods($parent);
}
foreach ($r->getMethods(ReflectionMethod::IS_STATIC) as $mr) {
$canon_name = \strtolower($mr->getDeclaringClass()->name.'::'.$mr->name);
if ($mr->getDeclaringClass()->name === $class) {
$method = new MethodValue(new MethodContext($mr), new DeclaredCallableBag($mr));
$methods[$canon_name] = $method;
} elseif (isset($parent_methods[$canon_name])) {
$methods[$canon_name] = $parent_methods[$canon_name];
} elseif ($mr->getDeclaringClass()->isInterface()) {
$c = new MethodContext($mr);
$c->inherited = true;
$methods[$canon_name] = new MethodValue($c, new DeclaredCallableBag($mr));
}
unset($parent_methods[$canon_name]);
}
$this->static_cache[$class] = $methods + $parent_methods;
}
return $this->static_cache[$class];
}
}
+230
View File
@@ -0,0 +1,230 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ClassConstContext;
use Kint\Value\Context\ClassDeclaredContext;
use Kint\Value\Context\StaticPropertyContext;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\UninitializedValue;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionProperty;
use UnitEnum;
class ClassStaticsPlugin extends AbstractPlugin implements PluginCompleteInterface
{
/** @psalm-var array<class-string, array<1|0, array<AbstractValue>>> */
private array $cache = [];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
/**
* @psalm-template T of AbstractValue
*
* @psalm-param mixed $var
* @psalm-param T $v
*
* @psalm-return T
*/
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$v instanceof InstanceValue) {
return $v;
}
$deep = 0 === $this->getParser()->getDepthLimit();
$r = new ReflectionClass($v->getClassName());
if ($statics = $this->getStatics($r, $v->getContext()->getDepth() + 1)) {
$v->addRepresentation(new ContainerRepresentation('Static properties', \array_values($statics), 'statics'));
}
if ($consts = $this->getCachedConstants($r, $deep)) {
$v->addRepresentation(new ContainerRepresentation('Class constants', \array_values($consts), 'constants'));
}
return $v;
}
/** @psalm-return array<AbstractValue> */
private function getStatics(ReflectionClass $r, int $depth): array
{
$cdepth = $depth ?: 1;
$class = $r->getName();
$parent = $r->getParentClass();
$parent_statics = $parent ? $this->getStatics($parent, $depth) : [];
$statics = [];
foreach ($r->getProperties(ReflectionProperty::IS_STATIC) as $pr) {
$canon_name = \strtolower($pr->getDeclaringClass()->name.'::'.$pr->name);
if ($pr->getDeclaringClass()->name === $class) {
$statics[$canon_name] = $this->buildStaticValue($pr, $cdepth);
} elseif (isset($parent_statics[$canon_name])) {
$statics[$canon_name] = $parent_statics[$canon_name];
unset($parent_statics[$canon_name]);
} else {
// This should never happen since abstract static properties can't exist
$statics[$canon_name] = $this->buildStaticValue($pr, $cdepth); // @codeCoverageIgnore
}
}
foreach ($parent_statics as $canon_name => $value) {
$statics[$canon_name] = $value;
}
return $statics;
}
private function buildStaticValue(ReflectionProperty $pr, int $depth): AbstractValue
{
$context = new StaticPropertyContext(
$pr->name,
$pr->getDeclaringClass()->name,
ClassDeclaredContext::ACCESS_PUBLIC
);
$context->depth = $depth;
$context->final = KINT_PHP84 && $pr->isFinal();
if ($pr->isProtected()) {
$context->access = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($pr->isPrivate()) {
$context->access = ClassDeclaredContext::ACCESS_PRIVATE;
}
$parser = $this->getParser();
if ($context->isAccessible($parser->getCallerClass())) {
$context->access_path = '\\'.$context->owner_class.'::$'.$context->name;
}
$pr->setAccessible(true);
/**
* @psalm-suppress TooFewArguments
* Appears to have been fixed in master.
*/
if (!$pr->isInitialized()) {
$context->access_path = null;
return new UninitializedValue($context);
}
$val = $pr->getValue();
$out = $this->getParser()->parse($val, $context);
$context->access_path = null;
return $out;
}
/** @psalm-return array<AbstractValue> */
private function getCachedConstants(ReflectionClass $r, bool $deep): array
{
$parser = $this->getParser();
$cdepth = $parser->getDepthLimit() ?: 1;
$deepkey = (int) $deep;
$class = $r->getName();
// Separate cache for dumping with/without depth limit
// This means we can do immediate depth limit on normal dumps
if (!isset($this->cache[$class][$deepkey])) {
$consts = [];
$parent_consts = [];
if ($parent = $r->getParentClass()) {
$parent_consts = $this->getCachedConstants($parent, $deep);
}
foreach ($r->getConstants() as $name => $val) {
$cr = new ReflectionClassConstant($class, $name);
// Skip enum constants
if ($cr->class === $class && \is_a($class, UnitEnum::class, true)) {
continue;
}
$canon_name = \strtolower($cr->getDeclaringClass()->name.'::'.$name);
if ($cr->getDeclaringClass()->name === $class) {
$context = $this->buildConstContext($cr);
$context->depth = $cdepth;
$consts[$canon_name] = $parser->parse($val, $context);
$context->access_path = null;
} elseif (isset($parent_consts[$canon_name])) {
$consts[$canon_name] = $parent_consts[$canon_name];
} else {
$context = $this->buildConstContext($cr);
$context->depth = $cdepth;
$consts[$canon_name] = $parser->parse($val, $context);
$context->access_path = null;
}
unset($parent_consts[$canon_name]);
}
$this->cache[$class][$deepkey] = $consts + $parent_consts;
}
return $this->cache[$class][$deepkey];
}
private function buildConstContext(ReflectionClassConstant $cr): ClassConstContext
{
$context = new ClassConstContext(
$cr->name,
$cr->getDeclaringClass()->name,
ClassDeclaredContext::ACCESS_PUBLIC
);
$context->final = KINT_PHP81 && $cr->isFinal();
if ($cr->isProtected()) {
$context->access = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($cr->isPrivate()) {
$context->access = ClassDeclaredContext::ACCESS_PRIVATE;
} else {
$context->access_path = '\\'.$context->owner_class.'::'.$context->name;
}
return $context;
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\InstanceValue;
use ReflectionClass;
class ClassStringsPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static array $blacklist = [];
protected ClassMethodsPlugin $methods_plugin;
protected ClassStaticsPlugin $statics_plugin;
public function __construct(Parser $parser)
{
parent::__construct($parser);
$this->methods_plugin = new ClassMethodsPlugin($parser);
$this->statics_plugin = new ClassStaticsPlugin($parser);
}
public function setParser(Parser $p): void
{
parent::setParser($p);
$this->methods_plugin->setParser($p);
$this->statics_plugin->setParser($p);
}
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
$c = $v->getContext();
if ($c->getDepth() > 0) {
return $v;
}
if (!\class_exists($var, true)) {
return $v;
}
if (\in_array($var, self::$blacklist, true)) {
return $v;
}
$r = new ReflectionClass($var);
$fakeC = new BaseContext($c->getName());
$fakeC->access_path = null;
$fakeV = new InstanceValue($fakeC, $r->getName(), 'badhash', -1);
$fakeVar = null;
$fakeV = $this->methods_plugin->parseComplete($fakeVar, $fakeV, Parser::TRIGGER_SUCCESS);
$fakeV = $this->statics_plugin->parseComplete($fakeVar, $fakeV, Parser::TRIGGER_SUCCESS);
foreach (['methods', 'static_methods', 'statics', 'constants'] as $rep) {
if ($rep = $fakeV->getRepresentation($rep)) {
$v->addRepresentation($rep);
}
}
return $v;
}
}
+93
View File
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Closure;
use Kint\Value\AbstractValue;
use Kint\Value\ClosureValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Representation\ContainerRepresentation;
use ReflectionFunction;
use ReflectionReference;
class ClosurePlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$var instanceof Closure) {
return $v;
}
$c = $v->getContext();
$object = new ClosureValue($c, $var);
$object->flags = $v->flags;
$object->appendRepresentations($v->getRepresentations());
$object->removeRepresentation('properties');
$closure = new ReflectionFunction($var);
$statics = [];
if ($v = $closure->getClosureThis()) {
$statics = ['this' => $v];
}
$statics = $statics + $closure->getStaticVariables();
$cdepth = $c->getDepth();
if (\count($statics)) {
$statics_parsed = [];
$parser = $this->getParser();
foreach ($statics as $name => $_) {
$base = new BaseContext('$'.$name);
$base->depth = $cdepth + 1;
$base->reference = null !== ReflectionReference::fromArrayElement($statics, $name);
$statics_parsed[$name] = $parser->parse($statics[$name], $base);
}
$object->addRepresentation(new ContainerRepresentation('Uses', $statics_parsed), 0);
}
return $object;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use InvalidArgumentException;
use Kint\Value\AbstractValue;
use Kint\Value\ColorValue;
use Kint\Value\Representation\ColorRepresentation;
use Kint\Value\StringValue;
class ColorPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (\strlen($var) > 32) {
return $v;
}
if (!$v instanceof StringValue) {
return $v;
}
$trimmed = \strtolower(\trim($var));
if (!isset(ColorRepresentation::$color_map[$trimmed]) && !\preg_match('/^(?:(?:rgb|hsl)[^\\)]{6,}\\)|#[0-9a-fA-F]{3,8})$/', $trimmed)) {
return $v;
}
try {
$rep = new ColorRepresentation($var);
} catch (InvalidArgumentException $e) {
return $v;
}
$out = new ColorValue($v->getContext(), $v->getValue(), $v->getEncoding());
$out->flags = $v->flags;
$out->appendRepresentations($v->getRepresentations());
$out->removeRepresentation('contents');
$out->addRepresentation($rep, 0);
return $out;
}
}
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
interface ConstructablePluginInterface extends PluginInterface
{
public function __construct(Parser $p);
}
+67
View File
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use DateTimeInterface;
use Error;
use Kint\Value\AbstractValue;
use Kint\Value\DateTimeValue;
use Kint\Value\InstanceValue;
class DateTimePlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$var instanceof DateTimeInterface || !$v instanceof InstanceValue) {
return $v;
}
try {
$dtv = new DateTimeValue($v->getContext(), $var);
} catch (Error $e) {
// Only happens if someone makes a DateTimeInterface with a private __clone
return $v;
}
$dtv->setChildren($v->getChildren());
$dtv->flags = $v->flags;
$dtv->appendRepresentations($v->getRepresentations());
return $dtv;
}
}
+543
View File
@@ -0,0 +1,543 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Dom\Attr;
use Dom\CharacterData;
use Dom\Document;
use Dom\DocumentType;
use Dom\Element;
use Dom\HTMLElement;
use Dom\NamedNodeMap;
use Dom\Node;
use Dom\NodeList;
use DOMAttr;
use DOMCharacterData;
use DOMDocumentType;
use DOMElement;
use DOMNamedNodeMap;
use DOMNode;
use DOMNodeList;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ClassDeclaredContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Context\PropertyContext;
use Kint\Value\DomNodeListValue;
use Kint\Value\DomNodeValue;
use Kint\Value\FixedWidthValue;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\StringValue;
use LogicException;
class DomPlugin extends AbstractPlugin implements PluginBeginInterface
{
/**
* Reflection doesn't work below 8.1, also it won't show readonly status.
*
* In order to ensure this is stable enough we're only going to provide
* properties for element and node. If subclasses like attr or document
* have their own fields then tough shit we're not showing them.
*
* @psalm-var non-empty-array<string, bool> Property names to readable status
*/
public const NODE_PROPS = [
'nodeType' => true,
'nodeName' => true,
'baseURI' => true,
'isConnected' => true,
'ownerDocument' => true,
'parentNode' => true,
'parentElement' => true,
'childNodes' => true,
'firstChild' => true,
'lastChild' => true,
'previousSibling' => true,
'nextSibling' => true,
'nodeValue' => true,
'textContent' => false,
];
/**
* @psalm-var non-empty-array<string, bool> Property names to readable status
*/
public const ELEMENT_PROPS = [
'namespaceURI' => true,
'prefix' => true,
'localName' => true,
'tagName' => true,
'id' => false,
'className' => false,
'classList' => true,
'attributes' => true,
'firstElementChild' => true,
'lastElementChild' => true,
'childElementCount' => true,
'previousElementSibling' => true,
'nextElementSibling' => true,
'innerHTML' => false,
'outerHTML' => false,
'substitutedNodeValue' => false,
];
public const DOM_NS_VERSIONS = [
'outerHTML' => KINT_PHP85,
];
/**
* @psalm-var non-empty-array<string, bool> Property names to readable status
*/
public const DOMNODE_PROPS = [
'nodeName' => true,
'nodeValue' => false,
'nodeType' => true,
'parentNode' => true,
'parentElement' => true,
'childNodes' => true,
'firstChild' => true,
'lastChild' => true,
'previousSibling' => true,
'nextSibling' => true,
'attributes' => true,
'isConnected' => true,
'ownerDocument' => true,
'namespaceURI' => true,
'prefix' => false,
'localName' => true,
'baseURI' => true,
'textContent' => false,
];
/**
* @psalm-var non-empty-array<string, bool> Property names to readable status
*/
public const DOMELEMENT_PROPS = [
'tagName' => true,
'className' => false,
'id' => false,
'schemaTypeInfo' => true,
'firstElementChild' => true,
'lastElementChild' => true,
'childElementCount' => true,
'previousElementSibling' => true,
'nextElementSibling' => true,
];
public const DOM_VERSIONS = [
'parentElement' => KINT_PHP83,
'isConnected' => KINT_PHP83,
'className' => KINT_PHP83,
'id' => KINT_PHP83,
'firstElementChild' => KINT_PHP80,
'lastElementChild' => KINT_PHP80,
'childElementCount' => KINT_PHP80,
'previousElementSibling' => KINT_PHP80,
'nextElementSibling' => KINT_PHP80,
];
/**
* List of properties to skip parsing.
*
* The properties of a Dom\Node can do a *lot* of damage to debuggers. The
* Dom\Node contains not one, not two, but 13 different ways to recurse into itself:
* * parentNode
* * firstChild
* * lastChild
* * previousSibling
* * nextSibling
* * parentElement
* * firstElementChild
* * lastElementChild
* * previousElementSibling
* * nextElementSibling
* * childNodes
* * attributes
* * ownerDocument
*
* All of this combined: the tiny SVGs used as the caret in Kint were already
* enough to make parsing and rendering take over a second, and send memory
* usage over 128 megs, back in the old DOM API. So we blacklist every field
* we don't strictly need and hope that that's good enough.
*
* In retrospect -- this is probably why print_r does the same
*
* @psalm-var array<string, true>
*/
public static array $blacklist = [
'parentNode' => true,
'firstChild' => true,
'lastChild' => true,
'previousSibling' => true,
'nextSibling' => true,
'firstElementChild' => true,
'lastElementChild' => true,
'parentElement' => true,
'previousElementSibling' => true,
'nextElementSibling' => true,
'ownerDocument' => true,
];
/**
* Show all properties and methods.
*/
public static bool $verbose = false;
protected ClassMethodsPlugin $methods_plugin;
protected ClassStaticsPlugin $statics_plugin;
public function __construct(Parser $parser)
{
parent::__construct($parser);
$this->methods_plugin = new ClassMethodsPlugin($parser);
$this->statics_plugin = new ClassStaticsPlugin($parser);
}
public function setParser(Parser $p): void
{
parent::setParser($p);
$this->methods_plugin->setParser($p);
$this->statics_plugin->setParser($p);
}
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_BEGIN;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
// Attributes and chardata (Which is parent of comments and text
// nodes) don't need children or attributes of their own
if ($var instanceof Attr || $var instanceof CharacterData || $var instanceof DOMAttr || $var instanceof DOMCharacterData) {
return $this->parseText($var, $c);
}
if ($var instanceof NamedNodeMap || $var instanceof NodeList || $var instanceof DOMNamedNodeMap || $var instanceof DOMNodeList) {
return $this->parseList($var, $c);
}
if ($var instanceof Node || $var instanceof DOMNode) {
return $this->parseNode($var, $c);
}
return null;
}
/** @psalm-param Node|DOMNode $var */
private function parseProperty(object $var, string $prop, ContextInterface $c): AbstractValue
{
if (!isset($var->{$prop})) {
return new FixedWidthValue($c, null);
}
$parser = $this->getParser();
$value = $var->{$prop};
if (\is_scalar($value)) {
return $parser->parse($value, $c);
}
if (isset(self::$blacklist[$prop])) {
$b = new InstanceValue($c, \get_class($value), \spl_object_hash($value), \spl_object_id($value));
$b->flags |= AbstractValue::FLAG_GENERATED | AbstractValue::FLAG_BLACKLIST;
return $b;
}
// Everything we can handle in parseBegin
if ($value instanceof Attr || $value instanceof CharacterData || $value instanceof DOMAttr || $value instanceof DOMCharacterData || $value instanceof NamedNodeMap || $value instanceof NodeList || $value instanceof DOMNamedNodeMap || $value instanceof DOMNodeList || $value instanceof Node || $value instanceof DOMNode) {
$out = $this->parseBegin($value, $c);
}
if (!isset($out)) {
// Shouldn't ever happen
$out = $parser->parse($value, $c); // @codeCoverageIgnore
}
$out->flags |= AbstractValue::FLAG_GENERATED;
return $out;
}
/** @psalm-param Attr|CharacterData|DOMAttr|DOMCharacterData $var */
private function parseText(object $var, ContextInterface $c): AbstractValue
{
if ($c instanceof BaseContext && null !== $c->access_path) {
$c->access_path .= '->nodeValue';
}
return $this->parseProperty($var, 'nodeValue', $c);
}
/** @psalm-param NamedNodeMap|NodeList|DOMNamedNodeMap|DOMNodeList $var */
private function parseList(object $var, ContextInterface $c): InstanceValue
{
if ($var instanceof NodeList || $var instanceof DOMNodeList) {
$v = new DomNodeListValue($c, $var);
} else {
$v = new InstanceValue($c, \get_class($var), \spl_object_hash($var), \spl_object_id($var));
}
$parser = $this->getParser();
$pdepth = $parser->getDepthLimit();
// Depth limit
// Use empty iterator representation since we need it to point out depth limits
if (($var instanceof NodeList || $var instanceof DOMNodeList) && $pdepth && $c->getDepth() >= $pdepth) {
$v->flags |= AbstractValue::FLAG_DEPTH_LIMIT;
return $v;
}
if (self::$verbose) {
$v = $this->methods_plugin->parseComplete($var, $v, Parser::TRIGGER_SUCCESS);
$v = $this->statics_plugin->parseComplete($var, $v, Parser::TRIGGER_SUCCESS);
}
if (0 === $var->length) {
$v->setChildren([]);
return $v;
}
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
$contents = [];
foreach ($var as $key => $item) {
$base_obj = new BaseContext($item->nodeName);
$base_obj->depth = $cdepth + 1;
if ($var instanceof NamedNodeMap || $var instanceof DOMNamedNodeMap) {
if (null !== $ap) {
$base_obj->access_path = $ap.'['.\var_export($item->nodeName, true).']';
}
} else { // NodeList
if (null !== $ap) {
$base_obj->access_path = $ap.'['.\var_export($key, true).']';
}
}
if ($item instanceof HTMLElement) {
$base_obj->name = $item->localName;
}
$item = $parser->parse($item, $base_obj);
$item->flags |= AbstractValue::FLAG_GENERATED;
$contents[] = $item;
}
$v->setChildren($contents);
if ($contents) {
$v->addRepresentation(new ContainerRepresentation('Iterator', $contents), 0);
}
return $v;
}
/** @psalm-param Node|DOMNode $var */
private function parseNode(object $var, ContextInterface $c): DomNodeValue
{
$class = \get_class($var);
$pdepth = $this->getParser()->getDepthLimit();
if ($pdepth && $c->getDepth() >= $pdepth) {
$v = new DomNodeValue($c, $var);
$v->flags |= AbstractValue::FLAG_DEPTH_LIMIT;
return $v;
}
if (($var instanceof DocumentType || $var instanceof DOMDocumentType) && $c instanceof BaseContext && $c->name === $var->nodeName) {
$c->name = '!DOCTYPE '.$c->name;
}
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
$properties = [];
$children = [];
$attributes = [];
foreach (self::getKnownProperties($var) as $prop => $readonly) {
$prop_c = new PropertyContext($prop, $class, ClassDeclaredContext::ACCESS_PUBLIC);
$prop_c->depth = $cdepth + 1;
$prop_c->readonly = KINT_PHP81 && $readonly;
if (null !== $ap) {
$prop_c->access_path = $ap.'->'.$prop;
}
$properties[] = $prop_obj = $this->parseProperty($var, $prop, $prop_c);
if ('childNodes' === $prop) {
if (!$prop_obj instanceof DomNodeListValue) {
throw new LogicException('childNodes property parsed incorrectly'); // @codeCoverageIgnore
}
$children = self::getChildren($prop_obj);
} elseif ('attributes' === $prop) {
$attributes = $prop_obj->getRepresentation('iterator');
$attributes = $attributes instanceof ContainerRepresentation ? $attributes->getContents() : [];
} elseif ('classList' === $prop) {
if ($iter = $prop_obj->getRepresentation('iterator')) {
$prop_obj->removeRepresentation($iter);
$prop_obj->addRepresentation($iter, 0);
}
}
}
$v = new DomNodeValue($c, $var);
// If we're in text mode, we can see children through the childNodes property
$v->setChildren($properties);
if ($children) {
$v->addRepresentation(new ContainerRepresentation('Children', $children, null, true));
}
if ($attributes) {
$v->addRepresentation(new ContainerRepresentation('Attributes', $attributes));
}
if (self::$verbose) {
$v->addRepresentation(new ContainerRepresentation('Properties', $properties));
$v = $this->methods_plugin->parseComplete($var, $v, Parser::TRIGGER_SUCCESS);
$v = $this->statics_plugin->parseComplete($var, $v, Parser::TRIGGER_SUCCESS);
}
return $v;
}
/**
* @psalm-param Node|DOMNode $var
*
* @psalm-return non-empty-array<string, bool>
*/
public static function getKnownProperties(object $var): array
{
if ($var instanceof Node) {
$known_properties = self::NODE_PROPS;
if ($var instanceof Element) {
$known_properties += self::ELEMENT_PROPS;
}
if ($var instanceof Document) {
$known_properties['textContent'] = true;
}
if ($var instanceof Attr || $var instanceof CharacterData) {
$known_properties['nodeValue'] = false;
}
foreach (self::DOM_NS_VERSIONS as $key => $val) {
/**
* @psalm-var bool $val
* Psalm bug #4509
*/
if (false === $val) {
unset($known_properties[$key]); // @codeCoverageIgnore
}
}
} else {
$known_properties = self::DOMNODE_PROPS;
if ($var instanceof DOMElement) {
$known_properties += self::DOMELEMENT_PROPS;
}
foreach (self::DOM_VERSIONS as $key => $val) {
/**
* @psalm-var bool $val
* Psalm bug #4509
*/
if (false === $val) {
unset($known_properties[$key]); // @codeCoverageIgnore
}
}
}
/** @psalm-var non-empty-array $known_properties */
if (!self::$verbose) {
$known_properties = \array_intersect_key($known_properties, [
'nodeValue' => null,
'childNodes' => null,
'attributes' => null,
]);
}
return $known_properties;
}
/** @psalm-return list<AbstractValue> */
private static function getChildren(DomNodeListValue $property): array
{
if (0 === $property->getLength()) {
return [];
}
if ($property->flags & AbstractValue::FLAG_DEPTH_LIMIT) {
return [$property];
}
$list_items = $property->getChildren();
if (null === $list_items) {
// This is here for psalm but all DomNodeListValue should
// either be depth_limit or have array children
return []; // @codeCoverageIgnore
}
$children = [];
foreach ($list_items as $node) {
// Remove text nodes if theyre empty
if ($node instanceof StringValue && '#text' === $node->getContext()->getName()) {
/**
* @psalm-suppress InvalidArgument
* Psalm bug #11055
*/
if (\ctype_space($node->getValue()) || '' === $node->getValue()) {
continue;
}
}
$children[] = $node;
}
return $children;
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\EnumValue;
use Kint\Value\Representation\ContainerRepresentation;
use UnitEnum;
class EnumPlugin extends AbstractPlugin implements PluginCompleteInterface
{
private array $cache = [];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
if (!KINT_PHP81) {
return Parser::TRIGGER_NONE;
}
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$var instanceof UnitEnum) {
return $v;
}
$c = $v->getContext();
$class = \get_class($var);
if (!isset($this->cache[$class])) {
$contents = [];
foreach ($var->cases() as $case) {
$base = new BaseContext($case->name);
$base->access_path = '\\'.$class.'::'.$case->name;
$base->depth = $c->getDepth() + 1;
$contents[] = new EnumValue($base, $case);
}
/** @psalm-var non-empty-array<EnumValue> $contents */
$this->cache[$class] = new ContainerRepresentation('Enum values', $contents, 'enum');
}
$object = new EnumValue($c, $var);
$object->flags = $v->flags;
$object->appendRepresentations($v->getRepresentations());
$object->addRepresentation($this->cache[$class], 0);
return $object;
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\SplFileInfoRepresentation;
use SplFileInfo;
use TypeError;
class FsPathPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static array $blacklist = ['/', '.'];
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (\strlen($var) > 2048) {
return $v;
}
if (!\preg_match('/[\\/\\'.DIRECTORY_SEPARATOR.']/', $var)) {
return $v;
}
if (\preg_match('/[?<>"*|]/', $var)) {
return $v;
}
try {
if (!@\file_exists($var)) {
return $v;
}
} catch (TypeError $e) {// @codeCoverageIgnore
// Only possible in PHP 7
return $v; // @codeCoverageIgnore
}
if (\in_array($var, self::$blacklist, true)) {
return $v;
}
$v->addRepresentation(new SplFileInfoRepresentation(new SplFileInfo($var)), 0);
return $v;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Dom\HTMLDocument;
use DOMException;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\ValueRepresentation;
class HtmlPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
if (!KINT_PHP84) {
return Parser::TRIGGER_NONE; // @codeCoverageIgnore
}
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if ('<!doctype html>' !== \strtolower(\substr($var, 0, 15))) {
return $v;
}
try {
$html = HTMLDocument::createFromString($var, LIBXML_NOERROR);
} catch (DOMException $e) { // @codeCoverageIgnore
return $v; // @codeCoverageIgnore
}
$c = $v->getContext();
$base = new BaseContext('childNodes');
$base->depth = $c->getDepth();
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = '\\Dom\\HTMLDocument::createFromString('.$ap.')->childNodes';
}
$out = $this->getParser()->parse($html->childNodes, $base);
$iter = $out->getRepresentation('iterator');
if ($out->flags & AbstractValue::FLAG_DEPTH_LIMIT) {
$out->flags |= AbstractValue::FLAG_GENERATED;
$v->addRepresentation(new ValueRepresentation('HTML', $out), 0);
} elseif ($iter instanceof ContainerRepresentation) {
$v->addRepresentation(new ContainerRepresentation('HTML', $iter->getContents()), 0);
}
return $v;
}
}
+146
View File
@@ -0,0 +1,146 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Dom\NamedNodeMap;
use Dom\NodeList;
use DOMNamedNodeMap;
use DOMNodeList;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\ValueRepresentation;
use Kint\Value\UninitializedValue;
use mysqli_result;
use PDOStatement;
use SimpleXMLElement;
use SplFileObject;
use Throwable;
use Traversable;
class IteratorPlugin extends AbstractPlugin implements PluginCompleteInterface
{
/**
* List of classes and interfaces to blacklist.
*
* Certain classes (Such as PDOStatement) irreversibly lose information
* when traversed. Others are just huge. Either way, put them in here
* and you won't have to worry about them being parsed.
*
* @psalm-var class-string[]
*/
public static array $blacklist = [
NamedNodeMap::class,
NodeList::class,
DOMNamedNodeMap::class,
DOMNodeList::class,
mysqli_result::class,
PDOStatement::class,
SimpleXMLElement::class,
SplFileObject::class,
];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$var instanceof Traversable || !$v instanceof InstanceValue || $v->getRepresentation('iterator')) {
return $v;
}
$c = $v->getContext();
foreach (self::$blacklist as $class) {
/**
* @psalm-suppress RedundantCondition
* Psalm bug #11076
*/
if ($var instanceof $class) {
$base = new BaseContext($class.' Iterator Contents');
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'iterator_to_array('.$ap.', false)';
}
$b = new UninitializedValue($base);
$b->flags |= AbstractValue::FLAG_BLACKLIST;
$v->addRepresentation(new ValueRepresentation('Iterator', $b));
return $v;
}
}
try {
$data = \iterator_to_array($var, false);
} catch (Throwable $t) {
return $v;
}
if (!\count($data)) {
return $v;
}
$base = new BaseContext('Iterator Contents');
$base->depth = $c->getDepth();
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'iterator_to_array('.$ap.', false)';
}
$iter_val = $this->getParser()->parse($data, $base);
// Since we didn't get TRIGGER_DEPTH_LIMIT and set the iterator to the
// same depth we can assume at least 1 level deep will exist
if ($iter_val instanceof ArrayValue && $iterator_items = $iter_val->getContents()) {
$r = new ContainerRepresentation('Iterator', $iterator_items);
$iterator_items = \array_values($iterator_items);
} else {
$r = new ValueRepresentation('Iterator', $iter_val);
$iterator_items = [$iter_val];
}
if ((bool) $v->getChildren()) {
$v->addRepresentation($r);
} else {
$v->setChildren($iterator_items);
$v->addRepresentation($r, 0);
}
return $v;
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use JsonException;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\ValueRepresentation;
class JsonPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!isset($var[0]) || ('{' !== $var[0] && '[' !== $var[0])) {
return $v;
}
try {
$json = \json_decode($var, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
return $v;
}
$json = (array) $json;
$c = $v->getContext();
$base = new BaseContext('JSON Decode');
$base->depth = $c->getDepth();
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'json_decode('.$ap.', true)';
}
$json = $this->getParser()->parse($json, $base);
if ($json instanceof ArrayValue && (~$json->flags & AbstractValue::FLAG_DEPTH_LIMIT) && $contents = $json->getContents()) {
foreach ($contents as $value) {
$value->flags |= AbstractValue::FLAG_GENERATED;
}
$v->addRepresentation(new ContainerRepresentation('Json', $contents), 0);
} else {
$json->flags |= AbstractValue::FLAG_GENERATED;
$v->addRepresentation(new ValueRepresentation('Json', $json), 0);
}
return $v;
}
}
+125
View File
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\MicrotimeValue;
use Kint\Value\Representation\MicrotimeRepresentation;
class MicrotimePlugin extends AbstractPlugin implements PluginCompleteInterface
{
private static ?array $last = null;
private static ?float $start = null;
private static int $times = 0;
private static ?string $group = null;
public function getTypes(): array
{
return ['string', 'double'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
$c = $v->getContext();
if ($c->getDepth() > 0) {
return $v;
}
if (\is_string($var)) {
if ('microtime()' !== $c->getName() || !\preg_match('/^0\\.[0-9]{8} [0-9]{10}$/', $var)) {
return $v;
}
$usec = (int) \substr($var, 2, 6);
$sec = (int) \substr($var, 11, 10);
} else {
if ('microtime(...)' !== $c->getName()) {
return $v;
}
$sec = (int) \floor($var);
$usec = $var - $sec;
$usec = (int) \floor($usec * 1000000);
}
$time = $sec + ($usec / 1000000);
if (null !== self::$last) {
$last_time = self::$last[0] + (self::$last[1] / 1000000);
$lap = $time - $last_time;
++self::$times;
} else {
$lap = null;
self::$start = $time;
}
self::$last = [$sec, $usec];
if (null !== $lap) {
$total = $time - self::$start;
$r = new MicrotimeRepresentation($sec, $usec, self::getGroup(), $lap, $total, self::$times);
} else {
$r = new MicrotimeRepresentation($sec, $usec, self::getGroup());
}
$out = new MicrotimeValue($v);
$out->removeRepresentation('contents');
$out->addRepresentation($r);
return $out;
}
/** @psalm-api */
public static function clean(): void
{
self::$last = null;
self::$start = null;
self::$times = 0;
self::newGroup();
}
private static function getGroup(): string
{
if (null === self::$group) {
return self::newGroup();
}
return self::$group;
}
private static function newGroup(): string
{
return self::$group = \bin2hex(\random_bytes(4));
}
}
+176
View File
@@ -0,0 +1,176 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\PropertyContext;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\ContainerRepresentation;
use mysqli;
use Throwable;
/**
* Adds support for mysqli object parsing.
*
* Due to the way mysqli is implemented in PHP, this will cause
* warnings on certain mysqli objects if screaming is enabled.
*/
class MysqliPlugin extends AbstractPlugin implements PluginCompleteInterface
{
// These 'properties' are actually globals
public const ALWAYS_READABLE = [
'client_version' => true,
'connect_errno' => true,
'connect_error' => true,
];
// These are readable on empty mysqli objects, but not on failed connections
public const EMPTY_READABLE = [
'client_info' => true,
'errno' => true,
'error' => true,
];
// These are only readable on connected mysqli objects
public const CONNECTED_READABLE = [
'affected_rows' => true,
'error_list' => true,
'field_count' => true,
'host_info' => true,
'info' => true,
'insert_id' => true,
'server_info' => true,
'server_version' => true,
'sqlstate' => true,
'protocol_version' => true,
'thread_id' => true,
'warning_count' => true,
];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_COMPLETE;
}
/**
* Before 8.1: Properties were nulls when cast to array
* After 8.1: Properties are readonly and uninitialized when cast to array (Aka missing).
*/
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$var instanceof mysqli || !$v instanceof InstanceValue) {
return $v;
}
$props = $v->getRepresentation('properties');
if (!$props instanceof ContainerRepresentation) {
return $v;
}
/**
* @psalm-var ?string $var->sqlstate
* @psalm-var ?string $var->client_info
* Psalm bug #4502
*/
try {
$connected = \is_string(@$var->sqlstate);
} catch (Throwable $t) {
$connected = false;
}
try {
$empty = !$connected && \is_string(@$var->client_info);
} catch (Throwable $t) { // @codeCoverageIgnore
// Only possible in PHP 8.0. Before 8.0 there's no exception,
// after 8.1 there are no failed connection objects
$empty = false; // @codeCoverageIgnore
}
$parser = $this->getParser();
$new_contents = [];
foreach ($props->getContents() as $key => $obj) {
$new_contents[$key] = $obj;
$c = $obj->getContext();
if (!$c instanceof PropertyContext) {
continue;
}
if (isset(self::CONNECTED_READABLE[$c->getName()])) {
$c->readonly = KINT_PHP81;
if (!$connected) {
// No failed connections after PHP 8.1
continue; // @codeCoverageIgnore
}
} elseif (isset(self::EMPTY_READABLE[$c->getName()])) {
$c->readonly = KINT_PHP81;
// No failed connections after PHP 8.1
if (!$connected && !$empty) { // @codeCoverageIgnore
continue; // @codeCoverageIgnore
}
} elseif (!isset(self::ALWAYS_READABLE[$c->getName()])) {
continue; // @codeCoverageIgnore
}
$c->readonly = KINT_PHP81;
// Only handle unparsed properties
if ((KINT_PHP81 ? 'uninitialized' : 'null') !== $obj->getType()) {
continue;
}
$param = $var->{$c->getName()};
// If it really was a null
if (!KINT_PHP81 && null === $param) {
continue; // @codeCoverageIgnore
}
$new_contents[$key] = $parser->parse($param, $c);
}
$new_contents = \array_values($new_contents);
$v->setChildren($new_contents);
if ($new_contents) {
$v->replaceRepresentation(new ContainerRepresentation('Properties', $new_contents));
}
return $v;
}
}
+592
View File
@@ -0,0 +1,592 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use DomainException;
use InvalidArgumentException;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\ClosedResourceValue;
use Kint\Value\Context\ArrayContext;
use Kint\Value\Context\ClassDeclaredContext;
use Kint\Value\Context\ClassOwnedContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Context\PropertyContext;
use Kint\Value\FixedWidthValue;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\StringRepresentation;
use Kint\Value\ResourceValue;
use Kint\Value\StringValue;
use Kint\Value\UninitializedValue;
use Kint\Value\UnknownValue;
use Kint\Value\VirtualValue;
use ReflectionClass;
use ReflectionObject;
use ReflectionProperty;
use ReflectionReference;
use Throwable;
/**
* @psalm-type ParserTrigger int-mask-of<Parser::TRIGGER_*>
*/
class Parser
{
/**
* Plugin triggers.
*
* These are constants indicating trigger points for plugins
*
* BEGIN: Before normal parsing
* SUCCESS: After successful parsing
* RECURSION: After parsing cancelled by recursion
* DEPTH_LIMIT: After parsing cancelled by depth limit
* COMPLETE: SUCCESS | RECURSION | DEPTH_LIMIT
*
* While a plugin's getTriggers may return any of these only one should
* be given to the plugin when PluginInterface::parse is called
*/
public const TRIGGER_NONE = 0;
public const TRIGGER_BEGIN = 1 << 0;
public const TRIGGER_SUCCESS = 1 << 1;
public const TRIGGER_RECURSION = 1 << 2;
public const TRIGGER_DEPTH_LIMIT = 1 << 3;
public const TRIGGER_COMPLETE = self::TRIGGER_SUCCESS | self::TRIGGER_RECURSION | self::TRIGGER_DEPTH_LIMIT;
/** @psalm-var ?class-string */
protected ?string $caller_class;
protected int $depth_limit = 0;
protected array $array_ref_stack = [];
protected array $object_hashes = [];
protected array $plugins = [];
/**
* @param int $depth_limit Maximum depth to parse data
* @param ?string $caller Caller class name
*
* @psalm-param ?class-string $caller
*/
public function __construct(int $depth_limit = 0, ?string $caller = null)
{
$this->depth_limit = $depth_limit;
$this->caller_class = $caller;
}
/**
* Set the caller class.
*
* @psalm-param ?class-string $caller
*/
public function setCallerClass(?string $caller = null): void
{
$this->noRecurseCall();
$this->caller_class = $caller;
}
/** @psalm-return ?class-string */
public function getCallerClass(): ?string
{
return $this->caller_class;
}
/**
* Set the depth limit.
*
* @param int $depth_limit Maximum depth to parse data, 0 for none
*/
public function setDepthLimit(int $depth_limit = 0): void
{
$this->noRecurseCall();
$this->depth_limit = $depth_limit;
}
public function getDepthLimit(): int
{
return $this->depth_limit;
}
/**
* Parses a variable into a Kint object structure.
*
* @param mixed &$var The input variable
*/
public function parse(&$var, ContextInterface $c): AbstractValue
{
$type = \strtolower(\gettype($var));
if ($v = $this->applyPluginsBegin($var, $c, $type)) {
return $v;
}
switch ($type) {
case 'array':
return $this->parseArray($var, $c);
case 'boolean':
case 'double':
case 'integer':
case 'null':
return $this->parseFixedWidth($var, $c);
case 'object':
return $this->parseObject($var, $c);
case 'resource':
return $this->parseResource($var, $c);
case 'string':
return $this->parseString($var, $c);
case 'resource (closed)':
return $this->parseResourceClosed($var, $c);
case 'unknown type': // @codeCoverageIgnore
default:
// These should never happen. Unknown is resource (closed) from old
// PHP versions and there shouldn't be any other types.
return $this->parseUnknown($var, $c); // @codeCoverageIgnore
}
}
public function addPlugin(PluginInterface $p): void
{
try {
$this->noRecurseCall();
} catch (DomainException $e) { // @codeCoverageIgnore
\trigger_error('Calling Kint\\Parser::addPlugin from inside a parse is deprecated', E_USER_DEPRECATED); // @codeCoverageIgnore
}
if (!$types = $p->getTypes()) {
return;
}
if (!$triggers = $p->getTriggers()) {
return;
}
if ($triggers & self::TRIGGER_BEGIN && !$p instanceof PluginBeginInterface) {
throw new InvalidArgumentException('Parsers triggered on begin must implement PluginBeginInterface');
}
if ($triggers & self::TRIGGER_COMPLETE && !$p instanceof PluginCompleteInterface) {
throw new InvalidArgumentException('Parsers triggered on completion must implement PluginCompleteInterface');
}
$p->setParser($this);
foreach ($types as $type) {
$this->plugins[$type] ??= [
self::TRIGGER_BEGIN => [],
self::TRIGGER_SUCCESS => [],
self::TRIGGER_RECURSION => [],
self::TRIGGER_DEPTH_LIMIT => [],
];
foreach ($this->plugins[$type] as $trigger => &$pool) {
if ($triggers & $trigger) {
$pool[] = $p;
}
}
}
}
public function clearPlugins(): void
{
try {
$this->noRecurseCall();
} catch (DomainException $e) { // @codeCoverageIgnore
\trigger_error('Calling Kint\\Parser::clearPlugins from inside a parse is deprecated', E_USER_DEPRECATED); // @codeCoverageIgnore
}
$this->plugins = [];
}
protected function noRecurseCall(): void
{
$bt = \debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
\reset($bt);
/** @psalm-var class-string $caller_frame['class'] */
$caller_frame = \next($bt);
foreach ($bt as $frame) {
if (isset($frame['object']) && $frame['object'] === $this && 'parse' === $frame['function']) {
throw new DomainException($caller_frame['class'].'::'.$caller_frame['function'].' cannot be called from inside a parse');
}
}
}
/**
* @psalm-param null|bool|float|int &$var
*/
private function parseFixedWidth(&$var, ContextInterface $c): AbstractValue
{
$v = new FixedWidthValue($c, $var);
return $this->applyPluginsComplete($var, $v, self::TRIGGER_SUCCESS);
}
private function parseString(string &$var, ContextInterface $c): AbstractValue
{
$string = new StringValue($c, $var, Utils::detectEncoding($var));
if (false !== $string->getEncoding() && \strlen($var)) {
$string->addRepresentation(new StringRepresentation('Contents', $var, null, true));
}
return $this->applyPluginsComplete($var, $string, self::TRIGGER_SUCCESS);
}
private function parseArray(array &$var, ContextInterface $c): AbstractValue
{
$size = \count($var);
$contents = [];
$parentRef = ReflectionReference::fromArrayElement([&$var], 0)->getId();
if (isset($this->array_ref_stack[$parentRef])) {
$array = new ArrayValue($c, $size, $contents);
$array->flags |= AbstractValue::FLAG_RECURSION;
return $this->applyPluginsComplete($var, $array, self::TRIGGER_RECURSION);
}
try {
$this->array_ref_stack[$parentRef] = true;
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
if ($size > 0 && $this->depth_limit && $cdepth >= $this->depth_limit) {
$array = new ArrayValue($c, $size, $contents);
$array->flags |= AbstractValue::FLAG_DEPTH_LIMIT;
return $this->applyPluginsComplete($var, $array, self::TRIGGER_DEPTH_LIMIT);
}
foreach ($var as $key => $_) {
$child = new ArrayContext($key);
$child->depth = $cdepth + 1;
$child->reference = null !== ReflectionReference::fromArrayElement($var, $key);
if (null !== $ap) {
$child->access_path = $ap.'['.\var_export($key, true).']';
}
$contents[$key] = $this->parse($var[$key], $child);
}
$array = new ArrayValue($c, $size, $contents);
if ($contents) {
$array->addRepresentation(new ContainerRepresentation('Contents', $contents, null, true));
}
return $this->applyPluginsComplete($var, $array, self::TRIGGER_SUCCESS);
} finally {
unset($this->array_ref_stack[$parentRef]);
}
}
/**
* @psalm-return ReflectionProperty[]
*/
private function getPropsOrdered(ReflectionClass $r): array
{
if ($parent = $r->getParentClass()) {
$props = self::getPropsOrdered($parent);
} else {
$props = [];
}
foreach ($r->getProperties() as $prop) {
if ($prop->isStatic()) {
continue;
}
if ($prop->isPrivate()) {
$props[] = $prop;
} else {
$props[$prop->name] = $prop;
}
}
return $props;
}
/**
* @codeCoverageIgnore
*
* @psalm-return ReflectionProperty[]
*/
private function getPropsOrderedOld(ReflectionClass $r): array
{
$props = [];
foreach ($r->getProperties() as $prop) {
if ($prop->isStatic()) {
continue;
}
$props[] = $prop;
}
while ($r = $r->getParentClass()) {
foreach ($r->getProperties(ReflectionProperty::IS_PRIVATE) as $prop) {
if ($prop->isStatic()) {
continue;
}
$props[] = $prop;
}
}
return $props;
}
private function parseObject(object &$var, ContextInterface $c): AbstractValue
{
$hash = \spl_object_hash($var);
$classname = \get_class($var);
if (isset($this->object_hashes[$hash])) {
$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
$object->flags |= AbstractValue::FLAG_RECURSION;
return $this->applyPluginsComplete($var, $object, self::TRIGGER_RECURSION);
}
try {
$this->object_hashes[$hash] = true;
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
if ($this->depth_limit && $cdepth >= $this->depth_limit) {
$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
$object->flags |= AbstractValue::FLAG_DEPTH_LIMIT;
return $this->applyPluginsComplete($var, $object, self::TRIGGER_DEPTH_LIMIT);
}
if (KINT_PHP81) {
$props = $this->getPropsOrdered(new ReflectionObject($var));
} else {
$props = $this->getPropsOrderedOld(new ReflectionObject($var)); // @codeCoverageIgnore
}
$values = (array) $var;
$properties = [];
foreach ($props as $rprop) {
$rprop->setAccessible(true);
$name = $rprop->getName();
// Casting object to array:
// private properties show in the form "\0$owner_class_name\0$property_name";
// protected properties show in the form "\0*\0$property_name";
// public properties show in the form "$property_name";
// http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
$key = $name;
if ($rprop->isProtected()) {
$key = "\0*\0".$name;
} elseif ($rprop->isPrivate()) {
$key = "\0".$rprop->getDeclaringClass()->getName()."\0".$name;
}
$initialized = \array_key_exists($key, $values);
if ($key === (string) (int) $key) {
$key = (int) $key;
}
if ($rprop->isDefault()) {
$child = new PropertyContext(
$name,
$rprop->getDeclaringClass()->getName(),
ClassDeclaredContext::ACCESS_PUBLIC
);
$child->readonly = KINT_PHP81 && $rprop->isReadOnly();
if ($rprop->isProtected()) {
$child->access = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($rprop->isPrivate()) {
$child->access = ClassDeclaredContext::ACCESS_PRIVATE;
}
if (KINT_PHP84) {
if ($rprop->isProtectedSet()) {
$child->access_set = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($rprop->isPrivateSet()) {
$child->access_set = ClassDeclaredContext::ACCESS_PRIVATE;
}
$hooks = $rprop->getHooks();
if (isset($hooks['get'])) {
$child->hooks |= PropertyContext::HOOK_GET;
if ($hooks['get']->returnsReference()) {
$child->hooks |= PropertyContext::HOOK_GET_REF;
}
}
if (isset($hooks['set'])) {
$child->hooks |= PropertyContext::HOOK_SET;
$child->hook_set_type = (string) $rprop->getSettableType();
if ($child->hook_set_type !== (string) $rprop->getType()) {
$child->hooks |= PropertyContext::HOOK_SET_TYPE;
} elseif ('' === $child->hook_set_type) {
$child->hook_set_type = null;
}
}
}
} else {
$child = new ClassOwnedContext($name, $rprop->getDeclaringClass()->getName());
}
$child->reference = $initialized && null !== ReflectionReference::fromArrayElement($values, $key);
$child->depth = $cdepth + 1;
if (null !== $ap && $child->isAccessible($this->caller_class)) {
/** @psalm-var string $child->name */
if (Utils::isValidPhpName($child->name)) {
$child->access_path = $ap.'->'.$child->name;
} else {
$child->access_path = $ap.'->{'.\var_export($child->name, true).'}';
}
}
if (KINT_PHP84 && $rprop->isVirtual()) {
$properties[] = new VirtualValue($child);
} elseif (!$initialized) {
$properties[] = new UninitializedValue($child);
} else {
$properties[] = $this->parse($values[$key], $child);
}
}
$object = new InstanceValue($c, $classname, $hash, \spl_object_id($var));
if ($props) {
$object->setChildren($properties);
}
if ($properties) {
$object->addRepresentation(new ContainerRepresentation('Properties', $properties));
}
return $this->applyPluginsComplete($var, $object, self::TRIGGER_SUCCESS);
} finally {
unset($this->object_hashes[$hash]);
}
}
/**
* @psalm-param resource $var
*/
private function parseResource(&$var, ContextInterface $c): AbstractValue
{
$resource = new ResourceValue($c, \get_resource_type($var));
$resource = $this->applyPluginsComplete($var, $resource, self::TRIGGER_SUCCESS);
return $resource;
}
/**
* @psalm-param mixed $var
*/
private function parseResourceClosed(&$var, ContextInterface $c): AbstractValue
{
$v = new ClosedResourceValue($c);
$v = $this->applyPluginsComplete($var, $v, self::TRIGGER_SUCCESS);
return $v;
}
/**
* Catch-all for any unexpectedgettype.
*
* This should never happen.
*
* @codeCoverageIgnore
*
* @psalm-param mixed $var
*/
private function parseUnknown(&$var, ContextInterface $c): AbstractValue
{
$v = new UnknownValue($c);
$v = $this->applyPluginsComplete($var, $v, self::TRIGGER_SUCCESS);
return $v;
}
/**
* Applies plugins for a yet-unparsed value.
*
* @param mixed &$var The input variable
*/
private function applyPluginsBegin(&$var, ContextInterface $c, string $type): ?AbstractValue
{
$plugins = $this->plugins[$type][self::TRIGGER_BEGIN] ?? [];
foreach ($plugins as $plugin) {
try {
if ($v = $plugin->parseBegin($var, $c)) {
return $v;
}
} catch (Throwable $e) {
\trigger_error(
Utils::errorSanitizeString(\get_class($e)).' was thrown in '.$e->getFile().' on line '.$e->getLine().' while executing '.Utils::errorSanitizeString(\get_class($plugin)).'->parseBegin. Error message: '.Utils::errorSanitizeString($e->getMessage()),
E_USER_WARNING
);
}
}
return null;
}
/**
* Applies plugins for a parsed AbstractValue.
*
* @param mixed &$var The input variable
*/
private function applyPluginsComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
$plugins = $this->plugins[$v->getType()][$trigger] ?? [];
foreach ($plugins as $plugin) {
try {
$v = $plugin->parseComplete($var, $v, $trigger);
} catch (Throwable $e) {
\trigger_error(
Utils::errorSanitizeString(\get_class($e)).' was thrown in '.$e->getFile().' on line '.$e->getLine().' while executing '.Utils::errorSanitizeString(\get_class($plugin)).'->parseComplete. Error message: '.Utils::errorSanitizeString($e->getMessage()),
E_USER_WARNING
);
}
}
return $v;
}
}
+39
View File
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ContextInterface;
interface PluginBeginInterface extends PluginInterface
{
/**
* @psalm-param mixed &$var
*/
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue;
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
/**
* @psalm-import-type ParserTrigger from Parser
*/
interface PluginCompleteInterface extends PluginInterface
{
/**
* @psalm-param mixed &$var
* @psalm-param ParserTrigger $trigger
*/
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue;
}
+43
View File
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
/**
* @psalm-import-type ParserTrigger from Parser
*/
interface PluginInterface
{
public function setParser(Parser $p): void;
public function getTypes(): array;
/**
* @psalm-return ParserTrigger
*/
public function getTriggers(): int;
}
+174
View File
@@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\FixedWidthValue;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\ProfileRepresentation;
/** @psalm-api */
class ProfilePlugin extends AbstractPlugin implements PluginBeginInterface, PluginCompleteInterface
{
protected array $instance_counts = [];
protected array $instance_complexity = [];
protected array $instance_count_stack = [];
protected array $class_complexity = [];
protected array $class_count_stack = [];
public function getTypes(): array
{
return ['string', 'object', 'array', 'integer', 'double', 'resource'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_BEGIN | Parser::TRIGGER_COMPLETE;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
if (0 === $c->getDepth()) {
$this->instance_counts = [];
$this->instance_complexity = [];
$this->instance_count_stack = [];
$this->class_complexity = [];
$this->class_count_stack = [];
}
if (\is_object($var)) {
$hash = \spl_object_hash($var);
$this->instance_counts[$hash] ??= 0;
$this->instance_complexity[$hash] ??= 0;
$this->instance_count_stack[$hash] ??= 0;
if (0 === $this->instance_count_stack[$hash]) {
foreach (\class_parents($var) as $class) {
$this->class_count_stack[$class] ??= 0;
++$this->class_count_stack[$class];
}
foreach (\class_implements($var) as $iface) {
$this->class_count_stack[$iface] ??= 0;
++$this->class_count_stack[$iface];
}
}
++$this->instance_count_stack[$hash];
}
return null;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if ($v instanceof InstanceValue) {
--$this->instance_count_stack[$v->getSplObjectHash()];
if (0 === $this->instance_count_stack[$v->getSplObjectHash()]) {
foreach (\class_parents($var) as $class) {
--$this->class_count_stack[$class];
}
foreach (\class_implements($var) as $iface) {
--$this->class_count_stack[$iface];
}
}
}
// Don't check subs if we're in recursion or array limit
if (~$trigger & Parser::TRIGGER_SUCCESS) {
return $v;
}
$sub_complexity = 1;
foreach ($v->getRepresentations() as $rep) {
if ($rep instanceof ContainerRepresentation) {
foreach ($rep->getContents() as $value) {
$profile = $value->getRepresentation('profiling');
$sub_complexity += $profile instanceof ProfileRepresentation ? $profile->complexity : 1;
}
} else {
++$sub_complexity;
}
}
if ($v instanceof InstanceValue) {
++$this->instance_counts[$v->getSplObjectHash()];
if (0 === $this->instance_count_stack[$v->getSplObjectHash()]) {
$this->instance_complexity[$v->getSplObjectHash()] += $sub_complexity;
$this->class_complexity[$v->getClassName()] ??= 0;
$this->class_complexity[$v->getClassName()] += $sub_complexity;
foreach (\class_parents($var) as $class) {
$this->class_complexity[$class] ??= 0;
if (0 === $this->class_count_stack[$class]) {
$this->class_complexity[$class] += $sub_complexity;
}
}
foreach (\class_implements($var) as $iface) {
$this->class_complexity[$iface] ??= 0;
if (0 === $this->class_count_stack[$iface]) {
$this->class_complexity[$iface] += $sub_complexity;
}
}
}
}
if (0 === $v->getContext()->getDepth()) {
$contents = [];
\arsort($this->class_complexity);
foreach ($this->class_complexity as $name => $complexity) {
$contents[] = new FixedWidthValue(new BaseContext($name), $complexity);
}
if ($contents) {
$v->addRepresentation(new ContainerRepresentation('Class complexity', $contents), 0);
}
}
$rep = new ProfileRepresentation($sub_complexity);
/** @psalm-suppress UnsupportedReferenceUsage */
if ($v instanceof InstanceValue) {
$rep->instance_counts = &$this->instance_counts[$v->getSplObjectHash()];
$rep->instance_complexity = &$this->instance_complexity[$v->getSplObjectHash()];
}
$v->addRepresentation($rep, 0);
return $v;
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ContextInterface;
/**
* @psalm-import-type ParserTrigger from Parser
*
* @psalm-api
*/
class ProxyPlugin implements PluginBeginInterface, PluginCompleteInterface
{
protected array $types;
/** @psalm-var ParserTrigger */
protected int $triggers;
/** @psalm-var callable */
protected $callback;
private ?Parser $parser = null;
/**
* @psalm-param ParserTrigger $triggers
* @psalm-param callable $callback
*/
public function __construct(array $types, int $triggers, $callback)
{
$this->types = $types;
$this->triggers = $triggers;
$this->callback = $callback;
}
public function setParser(Parser $p): void
{
$this->parser = $p;
}
public function getTypes(): array
{
return $this->types;
}
public function getTriggers(): int
{
return $this->triggers;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
return \call_user_func_array($this->callback, [
&$var,
$c,
Parser::TRIGGER_BEGIN,
$this->parser,
]);
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
return \call_user_func_array($this->callback, [
&$var,
$v,
$trigger,
$this->parser,
]);
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Representation\ValueRepresentation;
use Kint\Value\UninitializedValue;
/** @psalm-api */
class SerializePlugin extends AbstractPlugin implements PluginCompleteInterface
{
/**
* Disables automatic unserialization on arrays and objects.
*
* As the PHP manual notes:
*
* > Unserialization can result in code being loaded and executed due to
* > object instantiation and autoloading, and a malicious user may be able
* > to exploit this.
*
* The natural way to stop that from happening is to just refuse to unserialize
* stuff by default. Which is what we're doing for anything that's not scalar.
*/
public static bool $safe_mode = true;
/**
* @psalm-var bool|class-string[]
*/
public static $allowed_classes = false;
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
$trimmed = \rtrim($var);
if ('N;' !== $trimmed && !\preg_match('/^(?:[COabis]:\\d+[:;]|d:\\d+(?:\\.\\d+);)/', $trimmed)) {
return $v;
}
$options = ['allowed_classes' => self::$allowed_classes];
$c = $v->getContext();
$base = new BaseContext('unserialize('.$c->getName().')');
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'unserialize('.$ap;
if (true === self::$allowed_classes) {
$base->access_path .= ')';
} else {
$base->access_path .= ', '.\var_export($options, true).')';
}
}
if (self::$safe_mode && \in_array($trimmed[0], ['C', 'O', 'a'], true)) {
$data = new UninitializedValue($base);
$data->flags |= AbstractValue::FLAG_BLACKLIST;
} else {
// Suppress warnings on unserializeable variable
$data = @\unserialize($trimmed, $options);
if (false === $data && 'b:0;' !== \substr($trimmed, 0, 4)) {
return $v;
}
$data = $this->getParser()->parse($data, $base);
}
$data->flags |= AbstractValue::FLAG_GENERATED;
$v->addRepresentation(new ValueRepresentation('Serialized', $data), 0);
return $v;
}
}
+295
View File
@@ -0,0 +1,295 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ArrayContext;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ClassOwnedContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\ValueRepresentation;
use Kint\Value\SimpleXMLElementValue;
use SimpleXMLElement;
class SimpleXMLElementPlugin extends AbstractPlugin implements PluginBeginInterface
{
/**
* Show all properties and methods.
*/
public static bool $verbose = false;
protected ClassMethodsPlugin $methods_plugin;
public function __construct(Parser $parser)
{
parent::__construct($parser);
$this->methods_plugin = new ClassMethodsPlugin($parser);
}
public function setParser(Parser $p): void
{
parent::setParser($p);
$this->methods_plugin->setParser($p);
}
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
// SimpleXMLElement is a weirdo. No recursion (Or rather everything is
// recursion) and depth limit will have to be handled manually anyway.
return Parser::TRIGGER_BEGIN;
}
public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
{
if (!$var instanceof SimpleXMLElement) {
return null;
}
return $this->parseElement($var, $c);
}
protected function parseElement(SimpleXMLElement &$var, ContextInterface $c): SimpleXMLElementValue
{
$parser = $this->getParser();
$pdepth = $parser->getDepthLimit();
$cdepth = $c->getDepth();
$depthlimit = $pdepth && $cdepth >= $pdepth;
$has_children = self::hasChildElements($var);
if ($depthlimit && $has_children) {
$x = new SimpleXMLElementValue($c, $var, [], null);
$x->flags |= AbstractValue::FLAG_DEPTH_LIMIT;
return $x;
}
$children = $this->getChildren($c, $var);
$attributes = $this->getAttributes($c, $var);
$toString = (string) $var;
$string_body = !$has_children && \strlen($toString);
$x = new SimpleXMLElementValue($c, $var, $children, \strlen($toString) ? $toString : null);
if (self::$verbose) {
$x = $this->methods_plugin->parseComplete($var, $x, Parser::TRIGGER_SUCCESS);
}
if ($attributes) {
$x->addRepresentation(new ContainerRepresentation('Attributes', $attributes), 0);
}
if ($string_body) {
$base = new BaseContext('(string) '.$c->getName());
$base->depth = $cdepth + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = '(string) '.$ap;
}
$toString = $parser->parse($toString, $base);
$x->addRepresentation(new ValueRepresentation('toString', $toString, null, true), 0);
}
if ($children) {
$x->addRepresentation(new ContainerRepresentation('Children', $children), 0);
}
return $x;
}
/** @psalm-return list<AbstractValue> */
protected function getAttributes(ContextInterface $c, SimpleXMLElement $var): array
{
$parser = $this->getParser();
$namespaces = \array_merge(['' => null], $var->getDocNamespaces());
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
$contents = [];
foreach ($namespaces as $nsAlias => $_) {
if ((bool) $nsAttribs = $var->attributes($nsAlias, true)) {
foreach ($nsAttribs as $name => $attrib) {
$obj = new ArrayContext($name);
$obj->depth = $cdepth + 1;
if (null !== $ap) {
$obj->access_path = '(string) '.$ap;
if ('' !== $nsAlias) {
$obj->access_path .= '->attributes('.\var_export($nsAlias, true).', true)';
}
$obj->access_path .= '['.\var_export($name, true).']';
}
if ('' !== $nsAlias) {
$obj->name = $nsAlias.':'.$obj->name;
}
$string = (string) $attrib;
$attribute = $parser->parse($string, $obj);
$contents[] = $attribute;
}
}
}
return $contents;
}
/**
* Alright kids, let's learn about SimpleXMLElement::children!
* children can take a namespace url or alias and provide a list of
* child nodes. This is great since just accessing the members through
* properties doesn't work on SimpleXMLElement when they have a
* namespace at all!
*
* Unfortunately SimpleXML decided to go the retarded route of
* categorizing elements by their tag name rather than by their local
* name (to put it in Dom terms) so if you have something like this:
*
* <root xmlns:localhost="http://localhost/">
* <tag />
* <tag xmlns="http://localhost/" />
* <localhost:tag />
* </root>
*
* * children(null) will get the first 2 results
* * children('', true) will get the first 2 results
* * children('http://localhost/') will get the last 2 results
* * children('localhost', true) will get the last result
*
* So let's just give up and stick to aliases because fuck that mess!
*
* @psalm-return list<SimpleXMLElementValue>
*/
protected function getChildren(ContextInterface $c, SimpleXMLElement $var): array
{
$namespaces = \array_merge(['' => null], $var->getDocNamespaces());
$cdepth = $c->getDepth();
$ap = $c->getAccessPath();
$contents = [];
foreach ($namespaces as $nsAlias => $_) {
if ((bool) $nsChildren = $var->children($nsAlias, true)) {
$nsap = [];
foreach ($nsChildren as $name => $child) {
$base = new ClassOwnedContext((string) $name, SimpleXMLElement::class);
$base->depth = $cdepth + 1;
if ('' !== $nsAlias) {
$base->name = $nsAlias.':'.$name;
}
if (null !== $ap) {
if ('' === $nsAlias) {
$base->access_path = $ap.'->';
} else {
$base->access_path = $ap.'->children('.\var_export($nsAlias, true).', true)->';
}
if (Utils::isValidPhpName((string) $name)) {
$base->access_path .= (string) $name;
} else {
$base->access_path .= '{'.\var_export((string) $name, true).'}';
}
if (isset($nsap[$base->access_path])) {
++$nsap[$base->access_path];
$base->access_path .= '['.$nsap[$base->access_path].']';
} else {
$nsap[$base->access_path] = 0;
}
}
$v = $this->parseElement($child, $base);
$v->flags |= AbstractValue::FLAG_GENERATED;
$contents[] = $v;
}
}
}
return $contents;
}
/**
* More SimpleXMLElement bullshit.
*
* If we want to know if the element contains text we can cast to string.
* Except if it contains text mixed with elements simplexml for some stupid
* reason decides to concatenate the text from between those elements
* rather than all the text in the hierarchy...
*
* So we have NO way of getting text nodes between elements, but we can
* still tell if we have elements right? If we have elements we assume it's
* not a string and call it a day!
*
* Well if you cast the element to an array attributes will be on it so
* you'd have to remove that key, and if it's a string it'll also have the
* 0 index used for the string contents too...
*
* Wait, can we use the 0 index to tell if it's a string? Nope! CDATA
* doesn't show up AT ALL when casting to anything but string, and we'll
* still get those concatenated strings of mostly whitespace if we just do
* (string) and check the length.
*
* Luckily, I found the only way to do this reliably is through children().
* We still have to loop through all the namespaces and see if there's a
* match but then we have the problem of the attributes showing up again...
*
* Or at least that's what var_dump says. And when we cast the result to
* bool it's true too... But if we cast it to array then it's suddenly empty!
*
* Long story short the function below is the only way to reliably check if
* a SimpleXMLElement has children
*/
protected static function hasChildElements(SimpleXMLElement $var): bool
{
$namespaces = \array_merge(['' => null], $var->getDocNamespaces());
foreach ($namespaces as $nsAlias => $_) {
if ((array) $var->children($nsAlias, true)) {
return true;
}
}
return false;
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\SplFileInfoRepresentation;
use Kint\Value\SplFileInfoValue;
use SplFileInfo;
use SplFileObject;
class SplFileInfoPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
// SplFileObject throws exceptions in normal use in places SplFileInfo doesn't
if (!$var instanceof SplFileInfo || $var instanceof SplFileObject) {
return $v;
}
if (!$v instanceof InstanceValue) {
return $v;
}
$out = new SplFileInfoValue($v->getContext(), $var);
$out->setChildren($v->getChildren());
$out->flags = $v->flags;
$out->addRepresentation(new SplFileInfoRepresentation(clone $var));
$out->appendRepresentations($v->getRepresentations());
return $out;
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ArrayContext;
use Kint\Value\ResourceValue;
use Kint\Value\StreamValue;
use TypeError;
class StreamPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['resource'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$v instanceof ResourceValue) {
return $v;
}
// Doublecheck that the resource is open before we get the metadata
if (!\is_resource($var)) {
return $v;
}
try {
$meta = \stream_get_meta_data($var);
} catch (TypeError $e) {
return $v;
}
$c = $v->getContext();
$parser = $this->getParser();
$parsed_meta = [];
foreach ($meta as $key => $val) {
$base = new ArrayContext($key);
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'stream_get_meta_data('.$ap.')['.\var_export($key, true).']';
}
$val = $parser->parse($val, $base);
$val->flags |= AbstractValue::FLAG_GENERATED;
$parsed_meta[] = $val;
}
$stream = new StreamValue($c, $parsed_meta, $meta['uri'] ?? null);
$stream->flags = $v->flags;
$stream->appendRepresentations($v->getRepresentations());
return $stream;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\Representation\TableRepresentation;
// Note: Interaction with ArrayLimitPlugin:
// Any array limited children will be shown in tables identically to
// non-array-limited children since the table only shows that it is an array
// and it's size anyway. Because ArrayLimitPlugin halts the parse on finding
// a limit all other plugins including this one are stopped, so you cannot get
// a tabular representation of an array that is longer than the limit.
class TablePlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static int $max_width = 300;
public static int $min_width = 2;
public function getTypes(): array
{
return ['array'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$v instanceof ArrayValue) {
return $v;
}
if (\count($var) < 2) {
return $v;
}
// Ensure this is an array of arrays and that all child arrays have the
// same keys. We don't care about their children - if there's another
// "table" inside we'll just make another one down the value tab
$keys = null;
foreach ($var as $elem) {
if (!\is_array($elem)) {
return $v;
}
if (null === $keys) {
if (\count($elem) < self::$min_width || \count($elem) > self::$max_width) {
return $v;
}
$keys = \array_keys($elem);
} elseif (\array_keys($elem) !== $keys) {
return $v;
}
}
$children = $v->getContents();
if (!$children) {
return $v;
}
// Ensure none of the child arrays are recursion or depth limit. We
// don't care if their children are since they are the table cells
foreach ($children as $childarray) {
if (!$childarray instanceof ArrayValue || empty($childarray->getContents())) {
return $v;
}
}
$v->addRepresentation(new TableRepresentation($children), 0);
return $v;
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\InstanceValue;
use Kint\Value\Representation\SourceRepresentation;
use Kint\Value\ThrowableValue;
use RuntimeException;
use Throwable;
class ThrowablePlugin extends AbstractPlugin implements PluginCompleteInterface
{
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$var instanceof Throwable || !$v instanceof InstanceValue) {
return $v;
}
$throw = new ThrowableValue($v->getContext(), $var);
$throw->setChildren($v->getChildren());
$throw->flags = $v->flags;
$throw->appendRepresentations($v->getRepresentations());
try {
$throw->addRepresentation(new SourceRepresentation($var->getFile(), $var->getLine(), null, true), 0);
} catch (RuntimeException $e) {
}
return $throw;
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use DateTimeImmutable;
use Kint\Value\AbstractValue;
use Kint\Value\FixedWidthValue;
use Kint\Value\Representation\StringRepresentation;
use Kint\Value\StringValue;
class TimestampPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static array $blacklist = [
2147483648,
2147483647,
1073741824,
1073741823,
];
public function getTypes(): array
{
return ['string', 'integer'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (\is_string($var) && !\ctype_digit($var)) {
return $v;
}
if ($var < 0) {
return $v;
}
if (\in_array($var, self::$blacklist, true)) {
return $v;
}
$len = \strlen((string) $var);
// Guess for anything between March 1973 and November 2286
if ($len < 9 || $len > 10) {
return $v;
}
if (!$v instanceof StringValue && !$v instanceof FixedWidthValue) {
return $v;
}
if (!$dt = DateTimeImmutable::createFromFormat('U', (string) $var)) {
return $v;
}
$v->removeRepresentation('contents');
$v->addRepresentation(new StringRepresentation('Timestamp', $dt->format('c'), null, true));
return $v;
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Representation\ValueRepresentation;
use ReflectionClass;
use SimpleXMLElement;
use SplFileInfo;
use Throwable;
class ToStringPlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static array $blacklist = [
SimpleXMLElement::class,
SplFileInfo::class,
];
public function getTypes(): array
{
return ['object'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
$reflection = new ReflectionClass($var);
if (!$reflection->hasMethod('__toString')) {
return $v;
}
foreach (self::$blacklist as $class) {
if ($var instanceof $class) {
return $v;
}
}
try {
$string = (string) $var;
} catch (Throwable $t) {
return $v;
}
$c = $v->getContext();
$base = new BaseContext($c->getName());
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = '(string) '.$ap;
}
$string = $this->getParser()->parse($string, $base);
$v->addRepresentation(new ValueRepresentation('toString', $string));
return $v;
}
}
+156
View File
@@ -0,0 +1,156 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\Context\ArrayContext;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\SourceRepresentation;
use Kint\Value\Representation\ValueRepresentation;
use Kint\Value\TraceFrameValue;
use Kint\Value\TraceValue;
use RuntimeException;
/**
* @psalm-import-type TraceFrame from TraceFrameValue
*/
class TracePlugin extends AbstractPlugin implements PluginCompleteInterface
{
public static array $blacklist = ['spl_autoload_call'];
public static array $path_blacklist = [];
public function getTypes(): array
{
return ['array'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if (!$v instanceof ArrayValue) {
return $v;
}
// Shallow copy so we don't have to worry about touching var
$trace = $var;
if (!Utils::isTrace($trace)) {
return $v;
}
$pdepth = $this->getParser()->getDepthLimit();
$c = $v->getContext();
// We need at least 2 levels in order to get $trace[n]['args']
if ($pdepth && $c->getDepth() + 2 >= $pdepth) {
return $v;
}
$contents = $v->getContents();
self::$blacklist = Utils::normalizeAliases(self::$blacklist);
$path_blacklist = self::normalizePaths(self::$path_blacklist);
$frames = [];
foreach ($contents as $frame) {
if (!$frame instanceof ArrayValue || !$frame->getContext() instanceof ArrayContext) {
continue;
}
$index = $frame->getContext()->getName();
if (!isset($trace[$index]) || Utils::traceFrameIsListed($trace[$index], self::$blacklist)) {
continue;
}
if (isset($trace[$index]['file']) && false !== ($realfile = \realpath($trace[$index]['file']))) {
foreach ($path_blacklist as $path) {
if (0 === \strpos($realfile, $path)) {
continue 2;
}
}
}
$frame = new TraceFrameValue($frame, $trace[$index]);
if (null !== ($file = $frame->getFile()) && null !== ($line = $frame->getLine())) {
try {
$frame->addRepresentation(new SourceRepresentation($file, $line));
} catch (RuntimeException $e) {
}
}
if ($args = $frame->getArgs()) {
$frame->addRepresentation(new ContainerRepresentation('Arguments', $args));
}
if ($obj = $frame->getObject()) {
$frame->addRepresentation(
new ValueRepresentation(
'Callee object ['.$obj->getClassName().']',
$obj,
'callee_object'
)
);
}
$frames[$index] = $frame;
}
$traceobj = new TraceValue($c, \count($frames), $frames);
if ($frames) {
$traceobj->addRepresentation(new ContainerRepresentation('Contents', $frames, null, true));
}
return $traceobj;
}
protected static function normalizePaths(array $paths): array
{
$normalized = [];
foreach ($paths as $path) {
$realpath = \realpath($path);
if (\is_dir($realpath)) {
$realpath .= DIRECTORY_SEPARATOR;
}
$normalized[] = $realpath;
}
return $normalized;
}
}
+179
View File
@@ -0,0 +1,179 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Parser;
use Dom\Node;
use Dom\XMLDocument;
use DOMDocument;
use DOMException;
use DOMNode;
use InvalidArgumentException;
use Kint\Value\AbstractValue;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Representation\ValueRepresentation;
use Throwable;
class XmlPlugin extends AbstractPlugin implements PluginCompleteInterface
{
/**
* Which method to parse the variable with.
*
* DOMDocument provides more information including the text between nodes,
* however it's memory usage is very high and it takes longer to parse and
* render. Plus it's a pain to work with. So SimpleXML is the default.
*
* @psalm-var 'SimpleXML'|'DOMDocument'|'XMLDocument'
*/
public static string $parse_method = 'SimpleXML';
public function getTypes(): array
{
return ['string'];
}
public function getTriggers(): int
{
return Parser::TRIGGER_SUCCESS;
}
public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if ('<?xml' !== \substr($var, 0, 5)) {
return $v;
}
if (!\method_exists($this, 'xmlTo'.self::$parse_method)) {
return $v;
}
$c = $v->getContext();
$out = \call_user_func([$this, 'xmlTo'.self::$parse_method], $var, $c);
if (null === $out) {
return $v;
}
$out->flags |= AbstractValue::FLAG_GENERATED;
$v->addRepresentation(new ValueRepresentation('XML', $out), 0);
return $v;
}
/** @psalm-suppress PossiblyUnusedMethod */
protected function xmlToSimpleXML(string $var, ContextInterface $c): ?AbstractValue
{
$errors = \libxml_use_internal_errors(true);
try {
$xml = \simplexml_load_string($var);
if (!(bool) $xml) {
throw new InvalidArgumentException('Bad XML parse in XmlPlugin::xmlToSimpleXML');
}
} catch (Throwable $t) {
return null;
} finally {
\libxml_use_internal_errors($errors);
\libxml_clear_errors();
}
$base = new BaseContext($xml->getName());
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = 'simplexml_load_string('.$ap.')';
}
return $this->getParser()->parse($xml, $base);
}
/**
* Get the DOMDocument info.
*
* If it errors loading then we wouldn't have gotten this far in the first place.
*
* @psalm-suppress PossiblyUnusedMethod
*
* @psalm-param non-empty-string $var
*/
protected function xmlToDOMDocument(string $var, ContextInterface $c): ?AbstractValue
{
try {
$xml = new DOMDocument();
$check = $xml->loadXML($var, LIBXML_NOWARNING | LIBXML_NOERROR);
if (false === $check) {
throw new InvalidArgumentException('Bad XML parse in XmlPlugin::xmlToDOMDocument');
}
} catch (Throwable $t) {
return null;
}
$xml = $xml->firstChild;
/**
* @psalm-var DOMNode $xml
* Psalm bug #11120
*/
$base = new BaseContext($xml->nodeName);
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = '(function($s){$x = new \\DomDocument(); $x->loadXML($s); return $x;})('.$ap.')->firstChild';
}
return $this->getParser()->parse($xml, $base);
}
/** @psalm-suppress PossiblyUnusedMethod */
protected function xmlToXMLDocument(string $var, ContextInterface $c): ?AbstractValue
{
if (!KINT_PHP84) {
return null; // @codeCoverageIgnore
}
try {
$xml = XMLDocument::createFromString($var, LIBXML_NOWARNING | LIBXML_NOERROR);
} catch (DOMException $e) {
return null;
}
$xml = $xml->firstChild;
/**
* @psalm-var Node $xml
* Psalm bug #11120
*/
$base = new BaseContext($xml->nodeName);
$base->depth = $c->getDepth() + 1;
if (null !== ($ap = $c->getAccessPath())) {
$base->access_path = '\\Dom\\XMLDocument::createFromString('.$ap.')->firstChild';
}
return $this->getParser()->parse($xml, $base);
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
abstract class AbstractRenderer implements ConstructableRendererInterface
{
public static ?string $js_nonce = null;
public static ?string $css_nonce = null;
/** @psalm-var ?non-empty-string */
public static ?string $file_link_format = null;
protected bool $show_trace = true;
protected ?array $callee = null;
protected array $trace = [];
protected bool $render_spl_ids = true;
public function __construct()
{
}
public function shouldRenderObjectIds(): bool
{
return $this->render_spl_ids;
}
public function setCallInfo(array $info): void
{
$this->callee = $info['callee'] ?? null;
$this->trace = $info['trace'] ?? [];
}
public function setStatics(array $statics): void
{
$this->show_trace = !empty($statics['display_called_from']);
}
public function filterParserPlugins(array $plugins): array
{
return $plugins;
}
public function preRender(): string
{
return '';
}
public function postRender(): string
{
return '';
}
public static function getFileLink(string $file, int $line): ?string
{
if (null === self::$file_link_format) {
return null;
}
return \str_replace(['%f', '%l'], [$file, $line], self::$file_link_format);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
trait AssetRendererTrait
{
public static ?string $theme = null;
/** @psalm-var array{js?:string, css?:array<path, string|false>} */
private static array $assetCache = [];
/** @psalm-api */
public static function renderJs(): string
{
if (!isset(self::$assetCache['js'])) {
self::$assetCache['js'] = \file_get_contents(KINT_DIR.'/resources/compiled/main.js');
}
return self::$assetCache['js'];
}
/** @psalm-api */
public static function renderCss(): ?string
{
if (!isset(self::$theme)) {
return null;
}
if (!isset(self::$assetCache['css'][self::$theme])) {
if (\file_exists(KINT_DIR.'/resources/compiled/'.self::$theme)) {
self::$assetCache['css'][self::$theme] = \file_get_contents(KINT_DIR.'/resources/compiled/'.self::$theme);
} elseif (\file_exists(self::$theme)) {
self::$assetCache['css'][self::$theme] = \file_get_contents(self::$theme);
} else {
self::$assetCache['css'][self::$theme] = false;
}
}
if (false === self::$assetCache['css'][self::$theme]) {
return null;
}
return self::$assetCache['css'][self::$theme];
}
}
+183
View File
@@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
use Kint\Value\AbstractValue;
use Throwable;
class CliRenderer extends TextRenderer
{
/**
* @var bool enable colors
*/
public static bool $cli_colors = true;
/**
* Detects the terminal width on startup.
*/
public static bool $detect_width = true;
/**
* The minimum width to detect terminal size as.
*
* Less than this is ignored and falls back to default width.
*/
public static int $min_terminal_width = 40;
/**
* Forces utf8 output on windows.
*/
public static bool $force_utf8 = false;
/**
* Which stream to check for VT100 support on windows.
*
* uses STDOUT by default if it's defined
*
* @psalm-var ?resource
*/
public static $windows_stream = null;
protected static ?int $terminal_width = null;
protected bool $windows_output = false;
protected bool $colors = false;
public function __construct()
{
parent::__construct();
if (!self::$force_utf8 && KINT_WIN) {
if (!\function_exists('sapi_windows_vt100_support')) {
$this->windows_output = true;
} else {
$stream = self::$windows_stream;
if (!$stream && \defined('STDOUT')) {
$stream = STDOUT;
}
if (!$stream) {
$this->windows_output = true;
} else {
$this->windows_output = !\sapi_windows_vt100_support($stream);
}
}
}
if (null === self::$terminal_width) {
if (self::$detect_width) {
try {
$tput = KINT_WIN ? \exec('tput cols 2>nul') : \exec('tput cols 2>/dev/null');
if ((bool) $tput) {
/**
* @psalm-suppress InvalidCast
* Psalm bug #11080
*/
self::$terminal_width = (int) $tput;
}
} catch (Throwable $t) {
self::$terminal_width = self::$default_width;
}
}
if (!isset(self::$terminal_width) || self::$terminal_width < self::$min_terminal_width) {
self::$terminal_width = self::$default_width;
}
}
$this->colors = $this->windows_output ? false : self::$cli_colors;
$this->header_width = self::$terminal_width;
}
public function colorValue(string $string): string
{
if (!$this->colors) {
return $string;
}
return "\x1b[32m".\str_replace("\n", "\x1b[0m\n\x1b[32m", $string)."\x1b[0m";
}
public function colorType(string $string): string
{
if (!$this->colors) {
return $string;
}
return "\x1b[35;1m".\str_replace("\n", "\x1b[0m\n\x1b[35;1m", $string)."\x1b[0m";
}
public function colorTitle(string $string): string
{
if (!$this->colors) {
return $string;
}
return "\x1b[36m".\str_replace("\n", "\x1b[0m\n\x1b[36m", $string)."\x1b[0m";
}
public function renderTitle(AbstractValue $v): string
{
if ($this->windows_output) {
return $this->utf8ToWindows(parent::renderTitle($v));
}
return parent::renderTitle($v);
}
public function preRender(): string
{
return PHP_EOL;
}
public function postRender(): string
{
if ($this->windows_output) {
return $this->utf8ToWindows(parent::postRender());
}
return parent::postRender();
}
public function escape(string $string, $encoding = false): string
{
return \str_replace("\x1b", '\\x1b', $string);
}
protected function utf8ToWindows(string $string): string
{
return \str_replace(
['┌', '═', '┐', '│', '└', '─', '┘'],
[' ', '=', ' ', '|', ' ', '-', ' '],
$string
);
}
}
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
interface ConstructableRendererInterface extends RendererInterface
{
public function __construct();
}
+217
View File
@@ -0,0 +1,217 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
use Kint\Utils;
use Kint\Value\AbstractValue;
class PlainRenderer extends TextRenderer
{
use AssetRendererTrait;
public static array $pre_render_sources = [
'script' => [
[self::class, 'renderJs'],
],
'style' => [
[self::class, 'renderCss'],
],
'raw' => [],
];
/**
* Output htmlentities instead of utf8.
*/
public static bool $disable_utf8 = false;
public static bool $needs_pre_render = true;
public static bool $always_pre_render = false;
protected bool $force_pre_render = false;
public function __construct()
{
parent::__construct();
self::$theme ??= 'plain.css';
$this->setForcePreRender(self::$always_pre_render);
}
public function setCallInfo(array $info): void
{
parent::setCallInfo($info);
if (\in_array('@', $info['modifiers'], true)) {
$this->setForcePreRender(true);
}
}
public function setStatics(array $statics): void
{
parent::setStatics($statics);
if (!empty($statics['return'])) {
$this->setForcePreRender(true);
}
}
public function setForcePreRender(bool $force_pre_render): void
{
$this->force_pre_render = $force_pre_render;
}
public function getForcePreRender(): bool
{
return $this->force_pre_render;
}
public function shouldPreRender(): bool
{
return $this->getForcePreRender() || self::$needs_pre_render;
}
public function colorValue(string $string): string
{
return '<i>'.$string.'</i>';
}
public function colorType(string $string): string
{
return '<b>'.$string.'</b>';
}
public function colorTitle(string $string): string
{
return '<u>'.$string.'</u>';
}
public function renderTitle(AbstractValue $v): string
{
if (self::$disable_utf8) {
return $this->utf8ToHtmlentity(parent::renderTitle($v));
}
return parent::renderTitle($v);
}
public function preRender(): string
{
$output = '';
if ($this->shouldPreRender()) {
foreach (self::$pre_render_sources as $type => $values) {
$contents = '';
foreach ($values as $v) {
$contents .= \call_user_func($v, $this);
}
if (!\strlen($contents)) {
continue;
}
switch ($type) {
case 'script':
$output .= '<script class="kint-plain-script"';
if (null !== self::$js_nonce) {
$output .= ' nonce="'.\htmlspecialchars(self::$js_nonce).'"';
}
$output .= '>'.$contents.'</script>';
break;
case 'style':
$output .= '<style class="kint-plain-style"';
if (null !== self::$css_nonce) {
$output .= ' nonce="'.\htmlspecialchars(self::$css_nonce).'"';
}
$output .= '>'.$contents.'</style>';
break;
default:
$output .= $contents;
}
}
// Don't pre-render on every dump
if (!$this->getForcePreRender()) {
self::$needs_pre_render = false;
}
}
return $output.'<div class="kint-plain">';
}
public function postRender(): string
{
if (self::$disable_utf8) {
return $this->utf8ToHtmlentity(parent::postRender()).'</div>';
}
return parent::postRender().'</div>';
}
public function ideLink(string $file, int $line): string
{
$path = $this->escape(Utils::shortenPath($file)).':'.$line;
$ideLink = self::getFileLink($file, $line);
if (null === $ideLink) {
return $path;
}
return '<a href="'.$this->escape($ideLink).'">'.$path.'</a>';
}
public function escape(string $string, $encoding = false): string
{
if (false === $encoding) {
$encoding = Utils::detectEncoding($string);
}
$original_encoding = $encoding;
if (false === $encoding || 'ASCII' === $encoding) {
$encoding = 'UTF-8';
}
$string = \htmlspecialchars($string, ENT_NOQUOTES, $encoding);
// this call converts all non-ASCII characters into numeirc htmlentities
if (\function_exists('mb_encode_numericentity') && 'ASCII' !== $original_encoding) {
$string = \mb_encode_numericentity($string, [0x80, 0xFFFF, 0, 0xFFFF], $encoding);
}
return $string;
}
protected function utf8ToHtmlentity(string $string): string
{
return \str_replace(
['┌', '═', '┐', '│', '└', '─', '┘'],
['&#9484;', '&#9552;', '&#9488;', '&#9474;', '&#9492;', '&#9472;', '&#9496;'],
$string
);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
use Kint\Value\AbstractValue;
interface RendererInterface
{
public function render(AbstractValue $v): string;
public function shouldRenderObjectIds(): bool;
public function setCallInfo(array $info): void;
public function setStatics(array $statics): void;
public function filterParserPlugins(array $plugins): array;
public function preRender(): string;
public function postRender(): string;
}
+107
View File
@@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Renderer\RichRenderer;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ClassDeclaredContext;
use Kint\Value\Context\PropertyContext;
use Kint\Value\InstanceValue;
abstract class AbstractPlugin implements PluginInterface
{
protected RichRenderer $renderer;
public function __construct(RichRenderer $r)
{
$this->renderer = $r;
}
/**
* @param string $content The replacement for the getValueShort contents
*/
public function renderLockedHeader(AbstractValue $v, string $content): string
{
$header = '<dt class="kint-parent kint-locked">';
$c = $v->getContext();
if (RichRenderer::$access_paths && $c->getDepth() > 0 && null !== ($ap = $c->getAccessPath())) {
$header .= '<span class="kint-access-path-trigger" title="Show access path">&rlarr;</span>';
}
$header .= '<nav></nav>';
if ($c instanceof ClassDeclaredContext) {
$header .= '<var>'.$c->getModifiers().'</var> ';
}
$header .= '<dfn>'.$this->renderer->escape($v->getDisplayName()).'</dfn> ';
if ($c instanceof PropertyContext && null !== ($s = $c->getHooks())) {
$header .= '<var>'.$this->renderer->escape($s).'</var> ';
}
if (null !== ($s = $c->getOperator())) {
$header .= $this->renderer->escape($s, 'ASCII').' ';
}
$s = $v->getDisplayType();
if (RichRenderer::$escape_types) {
$s = $this->renderer->escape($s);
}
if ($c->isRef()) {
$s = '&amp;'.$s;
}
$header .= '<var>'.$s.'</var>';
if ($v instanceof InstanceValue && $this->renderer->shouldRenderObjectIds()) {
$header .= '#'.$v->getSplObjectId();
}
$header .= ' ';
if (null !== ($s = $v->getDisplaySize())) {
if (RichRenderer::$escape_types) {
$s = $this->renderer->escape($s);
}
$header .= '('.$s.') ';
}
$header .= $content;
if (!empty($ap)) {
$header .= '<div class="access-path">'.$this->renderer->escape($ap).'</div>';
}
return $header.'</dt>';
}
}
+64
View File
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\BinaryRepresentation;
use Kint\Value\Representation\RepresentationInterface;
class BinaryPlugin extends AbstractPlugin implements TabPluginInterface
{
/** @psalm-var positive-int */
public static int $line_length = 0x10;
/** @psalm-var positive-int */
public static int $chunk_length = 0x2;
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof BinaryRepresentation) {
return null;
}
$out = '<pre>';
$lines = \str_split($r->getValue(), self::$line_length);
foreach ($lines as $index => $line) {
$out .= \sprintf('%08X', $index * self::$line_length).":\t";
$chunks = \str_split(\str_pad(\bin2hex($line), 2 * self::$line_length, ' '), 2 * self::$chunk_length);
$out .= \implode(' ', $chunks);
$out .= "\t".\preg_replace('/[^\\x20-\\x7E]/', '.', $line)."\n";
}
$out .= '</pre>';
return $out;
}
}
@@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\MethodValue;
use Kint\Value\Representation\CallableDefinitionRepresentation;
use Kint\Value\Representation\RepresentationInterface;
class CallableDefinitionPlugin extends AbstractPlugin implements TabPluginInterface
{
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof CallableDefinitionRepresentation) {
return null;
}
$docstring = [];
if ($v instanceof MethodValue) {
$c = $v->getContext();
if ($c->inherited) {
$docstring[] = 'Inherited from '.$this->renderer->escape($c->owner_class);
}
}
$docstring[] = 'Defined in '.$this->renderer->escape(Utils::shortenPath($r->getFileName())).':'.$r->getLine();
$docstring = '<small>'.\implode("\n", $docstring).'</small>';
if (null !== ($trimmed = $r->getDocstringTrimmed())) {
$docstring = $this->renderer->escape($trimmed)."\n\n".$docstring;
}
return '<pre>'.$docstring.'</pre>';
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Renderer\RichRenderer;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\Context\MethodContext;
use Kint\Value\MethodValue;
class CallablePlugin extends AbstractPlugin implements ValuePluginInterface
{
protected static array $method_cache = [];
public function renderValue(AbstractValue $v): ?string
{
if (!$v instanceof MethodValue) {
return null;
}
$c = $v->getContext();
if (!$c instanceof MethodContext) {
return null;
}
if (!isset(self::$method_cache[$c->owner_class][$c->name])) {
$children = $this->renderer->renderChildren($v);
$header = '<var>'.$c->getModifiers();
if ($v->getCallableBag()->return_reference) {
$header .= ' &amp;';
}
$header .= '</var> ';
$function = $this->renderer->escape($v->getDisplayName());
if (null !== ($url = $v->getPhpDocUrl())) {
$function = '<a href="'.$url.'" target=_blank>'.$function.'</a>';
}
$header .= '<dfn>'.$function.'</dfn>';
if (null !== ($rt = $v->getCallableBag()->returntype)) {
$header .= ': <var>';
$header .= $this->renderer->escape($rt).'</var>';
} elseif (null !== ($ds = $v->getCallableBag()->docstring)) {
if (\preg_match('/@return\\s+(.*)\\r?\\n/m', $ds, $matches)) {
if (\trim($matches[1])) {
$header .= ': <var>'.$this->renderer->escape(\trim($matches[1])).'</var>';
}
}
}
if (null !== ($s = $v->getDisplayValue())) {
if (RichRenderer::$strlen_max) {
$s = Utils::truncateString($s, RichRenderer::$strlen_max);
}
$header .= ' '.$this->renderer->escape($s);
}
self::$method_cache[$c->owner_class][$c->name] = [
'header' => $header,
'children' => $children,
];
}
$children = self::$method_cache[$c->owner_class][$c->name]['children'];
$header = $this->renderer->renderHeaderWrapper(
$c,
(bool) \strlen($children),
self::$method_cache[$c->owner_class][$c->name]['header']
);
return '<dl>'.$header.$children.'</dl>';
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\ColorRepresentation;
use Kint\Value\Representation\RepresentationInterface;
class ColorPlugin extends AbstractPlugin implements TabPluginInterface, ValuePluginInterface
{
public function renderValue(AbstractValue $v): ?string
{
$r = $v->getRepresentation('color');
if (!$r instanceof ColorRepresentation) {
return null;
}
$children = $this->renderer->renderChildren($v);
$header = $this->renderer->renderHeader($v);
$header .= '<div class="kint-color-preview"><div style="background:';
$header .= $r->getColor(ColorRepresentation::COLOR_RGBA);
$header .= '"></div></div>';
$header = $this->renderer->renderHeaderWrapper($v->getContext(), (bool) \strlen($children), $header);
return '<dl>'.$header.$children.'</dl>';
}
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof ColorRepresentation) {
return null;
}
$out = '';
if ($color = $r->getColor(ColorRepresentation::COLOR_NAME)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($color = $r->getColor(ColorRepresentation::COLOR_HEX_3)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($color = $r->getColor(ColorRepresentation::COLOR_HEX_6)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($r->hasAlpha()) {
if ($color = $r->getColor(ColorRepresentation::COLOR_HEX_4)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($color = $r->getColor(ColorRepresentation::COLOR_HEX_8)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($color = $r->getColor(ColorRepresentation::COLOR_RGBA)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($color = $r->getColor(ColorRepresentation::COLOR_HSLA)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
} else {
if ($color = $r->getColor(ColorRepresentation::COLOR_RGB)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
if ($color = $r->getColor(ColorRepresentation::COLOR_HSL)) {
$out .= '<dfn>'.$color."</dfn>\n";
}
}
if (!\strlen($out)) {
return null;
}
return '<pre>'.$out.'</pre>';
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
class LockPlugin extends AbstractPlugin implements ValuePluginInterface
{
public function renderValue(AbstractValue $v): ?string
{
switch ($v->getHint()) {
case 'blacklist':
return '<dl>'.$this->renderLockedHeader($v, '<var>Blacklisted</var>').'</dl>';
case 'recursion':
return '<dl>'.$this->renderLockedHeader($v, '<var>Recursion</var>').'</dl>';
case 'depth_limit':
return '<dl>'.$this->renderLockedHeader($v, '<var>Depth Limit</var>').'</dl>';
case 'array_limit':
return '<dl>'.$this->renderLockedHeader($v, '<var>Array Limit</var>').'</dl>';
}
return null;
}
}
@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\MicrotimeRepresentation;
use Kint\Value\Representation\RepresentationInterface;
class MicrotimePlugin extends AbstractPlugin implements TabPluginInterface
{
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof MicrotimeRepresentation || !($dt = $r->getDateTime())) {
return null;
}
$out = $dt->format('Y-m-d H:i:s.u');
if (null !== ($lap = $r->getLapTime())) {
$out .= '<br><b>SINCE LAST CALL:</b> <span class="kint-microtime-lap">'.\round($lap, 4).'</span>s.';
}
if (null !== ($total = $r->getTotalTime())) {
$out .= '<br><b>SINCE START:</b> '.\round($total, 4).'s.';
}
if (null !== ($avg = $r->getAverageTime())) {
$out .= '<br><b>AVERAGE DURATION:</b> <span class="kint-microtime-avg">'.\round($avg, 4).'</span>s.';
}
$bytes = Utils::getHumanReadableBytes($r->getMemoryUsage());
$out .= '<br><b>MEMORY USAGE:</b> '.$r->getMemoryUsage().' bytes ('.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$bytes = Utils::getHumanReadableBytes($r->getMemoryUsageReal());
$out .= ' (real '.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$bytes = Utils::getHumanReadableBytes($r->getMemoryPeakUsage());
$out .= '<br><b>PEAK MEMORY USAGE:</b> '.$r->getMemoryPeakUsage().' bytes ('.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$bytes = Utils::getHumanReadableBytes($r->getMemoryPeakUsageReal());
$out .= ' (real '.\round($bytes['value'], 3).' '.$bytes['unit'].')';
return '<pre data-kint-microtime-group="'.$r->getGroup().'">'.$out.'</pre>';
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Renderer\RichRenderer;
interface PluginInterface
{
public function __construct(RichRenderer $r);
}
+56
View File
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\ProfileRepresentation;
use Kint\Value\Representation\RepresentationInterface;
class ProfilePlugin extends AbstractPlugin implements TabPluginInterface
{
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof ProfileRepresentation) {
return null;
}
$out = '<pre>';
$out .= 'Complexity: '.$r->complexity.PHP_EOL;
if (isset($r->instance_counts)) {
$out .= 'Instance repetitions: '.\var_export($r->instance_counts, true).PHP_EOL;
}
if (isset($r->instance_complexity)) {
$out .= 'Instance complexity: '.\var_export($r->instance_complexity, true).PHP_EOL;
}
$out .= '</pre>';
return $out;
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\RepresentationInterface;
use Kint\Value\Representation\SourceRepresentation;
class SourcePlugin extends AbstractPlugin implements TabPluginInterface
{
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof SourceRepresentation) {
return null;
}
$source = $r->getSourceLines();
// Trim empty lines from the start and end of the source
foreach ($source as $linenum => $line) {
if (\strlen(\trim($line)) || $linenum === $r->getLine()) {
break;
}
unset($source[$linenum]);
}
foreach (\array_reverse($source, true) as $linenum => $line) {
if (\strlen(\trim($line)) || $linenum === $r->getLine()) {
break;
}
unset($source[$linenum]);
}
$output = '';
foreach ($source as $linenum => $line) {
if ($linenum === $r->getLine()) {
$output .= '<div class="kint-highlight">'.$this->renderer->escape($line)."\n".'</div>';
} else {
$output .= '<div>'.$this->renderer->escape($line)."\n".'</div>';
}
}
if ($output) {
$data = '';
if ($r->showFileName()) {
$data = ' data-kint-filename="'.$this->renderer->escape($r->getFileName()).'"';
}
return '<div><pre class="kint-source"'.$data.' style="counter-reset: kint-l '.((int) \array_key_first($source) - 1).';">'.$output.'</pre></div><div></div>';
}
return null;
}
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\RepresentationInterface;
interface TabPluginInterface extends PluginInterface
{
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string;
}
+132
View File
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Renderer\RichRenderer;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\FixedWidthValue;
use Kint\Value\Representation\RepresentationInterface;
use Kint\Value\Representation\TableRepresentation;
use Kint\Value\StringValue;
class TablePlugin extends AbstractPlugin implements TabPluginInterface
{
public static bool $respect_str_length = true;
public function renderTab(RepresentationInterface $r, AbstractValue $v): ?string
{
if (!$r instanceof TableRepresentation) {
return null;
}
$contents = $r->getContents();
$firstrow = \reset($contents);
if (!$firstrow instanceof ArrayValue) {
return null;
}
$out = '<pre><table><thead><tr><th></th>';
foreach ($firstrow->getContents() as $field) {
$out .= '<th>'.$this->renderer->escape($field->getDisplayName()).'</th>';
}
$out .= '</tr></thead><tbody>';
foreach ($contents as $row) {
if (!$row instanceof ArrayValue) {
return null;
}
$out .= '<tr><th>'.$this->renderer->escape($row->getDisplayName()).'</th>';
foreach ($row->getContents() as $field) {
$ref = $field->getContext()->isRef() ? '&amp;' : '';
$type = $this->renderer->escape($field->getDisplayType());
$out .= '<td title="'.$ref.$type;
if (null !== ($size = $field->getDisplaySize())) {
$size = $this->renderer->escape($size);
$out .= ' ('.$size.')';
}
$out .= '">';
if ($field instanceof FixedWidthValue) {
if (null === ($dv = $field->getDisplayValue())) {
$out .= '<var>'.$ref.'null</var>';
} elseif ('boolean' === $field->getType()) {
$out .= '<var>'.$ref.$dv.'</var>';
} else {
$out .= $dv;
}
} elseif ($field instanceof StringValue) {
if (false !== $field->getEncoding()) {
$val = $field->getValueUtf8();
if (RichRenderer::$strlen_max && self::$respect_str_length) {
$val = Utils::truncateString($val, RichRenderer::$strlen_max, 'UTF-8');
}
$out .= $this->renderer->escape($val);
} else {
$out .= '<var>'.$ref.$type.'</var>';
}
} elseif ($field instanceof ArrayValue) {
$out .= '<var>'.$ref.'array</var> ('.$field->getSize().')';
} else {
$out .= '<var>'.$ref.$type.'</var>';
if (null !== $size) {
$out .= ' ('.$size.')';
}
}
if ($field->flags & AbstractValue::FLAG_BLACKLIST) {
$out .= ' <var>Blacklisted</var>';
} elseif ($field->flags & AbstractValue::FLAG_RECURSION) {
$out .= ' <var>Recursion</var>';
} elseif ($field->flags & AbstractValue::FLAG_DEPTH_LIMIT) {
$out .= ' <var>Depth Limit</var>';
}
$out .= '</td>';
}
$out .= '</tr>';
}
$out .= '</tbody></table></pre>';
return $out;
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
use Kint\Value\MethodValue;
use Kint\Value\TraceFrameValue;
class TraceFramePlugin extends AbstractPlugin implements ValuePluginInterface
{
public function renderValue(AbstractValue $v): ?string
{
if (!$v instanceof TraceFrameValue) {
return null;
}
if (null !== ($file = $v->getFile()) && null !== ($line = $v->getLine())) {
$header = '<var>'.$this->renderer->ideLink($file, $line).'</var> ';
} else {
$header = '<var>PHP internal call</var> ';
}
if ($callable = $v->getCallable()) {
if ($callable instanceof MethodValue) {
$function = $callable->getFullyQualifiedDisplayName();
} else {
$function = $callable->getDisplayName();
}
$function = $this->renderer->escape($function);
if (null !== ($url = $callable->getPhpDocUrl())) {
$function = '<a href="'.$url.'" target=_blank>'.$function.'</a>';
}
$header .= $function;
}
$children = $this->renderer->renderChildren($v);
$header = $this->renderer->renderHeaderWrapper($v->getContext(), (bool) \strlen($children), $header);
return '<dl>'.$header.$children.'</dl>';
}
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Rich;
use Kint\Value\AbstractValue;
interface ValuePluginInterface extends PluginInterface
{
public function renderValue(AbstractValue $v): ?string;
}
+623
View File
@@ -0,0 +1,623 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
use Kint\Renderer\Rich\TabPluginInterface;
use Kint\Renderer\Rich\ValuePluginInterface;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\Context\ClassDeclaredContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Context\PropertyContext;
use Kint\Value\InstanceValue;
use Kint\Value\Representation;
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\Representation\RepresentationInterface;
use Kint\Value\Representation\StringRepresentation;
use Kint\Value\Representation\ValueRepresentation;
use Kint\Value\StringValue;
/**
* @psalm-import-type Encoding from StringValue
*/
class RichRenderer extends AbstractRenderer
{
use AssetRendererTrait;
/**
* RichRenderer value plugins should implement ValuePluginInterface.
*
* @psalm-var class-string<ValuePluginInterface>[]
*/
public static array $value_plugins = [
'array_limit' => Rich\LockPlugin::class,
'blacklist' => Rich\LockPlugin::class,
'callable' => Rich\CallablePlugin::class,
'color' => Rich\ColorPlugin::class,
'depth_limit' => Rich\LockPlugin::class,
'recursion' => Rich\LockPlugin::class,
'trace_frame' => Rich\TraceFramePlugin::class,
];
/**
* RichRenderer tab plugins should implement TabPluginInterface.
*
* @psalm-var array<string, class-string<TabPluginInterface>>
*/
public static array $tab_plugins = [
'binary' => Rich\BinaryPlugin::class,
'callable' => Rich\CallableDefinitionPlugin::class,
'color' => Rich\ColorPlugin::class,
'microtime' => Rich\MicrotimePlugin::class,
'profiling' => Rich\ProfilePlugin::class,
'source' => Rich\SourcePlugin::class,
'table' => Rich\TablePlugin::class,
];
public static array $pre_render_sources = [
'script' => [
[self::class, 'renderJs'],
],
'style' => [
[self::class, 'renderCss'],
],
'raw' => [],
];
/**
* The maximum length of a string before it is truncated.
*
* Falsey to disable
*/
public static int $strlen_max = 80;
/**
* Timestamp to print in footer in date() format.
*/
public static ?string $timestamp = null;
/**
* Whether or not to render access paths.
*
* Access paths can become incredibly heavy with very deep and wide
* structures. Given mostly public variables it will typically make
* up one quarter of the output HTML size.
*
* If this is an unacceptably large amount and your browser is groaning
* under the weight of the access paths - your first order of buisiness
* should be to get a new browser. Failing that, use this to turn them off.
*/
public static bool $access_paths = true;
/**
* Assume types and sizes don't need to be escaped.
*
* Turn this off if you use anything but ascii in your class names,
* but it'll cause a slowdown of around 10%
*/
public static bool $escape_types = false;
/**
* Move all dumps to a folder at the bottom of the body.
*/
public static bool $folder = false;
public static bool $needs_pre_render = true;
public static bool $always_pre_render = false;
protected array $plugin_objs = [];
protected bool $expand = false;
protected bool $force_pre_render = false;
protected bool $use_folder = false;
public function __construct()
{
parent::__construct();
self::$theme ??= 'original.css';
$this->use_folder = self::$folder;
$this->force_pre_render = self::$always_pre_render;
}
public function setCallInfo(array $info): void
{
parent::setCallInfo($info);
if (\in_array('!', $info['modifiers'], true)) {
$this->expand = true;
$this->use_folder = false;
}
if (\in_array('@', $info['modifiers'], true)) {
$this->force_pre_render = true;
}
}
public function setStatics(array $statics): void
{
parent::setStatics($statics);
if (!empty($statics['expanded'])) {
$this->expand = true;
}
if (!empty($statics['return'])) {
$this->force_pre_render = true;
}
}
public function shouldPreRender(): bool
{
return $this->force_pre_render || self::$needs_pre_render;
}
public function render(AbstractValue $v): string
{
$render_spl_ids_stash = $this->render_spl_ids;
if ($this->render_spl_ids && $v->flags & AbstractValue::FLAG_GENERATED) {
$this->render_spl_ids = false;
}
if ($plugin = $this->getValuePlugin($v)) {
$output = $plugin->renderValue($v);
if (null !== $output && \strlen($output)) {
if (!$this->render_spl_ids && $render_spl_ids_stash) {
$this->render_spl_ids = true;
}
return $output;
}
}
$children = $this->renderChildren($v);
$header = $this->renderHeaderWrapper($v->getContext(), (bool) \strlen($children), $this->renderHeader($v));
if (!$this->render_spl_ids && $render_spl_ids_stash) {
$this->render_spl_ids = true;
}
return '<dl>'.$header.$children.'</dl>';
}
public function renderHeaderWrapper(ContextInterface $c, bool $has_children, string $contents): string
{
$out = '<dt';
if ($has_children) {
$out .= ' class="kint-parent';
if ($this->expand) {
$out .= ' kint-show';
}
$out .= '"';
}
$out .= '>';
if (self::$access_paths && $c->getDepth() > 0 && null !== ($ap = $c->getAccessPath())) {
$out .= '<span class="kint-access-path-trigger" title="Show access path"></span>';
}
if ($has_children) {
if (0 === $c->getDepth()) {
if (!$this->use_folder) {
$out .= '<span class="kint-folder-trigger" title="Move to folder"></span>';
}
$out .= '<span class="kint-search-trigger" title="Show search box"></span>';
$out .= '<input type="text" class="kint-search" value="">';
}
$out .= '<nav></nav>';
}
$out .= $contents;
if (!empty($ap)) {
$out .= '<div class="access-path">'.$this->escape($ap).'</div>';
}
return $out.'</dt>';
}
public function renderHeader(AbstractValue $v): string
{
$c = $v->getContext();
$output = '';
if ($c instanceof ClassDeclaredContext) {
$output .= '<var>'.$c->getModifiers().'</var> ';
}
$output .= '<dfn>'.$this->escape($v->getDisplayName()).'</dfn> ';
if ($c instanceof PropertyContext && null !== ($s = $c->getHooks())) {
$output .= '<var>'.$this->escape($s).'</var> ';
}
if (null !== ($s = $c->getOperator())) {
$output .= $this->escape($s, 'ASCII').' ';
}
$s = $v->getDisplayType();
if (self::$escape_types) {
$s = $this->escape($s);
}
if ($c->isRef()) {
$s = '&amp;'.$s;
}
$output .= '<var>'.$s.'</var>';
if ($v instanceof InstanceValue && $this->shouldRenderObjectIds()) {
$output .= '#'.$v->getSplObjectId();
}
$output .= ' ';
if (null !== ($s = $v->getDisplaySize())) {
if (self::$escape_types) {
$s = $this->escape($s);
}
$output .= '('.$s.') ';
}
if (null !== ($s = $v->getDisplayValue())) {
$s = \preg_replace('/\\s+/', ' ', $s);
if (self::$strlen_max) {
$s = Utils::truncateString($s, self::$strlen_max);
}
$output .= $this->escape($s);
}
return \trim($output);
}
public function renderChildren(AbstractValue $v): string
{
$contents = [];
$tabs = [];
foreach ($v->getRepresentations() as $rep) {
$result = $this->renderTab($v, $rep);
if (\strlen($result)) {
$contents[] = $result;
$tabs[] = $rep;
}
}
if (empty($tabs)) {
return '';
}
$output = '<dd>';
if (1 === \count($tabs) && $tabs[0]->labelIsImplicit()) {
$output .= \reset($contents);
} else {
$output .= '<ul class="kint-tabs">';
foreach ($tabs as $i => $tab) {
if (0 === $i) {
$output .= '<li class="kint-active-tab">';
} else {
$output .= '<li>';
}
$output .= $this->escape($tab->getLabel()).'</li>';
}
$output .= '</ul><ul class="kint-tab-contents">';
foreach ($contents as $i => $tab) {
if (0 === $i) {
$output .= '<li class="kint-show">';
} else {
$output .= '<li>';
}
$output .= $tab.'</li>';
}
$output .= '</ul>';
}
return $output.'</dd>';
}
public function preRender(): string
{
$output = '';
if ($this->shouldPreRender()) {
foreach (self::$pre_render_sources as $type => $values) {
$contents = '';
foreach ($values as $v) {
$contents .= \call_user_func($v, $this);
}
if (!\strlen($contents)) {
continue;
}
switch ($type) {
case 'script':
$output .= '<script class="kint-rich-script"';
if (null !== self::$js_nonce) {
$output .= ' nonce="'.\htmlspecialchars(self::$js_nonce).'"';
}
$output .= '>'.$contents.'</script>';
break;
case 'style':
$output .= '<style class="kint-rich-style"';
if (null !== self::$css_nonce) {
$output .= ' nonce="'.\htmlspecialchars(self::$css_nonce).'"';
}
$output .= '>'.$contents.'</style>';
break;
default:
$output .= $contents;
}
}
// Don't pre-render on every dump
if (!$this->force_pre_render) {
self::$needs_pre_render = false;
}
}
$output .= '<div class="kint-rich';
if ($this->use_folder) {
$output .= ' kint-file';
}
$output .= '">';
return $output;
}
public function postRender(): string
{
if (!$this->show_trace) {
return '</div>';
}
$output = '<footer';
if ($this->expand) {
$output .= ' class="kint-show"';
}
$output .= '>';
if (!$this->use_folder) {
$output .= '<span class="kint-folder-trigger" title="Move to folder">&mapstodown;</span>';
}
if (!empty($this->trace) && \count($this->trace) > 1) {
$output .= '<nav></nav>';
}
$output .= $this->calledFrom();
if (!empty($this->trace) && \count($this->trace) > 1) {
$output .= '<ol>';
foreach ($this->trace as $index => $step) {
if (!$index) {
continue;
}
$output .= '<li>'.$this->ideLink($step['file'], $step['line']); // closing tag not required
if (isset($step['function']) &&
!\in_array($step['function'], ['include', 'include_once', 'require', 'require_once'], true)
) {
$output .= ' [';
$output .= $step['class'] ?? '';
$output .= $step['type'] ?? '';
$output .= $step['function'].'()]';
}
}
$output .= '</ol>';
}
$output .= '</footer></div>';
return $output;
}
/**
* @psalm-param Encoding $encoding
*/
public function escape(string $string, $encoding = false): string
{
if (false === $encoding) {
$encoding = Utils::detectEncoding($string);
}
$original_encoding = $encoding;
if (false === $encoding || 'ASCII' === $encoding) {
$encoding = 'UTF-8';
}
$string = \htmlspecialchars($string, ENT_NOQUOTES, $encoding);
// this call converts all non-ASCII characters into numeirc htmlentities
if (\function_exists('mb_encode_numericentity') && 'ASCII' !== $original_encoding) {
$string = \mb_encode_numericentity($string, [0x80, 0xFFFF, 0, 0xFFFF], $encoding);
}
return $string;
}
public function ideLink(string $file, int $line): string
{
$path = $this->escape(Utils::shortenPath($file)).':'.$line;
$ideLink = self::getFileLink($file, $line);
if (null === $ideLink) {
return $path;
}
return '<a href="'.$this->escape($ideLink).'">'.$path.'</a>';
}
protected function calledFrom(): string
{
$output = '';
if (isset($this->callee['file'])) {
$output .= ' '.$this->ideLink(
$this->callee['file'],
$this->callee['line']
);
}
if (
isset($this->callee['function']) &&
(
!empty($this->callee['class']) ||
!\in_array(
$this->callee['function'],
['include', 'include_once', 'require', 'require_once'],
true
)
)
) {
$output .= ' [';
$output .= $this->callee['class'] ?? '';
$output .= $this->callee['type'] ?? '';
$output .= $this->callee['function'].'()]';
}
if ('' !== $output) {
$output = 'Called from'.$output;
}
if (null !== self::$timestamp) {
$output .= ' '.\date(self::$timestamp);
}
return $output;
}
protected function renderTab(AbstractValue $v, RepresentationInterface $rep): string
{
if ($plugin = $this->getTabPlugin($rep)) {
$output = $plugin->renderTab($rep, $v);
if (null !== $output) {
return $output;
}
}
if ($rep instanceof ValueRepresentation) {
return $this->render($rep->getValue());
}
if ($rep instanceof ContainerRepresentation) {
$output = '';
foreach ($rep->getContents() as $obj) {
$output .= $this->render($obj);
}
return $output;
}
if ($rep instanceof StringRepresentation) {
// If we're dealing with the content representation
if ($v instanceof StringValue && $rep->getValue() === $v->getValue()) {
// Only show the contents if:
if (\preg_match('/(:?[\\r\\n\\t\\f\\v]| {2})/', $rep->getValue())) {
// We have unrepresentable whitespace (Without whitespace preservation)
$show_contents = true;
} elseif (self::$strlen_max && Utils::strlen($v->getDisplayValue()) > self::$strlen_max) {
// We had to truncate getDisplayValue
$show_contents = true;
} else {
$show_contents = false;
}
} else {
$show_contents = true;
}
if ($show_contents) {
return '<pre>'.$this->escape($rep->getValue())."\n</pre>";
}
}
return '';
}
protected function getValuePlugin(AbstractValue $v): ?ValuePluginInterface
{
$hint = $v->getHint();
if (null === $hint || !isset(self::$value_plugins[$hint])) {
return null;
}
$plugin = self::$value_plugins[$hint];
if (!\is_a($plugin, ValuePluginInterface::class, true)) {
return null;
}
if (!isset($this->plugin_objs[$plugin])) {
$this->plugin_objs[$plugin] = new $plugin($this);
}
return $this->plugin_objs[$plugin];
}
protected function getTabPlugin(RepresentationInterface $r): ?TabPluginInterface
{
$hint = $r->getHint();
if (null === $hint || !isset(self::$tab_plugins[$hint])) {
return null;
}
$plugin = self::$tab_plugins[$hint];
if (!\is_a($plugin, TabPluginInterface::class, true)) {
return null;
}
if (!isset($this->plugin_objs[$plugin])) {
$this->plugin_objs[$plugin] = new $plugin($this);
}
return $this->plugin_objs[$plugin];
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Text;
use Kint\Renderer\TextRenderer;
use Kint\Value\AbstractValue;
abstract class AbstractPlugin implements PluginInterface
{
protected TextRenderer $renderer;
public function __construct(TextRenderer $r)
{
$this->renderer = $r;
}
public function renderLockedHeader(AbstractValue $v, ?string $content = null): string
{
$out = '';
if (0 === $v->getContext()->getDepth()) {
$out .= $this->renderer->colorTitle($this->renderer->renderTitle($v)).PHP_EOL;
}
$out .= $this->renderer->renderHeader($v);
if (null !== $content) {
$out .= ' '.$this->renderer->colorValue($content);
}
$out .= PHP_EOL;
return $out;
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Text;
use Kint\Value\AbstractValue;
class LockPlugin extends AbstractPlugin
{
public function render(AbstractValue $v): ?string
{
switch ($v->getHint()) {
case 'blacklist':
return $this->renderLockedHeader($v, 'BLACKLISTED');
case 'recursion':
return $this->renderLockedHeader($v, 'RECURSION');
case 'depth_limit':
return $this->renderLockedHeader($v, 'DEPTH LIMIT');
case 'array_limit':
return $this->renderLockedHeader($v, 'ARRAY LIMIT');
}
return null;
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Text;
use Kint\Renderer\PlainRenderer;
use Kint\Renderer\TextRenderer;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\MicrotimeRepresentation;
class MicrotimePlugin extends AbstractPlugin
{
protected bool $useJs = false;
public function __construct(TextRenderer $r)
{
parent::__construct($r);
if ($this->renderer instanceof PlainRenderer) {
$this->useJs = true;
}
}
public function render(AbstractValue $v): ?string
{
$r = $v->getRepresentation('microtime');
if (!$r instanceof MicrotimeRepresentation || !($dt = $r->getDateTime())) {
return null;
}
$c = $v->getContext();
$out = '';
if (0 === $c->getDepth()) {
$out .= $this->renderer->colorTitle($this->renderer->renderTitle($v)).PHP_EOL;
}
$out .= $this->renderer->renderHeader($v);
$out .= $this->renderer->renderChildren($v).PHP_EOL;
$indent = \str_repeat(' ', ($c->getDepth() + 1) * $this->renderer->indent_width);
if ($this->useJs) {
$out .= '<span data-kint-microtime-group="'.$r->getGroup().'">';
}
$out .= $indent.$this->renderer->colorType('TIME:').' ';
$out .= $this->renderer->colorValue($dt->format('Y-m-d H:i:s.u')).PHP_EOL;
if (null !== ($lap = $r->getLapTime())) {
$out .= $indent.$this->renderer->colorType('SINCE LAST CALL:').' ';
$lap = \round($lap, 4);
if ($this->useJs) {
$lap = '<span class="kint-microtime-lap">'.$lap.'</span>';
}
$out .= $this->renderer->colorValue($lap.'s').'.'.PHP_EOL;
}
if (null !== ($total = $r->getTotalTime())) {
$out .= $indent.$this->renderer->colorType('SINCE START:').' ';
$out .= $this->renderer->colorValue(\round($total, 4).'s').'.'.PHP_EOL;
}
if (null !== ($avg = $r->getAverageTime())) {
$out .= $indent.$this->renderer->colorType('AVERAGE DURATION:').' ';
$avg = \round($avg, 4);
if ($this->useJs) {
$avg = '<span class="kint-microtime-avg">'.$avg.'</span>';
}
$out .= $this->renderer->colorValue($avg.'s').'.'.PHP_EOL;
}
$bytes = Utils::getHumanReadableBytes($r->getMemoryUsage());
$mem = $r->getMemoryUsage().' bytes ('.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$bytes = Utils::getHumanReadableBytes($r->getMemoryUsageReal());
$mem .= ' (real '.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$out .= $indent.$this->renderer->colorType('MEMORY USAGE:').' ';
$out .= $this->renderer->colorValue($mem).'.'.PHP_EOL;
$bytes = Utils::getHumanReadableBytes($r->getMemoryPeakUsage());
$mem = $r->getMemoryPeakUsage().' bytes ('.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$bytes = Utils::getHumanReadableBytes($r->getMemoryPeakUsageReal());
$mem .= ' (real '.\round($bytes['value'], 3).' '.$bytes['unit'].')';
$out .= $indent.$this->renderer->colorType('PEAK MEMORY USAGE:').' ';
$out .= $this->renderer->colorValue($mem).'.'.PHP_EOL;
if ($this->useJs) {
$out .= '</span>';
}
return $out;
}
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Text;
use Kint\Renderer\TextRenderer;
use Kint\Value\AbstractValue;
interface PluginInterface
{
public function __construct(TextRenderer $r);
public function render(AbstractValue $v): ?string;
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Text;
use Kint\Value\AbstractValue;
use Kint\Value\Representation\SplFileInfoRepresentation;
class SplFileInfoPlugin extends AbstractPlugin
{
public function render(AbstractValue $v): ?string
{
$r = $v->getRepresentation('splfileinfo');
if (!$r instanceof SplFileInfoRepresentation) {
return null;
}
$out = '';
$c = $v->getContext();
if (0 === $c->getDepth()) {
$out .= $this->renderer->colorTitle($this->renderer->renderTitle($v)).PHP_EOL;
}
$out .= $this->renderer->renderHeader($v);
if (null !== $v->getDisplayValue()) {
$out .= ' =>';
}
$out .= ' '.$this->renderer->colorValue($this->renderer->escape($r->getValue())).PHP_EOL;
return $out;
}
}
+119
View File
@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer\Text;
use Kint\Value\AbstractValue;
use Kint\Value\MethodValue;
use Kint\Value\Representation\SourceRepresentation;
use Kint\Value\TraceFrameValue;
use Kint\Value\TraceValue;
class TracePlugin extends AbstractPlugin
{
public function render(AbstractValue $v): ?string
{
if (!$v instanceof TraceValue) {
return null;
}
$c = $v->getContext();
$out = '';
if (0 === $c->getDepth()) {
$out .= $this->renderer->colorTitle($this->renderer->renderTitle($v)).PHP_EOL;
}
$out .= $this->renderer->renderHeader($v).':'.PHP_EOL;
$indent = \str_repeat(' ', ($c->getDepth() + 1) * $this->renderer->indent_width);
$i = 1;
foreach ($v->getContents() as $frame) {
if (!$frame instanceof TraceFrameValue) {
continue;
}
$framedesc = $indent.\str_pad($i.': ', 4, ' ');
if (null !== ($file = $frame->getFile()) && null !== ($line = $frame->getLine())) {
$framedesc .= $this->renderer->ideLink($file, $line).PHP_EOL;
} else {
$framedesc .= 'PHP internal call'.PHP_EOL;
}
if ($callable = $frame->getCallable()) {
$framedesc .= $indent.' ';
if ($callable instanceof MethodValue) {
$framedesc .= $this->renderer->escape($callable->getContext()->owner_class.$callable->getContext()->getOperator());
}
$framedesc .= $this->renderer->escape($callable->getDisplayName());
}
$out .= $this->renderer->colorType($framedesc).PHP_EOL.PHP_EOL;
$source = $frame->getRepresentation('source');
if ($source instanceof SourceRepresentation) {
$line_wanted = $source->getLine();
$source = $source->getSourceLines();
// Trim empty lines from the start and end of the source
foreach ($source as $linenum => $line) {
if (\trim($line) || $linenum === $line_wanted) {
break;
}
unset($source[$linenum]);
}
foreach (\array_reverse($source, true) as $linenum => $line) {
if (\trim($line) || $linenum === $line_wanted) {
break;
}
unset($source[$linenum]);
}
foreach ($source as $lineno => $line) {
if ($lineno === $line_wanted) {
$out .= $indent.$this->renderer->colorValue($this->renderer->escape($line)).PHP_EOL;
} else {
$out .= $indent.$this->renderer->escape($line).PHP_EOL;
}
}
}
++$i;
}
return $out;
}
}
+400
View File
@@ -0,0 +1,400 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Renderer;
use Kint\Parser;
use Kint\Parser\PluginInterface as ParserPluginInterface;
use Kint\Renderer\Text\PluginInterface;
use Kint\Utils;
use Kint\Value\AbstractValue;
use Kint\Value\ArrayValue;
use Kint\Value\Context\ArrayContext;
use Kint\Value\Context\ClassDeclaredContext;
use Kint\Value\Context\PropertyContext;
use Kint\Value\InstanceValue;
use Kint\Value\StringValue;
/**
* @psalm-import-type Encoding from StringValue
*/
class TextRenderer extends AbstractRenderer
{
/**
* TextRenderer plugins should implement PluginInterface.
*
* @psalm-var class-string<PluginInterface>[]
*/
public static array $plugins = [
'array_limit' => Text\LockPlugin::class,
'blacklist' => Text\LockPlugin::class,
'depth_limit' => Text\LockPlugin::class,
'splfileinfo' => Text\SplFileInfoPlugin::class,
'microtime' => Text\MicrotimePlugin::class,
'recursion' => Text\LockPlugin::class,
'trace' => Text\TracePlugin::class,
];
/**
* Parser plugins must be instanceof one of these or
* it will be removed for performance reasons.
*
* @psalm-var class-string<ParserPluginInterface>[]
*/
public static array $parser_plugin_whitelist = [
Parser\ArrayLimitPlugin::class,
Parser\ArrayObjectPlugin::class,
Parser\BlacklistPlugin::class,
Parser\ClosurePlugin::class,
Parser\DateTimePlugin::class,
Parser\DomPlugin::class,
Parser\EnumPlugin::class,
Parser\IteratorPlugin::class,
Parser\MicrotimePlugin::class,
Parser\MysqliPlugin::class,
Parser\SimpleXMLElementPlugin::class,
Parser\SplFileInfoPlugin::class,
Parser\StreamPlugin::class,
Parser\TracePlugin::class,
];
/**
* The maximum length of a string before it is truncated.
*
* Falsey to disable
*/
public static int $strlen_max = 0;
/**
* Timestamp to print in footer in date() format.
*/
public static ?string $timestamp = null;
/**
* The default width of the terminal for headers.
*/
public static int $default_width = 80;
/**
* Indentation width.
*/
public static int $default_indent = 4;
/**
* Decorate the header and footer.
*/
public static bool $decorations = true;
public int $header_width = 80;
public int $indent_width = 4;
protected array $plugin_objs = [];
public function __construct()
{
parent::__construct();
$this->header_width = self::$default_width;
$this->indent_width = self::$default_indent;
}
public function render(AbstractValue $v): string
{
$render_spl_ids_stash = $this->render_spl_ids;
if ($this->render_spl_ids && ($v->flags & AbstractValue::FLAG_GENERATED)) {
$this->render_spl_ids = false;
}
if ($plugin = $this->getPlugin($v)) {
$output = $plugin->render($v);
if (null !== $output && \strlen($output)) {
if (!$this->render_spl_ids && $render_spl_ids_stash) {
$this->render_spl_ids = true;
}
return $output;
}
}
$out = '';
$c = $v->getContext();
if (0 === $c->getDepth()) {
$out .= $this->colorTitle($this->renderTitle($v)).PHP_EOL;
}
$out .= $header = $this->renderHeader($v);
$out .= $this->renderChildren($v);
if (\strlen($header)) {
$out .= PHP_EOL;
}
if (!$this->render_spl_ids && $render_spl_ids_stash) {
$this->render_spl_ids = true;
}
return $out;
}
public function boxText(string $text, int $width): string
{
$out = '┌'.\str_repeat('─', $width - 2).'┐'.PHP_EOL;
if (\strlen($text)) {
$text = Utils::truncateString($text, $width - 4);
$text = \str_pad($text, $width - 4);
$out .= '│ '.$this->escape($text).' │'.PHP_EOL;
}
$out .= '└'.\str_repeat('─', $width - 2).'┘';
return $out;
}
public function renderTitle(AbstractValue $v): string
{
if (self::$decorations) {
return $this->boxText($v->getDisplayName(), $this->header_width);
}
return Utils::truncateString($v->getDisplayName(), $this->header_width);
}
public function renderHeader(AbstractValue $v): string
{
$output = [];
$c = $v->getContext();
if ($c->getDepth() > 0) {
if ($c instanceof ClassDeclaredContext) {
$output[] = $this->colorType($c->getModifiers());
}
if ($c instanceof ArrayContext) {
$output[] = $this->escape(\var_export($c->getName(), true));
} else {
$output[] = $this->escape((string) $c->getName());
}
if ($c instanceof PropertyContext && null !== ($s = $c->getHooks())) {
$output[] = $this->colorType($this->escape($s));
}
if (null !== ($s = $c->getOperator())) {
$output[] = $this->escape($s);
}
}
$s = $v->getDisplayType();
if ($c->isRef()) {
$s = '&'.$s;
}
$s = $this->colorType($this->escape($s));
if ($v instanceof InstanceValue && $this->shouldRenderObjectIds()) {
$s .= '#'.$v->getSplObjectId();
}
$output[] = $s;
if (null !== ($s = $v->getDisplaySize())) {
$output[] = '('.$this->escape($s).')';
}
if (null !== ($s = $v->getDisplayValue())) {
if (self::$strlen_max) {
$s = Utils::truncateString($s, self::$strlen_max);
}
$output[] = $this->colorValue($this->escape($s));
}
return \str_repeat(' ', $c->getDepth() * $this->indent_width).\implode(' ', $output);
}
public function renderChildren(AbstractValue $v): string
{
$children = $v->getDisplayChildren();
if (!$children) {
if ($v instanceof ArrayValue) {
return ' []';
}
return '';
}
if ($v instanceof ArrayValue) {
$output = ' [';
} elseif ($v instanceof InstanceValue) {
$output = ' (';
} else {
$output = '';
}
$output .= PHP_EOL;
foreach ($children as $child) {
$output .= $this->render($child);
}
$indent = \str_repeat(' ', $v->getContext()->getDepth() * $this->indent_width);
if ($v instanceof ArrayValue) {
$output .= $indent.']';
} elseif ($v instanceof InstanceValue) {
$output .= $indent.')';
}
return $output;
}
public function colorValue(string $string): string
{
return $string;
}
public function colorType(string $string): string
{
return $string;
}
public function colorTitle(string $string): string
{
return $string;
}
public function postRender(): string
{
if (self::$decorations) {
$output = \str_repeat('═', $this->header_width);
} else {
$output = '';
}
if (!$this->show_trace) {
return $this->colorTitle($output);
}
if ($output) {
$output .= PHP_EOL;
}
return $this->colorTitle($output.$this->calledFrom().PHP_EOL);
}
public function filterParserPlugins(array $plugins): array
{
$return = [];
foreach ($plugins as $plugin) {
foreach (self::$parser_plugin_whitelist as $whitelist) {
if ($plugin instanceof $whitelist) {
$return[] = $plugin;
continue 2;
}
}
}
return $return;
}
public function ideLink(string $file, int $line): string
{
return $this->escape(Utils::shortenPath($file)).':'.$line;
}
/**
* @psalm-param Encoding $encoding
*/
public function escape(string $string, $encoding = false): string
{
return $string;
}
protected function calledFrom(): string
{
$output = '';
if (isset($this->callee['file'])) {
$output .= 'Called from '.$this->ideLink(
$this->callee['file'],
$this->callee['line']
);
}
if (
isset($this->callee['function']) &&
(
!empty($this->callee['class']) ||
!\in_array(
$this->callee['function'],
['include', 'include_once', 'require', 'require_once'],
true
)
)
) {
$output .= ' [';
$output .= $this->callee['class'] ?? '';
$output .= $this->callee['type'] ?? '';
$output .= $this->callee['function'].'()]';
}
if (null !== self::$timestamp) {
if (\strlen($output)) {
$output .= ' ';
}
$output .= \date(self::$timestamp);
}
return $output;
}
protected function getPlugin(AbstractValue $v): ?PluginInterface
{
$hint = $v->getHint();
if (null === $hint || !isset(self::$plugins[$hint])) {
return null;
}
$plugin = self::$plugins[$hint];
if (!\is_a($plugin, PluginInterface::class, true)) {
return null;
}
if (!isset($this->plugin_objs[$plugin])) {
$this->plugin_objs[$plugin] = new $plugin($this);
}
return $this->plugin_objs[$plugin];
}
}
+567
View File
@@ -0,0 +1,567 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint;
use Kint\Value\StringValue;
use Kint\Value\TraceFrameValue;
use ReflectionNamedType;
use ReflectionType;
use UnexpectedValueException;
/**
* A collection of utility methods. Should all be static methods with no dependencies.
*
* @psalm-import-type Encoding from StringValue
* @psalm-import-type TraceFrame from TraceFrameValue
*/
final class Utils
{
public const BT_STRUCTURE = [
'function' => 'string',
'line' => 'integer',
'file' => 'string',
'class' => 'string',
'object' => 'object',
'type' => 'string',
'args' => 'array',
];
public const BYTE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];
/**
* @var array Character encodings to detect
*
* @see https://secure.php.net/function.mb-detect-order
*
* In practice, mb_detect_encoding can only successfully determine the
* difference between the following common charsets at once without
* breaking things for one of the other charsets:
* - ASCII
* - UTF-8
* - SJIS
* - EUC-JP
*
* The order of the charsets is significant. If you put UTF-8 before ASCII
* it will never match ASCII, because UTF-8 is a superset of ASCII.
* Similarly, SJIS and EUC-JP frequently match UTF-8 strings, so you should
* check UTF-8 first. SJIS and EUC-JP seem to work either way, but SJIS is
* more common so it should probably be first.
*
* While you're free to experiment with other charsets, remember to keep
* this behavior in mind when setting up your char_encodings array.
*
* This depends on the mbstring extension
*/
public static array $char_encodings = [
'ASCII',
'UTF-8',
];
/**
* @var array Legacy character encodings to detect
*
* @see https://secure.php.net/function.iconv
*
* Assuming the other encoding checks fail, this will perform a
* simple iconv conversion to check for invalid bytes. If any are
* found it will not match.
*
* This can be useful for ambiguous single byte encodings like
* windows-125x and iso-8859-x which have practically undetectable
* differences because they use every single byte available.
*
* This is *NOT* reliable and should not be trusted implicitly. Since it
* works by triggering and suppressing conversion warnings, your error
* handler may complain.
*
* As with char_encodings, the order of the charsets is significant.
*
* This depends on the iconv extension
*/
public static array $legacy_encodings = [];
/**
* @var array Path aliases that will be displayed instead of the full path.
*
* Keys are paths, values are replacement strings
*
* Example for laravel:
*
* Utils::$path_aliases = [
* base_path() => '<BASE>',
* app_path() => '<APP>',
* base_path().'/vendor' => '<VENDOR>',
* ];
*
* Defaults to [$_SERVER['DOCUMENT_ROOT'] => '<ROOT>']
*
* @psalm-var array<non-empty-string, string>
*/
public static array $path_aliases = [];
/**
* @codeCoverageIgnore
*
* @psalm-suppress UnusedConstructor
*/
private function __construct()
{
}
/**
* Turns a byte value into a human-readable representation.
*
* @param int $value Amount of bytes
*
* @return array Human readable value and unit
*
* @psalm-return array{value: float, unit: 'B'|'KB'|'MB'|'GB'|'TB'}
*
* @psalm-pure
*/
public static function getHumanReadableBytes(int $value): array
{
$negative = $value < 0;
$value = \abs($value);
if ($value < 1024) {
$i = 0;
$value = \floor($value);
} elseif ($value < 0xFFFCCCCCCCCCCCC >> 40) {
$i = 1;
} elseif ($value < 0xFFFCCCCCCCCCCCC >> 30) {
$i = 2;
} elseif ($value < 0xFFFCCCCCCCCCCCC >> 20) {
$i = 3;
} else {
$i = 4;
}
if ($i) {
$value = $value / \pow(1024, $i);
}
if ($negative) {
$value *= -1;
}
return [
'value' => \round($value, 1),
'unit' => self::BYTE_UNITS[$i],
];
}
/** @psalm-pure */
public static function isSequential(array $array): bool
{
return \array_keys($array) === \range(0, \count($array) - 1);
}
/** @psalm-pure */
public static function isAssoc(array $array): bool
{
return (bool) \count(\array_filter(\array_keys($array), 'is_string'));
}
/**
* @psalm-assert-if-true list<TraceFrame> $trace
*/
public static function isTrace(array $trace): bool
{
if (!self::isSequential($trace)) {
return false;
}
$file_found = false;
foreach ($trace as $frame) {
if (!\is_array($frame) || !isset($frame['function'])) {
return false;
}
if (isset($frame['class']) && !\class_exists($frame['class'], false)) {
return false;
}
foreach ($frame as $key => $val) {
if (!isset(self::BT_STRUCTURE[$key])) {
return false;
}
if (\gettype($val) !== self::BT_STRUCTURE[$key]) {
return false;
}
if ('file' === $key) {
$file_found = true;
}
}
}
return $file_found;
}
/**
* @psalm-param TraceFrame $frame
*
* @psalm-pure
*/
public static function traceFrameIsListed(array $frame, array $matches): bool
{
if (isset($frame['class'])) {
$called = [\strtolower($frame['class']), \strtolower($frame['function'])];
} else {
$called = \strtolower($frame['function']);
}
return \in_array($called, $matches, true);
}
/** @psalm-pure */
public static function normalizeAliases(array $aliases): array
{
foreach ($aliases as $index => $alias) {
if (\is_array($alias) && 2 === \count($alias)) {
$alias = \array_values(\array_filter($alias, 'is_string'));
if (2 === \count($alias) && self::isValidPhpName($alias[1]) && self::isValidPhpNamespace($alias[0])) {
$aliases[$index] = [
\strtolower(\ltrim($alias[0], '\\')),
\strtolower($alias[1]),
];
} else {
unset($aliases[$index]);
continue;
}
} elseif (\is_string($alias)) {
if (self::isValidPhpNamespace($alias)) {
$alias = \explode('\\', \strtolower($alias));
$aliases[$index] = \end($alias);
} else {
unset($aliases[$index]);
continue;
}
} else {
unset($aliases[$index]);
}
}
return \array_values($aliases);
}
/** @psalm-pure */
public static function isValidPhpName(string $name): bool
{
return (bool) \preg_match('/^[a-zA-Z_\\x80-\\xff][a-zA-Z0-9_\\x80-\\xff]*$/', $name);
}
/** @psalm-pure */
public static function isValidPhpNamespace(string $ns): bool
{
$parts = \explode('\\', $ns);
if ('' === \reset($parts)) {
\array_shift($parts);
}
if (!\count($parts)) {
return false;
}
foreach ($parts as $part) {
if (!self::isValidPhpName($part)) {
return false;
}
}
return true;
}
/**
* trigger_error before PHP 8.1 truncates the error message at nul
* so we have to sanitize variable strings before using them.
*
* @psalm-pure
*/
public static function errorSanitizeString(string $input): string
{
if (KINT_PHP82 || '' === $input) {
return $input;
}
return \strtok($input, "\0"); // @codeCoverageIgnore
}
/** @psalm-pure */
public static function getTypeString(ReflectionType $type): string
{
// @codeCoverageIgnoreStart
// ReflectionType::__toString was deprecated in 7.4 and undeprecated in 8
// and toString doesn't correctly show the nullable ? in the type before 8
if (!KINT_PHP80) {
if (!$type instanceof ReflectionNamedType) {
throw new UnexpectedValueException('ReflectionType on PHP 7 must be ReflectionNamedType');
}
$name = $type->getName();
if ($type->allowsNull() && 'mixed' !== $name && false === \strpos($name, '|')) {
$name = '?'.$name;
}
return $name;
}
// @codeCoverageIgnoreEnd
return (string) $type;
}
/**
* @psalm-param Encoding $encoding
*/
public static function truncateString(string $input, int $length = PHP_INT_MAX, string $end = '...', $encoding = false): string
{
$endlength = self::strlen($end);
if ($endlength >= $length) {
$endlength = 0;
$end = '';
}
if (self::strlen($input, $encoding) > $length) {
return self::substr($input, 0, $length - $endlength, $encoding).$end;
}
return $input;
}
/**
* @psalm-return Encoding
*/
public static function detectEncoding(string $string)
{
if (\function_exists('mb_detect_encoding')) {
$ret = \mb_detect_encoding($string, self::$char_encodings, true);
if (false !== $ret) {
return $ret;
}
}
// Pretty much every character encoding uses first 32 bytes as control
// characters. If it's not a multi-byte format it's safe to say matching
// any control character besides tab, nl, and cr means it's binary.
if (\preg_match('/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]/', $string)) {
return false;
}
if (\function_exists('iconv')) {
foreach (self::$legacy_encodings as $encoding) {
// Iconv detection works by triggering
// "Detected an illegal character in input string" notices
// This notice does not become a TypeError with strict_types
// so we don't have to wrap this in a try catch
if (@\iconv($encoding, $encoding, $string) === $string) {
return $encoding;
}
}
} elseif (!\function_exists('mb_detect_encoding')) { // @codeCoverageIgnore
// If a user has neither mb_detect_encoding, nor iconv, nor the
// polyfills, there's not much we can do about it...
// Pretend it's ASCII and pray the browser renders it properly.
return 'ASCII'; // @codeCoverageIgnore
}
return false;
}
/**
* @psalm-param Encoding $encoding
*/
public static function strlen(string $string, $encoding = false): int
{
if (\function_exists('mb_strlen')) {
if (false === $encoding) {
$encoding = self::detectEncoding($string);
}
if (false !== $encoding && 'ASCII' !== $encoding) {
return \mb_strlen($string, $encoding);
}
}
return \strlen($string);
}
/**
* @psalm-param Encoding $encoding
*/
public static function substr(string $string, int $start, ?int $length = null, $encoding = false): string
{
if (\function_exists('mb_substr')) {
if (false === $encoding) {
$encoding = self::detectEncoding($string);
}
if (false !== $encoding && 'ASCII' !== $encoding) {
return \mb_substr($string, $start, $length, $encoding);
}
}
// Special case for substr/mb_substr discrepancy
if ('' === $string) {
return '';
}
return \substr($string, $start, $length ?? PHP_INT_MAX);
}
public static function shortenPath(string $file): string
{
$split = \explode('/', \str_replace('\\', '/', $file));
$longest_match = 0;
$match = '';
foreach (self::$path_aliases as $path => $alias) {
$path = \explode('/', \str_replace('\\', '/', $path));
if (\count($path) < 2) {
continue;
}
if (\array_slice($split, 0, \count($path)) === $path && \count($path) > $longest_match) {
$longest_match = \count($path);
$match = $alias;
}
}
if ($longest_match) {
$suffix = \implode('/', \array_slice($split, $longest_match));
if (\preg_match('%^/*$%', $suffix)) {
return $match;
}
return $match.'/'.$suffix;
}
// fallback to find common path with Kint dir
$kint = \explode('/', \str_replace('\\', '/', KINT_DIR));
$had_real_path_part = false;
foreach ($split as $i => $part) {
if (!isset($kint[$i]) || $kint[$i] !== $part) {
if (!$had_real_path_part) {
break;
}
$suffix = \implode('/', \array_slice($split, $i));
if (\preg_match('%^/*$%', $suffix)) {
break;
}
$prefix = $i > 1 ? '.../' : '/';
return $prefix.$suffix;
}
if ($i > 0 && \strlen($kint[$i])) {
$had_real_path_part = true;
}
}
return $file;
}
public static function composerGetExtras(string $key = 'kint'): array
{
if (0 === \strpos(KINT_DIR, 'phar://')) {
// Only run inside phar file, so skip for code coverage
return []; // @codeCoverageIgnore
}
$extras = [];
$folder = KINT_DIR.'/vendor';
for ($i = 0; $i < 4; ++$i) {
$installed = $folder.'/composer/installed.json';
if (\file_exists($installed) && \is_readable($installed)) {
$packages = \json_decode(\file_get_contents($installed), true);
if (!\is_array($packages)) {
continue;
}
// Composer 2.0 Compatibility: packages are now wrapped into a "packages" top level key instead of the whole file being the package array
// @see https://getcomposer.org/upgrade/UPGRADE-2.0.md
foreach ($packages['packages'] ?? $packages as $package) {
if (\is_array($package['extra'][$key] ?? null)) {
$extras = \array_replace($extras, $package['extra'][$key]);
}
}
$folder = \dirname($folder);
if (\file_exists($folder.'/composer.json') && \is_readable($folder.'/composer.json')) {
$composer = \json_decode(\file_get_contents($folder.'/composer.json'), true);
if (\is_array($composer['extra'][$key] ?? null)) {
$extras = \array_replace($extras, $composer['extra'][$key]);
}
}
break;
}
$folder = \dirname($folder);
}
return $extras;
}
/**
* @codeCoverageIgnore
*/
public static function composerSkipFlags(): void
{
if (\defined('KINT_SKIP_FACADE') && \defined('KINT_SKIP_HELPERS')) {
return;
}
$extras = self::composerGetExtras();
if (!empty($extras['disable-facade']) && !\defined('KINT_SKIP_FACADE')) {
\define('KINT_SKIP_FACADE', true);
}
if (!empty($extras['disable-helpers']) && !\defined('KINT_SKIP_HELPERS')) {
\define('KINT_SKIP_HELPERS', true);
}
}
}
+190
View File
@@ -0,0 +1,190 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Representation\RepresentationInterface;
use OutOfRangeException;
/**
* @psalm-import-type ValueName from ContextInterface
*
* @psalm-type ValueFlags int-mask-of<AbstractValue::FLAG_*>
*/
abstract class AbstractValue
{
public const FLAG_NONE = 0;
public const FLAG_GENERATED = 1 << 0;
public const FLAG_BLACKLIST = 1 << 1;
public const FLAG_RECURSION = 1 << 2;
public const FLAG_DEPTH_LIMIT = 1 << 3;
public const FLAG_ARRAY_LIMIT = 1 << 4;
/** @psalm-var ValueFlags */
public int $flags = self::FLAG_NONE;
/** @psalm-readonly */
protected ContextInterface $context;
/** @psalm-readonly string */
protected string $type;
/** @psalm-var RepresentationInterface[] */
protected array $representations = [];
public function __construct(ContextInterface $context, string $type)
{
$this->context = $context;
$this->type = $type;
}
public function __clone()
{
$this->context = clone $this->context;
}
public function getContext(): ContextInterface
{
return $this->context;
}
public function getHint(): ?string
{
if (self::FLAG_NONE === $this->flags) {
return null;
}
if ($this->flags & self::FLAG_BLACKLIST) {
return 'blacklist';
}
if ($this->flags & self::FLAG_RECURSION) {
return 'recursion';
}
if ($this->flags & self::FLAG_DEPTH_LIMIT) {
return 'depth_limit';
}
if ($this->flags & self::FLAG_ARRAY_LIMIT) {
return 'array_limit';
}
return null;
}
public function getType(): string
{
return $this->type;
}
public function addRepresentation(RepresentationInterface $rep, ?int $pos = null): void
{
if (isset($this->representations[$rep->getName()])) {
throw new OutOfRangeException('Representation already exists');
}
if (null === $pos) {
$this->representations[$rep->getName()] = $rep;
} else {
$this->representations = \array_merge(
\array_slice($this->representations, 0, $pos),
[$rep->getName() => $rep],
\array_slice($this->representations, $pos)
);
}
}
public function replaceRepresentation(RepresentationInterface $rep, ?int $pos = null): void
{
if (null === $pos) {
$this->representations[$rep->getName()] = $rep;
} else {
$this->removeRepresentation($rep);
$this->addRepresentation($rep, $pos);
}
}
/**
* @param RepresentationInterface|string $rep
*/
public function removeRepresentation($rep): void
{
if ($rep instanceof RepresentationInterface) {
unset($this->representations[$rep->getName()]);
} else { // String
unset($this->representations[$rep]);
}
}
public function getRepresentation(string $name): ?RepresentationInterface
{
return $this->representations[$name] ?? null;
}
/** @psalm-return RepresentationInterface[] */
public function getRepresentations(): array
{
return $this->representations;
}
/** @psalm-param RepresentationInterface[] $reps */
public function appendRepresentations(array $reps): void
{
foreach ($reps as $rep) {
$this->addRepresentation($rep);
}
}
/** @psalm-api */
public function clearRepresentations(): void
{
$this->representations = [];
}
public function getDisplayType(): string
{
return $this->type;
}
public function getDisplayName(): string
{
return (string) $this->context->getName();
}
public function getDisplaySize(): ?string
{
return null;
}
public function getDisplayValue(): ?string
{
return null;
}
/** @psalm-return AbstractValue[] */
public function getDisplayChildren(): array
{
return [];
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Kint\Value\Context\ContextInterface;
class ArrayValue extends AbstractValue
{
/** @psalm-readonly */
protected int $size;
/**
* @psalm-readonly
*
* @psalm-var AbstractValue[]
*/
protected array $contents;
/** @psalm-param AbstractValue[] $contents */
public function __construct(ContextInterface $context, int $size, array $contents)
{
parent::__construct($context, 'array');
$this->size = $size;
$this->contents = $contents;
}
public function getSize(): int
{
return $this->size;
}
/** @psalm-return AbstractValue[] */
public function getContents()
{
return $this->contents;
}
public function getDisplaySize(): string
{
return (string) $this->size;
}
public function getDisplayChildren(): array
{
return $this->contents;
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Kint\Value\Context\ContextInterface;
class ClosedResourceValue extends AbstractValue
{
public function __construct(ContextInterface $context)
{
parent::__construct($context, 'resource (closed)');
}
public function getDisplayType(): string
{
return 'closed resource';
}
}
+120
View File
@@ -0,0 +1,120 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Closure;
use Kint\Utils;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ContextInterface;
use ReflectionFunction;
class ClosureValue extends InstanceValue
{
use ParameterHoldingTrait;
/** @psalm-readonly */
protected ?string $filename;
/** @psalm-readonly */
protected ?int $startline;
public function __construct(ContextInterface $context, Closure $cl)
{
parent::__construct($context, \get_class($cl), \spl_object_hash($cl), \spl_object_id($cl));
/**
* @psalm-suppress UnnecessaryVarAnnotation
*
* @psalm-var ContextInterface $this->context
* Psalm bug #11113
*/
$closure = new ReflectionFunction($cl);
if ($closure->isUserDefined()) {
$this->filename = $closure->getFileName();
$this->startline = $closure->getStartLine();
} else {
$this->filename = null;
$this->startline = null;
}
$parameters = [];
foreach ($closure->getParameters() as $param) {
$parameters[] = new ParameterBag($param);
}
$this->parameters = $parameters;
if (!$this->context instanceof BaseContext) {
return;
}
if (0 !== $this->context->getDepth()) {
return;
}
$ap = $this->context->getAccessPath();
if (null === $ap) {
return;
}
if (\preg_match('/^\\((function|fn)\\s*\\(/i', $ap, $match)) {
$this->context->name = \strtolower($match[1]);
}
}
/** @psalm-api */
public function getFileName(): ?string
{
return $this->filename;
}
/** @psalm-api */
public function getStartLine(): ?int
{
return $this->startline;
}
public function getDisplaySize(): ?string
{
return null;
}
public function getDisplayName(): string
{
return $this->context->getName().'('.$this->getParams().')';
}
public function getDisplayValue(): ?string
{
if (null !== $this->filename && null !== $this->startline) {
return Utils::shortenPath($this->filename).':'.$this->startline;
}
return parent::getDisplayValue();
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
class ColorValue extends StringValue
{
public function getHint(): string
{
return parent::getHint() ?? 'color';
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
class ArrayContext extends BaseContext
{
public function getOperator(): ?string
{
return '=>';
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
use InvalidArgumentException;
/**
* @psalm-import-type ValueName from ContextInterface
*/
class BaseContext implements ContextInterface
{
/** @psalm-var ValueName */
public $name;
public int $depth = 0;
public bool $reference = false;
public ?string $access_path = null;
/** @psalm-param mixed $name */
public function __construct($name)
{
if (!\is_string($name) && !\is_int($name)) {
throw new InvalidArgumentException('Context names must be string|int');
}
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function getDepth(): int
{
return $this->depth;
}
public function getOperator(): ?string
{
return null;
}
public function isRef(): bool
{
return $this->reference;
}
/** @psalm-param ?class-string $scope */
public function isAccessible(?string $scope): bool
{
return true;
}
public function getAccessPath(): ?string
{
return $this->access_path;
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
class ClassConstContext extends ClassDeclaredContext
{
public bool $final = false;
public function getName(): string
{
return $this->owner_class.'::'.$this->name;
}
public function getOperator(): string
{
return '::';
}
public function getModifiers(): string
{
$final = $this->final ? 'final ' : '';
return $final.$this->getAccess().' const';
}
}
@@ -0,0 +1,94 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
use __PHP_Incomplete_Class;
abstract class ClassDeclaredContext extends ClassOwnedContext
{
public const ACCESS_PUBLIC = 1;
public const ACCESS_PROTECTED = 2;
public const ACCESS_PRIVATE = 3;
/** @psalm-var self::ACCESS_* */
public int $access;
/**
* @psalm-param class-string $owner_class
* @psalm-param self::ACCESS_* $access
*/
public function __construct(string $name, string $owner_class, int $access)
{
parent::__construct($name, $owner_class);
$this->access = $access;
}
abstract public function getModifiers(): string;
/** @psalm-param ?class-string $scope */
public function isAccessible(?string $scope): bool
{
if (__PHP_Incomplete_Class::class === $this->owner_class) {
return false;
}
if (self::ACCESS_PUBLIC === $this->access) {
return true;
}
if (null === $scope) {
return false;
}
if (self::ACCESS_PRIVATE === $this->access) {
return $scope === $this->owner_class;
}
if (\is_a($scope, $this->owner_class, true)) {
return true;
}
if (\is_a($this->owner_class, $scope, true)) {
return true;
}
return false;
}
protected function getAccess(): string
{
switch ($this->access) {
case self::ACCESS_PUBLIC:
return 'public';
case self::ACCESS_PROTECTED:
return 'protected';
case self::ACCESS_PRIVATE:
return 'private';
}
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
use __PHP_Incomplete_Class;
class ClassOwnedContext extends BaseContext
{
/** @psalm-var class-string */
public string $owner_class;
/** @psalm-param class-string $owner_class */
public function __construct(string $name, string $owner_class)
{
parent::__construct($name);
$this->owner_class = $owner_class;
}
public function getName(): string
{
return (string) $this->name;
}
public function getOperator(): string
{
return '->';
}
/** @psalm-param ?class-string $scope */
public function isAccessible(?string $scope): bool
{
return __PHP_Incomplete_Class::class !== $this->owner_class;
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
/**
* Contexts represent the data that has to be found out about a zval _before_
* passing it into the next parse depth. This includes the access path, whether
* it's a reference or not, and OOP related stuff like visibility.
*
* @psalm-type ValueName = string|int
*/
interface ContextInterface
{
/** @psalm-return ValueName */
public function getName();
public function getDepth(): int;
public function isRef(): bool;
/** @psalm-param ?class-string $scope */
public function isAccessible(?string $scope): bool;
public function getAccessPath(): ?string;
/** @psalm-return ?non-empty-string */
public function getOperator(): ?string;
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
abstract class DoubleAccessMemberContext extends ClassDeclaredContext
{
/** @psalm-var ?self::ACCESS_* */
public ?int $access_set = null;
protected function getAccess(): string
{
switch ($this->access) {
case self::ACCESS_PUBLIC:
if (self::ACCESS_PRIVATE === $this->access_set) {
return 'private(set)';
}
if (self::ACCESS_PROTECTED === $this->access_set) {
return 'protected(set)';
}
return 'public';
case self::ACCESS_PROTECTED:
if (self::ACCESS_PRIVATE === $this->access_set) {
return 'protected private(set)';
}
return 'protected';
case self::ACCESS_PRIVATE:
return 'private';
}
}
}
+140
View File
@@ -0,0 +1,140 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
use Kint\Value\InstanceValue;
use ReflectionMethod;
class MethodContext extends ClassDeclaredContext
{
public const MAGIC_NAMES = [
'__construct' => true,
'__destruct' => true,
'__call' => true,
'__callstatic' => true,
'__get' => true,
'__set' => true,
'__isset' => true,
'__unset' => true,
'__sleep' => true,
'__wakeup' => true,
'__serialize' => true,
'__unserialize' => true,
'__tostring' => true,
'__invoke' => true,
'__set_state' => true,
'__clone' => true,
'__debuginfo' => true,
];
public bool $final = false;
public bool $abstract = false;
public bool $static = false;
/**
* Whether the method was inherited from a parent class or interface.
*
* It's important to note that we never show static methods as
* "inherited" except when abstract via an interface.
*/
public bool $inherited = false;
public function __construct(ReflectionMethod $method)
{
parent::__construct(
$method->getName(),
$method->getDeclaringClass()->name,
ClassDeclaredContext::ACCESS_PUBLIC
);
$this->depth = 1;
$this->static = $method->isStatic();
$this->abstract = $method->isAbstract();
$this->final = $method->isFinal();
if ($method->isProtected()) {
$this->access = ClassDeclaredContext::ACCESS_PROTECTED;
} elseif ($method->isPrivate()) {
$this->access = ClassDeclaredContext::ACCESS_PRIVATE;
}
}
public function getOperator(): string
{
if ($this->static) {
return '::';
}
return '->';
}
public function getModifiers(): string
{
if ($this->abstract) {
$out = 'abstract ';
} elseif ($this->final) {
$out = 'final ';
} else {
$out = '';
}
$out .= $this->getAccess();
if ($this->static) {
$out .= ' static';
}
return $out;
}
public function setAccessPathFromParent(?InstanceValue $parent): void
{
$name = \strtolower($this->getName());
if ($this->static && !isset(self::MAGIC_NAMES[$name])) {
$this->access_path = '\\'.$this->owner_class.'::'.$this->name.'()';
} elseif (null === $parent) {
$this->access_path = null;
} else {
$c = $parent->getContext();
if ('__construct' === $name) {
$this->access_path = 'new \\'.$parent->getClassName().'()';
} elseif (null === $c->getAccessPath()) {
$this->access_path = null;
} elseif ('__invoke' === $name) {
$this->access_path = $c->getAccessPath().'()';
} elseif ('__clone' === $name) {
$this->access_path = 'clone '.$c->getAccessPath();
} elseif ('__tostring' === $name) {
$this->access_path = '(string) '.$c->getAccessPath();
} elseif (isset(self::MAGIC_NAMES[$name])) {
$this->access_path = null;
} else {
$this->access_path = $c->getAccessPath().'->'.$this->name.'()';
}
}
}
}
@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
class PropertyContext extends DoubleAccessMemberContext
{
public const HOOK_NONE = 0;
public const HOOK_GET = 1 << 0;
public const HOOK_GET_REF = 1 << 1;
public const HOOK_SET = 1 << 2;
public const HOOK_SET_TYPE = 1 << 3;
public bool $readonly = false;
/** @psalm-var int-mask-of<self::HOOK_*> */
public int $hooks = self::HOOK_NONE;
public ?string $hook_set_type = null;
public function getModifiers(): string
{
$out = $this->getAccess();
if ($this->readonly) {
$out .= ' readonly';
}
return $out;
}
public function getHooks(): ?string
{
if (self::HOOK_NONE === $this->hooks) {
return null;
}
$out = '{ ';
if ($this->hooks & self::HOOK_GET) {
if ($this->hooks & self::HOOK_GET_REF) {
$out .= '&';
}
$out .= 'get; ';
}
if ($this->hooks & self::HOOK_SET) {
if ($this->hooks & self::HOOK_SET_TYPE && '' !== ($this->hook_set_type ?? '')) {
$out .= 'set('.$this->hook_set_type.'); ';
} else {
$out .= 'set; ';
}
}
$out .= '}';
return $out;
}
}
@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value\Context;
class StaticPropertyContext extends DoubleAccessMemberContext
{
public bool $final = false;
public function getName(): string
{
return $this->owner_class.'::$'.$this->name;
}
public function getOperator(): string
{
return '::';
}
public function getModifiers(): string
{
$final = $this->final ? 'final ' : '';
return $final.$this->getAccess().' static';
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use DateTimeInterface;
use Kint\Value\Context\ContextInterface;
class DateTimeValue extends InstanceValue
{
/** @psalm-readonly */
protected DateTimeInterface $dt;
public function __construct(ContextInterface $context, DateTimeInterface $dt)
{
parent::__construct($context, \get_class($dt), \spl_object_hash($dt), \spl_object_id($dt));
$this->dt = clone $dt;
}
public function getHint(): string
{
return parent::getHint() ?? 'datetime';
}
public function getDisplayValue(): string
{
$stamp = $this->dt->format('Y-m-d H:i:s');
if ((int) ($micro = $this->dt->format('u'))) {
$stamp .= '.'.$micro;
}
$stamp .= $this->dt->format(' P');
$tzn = $this->dt->getTimezone()->getName();
if ('+' !== $tzn[0] && '-' !== $tzn[0]) {
$stamp .= $this->dt->format(' T');
}
return $stamp;
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Kint\Utils;
use ReflectionFunctionAbstract;
/** @psalm-api */
final class DeclaredCallableBag
{
use ParameterHoldingTrait;
/** @psalm-readonly */
public bool $internal;
/** @psalm-readonly */
public ?string $filename;
/** @psalm-readonly */
public ?int $startline;
/** @psalm-readonly */
public ?int $endline;
/**
* @psalm-readonly
*
* @psalm-var ?non-empty-string
*/
public ?string $docstring;
/** @psalm-readonly */
public bool $return_reference;
/** @psalm-readonly */
public ?string $returntype = null;
public function __construct(ReflectionFunctionAbstract $callable)
{
$this->internal = $callable->isInternal();
$t = $callable->getFileName();
$this->filename = false === $t ? null : $t;
$t = $callable->getStartLine();
$this->startline = false === $t ? null : $t;
$t = $callable->getEndLine();
$this->endline = false === $t ? null : $t;
$t = $callable->getDocComment();
$this->docstring = false === $t ? null : $t;
$this->return_reference = $callable->returnsReference();
$rt = $callable->getReturnType();
if ($rt) {
$this->returntype = Utils::getTypeString($rt);
}
$parameters = [];
foreach ($callable->getParameters() as $param) {
$parameters[] = new ParameterBag($param);
}
$this->parameters = $parameters;
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Dom\NodeList;
use DOMNodeList;
use Kint\Value\Context\ContextInterface;
class DomNodeListValue extends InstanceValue
{
protected int $length;
/**
* @psalm-param DOMNodeList|NodeList $node
*/
public function __construct(ContextInterface $context, object $node)
{
parent::__construct($context, \get_class($node), \spl_object_hash($node), \spl_object_id($node));
$this->length = $node->length;
}
public function getLength(): int
{
return $this->length;
}
public function getDisplaySize(): string
{
return (string) $this->length;
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Dom\Node;
use DOMNode;
use Kint\Value\Context\ContextInterface;
class DomNodeValue extends InstanceValue
{
/**
* @psalm-param DOMNode|Node $node
*/
public function __construct(ContextInterface $context, object $node)
{
parent::__construct($context, \get_class($node), \spl_object_hash($node), \spl_object_id($node));
}
public function getDisplaySize(): ?string
{
return null;
}
}
+68
View File
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use BackedEnum;
use Kint\Value\Context\ContextInterface;
use UnitEnum;
class EnumValue extends InstanceValue
{
/** @psalm-readonly */
protected UnitEnum $enumval;
public function __construct(ContextInterface $context, UnitEnum $enumval)
{
parent::__construct($context, \get_class($enumval), \spl_object_hash($enumval), \spl_object_id($enumval));
$this->enumval = $enumval;
}
public function getHint(): string
{
return parent::getHint() ?? 'enum';
}
public function getDisplayType(): string
{
return $this->classname.'::'.$this->enumval->name;
}
public function getDisplayValue(): ?string
{
if ($this->enumval instanceof BackedEnum) {
if (\is_string($this->enumval->value)) {
return '"'.$this->enumval->value.'"';
}
return (string) $this->enumval->value;
}
return null;
}
}
+85
View File
@@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use InvalidArgumentException;
use Kint\Value\Context\ContextInterface;
/**
* @psalm-type FixedWidthType = null|boolean|integer|double
*/
class FixedWidthValue extends AbstractValue
{
/**
* @psalm-readonly
*
* @psalm-var FixedWidthType
*/
protected $value;
/** @psalm-param FixedWidthType $value */
public function __construct(ContextInterface $context, $value)
{
$type = \strtolower(\gettype($value));
if ('null' === $type || 'boolean' === $type || 'integer' === $type || 'double' === $type) {
parent::__construct($context, $type);
$this->value = $value;
} else {
throw new InvalidArgumentException('FixedWidthValue can only contain fixed width types, got '.$type);
}
}
/**
* @psalm-api
*
* @psalm-return FixedWidthType
*/
public function getValue()
{
return $this->value;
}
public function getDisplaySize(): ?string
{
return null;
}
public function getDisplayValue(): ?string
{
if ('boolean' === $this->type) {
return ((bool) $this->value) ? 'true' : 'false';
}
if ('integer' === $this->type || 'double' === $this->type) {
return (string) $this->value;
}
return null;
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Kint\Value\Context\ContextInterface;
use Kint\Value\Representation\CallableDefinitionRepresentation;
class FunctionValue extends AbstractValue
{
/** @psalm-readonly */
protected DeclaredCallableBag $callable_bag;
/** @psalm-readonly */
protected ?CallableDefinitionRepresentation $definition_rep;
public function __construct(ContextInterface $c, DeclaredCallableBag $bag)
{
parent::__construct($c, 'function');
$this->callable_bag = $bag;
if ($this->callable_bag->internal) {
$this->definition_rep = null;
return;
}
/**
* @psalm-var string $this->callable_bag->filename
* @psalm-var int $this->callable_bag->startline
* Psalm issue #11121
*/
$this->definition_rep = new CallableDefinitionRepresentation(
$this->callable_bag->filename,
$this->callable_bag->startline,
$this->callable_bag->docstring
);
$this->addRepresentation($this->definition_rep);
}
public function getCallableBag(): DeclaredCallableBag
{
return $this->callable_bag;
}
/** @psalm-api */
public function getDefinitionRepresentation(): ?CallableDefinitionRepresentation
{
return $this->definition_rep;
}
public function getDisplayName(): string
{
return $this->context->getName().'('.$this->callable_bag->getParams().')';
}
public function getDisplayValue(): ?string
{
if ($this->definition_rep instanceof CallableDefinitionRepresentation) {
return $this->definition_rep->getDocstringFirstLine();
}
return parent::getDisplayValue();
}
public function getPhpDocUrl(): ?string
{
if (!$this->callable_bag->internal) {
return null;
}
return 'https://www.php.net/function.'.\str_replace('_', '-', \strtolower((string) $this->context->getName()));
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2013 Jonathan Vollebregt (jnvsor@gmail.com), Rokas Šleinius (raveren@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace Kint\Value;
use Kint\Value\Context\ContextInterface;
class InstanceValue extends AbstractValue
{
/**
* @psalm-readonly
*
* @psalm-var class-string
*/
protected string $classname;
/** @psalm-readonly */
protected string $spl_object_hash;
/** @psalm-readonly */
protected int $spl_object_id;
/**
* The canonical children of this value, for text renderers.
*
* @psalm-var null|list<AbstractValue>
*/
protected ?array $children = null;
/** @psalm-param class-string $classname */
public function __construct(
ContextInterface $context,
string $classname,
string $spl_object_hash,
int $spl_object_id
) {
parent::__construct($context, 'object');
$this->classname = $classname;
$this->spl_object_hash = $spl_object_hash;
$this->spl_object_id = $spl_object_id;
}
/** @psalm-return class-string */
public function getClassName(): string
{
return $this->classname;
}
public function getSplObjectHash(): string
{
return $this->spl_object_hash;
}
public function getSplObjectId(): int
{
return $this->spl_object_id;
}
/** @psalm-param null|list<AbstractValue> $children */
public function setChildren(?array $children): void
{
$this->children = $children;
}
/** @psalm-return null|list<AbstractValue> */
public function getChildren(): ?array
{
return $this->children;
}
public function getDisplayType(): string
{
return $this->classname;
}
public function getDisplaySize(): ?string
{
if (null === $this->children) {
return null;
}
return (string) \count($this->children);
}
public function getDisplayChildren(): array
{
return $this->children ?? [];
}
}

Some files were not shown because too many files have changed in this diff Show More