diff --git a/README.MD b/README.MD
index f4c4c887b..4f1d1f23c 100644
--- a/README.MD
+++ b/README.MD
@@ -36,10 +36,10 @@ golang版本 >= 1.9.2
1. 提供了对基本数据类型的并发安全封装,提供了常用的数据结构容器;
1. 支持Go变量/Json/Xml/Yml/Toml任意数据格式之间的相互转换及创建;
1. 强大的数据库ORM,支持应用层级的集群管理、读写分离、负载均衡,查询缓存、方法及链式ORM操作;
-1. 更多特点请查阅框架[手册](http://gf.johng.cn)和[源码](https://godoc.org/github.com/johng-cn/gf);
+1. 更多特点请查阅框架[手册](https://gfer.me)和[源码](https://godoc.org/github.com/johng-cn/gf);
# 文档
-GoFrame开发文档:[http://gf.johng.cn](http://gf.johng.cn)
+GoFrame开发文档:[gfer.me](https://gfer.me)
# 使用
@@ -178,4 +178,4 @@ if tx, err := db.Begin(); err == nil {
...
-更多特性及示例请查看官方开发文档:[gf.johng.cn](http://gf.johng.cn)
+更多特性及示例请查看官方开发文档:[gfer.me](https://gfer.me)
diff --git a/TODO b/TODO
index 7ba360abe..ae76a2dcc 100644
--- a/TODO
+++ b/TODO
@@ -45,6 +45,7 @@ gform参考 https://gohouse.github.io/gorose/dist/index.html 进行改进
完善gform配置管理说明,g.DB/Database和gdb.New的区别;
+
DONE:
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
2. ghttp.Server请求执行中增加服务退出的方法,不再执行后续操作;
diff --git a/g/frame/gins/gins.go b/g/frame/gins/gins.go
index 76d409407..6404848e6 100644
--- a/g/frame/gins/gins.go
+++ b/g/frame/gins/gins.go
@@ -12,6 +12,7 @@ import (
"gitee.com/johng/gf/g/os/gcfg"
"gitee.com/johng/gf/g/os/gcmd"
"gitee.com/johng/gf/g/os/genv"
+ "gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gview"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/container/gmap"
@@ -125,7 +126,7 @@ func Database(name...string) *gdb.Db {
db := instances.GetOrSetFuncLock(key, func() interface{} {
m := config.GetMap("database")
if m == nil {
- panic(fmt.Sprintf(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath()))
+ glog.Errorfln(`incomplete configuration for database: "database" node not found in config file "%s"`, config.GetFilePath())
}
for group, v := range m {
cg := gdb.ConfigGroup{}
@@ -181,7 +182,7 @@ func Database(name...string) *gdb.Db {
if db, err := gdb.New(name...); err == nil {
return db
} else {
- panic(err)
+ glog.Error(err)
}
return nil
})
@@ -213,13 +214,13 @@ func Redis(name...string) *gredis.Redis {
Pass : array[4],
})
} else {
- panic(fmt.Sprintf(`invalid redis node configuration: "%s"`, line))
+ glog.Errorfln(`invalid redis node configuration: "%s"`, line)
}
} else {
- panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group))
+ glog.Errorfln(`configuration for redis not found for group "%s"`, group)
}
} else {
- panic(fmt.Sprintf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.GetFilePath()))
+ glog.Errorfln(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.GetFilePath())
}
return nil
})
diff --git a/g/g_func.go b/g/g_func.go
index bef7e7fd5..78459c0e0 100644
--- a/g/g_func.go
+++ b/g/g_func.go
@@ -33,16 +33,6 @@ func Wait() {
ghttp.Wait()
}
-// 是否显示调试信息
-func SetDebug(debug bool) {
- glog.SetDebug(debug)
-}
-
-// 设置日志的显示等级
-func SetLogLevel(level int) {
- glog.SetLevel(level)
-}
-
// 打印变量
func Dump(i...interface{}) {
gutil.Dump(i...)
diff --git a/g/g_logger.go b/g/g_logger.go
index 6532bc601..ac9f1d6cf 100644
--- a/g/g_logger.go
+++ b/g/g_logger.go
@@ -6,3 +6,19 @@
package g
+import "gitee.com/johng/gf/g/os/glog"
+
+// 是否显示调试信息
+func SetDebug(debug bool) {
+ glog.SetDebug(debug)
+}
+
+// 设置日志的显示等级
+func SetLogLevel(level int) {
+ glog.SetLevel(level)
+}
+
+// 获取设置的日志显示等级
+func GetLogLevel() int {
+ return glog.GetLevel()
+}
\ No newline at end of file
diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go
index 3ac8f322f..954864822 100644
--- a/g/net/ghttp/ghttp_server.go
+++ b/g/net/ghttp/ghttp_server.go
@@ -213,9 +213,14 @@ func (s *Server) Start() error {
// 如果设置了静态文件目录,那么优先按照静态文件目录进行检索,其次是当前可执行文件工作目录;
// 并且如果是开发环境,默认也会添加main包的源码目录路径做为二级检索。
if s.config.ServerRoot != "" {
- s.paths.Set(s.config.ServerRoot)
+ if rp, err := s.paths.Set(s.config.ServerRoot); err != nil {
+ glog.Error("ghttp.SetServerRoot failed:", err.Error())
+ return err
+ } else {
+ glog.Debug("ghttp.SetServerRoot:", rp)
+ }
}
- s.paths.Add(gfile.SelfDir())
+ s.AddSearchPath(gfile.SelfDir())
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
s.paths.Add(p)
}
@@ -271,7 +276,7 @@ func (s *Server) Start() error {
// 打印展示路由表
func (s *Server) DumpRoutesMap() {
- if s.config.DumpRouteMap {
+ if s.config.DumpRouteMap && len(s.routesMap) > 0 {
// (等待一定时间后)当所有框架初始化信息打印完毕之后才打印路由表信息
gtime.SetTimeout(50*time.Millisecond, func() {
glog.Header(false).Println(fmt.Sprintf("\n%s\n", s.GetRouteMap()))
diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go
index 3b84a7652..ae3bd90ed 100644
--- a/g/net/ghttp/ghttp_server_config.go
+++ b/g/net/ghttp/ghttp_server_config.go
@@ -303,7 +303,13 @@ func (s *Server) SetDumpRouteMap(enabled bool) {
// 添加静态文件搜索目录,必须给定目录的绝对路径
func (s *Server) AddSearchPath(path string) error {
- return s.paths.Add(path)
+ if rp, err := s.paths.Add(path); err != nil {
+ glog.Error("ghttp.AddSearchPath failed:", err.Error())
+ return err
+ } else {
+ glog.Debug("ghttp.AddSearchPath:", rp)
+ }
+ return nil
}
// 获取
diff --git a/g/net/ghttp/ghttp_server_service_controller.go b/g/net/ghttp/ghttp_server_service_controller.go
index 4455f4697..e20fddb94 100644
--- a/g/net/ghttp/ghttp_server_service_controller.go
+++ b/g/net/ghttp/ghttp_server_service_controller.go
@@ -9,6 +9,7 @@ package ghttp
import (
"errors"
+ "gitee.com/johng/gf/g/os/glog"
"strings"
"reflect"
"fmt"
@@ -42,6 +43,14 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e
if mname == "Init" || mname == "Shut" || mname == "Exit" {
continue
}
+ if _, ok := v.Method(i).Interface().(func()); !ok {
+ if methodMap != nil {
+ s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String())
+ glog.Error(s)
+ return errors.New(s)
+ }
+ continue
+ }
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if ctlName[0] == '*' {
ctlName = fmt.Sprintf(`(%s)`, ctlName)
@@ -82,9 +91,15 @@ func (s *Server)BindControllerMethod(pattern string, c Controller, method string
t := v.Type()
sname := t.Elem().Name()
mname := strings.TrimSpace(method)
- if !v.MethodByName(mname).IsValid() {
+ fval := v.MethodByName(mname)
+ if !fval.IsValid() {
return errors.New("invalid method name:" + mname)
}
+ if _, ok := fval.Interface().(func()); !ok {
+ s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, fval.Type().String())
+ glog.Error(s)
+ return errors.New(s)
+ }
pkgPath := t.Elem().PkgPath()
pkgName := gfile.Basename(pkgPath)
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
@@ -119,6 +134,11 @@ func (s *Server)BindControllerRest(pattern string, c Controller) error {
if _, ok := s.methodsMap[method]; !ok {
continue
}
+ if _, ok := v.Method(i).Interface().(func()); !ok {
+ s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String())
+ glog.Error(s)
+ return errors.New(s)
+ }
pkgName := gfile.Basename(pkgPath)
ctlName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if ctlName[0] == '*' {
diff --git a/g/net/ghttp/ghttp_server_service_object.go b/g/net/ghttp/ghttp_server_service_object.go
index 8603ab6f1..ef64df4a1 100644
--- a/g/net/ghttp/ghttp_server_service_object.go
+++ b/g/net/ghttp/ghttp_server_service_object.go
@@ -9,6 +9,7 @@ package ghttp
import (
"errors"
+ "gitee.com/johng/gf/g/os/glog"
"strings"
"reflect"
"fmt"
@@ -48,6 +49,15 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
if mname == "Init" || mname == "Shut" {
continue
}
+ faddr, ok := v.Method(i).Interface().(func(*Request))
+ if !ok {
+ if methodMap != nil {
+ s := fmt.Sprintf(`invalid medthod definition "%s", while "func(*Request))" is required`, v.Method(i).Type().String())
+ glog.Error(s)
+ return errors.New(s)
+ }
+ continue
+ }
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
objName = fmt.Sprintf(`(%s)`, objName)
@@ -58,7 +68,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
- faddr : v.Method(i).Interface().(func(*Request)),
+ faddr : faddr,
finit : finit,
fshut : fshut,
}
@@ -76,7 +86,7 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
- faddr : v.Method(i).Interface().(func(*Request)),
+ faddr : faddr,
finit : finit,
fshut : fshut,
}
@@ -97,6 +107,12 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
if !fval.IsValid() {
return errors.New("invalid method name:" + mname)
}
+ faddr, ok := fval.Interface().(func(*Request))
+ if !ok {
+ s := fmt.Sprintf(`invalid medthod definition "%s", while "func(*Request)" is required`, fval.Type().String())
+ glog.Error(s)
+ return errors.New(s)
+ }
finit := (func(*Request))(nil)
fshut := (func(*Request))(nil)
if v.MethodByName("Init").IsValid() {
@@ -117,7 +133,7 @@ func (s *Server)BindObjectMethod(pattern string, obj interface{}, method string)
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
- faddr : fval.Interface().(func(*Request)),
+ faddr : faddr,
finit : finit,
fshut : fshut,
}
@@ -146,6 +162,12 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
if _, ok := s.methodsMap[method]; !ok {
continue
}
+ faddr, ok := v.Method(i).Interface().(func(*Request))
+ if !ok {
+ s := fmt.Sprintf(`invalid medthod definition "%s", while "func()" is required`, v.Method(i).Type().String())
+ glog.Error(s)
+ return errors.New(s)
+ }
pkgName := gfile.Basename(pkgPath)
objName := gstr.Replace(t.String(), fmt.Sprintf(`%s.`, pkgName), "")
if objName[0] == '*' {
@@ -157,7 +179,7 @@ func (s *Server)BindObjectRest(pattern string, obj interface{}) error {
rtype : gROUTE_REGISTER_OBJECT,
ctype : nil,
fname : "",
- faddr : v.Method(i).Interface().(func(*Request)),
+ faddr : faddr,
finit : finit,
fshut : fshut,
}
diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go
index a7e5094d4..08530608e 100644
--- a/g/os/gcfg/gcfg.go
+++ b/g/os/gcfg/gcfg.go
@@ -9,14 +9,14 @@
package gcfg
import (
- "gitee.com/johng/gf/g/container/gvar"
- "gitee.com/johng/gf/g/os/gspath"
- "gitee.com/johng/gf/g/os/gfsnotify"
- "gitee.com/johng/gf/g/container/gmap"
- "gitee.com/johng/gf/g/encoding/gjson"
- "gitee.com/johng/gf/g/container/gtype"
"errors"
+ "gitee.com/johng/gf/g/container/gmap"
+ "gitee.com/johng/gf/g/container/gtype"
+ "gitee.com/johng/gf/g/container/gvar"
+ "gitee.com/johng/gf/g/encoding/gjson"
+ "gitee.com/johng/gf/g/os/gfsnotify"
"gitee.com/johng/gf/g/os/glog"
+ "gitee.com/johng/gf/g/os/gspath"
)
const (
@@ -37,14 +37,14 @@ func New(path string, file...string) *Config {
if len(file) > 0 {
name = file[0]
}
- s := gspath.New()
- s.Set(path)
- return &Config {
+ c := &Config {
name : gtype.NewString(name),
- paths : s,
+ paths : gspath.New(),
jsons : gmap.NewStringInterfaceMap(),
vc : gtype.NewBool(),
}
+ c.SetPath(path)
+ return c
}
// 判断从哪个配置文件中获取内容,返回配置文件的绝对路径
@@ -58,12 +58,13 @@ func (c *Config) filePath(file...string) string {
// 设置配置管理器的配置文件存放目录绝对路径
func (c *Config) SetPath(path string) error {
- if err := c.paths.Set(path); err != nil {
- glog.Error("gcfg.SetPath failed:", path, err)
+ if rp, err := c.paths.Set(path); err != nil {
+ glog.Error("gcfg.SetPath failed:", err.Error())
return err
+ } else {
+ c.jsons.Clear()
+ glog.Debug("gcfg.SetPath:", rp)
}
- c.jsons.Clear()
- glog.Debug("gcfg.SetPath:", path)
return nil
}
@@ -76,11 +77,12 @@ func (c *Config) SetViolenceCheck(check bool) {
// 添加配置管理器的配置文件搜索路径
func (c *Config) AddPath(path string) error {
- if err := c.paths.Add(path); err != nil {
- glog.Debug("gcfg.AddPath failed:", path, err)
+ if rp, err := c.paths.Add(path); err != nil {
+ glog.Debug("gcfg.AddPath failed:", err.Error())
return err
+ } else {
+ glog.Debug("gcfg.AddPath:", rp)
}
- glog.Debug("gcfg.AddPath:", path)
return nil
}
@@ -95,6 +97,7 @@ func (c *Config) GetFilePath(file...string) string {
// 设置配置管理对象的默认文件名称
func (c *Config) SetFileName(name string) {
+ glog.Debug("gcfg.SetFileName:", name)
c.name.Set(name)
}
diff --git a/g/os/gspath/gspath.go b/g/os/gspath/gspath.go
index c0d25edee..38d0bde35 100644
--- a/g/os/gspath/gspath.go
+++ b/g/os/gspath/gspath.go
@@ -10,6 +10,7 @@ package gspath
import (
"errors"
+ "fmt"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/os/gfsnotify"
@@ -32,46 +33,52 @@ func New () *SPath {
}
// 设置搜索路径,只保留当前设置项,其他搜索路径被清空
-func (sp *SPath) Set(path string) error {
- r := gfile.RealPath(path)
- if r == "" {
- r = sp.Search(path)
- if r == "" {
- r = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
+func (sp *SPath) Set(path string) (realpath string, err error) {
+ realpath = gfile.RealPath(path)
+ if realpath == "" {
+ realpath = sp.Search(path)
+ if realpath == "" {
+ realpath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
}
}
- if r != "" && gfile.IsDir(r) {
- r = strings.TrimRight(r, gfile.Separator)
+ if realpath == "" {
+ return realpath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
+ }
+ if realpath != "" && gfile.IsDir(realpath) {
+ realpath = strings.TrimRight(realpath, gfile.Separator)
sp.mu.Lock()
- sp.paths = []string{r}
+ sp.paths = []string{realpath}
sp.mu.Unlock()
sp.cache.Clear()
//glog.Debug("gspath.SetPath:", r)
- return nil
+ return realpath, nil
}
//glog.Warning("gspath.SetPath failed:", path)
- return errors.New("invalid path:" + path)
+ return realpath, errors.New("invalid path:" + path)
}
// 添加搜索路径
-func (sp *SPath) Add(path string) error {
- r := gfile.RealPath(path)
- if r == "" {
- r = sp.Search(path)
- if r == "" {
- r = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
+func (sp *SPath) Add(path string) (realpath string, err error) {
+ realpath = gfile.RealPath(path)
+ if realpath == "" {
+ realpath = sp.Search(path)
+ if realpath == "" {
+ realpath = gfile.RealPath(gfile.Pwd() + gfile.Separator + path)
}
}
- if r != "" && gfile.IsDir(r) {
- r = strings.TrimRight(r, gfile.Separator)
+ if realpath == "" {
+ return realpath, errors.New(fmt.Sprintf(`path "%s" does not exist`, path))
+ }
+ if realpath != "" && gfile.IsDir(realpath) {
+ realpath = strings.TrimRight(realpath, gfile.Separator)
sp.mu.Lock()
- sp.paths = append(sp.paths, r)
+ sp.paths = append(sp.paths, realpath)
sp.mu.Unlock()
//glog.Debug("gspath.Add:", r)
- return nil
+ return realpath, nil
}
//glog.Warning("gspath.Add failed:", path)
- return errors.New("invalid path:" + path)
+ return realpath, errors.New("invalid path:" + path)
}
// 按照优先级搜索文件,返回搜索到的文件绝对路径,找不到该文件时,返回空字符串
diff --git a/g/os/gtime/gtime.go b/g/os/gtime/gtime.go
index 0b8647f8e..cc1f6d619 100644
--- a/g/os/gtime/gtime.go
+++ b/g/os/gtime/gtime.go
@@ -25,9 +25,10 @@ const (
// "2018-02-09 20:46:17.897",
// "2018-02-09T20:46:17Z",
// "2018-02-09 20:46:17",
+ // "2018/10/31 - 16:38:46"
// "2018-02-09",
// 日期连接符号支持'-'或者'/'
- TIME_REAGEX_PATTERN = `(\d{2,4}[-/]\d{2}[-/]\d{2})[\sT]{0,1}(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)`
+ TIME_REAGEX_PATTERN = `(\d{2,4}[-/]\d{2}[-/]\d{2})[\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)`
)
var (
diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go
index 7a16c80d6..76852c9cf 100644
--- a/g/os/gview/gview.go
+++ b/g/os/gview/gview.go
@@ -8,7 +8,9 @@
package gview
import (
+ "fmt"
"gitee.com/johng/gf/g/encoding/gurl"
+ "gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gstr"
"strings"
@@ -70,40 +72,55 @@ func Get(path string) *View {
// 生成一个视图对象
func New(path string) *View {
- s := gspath.New()
- s.Set(path)
view := &View {
- paths : s,
+ paths : gspath.New(),
data : make(map[string]interface{}),
funcmap : make(map[string]interface{}),
delimiters : make([]string, 2),
}
+ view.SetPath(path)
view.SetDelimiters("{{", "}}")
// 内置方法
view.BindFunc("text", view.funcText)
view.BindFunc("html", view.funcHtmlEncode)
view.BindFunc("htmlencode", view.funcHtmlEncode)
view.BindFunc("htmldecode", view.funcHtmlDecode)
- //view.BindFunc("htmlchars", view.funcHtmlChars)
- //view.BindFunc("htmldechars", view.funcHtmlCharsDecode)
view.BindFunc("url", view.funcUrlEncode)
view.BindFunc("urlencode", view.funcUrlEncode)
view.BindFunc("urldecode", view.funcUrlDecode)
view.BindFunc("date", view.funcDate)
view.BindFunc("substr", view.funcSubStr)
+ view.BindFunc("strlimit", view.funcStrLimit)
view.BindFunc("compare", view.funcCompare)
+ view.BindFunc("hidestr", view.funcHideStr)
+ view.BindFunc("highlight", view.funcHighlight)
+ view.BindFunc("toupper", view.funcToUpper)
+ view.BindFunc("tolower", view.funcToLower)
+ view.BindFunc("nl2br", view.funcNl2Br)
view.BindFunc("include", view.funcInclude)
return view
}
// 设置模板目录绝对路径
func (view *View) SetPath(path string) error {
- return view.paths.Set(path)
+ if rp, err := view.paths.Set(path); err != nil {
+ glog.Error("gview.SetPath failed:", err.Error())
+ return err
+ } else {
+ glog.Debug("gview.SetPath:", rp)
+ }
+ return nil
}
// 添加模板目录搜索路径
func (view *View) AddPath(path string) error {
- return view.paths.Add(path)
+ if rp, err := view.paths.Add(path); err != nil {
+ glog.Error("gview.AddPath failed:", err.Error())
+ return err
+ } else {
+ glog.Debug("gview.SetPath:", rp)
+ }
+ return nil
}
// 批量绑定模板变量,即调用之后每个线程都会生效,因此有并发安全控制
@@ -244,16 +261,6 @@ func (view *View) funcHtmlDecode(html interface{}) string {
return ghtml.EntitiesDecode(gconv.String(html))
}
-// 模板内置方法:htmlchars
-func (view *View) funcHtmlChars(html interface{}) string {
- return ghtml.SpecialChars(gconv.String(html))
-}
-
-// 模板内置方法:htmlcharsdecode
-func (view *View) funcHtmlCharsDecode(html interface{}) string {
- return ghtml.SpecialCharsDecode(gconv.String(html))
-}
-
// 模板内置方法:url
func (view *View) funcUrlEncode(url interface{}) string {
return gurl.Encode(gconv.String(url))
@@ -283,4 +290,34 @@ func (view *View) funcSubStr(start, end int, str interface{}) string {
return gstr.SubStr(gconv.String(str), start, end)
}
+// 模板内置方法:strlimit
+func (view *View) funcStrLimit(length int, suffix string, str interface{}) string {
+ return gstr.StrLimit(gconv.String(str), length, suffix)
+}
+
+// 模板内置方法:highlight
+func (view *View) funcHighlight(key string, color string, str interface{}) string {
+ return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`%s`, color, key))
+}
+
+// 模板内置方法:hidestr
+func (view *View) funcHideStr(percent int, hide string, str interface{}) string {
+ return gstr.HideStr(gconv.String(str), percent, hide)
+}
+
+// 模板内置方法:toupper
+func (view *View) funcToUpper(str interface{}) string {
+ return gstr.ToUpper(gconv.String(str))
+}
+
+// 模板内置方法:toupper
+func (view *View) funcToLower(str interface{}) string {
+ return gstr.ToLower(gconv.String(str))
+}
+
+// 模板内置方法:nl2br
+func (view *View) funcNl2Br(str interface{}) string {
+ return gstr.Nl2Br(gconv.String(str))
+}
+
diff --git a/g/util/gconv/gconv_struct.go b/g/util/gconv/gconv_struct.go
index 2ed0e0819..24ad7c9c6 100644
--- a/g/util/gconv/gconv_struct.go
+++ b/g/util/gconv/gconv_struct.go
@@ -7,6 +7,7 @@
package gconv
import (
+ "gitee.com/johng/gf/g/container/gset"
"gitee.com/johng/gf/g/util/gstr"
"reflect"
"gitee.com/johng/gf/third/github.com/fatih/structs"
@@ -83,16 +84,38 @@ func Struct(params interface{}, objPointer interface{}, attrMapping...map[string
}
}
// 最后按照默认规则进行匹配
+ attrset := gset.NewStringSet(false)
+ elemtype := elem.Type()
+ for i := 0; i < elem.NumField(); i++ {
+ attrset.Add(elemtype.Field(i).Name)
+ }
for mapk, mapv := range paramsMap {
- name := gstr.UcFirst(mapk)
- if _, ok := dmap[name]; ok {
+ name := ""
+ for _, v := range []string{gstr.UcFirst(mapk), gstr.ToLower(mapk), gstr.ToUpper(mapk)} {
+ if _, ok := dmap[v]; ok {
+ continue
+ }
+ if _, ok := tagmap[v]; ok {
+ continue
+ }
+ // 循环查找属性名称进行匹配
+ attrset.Iterator(func(value string) bool {
+ if strings.EqualFold(value, v) {
+ name = value
+ return false
+ }
+ return true
+ })
+ if name != "" {
+ break
+ }
+ }
+ // 如果没有匹配到属性名称,放弃
+ if name == "" {
continue
}
- // 后续tag逻辑中会处理的key(重复的键名)这里便不处理
- if _, ok := tagmap[mapk]; !ok {
- if err := bindVarToStruct(elem, name, mapv); err != nil {
- return err
- }
+ if err := bindVarToStruct(elem, name, mapv); err != nil {
+ return err
}
}
return nil
@@ -120,7 +143,7 @@ func getTagMapOfStruct(objPointer interface{}) map[string]string {
}
// 将参数值绑定到对象指定名称的属性上
-func bindVarToStruct(elem reflect.Value, name string, value interface{}) error {
+func bindVarToStruct(elem reflect.Value, name string, value interface{}) (err error) {
structFieldValue := elem.FieldByName(name)
// 键名与对象属性匹配检测,map中如果有struct不存在的属性,那么不做处理,直接return
if !structFieldValue.IsValid() {
@@ -136,7 +159,7 @@ func bindVarToStruct(elem reflect.Value, name string, value interface{}) error {
defer func() {
// 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换
if recover() != nil {
- bindVarToStructIfDefaultConvertionFailed(structFieldValue, value)
+ err = bindVarToStructIfDefaultConvertionFailed(structFieldValue, value)
}
}()
structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
@@ -144,7 +167,7 @@ func bindVarToStruct(elem reflect.Value, name string, value interface{}) error {
}
// 将参数值绑定到对象指定索引位置的属性上
-func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) error {
+func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) (err error) {
structFieldValue := elem.FieldByIndex([]int{index})
// 键名与对象属性匹配检测
if !structFieldValue.IsValid() {
@@ -160,7 +183,7 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) er
defer func() {
// 如果转换失败,那么可能是类型不匹配造成(例如属性包含自定义类型),那么执行递归转换
if recover() != nil {
- bindVarToStructIfDefaultConvertionFailed(structFieldValue, value)
+ err = bindVarToStructIfDefaultConvertionFailed(structFieldValue, value)
}
}()
structFieldValue.Set(reflect.ValueOf(Convert(value, structFieldValue.Type().String())))
@@ -168,7 +191,7 @@ func bindVarToStructByIndex(elem reflect.Value, index int, value interface{}) er
}
// 当默认的基本类型转换失败时,通过recover判断后执行反射类型转换
-func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, value interface{}) {
+func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, value interface{}) error {
switch structFieldValue.Kind() {
case reflect.Struct:
Struct(value, structFieldValue)
@@ -190,7 +213,8 @@ func bindVarToStructIfDefaultConvertionFailed(structFieldValue reflect.Value, va
}
structFieldValue.Set(a)
default:
- panic(errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String())))
+ return errors.New(fmt.Sprintf(`cannot convert to type "%s"`, structFieldValue.Type().String()))
}
+ return nil
}
diff --git a/g/util/gconv/gconv_test.go b/g/util/gconv/gconv_test.go
new file mode 100644
index 000000000..b740eb2ff
--- /dev/null
+++ b/g/util/gconv/gconv_test.go
@@ -0,0 +1,137 @@
+// Copyright 2017-2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
+//
+// This Source Code Form is subject to the terms of the MIT License.
+// If a copy of the MIT was not distributed with this file,
+// You can obtain one at https://gitee.com/johng/gf.
+
+// go test *.go -bench=".*" -benchmem
+
+package gconv
+
+import (
+ "testing"
+)
+
+var value = 123456789
+
+func BenchmarkString(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ String(value)
+ }
+}
+
+func BenchmarkInt(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Int(value)
+ }
+}
+
+func BenchmarkInt8(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Int8(value)
+ }
+}
+
+func BenchmarkInt16(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Int16(value)
+ }
+}
+
+func BenchmarkInt32(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Int32(value)
+ }
+}
+
+func BenchmarkInt64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Int(value)
+ }
+}
+
+func BenchmarkUint(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Uint(value)
+ }
+}
+
+func BenchmarkUint8(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Uint8(value)
+ }
+}
+
+func BenchmarkUint16(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Uint16(value)
+ }
+}
+
+func BenchmarkUint32(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Uint32(value)
+ }
+}
+
+func BenchmarkUint64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Uint64(value)
+ }
+}
+
+func BenchmarkFloat32(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Float32(value)
+ }
+}
+
+func BenchmarkFloat64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Float64(value)
+ }
+}
+
+
+func BenchmarkTime(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Time(value)
+ }
+}
+
+func BenchmarkTimeDuration(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ TimeDuration(value)
+ }
+}
+
+
+func BenchmarkBytes(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Bytes(value)
+ }
+}
+
+func BenchmarkStrings(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Strings(value)
+ }
+}
+
+func BenchmarkInts(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Ints(value)
+ }
+}
+
+func BenchmarkFloats(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Floats(value)
+ }
+}
+
+func BenchmarkInterfaces(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ Interfaces(value)
+ }
+}
\ No newline at end of file
diff --git a/g/util/gregex/gregex.go b/g/util/gregex/gregex.go
index 73d92ae88..c6c74ebbc 100644
--- a/g/util/gregex/gregex.go
+++ b/g/util/gregex/gregex.go
@@ -8,12 +8,12 @@
package gregex
import (
+ "gitee.com/johng/gf/g/container/gmap"
"regexp"
- "gitee.com/johng/gf/g/os/gcache"
)
// 缓存对象,主要用于缓存底层regx对象
-var regxCache = gcache.New()
+var regxCache = gmap.NewStringInterfaceMap(true)
// 根据pattern生成对应的regexp正则对象
func getRegexp(pattern string) (*regexp.Regexp, error) {
@@ -21,7 +21,7 @@ func getRegexp(pattern string) (*regexp.Regexp, error) {
return v.(*regexp.Regexp), nil
}
if r, err := regexp.Compile(pattern); err == nil {
- regxCache.Set(pattern, r, 0)
+ regxCache.Set(pattern, r)
return r, nil
} else {
return nil, err
diff --git a/g/util/gstr/gstr.go b/g/util/gstr/gstr.go
index eeb46a7da..8decd74ba 100644
--- a/g/util/gstr/gstr.go
+++ b/g/util/gstr/gstr.go
@@ -7,11 +7,19 @@
// 字符串操作.
package gstr
-import "strings"
+import (
+ "bytes"
+ "math"
+ "strings"
+)
// 字符串替换
-func Replace(origin, search, replace string) string {
- return strings.Replace(origin, search, replace, -1)
+func Replace(origin, search, replace string, count...int) string {
+ n := -1
+ if len(count) > 0 {
+ n = count[0]
+ }
+ return strings.Replace(origin, search, replace, n)
}
// 使用map进行字符串替换
@@ -23,6 +31,16 @@ func ReplaceByMap(origin string, replaces map[string]string) string {
return result
}
+// 字符串转换为小写
+func ToLower(s string) string {
+ return strings.ToLower(s)
+}
+
+// 字符串转换为大写
+func ToUpper(s string) string {
+ return strings.ToUpper(s)
+}
+
// 字符串首字母转换为大写
func UcFirst(s string) string {
if len(s) == 0 {
@@ -110,4 +128,51 @@ func SubStr(str string, start int, length...int) (substr string) {
}
// 返回子串
return string(rs[start : end])
+}
+
+// 字符串长度截取限制,超过长度限制被截取并在字符串末尾追加指定的内容,支持中文
+func StrLimit(str string, length int, suffix...string) (string) {
+ rs := []rune(str)
+ if len(str) < length {
+ return str
+ }
+ addstr := "..."
+ if len(suffix) > 0 {
+ addstr = suffix[0]
+ }
+ return string(rs[0 : length]) + addstr
+}
+
+// 按照百分比从字符串中间向两边隐藏字符(主要用于姓名、手机号、邮箱地址、身份证号等的隐藏),支持utf-8中文,支持email格式。
+func HideStr(str string, percent int, hide string) string {
+ array := strings.Split(str, "@")
+ if len(array) > 1 {
+ str = array[0]
+ }
+ rs := []rune(str)
+ length := len(rs)
+ mid := math.Floor(float64(length/2))
+ hideLen := int(math.Floor(float64(length) * (float64(percent)/100)))
+ start := int(mid - math.Floor(float64(hideLen) / 2))
+ hideStr := []rune("")
+ hideRune := []rune(hide)
+ for i := 0; i < int(hideLen); i++ {
+ hideStr = append(hideStr, hideRune...)
+ }
+ buffer := bytes.NewBuffer(nil)
+ buffer.WriteString(string(rs[0 : start]))
+ buffer.WriteString(string(hideStr))
+ buffer.WriteString(string(rs[start + hideLen : ]))
+ if len(array) > 1 {
+ buffer.WriteString(array[1])
+ }
+ return buffer.String()
+}
+
+// 将\n\r替换为html中的
标签。
+func Nl2Br(str string) string {
+ str = Replace(str, "\r\n", "\n")
+ str = Replace(str, "\n\r", "\n")
+ str = Replace(str, "\n", "
")
+ return str
}
\ No newline at end of file
diff --git a/geg/net/ghttp/server/controller/user.go b/geg/net/ghttp/server/controller/user.go
index f0283ec90..50db001c9 100644
--- a/geg/net/ghttp/server/controller/user.go
+++ b/geg/net/ghttp/server/controller/user.go
@@ -1,8 +1,8 @@
package main
import (
- "gitee.com/johng/gf/g/frame/gmvc"
"gitee.com/johng/gf/g"
+ "gitee.com/johng/gf/g/frame/gmvc"
)
type User struct {
@@ -13,8 +13,13 @@ func (c *User) Index() {
c.View.Display("index.html")
}
+// 不符合规范,不会被自动注册
+func (c *User) Test(value interface{}) {
+ c.View.Display("index.html")
+}
+
func main() {
- g.View().SetPath("C:/www/static")
+ //g.View().SetPath("C:/www/static")
s := g.Server()
s.BindController("/user", new(User))
s.SetPort(8199)
diff --git a/geg/net/ghttp/server/object/user.go b/geg/net/ghttp/server/object/user.go
new file mode 100644
index 000000000..cd39824c8
--- /dev/null
+++ b/geg/net/ghttp/server/object/user.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "gitee.com/johng/gf/g"
+ "gitee.com/johng/gf/g/net/ghttp"
+)
+
+type User struct {
+
+}
+
+func (c *User) Index(r *ghttp.Request) {
+ r.Response.Write("Index")
+}
+
+// 不符合规范,不会被注册
+func (c *User) Test(r *ghttp.Request, value interface{}) {
+ r.Response.Write("Test")
+}
+
+func main() {
+ s := g.Server()
+ s.BindObjectMethod("/user", new(User), "Test")
+ s.SetPort(8199)
+ s.Run()
+}
+
+
+
+
+
diff --git a/geg/os/gtime/gtime_regex.go b/geg/os/gtime/gtime_regex.go
index 4f4463a28..03f0d51fa 100644
--- a/geg/os/gtime/gtime_regex.go
+++ b/geg/os/gtime/gtime_regex.go
@@ -19,6 +19,7 @@ func main() {
"2018-02-09 20:46:17.897",
"2018-02-09T20:46:17Z",
"2018-02-09 20:46:17",
+ "2018/10/31 - 16:38:46",
"2018-02-09",
"2017/12/14 04:51:34 +0805 LMT",
"2018/02/09 12:00:15",
diff --git a/geg/os/gview/build_in_funcs/build_in_funcs.go b/geg/os/gview/build_in_funcs/build_in_funcs.go
index 462b8d763..d39c683dd 100644
--- a/geg/os/gview/build_in_funcs/build_in_funcs.go
+++ b/geg/os/gview/build_in_funcs/build_in_funcs.go
@@ -20,6 +20,13 @@ func main() {
{{compare 1 1}}
{{"我是中国人" | substr 2 -1}}
{{"我是中国人" | substr 2 2}}
+{{"我是中国人" | strlimit 2 "..."}}
+{{"热爱GF热爱生活" | hidestr 20 "*"}}
+{{"热爱GF热爱生活" | hidestr 50 "*"}}
+{{"热爱GF热爱生活" | highlight "GF" "red"}}
+{{"gf" | toupper}}
+{{"GF" | tolower}}
+{{"Go\nFrame" | nl2br}}
`
content, err := g.View().ParseContent(tplContent, nil)
fmt.Println(err)
diff --git a/geg/other/test2.go b/geg/other/test2.go
index d51d46561..e2811e825 100644
--- a/geg/other/test2.go
+++ b/geg/other/test2.go
@@ -1,10 +1,20 @@
package main
import (
- "fmt"
- "gitee.com/johng/gf/g/os/gfile"
+ "gitee.com/johng/gf/g"
+ "gitee.com/johng/gf/g/net/ghttp"
)
+
func main() {
- fmt.Println(gfile.Dir("c:\111\222"))
+ s := g.Server()
+ s.Domain("www.a.com").BindHandler("/*", func(r *ghttp.Request) {
+ r.Response.ServeFile("/home/john/www1" + r.URL.Path)
+ })
+ s.Domain("www.b.com").BindHandler("/*", func(r *ghttp.Request) {
+ r.Response.ServeFile("/home/john/www2" + r.URL.Path)
+ })
+ s.SetIndexFolder(true)
+ s.SetPort(8080)
+ s.Run()
}
\ No newline at end of file
diff --git a/geg/util/gconv/gconv_struct1.go b/geg/util/gconv/gconv_struct1.go
index 2e6a971e7..5481c1599 100644
--- a/geg/util/gconv/gconv_struct1.go
+++ b/geg/util/gconv/gconv_struct1.go
@@ -16,13 +16,13 @@ type User struct {
func main() {
user := (*User)(nil)
- // 使用map直接映射绑定属性值到对象
+ // 使用默认映射规则绑定属性值到对象
user = new(User)
params1 := g.Map{
"uid" : 1,
- "name" : "john",
- "pass1" : "123",
- "pass2" : "123",
+ "Name" : "john",
+ "PASS1" : "123",
+ "PaSs2" : "456",
}
if err := gconv.Struct(params1, user); err == nil {
fmt.Println(user)
@@ -33,8 +33,8 @@ func main() {
params2 := g.Map {
"uid" : 2,
"name" : "smith",
- "password1" : "456",
- "password2" : "456",
+ "password1" : "111",
+ "password2" : "222",
}
if err := gconv.Struct(params2, user); err == nil {
fmt.Println(user)
diff --git a/geg/util/gconv/gconv_struct2.go b/geg/util/gconv/gconv_struct2.go
index 0b3bf6132..c9ad3a369 100644
--- a/geg/util/gconv/gconv_struct2.go
+++ b/geg/util/gconv/gconv_struct2.go
@@ -1,31 +1,28 @@
package main
import (
- "gitee.com/johng/gf/g/util/gconv"
- "gitee.com/johng/gf/g"
"fmt"
+ "gitee.com/johng/gf/g"
+ "gitee.com/johng/gf/g/util/gconv"
)
-// 演示slice类型属性的赋值
+
+// 使用默认映射规则绑定属性值到对象
func main() {
type User struct {
- Scores []int
+ Uid int
+ Name string
+ Pass1 string
+ Pass2 string
}
-
- user := new(User)
- scores := []interface{}{99, 100, 60, 140}
-
- // 通过map映射转换
- if err := gconv.Struct(g.Map{"Scores" : scores}, user); err != nil {
- fmt.Println(err)
- } else {
- g.Dump(user)
+ user := new(User)
+ params := g.Map {
+ "uid" : 1,
+ "Name" : "john",
+ "PASS1" : "123",
+ "PASS2" : "456",
}
-
- // 通过变量映射转换,直接slice赋值
- if err := gconv.Struct(scores, user); err != nil {
- fmt.Println(err)
- } else {
- g.Dump(user)
+ if err := gconv.Struct(params, user); err == nil {
+ fmt.Println(user)
}
}
\ No newline at end of file
diff --git a/geg/util/gconv/gconv_struct3.go b/geg/util/gconv/gconv_struct3.go
index 797ce13db..0b3bf6132 100644
--- a/geg/util/gconv/gconv_struct3.go
+++ b/geg/util/gconv/gconv_struct3.go
@@ -6,24 +6,23 @@ import (
"fmt"
)
+// 演示slice类型属性的赋值
func main() {
- type Score struct {
- Name string
- Result int
- }
type User struct {
- Scores Score
+ Scores []int
}
user := new(User)
- scores := map[string]interface{}{
- "Scores" : map[string]interface{}{
- "Name" : "john",
- "Result" : 100,
- },
+ scores := []interface{}{99, 100, 60, 140}
+
+ // 通过map映射转换
+ if err := gconv.Struct(g.Map{"Scores" : scores}, user); err != nil {
+ fmt.Println(err)
+ } else {
+ g.Dump(user)
}
- // 嵌套struct转换
+ // 通过变量映射转换,直接slice赋值
if err := gconv.Struct(scores, user); err != nil {
fmt.Println(err)
} else {
diff --git a/geg/util/gconv/gconv_struct4.go b/geg/util/gconv/gconv_struct4.go
index 99dcb221b..797ce13db 100644
--- a/geg/util/gconv/gconv_struct4.go
+++ b/geg/util/gconv/gconv_struct4.go
@@ -12,7 +12,7 @@ func main() {
Result int
}
type User struct {
- Scores []Score
+ Scores Score
}
user := new(User)
@@ -23,7 +23,7 @@ func main() {
},
}
- // 嵌套struct转换,属性为slice类型,数值为map类型
+ // 嵌套struct转换
if err := gconv.Struct(scores, user); err != nil {
fmt.Println(err)
} else {
diff --git a/geg/util/gconv/gconv_struct5.go b/geg/util/gconv/gconv_struct5.go
index 5e31d47e7..99dcb221b 100644
--- a/geg/util/gconv/gconv_struct5.go
+++ b/geg/util/gconv/gconv_struct5.go
@@ -17,19 +17,13 @@ func main() {
user := new(User)
scores := map[string]interface{}{
- "Scores" : []interface{}{
- map[string]interface{}{
- "Name" : "john",
- "Result" : 100,
- },
- map[string]interface{}{
- "Name" : "smith",
- "Result" : 60,
- },
+ "Scores" : map[string]interface{}{
+ "Name" : "john",
+ "Result" : 100,
},
}
- // 嵌套struct转换,属性为slice类型,数值为slice map类型
+ // 嵌套struct转换,属性为slice类型,数值为map类型
if err := gconv.Struct(scores, user); err != nil {
fmt.Println(err)
} else {
diff --git a/geg/util/gconv/gconv_struct6.go b/geg/util/gconv/gconv_struct6.go
new file mode 100644
index 000000000..5e31d47e7
--- /dev/null
+++ b/geg/util/gconv/gconv_struct6.go
@@ -0,0 +1,38 @@
+package main
+
+import (
+ "gitee.com/johng/gf/g/util/gconv"
+ "gitee.com/johng/gf/g"
+ "fmt"
+)
+
+func main() {
+ type Score struct {
+ Name string
+ Result int
+ }
+ type User struct {
+ Scores []Score
+ }
+
+ user := new(User)
+ scores := map[string]interface{}{
+ "Scores" : []interface{}{
+ map[string]interface{}{
+ "Name" : "john",
+ "Result" : 100,
+ },
+ map[string]interface{}{
+ "Name" : "smith",
+ "Result" : 60,
+ },
+ },
+ }
+
+ // 嵌套struct转换,属性为slice类型,数值为slice map类型
+ if err := gconv.Struct(scores, user); err != nil {
+ fmt.Println(err)
+ } else {
+ g.Dump(user)
+ }
+}
\ No newline at end of file
diff --git a/geg/util/gstr/gstr_hidestr.go b/geg/util/gstr/gstr_hidestr.go
new file mode 100644
index 000000000..3cc38d4f2
--- /dev/null
+++ b/geg/util/gstr/gstr_hidestr.go
@@ -0,0 +1,11 @@
+package main
+
+import (
+ "fmt"
+ "gitee.com/johng/gf/g/util/gstr"
+)
+
+func main() {
+ fmt.Println(gstr.HideStr("热爱GF热爱生活", 20, "*"))
+ fmt.Println(gstr.HideStr("热爱GF热爱生活", 50, "*"))
+}