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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 DownExpand 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 DownExpand 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 numberDiff line numberDiff 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 numberDiff line numberDiff 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