Skip to content

Commit a4763c1

Browse files
ondrejmirtesclaude
andcommitted
Compute constant substr() result independently of the runtime PHP version
The is_bool($substr) branch re-checked substrReturnFalseInsteadOfEmptyString(), which the sharper conditional guards proved always-true when analysing on PHP >= 8 (there substr() never returns false, so $substr is only bool via substrOrFalse()). The finding is genuinely version-specific and cannot live in the shared baseline. Route the non-mb_substr path through substrOrFalse(), whose false|string result does not depend on the runtime substr() semantics, and map false to the analysed version's result. Behaviour is unchanged; drops the version-specific baseline entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGhnJQpUWRRpg6H5LuWJkA
1 parent 2c1152e commit a4763c1

2 files changed

Lines changed: 17 additions & 30 deletions

File tree

‎phpstan-baseline.neon‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,12 +1644,6 @@ parameters:
16441644
count: 1
16451645
path: src/Type/Php/ReflectionMethodConstructorThrowTypeExtension.php
16461646

1647-
-
1648-
rawMessage: If condition is always true.
1649-
identifier: if.alwaysTrue
1650-
count: 1
1651-
path: src/Type/Php/SubstrDynamicReturnTypeExtension.php
1652-
16531647
-
16541648
rawMessage: Foreach overwrites $group with its value variable.
16551649
identifier: foreach.valueOverwrite

‎src/Type/Php/SubstrDynamicReturnTypeExtension.php‎

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,25 @@ public function getTypeFromFunctionCall(
7575
) {
7676
$results = [];
7777
foreach ($constantStrings as $constantString) {
78-
if ($length !== null) {
79-
if ($functionReflection->getName() === 'mb_substr') {
80-
$substr = mb_substr($constantString->getValue(), $offset->getValue(), $length->getValue());
81-
} elseif ($this->phpVersion->substrReturnFalseInsteadOfEmptyString()) {
82-
$substr = $this->substrOrFalse($constantString->getValue(), $offset->getValue(), $length->getValue());
83-
} else {
84-
$substr = substr($constantString->getValue(), $offset->getValue(), $length->getValue());
85-
}
86-
} else {
87-
if ($functionReflection->getName() === 'mb_substr') {
88-
$substr = mb_substr($constantString->getValue(), $offset->getValue());
89-
} elseif ($this->phpVersion->substrReturnFalseInsteadOfEmptyString()) {
90-
// Simulate substr call on an older PHP version if the runtime one is too new.
91-
$substr = $this->substrOrFalse($constantString->getValue(), $offset->getValue());
92-
} else {
93-
$substr = substr($constantString->getValue(), $offset->getValue());
94-
}
78+
if ($functionReflection->getName() === 'mb_substr') {
79+
$substr = $length !== null
80+
? mb_substr($constantString->getValue(), $offset->getValue(), $length->getValue())
81+
: mb_substr($constantString->getValue(), $offset->getValue());
82+
$results[] = new ConstantStringType($substr);
83+
continue;
9584
}
9685

97-
if (is_bool($substr)) {
98-
if ($this->phpVersion->substrReturnFalseInsteadOfEmptyString()) {
99-
$results[] = new ConstantBooleanType($substr);
100-
} else {
101-
// Simulate substr call on a recent PHP version if the runtime one is too old.
102-
$results[] = new ConstantStringType('');
103-
}
86+
// substrOrFalse() detects an out-of-range offset with its own length
87+
// check, so the result does not depend on the runtime PHP version's
88+
// substr() semantics. false is then mapped to the analysed version's
89+
// result: false on PHP < 8, an empty string on PHP >= 8.
90+
$substr = $length !== null
91+
? $this->substrOrFalse($constantString->getValue(), $offset->getValue(), $length->getValue())
92+
: $this->substrOrFalse($constantString->getValue(), $offset->getValue());
93+
if ($substr === false) {
94+
$results[] = $this->phpVersion->substrReturnFalseInsteadOfEmptyString()
95+
? new ConstantBooleanType(false)
96+
: new ConstantStringType('');
10497
} else {
10598
$results[] = new ConstantStringType($substr);
10699
}

0 commit comments

Comments
 (0)