diff --git a/internal/extgen/cfile_phpmethod_test.go b/internal/extgen/cfile_phpmethod_test.go index 0f7329d8f3..7e666dcd8e 100644 --- a/internal/extgen/cfile_phpmethod_test.go +++ b/internal/extgen/cfile_phpmethod_test.go @@ -214,3 +214,127 @@ func TestCFile_ClassMethodStringReturn(t *testing.T) { require.Contains(t, content, "RETURN_STR(result)", "Expected RETURN_STR macro") require.Contains(t, content, "RETURN_EMPTY_STRING()", "Expected RETURN_EMPTY_STRING fallback") } + +func TestCFile_HeaderIncludesNotCommentedOut(t *testing.T) { + generator := &Generator{ + BaseName: "test_extension", + Classes: []phpClass{ + { + Name: "Foo", + GoStruct: "Foo", + Methods: []phpClassMethod{{Name: "m", PhpName: "m", ReturnType: phpInt, ClassName: "Foo"}}, + }, + }, + BuildDir: t.TempDir(), + } + + cFileGen := cFileGenerator{generator: generator} + content, err := cFileGen.getTemplateContent() + require.NoError(t, err) + + require.Contains(t, content, "\n#include ", "#include must start on its own line, not be glued to the trailing // comment") + require.NotContains(t, content, "going forward.#include", "header preamble must not be glued to the first #include") +} + +func TestCFile_ClassMethodParamCastsByParamType(t *testing.T) { + tests := []struct { + name string + returnType phpType + params []phpParameter + wantContain []string + wantAbsent []string + }{ + { + name: "int return with string param does not cast string param", + returnType: phpInt, + params: []phpParameter{{Name: "name", PhpType: phpString}}, + wantContain: []string{ + "getVarInt_wrapper(intern->go_handle, name);", + }, + wantAbsent: []string{ + "(long)name", + }, + }, + { + name: "float return with string param does not cast string param", + returnType: phpFloat, + params: []phpParameter{{Name: "name", PhpType: phpString}}, + wantContain: []string{ + "getVarInt_wrapper(intern->go_handle, name);", + }, + wantAbsent: []string{ + "(double)name", + }, + }, + { + name: "bool return with string param does not cast string param", + returnType: phpBool, + params: []phpParameter{{Name: "name", PhpType: phpString}}, + wantContain: []string{ + "getVarInt_wrapper(intern->go_handle, name);", + }, + wantAbsent: []string{ + "(int)name", + }, + }, + { + name: "string return with int param casts int param to long", + returnType: phpString, + params: []phpParameter{{Name: "count", PhpType: phpInt}}, + wantContain: []string{ + "getVarInt_wrapper(intern->go_handle, (long)count);", + }, + }, + { + name: "int return with mixed params casts each by its own type", + returnType: phpInt, + params: []phpParameter{ + {Name: "name", PhpType: phpString}, + {Name: "count", PhpType: phpInt}, + {Name: "ratio", PhpType: phpFloat}, + {Name: "enabled", PhpType: phpBool}, + }, + wantContain: []string{ + "getVarInt_wrapper(intern->go_handle, name, (long)count, (double)ratio, (int)enabled);", + }, + wantAbsent: []string{ + "(long)name", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + generator := &Generator{ + BaseName: "test_extension", + Classes: []phpClass{ + { + Name: "Scriptling", + GoStruct: "Scriptling", + Methods: []phpClassMethod{ + { + Name: "getVarInt", + PhpName: "getVarInt", + ReturnType: tt.returnType, + ClassName: "Scriptling", + Params: tt.params, + }, + }, + }, + }, + BuildDir: t.TempDir(), + } + + cFileGen := cFileGenerator{generator: generator} + content, err := cFileGen.getTemplateContent() + require.NoError(t, err) + + for _, want := range tt.wantContain { + require.Contains(t, content, want) + } + for _, absent := range tt.wantAbsent { + require.NotContains(t, content, absent) + } + }) + } +} diff --git a/internal/extgen/templates/extension.c.tpl b/internal/extgen/templates/extension.c.tpl index 87b67475bb..fd7911d812 100644 --- a/internal/extgen/templates/extension.c.tpl +++ b/internal/extgen/templates/extension.c.tpl @@ -7,6 +7,14 @@ // You may edit the file and remove this comment if you plan to manually maintain // this file going forward. +{{define "methodCallArg" -}} +{{- if .IsNullable -}} +{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}} +{{- else -}} +{{if eq .PhpType "string"}}{{.Name}}{{else if eq .PhpType "int"}}(long){{.Name}}{{else if eq .PhpType "float"}}(double){{.Name}}{{else if eq .PhpType "bool"}}(int){{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}} +{{- end -}} +{{- end}} + #include #include #include @@ -124,22 +132,22 @@ PHP_METHOD({{namespacedClassName $.Namespace .ClassName}}, {{.PhpName}}) { {{- if ne .ReturnType "void"}} {{- if eq .ReturnType "string"}} - zend_string* result = {{.Name}}_wrapper(intern->go_handle{{if .Params}}{{range .Params}}, {{if .IsNullable}}{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{else}}{{.Name}}{{end}}{{end}}{{end}}{{end}}); + zend_string* result = {{.Name}}_wrapper(intern->go_handle{{range .Params}}, {{template "methodCallArg" .}}{{end}}); if (result) { RETURN_STR(result); } RETURN_EMPTY_STRING(); {{- else if eq .ReturnType "int"}} - zend_long result = {{.Name}}_wrapper(intern->go_handle{{if .Params}}{{range .Params}}, {{if .IsNullable}}{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{else}}(long){{.Name}}{{end}}{{end}}{{end}}{{end}}); + zend_long result = {{.Name}}_wrapper(intern->go_handle{{range .Params}}, {{template "methodCallArg" .}}{{end}}); RETURN_LONG(result); {{- else if eq .ReturnType "float"}} - double result = {{.Name}}_wrapper(intern->go_handle{{if .Params}}{{range .Params}}, {{if .IsNullable}}{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{else}}(double){{.Name}}{{end}}{{end}}{{end}}{{end}}); + double result = {{.Name}}_wrapper(intern->go_handle{{range .Params}}, {{template "methodCallArg" .}}{{end}}); RETURN_DOUBLE(result); {{- else if eq .ReturnType "bool"}} - int result = {{.Name}}_wrapper(intern->go_handle{{if .Params}}{{range .Params}}, {{if .IsNullable}}{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{else}}(int){{.Name}}{{end}}{{end}}{{end}}{{end}}); + int result = {{.Name}}_wrapper(intern->go_handle{{range .Params}}, {{template "methodCallArg" .}}{{end}}); RETURN_BOOL(result); {{- else if eq .ReturnType "array"}} - void* result = {{.Name}}_wrapper(intern->go_handle{{if .Params}}{{range .Params}}, {{if .IsNullable}}{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{else}}{{.Name}}{{end}}{{end}}{{end}}{{end}}); + void* result = {{.Name}}_wrapper(intern->go_handle{{range .Params}}, {{template "methodCallArg" .}}{{end}}); if (result != NULL) { HashTable *ht = (HashTable*)result; RETURN_ARR(ht); @@ -148,7 +156,7 @@ PHP_METHOD({{namespacedClassName $.Namespace .ClassName}}, {{.PhpName}}) { } {{- end}} {{- else}} - {{.Name}}_wrapper(intern->go_handle{{if .Params}}{{range .Params}}, {{if .IsNullable}}{{if eq .PhpType "string"}}{{.Name}}_is_null ? NULL : {{.Name}}{{else if eq .PhpType "int"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "float"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "bool"}}{{.Name}}_is_null ? NULL : &{{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{else}}{{if eq .PhpType "string"}}{{.Name}}{{else if eq .PhpType "int"}}(long){{.Name}}{{else if eq .PhpType "float"}}(double){{.Name}}{{else if eq .PhpType "bool"}}(int){{.Name}}{{else if eq .PhpType "array"}}{{.Name}}{{else if eq .PhpType "callable"}}{{.Name}}_callback{{end}}{{end}}{{end}}{{end}}); + {{.Name}}_wrapper(intern->go_handle{{range .Params}}, {{template "methodCallArg" .}}{{end}}); {{- end}} } {{end}}{{end}}