Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion admin/framework/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"codeigniter/coding-standard": "^1.7",
"fakerphp/faker": "^1.24",
"friendsofphp/php-cs-fixer": "^3.47.1",
"kint-php/kint": "^6.0",
"kint-php/kint": "^6.1",
"mikey179/vfsstream": "^1.6.12",
"nexusphp/cs-config": "^3.6",
"phpunit/phpunit": "^10.5.16 || ^11.2",
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"require-dev": {
"codeigniter/phpstan-codeigniter": "1.x-dev",
"fakerphp/faker": "^1.24",
"kint-php/kint": "^6.0",
"kint-php/kint": "^6.1",
"mikey179/vfsstream": "^1.6.12",
"nexusphp/tachycardia": "^2.0",
"phpstan/extension-installer": "^1.4",
Expand Down
63 changes: 55 additions & 8 deletions system/ThirdParty/Kint/CallFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ class CallFinder
];

/**
* @psalm-param callable-array|callable-string $function
*
* @psalm-return list<array{parameters: list<CallParameter>, modifiers: list<PhpToken>}>
* @psalm-param callable-string|(callable-array&list{class-string, non-empty-string}) $function
*
* @return array List of matching calls on the relevant line
*
* @psalm-return list<array{parameters: list<CallParameter>, modifiers: list<PhpToken>}>
*/
public static function getFunctionCalls(string $source, int $line, $function): array
{
Expand Down Expand Up @@ -198,6 +198,11 @@ public static function getFunctionCalls(string $source, int $line, $function): a
self::$operator[T_NEW] = true; // @codeCoverageIgnore
}

if (KINT_PHP85) {
/** @psalm-suppress UndefinedConstant */
self::$operator[T_PIPE] = true;
}

/** @psalm-var list<PhpToken> */
$tokens = \token_get_all($source);
$function_calls = [];
Expand All @@ -212,10 +217,6 @@ public static function getFunctionCalls(string $source, int $line, $function): a
$function = \strtolower($function[1]);
} else {
$class = null;
/**
* @psalm-suppress RedundantFunctionCallGivenDocblockType
* Psalm bug #11075
*/
$function = \strtolower($function);
}

Expand Down Expand Up @@ -294,6 +295,8 @@ public static function getFunctionCalls(string $source, int $line, $function): a
$params = []; // All our collected parameters
$shortparam = []; // The short version of the parameter
$param_start = $offset; // The distance to the start of the parameter
$quote = null; // Buffer to store quote type for shortparam
$in_ternary = false;

// Loop through the following tokens until the function call ends
while (isset($tokens[$offset])) {
Expand Down Expand Up @@ -341,6 +344,26 @@ public static function getFunctionCalls(string $source, int $line, $function): a
$instring = !$instring;

$shortparam[] = $token;
} elseif (T_START_HEREDOC === $token[0]) {
if (1 === $depth) {
$quote = \ltrim($token[1], " \t<")[0];
if ("'" !== $quote) {
$quote = '"';
}
$shortparam[] = [T_START_HEREDOC, $quote];
$instring = true;
}

++$depth;
} elseif (T_END_HEREDOC === $token[0]) {
--$depth;

if (1 === $depth) {
if ($realtokens) {
$shortparam[] = '...';
}
$shortparam[] = [T_END_HEREDOC, $quote];
}
} elseif (1 === $depth) {
if (',' === $token[0]) {
$params[] = [
Expand All @@ -349,6 +372,7 @@ public static function getFunctionCalls(string $source, int $line, $function): a
];
$shortparam = [];
$paramrealtokens = false;
$in_ternary = false;
$param_start = $offset + 1;
} elseif (T_CONSTANT_ENCAPSED_STRING === $token[0]) {
$quote = $token[1][0];
Expand All @@ -364,6 +388,15 @@ public static function getFunctionCalls(string $source, int $line, $function): a
}
$shortparam[] = $token;
} else {
// We can't tell the order of named parameters or if they're splatting
// without parsing the called function and that's too much work for this
// edge case so we'll just skip parameters altogether.
if ('?' === $token) {
$in_ternary = true;
} elseif (!$in_ternary && ':' === $token) {
$params = [];
break;
}
$shortparam[] = $token;
}
}
Expand Down Expand Up @@ -400,13 +433,26 @@ public static function getFunctionCalls(string $source, int $line, $function): a
$literal = false;
$new_without_parens = false;

