diff --git a/phpunit/src/SsaAnalysisTest.php b/phpunit/src/SsaAnalysisTest.php index 89396f87..e0739242 100644 --- a/phpunit/src/SsaAnalysisTest.php +++ b/phpunit/src/SsaAnalysisTest.php @@ -1263,6 +1263,15 @@ class SsaAnalysisTest extends TestCase } } + public function testDetectTypeOfConcatExpressions(): void + { + $concat = new Expr\BinaryOp\Concat(new Scalar\LNumber(1), new Scalar\String_('')); + $concatAssign = new Expr\AssignOp\Concat(new Expr\Variable('value'), new Scalar\LNumber(2)); + + $this->assertSame(Type::STR, $this->invoke('detectTypeOfExpr', $concat)); + $this->assertSame(Type::STR, $this->invoke('detectTypeOfExpr', $concatAssign)); + } + public function testDetectSsaDefTypeNull(): void { $ssaVar = new SsaVar(1, 'x'); diff --git a/src/CompilerBase.php b/src/CompilerBase.php index 2d8f90dd..36da7790 100644 --- a/src/CompilerBase.php +++ b/src/CompilerBase.php @@ -2299,6 +2299,9 @@ class CompilerBase implements PropertyAccessContext case 'Expr_Array': case 'Expr_Cast_Array': return Type::ARRAY; + case 'Expr_BinaryOp_Concat': + case 'Expr_AssignOp_Concat': + return Type::STR; case 'Expr_BinaryOp_Plus': case 'Expr_BinaryOp_Minus': case 'Expr_BinaryOp_Mul': diff --git a/src/Parser/BinaryOpTrait.php b/src/Parser/BinaryOpTrait.php index e57f987c..83332cb4 100644 --- a/src/Parser/BinaryOpTrait.php +++ b/src/Parser/BinaryOpTrait.php @@ -265,6 +265,13 @@ trait BinaryOpTrait $argList = $prefixExpressions; foreach ($items as $item) { + // Keep one operand so concat still performs PHP string coercion. + // Prefix expressions are operands too (for example, the left-hand + // value of `.=`), so an empty RHS literal can be omitted there. + if ($argList !== [] && $this->isScalarString($item) && $item->value === '') { + continue; + } + $type = $this->detectTypeOfExpr($item); $parsed = $this->parseExprAsValue($item); $argList[] = $this->prepareConcatOperand($parsed, $type); diff --git a/tests/compiler/concat_empty.phpt b/tests/compiler/concat_empty.phpt new file mode 100644 index 00000000..a0aeebf1 --- /dev/null +++ b/tests/compiler/concat_empty.phpt @@ -0,0 +1,33 @@ +--TEST-- +Concat empty strings +--FILE-- + +--EXPECT-- +string(2) "1a" +string(1) "1" +string(1) "1" +string(0) "" +string(0) "" +string(5) "value" +string(6) "called" +string(1) "1"