Skip to content
Merged
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
6 changes: 3 additions & 3 deletions docs/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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))
Expand All @@ -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 {
Expand Down
10 changes: 5 additions & 5 deletions internal/extgen/templates/extension.c.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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}}
Expand Down
4 changes: 3 additions & 1 deletion internal/extgen/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 5 additions & 5 deletions internal/extgen/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}`,
},
Expand All @@ -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
}`,
},
Expand All @@ -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
}`,
},
Expand Down Expand Up @@ -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"},
}

Expand Down
2 changes: 2 additions & 0 deletions types.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 69 additions & 48 deletions types.go
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -13,7 +27,7 @@ import (
)

type toZval interface {
toZval() *C.zval
toZval(*C.zval)
}

// EXPERIMENTAL: GoString copies a zend_string to a Go string.
Expand Down Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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)

hashTable := (*C.HashTable)(v)
if array == nil {
return nil, nil, fmt.Errorf("received a *zval that wasn't a HashTable on array conversion")
}

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)
Expand All @@ -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
}
Expand Down Expand Up @@ -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)

hashTable := (*C.HashTable)(v)
if array == nil {
return nil, fmt.Errorf("GoPackedArray received *zval that wasn't a HashTable")
}

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 {
Expand All @@ -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 {
Expand All @@ -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)))
Expand All @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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))
}
Expand All @@ -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
Expand Down Expand Up @@ -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 change: 1 addition & 0 deletions types.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading
Loading