Skip to content
Closed
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
33 changes: 33 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@
class NodeScopeResolver
{

private const BOOLEAN_EXPRESSION_MAX_PROCESS_DEPTH = 4;

private const LOOP_SCOPE_ITERATIONS = 3;
private const GENERALIZE_AFTER_ITERATION = 1;

Expand Down Expand Up @@ -3659,6 +3661,23 @@ function (MutatingScope $scope) use ($stmt, $expr, $nodeCallback, $context, $sto
);
return $result;
} elseif ($expr instanceof BooleanOr || $expr instanceof BinaryOp\LogicalOr) {
if ($this->getBooleanExpressionDepth($expr->left) > self::BOOLEAN_EXPRESSION_MAX_PROCESS_DEPTH) {
$leftResult = $this->processExprNode($stmt, $expr->left, $scope, $storage, $nodeCallback, $context->enterDeep());
$rightResult = $this->processExprNode($stmt, $expr->right, $scope, $storage, $nodeCallback, $context);

$this->callNodeCallbackWithExpression($nodeCallback, new BooleanOrNode($expr, $scope), $scope, $storage, $context);

return new ExpressionResult(
$scope,
$leftResult->hasYield() || $rightResult->hasYield(),
$leftResult->isAlwaysTerminating(),
array_merge($leftResult->getThrowPoints(), $rightResult->getThrowPoints()),
array_merge($leftResult->getImpurePoints(), $rightResult->getImpurePoints()),
static fn (): MutatingScope => $scope,
static fn (): MutatingScope => $scope,
);
}

$leftResult = $this->processExprNode($stmt, $expr->left, $scope, $storage, $nodeCallback, $context->enterDeep());
$rightResult = $this->processExprNode($stmt, $expr->right, $leftResult->getFalseyScope(), $storage, $nodeCallback, $context);
$rightExprType = $rightResult->getScope()->getType($expr->right);
Expand Down Expand Up @@ -7848,4 +7867,18 @@ private function inferForLoopExpressions(For_ $stmt, Expr $lastCondExpr, Mutatin
return $bodyScope;
}

private function getBooleanExpressionDepth(Expr $expr, int $depth = 0): int
{
while (
$expr instanceof BinaryOp\BooleanOr
|| $expr instanceof BinaryOp\LogicalOr
|| $expr instanceof BinaryOp\BooleanAnd
|| $expr instanceof BinaryOp\LogicalAnd
) {
return $this->getBooleanExpressionDepth($expr->left, $depth + 1);
}

return $depth;
}

}
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,12 @@ public function testBug13980(): void
$this->assertNoErrors($errors);
}

public function testBug14207(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-14207.php');
$this->assertNoErrors($errors);
}

public function testBug13945(): void
{
$errors = $this->runAnalyse(__DIR__ . '/data/bug-13945.php');
Expand Down
139 changes: 139 additions & 0 deletions tests/PHPStan/Analyser/data/bug-14207.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php declare(strict_types = 1);

namespace Bug14207;

use function assert;
use function PHPStan\Testing\assertType;

class WP_HTML_Token {
public string $namespace;
public string $node_name;
}

class HelloWorld
{
/**
* Returns whether an element of a given name is in the HTML special category.
*
* @since 6.4.0
*
* @see https://html.spec.whatwg.org/#special
*
* @param WP_HTML_Token|string $tag_name Node to check, or only its name if in the HTML namespace.
* @return bool Whether the element of the given name is in the special category.
*/
public static function is_special( $tag_name ): bool {
if ( is_string( $tag_name ) ) {
$tag_name = strtoupper( $tag_name );
} else {
$tag_name = 'html' === $tag_name->namespace
? strtoupper( $tag_name->node_name )
: "{$tag_name->namespace} {$tag_name->node_name}";
}

assertType('non-falsy-string|uppercase-string', $tag_name);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type is not correct, but its a pre-existing unrelated problem: created a separate issue for that


$x = (
'ADDRESS' === $tag_name ||
'APPLET' === $tag_name ||
'AREA' === $tag_name ||
'ARTICLE' === $tag_name ||
'ASIDE' === $tag_name ||
'BASE' === $tag_name ||
'BASEFONT' === $tag_name ||
'BGSOUND' === $tag_name ||
'BLOCKQUOTE' === $tag_name ||
'BODY' === $tag_name ||
'BR' === $tag_name ||
'BUTTON' === $tag_name ||
'CAPTION' === $tag_name ||
'CENTER' === $tag_name ||
'COL' === $tag_name ||
'COLGROUP' === $tag_name ||
'DD' === $tag_name ||
'DETAILS' === $tag_name ||
'DIR' === $tag_name ||
'DIV' === $tag_name ||
'DL' === $tag_name ||
'DT' === $tag_name ||
'EMBED' === $tag_name ||
'FIELDSET' === $tag_name ||
'FIGCAPTION' === $tag_name ||
'FIGURE' === $tag_name ||
'FOOTER' === $tag_name ||
'FORM' === $tag_name ||
'FRAME' === $tag_name ||
'FRAMESET' === $tag_name ||
'H1' === $tag_name ||
'H2' === $tag_name ||
'H3' === $tag_name ||
'H4' === $tag_name ||
'H5' === $tag_name ||
'H6' === $tag_name ||
'HEAD' === $tag_name ||
'HEADER' === $tag_name ||
'HGROUP' === $tag_name ||
'HR' === $tag_name ||
'HTML' === $tag_name ||
'IFRAME' === $tag_name ||
'IMG' === $tag_name ||
'INPUT' === $tag_name ||
'KEYGEN' === $tag_name ||
'LI' === $tag_name ||
'LINK' === $tag_name ||
'LISTING' === $tag_name ||
'MAIN' === $tag_name ||
'MARQUEE' === $tag_name ||
'MENU' === $tag_name ||
'META' === $tag_name ||
'NAV' === $tag_name ||
'NOEMBED' === $tag_name ||
'NOFRAMES' === $tag_name ||
'NOSCRIPT' === $tag_name ||
'OBJECT' === $tag_name ||
'OL' === $tag_name ||
'P' === $tag_name ||
'PARAM' === $tag_name ||
'PLAINTEXT' === $tag_name ||
'PRE' === $tag_name ||
'SCRIPT' === $tag_name ||
'SEARCH' === $tag_name ||
'SECTION' === $tag_name ||
'SELECT' === $tag_name ||
'SOURCE' === $tag_name ||
'STYLE' === $tag_name ||
'SUMMARY' === $tag_name ||
'TABLE' === $tag_name ||
'TBODY' === $tag_name ||
'TD' === $tag_name ||
'TEMPLATE' === $tag_name ||
'TEXTAREA' === $tag_name ||
'TFOOT' === $tag_name ||
'TH' === $tag_name ||
'THEAD' === $tag_name ||
'TITLE' === $tag_name ||
'TR' === $tag_name ||
'TRACK' === $tag_name ||
'UL' === $tag_name ||
'WBR' === $tag_name ||
'XMP' === $tag_name ||

// MathML.
'math MI' === $tag_name ||
'math MO' === $tag_name ||
'math MN' === $tag_name ||
'math MS' === $tag_name ||
'math MTEXT' === $tag_name ||
'math ANNOTATION-XML' === $tag_name ||

// SVG.
'svg DESC' === $tag_name ||
'svg FOREIGNOBJECT' === $tag_name ||
'svg TITLE' === $tag_name
);

assertType('bool', $x);

return $x;
}
}
Loading