foreach ($name as $token) {
foreach ($name as $name_index => $token) {
if (KINT_PHP85 && T_CLONE === $token[0]) {
$nextReal = self::realTokenIndex($name, $name_index + 1);

if (null !== $nextReal && '(' === $name[$nextReal]) {
continue;
}
}

if (self::tokenIsOperator($token)) {
$expression = true;
break;
}
}

if (!$expression && T_START_HEREDOC === $name[0][0]) {
$expression = true;
$literal = true;
}

// As of 8.4 new is only an expression when parentheses are
// omitted. In that case we can cheat and add them ourselves.
//
Expand Down Expand Up @@ -593,6 +639,7 @@ private static function tokensTrim(array $tokens): array
return \array_reverse($tokens);
}

/** @psalm-return list<PhpToken> */
private static function tokensFormatted(array $tokens): array
{
$tokens = self::tokensTrim($tokens);
Expand Down
23 changes: 16 additions & 7 deletions system/ThirdParty/Kint/Kint.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,24 @@
use Kint\Renderer\TextRenderer;
use Kint\Value\Context\BaseContext;
use Kint\Value\Context\ContextInterface;
use Kint\Value\TraceFrameValue;
use Kint\Value\UninitializedValue;

/**
* @psalm-consistent-constructor
* Psalm bug #8523
*
* @psalm-import-type CallParameter from CallFinder
* @psalm-import-type TraceFrame from TraceFrameValue
*
* @psalm-type KintMode = Kint::MODE_*|bool
* @psalm-type KintMode = array-key|bool
* @psalm-type KintCallInfo = array{
* params: ?list<CallParameter>,
* modifiers: array,
* callee: ?callable,
* caller: ?callable,
* trace: TraceFrame[],
* }
*
* @psalm-api
*/
Expand Down Expand Up @@ -396,9 +405,9 @@ public static function getBasesFromParamInfo(array $params, int $argc): array
* @param array[] $trace Backtrace
* @param array $args Arguments
*
* @return array Call info
*
* @psalm-param list<non-empty-array> $trace
*
* @return KintCallInfo Call info
*/
public static function getCallInfo(array $aliases, array $trace, array $args): array
{
Expand Down Expand Up @@ -622,8 +631,8 @@ protected static function getSingleCall(array $frame, array $args): ?array

foreach ($keys as $key) {
$call['parameters'][] = [
'name' => \substr($param['name'], 3).'['.\var_export($key, true).']',
'path' => \substr($param['path'], 3).'['.\var_export($key, true).']',
'name' => ((string) \substr($param['name'], 3)).'['.\var_export($key, true).']',
'path' => ((string) \substr($param['path'], 3)).'['.\var_export($key, true).']',
'expression' => false,
'literal' => false,
'new_without_parens' => false,
Expand All @@ -634,8 +643,8 @@ protected static function getSingleCall(array $frame, array $args): ?array
// 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.']',
'name' => 'array_values('.((string) \substr($param['name'], 3)).')['.$j.']',
'path' => 'array_values('.((string) \substr($param['path'], 3)).')['.$j.']',
'expression' => false,
'literal' => false,
'new_without_parens' => false,
Expand Down
4 changes: 3 additions & 1 deletion system/ThirdParty/Kint/Parser/ClassStaticsPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ private function buildStaticValue(ReflectionProperty $pr, int $depth): AbstractV
$context->access_path = '\\'.$context->owner_class.'::$'.$context->name;
}

$pr->setAccessible(true);
if (KINT_PHP81 === false) {
$pr->setAccessible(true);
}

/**
* @psalm-suppress TooFewArguments
Expand Down
2 changes: 1 addition & 1 deletion system/ThirdParty/Kint/Parser/ColorPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa

$trimmed = \strtolower(\trim($var));

if (!isset(ColorRepresentation::$color_map[$trimmed]) && !\preg_match('/^(?:(?:rgb|hsl)[^\\)]{6,}\\)|#[0-9a-fA-F]{3,8})$/', $trimmed)) {
if (!isset(ColorRepresentation::$color_map[$trimmed]) && !\preg_match('/^(?:(?:rgb|hsl)a?[^\\)]{6,}\\)|#[0-9a-f]{3,8})$/', $trimmed)) {
return $v;
}

Expand Down
74 changes: 43 additions & 31 deletions system/ThirdParty/Kint/Parser/DomPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,12 @@
use Kint\Value\Representation\ContainerRepresentation;
use Kint\Value\StringValue;
use LogicException;
use ReflectionClass;

class DomPlugin extends AbstractPlugin implements PluginBeginInterface
{
/**
* Reflection doesn't work below 8.1, also it won't show readonly status.
* Reflection doesn'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
Expand Down Expand Up @@ -102,12 +103,9 @@ class DomPlugin extends AbstractPlugin implements PluginBeginInterface
'previousElementSibling' => true,
'nextElementSibling' => true,
'innerHTML' => false,
'outerHTML' => false,
'outerHTML' => true,
'substitutedNodeValue' => false,
];

public const DOM_NS_VERSIONS = [
'outerHTML' => KINT_PHP85,
'children' => true,
];

/**
Expand Down Expand Up @@ -208,6 +206,9 @@ class DomPlugin extends AbstractPlugin implements PluginBeginInterface
*/
public static bool $verbose = false;

/** @psalm-var array<class-string, array<string, bool>> cache of properties for getKnownProperties */
protected static array $property_cache = [];

protected ClassMethodsPlugin $methods_plugin;
protected ClassStaticsPlugin $statics_plugin;

Expand Down Expand Up @@ -259,12 +260,14 @@ public function parseBegin(&$var, ContextInterface $c): ?AbstractValue
/** @psalm-param Node|DOMNode $var */
private function parseProperty(object $var, string $prop, ContextInterface $c): AbstractValue
{
if (!isset($var->{$prop})) {
// Suppress deprecation message
if (@!isset($var->{$prop})) {
return new FixedWidthValue($c, null);
}

$parser = $this->getParser();
$value = $var->{$prop};
// Suppress deprecation message
@$value = $var->{$prop};

if (\is_scalar($value)) {
return $parser->parse($value, $c);
Expand Down Expand Up @@ -450,40 +453,49 @@ private function parseNode(object $var, ContextInterface $c): DomNodeValue
*/
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 (KINT_PHP81) {
$r = new ReflectionClass($var);
$classname = $r->getName();

if (!isset(self::$property_cache[$classname])) {
self::$property_cache[$classname] = [];

foreach ($r->getProperties() as $prop) {
if ($prop->isStatic()) {
continue;
}

$declaring = $prop->getDeclaringClass()->getName();
$name = $prop->name;

if (\in_array($declaring, [Node::class, Element::class], true)) {
$readonly = self::NODE_PROPS[$name] ?? self::ELEMENT_PROPS[$name];
} elseif (\in_array($declaring, [DOMNode::class, DOMElement::class], true)) {
$readonly = self::DOMNODE_PROPS[$name] ?? self::DOMELEMENT_PROPS[$name];
} else {
continue;
}

self::$property_cache[$classname][$prop->name] = $readonly;
}

if ($var instanceof Attr || $var instanceof CharacterData) {
$known_properties['nodeValue'] = false;
}
if ($var instanceof Document) {
self::$property_cache[$classname]['textContent'] = true;
}

foreach (self::DOM_NS_VERSIONS as $key => $val) {
/**
* @psalm-var bool $val
* Psalm bug #4509
*/
if (false === $val) {
unset($known_properties[$key]); // @codeCoverageIgnore
if ($var instanceof Attr || $var instanceof CharacterData) {
self::$property_cache[$classname]['nodeValue'] = false;
}
}

$known_properties = self::$property_cache[$classname];
} 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
}
Expand Down
2 changes: 1 addition & 1 deletion system/ThirdParty/Kint/Parser/HtmlPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public function getTriggers(): int

public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractValue
{
if ('<!doctype html>' !== \strtolower(\substr($var, 0, 15))) {
if ('<!doctype html>' !== \strtolower((string) \substr($var, 0, 15))) {
return $v;
}

Expand Down
4 changes: 0 additions & 4 deletions system/ThirdParty/Kint/Parser/IteratorPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,6 @@ public function parseComplete(&$var, AbstractValue $v, int $trigger): AbstractVa
$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;
Expand Down
Loading
Loading