Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions internal/extgen/cfile_phpmethod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 <php.h>", "#include <php.h> 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)
}
})
}
}
20 changes: 14 additions & 6 deletions internal/extgen/templates/extension.c.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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 <php.h>
#include <Zend/zend_API.h>
#include <Zend/zend_hash.h>
Expand Down Expand Up @@ -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);
Expand All @@ -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}}
Expand Down
Loading