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
13 changes: 9 additions & 4 deletions src/Analyser/ScopeOps.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ final class ScopeOps
private const CONTAINS_SUPER_GLOBAL_ATTRIBUTE_NAME = 'containsSuperGlobal';

/** Virtual-node key prefixes whose printers include all children verbatim. */
private const COMPOSITIONAL_VIRTUAL_KEY_PREFIXES = ['__phpstanPossiblyImpure(', '__phpstanRemembered('];
// A prefix may be listed only when the printer emits every getSubNodeNames()
// sub-node verbatim (or the node walks no sub-nodes at all). The foreach/
// parameter original-value markers (__phpstanOriginalForeachKey etc.) hide a
// synthesized Variable child on purpose - reassigning the variable must
// invalidate them through containment - so they can never be listed here.
private const COMPOSITIONAL_VIRTUAL_KEY_PREFIXES = ['__phpstanForeachValueByRef(', '__phpstanIntertwinedVariableByReference(', '__phpstanPossiblyImpure(', '__phpstanPropertyInitialization(', '__phpstanRemembered('];

/**
* Mirrors MutatingScope::getNodeKey().
Expand Down Expand Up @@ -611,7 +616,7 @@ public static function invalidateExpressionEntries(
// a substring cannot belong to an expression containing the invalidated one, so
// the much more expensive per-expression check can be skipped without being called.
$canUseKeyPrefilter = $exprStringToInvalidate !== '$this'
&& !str_contains($exprStringToInvalidate, '__phpstan')
&& !self::keyMayHideSubExpressions($exprStringToInvalidate)
&& !str_contains($exprStringToInvalidate, '/*');

foreach ($expressionTypes as $exprString => $exprTypeHolder) {
Expand Down Expand Up @@ -735,7 +740,7 @@ public static function invalidateMethodsOnExpression(
// call's key embeds its receiver's key verbatim, so when the invalidated key
// does not occur in the entry's key, the receiver cannot match and the entry
// can be kept without re-printing the receiver.
$canUseKeyPrefilter = !str_contains($exprStringToInvalidate, '__phpstan')
$canUseKeyPrefilter = !self::keyMayHideSubExpressions($exprStringToInvalidate)
&& !str_contains($exprStringToInvalidate, '/*');

foreach ($expressionTypes as $exprString => $exprTypeHolder) {
Expand Down Expand Up @@ -844,7 +849,7 @@ public static function shouldInvalidateExpression(MutatingScope $scope, ExprPrin
// - keys carrying a getNodeKey() suffix ('/*…*/') are not plain substrings.
if (
$exprStringToInvalidate !== '$this'
&& !str_contains($exprStringToInvalidate, '__phpstan')
&& !self::keyMayHideSubExpressions($exprStringToInvalidate)
&& !str_contains($exprStringToInvalidate, '/*')
&& !str_contains($exprString, $exprStringToInvalidate)
&& !self::keyMayHideSubExpressions($exprString)
Expand Down
2 changes: 1 addition & 1 deletion src/Turbo/TurboExtensionEnabler.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ final class TurboExtensionEnabler
* version is the short SHA of the last commit touching turbo-ext/src/,
* enforced by the phar.yml turbo-version job.
*/
public const EXPECTED_EXTENSION_VERSION = '86312b7';
public const EXPECTED_EXTENSION_VERSION = '1ecf6e0';

private static bool $typeCombinatorCacheEnabled = false;

Expand Down
13 changes: 10 additions & 3 deletions turbo-ext/src/ScopeOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ class ScopeOps
* containing the invalidated one, so the much more expensive
* per-expression check can be skipped without being called. */
const bool canUseKeyPrefilter = !query.isThis
&& !strContains(exprStringToInvalidate, "__phpstan", sizeof("__phpstan") - 1)
&& !keyMayHideSubExpressions(exprStringToInvalidate)
&& !strContains(exprStringToInvalidate, "/*", 2);

bool invalidated = false;
Expand Down Expand Up @@ -1006,7 +1006,7 @@ class ScopeOps
* method call's key embeds its receiver's key verbatim, so when the
* invalidated key does not occur in the entry's key, the receiver cannot
* match and the entry can be kept without re-printing the receiver. */
const bool canUseKeyPrefilter = !strContains(exprStringToInvalidate, "__phpstan", sizeof("__phpstan") - 1)
const bool canUseKeyPrefilter = !keyMayHideSubExpressions(exprStringToInvalidate)
&& !strContains(exprStringToInvalidate, "/*", 2);

for (auto entry : expressionTypes) {
Expand Down Expand Up @@ -1657,8 +1657,15 @@ class ScopeOps
*/
static bool keyMayHideSubExpressions(zend_string *key)
{
/* Mirror of ScopeOps::COMPOSITIONAL_VIRTUAL_KEY_PREFIXES - a prefix may
* be listed only when the printer emits every getSubNodeNames() sub-node
* verbatim (or the node walks no sub-nodes at all); the foreach/parameter
* original-value markers hide a synthesized Variable child on purpose. */
static const struct { const char *prefix; size_t len; } compositionalPrefixes[] = {
{ "__phpstanForeachValueByRef(", sizeof("__phpstanForeachValueByRef(") - 1 },
{ "__phpstanIntertwinedVariableByReference(", sizeof("__phpstanIntertwinedVariableByReference(") - 1 },
{ "__phpstanPossiblyImpure(", sizeof("__phpstanPossiblyImpure(") - 1 },
{ "__phpstanPropertyInitialization(", sizeof("__phpstanPropertyInitialization(") - 1 },
{ "__phpstanRemembered(", sizeof("__phpstanRemembered(") - 1 },
};

Expand Down Expand Up @@ -1901,7 +1908,7 @@ class ScopeOps

/* Compositional-key substring gate */
if (!query.isThis
&& !strContains(query.exprStringToInvalidate, "__phpstan", sizeof("__phpstan") - 1)
&& !keyMayHideSubExpressions(query.exprStringToInvalidate)
&& !strContains(query.exprStringToInvalidate, "/*", 2)
&& !strContainsStr(exprString, query.exprStringToInvalidate)
&& !keyMayHideSubExpressions(exprString)) {
Expand Down
Loading