1
0
mirror of https://github.com/deuill/go-php.git synced 2024-09-21 00:40:45 +00:00

First version of variable bindings to context.

This commit contains an initial version of variable bindings
to a context, along with tests. Currently supported types are
integers, floating point numbers, and strings.
This commit is contained in:
Alex Palaistras 2015-09-20 01:16:43 +01:00
parent ee779b3698
commit 6fa93f5160
9 changed files with 158 additions and 3 deletions

View File

@ -61,6 +61,17 @@ void context_exec(engine_context *context, char *filename) {
return_multi(NULL, 0);
}
void context_bind(engine_context *context, char *name, void *zvalptr) {
zval *value = (zval *) zvalptr;
#ifdef ZTS
void ***tsrm_ls = context->engine->tsrm_ls;
#endif
ZEND_SET_SYMBOL(EG(active_symbol_table), name, value);
return_multi(NULL, 0);
}
int context_write(engine_context *context, const char *message, unsigned int length) {
int written = contextWrite(context->parent, (void *) message, length);
if (written != length) {

View File

@ -4,6 +4,7 @@ package php
// #include <stdlib.h>
// #include "engine.h"
// #include "context.h"
// #include "value.h"
import "C"
import (
@ -15,6 +16,42 @@ import (
type Context struct {
context *C.struct__engine_context
writer io.Writer
zvals map[string]unsafe.Pointer
}
func (c *Context) Bind(name string, value interface{}) error {
var zval unsafe.Pointer
var err error
switch v := value.(type) {
case int:
zval, err = C.value_long(C.long(v))
case float64:
zval, err = C.value_double(C.double(v))
case string:
str := C.CString(v)
defer C.free(unsafe.Pointer(str))
zval, err = C.value_string(str)
default:
return fmt.Errorf("Cannot bind unknown type '%T'", v)
}
if err != nil {
return fmt.Errorf("Binding value '%v' to context failed", value)
}
n := C.CString(name)
defer C.free(unsafe.Pointer(n))
if _, err = C.context_bind(c.context, n, zval); err != nil {
C.value_destroy(zval)
return fmt.Errorf("Binding value '%v' to context failed", value)
}
c.zvals[name] = zval
return nil
}
func (c *Context) Exec(filename string) error {
@ -30,6 +67,10 @@ func (c *Context) Exec(filename string) error {
}
func (c *Context) Destroy() {
for _, zval := range c.zvals {
C.value_destroy(zval)
}
C.context_destroy(c.context)
c = nil
}

View File

@ -8,6 +8,7 @@ typedef struct _engine_context {
engine_context *context_new(php_engine *engine, void *parent);
void context_exec(engine_context *context, char *filename);
void context_bind(engine_context *context, char *name, void *vptr);
int context_write(engine_context *context, const char *message, unsigned int length);
void context_destroy(engine_context *context);

View File

@ -3,6 +3,7 @@ package php
import (
"os"
"path"
"strconv"
"testing"
)
@ -27,12 +28,48 @@ func TestContextExec(t *testing.T) {
for _, tt := range execTests {
file := path.Join(testDir, tt.file)
if err := ctx.Exec(file); err != nil {
t.Errorf("ContextExec(%s): %s", tt.file, err)
t.Errorf("Context.Exec(%s): %s", tt.file, err)
}
actual := w.String()
w.Reset()
if actual != tt.expected {
t.Errorf("ContextExec(%s): expected '%s', actual '%s'", tt.file, tt.expected, actual)
t.Errorf("Context.Exec(%s): expected '%s', actual '%s'", tt.file, tt.expected, actual)
}
}
}
var bindTests = []struct {
value interface{} // Value to bind
expected string // Serialized form of value
}{
{42, "i:42;"}, // Integer
{3.14159, "d:3.1415899999999999;"}, // Floating point
{"Such bind", `s:9:"Such bind";`}, // String
}
func TestContextBind(t *testing.T) {
var w MockWriter
e, _ := New()
ctx, _ := e.NewContext(&w)
defer e.Destroy()
defer ctx.Destroy()
for i, tt := range bindTests {
if err := ctx.Bind(strconv.FormatInt(int64(i), 10), tt.value); err != nil {
t.Errorf("Context.Bind(%v): %s", tt.value, err)
}
ctx.Exec(path.Join(testDir, "bind.php"))
actual := w.String()
w.Reset()
if actual != tt.expected {
t.Errorf("Context.Bind(%v): expected '%s', actual '%s'", tt.value, tt.expected, actual)
}
}
}

View File

@ -20,7 +20,7 @@ type Engine struct {
}
func (e *Engine) NewContext(w io.Writer) (*Context, error) {
ctx := &Context{writer: w}
ctx := &Context{writer: w, zvals: make(map[string]unsafe.Pointer)}
ptr, err := C.context_new(e.engine, unsafe.Pointer(ctx))
if err != nil {

View File

@ -26,6 +26,12 @@ func (m *MockWriter) String() string {
return string(m.buffer)
}
func (m *MockWriter) Reset() {
if m.buffer != nil {
m.buffer = m.buffer[:0]
}
}
func TestNewEngineContext(t *testing.T) {
e, err := New()
if err != nil {

9
test/bind.php Normal file
View File

@ -0,0 +1,9 @@
<?php
if (isset($i)) {
$i += 1;
} else {
$i = 0;
}
echo serialize($$i);

41
value.c Normal file
View File

@ -0,0 +1,41 @@
// Standard library.
#include <stdio.h>
#include <errno.h>
// PHP includes.
#include <main/php.h>
// Local includes.
#include "engine.h"
#include "value.h"
void *value_long(long int value) {
zval *v;
MAKE_STD_ZVAL(v);
ZVAL_LONG(v, value);
return_multi((void *) v, 0);
}
void *value_double(double value) {
zval *v;
MAKE_STD_ZVAL(v);
ZVAL_DOUBLE(v, value);
return_multi((void *) v, 0);
}
void *value_string(char *value) {
zval *v;
MAKE_STD_ZVAL(v);
ZVAL_STRING(v, value, 1);
return_multi((void *) v, 0);
}
void value_destroy(void *zvalptr) {
zval_dtor((zval *) zvalptr);
}

9
value.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef VALUE_H
#define VALUE_H
void *value_long(long int value);
void *value_double(double value);
void *value_string(char *value);
void value_destroy(void *zvalptr);
#endif