- Preserve PHP's concat-assignment operation for statically typed strings using in-place append - Add parseInPlaceStringConcatAssign method to handle string concatenation assignments efficiently - Prevent O(n^2) work when rebuilding target = concat(target, rhs) operations - Keep compound RHS evaluated completely before target changes in concat assignments - Optimize native scalar binary operand handling to avoid unnecessary Variant boxing - Add native scalar call results remain unboxed when ordered in native-types mode - Create string concatenation assignment tests with proper PHP value and COW semantics - Update version number from 1109 to 1110master
parent
65d3710a61
commit
7418b6247e
10 changed files with 177 additions and 10 deletions
@ -0,0 +1,8 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
function recursivePhpInt(int $value): int |
||||||
|
{ |
||||||
|
return $value < 2 |
||||||
|
? 1 |
||||||
|
: recursivePhpInt($value - 2) + recursivePhpInt($value - 1); |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use native_types; |
||||||
|
|
||||||
|
function recursiveNativeInt(int $value): int |
||||||
|
{ |
||||||
|
return $value < 2 |
||||||
|
? 1 |
||||||
|
: recursiveNativeInt($value - 2) + recursiveNativeInt($value - 1); |
||||||
|
} |
||||||
@ -0,0 +1,10 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
function stringConcatAssignCodegen(string $suffix): string |
||||||
|
{ |
||||||
|
$value = ''; |
||||||
|
$value .= 'hello'; |
||||||
|
$value .= ', ' . $suffix; |
||||||
|
$result = ($value .= $value); |
||||||
|
return $result; |
||||||
|
} |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use TypePhp\CompilerTest; |
||||||
|
|
||||||
|
final class NativeScalarBinaryOperandTest extends \BaseTest |
||||||
|
{ |
||||||
|
public function testNativeScalarCallResultsRemainUnboxedWhenOrdered(): void |
||||||
|
{ |
||||||
|
global $translator; |
||||||
|
|
||||||
|
$compiler = CompilerTest::create(ROOT_PATH); |
||||||
|
$translator = $compiler; |
||||||
|
$source = ROOT_PATH . '/phpunit/code/native-scalar-binary-operands.php'; |
||||||
|
$compiler->addFiles([$source]); |
||||||
|
$compiler->prepareFile($source); |
||||||
|
$generated = $compiler->convertFile($source); |
||||||
|
$code = file_get_contents($generated); |
||||||
|
|
||||||
|
self::assertIsString($code); |
||||||
|
self::assertStringContainsString('php::Int php_recursivenativeint(php::Int value)', $code); |
||||||
|
self::assertStringNotContainsString('php::Var tmp_var_', $code); |
||||||
|
} |
||||||
|
|
||||||
|
public function testPhpCompatibleScalarCallResultsRemainBoxedWhenOrdered(): void |
||||||
|
{ |
||||||
|
global $translator; |
||||||
|
|
||||||
|
$compiler = CompilerTest::create(ROOT_PATH); |
||||||
|
$translator = $compiler; |
||||||
|
$source = ROOT_PATH . '/phpunit/code/dynamic-scalar-binary-operands.php'; |
||||||
|
$compiler->addFiles([$source]); |
||||||
|
$compiler->prepareFile($source); |
||||||
|
$generated = $compiler->convertFile($source); |
||||||
|
$code = file_get_contents($generated); |
||||||
|
|
||||||
|
self::assertIsString($code); |
||||||
|
self::assertStringContainsString('php::Int php_recursivephpint(php::Int value)', $code); |
||||||
|
self::assertStringContainsString('php::Var tmp_var_', $code); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use TypePhp\CompilerTest; |
||||||
|
|
||||||
|
final class StringConcatAssignTest extends \BaseTest |
||||||
|
{ |
||||||
|
public function testTypedStringConcatAssignmentUsesInPlaceAppend(): void |
||||||
|
{ |
||||||
|
global $translator; |
||||||
|
|
||||||
|
$compiler = CompilerTest::create(ROOT_PATH); |
||||||
|
$translator = $compiler; |
||||||
|
$source = ROOT_PATH . '/phpunit/code/string-concat-assign.php'; |
||||||
|
$compiler->addFiles([$source]); |
||||||
|
$compiler->prepareFile($source); |
||||||
|
$generated = $compiler->convertFile($source); |
||||||
|
$code = file_get_contents($generated); |
||||||
|
|
||||||
|
self::assertIsString($code); |
||||||
|
self::assertGreaterThanOrEqual(3, substr_count($code, 'value.append(')); |
||||||
|
self::assertStringContainsString('value.append(php::concat({', $code); |
||||||
|
self::assertStringNotContainsString('value = php::concat({value,', $code); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
--TEST-- |
||||||
|
String concat assignment preserves PHP value, COW and expression semantics |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
final class ConcatAssignStringable |
||||||
|
{ |
||||||
|
public function __toString(): string |
||||||
|
{ |
||||||
|
echo "convert\n"; |
||||||
|
return 'object'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$value = 'start'; |
||||||
|
$copy = $value; |
||||||
|
$suffix = 'tail'; |
||||||
|
|
||||||
|
$value .= ':'; |
||||||
|
$value .= $suffix . ':' . new ConcatAssignStringable(); |
||||||
|
var_dump($value, $copy); |
||||||
|
|
||||||
|
$self = 'ab'; |
||||||
|
$self .= $self; |
||||||
|
var_dump($self); |
||||||
|
|
||||||
|
$result = ($value .= '!'); |
||||||
|
var_dump($value, $result); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
convert |
||||||
|
string(17) "start:tail:object" |
||||||
|
string(5) "start" |
||||||
|
string(4) "abab" |
||||||
|
string(18) "start:tail:object!" |
||||||
|
string(18) "start:tail:object!" |
||||||
@ -1 +1 @@ |
|||||||
1109 |
1110 |
||||||
Loading…
Reference in new issue