-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjhp.go
150 lines (130 loc) · 2.96 KB
/
jhp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package jhp
import (
"database/sql"
"encoding/json"
"fmt"
"github.com/dop251/goja"
"io"
"log"
"net/http"
"strings"
)
type RequestReader struct {
DB *sql.DB
Params map[string]interface{}
}
type ResponseWriter interface {
io.Writer
}
func ToParamMap(r *http.Request) map[string]interface{} {
params := make(map[string]interface{})
for s, v := range r.URL.Query() {
if len(v) == 1 {
params[s] = v[0]
} else {
params[s] = v
}
}
for s, v := range r.Form {
if len(v) == 1 {
params[s] = v[0]
} else {
params[s] = v
}
}
return params
}
// Register will setup the vm for a request context
func Register(vm *goja.Runtime, r RequestReader, w ResponseWriter) error {
err := vm.Set("params", vm.ToValue(r.Params))
if err != nil {
return err
}
// register echo function
err = vm.Set("echo", func(fn goja.FunctionCall) goja.Value {
val := fn.Argument(0)
fmt.Fprint(w, val)
return vm.ToValue(true)
})
if err != nil {
return err
}
// register a way to query the database
err = vm.Set("sql", func(fn goja.FunctionCall) goja.Value {
query := fn.Argument(0).String()
queryResult, err := r.DB.Query(query)
if err != nil {
// how to throw??
log.Println("Error with sql: ", query, ":", err)
return goja.Null()
}
rows := make([]map[string]interface{}, 0)
cols, err := queryResult.Columns()
if err != nil {
log.Println("error with SQL fetching columns: ", err)
return goja.Null()
}
for queryResult.Next() {
columns := make([]interface{}, len(cols))
columnPointers := make([]interface{}, len(cols))
for i, _ := range columns {
columnPointers[i] = &columns[i]
}
err = queryResult.Scan(columnPointers...)
if err != nil {
// how to throw??
log.Println("Error with sql: ", query, ":", err)
return goja.Null()
}
m := make(map[string]interface{})
for i, colName := range cols {
val := columnPointers[i].(*interface{})
m[colName] = *val
}
rows = append(rows, m)
}
return vm.ToValue(rows)
})
if err != nil {
return err
}
// register parameter fetching code
return nil
}
func Render(vm *goja.Runtime, r RequestReader, w ResponseWriter, content string) error {
err := Register(vm, r, w)
if err != nil {
return err
}
_, err = vm.RunString(toEcho(content))
return err
}
// turn a jhp page into a runnable expression
func toEcho(in string) string {
if len(in) == 0 {
return ""
}
// start with echo
out := "echo("
// search for the opening <?jhp
idx := strings.Index(in, "<?jhp")
if idx == -1 {
return out + toJSString(in) + ");"
}
// TODO escape the bit in between
out += toJSString(in[:idx]) + ");\n"
idx += 5 // skip <?jhp
in = in[idx:]
// search for the close ?>
idx = strings.IndexAny(in, "?>")
if idx == -1 {
return in
}
out += in[:idx]
// the remaining can be continued with the same old logic
return out + toEcho(in[idx + 2:]) // +2 to exclude ?> on the end here
}
func toJSString(in string) string {
o, _ := json.Marshal(in)
return string(o)
}