mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-12-24 13:38:11 +08:00
Fixes all leaks.
This commit is contained in:
10
testdata/tasks/task-dispatcher-date.php
vendored
Normal file
10
testdata/tasks/task-dispatcher-date.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/../_executor.php';
|
||||
|
||||
return function () {
|
||||
$date = new DateTime('2024-01-01 12:00:00', new DateTimeZone('Europe/Vienna'));
|
||||
frankenphp_dispatch_task($date);
|
||||
|
||||
echo "dispatched tasks\n";
|
||||
};
|
||||
6
testdata/tasks/task-dispatcher-string.php
vendored
6
testdata/tasks/task-dispatcher-string.php
vendored
@@ -6,11 +6,7 @@ return function () {
|
||||
$taskCount = $_GET['count'] ?? 0;
|
||||
$workerName = $_GET['worker'] ?? '';
|
||||
for ($i = 0; $i < $taskCount; $i++) {
|
||||
$c = new DateTime();
|
||||
$c->setTimestamp(time()+123);
|
||||
$c->setTimezone(new Datetimezone('America/New_York'));
|
||||
#$c = serialize($c);
|
||||
frankenphp_dispatch_task($c, $workerName);
|
||||
frankenphp_dispatch_task("task$i", $workerName);
|
||||
}
|
||||
echo "dispatched $taskCount tasks\n";
|
||||
};
|
||||
|
||||
@@ -241,10 +241,11 @@ func go_frankenphp_worker_handle_task(threadIndex C.uintptr_t) *C.zval {
|
||||
}
|
||||
|
||||
// if the task has no callback, forward it to PHP
|
||||
zval := phpValue(task.arg)
|
||||
thread.Pin(unsafe.Pointer(zval))
|
||||
var zval C.zval
|
||||
phpValue(&zval, task.arg)
|
||||
thread.Pin(unsafe.Pointer(&zval))
|
||||
|
||||
return zval
|
||||
return &zval
|
||||
case <-handler.thread.drainChan:
|
||||
thread.state.markAsWaiting(false)
|
||||
// send an empty task to drain the thread
|
||||
|
||||
@@ -117,3 +117,26 @@ func TestDispatchToMultipleWorkers(t *testing.T) {
|
||||
assertGetRequest(t, script+"?count=1&worker=worker2", "dispatched 1 tasks")
|
||||
assertGetRequest(t, script+"?count=1&worker=worker3", "No worker found to handle this task") // fail
|
||||
}
|
||||
|
||||
func TestDispatchInternalDateObject(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
handler := slog.NewTextHandler(&buf, &slog.HandlerOptions{Level: slog.LevelDebug})
|
||||
logger := slog.New(handler)
|
||||
|
||||
assert.NoError(t, Init(
|
||||
WithWorkers("worker1", "./testdata/tasks/task-worker.php", 1, AsTaskWorker(true, 0)),
|
||||
WithNumThreads(2),
|
||||
WithLogger(logger),
|
||||
))
|
||||
|
||||
assertGetRequest(t, "http://example.com/testdata/tasks/task-dispatcher-date.php", "dispatched task")
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
Shutdown()
|
||||
|
||||
// task output appears in logs at info level
|
||||
logOutput := buf.String()
|
||||
assert.Contains(t, logOutput, "object(DateTime)")
|
||||
assert.Contains(t, logOutput, "2024")
|
||||
assert.Contains(t, logOutput, "Europe/Vienna")
|
||||
}
|
||||
|
||||
5
types.c
5
types.c
@@ -78,3 +78,8 @@ void __zval_unserialize__(zval *retval, zend_string *str) {
|
||||
zval_ptr_dtor(&func);
|
||||
zend_string_release(str);
|
||||
}
|
||||
|
||||
zval *__init_zval__() {
|
||||
zval *zv = (zval *)emalloc(sizeof(zval));
|
||||
return zv;
|
||||
}
|
||||
66
types.go
66
types.go
@@ -198,14 +198,16 @@ func phpArray(entries map[string]any, order []string) unsafe.Pointer {
|
||||
zendArray = createNewArray((uint32)(len(order)))
|
||||
for _, key := range order {
|
||||
val := entries[key]
|
||||
zval := phpValue(val)
|
||||
C.zend_hash_str_update(zendArray, toUnsafeChar(key), C.size_t(len(key)), zval)
|
||||
var zval C.zval
|
||||
phpValue(&zval, val)
|
||||
C.zend_hash_str_update(zendArray, toUnsafeChar(key), C.size_t(len(key)), &zval)
|
||||
}
|
||||
} else {
|
||||
zendArray = createNewArray((uint32)(len(entries)))
|
||||
for key, val := range entries {
|
||||
zval := phpValue(val)
|
||||
C.zend_hash_str_update(zendArray, toUnsafeChar(key), C.size_t(len(key)), zval)
|
||||
var zval C.zval
|
||||
phpValue(&zval, val)
|
||||
C.zend_hash_str_update(zendArray, toUnsafeChar(key), C.size_t(len(key)), &zval)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,8 +218,9 @@ func phpArray(entries map[string]any, order []string) unsafe.Pointer {
|
||||
func PHPPackedArray(slice []any) unsafe.Pointer {
|
||||
zendArray := createNewArray((uint32)(len(slice)))
|
||||
for _, val := range slice {
|
||||
zval := phpValue(val)
|
||||
C.zend_hash_next_index_insert(zendArray, zval)
|
||||
var zval C.zval
|
||||
phpValue(&zval, val)
|
||||
C.zend_hash_next_index_insert(zendArray, &zval)
|
||||
}
|
||||
|
||||
return unsafe.Pointer(zendArray)
|
||||
@@ -280,43 +283,41 @@ func goValue(zval *C.zval) any {
|
||||
|
||||
// EXPERIMENTAL: PHPValue converts a Go any to a PHP zval
|
||||
func PHPValue(value any) unsafe.Pointer {
|
||||
return unsafe.Pointer(phpValue(value))
|
||||
var zval C.zval // TODO: emalloc?
|
||||
phpValue(&zval, value)
|
||||
return unsafe.Pointer(&zval)
|
||||
}
|
||||
|
||||
func phpValue(value any) *C.zval {
|
||||
var zval C.zval
|
||||
|
||||
func phpValue(zval *C.zval, value any) {
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
C.__zval_null__(&zval)
|
||||
C.__zval_null__(zval)
|
||||
case bool:
|
||||
C.__zval_bool__(&zval, C._Bool(v))
|
||||
C.__zval_bool__(zval, C._Bool(v))
|
||||
case int:
|
||||
C.__zval_long__(&zval, C.zend_long(v))
|
||||
C.__zval_long__(zval, C.zend_long(v))
|
||||
case int64:
|
||||
C.__zval_long__(&zval, C.zend_long(v))
|
||||
C.__zval_long__(zval, C.zend_long(v))
|
||||
case float64:
|
||||
C.__zval_double__(&zval, C.double(v))
|
||||
C.__zval_double__(zval, C.double(v))
|
||||
case string:
|
||||
if v == "" {
|
||||
C.__zval_empty_string__(&zval)
|
||||
C.__zval_empty_string__(zval)
|
||||
break
|
||||
}
|
||||
str := (*C.zend_string)(PHPString(v, false))
|
||||
C.__zval_string__(&zval, str)
|
||||
C.__zval_string__(zval, str)
|
||||
case AssociativeArray:
|
||||
C.__zval_arr__(&zval, (*C.zend_array)(PHPAssociativeArray(v)))
|
||||
C.__zval_arr__(zval, (*C.zend_array)(PHPAssociativeArray(v)))
|
||||
case map[string]any:
|
||||
C.__zval_arr__(&zval, (*C.zend_array)(PHPMap(v)))
|
||||
C.__zval_arr__(zval, (*C.zend_array)(PHPMap(v)))
|
||||
case []any:
|
||||
return (*C.zval)(PHPPackedArray(v))
|
||||
C.__zval_arr__(zval, (*C.zend_array)(PHPPackedArray(v)))
|
||||
case Object:
|
||||
phpObject(&zval, v)
|
||||
phpObject(zval, v)
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported Go type %T", v))
|
||||
}
|
||||
|
||||
return &zval
|
||||
}
|
||||
|
||||
func GoObject(obj unsafe.Pointer) Object {
|
||||
@@ -334,13 +335,13 @@ func goObject(obj *C.zend_object) Object {
|
||||
className := GoString(unsafe.Pointer(classEntry.name))
|
||||
|
||||
//C.instanceof_function(obj.ce, dateTimeCe)
|
||||
if C.is_internal_class(classEntry){
|
||||
return Object{
|
||||
if C.is_internal_class(classEntry) {
|
||||
return Object{
|
||||
ClassName: className,
|
||||
serialized: C.__zval_serialize__(obj),
|
||||
ce: classEntry,
|
||||
ce: classEntry,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
props := make(map[string]any)
|
||||
|
||||
@@ -410,13 +411,14 @@ func phpObject(zv *C.zval, obj Object) {
|
||||
C.object_init_ex(zv, classEntry)
|
||||
|
||||
var zendObj *C.zend_object
|
||||
zendObj = (*C.zend_object)(extractZvalValue(zv, C.IS_OBJECT))
|
||||
zendObj = *(**C.zend_object)(unsafe.Pointer(&zv.value[0]))
|
||||
|
||||
// set the properties
|
||||
for key, val := range obj.Props {
|
||||
zval := phpValue(val) // TODO: put on stack
|
||||
C.zend_update_property(classEntry, zendObj, toUnsafeChar(key), C.size_t(len(key)), zval)
|
||||
C.zval_ptr_dtor(zval)
|
||||
var zval C.zval
|
||||
phpValue(&zval, val)
|
||||
C.zend_update_property(classEntry, zendObj, toUnsafeChar(key), C.size_t(len(key)), &zval)
|
||||
C.zval_ptr_dtor(&zval)
|
||||
}
|
||||
|
||||
// TODO: wakeup?
|
||||
@@ -471,5 +473,5 @@ func zendStringRelease(p unsafe.Pointer) {
|
||||
|
||||
func zendHashDestroy(p unsafe.Pointer) {
|
||||
ht := (*C.zend_array)(p)
|
||||
C.zend_hash_destroy(ht)
|
||||
C.zend_array_destroy(ht)
|
||||
}
|
||||
|
||||
1
types.h
1
types.h
@@ -26,5 +26,6 @@ zend_array *__zend_new_array__(uint32_t size);
|
||||
bool is_internal_class(zend_class_entry *entry);
|
||||
zend_string *__zval_serialize__(zend_object *obj);
|
||||
void __zval_unserialize__(zval *retval, zend_string *str);
|
||||
zval *__init_zval__();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -163,9 +163,9 @@ func TestPHPObject(t *testing.T) {
|
||||
|
||||
phpObject := PHPObject(originalObject)
|
||||
defer zvalPtrDtor(phpObject)
|
||||
convertedObject := GoObject(phpObject)
|
||||
|
||||
assert.Equal(t, originalObject.ClassName, convertedObject.ClassName, "nested mixed array should be equal after conversion")
|
||||
assert.Equal(t, originalObject.Props, convertedObject.Props, "nested mixed array should be equal after conversion")
|
||||
convertedObject := GoObject(phpObject)
|
||||
assert.Equal(t, originalObject.ClassName, convertedObject.ClassName, "object class should be equal after conversion")
|
||||
assert.Equal(t, originalObject.Props, convertedObject.Props, "object props should be equal after conversion")
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user