mirror of
https://github.com/php/frankenphp.git
synced 2026-03-24 00:52:11 +01:00
fix: returns a zend_array directly in types.go (#1894)
This commit is contained in:
committed by
GitHub
parent
41da660088
commit
11213fd1de
@@ -132,7 +132,7 @@ import (
|
||||
)
|
||||
|
||||
// export_php:function process_data_ordered(array $input): array
|
||||
func process_data_ordered_map(arr *C.zval) unsafe.Pointer {
|
||||
func process_data_ordered_map(arr *C.zend_array) unsafe.Pointer {
|
||||
// Convert PHP associative array to Go while keeping the order
|
||||
associativeArray, err := frankenphp.GoAssociativeArray[any](unsafe.Pointer(arr))
|
||||
if err != nil {
|
||||
@@ -157,7 +157,7 @@ func process_data_ordered_map(arr *C.zval) unsafe.Pointer {
|
||||
}
|
||||
|
||||
// export_php:function process_data_unordered(array $input): array
|
||||
func process_data_unordered_map(arr *C.zval) unsafe.Pointer {
|
||||
func process_data_unordered_map(arr *C.zend_array) unsafe.Pointer {
|
||||
// Convert PHP associative array to a Go map without keeping the order
|
||||
// ignoring the order will be more performant
|
||||
goMap, err := frankenphp.GoMap[any](unsafe.Pointer(arr))
|
||||
@@ -178,7 +178,7 @@ func process_data_unordered_map(arr *C.zval) unsafe.Pointer {
|
||||
}
|
||||
|
||||
// export_php:function process_data_packed(array $input): array
|
||||
func process_data_packed(arr *C.zval) unsafe.Pointer {
|
||||
func process_data_packed(arr *C.zend_array) unsafe.Pointer {
|
||||
// Convert PHP packed array to Go
|
||||
goSlice, err := frankenphp.GoPackedArray(unsafe.Pointer(arr))
|
||||
if err != nil {
|
||||
|
||||
@@ -119,16 +119,16 @@ PHP_METHOD({{namespacedClassName $.Namespace .ClassName}}, {{.PhpName}}) {
|
||||
}
|
||||
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}}{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else}}(long){{.Name}}{{end}}{{end}}{{end}}{{end}});
|
||||
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}} ? Z_ARRVAL_P({{.Name}}) : NULL{{end}}{{else}}{{if eq .PhpType "array"}}Z_ARRVAL_P({{.Name}}){{else}}(long){{.Name}}{{end}}{{end}}{{end}}{{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}}{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else}}(double){{.Name}}{{end}}{{end}}{{end}}{{end}});
|
||||
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}} ? Z_ARRVAL_P({{.Name}}) : NULL{{end}}{{else}}{{if eq .PhpType "array"}}Z_ARRVAL_P({{.Name}}){{else}}(double){{.Name}}{{end}}{{end}}{{end}}{{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}}{{end}}{{else}}{{if eq .PhpType "array"}}{{.Name}}{{else}}(int){{.Name}}{{end}}{{end}}{{end}}{{end}});
|
||||
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}} ? Z_ARRVAL_P({{.Name}}) : NULL{{end}}{{else}}{{if eq .PhpType "array"}}Z_ARRVAL_P({{.Name}}){{else}}(int){{.Name}}{{end}}{{end}}{{end}}{{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}}{{end}}{{else}}{{.Name}}{{end}}{{end}}{{end}});
|
||||
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}} ? Z_ARRVAL_P({{.Name}}) : NULL{{end}}{{else}}{{if eq .PhpType "array"}}Z_ARRVAL_P({{.Name}}){{else}}{{.Name}}{{end}}{{end}}{{end}}{{end}});
|
||||
if (result != NULL) {
|
||||
HashTable *ht = (HashTable*)result;
|
||||
RETURN_ARR(ht);
|
||||
@@ -137,7 +137,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}}{{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}}{{end}}{{end}}{{end}}{{end}});
|
||||
{{.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}} ? Z_ARRVAL_P({{.Name}}) : NULL{{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"}}Z_ARRVAL_P({{.Name}}){{end}}{{end}}{{end}}{{end}});
|
||||
{{- end}}
|
||||
}
|
||||
{{end}}{{end}}
|
||||
|
||||
@@ -203,7 +203,9 @@ func (v *Validator) phpTypeToGoType(t phpType, isNullable bool) string {
|
||||
baseType = "float64"
|
||||
case phpBool:
|
||||
baseType = "bool"
|
||||
case phpArray, phpMixed:
|
||||
case phpArray:
|
||||
baseType = "*C.zend_array"
|
||||
case phpMixed:
|
||||
baseType = "*C.zval"
|
||||
default:
|
||||
baseType = "any"
|
||||
|
||||
@@ -669,7 +669,7 @@ func TestValidateGoFunctionSignature(t *testing.T) {
|
||||
Params: []phpParameter{
|
||||
{Name: "items", PhpType: phpArray},
|
||||
},
|
||||
GoFunction: `func arrayFunc(items *C.zval) unsafe.Pointer {
|
||||
GoFunction: `func arrayFunc(items *C.zend_array) unsafe.Pointer {
|
||||
return nil
|
||||
}`,
|
||||
},
|
||||
@@ -684,7 +684,7 @@ func TestValidateGoFunctionSignature(t *testing.T) {
|
||||
{Name: "items", PhpType: phpArray, IsNullable: true},
|
||||
{Name: "name", PhpType: phpString},
|
||||
},
|
||||
GoFunction: `func nullableArrayFunc(items *C.zval, name *C.zend_string) unsafe.Pointer {
|
||||
GoFunction: `func nullableArrayFunc(items *C.zend_array, name *C.zend_string) unsafe.Pointer {
|
||||
return nil
|
||||
}`,
|
||||
},
|
||||
@@ -700,7 +700,7 @@ func TestValidateGoFunctionSignature(t *testing.T) {
|
||||
{Name: "filter", PhpType: phpString},
|
||||
{Name: "limit", PhpType: phpInt},
|
||||
},
|
||||
GoFunction: `func mixedFunc(data *C.zval, filter *C.zend_string, limit int64) unsafe.Pointer {
|
||||
GoFunction: `func mixedFunc(data *C.zend_array, filter *C.zend_string, limit int64) unsafe.Pointer {
|
||||
return nil
|
||||
}`,
|
||||
},
|
||||
@@ -737,8 +737,8 @@ func TestPhpTypeToGoType(t *testing.T) {
|
||||
{"float", true, "*float64"},
|
||||
{"bool", false, "bool"},
|
||||
{"bool", true, "*bool"},
|
||||
{"array", false, "*C.zval"},
|
||||
{"array", true, "*C.zval"},
|
||||
{"array", false, "*C.zend_array"},
|
||||
{"array", true, "*C.zend_array"},
|
||||
{"unknown", false, "any"},
|
||||
}
|
||||
|
||||
|
||||
2
types.c
2
types.c
@@ -31,6 +31,8 @@ void __zval_double__(zval *zv, double val) { ZVAL_DOUBLE(zv, val); }
|
||||
|
||||
void __zval_string__(zval *zv, zend_string *str) { ZVAL_STR(zv, str); }
|
||||
|
||||
void __zval_empty_string__(zval *zv) { ZVAL_EMPTY_STRING(zv); }
|
||||
|
||||
void __zval_arr__(zval *zv, zend_array *arr) { ZVAL_ARR(zv, arr); }
|
||||
|
||||
zend_array *__zend_new_array__(uint32_t size) { return zend_new_array(size); }
|
||||
|
||||
117
types.go
117
types.go
@@ -1,6 +1,20 @@
|
||||
package frankenphp
|
||||
|
||||
/*
|
||||
#cgo nocallback __zend_new_array__
|
||||
#cgo nocallback __zval_null__
|
||||
#cgo nocallback __zval_bool__
|
||||
#cgo nocallback __zval_long__
|
||||
#cgo nocallback __zval_double__
|
||||
#cgo nocallback __zval_string__
|
||||
#cgo nocallback __zval_arr__
|
||||
#cgo noescape __zend_new_array__
|
||||
#cgo noescape __zval_null__
|
||||
#cgo noescape __zval_bool__
|
||||
#cgo noescape __zval_long__
|
||||
#cgo noescape __zval_double__
|
||||
#cgo noescape __zval_string__
|
||||
#cgo noescape __zval_arr__
|
||||
#include "types.h"
|
||||
*/
|
||||
import "C"
|
||||
@@ -13,7 +27,7 @@ import (
|
||||
)
|
||||
|
||||
type toZval interface {
|
||||
toZval() *C.zval
|
||||
toZval(*C.zval)
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: GoString copies a zend_string to a Go string.
|
||||
@@ -50,8 +64,8 @@ type AssociativeArray[T any] struct {
|
||||
Order []string
|
||||
}
|
||||
|
||||
func (a AssociativeArray[T]) toZval() *C.zval {
|
||||
return (*C.zval)(PHPAssociativeArray[T](a))
|
||||
func (a AssociativeArray[T]) toZval(zval *C.zval) {
|
||||
C.__zval_arr__(zval, (*C.zend_array)(PHPAssociativeArray[T](a)))
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: GoAssociativeArray converts a zend_array to a Go AssociativeArray
|
||||
@@ -61,7 +75,7 @@ func GoAssociativeArray[T any](arr unsafe.Pointer) (AssociativeArray[T], error)
|
||||
return AssociativeArray[T]{entries, order}, err
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: GoMap converts a zval having a zend_array value to an unordered Go map
|
||||
// EXPERIMENTAL: GoMap converts a zend_array to an unordered Go map
|
||||
func GoMap[T any](arr unsafe.Pointer) (map[string]T, error) {
|
||||
entries, _, err := goArray[T](arr, false)
|
||||
|
||||
@@ -73,27 +87,25 @@ func goArray[T any](arr unsafe.Pointer, ordered bool) (map[string]T, []string, e
|
||||
return nil, nil, errors.New("received a nil pointer on array conversion")
|
||||
}
|
||||
|
||||
zval := (*C.zval)(arr)
|
||||
v, err := extractZvalValue(zval, C.IS_ARRAY)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("received a *zval that wasn't a HashTable on array conversion: %w", err)
|
||||
array := (*C.zend_array)(arr)
|
||||
|
||||
if array == nil {
|
||||
return nil, nil, fmt.Errorf("received a *zval that wasn't a HashTable on array conversion")
|
||||
}
|
||||
|
||||
hashTable := (*C.HashTable)(v)
|
||||
|
||||
nNumUsed := hashTable.nNumUsed
|
||||
nNumUsed := array.nNumUsed
|
||||
entries := make(map[string]T, nNumUsed)
|
||||
var order []string
|
||||
if ordered {
|
||||
order = make([]string, 0, nNumUsed)
|
||||
}
|
||||
|
||||
if htIsPacked(hashTable) {
|
||||
// if the HashTable is packed, convert all integer keys to strings
|
||||
if htIsPacked(array) {
|
||||
// if the array is packed, convert all integer keys to strings
|
||||
// this is probably a bug by the dev using this function
|
||||
// still, we'll (inefficiently) convert to an associative array
|
||||
for i := C.uint32_t(0); i < nNumUsed; i++ {
|
||||
v := C.get_ht_packed_data(hashTable, i)
|
||||
v := C.get_ht_packed_data(array, i)
|
||||
if v != nil && C.zval_get_type(v) != C.IS_UNDEF {
|
||||
strIndex := strconv.Itoa(int(i))
|
||||
e, err := goValue[T](v)
|
||||
@@ -114,7 +126,7 @@ func goArray[T any](arr unsafe.Pointer, ordered bool) (map[string]T, []string, e
|
||||
var zeroVal T
|
||||
|
||||
for i := C.uint32_t(0); i < nNumUsed; i++ {
|
||||
bucket := C.get_ht_bucket_data(hashTable, i)
|
||||
bucket := C.get_ht_bucket_data(array, i)
|
||||
if bucket == nil || C.zval_get_type(&bucket.val) == C.IS_UNDEF {
|
||||
continue
|
||||
}
|
||||
@@ -150,26 +162,24 @@ func goArray[T any](arr unsafe.Pointer, ordered bool) (map[string]T, []string, e
|
||||
return entries, order, nil
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: GoPackedArray converts a zval with a zend_array value to a Go slice
|
||||
// EXPERIMENTAL: GoPackedArray converts a zend_array to a Go slice
|
||||
func GoPackedArray[T any](arr unsafe.Pointer) ([]T, error) {
|
||||
if arr == nil {
|
||||
return nil, errors.New("GoPackedArray received a nil value")
|
||||
}
|
||||
|
||||
zval := (*C.zval)(arr)
|
||||
v, err := extractZvalValue(zval, C.IS_ARRAY)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GoPackedArray received *zval that wasn't a HashTable: %w", err)
|
||||
array := (*C.zend_array)(arr)
|
||||
|
||||
if array == nil {
|
||||
return nil, fmt.Errorf("GoPackedArray received *zval that wasn't a HashTable")
|
||||
}
|
||||
|
||||
hashTable := (*C.HashTable)(v)
|
||||
|
||||
nNumUsed := hashTable.nNumUsed
|
||||
nNumUsed := array.nNumUsed
|
||||
result := make([]T, 0, nNumUsed)
|
||||
|
||||
if htIsPacked(hashTable) {
|
||||
if htIsPacked(array) {
|
||||
for i := C.uint32_t(0); i < nNumUsed; i++ {
|
||||
v := C.get_ht_packed_data(hashTable, i)
|
||||
v := C.get_ht_packed_data(array, i)
|
||||
if v != nil && C.zval_get_type(v) != C.IS_UNDEF {
|
||||
v, err := goValue[T](v)
|
||||
if err != nil {
|
||||
@@ -185,7 +195,7 @@ func GoPackedArray[T any](arr unsafe.Pointer) ([]T, error) {
|
||||
|
||||
// fallback if ht isn't packed - equivalent to array_values()
|
||||
for i := C.uint32_t(0); i < nNumUsed; i++ {
|
||||
bucket := C.get_ht_bucket_data(hashTable, i)
|
||||
bucket := C.get_ht_bucket_data(array, i)
|
||||
if bucket != nil && C.zval_get_type(&bucket.val) != C.IS_UNDEF {
|
||||
v, err := goValue[T](&bucket.val)
|
||||
if err != nil {
|
||||
@@ -199,18 +209,18 @@ func GoPackedArray[T any](arr unsafe.Pointer) ([]T, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: PHPMap converts an unordered Go map to a PHP zend_array
|
||||
// EXPERIMENTAL: PHPMap converts an unordered Go map to a zend_array
|
||||
func PHPMap[T any](arr map[string]T) unsafe.Pointer {
|
||||
return phpArray[T](arr, nil)
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: PHPAssociativeArray converts a Go AssociativeArray to a PHP zval with a zend_array value
|
||||
// EXPERIMENTAL: PHPAssociativeArray converts a Go AssociativeArray to a zend_array
|
||||
func PHPAssociativeArray[T any](arr AssociativeArray[T]) unsafe.Pointer {
|
||||
return phpArray[T](arr.Map, arr.Order)
|
||||
}
|
||||
|
||||
func phpArray[T any](entries map[string]T, order []string) unsafe.Pointer {
|
||||
var zendArray *C.HashTable
|
||||
var zendArray *C.zend_array
|
||||
|
||||
if len(order) != 0 {
|
||||
zendArray = createNewArray((uint32)(len(order)))
|
||||
@@ -227,10 +237,7 @@ func phpArray[T any](entries map[string]T, order []string) unsafe.Pointer {
|
||||
}
|
||||
}
|
||||
|
||||
var zval C.zval
|
||||
C.__zval_arr__(&zval, zendArray)
|
||||
|
||||
return unsafe.Pointer(&zval)
|
||||
return unsafe.Pointer(zendArray)
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: PHPPackedArray converts a Go slice to a PHP zval with a zend_array value.
|
||||
@@ -241,10 +248,7 @@ func PHPPackedArray[T any](slice []T) unsafe.Pointer {
|
||||
C.zend_hash_next_index_insert(zendArray, zval)
|
||||
}
|
||||
|
||||
var zval C.zval
|
||||
C.__zval_arr__(&zval, zendArray)
|
||||
|
||||
return unsafe.Pointer(&zval)
|
||||
return unsafe.Pointer(zendArray)
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: GoValue converts a PHP zval to a Go value
|
||||
@@ -316,11 +320,11 @@ func goValue[T any](zval *C.zval) (res T, err error) {
|
||||
return resZero, err
|
||||
}
|
||||
|
||||
hashTable := (*C.HashTable)(v)
|
||||
if hashTable != nil && htIsPacked(hashTable) {
|
||||
array := (*C.zend_array)(v)
|
||||
if array != nil && htIsPacked(array) {
|
||||
typ := reflect.TypeOf(res)
|
||||
if typ == nil || typ.Kind() == reflect.Interface && typ.NumMethod() == 0 {
|
||||
r, e := GoPackedArray[any](unsafe.Pointer(zval))
|
||||
r, e := GoPackedArray[any](unsafe.Pointer(array))
|
||||
if e != nil {
|
||||
return resZero, e
|
||||
}
|
||||
@@ -333,7 +337,7 @@ func goValue[T any](zval *C.zval) (res T, err error) {
|
||||
return resZero, fmt.Errorf("cannot convert packed array to non-any Go type %s", typ.String())
|
||||
}
|
||||
|
||||
a, err := GoAssociativeArray[T](unsafe.Pointer(zval))
|
||||
a, err := GoAssociativeArray[T](unsafe.Pointer(array))
|
||||
if err != nil {
|
||||
return resZero, err
|
||||
}
|
||||
@@ -367,7 +371,8 @@ func phpValue(value any) *C.zval {
|
||||
var zval C.zval
|
||||
|
||||
if toZvalObj, ok := value.(toZval); ok {
|
||||
return toZvalObj.toZval()
|
||||
toZvalObj.toZval(&zval)
|
||||
return &zval
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
@@ -382,12 +387,18 @@ func phpValue(value any) *C.zval {
|
||||
case float64:
|
||||
C.__zval_double__(&zval, C.double(v))
|
||||
case string:
|
||||
if v == "" {
|
||||
C.__zval_empty_string__(&zval)
|
||||
break
|
||||
}
|
||||
str := (*C.zend_string)(PHPString(v, false))
|
||||
C.__zval_string__(&zval, str)
|
||||
case AssociativeArray[any]:
|
||||
C.__zval_arr__(&zval, (*C.zend_array)(PHPAssociativeArray[any](v)))
|
||||
case map[string]any:
|
||||
return (*C.zval)(PHPAssociativeArray[any](AssociativeArray[any]{Map: v}))
|
||||
C.__zval_arr__(&zval, (*C.zend_array)(PHPMap[any](v)))
|
||||
case []any:
|
||||
return (*C.zval)(PHPPackedArray(v))
|
||||
C.__zval_arr__(&zval, (*C.zend_array)(PHPPackedArray[any](v)))
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported Go type %T", v))
|
||||
}
|
||||
@@ -396,13 +407,13 @@ func phpValue(value any) *C.zval {
|
||||
}
|
||||
|
||||
// createNewArray creates a new zend_array with the specified size.
|
||||
func createNewArray(size uint32) *C.HashTable {
|
||||
func createNewArray(size uint32) *C.zend_array {
|
||||
arr := C.__zend_new_array__(C.uint32_t(size))
|
||||
return (*C.HashTable)(unsafe.Pointer(arr))
|
||||
return (*C.zend_array)(unsafe.Pointer(arr))
|
||||
}
|
||||
|
||||
// htIsPacked checks if a HashTable is a list (packed) or hashmap (not packed).
|
||||
func htIsPacked(ht *C.HashTable) bool {
|
||||
// htIsPacked checks if a zend_array is a list (packed) or hashmap (not packed).
|
||||
func htIsPacked(ht *C.zend_array) bool {
|
||||
flags := *(*C.uint32_t)(unsafe.Pointer(&ht.u[0]))
|
||||
|
||||
return (flags & C.HASH_FLAG_PACKED) != 0
|
||||
@@ -435,3 +446,13 @@ func extractZvalValue(zval *C.zval, expectedType C.uint8_t) (unsafe.Pointer, err
|
||||
|
||||
return nil, fmt.Errorf("unsupported zval type %d", expectedType)
|
||||
}
|
||||
|
||||
func zendStringRelease(p unsafe.Pointer) {
|
||||
zs := (*C.zend_string)(p)
|
||||
C.zend_string_release(zs)
|
||||
}
|
||||
|
||||
func zendHashDestroy(p unsafe.Pointer) {
|
||||
ht := (*C.zend_array)(p)
|
||||
C.zend_hash_destroy(ht)
|
||||
}
|
||||
|
||||
1
types.h
1
types.h
@@ -19,6 +19,7 @@ void __zval_bool__(zval *zv, bool val);
|
||||
void __zval_long__(zval *zv, zend_long val);
|
||||
void __zval_double__(zval *zv, double val);
|
||||
void __zval_string__(zval *zv, zend_string *str);
|
||||
void __zval_empty_string__(zval *zv);
|
||||
void __zval_arr__(zval *zv, zend_array *arr);
|
||||
zend_array *__zend_new_array__(uint32_t size);
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ func TestGoString(t *testing.T) {
|
||||
testOnDummyPHPThread(t, func() {
|
||||
originalString := "Hello, World!"
|
||||
|
||||
convertedString := GoString(PHPString(originalString, false))
|
||||
phpString := PHPString(originalString, false)
|
||||
defer zendStringRelease(phpString)
|
||||
|
||||
assert.Equal(t, originalString, convertedString, "string -> zend_string -> string should yield an equal string")
|
||||
assert.Equal(t, originalString, GoString(phpString), "string -> zend_string -> string should yield an equal string")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,7 +43,9 @@ func TestPHPMap(t *testing.T) {
|
||||
"foo2": "bar2",
|
||||
}
|
||||
|
||||
convertedMap, err := GoMap[string](PHPMap(originalMap))
|
||||
phpArray := PHPMap(originalMap)
|
||||
defer zendHashDestroy(phpArray)
|
||||
convertedMap, err := GoMap[string](phpArray)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, originalMap, convertedMap, "associative array should be equal after conversion")
|
||||
@@ -59,7 +62,9 @@ func TestOrderedPHPAssociativeArray(t *testing.T) {
|
||||
Order: []string{"foo2", "foo1"},
|
||||
}
|
||||
|
||||
convertedArray, err := GoAssociativeArray[string](PHPAssociativeArray(originalArray))
|
||||
phpArray := PHPAssociativeArray(originalArray)
|
||||
defer zendHashDestroy(phpArray)
|
||||
convertedArray, err := GoAssociativeArray[string](phpArray)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, originalArray, convertedArray, "associative array should be equal after conversion")
|
||||
@@ -70,7 +75,9 @@ func TestPHPPackedArray(t *testing.T) {
|
||||
testOnDummyPHPThread(t, func() {
|
||||
originalSlice := []string{"bar1", "bar2"}
|
||||
|
||||
convertedSlice, err := GoPackedArray[string](PHPPackedArray(originalSlice))
|
||||
phpArray := PHPPackedArray(originalSlice)
|
||||
defer zendHashDestroy(phpArray)
|
||||
convertedSlice, err := GoPackedArray[string](phpArray)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, originalSlice, convertedSlice, "slice should be equal after conversion")
|
||||
@@ -85,7 +92,9 @@ func TestPHPPackedArrayToGoMap(t *testing.T) {
|
||||
"1": "bar2",
|
||||
}
|
||||
|
||||
convertedMap, err := GoMap[string](PHPPackedArray(originalSlice))
|
||||
phpArray := PHPPackedArray(originalSlice)
|
||||
defer zendHashDestroy(phpArray)
|
||||
convertedMap, err := GoMap[string](phpArray)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expectedMap, convertedMap, "convert a packed to an associative array")
|
||||
@@ -103,7 +112,9 @@ func TestPHPAssociativeArrayToPacked(t *testing.T) {
|
||||
}
|
||||
expectedSlice := []string{"bar1", "bar2"}
|
||||
|
||||
convertedSlice, err := GoPackedArray[string](PHPAssociativeArray(originalArray))
|
||||
phpArray := PHPAssociativeArray(originalArray)
|
||||
defer zendHashDestroy(phpArray)
|
||||
convertedSlice, err := GoPackedArray[string](phpArray)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, expectedSlice, convertedSlice, "convert an associative array to a slice")
|
||||
@@ -126,7 +137,9 @@ func TestNestedMixedArray(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
convertedArray, err := GoMap[any](PHPMap(originalArray))
|
||||
phpArray := PHPMap(originalArray)
|
||||
defer zendHashDestroy(phpArray)
|
||||
convertedArray, err := GoMap[any](phpArray)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, originalArray, convertedArray, "nested mixed array should be equal after conversion")
|
||||
|
||||
Reference in New Issue
Block a user