extension.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. package phpgo
  2. /*
  3. #include "extension.h"
  4. #include "../zend/compat.h"
  5. #include "../zend/szend.h"
  6. #include <php.h>
  7. #include <zend_exceptions.h>
  8. #include <zend_interfaces.h>
  9. #include <zend_ini.h>
  10. #include <zend_constants.h>
  11. #include <SAPI.h>
  12. */
  13. import "C"
  14. import "unsafe"
  15. import "reflect"
  16. import "errors"
  17. import "fmt"
  18. import "log"
  19. import "os"
  20. import "strings"
  21. import "git.nspix.com/golang/php/zend"
  22. // 一个程序只能创建一个扩展
  23. // 所以使用全局变量也没有问题。
  24. var (
  25. ExtName string = ""
  26. ExtVer string = "1.0"
  27. )
  28. //export InitExtension
  29. func InitExtension(name string, version string) int {
  30. ExtName = name
  31. if len(version) > 0 {
  32. ExtVer = version
  33. }
  34. return 0
  35. }
  36. var gext = NewExtension()
  37. type FuncEntry struct {
  38. class string
  39. method string
  40. fn interface{}
  41. isctor bool
  42. isdtor bool
  43. }
  44. func NewFuncEntry(class string, method string, fn interface{}) *FuncEntry {
  45. return &FuncEntry{class, method, fn, false, false}
  46. }
  47. func (this *FuncEntry) Name() string {
  48. return this.class + "_" + this.method
  49. }
  50. func (this *FuncEntry) IsGlobal() bool {
  51. return this.class == "global"
  52. }
  53. func (this *FuncEntry) IsCtor() bool {
  54. return !this.IsGlobal() && this.isctor
  55. }
  56. func (this *FuncEntry) IsDtor() bool {
  57. return !this.IsGlobal() && this.isdtor
  58. }
  59. func (this *FuncEntry) IsMethod() bool {
  60. return !this.IsGlobal() && !this.isctor && !this.isdtor
  61. }
  62. // 支持的函数类型为,
  63. // 至少要是个函数或者方法
  64. // 最多只能返回一个值
  65. // 参数个数小于等于10
  66. // 返回值类型,必须是以下4类,string, intx, floatx, bool
  67. func (this *FuncEntry) IsSupported() bool {
  68. return true
  69. }
  70. type Extension struct {
  71. syms map[string]int
  72. classes map[string]int
  73. cbs map[int]*FuncEntry // cbid => callable callbak
  74. fidx int // = 0
  75. objs map[uintptr]interface{} // php's this => go's this
  76. objs_p map[unsafe.Pointer]interface{} // php's this => go's this
  77. // phpgo init function
  78. module_startup_func func(int, int) int
  79. module_shutdown_func func(int, int) int
  80. request_startup_func func(int, int) int
  81. request_shutdown_func func(int, int) int
  82. inis *zend.IniEntries // ini entries
  83. //
  84. me *C.zend_module_entry
  85. fe *C.zend_function_entry
  86. }
  87. // 与C中的同名结构体对应
  88. type phpgo_callback_signature struct {
  89. argtys [10]int8
  90. rety int
  91. varidict int
  92. }
  93. // TODO 把entry位置与cbid分开,这样cbfunc就能够更紧凑了
  94. func NewExtension() *Extension {
  95. syms := make(map[string]int, 0)
  96. classes := make(map[string]int, 0)
  97. cbs := make(map[int]*FuncEntry, 0)
  98. objs := make(map[uintptr]interface{}, 0)
  99. objs_p := make(map[unsafe.Pointer]interface{}, 0)
  100. classes["global"] = 0 // 可以看作内置函数的类
  101. this := &Extension{syms: syms, classes: classes, cbs: cbs,
  102. objs: objs, objs_p: objs_p}
  103. this.inis = zend.NewIniEntries()
  104. return this
  105. }
  106. // depcreated
  107. func gencbid(cidx int, fidx int) int {
  108. return cidx*128 + fidx
  109. }
  110. func nxtcbid() int {
  111. return len(gext.syms)
  112. }
  113. func AddFunc(name string, f interface{}) error {
  114. fe := NewFuncEntry("global", name, f)
  115. sname := fe.Name()
  116. if _, has := gext.syms[sname]; !has {
  117. // TODO check f type
  118. cidx := 0
  119. fidx := gext.fidx
  120. // cbid := gencbid(0, fidx)
  121. cbid := nxtcbid()
  122. argtys := zend.ArgTypes2Php(f)
  123. var cargtys *C.char = nil
  124. if argtys != nil {
  125. cargtys = C.CString(*argtys)
  126. }
  127. rety := zend.RetType2Php(f)
  128. cname := C.CString(name)
  129. n := C.zend_add_function(C.int(cidx), C.int(fidx), C.int(cbid), cname, cargtys, C.int(rety))
  130. C.free(unsafe.Pointer(cname))
  131. if argtys != nil {
  132. C.free(unsafe.Pointer(cargtys))
  133. }
  134. if int(n) == 0 {
  135. gext.syms[sname] = cbid
  136. gext.cbs[cbid] = fe
  137. gext.fidx += 1
  138. return nil
  139. }
  140. }
  141. return errors.New("add func error.")
  142. }
  143. // 添加新类的时候,可以把类的公共方法全部导出吧
  144. // 不用逐个方法添加,简单多了。
  145. // @param ctor 是该类的构造函数,原型 func NewClass(...) *Class
  146. func AddClass(name string, ctor interface{}) error {
  147. if _, has := gext.classes[name]; !has {
  148. cidx := len(gext.classes)
  149. var n C.int = 0
  150. // must add begin add class
  151. if int(n) == 0 {
  152. addCtor(cidx, name, ctor)
  153. addDtor(cidx, name, ctor)
  154. addMethods(cidx, name, ctor)
  155. }
  156. cname := C.CString(name)
  157. n = C.zend_add_class(C.int(cidx), cname)
  158. C.free(unsafe.Pointer(cname))
  159. if int(n) == 0 {
  160. gext.classes[name] = cidx
  161. }
  162. return nil
  163. }
  164. return errors.New("add class error.")
  165. }
  166. func addDtor(cidx int, cname string, ctor interface{}) {
  167. mname := "__destruct"
  168. fidx := 1
  169. addMethod(ctor, cidx, fidx, cname, mname, ctor, false, true)
  170. }
  171. func addCtor(cidx int, cname string, ctor interface{}) {
  172. mname := "__construct"
  173. fidx := 0
  174. addMethod(ctor, cidx, fidx, cname, mname, ctor, true, false)
  175. }
  176. func addMethods(cidx int, cname string, ctor interface{}) {
  177. fty := reflect.TypeOf(ctor)
  178. cls := fty.Out(0)
  179. for idx := 0; idx < cls.NumMethod(); idx++ {
  180. mth := cls.Method(idx)
  181. addMethod(ctor, cidx, idx+2, cname, mth.Name, mth.Func.Interface(), false, false)
  182. }
  183. }
  184. func addMethod(ctor interface{}, cidx int, fidx int, cname string, mname string, fn interface{}, isctor, isdtor bool) {
  185. // cidx := gext.classes[cname]
  186. // cbid := gencbid(cidx, fidx)
  187. cbid := nxtcbid()
  188. fe := NewFuncEntry(cname, mname, fn)
  189. fe.isctor = isctor
  190. fe.isdtor = isdtor
  191. argtys := zend.ArgTypes2Php(fn)
  192. var cargtys *C.char = nil
  193. if argtys != nil {
  194. cargtys = C.CString(*argtys)
  195. }
  196. isSelf := false
  197. methodRetType := reflect.TypeOf(fn)
  198. if methodRetType.NumOut() > 0 {
  199. classType := reflect.TypeOf(ctor).Out(0)
  200. isSelf = classType == methodRetType.Out(0)
  201. }
  202. var rety int
  203. if !isSelf {
  204. rety = zend.RetType2Php(fn)
  205. } else {
  206. rety = zend.PHPTY_IS_SELF
  207. }
  208. ccname := C.CString(cname)
  209. cmname := C.CString(mname)
  210. mn := C.zend_add_method(C.int(cidx), C.int(fidx), C.int(cbid), ccname, cmname, cargtys, C.int(rety))
  211. C.free(unsafe.Pointer(ccname))
  212. C.free(unsafe.Pointer(cmname))
  213. if argtys != nil {
  214. C.free(unsafe.Pointer(cargtys))
  215. }
  216. if mn == 0 {
  217. gext.cbs[cbid] = fe
  218. gext.syms[fe.Name()] = cbid
  219. }
  220. }
  221. func validFunc(fn interface{}) bool {
  222. fty := reflect.TypeOf(fn)
  223. if fty.Kind() != reflect.Func {
  224. log.Panicln("What's that?", fty.Kind().String())
  225. }
  226. if fty.IsVariadic() {
  227. log.Panicln("Can't support variadic func.", fty.Kind().String())
  228. }
  229. for idx := 0; idx < fty.NumIn(); idx++ {
  230. switch fty.In(idx).Kind() {
  231. case reflect.Func:
  232. fallthrough
  233. case reflect.Array:
  234. fallthrough
  235. case reflect.Slice:
  236. fallthrough
  237. case reflect.Chan:
  238. fallthrough
  239. case reflect.Map:
  240. fallthrough
  241. default:
  242. log.Panicln("Can't support arg type:", idx, fty.In(idx).Kind().String())
  243. }
  244. }
  245. return true
  246. }
  247. /*
  248. * @param namespace string optional
  249. */
  250. func AddConstant(name string, val interface{}, namespace interface{}) error {
  251. if len(name) == 0 {
  252. return nil
  253. }
  254. if namespace != nil {
  255. log.Println("Warning, namespace parameter not supported now. omited.")
  256. }
  257. module_number := C.phpgo_get_module_number()
  258. modname := C.CString(strings.ToUpper(name))
  259. defer C.free(unsafe.Pointer(modname))
  260. if val != nil {
  261. valty := reflect.TypeOf(val)
  262. switch valty.Kind() {
  263. case reflect.String:
  264. v := val.(string)
  265. modval := C.CString(v)
  266. defer C.free(unsafe.Pointer(modval))
  267. C.zend_register_stringl_constant_compat(modname, C.size_t(len(name)), modval, C.size_t(len(v)),
  268. C.CONST_CS|C.CONST_PERSISTENT, C.int(module_number))
  269. case reflect.Int, reflect.Int32, reflect.Uint32, reflect.Int64, reflect.Uint64,
  270. reflect.Int8, reflect.Uint8:
  271. iv := reflect.ValueOf(val).Convert(reflect.TypeOf(int64(1))).Interface()
  272. C.zend_register_long_constant_compat(modname, C.size_t(len(name)), C.zend_long(iv.(int64)),
  273. C.CONST_CS|C.CONST_PERSISTENT, C.int(module_number))
  274. case reflect.Float32, reflect.Float64:
  275. fv := reflect.ValueOf(val).Convert(reflect.TypeOf(float64(1.0))).Interface()
  276. C.zend_register_double_constant_compat(modname, C.size_t(len(name)), C.double(fv.(float64)),
  277. C.CONST_CS|C.CONST_PERSISTENT, C.int(module_number))
  278. case reflect.Bool:
  279. v := val.(bool)
  280. var bv int8 = 1
  281. if v == false {
  282. bv = 0
  283. }
  284. C.zend_register_bool_constant_compat(modname, C.size_t(len(name)), C.zend_bool(bv),
  285. C.CONST_CS|C.CONST_PERSISTENT, C.int(module_number))
  286. default:
  287. err := fmt.Errorf("Warning, unsported constant value type: %v", valty.Kind().String())
  288. log.Println(err)
  289. return err
  290. }
  291. } else {
  292. C.zend_register_null_constant_compat(modname, C.size_t(len(name)),
  293. C.CONST_CS|C.CONST_PERSISTENT, C.int(module_number))
  294. }
  295. return nil
  296. }
  297. func AddIniVar(name string, value interface{}, modifiable bool,
  298. onModifier func(*zend.IniEntry, string, int) int,
  299. displayer func(*zend.IniEntry, int)) {
  300. ie := zend.NewIniEntryDef()
  301. ie.Fill(name, value, modifiable, nil, nil)
  302. ie.SetModifier(onModifier)
  303. ie.SetDisplayer(displayer)
  304. gext.inis.Add(ie)
  305. }
  306. // TODO 如果比较多的话,可以摘出来,放在builtin.go中
  307. // 内置函数注册,内置类注册。
  308. func addBuiltins() {
  309. // nice fix exit crash bug.
  310. var iret C.int = 0
  311. if iret = C.gozend_function_registered(C.CString("GoExit")); iret == C.int(0) {
  312. AddFunc("GoExit", func(code int) { os.Exit(code) })
  313. }
  314. if iret = C.gozend_function_registered(C.CString("GoGo")); iret == C.int(0) {
  315. AddFunc("GoGo", func(fn interface{}) { log.Println(fn) })
  316. }
  317. if iret = C.gozend_function_registered(C.CString("GoPanic")); iret == C.int(0) {
  318. AddFunc("GoPanic", func() { panic("got") })
  319. }
  320. if iret = C.gozend_function_registered(C.CString("GoRecover")); iret == C.int(0) {
  321. AddFunc("GoRecover", func() { recover() })
  322. }
  323. if iret = C.gozend_function_registered(C.CString("GoPrintln")); iret == C.int(0) {
  324. AddFunc("GoPrintln", func(p0 int, v interface{}) { log.Println(v, 123333) })
  325. }
  326. }
  327. // TODO init func with go's //export
  328. // 注册php module 初始化函数
  329. func RegisterInitFunctions(module_startup_func func(int, int) int,
  330. module_shutdown_func func(int, int) int,
  331. request_startup_func func(int, int) int,
  332. request_shutdown_func func(int, int) int) {
  333. gext.module_startup_func = module_startup_func
  334. gext.module_shutdown_func = module_shutdown_func
  335. gext.request_startup_func = request_startup_func
  336. gext.request_shutdown_func = request_shutdown_func
  337. tocip := func(f interface{}) unsafe.Pointer {
  338. return unsafe.Pointer(&f)
  339. }
  340. C.phpgo_register_init_functions(tocip(go_module_startup_func), tocip(gext.module_shutdown_func),
  341. tocip(gext.request_startup_func), tocip(gext.request_shutdown_func))
  342. }
  343. // the module_startup_func proxy
  344. func go_module_startup_func(a0 int, a1 int) int {
  345. // for test begin
  346. modifier := func(ie *zend.IniEntry, newValue string, stage int) int {
  347. // log.Println(ie.Name(), newValue, stage)
  348. return 0
  349. }
  350. displayer := func(ie *zend.IniEntry, itype int) {
  351. // log.Println(ie.Name(), itype)
  352. }
  353. AddIniVar("phpgo.hehe_int", 123, true, modifier, displayer)
  354. AddIniVar("phpgo.hehe_bool", true, true, modifier, displayer)
  355. AddIniVar("phpgo.hehe_long", 123, true, modifier, displayer)
  356. AddIniVar("phpgo.hehe_string", "strval123", true, modifier, displayer)
  357. // for test end
  358. gext.inis.Register(a1)
  359. return gext.module_startup_func(a0, a1)
  360. }
  361. //
  362. // 以整数类型传递go值类型的实现的回调方式
  363. //export on_phpgo_function_callback
  364. func on_phpgo_function_callback(cbid int, phpthis uintptr,
  365. a0 uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr,
  366. a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr) uintptr {
  367. args := []uintptr{a0, a1, a2, a3, a4, a5, a6, a7, a8, a9}
  368. if len(args) > 0 {
  369. }
  370. log.Println("go callback called:", cbid, phpthis, gext.cbs[cbid])
  371. log.Println("go callback called:", args)
  372. fe := gext.cbs[cbid]
  373. // fe.fn.(func())()
  374. // 根据方法原型中的参数个数与类型,从当前函数中的a0-a9中提取正确的值出来
  375. fval := reflect.ValueOf(fe.fn)
  376. argv := zend.ArgValuesFromPhp(fe.fn, args)
  377. if fe.IsMethod() {
  378. if phpthis == 0 {
  379. panic("wtf")
  380. }
  381. if _, has := gext.objs[phpthis]; !has {
  382. panic("wtf")
  383. }
  384. gothis := gext.objs[phpthis]
  385. // argv = append([]reflect.Value{reflect.ValueOf(gothis)}, argv...)
  386. argv[0] = reflect.ValueOf(gothis)
  387. }
  388. outs := fval.Call(argv)
  389. ret := zend.RetValue2Php(fe.fn, outs)
  390. fmt.Println("meta call ret:", outs, ret)
  391. if fe.IsCtor() {
  392. if phpthis == 0 {
  393. panic("wtf")
  394. }
  395. if _, has := gext.objs[phpthis]; has {
  396. panic("wtf")
  397. }
  398. gext.objs[phpthis] = outs[0].Interface()
  399. }
  400. return ret
  401. }
  402. //
  403. // 以指针类型传递go值类型的实现的回调方式
  404. //export on_phpgo_function_callback_p
  405. func on_phpgo_function_callback_p(cbid int, phpthis unsafe.Pointer,
  406. a0 unsafe.Pointer, a1 unsafe.Pointer, a2 unsafe.Pointer, a3 unsafe.Pointer, a4 unsafe.Pointer,
  407. a5 unsafe.Pointer, a6 unsafe.Pointer, a7 unsafe.Pointer, a8 unsafe.Pointer, a9 unsafe.Pointer,
  408. retpp *unsafe.Pointer, op unsafe.Pointer) {
  409. args := []unsafe.Pointer{a0, a1, a2, a3, a4, a5, a6, a7, a8, a9}
  410. if len(args) > 0 {
  411. }
  412. log.Println("go callback called:", cbid, phpthis, gext.cbs[cbid], op)
  413. log.Println("go callback called:", args)
  414. fe := gext.cbs[cbid]
  415. // fe.fn.(func())()
  416. if op == nil && !fe.IsGlobal() {
  417. panic("is not a class or a function")
  418. }
  419. // 根据方法原型中的参数个数与类型,从当前函数中的a0-a9中提取正确的值出来
  420. fval := reflect.ValueOf(fe.fn)
  421. argv := zend.ArgValuesFromPhp_p(fe.fn, args, fe.IsMethod())
  422. if fe.IsMethod() {
  423. zend.CHKNILEXIT(phpthis, "wtf")
  424. gothis, has := gext.objs_p[op]
  425. if !has {
  426. panic("wtf")
  427. }
  428. // argv = append([]reflect.Value{reflect.ValueOf(gothis)}, argv...)
  429. argv[0] = reflect.ValueOf(gothis)
  430. }
  431. outs := fval.Call(argv)
  432. ret := zend.RetValue2Php_p(fe.fn, outs)
  433. log.Println("meta call ret:", outs, ret)
  434. if fe.IsCtor() {
  435. zend.CHKNILEXIT(phpthis, "wtf")
  436. if _, has := gext.objs_p[op]; has {
  437. panic("wtf")
  438. }
  439. gext.objs_p[op] = outs[0].Interface()
  440. }
  441. if fe.IsDtor() {
  442. zend.CHKNILEXIT(phpthis, "wtf")
  443. if _, has := gext.objs_p[op]; !has {
  444. panic("wtf")
  445. }
  446. delete(gext.objs_p, op)
  447. }
  448. *retpp = ret
  449. // return ret
  450. }
  451. //
  452. // 比较通用的在C中调用go任意函数的方法
  453. // on_phpgo_function_callback是根据cbid来确定如何调用函数
  454. // 该函数直接根据函数指定fp函数指针对应的函数。
  455. //export call_golang_function
  456. func call_golang_function(fp unsafe.Pointer, a0 uintptr, a1 uintptr, a2 uintptr, a3 uintptr, a4 uintptr,
  457. a5 uintptr, a6 uintptr, a7 uintptr, a8 uintptr, a9 uintptr) uintptr {
  458. fval := reflect.ValueOf(*(*interface{})(fp))
  459. if fval.Interface() == nil {
  460. panic("wtf")
  461. }
  462. args := []uintptr{a0, a1, a2, a3, a4, a5, a6, a7, a8, a9}
  463. if len(args) > 0 {
  464. }
  465. argv := zend.ArgValuesFromPhp(fval.Interface(), args)
  466. if len(argv) > 0 {
  467. }
  468. outs := fval.Call(argv)
  469. ret := zend.RetValue2Php(fval.Interface(), outs)
  470. return ret
  471. }
  472. //
  473. // 比较通用的在C中调用go任意函数的方法(但参数是都指针形式的)
  474. // 该函数直接根据函数指定fp函数指针对应的函数。
  475. //export call_golang_function_p
  476. func call_golang_function_p(fp unsafe.Pointer, a0 unsafe.Pointer, a1 unsafe.Pointer, a2 unsafe.Pointer,
  477. a3 unsafe.Pointer, a4 unsafe.Pointer, a5 unsafe.Pointer, a6 unsafe.Pointer,
  478. a7 unsafe.Pointer, a8 unsafe.Pointer, a9 unsafe.Pointer) unsafe.Pointer {
  479. return nil
  480. }