package printer_test import ( "bytes" "os" "testing" "github.com/VKCOM/php-parser/internal/php8" "github.com/VKCOM/php-parser/internal/tester" "github.com/VKCOM/php-parser/pkg/ast" "github.com/VKCOM/php-parser/pkg/conf" "github.com/VKCOM/php-parser/pkg/version" "github.com/VKCOM/php-parser/pkg/visitor/printer" "gotest.tools/assert" ) func ExamplePrinterPHP8() { src := `Hello >= $b ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } // test binary func TestParseAndPrintBinaryPHP8(t *testing.T) { src := `= $b ; $a > $b ; $a === $b ; $a and $b ; $a or $b ; $a xor $b ; $a - $b ; $a % $b ; $a * $b ; $a != $b ; $a <> $b ; $a !== $b ; $a + $b ; $a ** $b ; $a << $b ; $a >> $b ; $a <= $b ; $a < $b ; $a <=> $b ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } // test cast func TestParseAndPrintCastPHP8(t *testing.T) { src := ` $world , ... $unpack ] ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintArrayPHP8(t *testing.T) { src := ` 2 ) ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintBitwiseNotPHP8(t *testing.T) { src := ` $c ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintConstFetchPHP8(t *testing.T) { src := ` bar ( $arg , ) ;` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintNewPHP8(t *testing.T) { src := ` b ;` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintReferencePHP8(t *testing.T) { src := ` & $c ] ; $a = function ( ) use ( & $b ) { // do nothing } ; foreach ( $a as & $b ) { // do nothing }` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintRequirePHP8(t *testing.T) { src := ` & $b , // one $c , /* two */ ] ; $a = [0, 1, 2] ;` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintShortListPHP8(t *testing.T) { src := ` $v ; yield from $a ;` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } // test stmt func TestParseAndPrintAltIfPHP8(t *testing.T) { src := ` & $v ) : echo $v ; endforeach ;` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintAltSwitchPHP8(t *testing.T) { src := ` & $v ) { ; }` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintFunctionPHP8(t *testing.T) { src := `testtest call ( ) ; $a -> { $b . 'b' } ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintComplexString1PHP8(t *testing.T) { src := `bar" ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintComplexString2PHP8(t *testing.T) { src := ` bar }" ; "test ${ $foo -> bar ( ) }" ; "test ${ $a . '' }" ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintComplexString3PHP8(t *testing.T) { src := ` bar }" ; "test ${$foo -> bar ( ) }" ; "test ${$a . '' }" ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintComplexString4PHP8(t *testing.T) { src := ` bar }" ; "test {$foo -> bar ( ) }" ; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintNullsafePHP8(t *testing.T) { src := `method(); $a?->prop; "$a?->prop_inside_string"; (f())?->prop_for_expr; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintNamedMethods(t *testing.T) { src := `bar(some: $a); $foo->bar(some: $a, $b, ...$c); foo::bar(b: $b); foo::bar($a, b: $b, ...$c); $foo::bar(c: $a); $foo::bar($b, c: $a, ...$b); new foo(a: $a); new foo(a: $a, $c, ...$b); new class (name: $a) {}; new class (name: $a, $b, ...$c) {}; ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintMatchPHP8(t *testing.T) { src := ` 100, }; echo match($a) { default => 100 }; echo match($a) { default, => 100 }; echo match($a) { default, => 100, }; echo match($a) { 10 => 100 }; echo match($a) { 10 => 100, }; echo match($a) { 10, => 100 }; echo match($a) { 10, => 100, }; echo match($a) { 10, 20 => 100 }; echo match($a) { 10, 20 => 100, }; echo match($a) { 10, 20, => 100 }; echo match($a) { 10, 20, => 100, }; echo match($a) { 10, 20 => 100, 30, 40 => 101 }; echo match($a) { 10, 20 => 100, 30, 40 => 101, }; echo match($a) { 10, 20, => 100, 30, 40, => 101 }; echo match($a) { 10, 20 => 100, 30, 40 => 101, default => 102, }; ` actual := printPHP8(parsePHP8(src)) assert.Equal(t, src, actual) } func TestParseAndPrintUnionTypesPHP8(t *testing.T) { src := ` $a; $_ = fn(int|string|float $a) => $a; $_ = fn(int|string|float|bool $a) => $a; $_ = fn(int|\Foo $a) => $a; $_ = fn(\Foo|int $a) => $a; $_ = fn(\Foo|\Boo $a) => $a; // $_ = fn(\Foo|static $a) => $a; // static cannot be used here. $_ = fn(\Namespaced\Foo|\Boo $a) => $a; $_ = fn(): int|string => null; $_ = fn(): int|\Boo => null; $_ = fn(): \Foo|\Boo => null; $_ = fn(): \Namespaced\Foo|\Boo => null; $_ = fn(): \Foo|static => null; $_ = fn(): \Foo|void => null; } class Foo { public int|string $a; public int|string|float $a; public int|\Foo $a; public \Foo|\Boo $a; public \Namespaced\Foo|\Boo $a; public \Namespaced\Foo|\Boo|int $a; // public static $a; // static cannot be used here. public function f(int|\Foo $a) {} public function f(\Foo|int $a) {} public function f(\Foo|\Boo $a) {} public function f(\Namespaced\Foo|\Boo $a) {} // public function f(static|\Boo $a) {} // static cannot be used here. public function f(int|\Foo $a = null) {} public function f(\Foo|int $a = null) {} public function f(\Foo|\Boo $a = null) {} public function f(\Namespaced\Foo|\Boo $a = null) {} public function f(): int|string {} public function f(): int|\Boo {} public function f(): \Foo|\Boo {} public function f(): \Namespaced\Foo|\Boo {} public function f(): int|static {} // static can be used here. } ` actual := printPHP8(parsePHP8(src)) assert.Equal(t, src, actual) } func TestParseAndPrintTryCatchWithoutVariablePHP8(t *testing.T) { src := ` 10; $_ = fn( $a, ) => 10; } // Without comma. function f( $a, $b ) {} function f( $a ) {} class Foo { public function f( $a, $b ) {} public function f( $a ) {} } function f() { $_ = function( $a, $b ) {}; $_ = function( $a ) {}; $_ = fn( $a, $b ) => 10; $_ = fn( $a ) => 10; } ` actual := printPHP8(parsePHP8(src)) assert.Equal(t, src, actual) } func TestParseAndPrintThrowExprAndStmtPHP8(t *testing.T) { src := `createNotFoundException(); throw $exception = new Exception(); // As stmt. throw new InvalidArgumentException(); ` actual := printPHP8(parsePHP8(src)) assert.Equal(t, src, actual) } func TestParseAndPrintPropertyInConstructorPHP8(t *testing.T) { src := ` 1; $_ = #[AttributeWithArgs(100, SomeClass::class)] fn () => 1; $_ = #[FirstAttribute(100, SomeClass::class), SecondAttribute()] fn &(): void => 1; $_ = #[ FirstLineAttribute(100, SomeClass::class), SecondLineAttribute() ] fn () => 1; $_ = #[FirstAttribute(100, SomeClass::class), SecondAttribute()] #[SecondGroupFirstAttribute(SomeClass::class), SecondGroupSecondAttribute] fn &(): int|string => 1; $_ = #[\WithSlash] fn () => 1; $_ = #[\F\Q\N] fn &(): \Foo|string => 1; } ` actual := printPHP8(parsePHP8(src)) if src != actual { t.Errorf("\nexpected: %s\ngot: %s\n", src, actual) } } func TestParseAndPrintAnonClassWithAttributesPHP8(t *testing.T) { src := `length(); "foo$bar"[0]; "foo$bar"->length(); `) } func TestParseAndPrintConstantDerefencablePHP8(t *testing.T) { tester.NewParserPrintTestSuite(t).UsePHP8().Run(`length; A->length(); A[0]; A[0][1][2]; A{0}; A::B[0]; A::B[0][1][2]; A::B{0}; A::B->length; A::B->length(); A::B::C; A::B::$c; A::B::c(); `) } func TestParseAndPrintArbitraryExpressionsInNewAndInstanceOfPHP8(t *testing.T) { tester.NewParserPrintTestSuite(t).UsePHP8().Run(`length; __FUNCTION__->length(); `) }