mirror of https://github.com/deuill/go-php.git
Merge pull request #18 from deuill/feature/dependency-cleanup
Clean up method receiver constructors
This commit is contained in:
commit
3eaec657a3
|
@ -9,6 +9,7 @@ package engine
|
|||
//
|
||||
// #include <stdlib.h>
|
||||
// #include <main/php.h>
|
||||
// #include "receiver.h"
|
||||
// #include "context.h"
|
||||
import "C"
|
||||
|
||||
|
@ -30,26 +31,9 @@ type Context struct {
|
|||
// Header represents the HTTP headers set by current PHP context.
|
||||
Header http.Header
|
||||
|
||||
context *C.struct__engine_context
|
||||
values []*Value
|
||||
}
|
||||
|
||||
// NewContext creates a new execution context for the active engine and returns
|
||||
// an error if the execution context failed to initialize at any point.
|
||||
func NewContext() (*Context, error) {
|
||||
ctx := &Context{
|
||||
Header: make(http.Header),
|
||||
values: make([]*Value, 0),
|
||||
}
|
||||
|
||||
ptr, err := C.context_new(unsafe.Pointer(ctx))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to initialize context for PHP engine")
|
||||
}
|
||||
|
||||
ctx.context = ptr
|
||||
|
||||
return ctx, nil
|
||||
context *C.struct__engine_context
|
||||
values []*Value
|
||||
receivers map[string]*Receiver
|
||||
}
|
||||
|
||||
// Bind allows for binding Go values into the current execution context under
|
||||
|
@ -71,6 +55,34 @@ 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.
|
||||
|
@ -117,6 +129,12 @@ func (c *Context) Destroy() {
|
|||
return
|
||||
}
|
||||
|
||||
for _, r := range c.receivers {
|
||||
r.Destroy()
|
||||
}
|
||||
|
||||
c.receivers = nil
|
||||
|
||||
for _, v := range c.values {
|
||||
v.Destroy()
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func TestContextStart(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestContextNew(t *testing.T) {
|
||||
c, err := NewContext()
|
||||
c, err := e.NewContext()
|
||||
if err != nil {
|
||||
t.Fatalf("NewContext(): %s", err)
|
||||
}
|
||||
|
@ -31,6 +31,28 @@ 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
|
||||
|
@ -51,7 +73,7 @@ var execTests = []struct {
|
|||
func TestContextExec(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
c.Output = &w
|
||||
|
||||
for _, tt := range execTests {
|
||||
|
@ -99,7 +121,7 @@ var evalTests = []struct {
|
|||
func TestContextEval(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
c.Output = &w
|
||||
|
||||
for _, tt := range evalTests {
|
||||
|
@ -151,7 +173,7 @@ var headerTests = []struct {
|
|||
}
|
||||
|
||||
func TestContextHeader(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range headerTests {
|
||||
if _, err := c.Eval(tt.script); err != nil {
|
||||
|
@ -188,7 +210,7 @@ var logTests = []struct {
|
|||
func TestContextLog(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
c.Log = &w
|
||||
|
||||
for _, tt := range logTests {
|
||||
|
@ -258,7 +280,7 @@ var bindTests = []struct {
|
|||
func TestContextBind(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
c.Output = &w
|
||||
|
||||
for i, tt := range bindTests {
|
||||
|
@ -284,7 +306,7 @@ func TestContextBind(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestContextDestroy(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
c.Destroy()
|
||||
|
||||
if c.context != nil || c.values != nil {
|
||||
|
|
|
@ -18,15 +18,15 @@ import "C"
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Engine represents the core PHP engine bindings.
|
||||
type Engine struct {
|
||||
engine *C.struct__php_engine
|
||||
contexts []*Context
|
||||
receivers map[string]*Receiver
|
||||
engine *C.struct__php_engine
|
||||
contexts []*Context
|
||||
}
|
||||
|
||||
// New initializes a PHP engine instance on which contexts can be executed. It
|
||||
|
@ -38,43 +38,32 @@ func New() (*Engine, error) {
|
|||
}
|
||||
|
||||
e := &Engine{
|
||||
engine: ptr,
|
||||
contexts: make([]*Context, 0),
|
||||
receivers: make(map[string]*Receiver),
|
||||
engine: ptr,
|
||||
contexts: make([]*Context, 0),
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// NewContext creates a new execution context on which scripts can be executed
|
||||
// and variables can be binded. It corresponds to PHP's RINIT (request init)
|
||||
// phase.
|
||||
// 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) {
|
||||
c, err := NewContext()
|
||||
ctx := &Context{
|
||||
Header: make(http.Header),
|
||||
values: make([]*Value, 0),
|
||||
receivers: make(map[string]*Receiver),
|
||||
}
|
||||
|
||||
ptr, err := C.context_new(unsafe.Pointer(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("Failed to initialize context for PHP engine")
|
||||
}
|
||||
|
||||
e.contexts = append(e.contexts, c)
|
||||
ctx.context = ptr
|
||||
e.contexts = append(e.contexts, ctx)
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Define registers a PHP class under the name passed, using fn as the class
|
||||
// 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, err := NewReceiver(name, fn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.receivers[name] = rcvr
|
||||
|
||||
return nil
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
// Destroy shuts down and frees any resources related to the PHP engine bindings.
|
||||
|
@ -83,12 +72,6 @@ func (e *Engine) Destroy() {
|
|||
return
|
||||
}
|
||||
|
||||
for _, r := range e.receivers {
|
||||
r.Destroy()
|
||||
}
|
||||
|
||||
e.receivers = nil
|
||||
|
||||
for _, c := range e.contexts {
|
||||
c.Destroy()
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func TestEngineNew(t *testing.T) {
|
|||
t.Fatalf("New(): %s", err)
|
||||
}
|
||||
|
||||
if e.engine == nil || e.contexts == nil || e.receivers == nil {
|
||||
if e.engine == nil || e.contexts == nil {
|
||||
t.Fatalf("New(): Struct fields are `nil` but no error returned")
|
||||
}
|
||||
}
|
||||
|
@ -60,31 +60,10 @@ func TestEngineNewContext(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestEngineDefine(t *testing.T) {
|
||||
ctor := func(args []interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := e.Define("TestDefine", ctor)
|
||||
if err != nil {
|
||||
t.Errorf("Define(): %s", err)
|
||||
}
|
||||
|
||||
if len(e.receivers) != 1 {
|
||||
t.Errorf("Define(): `Engine.receivers` length is %d, should be 1", len(e.receivers))
|
||||
}
|
||||
|
||||
err = e.Define("TestDefine", ctor)
|
||||
if err == nil {
|
||||
t.Errorf("Define(): Incorrectly defined duplicate receiver")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestEngineDestroy(t *testing.T) {
|
||||
e.Destroy()
|
||||
|
||||
if e.engine != nil || e.contexts != nil || e.receivers != nil {
|
||||
if e.engine != nil || e.contexts != nil {
|
||||
t.Errorf("Engine.Destroy(): Did not set internal fields to `nil`")
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ import (
|
|||
"unsafe"
|
||||
)
|
||||
|
||||
type object struct {
|
||||
// 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
|
||||
|
@ -27,30 +28,7 @@ type object struct {
|
|||
type Receiver struct {
|
||||
name string
|
||||
create func(args []interface{}) interface{}
|
||||
objects []*object
|
||||
}
|
||||
|
||||
// NewReceiver 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 NewReceiver(name string, fn func(args []interface{}) interface{}) (*Receiver, error) {
|
||||
rcvr := &Receiver{
|
||||
name: name,
|
||||
create: fn,
|
||||
objects: make([]*object, 0),
|
||||
}
|
||||
|
||||
n := C.CString(name)
|
||||
defer C.free(unsafe.Pointer(n))
|
||||
|
||||
C.receiver_define(n, unsafe.Pointer(rcvr))
|
||||
|
||||
return rcvr, nil
|
||||
objects []*ReceiverObject
|
||||
}
|
||||
|
||||
// Destroy removes references to the generated PHP class for this receiver and
|
||||
|
@ -80,7 +58,7 @@ func receiverNew(rcvr unsafe.Pointer, args unsafe.Pointer) unsafe.Pointer {
|
|||
|
||||
defer va.Destroy()
|
||||
|
||||
obj := &object{
|
||||
obj := &ReceiverObject{
|
||||
instance: r.create(va.Slice()),
|
||||
values: make(map[string]reflect.Value),
|
||||
methods: make(map[string]reflect.Value),
|
||||
|
@ -120,7 +98,7 @@ func receiverNew(rcvr unsafe.Pointer, args unsafe.Pointer) unsafe.Pointer {
|
|||
|
||||
//export receiverGet
|
||||
func receiverGet(obj unsafe.Pointer, name *C.char) unsafe.Pointer {
|
||||
o := (*object)(obj)
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
if _, exists := o.values[n]; !exists || !o.values[n].CanInterface() {
|
||||
|
@ -137,7 +115,7 @@ func receiverGet(obj unsafe.Pointer, name *C.char) unsafe.Pointer {
|
|||
|
||||
//export receiverSet
|
||||
func receiverSet(obj unsafe.Pointer, name *C.char, val unsafe.Pointer) {
|
||||
o := (*object)(obj)
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
// Do not attempt to set non-existing or unset-able field.
|
||||
|
@ -155,7 +133,7 @@ func receiverSet(obj unsafe.Pointer, name *C.char, val unsafe.Pointer) {
|
|||
|
||||
//export receiverExists
|
||||
func receiverExists(obj unsafe.Pointer, name *C.char) C.int {
|
||||
o := (*object)(obj)
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
if _, exists := o.values[n]; !exists {
|
||||
|
@ -167,7 +145,7 @@ func receiverExists(obj unsafe.Pointer, name *C.char) C.int {
|
|||
|
||||
//export receiverCall
|
||||
func receiverCall(obj unsafe.Pointer, name *C.char, args unsafe.Pointer) unsafe.Pointer {
|
||||
o := (*object)(obj)
|
||||
o := (*ReceiverObject)(obj)
|
||||
n := C.GoString(name)
|
||||
|
||||
if _, exists := o.methods[n]; !exists {
|
||||
|
|
|
@ -49,7 +49,7 @@ func newTestReceiver(args []interface{}) interface{} {
|
|||
return &testReceiver{Var: value, hidden: 42}
|
||||
}
|
||||
|
||||
var newReceiverTests = []struct {
|
||||
var receiverDefineTests = []struct {
|
||||
script string
|
||||
expected string
|
||||
}{
|
||||
|
@ -65,7 +65,6 @@ var newReceiverTests = []struct {
|
|||
}`,
|
||||
"Failed to instantiate method receiver",
|
||||
},
|
||||
|
||||
{
|
||||
"$t = new TestReceiver; echo $t->Var;",
|
||||
"Foo",
|
||||
|
@ -78,7 +77,6 @@ var newReceiverTests = []struct {
|
|||
"$t = new TestReceiver('wow'); echo $t->Var;",
|
||||
"wow",
|
||||
},
|
||||
|
||||
{
|
||||
"$t = new TestReceiver; $t->Var = 'Bar'; echo $t->Var;",
|
||||
"Bar",
|
||||
|
@ -87,7 +85,6 @@ var newReceiverTests = []struct {
|
|||
"$t = new TestReceiver; $t->hello = 'wow'; echo $t->hello;",
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"$t = new TestReceiver; echo $t->Ignore();",
|
||||
"",
|
||||
|
@ -104,7 +101,6 @@ var newReceiverTests = []struct {
|
|||
"$t = new TestReceiver; echo $t->invalid();",
|
||||
"",
|
||||
},
|
||||
|
||||
{
|
||||
"$t = new TestReceiver; echo ($t->Var) ? 1 : 0;",
|
||||
"1",
|
||||
|
@ -123,18 +119,22 @@ var newReceiverTests = []struct {
|
|||
},
|
||||
}
|
||||
|
||||
func TestNewReceiver(t *testing.T) {
|
||||
func TestReceiverDefine(t *testing.T) {
|
||||
var w bytes.Buffer
|
||||
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
c.Output = &w
|
||||
|
||||
r, err := NewReceiver("TestReceiver", newTestReceiver)
|
||||
if err != nil {
|
||||
t.Fatalf("NewReceiver(): Failed to define method receiver: %s", err)
|
||||
if err := c.Define("TestReceiver", newTestReceiver); err != nil {
|
||||
t.Fatalf("Engine.Define(): Failed to define method receiver: %s", err)
|
||||
}
|
||||
|
||||
for _, tt := range newReceiverTests {
|
||||
// Attempting to define a receiver twice should fail.
|
||||
if err := c.Define("TestReceiver", newTestReceiver); err == nil {
|
||||
t.Fatalf("Engine.Define(): Defining duplicate receiver should fail")
|
||||
}
|
||||
|
||||
for _, tt := range receiverDefineTests {
|
||||
_, err := c.Eval(tt.script)
|
||||
if err != nil {
|
||||
t.Errorf("Context.Eval('%s'): %s", tt.script, err)
|
||||
|
@ -149,17 +149,20 @@ func TestNewReceiver(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
r.Destroy()
|
||||
c.Destroy()
|
||||
}
|
||||
|
||||
func TestReceiverDestroy(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
defer c.Destroy()
|
||||
|
||||
r, err := NewReceiver("TestReceiver", newTestReceiver)
|
||||
if err != nil {
|
||||
t.Fatalf("NewReceiver(): Failed to define method receiver: %s", err)
|
||||
if err := c.Define("TestReceiver", newTestReceiver); err != nil {
|
||||
t.Fatalf("Engine.Define(): Failed to define method receiver: %s", err)
|
||||
}
|
||||
|
||||
r := c.receivers["TestReceiver"]
|
||||
if r == nil {
|
||||
t.Fatalf("Receiver.Destroy(): Could not find defined receiver")
|
||||
}
|
||||
|
||||
r.Destroy()
|
||||
|
|
|
@ -66,7 +66,7 @@ var valueNewTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueNew(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueNewTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -105,7 +105,7 @@ var valueNewInvalidTests = []interface{}{
|
|||
}
|
||||
|
||||
func TestValueNewInvalid(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, value := range valueNewInvalidTests {
|
||||
val, err := NewValue(value)
|
||||
|
@ -156,7 +156,7 @@ var valueKindTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueKind(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueKindTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -215,7 +215,7 @@ var valueIntTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueInt(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueIntTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -274,7 +274,7 @@ var valueFloatTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueFloat(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueFloatTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -333,7 +333,7 @@ var valueBoolTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueBool(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueBoolTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -392,7 +392,7 @@ var valueStringTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueString(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueStringTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -451,7 +451,7 @@ var valueSliceTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueSlice(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueSliceTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -510,7 +510,7 @@ var valueMapTests = []struct {
|
|||
}
|
||||
|
||||
func TestValueMap(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
|
||||
for _, tt := range valueMapTests {
|
||||
val, err := NewValue(tt.value)
|
||||
|
@ -532,7 +532,7 @@ func TestValueMap(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValuePtr(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
defer c.Destroy()
|
||||
|
||||
val, err := NewValue(42)
|
||||
|
@ -546,7 +546,7 @@ func TestValuePtr(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestValueDestroy(t *testing.T) {
|
||||
c, _ := NewContext()
|
||||
c, _ := e.NewContext()
|
||||
defer c.Destroy()
|
||||
|
||||
val, err := NewValue(42)
|
||||
|
|
Loading…
Reference in New Issue