mirror of https://github.com/deuill/go-php.git
Merge pull request #19 from deuill/feature/go16-compatibility
Compatibility fixes for Go 1.6
This commit is contained in:
commit
d9b0735a69
|
@ -11,7 +11,7 @@
|
|||
#include "value.h"
|
||||
#include "context.h"
|
||||
|
||||
engine_context *context_new(void *ctx) {
|
||||
engine_context *context_new() {
|
||||
engine_context *context;
|
||||
|
||||
// Initialize context.
|
||||
|
@ -21,7 +21,6 @@ engine_context *context_new(void *ctx) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
context->ctx = ctx;
|
||||
SG(server_context) = context;
|
||||
|
||||
// Initialize request lifecycle.
|
||||
|
|
|
@ -9,7 +9,6 @@ package engine
|
|||
//
|
||||
// #include <stdlib.h>
|
||||
// #include <main/php.h>
|
||||
// #include "receiver.h"
|
||||
// #include "context.h"
|
||||
import "C"
|
||||
|
||||
|
@ -31,9 +30,8 @@ type Context struct {
|
|||
// Header represents the HTTP headers set by current PHP context.
|
||||
Header http.Header
|
||||
|
||||
context *C.struct__engine_context
|
||||
values []*Value
|
||||
receivers map[string]*Receiver
|
||||
context *C.struct__engine_context
|
||||
values []*Value
|
||||
}
|
||||
|
||||
// Bind allows for binding Go values into the current execution context under
|
||||
|
@ -55,34 +53,6 @@ func (c *Context) Bind(name string, val interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Define registers a PHP class for the name passed, using function fn as
|
||||
// constructor for individual object instances as needed by the PHP context.
|
||||
//
|
||||
// The class name registered is assumed to be unique for the active engine.
|
||||
//
|
||||
// The constructor function accepts a slice of arguments, as passed by the PHP
|
||||
// context, and should return a method receiver instance, or nil on error (in
|
||||
// which case, an exception is thrown on the PHP object constructor).
|
||||
func (c *Context) Define(name string, fn func(args []interface{}) interface{}) error {
|
||||
if _, exists := c.receivers[name]; exists {
|
||||
return fmt.Errorf("Failed to define duplicate receiver '%s'", name)
|
||||
}
|
||||
|
||||
rcvr := &Receiver{
|
||||
name: name,
|
||||
create: fn,
|
||||
objects: make([]*ReceiverObject, 0),
|
||||
}
|
||||
|
||||
n := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(n))
|
||||
|
||||
C.receiver_define(n, unsafe.Pointer(rcvr))
|
||||
c.receivers[name] = rcvr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exec executes a PHP script pointed to by filename in the current execution
|
||||
// context, and returns an error, if any. Output produced by the script is
|
||||
// written to the context's pre-defined io.Writer instance.
|
||||
|
@ -129,12 +99,6 @@ func (c *Context) Destroy() {
|
|||
return
|
||||
}
|
||||
|
||||
for _, r := range c.receivers {
|
||||
r.Destroy()
|
||||
}
|
||||
|
||||
c.receivers = nil
|
||||
|
||||
for _, v := range c.values {
|
||||
v.Destroy()
|
||||
}
|
||||
|
|
|
@ -31,28 +31,6 @@ func TestContextNew(t *testing.T) {
|
|||
c.Destroy()
|
||||
}
|
||||
|
||||
func TestContextDefine(t *testing.T) {
|
||||
ctor := func(args []interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
c, _ := e.NewContext()
|
||||
|
||||
if err := c.Define("TestDefine", ctor); err != nil {
|
||||
t.Errorf("Context.Define(): %s", err)
|
||||
}
|
||||
|
||||
if len(c.receivers) != 1 {
|
||||
t.Errorf("Context.Define(): `Context.receivers` length is %d, should be 1", len(c.receivers))
|
||||
}
|
||||
|
||||
if err := c.Define("TestDefine", ctor); err == nil {
|
||||
t.Errorf("Context.Define(): Incorrectly defined duplicate receiver")
|
||||
}
|
||||
|
||||
c.Destroy()
|
||||
}
|
||||
|
||||
var execTests = []struct {
|
||||
name string
|
||||
script string
|
||||
|
|
|
@ -33,7 +33,7 @@ const char engine_ini_defaults[] = {
|
|||
static ENGINE_UB_WRITE(str, len) {
|
||||
engine_context *context = SG(server_context);
|
||||
|
||||
int written = engineWriteOut(context->ctx, (void *) str, len);
|
||||
int written = engineWriteOut(context, (void *) str, len);
|
||||
if (written != len) {
|
||||
php_handle_aborted_connection();
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ static int engine_header_handler(sapi_header_struct *sapi_header, sapi_header_op
|
|||
case SAPI_HEADER_REPLACE:
|
||||
case SAPI_HEADER_ADD:
|
||||
case SAPI_HEADER_DELETE:
|
||||
engineSetHeader(context->ctx, op, (void *) sapi_header->header, sapi_header->header_len);
|
||||
engineSetHeader(context, op, (void *) sapi_header->header, sapi_header->header_len);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ static void engine_register_variables(zval *track_vars_array) {
|
|||
static void engine_log_message(char *str) {
|
||||
engine_context *context = SG(server_context);
|
||||
|
||||
engineWriteLog(context->ctx, (void *) str, strlen(str));
|
||||
engineWriteLog(context, (void *) str, strlen(str));
|
||||
}
|
||||
|
||||
static sapi_module_struct engine_module = {
|
||||
|
|
195
engine/engine.go
195
engine/engine.go
|
@ -11,6 +11,7 @@ package engine
|
|||
//
|
||||
// #include <stdlib.h>
|
||||
// #include <main/php.h>
|
||||
// #include "receiver.h"
|
||||
// #include "context.h"
|
||||
// #include "engine.h"
|
||||
import "C"
|
||||
|
@ -25,53 +26,96 @@ import (
|
|||
|
||||
// Engine represents the core PHP engine bindings.
|
||||
type Engine struct {
|
||||
engine *C.struct__php_engine
|
||||
contexts []*Context
|
||||
engine *C.struct__php_engine
|
||||
contexts map[*C.struct__engine_context]*Context
|
||||
receivers map[string]*Receiver
|
||||
}
|
||||
|
||||
// This contains a reference to the active engine, if any.
|
||||
var engine *Engine
|
||||
|
||||
// New initializes a PHP engine instance on which contexts can be executed. It
|
||||
// corresponds to PHP's MINIT (module init) phase.
|
||||
func New() (*Engine, error) {
|
||||
if engine != nil {
|
||||
return nil, fmt.Errorf("Cannot activate multiple engine instances")
|
||||
}
|
||||
|
||||
ptr, err := C.engine_init()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("PHP engine failed to initialize")
|
||||
}
|
||||
|
||||
e := &Engine{
|
||||
engine: ptr,
|
||||
contexts: make([]*Context, 0),
|
||||
engine = &Engine{
|
||||
engine: ptr,
|
||||
contexts: make(map[*C.struct__engine_context]*Context),
|
||||
receivers: make(map[string]*Receiver),
|
||||
}
|
||||
|
||||
return e, nil
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
// NewContext creates a new execution context for the active engine and returns
|
||||
// an error if the execution context failed to initialize at any point. This
|
||||
// corresponds to PHP's RINIT (request init) phase.
|
||||
func (e *Engine) NewContext() (*Context, error) {
|
||||
ctx := &Context{
|
||||
Header: make(http.Header),
|
||||
values: make([]*Value, 0),
|
||||
receivers: make(map[string]*Receiver),
|
||||
}
|
||||
|
||||
ptr, err := C.context_new(unsafe.Pointer(ctx))
|
||||
ptr, err := C.context_new()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to initialize context for PHP engine")
|
||||
}
|
||||
|
||||
ctx.context = ptr
|
||||
e.contexts = append(e.contexts, ctx)
|
||||
ctx := &Context{
|
||||
Header: make(http.Header),
|
||||
context: ptr,
|
||||
values: make([]*Value, 0),
|
||||
}
|
||||
|
||||
// Store reference to context, using pointer as key.
|
||||
e.contexts[ptr] = ctx
|
||||
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Define registers a PHP class for the name passed, using function fn as
|
||||
// constructor for individual object instances as needed by the PHP context.
|
||||
//
|
||||
// The class name registered is assumed to be unique for the active engine.
|
||||
//
|
||||
// The constructor function accepts a slice of arguments, as passed by the PHP
|
||||
// context, and should return a method receiver instance, or nil on error (in
|
||||
// which case, an exception is thrown on the PHP object constructor).
|
||||
func (e *Engine) Define(name string, fn func(args []interface{}) interface{}) error {
|
||||
if _, exists := e.receivers[name]; exists {
|
||||
return fmt.Errorf("Failed to define duplicate receiver '%s'", name)
|
||||
}
|
||||
|
||||
rcvr := &Receiver{
|
||||
name: name,
|
||||
create: fn,
|
||||
objects: make(map[*C.struct__engine_receiver]*ReceiverObject),
|
||||
}
|
||||
|
||||
n := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(n))
|
||||
|
||||
C.receiver_define(n)
|
||||
e.receivers[name] = rcvr
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy shuts down and frees any resources related to the PHP engine bindings.
|
||||
func (e *Engine) Destroy() {
|
||||
if e.engine == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, r := range e.receivers {
|
||||
r.Destroy()
|
||||
}
|
||||
|
||||
e.receivers = nil
|
||||
|
||||
for _, c := range e.contexts {
|
||||
c.Destroy()
|
||||
}
|
||||
|
@ -80,6 +124,8 @@ func (e *Engine) Destroy() {
|
|||
|
||||
C.engine_shutdown(e.engine)
|
||||
e.engine = nil
|
||||
|
||||
engine = nil
|
||||
}
|
||||
|
||||
func write(w io.Writer, buffer unsafe.Pointer, length C.uint) C.int {
|
||||
|
@ -97,22 +143,28 @@ func write(w io.Writer, buffer unsafe.Pointer, length C.uint) C.int {
|
|||
}
|
||||
|
||||
//export engineWriteOut
|
||||
func engineWriteOut(ctxptr, buffer unsafe.Pointer, length C.uint) C.int {
|
||||
c := (*Context)(ctxptr)
|
||||
func engineWriteOut(ctx *C.struct__engine_context, buffer unsafe.Pointer, length C.uint) C.int {
|
||||
if engine == nil || engine.contexts[ctx] == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return write(c.Output, buffer, length)
|
||||
return write(engine.contexts[ctx].Output, buffer, length)
|
||||
}
|
||||
|
||||
//export engineWriteLog
|
||||
func engineWriteLog(ctxptr unsafe.Pointer, buffer unsafe.Pointer, length C.uint) C.int {
|
||||
c := (*Context)(ctxptr)
|
||||
func engineWriteLog(ctx *C.struct__engine_context, buffer unsafe.Pointer, length C.uint) C.int {
|
||||
if engine == nil || engine.contexts[ctx] == nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return write(c.Log, buffer, length)
|
||||
return write(engine.contexts[ctx].Log, buffer, length)
|
||||
}
|
||||
|
||||
//export engineSetHeader
|
||||
func engineSetHeader(ctxptr unsafe.Pointer, operation C.uint, buffer unsafe.Pointer, length C.uint) {
|
||||
c := (*Context)(ctxptr)
|
||||
func engineSetHeader(ctx *C.struct__engine_context, operation C.uint, buffer unsafe.Pointer, length C.uint) {
|
||||
if engine == nil || engine.contexts[ctx] == nil {
|
||||
return
|
||||
}
|
||||
|
||||
header := (string)(C.GoBytes(buffer, C.int(length)))
|
||||
split := strings.SplitN(header, ":", 2)
|
||||
|
@ -124,15 +176,106 @@ func engineSetHeader(ctxptr unsafe.Pointer, operation C.uint, buffer unsafe.Poin
|
|||
switch operation {
|
||||
case 0: // Replace header.
|
||||
if len(split) == 2 && split[1] != "" {
|
||||
c.Header.Set(split[0], split[1])
|
||||
engine.contexts[ctx].Header.Set(split[0], split[1])
|
||||
}
|
||||
case 1: // Append header.
|
||||
if len(split) == 2 && split[1] != "" {
|
||||
c.Header.Add(split[0], split[1])
|
||||
engine.contexts[ctx].Header.Add(split[0], split[1])
|
||||
}
|
||||
case 2: // Delete header.
|
||||
if split[0] != "" {
|
||||
c.Header.Del(split[0])
|
||||
engine.contexts[ctx].Header.Del(split[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//export engineReceiverNew
|
||||
func engineReceiverNew(rcvr *C.struct__engine_receiver, args unsafe.Pointer) C.int {
|
||||
n := C.GoString(C.receiver_get_name(rcvr))
|
||||
if engine == nil || engine.receivers[n] == nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
va, err := NewValueFromPtr(args)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
defer va.Destroy()
|
||||
|
||||
obj, err := engine.receivers[n].NewObject(va.Slice())
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
engine.receivers[n].objects[rcvr] = obj
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//export engineReceiverGet
|
||||
func engineReceiverGet(rcvr *C.struct__engine_receiver, name *C.char) unsafe.Pointer {
|
||||
n := C.GoString(C.receiver_get_name(rcvr))
|
||||
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
val, err := engine.receivers[n].objects[rcvr].Get(C.GoString(name))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.Ptr()
|
||||
}
|
||||
|
||||
//export engineReceiverSet
|
||||
func engineReceiverSet(rcvr *C.struct__engine_receiver, name *C.char, val unsafe.Pointer) {
|
||||
n := C.GoString(C.receiver_get_name(rcvr))
|
||||
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err := NewValueFromPtr(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
engine.receivers[n].objects[rcvr].Set(C.GoString(name), v.Interface())
|
||||
}
|
||||
|
||||
//export engineReceiverExists
|
||||
func engineReceiverExists(rcvr *C.struct__engine_receiver, name *C.char) C.int {
|
||||
n := C.GoString(C.receiver_get_name(rcvr))
|
||||
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if engine.receivers[n].objects[rcvr].Exists(C.GoString(name)) {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
//export engineReceiverCall
|
||||
func engineReceiverCall(rcvr *C.struct__engine_receiver, name *C.char, args unsafe.Pointer) unsafe.Pointer {
|
||||
n := C.GoString(C.receiver_get_name(rcvr))
|
||||
if engine == nil || engine.receivers[n].objects[rcvr] == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process input arguments.
|
||||
va, err := NewValueFromPtr(args)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer va.Destroy()
|
||||
|
||||
val := engine.receivers[n].objects[rcvr].Call(C.GoString(name), va.Slice())
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.Ptr()
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func TestEngineNew(t *testing.T) {
|
|||
t.Fatalf("New(): %s", err)
|
||||
}
|
||||
|
||||
if e.engine == nil || e.contexts == nil {
|
||||
if e.engine == nil || e.contexts == nil || e.receivers == nil {
|
||||
t.Fatalf("New(): Struct fields are `nil` but no error returned")
|
||||
}
|
||||
}
|
||||
|
@ -60,10 +60,28 @@ func TestEngineNewContext(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEngineDefine(t *testing.T) {
|
||||
ctor := func(args []interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := e.Define("TestDefine", ctor); err != nil {
|
||||
t.Errorf("Engine.Define(): %s", err)
|
||||
}
|
||||
|
||||
if len(e.receivers) != 1 {
|
||||
t.Errorf("Engine.Define(): `Engine.receivers` length is %d, should be 1", len(e.receivers))
|
||||
}
|
||||
|
||||
if err := e.Define("TestDefine", ctor); err == nil {
|
||||
t.Errorf("Engine.Define(): Incorrectly defined duplicate receiver")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEngineDestroy(t *testing.T) {
|
||||
e.Destroy()
|
||||
|
||||
if e.engine != nil || e.contexts != nil {
|
||||
if e.engine != nil || e.contexts != nil || e.receivers != nil {
|
||||
t.Errorf("Engine.Destroy(): Did not set internal fields to `nil`")
|
||||
}
|
||||
|
||||
|
|
|
@ -5,16 +5,15 @@
|
|||
#ifndef __CONTEXT_H__
|
||||
#define __CONTEXT_H__
|
||||
|
||||
#include "_context.h"
|
||||
|
||||
typedef struct _engine_context {
|
||||
void *ctx;
|
||||
} engine_context;
|
||||
|
||||
engine_context *context_new(void *ctx);
|
||||
engine_context *context_new();
|
||||
void context_exec(engine_context *context, char *filename);
|
||||
void *context_eval(engine_context *context, char *script);
|
||||
void context_bind(engine_context *context, char *name, void *value);
|
||||
void context_destroy(engine_context *context);
|
||||
|
||||
#include "_context.h"
|
||||
|
||||
#endif
|
|
@ -5,12 +5,12 @@
|
|||
#ifndef __ENGINE_H__
|
||||
#define __ENGINE_H__
|
||||
|
||||
#include "_engine.h"
|
||||
|
||||
typedef struct _php_engine {
|
||||
} php_engine;
|
||||
|
||||
php_engine *engine_init(void);
|
||||
void engine_shutdown(php_engine *engine);
|
||||
|
||||
#include "_engine.h"
|
||||
|
||||
#endif
|
|
@ -59,4 +59,9 @@ static inline zval *RECEIVER_RETVAL() {
|
|||
h.get_class_entry = std->get_class_entry; \
|
||||
} while (0)
|
||||
|
||||
// Return class name for method receiver.
|
||||
static inline char *receiver_get_name(engine_receiver *rcvr) {
|
||||
return rcvr->obj.ce->name;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -15,10 +15,6 @@
|
|||
ZVAL_NULL(v); \
|
||||
} while (0)
|
||||
|
||||
#define VALUE_FREE(v) do { \
|
||||
zval_dtor(v); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_GET_CURRENT_KEY(h, k, i) zend_hash_get_current_key(h, k, i, 0)
|
||||
#define HASH_SET_CURRENT_KEY(h, v) do { \
|
||||
zval *t; \
|
||||
|
@ -52,4 +48,10 @@
|
|||
return v; \
|
||||
} while (0)
|
||||
|
||||
// Destroy and free engine value.
|
||||
static inline void value_destroy(engine_value *val) {
|
||||
zval_dtor(val->internal);
|
||||
free(val);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -50,4 +50,9 @@
|
|||
h.free_obj = receiver_free; \
|
||||
} while (0)
|
||||
|
||||
// Return class name for method receiver.
|
||||
static inline char *receiver_get_name(engine_receiver *rcvr) {
|
||||
return rcvr->obj.ce->name->val;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -15,11 +15,6 @@
|
|||
ZVAL_NULL(v); \
|
||||
} while (0)
|
||||
|
||||
#define VALUE_FREE(v) do { \
|
||||
zval_dtor(v); \
|
||||
free(v); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_GET_CURRENT_KEY(h, k, i) zend_hash_get_current_key(h, k, i)
|
||||
#define HASH_SET_CURRENT_KEY(h, v) do { \
|
||||
zval t; \
|
||||
|
@ -54,4 +49,11 @@
|
|||
return v; \
|
||||
} while (0)
|
||||
|
||||
// Destroy and free engine value.
|
||||
static inline void value_destroy(engine_value *val) {
|
||||
zval_dtor(val->internal);
|
||||
free(val->internal);
|
||||
free(val);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -5,21 +5,13 @@
|
|||
#ifndef __RECEIVER_H__
|
||||
#define __RECEIVER_H__
|
||||
|
||||
#include "_receiver.h"
|
||||
|
||||
#define RECEIVER_POINTER(ce, name) \
|
||||
(void *) Z_LVAL_P(zend_read_static_property(ce, name, sizeof(name) - 1, 1))
|
||||
|
||||
#define RECEIVER_POINTER_SET(ce, name, ptr) \
|
||||
zend_declare_property_long(ce, name, sizeof(name) - 1, (long int) ptr, \
|
||||
ZEND_ACC_STATIC | ZEND_ACC_PRIVATE)
|
||||
|
||||
typedef struct _engine_receiver {
|
||||
zend_object obj;
|
||||
void *rcvr;
|
||||
} engine_receiver;
|
||||
|
||||
void receiver_define(char *name, void *rcvr);
|
||||
void receiver_define(char *name);
|
||||
void receiver_destroy(char *name);
|
||||
|
||||
#include "_receiver.h"
|
||||
|
||||
#endif
|
|
@ -5,8 +5,6 @@
|
|||
#ifndef __VALUE_H__
|
||||
#define __VALUE_H__
|
||||
|
||||
#include "_value.h"
|
||||
|
||||
typedef struct _engine_value {
|
||||
zval *internal;
|
||||
int kind;
|
||||
|
@ -28,14 +26,8 @@ static inline void value_copy(zval *dst, zval *src) {
|
|||
zval_copy_ctor(dst);
|
||||
}
|
||||
|
||||
static inline void value_destroy(engine_value *val) {
|
||||
VALUE_FREE(val->internal);
|
||||
free(val);
|
||||
}
|
||||
|
||||
engine_value *value_new();
|
||||
int value_kind(engine_value *val);
|
||||
void value_destroy(engine_value *val);
|
||||
|
||||
void value_set_null(engine_value *val);
|
||||
void value_set_long(engine_value *val, long int num);
|
||||
|
@ -63,4 +55,6 @@ engine_value *value_array_next_get(engine_value *arr);
|
|||
engine_value *value_array_index_get(engine_value *arr, unsigned long idx);
|
||||
engine_value *value_array_key_get(engine_value *arr, char *key);
|
||||
|
||||
#include "_value.h"
|
||||
|
||||
#endif
|
|
@ -18,7 +18,7 @@ static zval *RECEIVER_GET(zval *object, zval *member) {
|
|||
engine_receiver *this = RECEIVER_THIS(object);
|
||||
zval *val = RECEIVER_RETVAL();
|
||||
|
||||
engine_value *result = receiverGet(this->rcvr, Z_STRVAL_P(member));
|
||||
engine_value *result = engineReceiverGet(this, Z_STRVAL_P(member));
|
||||
if (result == NULL) {
|
||||
ZVAL_NULL(val);
|
||||
return val;
|
||||
|
@ -33,14 +33,14 @@ static zval *RECEIVER_GET(zval *object, zval *member) {
|
|||
// Set field for method receiver.
|
||||
static void RECEIVER_SET(zval *object, zval *member, zval *value) {
|
||||
engine_receiver *this = RECEIVER_THIS(object);
|
||||
receiverSet(this->rcvr, Z_STRVAL_P(member), (void *) value);
|
||||
engineReceiverSet(this, Z_STRVAL_P(member), (void *) value);
|
||||
}
|
||||
|
||||
// Check if field exists for method receiver.
|
||||
static int RECEIVER_EXISTS(zval *object, zval *member, int check) {
|
||||
engine_receiver *this = RECEIVER_THIS(object);
|
||||
|
||||
if (!receiverExists(this->rcvr, Z_STRVAL_P(member))) {
|
||||
if (!engineReceiverExists(this, Z_STRVAL_P(member))) {
|
||||
// Value does not exist.
|
||||
return 0;
|
||||
} else if (check == 2) {
|
||||
|
@ -49,7 +49,7 @@ static int RECEIVER_EXISTS(zval *object, zval *member, int check) {
|
|||
}
|
||||
|
||||
int result = 0;
|
||||
engine_value *val = receiverGet(this->rcvr, Z_STRVAL_P(member));
|
||||
engine_value *val = engineReceiverGet(this, Z_STRVAL_P(member));
|
||||
|
||||
if (check == 1) {
|
||||
// Value exists and is "truthy".
|
||||
|
@ -78,7 +78,7 @@ static int RECEIVER_METHOD_CALL(method) {
|
|||
if (zend_copy_parameters_array(ZEND_NUM_ARGS(), &args) == FAILURE) {
|
||||
RETVAL_NULL();
|
||||
} else {
|
||||
engine_value *result = receiverCall(this->rcvr, name, (void *) &args);
|
||||
engine_value *result = engineReceiverCall(this, name, (void *) &args);
|
||||
if (result == NULL) {
|
||||
RETVAL_NULL();
|
||||
} else {
|
||||
|
@ -102,10 +102,8 @@ static void receiver_new(INTERNAL_FUNCTION_PARAMETERS) {
|
|||
zend_throw_exception(NULL, "Could not parse parameters for method receiver", 0);
|
||||
} else {
|
||||
// Create receiver instance. Throws an exception if creation fails.
|
||||
void *ctor = RECEIVER_POINTER(this->obj.ce, "__rcvr__");
|
||||
this->rcvr = receiverNew(ctor, (void *) &args);
|
||||
|
||||
if (this->rcvr == NULL) {
|
||||
int result = engineReceiverNew(this, (void *) &args);
|
||||
if (result != 0) {
|
||||
zend_throw_exception(NULL, "Failed to instantiate method receiver", 0);
|
||||
}
|
||||
}
|
||||
|
@ -191,8 +189,8 @@ static RECEIVER_INIT(zend_class_entry *class_type) {
|
|||
RECEIVER_OBJECT_CREATE(this, class_type);
|
||||
}
|
||||
|
||||
// Define class with unique name, using `rcvr` as the method receiver prototype.
|
||||
void receiver_define(char *name, void *rcvr) {
|
||||
// Define class with unique name.
|
||||
void receiver_define(char *name) {
|
||||
zend_class_entry tmp;
|
||||
INIT_CLASS_ENTRY_EX(tmp, name, strlen(name), NULL);
|
||||
|
||||
|
@ -203,12 +201,9 @@ void receiver_define(char *name, void *rcvr) {
|
|||
|
||||
// Set standard handlers for receiver.
|
||||
RECEIVER_HANDLERS_SET(receiver_handlers);
|
||||
|
||||
// Method receiver is stored as internal class property.
|
||||
RECEIVER_POINTER_SET(this, "__rcvr__", rcvr);
|
||||
}
|
||||
|
||||
void receiver_destroy(char *name) {
|
||||
name = php_strtolower(name, strlen(name));
|
||||
RECEIVER_DESTROY(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,63 +13,31 @@ package engine
|
|||
import "C"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ReceiverObject represents an object instance of a pre-defined method receiver.
|
||||
type ReceiverObject struct {
|
||||
instance interface{}
|
||||
values map[string]reflect.Value
|
||||
methods map[string]reflect.Value
|
||||
}
|
||||
|
||||
// Receiver represents a method receiver.
|
||||
type Receiver struct {
|
||||
name string
|
||||
create func(args []interface{}) interface{}
|
||||
objects []*ReceiverObject
|
||||
objects map[*C.struct__engine_receiver]*ReceiverObject
|
||||
}
|
||||
|
||||
// Destroy removes references to the generated PHP class for this receiver and
|
||||
// frees any memory used by object instances.
|
||||
func (r *Receiver) Destroy() {
|
||||
if r.create == nil {
|
||||
return
|
||||
}
|
||||
|
||||
n := C.CString(r.name)
|
||||
defer C.free(unsafe.Pointer(n))
|
||||
|
||||
C.receiver_destroy(n)
|
||||
|
||||
r.create = nil
|
||||
r.objects = nil
|
||||
}
|
||||
|
||||
//export receiverNew
|
||||
func receiverNew(rcvr unsafe.Pointer, args unsafe.Pointer) unsafe.Pointer {
|
||||
r := (*Receiver)(rcvr)
|
||||
|
||||
va, err := NewValueFromPtr(args)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer va.Destroy()
|
||||
|
||||
// NewObject instantiates a new method receiver object, using the Receiver's
|
||||
// create function and passing in a slice of values as a parameter.
|
||||
func (r *Receiver) NewObject(args []interface{}) (*ReceiverObject, error) {
|
||||
obj := &ReceiverObject{
|
||||
instance: r.create(va.Slice()),
|
||||
instance: r.create(args),
|
||||
values: make(map[string]reflect.Value),
|
||||
methods: make(map[string]reflect.Value),
|
||||
}
|
||||
|
||||
if obj.instance == nil {
|
||||
return nil
|
||||
return nil, fmt.Errorf("Failed to instantiate method receiver")
|
||||
}
|
||||
|
||||
r.objects = append(r.objects, obj)
|
||||
|
||||
v := reflect.ValueOf(obj.instance)
|
||||
vi := reflect.Indirect(v)
|
||||
|
||||
|
@ -93,93 +61,95 @@ func receiverNew(rcvr unsafe.Pointer, args unsafe.Pointer) unsafe.Pointer {
|
|||
}
|
||||
}
|
||||
|
||||
return unsafe.Pointer(obj)
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
//export receiverGet
|
||||
func receiverGet(obj unsafe.Pointer, name *C.char) unsafe.Pointer {
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
if _, exists := o.values[n]; !exists || !o.values[n].CanInterface() {
|
||||
return nil
|
||||
// Destroy removes references to the generated PHP class for this receiver and
|
||||
// frees any memory used by object instances.
|
||||
func (r *Receiver) Destroy() {
|
||||
if r.create == nil {
|
||||
return
|
||||
}
|
||||
|
||||
result, err := NewValue(o.values[n].Interface())
|
||||
n := C.CString(r.name)
|
||||
defer C.free(unsafe.Pointer(n))
|
||||
|
||||
C.receiver_destroy(n)
|
||||
r.create = nil
|
||||
r.objects = nil
|
||||
}
|
||||
|
||||
// ReceiverObject represents an object instance of a pre-defined method receiver.
|
||||
type ReceiverObject struct {
|
||||
instance interface{}
|
||||
values map[string]reflect.Value
|
||||
methods map[string]reflect.Value
|
||||
}
|
||||
|
||||
// Get returns a named internal property of the receiver object instance, or an
|
||||
// error if the property does not exist or is not addressable.
|
||||
func (o *ReceiverObject) Get(name string) (*Value, error) {
|
||||
if _, exists := o.values[name]; !exists || !o.values[name].CanInterface() {
|
||||
return nil, fmt.Errorf("Value '%s' does not exist or is not addressable", name)
|
||||
}
|
||||
|
||||
val, err := NewValue(o.values[name].Interface())
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result.Ptr()
|
||||
return val, nil
|
||||
}
|
||||
|
||||
//export receiverSet
|
||||
func receiverSet(obj unsafe.Pointer, name *C.char, val unsafe.Pointer) {
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
// Set assigns value to named internal property. If the named property does not
|
||||
// exist or cannot be set, the method does nothing.
|
||||
func (o *ReceiverObject) Set(name string, val interface{}) {
|
||||
// Do not attempt to set non-existing or unset-able field.
|
||||
if _, exists := o.values[n]; !exists || !o.values[n].CanSet() {
|
||||
if _, exists := o.values[name]; !exists || !o.values[name].CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
v, err := NewValueFromPtr(val)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
o.values[n].Set(reflect.ValueOf(v.Interface()))
|
||||
o.values[name].Set(reflect.ValueOf(val))
|
||||
}
|
||||
|
||||
//export receiverExists
|
||||
func receiverExists(obj unsafe.Pointer, name *C.char) C.int {
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
if _, exists := o.values[n]; !exists {
|
||||
return 0
|
||||
// Exists checks if named internal property exists and returns true, or false if
|
||||
// property does not exist.
|
||||
func (o *ReceiverObject) Exists(name string) bool {
|
||||
if _, exists := o.values[name]; !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
return 1
|
||||
return true
|
||||
}
|
||||
|
||||
//export receiverCall
|
||||
func receiverCall(obj unsafe.Pointer, name *C.char, args unsafe.Pointer) unsafe.Pointer {
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
if _, exists := o.methods[n]; !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process input arguments.
|
||||
va, err := NewValueFromPtr(args)
|
||||
if err != nil {
|
||||
// Call executes a method receiver's named internal method, passing a slice of
|
||||
// values as arguments to the method. If the method fails to execute or returns
|
||||
// no value, nil is returned, otherwise a Value instance is returned.
|
||||
func (o *ReceiverObject) Call(name string, args []interface{}) *Value {
|
||||
if _, exists := o.methods[name]; !exists {
|
||||
return nil
|
||||
}
|
||||
|
||||
in := make([]reflect.Value, 0)
|
||||
for _, v := range va.Slice() {
|
||||
for _, v := range args {
|
||||
in = append(in, reflect.ValueOf(v))
|
||||
}
|
||||
|
||||
va.Destroy()
|
||||
|
||||
// Call receiver method.
|
||||
var result interface{}
|
||||
ret := o.methods[n].Call(in)
|
||||
val := o.methods[name].Call(in)
|
||||
|
||||
// Process results, returning a single value if result slice contains a single
|
||||
// element, otherwise returns a slice of values.
|
||||
if len(ret) > 1 {
|
||||
t := make([]interface{}, len(ret))
|
||||
for i, v := range ret {
|
||||
if len(val) > 1 {
|
||||
t := make([]interface{}, len(val))
|
||||
for i, v := range val {
|
||||
t[i] = v.Interface()
|
||||
}
|
||||
|
||||
result = t
|
||||
} else if len(ret) == 1 {
|
||||
result = ret[0].Interface()
|
||||
} else if len(val) == 1 {
|
||||
result = val[0].Interface()
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
@ -189,5 +159,5 @@ func receiverCall(obj unsafe.Pointer, name *C.char, args unsafe.Pointer) unsafe.
|
|||
return nil
|
||||
}
|
||||
|
||||
return v.Ptr()
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -125,12 +125,12 @@ func TestReceiverDefine(t *testing.T) {
|
|||
c, _ := e.NewContext()
|
||||
c.Output = &w
|
||||
|
||||
if err := c.Define("TestReceiver", newTestReceiver); err != nil {
|
||||
if err := e.Define("TestReceiver", newTestReceiver); err != nil {
|
||||
t.Fatalf("Engine.Define(): Failed to define method receiver: %s", err)
|
||||
}
|
||||
|
||||
// Attempting to define a receiver twice should fail.
|
||||
if err := c.Define("TestReceiver", newTestReceiver); err == nil {
|
||||
if err := e.Define("TestReceiver", newTestReceiver); err == nil {
|
||||
t.Fatalf("Engine.Define(): Defining duplicate receiver should fail")
|
||||
}
|
||||
|
||||
|
@ -156,11 +156,7 @@ func TestReceiverDestroy(t *testing.T) {
|
|||
c, _ := e.NewContext()
|
||||
defer c.Destroy()
|
||||
|
||||
if err := c.Define("TestReceiver", newTestReceiver); err != nil {
|
||||
t.Fatalf("Engine.Define(): Failed to define method receiver: %s", err)
|
||||
}
|
||||
|
||||
r := c.receivers["TestReceiver"]
|
||||
r := e.receivers["TestReceiver"]
|
||||
if r == nil {
|
||||
t.Fatalf("Receiver.Destroy(): Could not find defined receiver")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue