mirror of
https://gitee.com/johng/gf
synced 2026-06-11 20:01:45 +08:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c07c4d7217 | |||
| b867b2a0bc | |||
| 872d674182 | |||
| 4682abafdf | |||
| b7d194cf52 | |||
| edf2366296 | |||
| 22af5be71f | |||
| f662ff8051 | |||
| 8c51121b3b | |||
| e9a0805801 | |||
| afadbc6621 | |||
| ca546fc30b | |||
| 7c7c168c3d | |||
| 16f0bb96db | |||
| 33a899d32e | |||
| f3a208f02f | |||
| 81fd3d06bb |
@ -2,23 +2,16 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
//db.SetDebug(true)
|
||||
db.SetDebug(true)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Name *gtime.Time
|
||||
}
|
||||
|
||||
user := new(User)
|
||||
e := db.Table("test").Where("id", 10000).Struct(user)
|
||||
one, e := db.Table("order.order o").LeftJoin("user.user u", "o.uid=u.id").Where("u.id", 1).One()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
g.Dump(user)
|
||||
g.Dump(one)
|
||||
|
||||
}
|
||||
|
||||
@ -5,4 +5,8 @@
|
||||
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
autoencode = true
|
||||
@ -1,14 +1,11 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Id int
|
||||
}
|
||||
u1 := &User{1}
|
||||
u2 := *u1
|
||||
u2.Id = 2
|
||||
fmt.Println(u1)
|
||||
fmt.Println(u2)
|
||||
fmt.Println(g.Cfg().FilePath())
|
||||
g.Cfg().Dump()
|
||||
}
|
||||
|
||||
21
README.MD
21
README.MD
@ -41,27 +41,6 @@ golang version >= 1.10
|
||||
<img src="https://goframe.org/images/arch.png?v=10"/>
|
||||
</div>
|
||||
|
||||
# Quick Start
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
[More Features...](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# License
|
||||
|
||||
|
||||
21
README_ZH.MD
21
README_ZH.MD
@ -51,27 +51,6 @@ golang版本 >= 1.11
|
||||
|
||||
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
|
||||
|
||||
# 使用
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write("Hello World")
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
[更多..](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
@ -57,7 +57,7 @@ func (v *Bool) Val() bool {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Bool) Cas(old, new bool) bool {
|
||||
func (v *Bool) Cas(old, new bool) (swapped bool) {
|
||||
var oldInt32, newInt32 int32
|
||||
if old {
|
||||
oldInt32 = 1
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Byte) Add(delta byte) (new byte) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Byte) Cas(old, new byte) bool {
|
||||
func (v *Byte) Cas(old, new byte) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt32(&v.value, int32(old), int32(new))
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ func (v *Float32) Add(delta float32) (new float32) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Float32) Cas(old, new float32) bool {
|
||||
func (v *Float32) Cas(old, new float32) (swapped bool) {
|
||||
return atomic.CompareAndSwapUint32(&v.value, math.Float32bits(old), math.Float32bits(new))
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ func (v *Float64) Add(delta float64) (new float64) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Float64) Cas(old, new float64) bool {
|
||||
func (v *Float64) Cas(old, new float64) (swapped bool) {
|
||||
return atomic.CompareAndSwapUint64(&v.value, math.Float64bits(old), math.Float64bits(new))
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Int) Add(delta int) (new int) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Int) Cas(old, new int) bool {
|
||||
func (v *Int) Cas(old, new int) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt64(&v.value, int64(old), int64(new))
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Int32) Add(delta int32) (new int32) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Int32) Cas(old, new int32) bool {
|
||||
func (v *Int32) Cas(old, new int32) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt32(&v.value, old, new)
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Int64) Add(delta int64) (new int64) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Int64) Cas(old, new int64) bool {
|
||||
func (v *Int64) Cas(old, new int64) (swapped bool) {
|
||||
return atomic.CompareAndSwapInt64(&v.value, old, new)
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Uint) Add(delta uint) (new uint) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Uint) Cas(old, new uint) bool {
|
||||
func (v *Uint) Cas(old, new uint) (swapped bool) {
|
||||
return atomic.CompareAndSwapUint64(&v.value, uint64(old), uint64(new))
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Uint32) Add(delta uint32) (new uint32) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Uint32) Cas(old, new uint32) bool {
|
||||
func (v *Uint32) Cas(old, new uint32) (swapped bool) {
|
||||
return atomic.CompareAndSwapUint32(&v.value, old, new)
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func (v *Uint64) Add(delta uint64) (new uint64) {
|
||||
}
|
||||
|
||||
// Cas executes the compare-and-swap operation for value.
|
||||
func (v *Uint64) Cas(old, new uint64) bool {
|
||||
func (v *Uint64) Cas(old, new uint64) (swapped bool) {
|
||||
return atomic.CompareAndSwapUint64(&v.value, old, new)
|
||||
}
|
||||
|
||||
|
||||
@ -49,6 +49,12 @@ func BenchmarkInt_Add(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInt_Cas(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
it.Cas(i, i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkInt32_Set(b *testing.B) {
|
||||
for i := int32(0); i < int32(b.N); i++ {
|
||||
it32.Set(i)
|
||||
|
||||
@ -77,6 +77,7 @@ type DB interface {
|
||||
// Create model.
|
||||
From(tables string) *Model
|
||||
Table(tables string) *Model
|
||||
Schema(schema string) *Schema
|
||||
|
||||
// Configuration methods.
|
||||
SetDebug(debug bool)
|
||||
|
||||
@ -52,7 +52,7 @@ var (
|
||||
)
|
||||
|
||||
// handleTableName adds prefix string and quote chars for the table. It handles table string like:
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut".
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut", "user as u, user_detail as ut", "user.user u".
|
||||
//
|
||||
// Note that, this will automatically checks the table prefix whether already added, if true it does
|
||||
// nothing to the table name, or else adds the prefix to the table name.
|
||||
@ -68,7 +68,7 @@ func doHandleTableName(table, prefix, charLeft, charRight string) string {
|
||||
array2[0] = prefix + array2[0]
|
||||
}
|
||||
// Add the security chars.
|
||||
array2[0] = doQuoteWord(array2[0], charLeft, charRight)
|
||||
array2[0] = doQuoteString(array2[0], charLeft, charRight)
|
||||
array1[k1] = gstr.Join(array2, " ")
|
||||
}
|
||||
return gstr.Join(array1, ",")
|
||||
@ -84,7 +84,8 @@ func doQuoteWord(s, charLeft, charRight string) string {
|
||||
}
|
||||
|
||||
// doQuoteString quotes string with quote chars. It handles strings like:
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut",
|
||||
// "user.user u, user.user_detail ut", "u.id asc".
|
||||
func doQuoteString(s, charLeft, charRight string) string {
|
||||
array1 := gstr.SplitAndTrim(s, ",")
|
||||
for k1, v1 := range array1 {
|
||||
@ -93,6 +94,7 @@ func doQuoteString(s, charLeft, charRight string) string {
|
||||
if len(array3) == 1 {
|
||||
array3[0] = doQuoteWord(array3[0], charLeft, charRight)
|
||||
} else if len(array3) == 2 {
|
||||
array3[0] = doQuoteWord(array3[0], charLeft, charRight)
|
||||
array3[1] = doQuoteWord(array3[1], charLeft, charRight)
|
||||
}
|
||||
array2[0] = gstr.Join(array3, ".")
|
||||
@ -236,7 +238,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
|
||||
if gstr.Pos(newWhere, "?") == -1 {
|
||||
if lastOperatorReg.MatchString(newWhere) {
|
||||
newWhere += "?"
|
||||
} else if quoteWordReg.MatchString(newWhere) {
|
||||
} else if gregex.IsMatchString(`^[\w\.\-]+$`, newWhere) {
|
||||
newWhere += "=?"
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ const (
|
||||
OPTION_ALLOWEMPTY
|
||||
)
|
||||
|
||||
// Table creates and returns a new ORM model.
|
||||
// Table creates and returns a new ORM model from given schema.
|
||||
// The parameter <tables> can be more than one table names, like :
|
||||
// "user", "user u", "user, user_detail", "user u, user_detail ud"
|
||||
func (bs *dbBase) Table(table string) *Model {
|
||||
@ -83,15 +83,15 @@ func (bs *dbBase) Table(table string) *Model {
|
||||
|
||||
// Model is alias of dbBase.Table.
|
||||
// See dbBase.Table.
|
||||
func (bs *dbBase) Model(tables string) *Model {
|
||||
return bs.db.Table(tables)
|
||||
func (bs *dbBase) Model(table string) *Model {
|
||||
return bs.db.Table(table)
|
||||
}
|
||||
|
||||
// From is alias of dbBase.Table.
|
||||
// See dbBase.Table.
|
||||
// Deprecated.
|
||||
func (bs *dbBase) From(tables string) *Model {
|
||||
return bs.db.Table(tables)
|
||||
func (bs *dbBase) From(table string) *Model {
|
||||
return bs.db.Table(table)
|
||||
}
|
||||
|
||||
// Table acts like dbBase.Table except it operates on transaction.
|
||||
@ -113,15 +113,25 @@ func (tx *TX) Table(table string) *Model {
|
||||
|
||||
// Model is alias of tx.Table.
|
||||
// See tx.Table.
|
||||
func (tx *TX) Model(tables string) *Model {
|
||||
return tx.Table(tables)
|
||||
func (tx *TX) Model(table string) *Model {
|
||||
return tx.Table(table)
|
||||
}
|
||||
|
||||
// From is alias of tx.Table.
|
||||
// See tx.Table.
|
||||
// Deprecated.
|
||||
func (tx *TX) From(tables string) *Model {
|
||||
return tx.Table(tables)
|
||||
func (tx *TX) From(table string) *Model {
|
||||
return tx.Table(table)
|
||||
}
|
||||
|
||||
// As sets an alias name for current table.
|
||||
func (m *Model) As(as string) *Model {
|
||||
if m.tables != "" {
|
||||
model := m.getModel()
|
||||
model.tables = gstr.TrimRight(model.tables) + " AS " + as
|
||||
return model
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// DB sets/changes the db object for current operation.
|
||||
@ -399,6 +409,9 @@ func (m *Model) Offset(offset int) *Model {
|
||||
// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
|
||||
func (m *Model) Page(page, limit int) *Model {
|
||||
model := m.getModel()
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
model.start = (page - 1) * limit
|
||||
model.limit = limit
|
||||
return model
|
||||
@ -831,16 +844,12 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
if len(where) > 0 {
|
||||
return m.Where(where[0], where[1:]...).Count()
|
||||
}
|
||||
defer func(fields string) {
|
||||
m.fields = fields
|
||||
}(m.fields)
|
||||
if m.fields == "" || m.fields == "*" {
|
||||
m.fields = "COUNT(1)"
|
||||
} else {
|
||||
m.fields = fmt.Sprintf(`COUNT(%s)`, m.fields)
|
||||
countFields := "COUNT(1)"
|
||||
if m.fields != "" && m.fields != "*" {
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
|
||||
}
|
||||
condition, conditionArgs := m.formatCondition(false)
|
||||
s := fmt.Sprintf("SELECT %s FROM %s %s", m.fields, m.tables, condition)
|
||||
s := fmt.Sprintf("SELECT %s FROM %s %s", countFields, m.tables, condition)
|
||||
if len(m.groupBy) > 0 {
|
||||
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
|
||||
}
|
||||
|
||||
51
database/gdb/gdb_schema.go
Normal file
51
database/gdb/gdb_schema.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
// Schema is a schema object from which it can then create a Model.
|
||||
type Schema struct {
|
||||
db DB
|
||||
tx *TX
|
||||
schema string
|
||||
}
|
||||
|
||||
// Schema creates and returns a schema.
|
||||
func (bs *dbBase) Schema(schema string) *Schema {
|
||||
return &Schema{
|
||||
db: bs.db,
|
||||
schema: schema,
|
||||
}
|
||||
}
|
||||
|
||||
// Schema creates and returns a initialization model from schema,
|
||||
// from which it can then create a Model.
|
||||
func (tx *TX) Schema(schema string) *Schema {
|
||||
return &Schema{
|
||||
tx: tx,
|
||||
schema: schema,
|
||||
}
|
||||
}
|
||||
|
||||
// Table creates and returns a new ORM model.
|
||||
// The parameter <tables> can be more than one table names, like :
|
||||
// "user", "user u", "user, user_detail", "user u, user_detail ud"
|
||||
func (s *Schema) Table(table string) *Model {
|
||||
var m *Model
|
||||
if s.tx != nil {
|
||||
m = s.tx.Table(table)
|
||||
} else {
|
||||
m = s.db.Table(table)
|
||||
}
|
||||
m.schema = s.schema
|
||||
return m
|
||||
}
|
||||
|
||||
// Model is alias of dbBase.Table.
|
||||
// See dbBase.Table.
|
||||
func (s *Schema) Model(table string) *Model {
|
||||
return s.Table(table)
|
||||
}
|
||||
@ -86,11 +86,19 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{}
|
||||
return gconv.Int(string(fieldValue))
|
||||
|
||||
case strings.Contains(t, "time"):
|
||||
t, _ := gtime.StrToTime(string(fieldValue))
|
||||
s := string(fieldValue)
|
||||
t, err := gtime.StrToTime(s)
|
||||
if err != nil {
|
||||
return s
|
||||
}
|
||||
return t.String()
|
||||
|
||||
case strings.Contains(t, "date"):
|
||||
t, _ := gtime.StrToTime(string(fieldValue))
|
||||
s := string(fieldValue)
|
||||
t, err := gtime.StrToTime(s)
|
||||
if err != nil {
|
||||
return s
|
||||
}
|
||||
return t.Format("Y-m-d")
|
||||
|
||||
default:
|
||||
|
||||
@ -32,12 +32,13 @@ func Test_Func_doQuoteString(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
|
||||
array := map[string]string{
|
||||
"user": "`user`",
|
||||
"user u": "`user` u",
|
||||
"user,user_detail": "`user`,`user_detail`",
|
||||
"user u, user_detail ut": "`user` u,`user_detail` ut",
|
||||
"u.id asc": "u.`id` asc",
|
||||
"u.id asc, ut.uid desc": "u.`id` asc,ut.`uid` desc",
|
||||
"user": "`user`",
|
||||
"user u": "`user` u",
|
||||
"user,user_detail": "`user`,`user_detail`",
|
||||
"user u, user_detail ut": "`user` u,`user_detail` ut",
|
||||
"u.id asc": "`u`.`id` asc",
|
||||
"u.id asc, ut.uid desc": "`u`.`id` asc,`ut`.`uid` desc",
|
||||
"user.user u, user.user_detail ut": "`user`.`user` u,`user`.`user_detail` ut",
|
||||
}
|
||||
for k, v := range array {
|
||||
gtest.Assert(doQuoteString(k, "`", "`"), v)
|
||||
|
||||
@ -1670,9 +1670,16 @@ func Test_Model_Prefix(t *testing.T) {
|
||||
gtest.Assert(r[0]["id"], "1")
|
||||
gtest.Assert(r[1]["id"], "2")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
r, err := db.Table(table).As("u1").LeftJoin(table+" as u2", "u2.id=u1.id").Where("u1.id in (?)", g.Slice{1, 2}).Order("u1.id asc").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(r), 2)
|
||||
gtest.Assert(r[0]["id"], "1")
|
||||
gtest.Assert(r[1]["id"], "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Schema(t *testing.T) {
|
||||
func Test_Model_Schema1(t *testing.T) {
|
||||
//db.SetDebug(true)
|
||||
|
||||
db.SetSchema(SCHEMA1)
|
||||
@ -1750,3 +1757,65 @@ func Test_Model_Schema(t *testing.T) {
|
||||
gtest.Assert(v.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Schema2(t *testing.T) {
|
||||
//db.SetDebug(true)
|
||||
|
||||
db.SetSchema(SCHEMA1)
|
||||
table := fmt.Sprintf(`%s_%s`, TABLE, gtime.TimestampNanoStr())
|
||||
createInitTableWithDb(db, table)
|
||||
db.SetSchema(SCHEMA2)
|
||||
createInitTableWithDb(db, table)
|
||||
defer func() {
|
||||
db.SetSchema(SCHEMA1)
|
||||
dropTableWithDb(db, table)
|
||||
db.SetSchema(SCHEMA2)
|
||||
dropTableWithDb(db, table)
|
||||
|
||||
db.SetSchema(SCHEMA1)
|
||||
}()
|
||||
// Schema.
|
||||
gtest.Case(t, func() {
|
||||
v, err := db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v.String(), "name_2")
|
||||
|
||||
r, err := db.Schema(SCHEMA1).Table(table).Update(g.Map{"nickname": "name_200"}, "id=2")
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
v, err = db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v.String(), "name_200")
|
||||
|
||||
v, err = db.Schema(SCHEMA2).Table(table).Value("nickname", "id=2")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v.String(), "name_2")
|
||||
|
||||
v, err = db.Schema(SCHEMA1).Table(table).Value("nickname", "id=2")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v.String(), "name_200")
|
||||
})
|
||||
// Schema.
|
||||
gtest.Case(t, func() {
|
||||
i := 1000
|
||||
_, err := db.Schema(SCHEMA1).Table(table).Filter().Insert(g.Map{
|
||||
"id": i,
|
||||
"passport": fmt.Sprintf(`user_%d`, i),
|
||||
"password": fmt.Sprintf(`pass_%d`, i),
|
||||
"nickname": fmt.Sprintf(`name_%d`, i),
|
||||
"create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(),
|
||||
"none-exist-field": 1,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
v, err := db.Schema(SCHEMA1).Table(table).Value("nickname", "id=?", i)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v.String(), "name_1000")
|
||||
|
||||
v, err = db.Schema(SCHEMA2).Table(table).Value("nickname", "id=?", i)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ func Test_Types(t *testing.T) {
|
||||
%s blob NOT NULL,
|
||||
%s binary(8) NOT NULL,
|
||||
%s date NOT NULL,
|
||||
%s time NOT NULL,
|
||||
%s decimal(5,2) NOT NULL,
|
||||
%s double NOT NULL,
|
||||
%s bit(2) NOT NULL,
|
||||
@ -31,7 +32,8 @@ func Test_Types(t *testing.T) {
|
||||
%s bool NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, "`blob`", "`binary`", "`date`", "`decimal`", "`double`", "`bit`", "`tinyint`", "`bool`")); err != nil {
|
||||
`, "`blob`", "`binary`", "`date`", "`time`",
|
||||
"`decimal`", "`double`", "`bit`", "`tinyint`", "`bool`")); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable("types")
|
||||
@ -40,6 +42,7 @@ func Test_Types(t *testing.T) {
|
||||
"blob": "i love gf",
|
||||
"binary": []byte("abcdefgh"),
|
||||
"date": "2018-10-24",
|
||||
"time": "10:00:01",
|
||||
"decimal": 123.456,
|
||||
"double": 123.456,
|
||||
"bit": 2,
|
||||
@ -57,6 +60,7 @@ func Test_Types(t *testing.T) {
|
||||
gtest.Assert(one["blob"].String(), data["blob"])
|
||||
gtest.Assert(one["binary"].String(), data["binary"])
|
||||
gtest.Assert(one["date"].String(), data["date"])
|
||||
gtest.Assert(one["time"].String(), data["time"])
|
||||
gtest.Assert(one["decimal"].String(), 123.46)
|
||||
gtest.Assert(one["double"].String(), data["double"])
|
||||
gtest.Assert(one["bit"].Int(), data["bit"])
|
||||
|
||||
@ -6,78 +6,3 @@
|
||||
|
||||
// Package gcompress provides kinds of compression algorithms for binary/bytes data.
|
||||
package gcompress
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Zlib compresses <data> with zlib algorithm.
|
||||
func Zlib(data []byte) ([]byte, error) {
|
||||
if data == nil || len(data) < 13 {
|
||||
return data, nil
|
||||
}
|
||||
var in bytes.Buffer
|
||||
var err error
|
||||
w := zlib.NewWriter(&in)
|
||||
if _, err = w.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = w.Close(); err != nil {
|
||||
return in.Bytes(), err
|
||||
}
|
||||
return in.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnZlib decompresses <data> with zlib algorithm.
|
||||
func UnZlib(data []byte) ([]byte, error) {
|
||||
if data == nil || len(data) < 13 {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
b := bytes.NewReader(data)
|
||||
var out bytes.Buffer
|
||||
var err error
|
||||
r, err := zlib.NewReader(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = io.Copy(&out, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
// Gzip compresses <data> with gzip algorithm.
|
||||
func Gzip(data []byte) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
var err error
|
||||
zip := gzip.NewWriter(&buf)
|
||||
_, err = zip.Write(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = zip.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnGzip decompresses <data> with gzip algorithm.
|
||||
func UnGzip(data []byte) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
content := bytes.NewReader(data)
|
||||
zipData, err := gzip.NewReader(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = io.Copy(&buf, zipData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = zipData.Close(); err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
55
encoding/gcompress/gcompress_gzip.go
Normal file
55
encoding/gcompress/gcompress_gzip.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
package gcompress
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Gzip compresses <data> with gzip algorithm.
|
||||
// The optional parameter <level> specifies the compression level from
|
||||
// 1 to 9 which means from none to the best compression.
|
||||
//
|
||||
// Note that it returns error if given <level> is invalid.
|
||||
func Gzip(data []byte, level ...int) ([]byte, error) {
|
||||
var writer *gzip.Writer
|
||||
var buf bytes.Buffer
|
||||
var err error
|
||||
if len(level) > 0 {
|
||||
writer, err = gzip.NewWriterLevel(&buf, level[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
writer = gzip.NewWriter(&buf)
|
||||
}
|
||||
if _, err = writer.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = writer.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnGzip decompresses <data> with gzip algorithm.
|
||||
func UnGzip(data []byte) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
reader, err := gzip.NewReader(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = io.Copy(&buf, reader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = reader.Close(); err != nil {
|
||||
return buf.Bytes(), err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
@ -24,7 +24,8 @@ import (
|
||||
// ZipPath compresses <paths> to <dest> using zip compressing algorithm.
|
||||
// The unnecessary parameter <prefix> indicates the path prefix for zip file.
|
||||
//
|
||||
// Note that parameter <paths> supports multiple paths join with ','.
|
||||
// Note that the parameter <paths> can be either a directory or a file, which
|
||||
// supports multiple paths join with ','.
|
||||
func ZipPath(paths, dest string, prefix ...string) error {
|
||||
writer, err := os.Create(dest)
|
||||
if err != nil {
|
||||
@ -37,7 +38,8 @@ func ZipPath(paths, dest string, prefix ...string) error {
|
||||
// ZipPathWriter compresses <paths> to <writer> using zip compressing algorithm.
|
||||
// The unnecessary parameter <prefix> indicates the path prefix for zip file.
|
||||
//
|
||||
// Note that parameter <paths> supports multiple paths join with ','.
|
||||
// Note that the parameter <paths> can be either a directory or a file, which
|
||||
// supports multiple paths join with ','.
|
||||
func ZipPathWriter(paths string, writer io.Writer, prefix ...string) error {
|
||||
zipWriter := zip.NewWriter(writer)
|
||||
defer zipWriter.Close()
|
||||
@ -51,24 +53,30 @@ func ZipPathWriter(paths string, writer io.Writer, prefix ...string) error {
|
||||
}
|
||||
|
||||
func doZipPathWriter(path string, zipWriter *zip.Writer, prefix ...string) error {
|
||||
var err error
|
||||
var files []string
|
||||
realPath, err := gfile.Search(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
files, err := gfile.ScanDir(path, "*", true)
|
||||
if err != nil {
|
||||
return err
|
||||
if gfile.IsDir(path) {
|
||||
files, err = gfile.ScanDir(path, "*", true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
files = []string{path}
|
||||
}
|
||||
headerPrefix := ""
|
||||
if len(prefix) > 0 && prefix[0] != "" {
|
||||
headerPrefix = prefix[0]
|
||||
}
|
||||
headerPrefix = strings.TrimRight(headerPrefix, "\\/")
|
||||
if gfile.IsDir(path) {
|
||||
if len(headerPrefix) > 0 {
|
||||
headerPrefix += "/"
|
||||
}
|
||||
headerPrefix = headerPrefix + gfile.Basename(path)
|
||||
if len(headerPrefix) > 0 && gfile.IsDir(path) {
|
||||
headerPrefix += "/"
|
||||
}
|
||||
if headerPrefix == "" {
|
||||
headerPrefix = gfile.Basename(path)
|
||||
}
|
||||
headerPrefix = strings.Replace(headerPrefix, "//", "/", -1)
|
||||
for _, file := range files {
|
||||
50
encoding/gcompress/gcompress_zlib.go
Normal file
50
encoding/gcompress/gcompress_zlib.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
// Package gcompress provides kinds of compression algorithms for binary/bytes data.
|
||||
package gcompress
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Zlib compresses <data> with zlib algorithm.
|
||||
func Zlib(data []byte) ([]byte, error) {
|
||||
if data == nil || len(data) < 13 {
|
||||
return data, nil
|
||||
}
|
||||
var in bytes.Buffer
|
||||
var err error
|
||||
w := zlib.NewWriter(&in)
|
||||
if _, err = w.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = w.Close(); err != nil {
|
||||
return in.Bytes(), err
|
||||
}
|
||||
return in.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnZlib decompresses <data> with zlib algorithm.
|
||||
func UnZlib(data []byte) ([]byte, error) {
|
||||
if data == nil || len(data) < 13 {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
b := bytes.NewReader(data)
|
||||
var out bytes.Buffer
|
||||
var err error
|
||||
r, err := zlib.NewReader(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err = io.Copy(&out, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
// Package utibytes provides some bytes functions for internal usage.
|
||||
package utilbytes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Export(b []byte) string {
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteString(`[]byte("`)
|
||||
for _, v := range b {
|
||||
fmt.Fprintf(buffer, `\x%02x`, v)
|
||||
}
|
||||
buffer.WriteString(`")`)
|
||||
return buffer.String()
|
||||
}
|
||||
@ -12,7 +12,9 @@ import "github.com/gogf/gf/container/gvar"
|
||||
// It returns <def> if <key> does not exist.
|
||||
func (r *Request) GetRouterValue(key string, def ...interface{}) interface{} {
|
||||
if r.routerMap != nil {
|
||||
return r.routerMap[key]
|
||||
if v, ok := r.routerMap[key]; ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
|
||||
@ -90,16 +90,24 @@ func (r *Response) ServeFileDownload(path string, name ...string) {
|
||||
r.Server.serveFile(r.Request, serveFile)
|
||||
}
|
||||
|
||||
// RedirectTo redirects client to another location using http status 302.
|
||||
func (r *Response) RedirectTo(location string) {
|
||||
// RedirectTo redirects client to another location.
|
||||
// The optional parameter <code> specifies the http status code for redirecting,
|
||||
// which commonly can be 301 or 302. It's 302 in default.
|
||||
func (r *Response) RedirectTo(location string, code ...int) {
|
||||
r.Header().Set("Location", location)
|
||||
r.WriteHeader(http.StatusFound)
|
||||
if len(code) > 0 {
|
||||
r.WriteHeader(code[0])
|
||||
} else {
|
||||
r.WriteHeader(http.StatusFound)
|
||||
}
|
||||
r.Request.Exit()
|
||||
}
|
||||
|
||||
// RedirectBack redirects client back to referer using http status 302.
|
||||
func (r *Response) RedirectBack() {
|
||||
r.RedirectTo(r.Request.GetReferer())
|
||||
// RedirectBack redirects client back to referer.
|
||||
// The optional parameter <code> specifies the http status code for redirecting,
|
||||
// which commonly can be 301 or 302. It's 302 in default.
|
||||
func (r *Response) RedirectBack(code ...int) {
|
||||
r.RedirectTo(r.Request.GetReferer(), code...)
|
||||
}
|
||||
|
||||
// BufferString returns the buffered content as []byte.
|
||||
|
||||
@ -20,28 +20,36 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// 静态文件目录映射关系对象
|
||||
// staticPathItem is the item struct for static path configuration.
|
||||
type staticPathItem struct {
|
||||
prefix string // 映射的URI前缀
|
||||
path string // 静态文件目录绝对路径
|
||||
prefix string // The router URI.
|
||||
path string // The static path.
|
||||
}
|
||||
|
||||
// 设置http server参数 - IndexFiles,默认展示文件,如:index.html, index.htm
|
||||
func (s *Server) SetIndexFiles(index []string) {
|
||||
s.config.IndexFiles = index
|
||||
// SetIndexFiles sets the index files for server.
|
||||
func (s *Server) SetIndexFiles(indexFiles []string) {
|
||||
s.config.IndexFiles = indexFiles
|
||||
}
|
||||
|
||||
// 允许展示访问目录的文件列表
|
||||
// GetIndexFiles retrieves and returns the index files from server.
|
||||
func (s *Server) GetIndexFiles() []string {
|
||||
return s.config.IndexFiles
|
||||
}
|
||||
|
||||
// SetIndexFolder enables/disables listing the sub-files if requesting a directory.
|
||||
func (s *Server) SetIndexFolder(enabled bool) {
|
||||
s.config.IndexFolder = enabled
|
||||
}
|
||||
|
||||
// 是否开启/关闭静态文件服务,当关闭时仅提供动态接口服务,路由性能会得到一定提升
|
||||
// SetFileServerEnabled enables/disables the static file service.
|
||||
// It's the main switch for the static file service. When static file service configuration
|
||||
// functions like SetServerRoot, AddSearchPath and AddStaticPath are called, this configuration
|
||||
// is automatically enabled.
|
||||
func (s *Server) SetFileServerEnabled(enabled bool) {
|
||||
s.config.FileServerEnabled = enabled
|
||||
}
|
||||
|
||||
// 设置http server参数 - ServerRoot
|
||||
// SetServerRoot sets the document root for static service.
|
||||
func (s *Server) SetServerRoot(root string) {
|
||||
realPath := root
|
||||
if !gres.Contains(realPath) {
|
||||
@ -56,7 +64,7 @@ func (s *Server) SetServerRoot(root string) {
|
||||
s.config.FileServerEnabled = true
|
||||
}
|
||||
|
||||
// 添加静态文件搜索**目录**,必须给定目录的绝对路径
|
||||
// AddSearchPath add searching directory path for static file service.
|
||||
func (s *Server) AddSearchPath(path string) {
|
||||
realPath := path
|
||||
if !gres.Contains(realPath) {
|
||||
@ -70,7 +78,7 @@ func (s *Server) AddSearchPath(path string) {
|
||||
s.config.FileServerEnabled = true
|
||||
}
|
||||
|
||||
// 添加URI与静态**目录**的映射
|
||||
// AddStaticPath sets the uri to static directory path mapping for static file service.
|
||||
func (s *Server) AddStaticPath(prefix string, path string) {
|
||||
realPath := path
|
||||
if !gres.Contains(realPath) {
|
||||
|
||||
@ -24,11 +24,11 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// 服务静态文件信息
|
||||
// staticServeFile is the file struct for static service.
|
||||
type staticServeFile struct {
|
||||
file *gres.File // 资源文件
|
||||
path string // 文件路径
|
||||
dir bool // 是否目录
|
||||
file *gres.File // Resource file object.
|
||||
path string // File path.
|
||||
dir bool // Is directory.
|
||||
}
|
||||
|
||||
// 默认HTTP Server处理入口,http包底层默认使用了gorutine异步处理请求,所以这里不再异步执行
|
||||
@ -172,7 +172,8 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// 查找静态文件的绝对路径
|
||||
// searchStaticFile searches the file with given URI.
|
||||
// It returns a file struct specifying the file information.
|
||||
func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
var file *gres.File
|
||||
var path string
|
||||
@ -185,7 +186,6 @@ func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
if len(uri) > len(item.prefix) && uri[len(item.prefix)] != '/' {
|
||||
continue
|
||||
}
|
||||
// Firstly searching resource manager.
|
||||
file = gres.GetWithIndex(item.path+uri[len(item.prefix):], s.config.IndexFiles)
|
||||
if file != nil {
|
||||
return &staticServeFile{
|
||||
@ -193,7 +193,6 @@ func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
dir: file.FileInfo().IsDir(),
|
||||
}
|
||||
}
|
||||
// Secondly searching the file system.
|
||||
path, dir = gspath.Search(item.path, uri[len(item.prefix):], s.config.IndexFiles...)
|
||||
if path != "" {
|
||||
return &staticServeFile{
|
||||
@ -205,10 +204,9 @@ func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 其次查找root和search path
|
||||
// Secondly search the root and searching paths.
|
||||
if len(s.config.SearchPaths) > 0 {
|
||||
for _, p := range s.config.SearchPaths {
|
||||
// 优先检索资源管理器
|
||||
file = gres.GetWithIndex(p+uri, s.config.IndexFiles)
|
||||
if file != nil {
|
||||
return &staticServeFile{
|
||||
@ -216,7 +214,6 @@ func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
dir: file.FileInfo().IsDir(),
|
||||
}
|
||||
}
|
||||
// 其次检索文件系统
|
||||
if path, dir = gspath.Search(p, uri, s.config.IndexFiles...); path != "" {
|
||||
return &staticServeFile{
|
||||
path: path,
|
||||
@ -225,7 +222,7 @@ func (s *Server) searchStaticFile(uri string) *staticServeFile {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 最后通过资源对象+URI进行文件检索
|
||||
// Lastly search the resource manager.
|
||||
if len(s.config.StaticPaths) == 0 && len(s.config.SearchPaths) == 0 {
|
||||
if file = gres.GetWithIndex(uri, s.config.IndexFiles); file != nil {
|
||||
return &staticServeFile{
|
||||
|
||||
60
net/ghttp/ghttp_unit_pprof_test.go
Normal file
60
net/ghttp/ghttp_unit_pprof_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/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://github.com/gogf/gf.
|
||||
|
||||
// static service testing.
|
||||
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
. "github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func TestServer_EnablePProf(t *testing.T) {
|
||||
Case(t, func() {
|
||||
p := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.EnablePProf("/pprof")
|
||||
s.SetDumpRouterMap(false)
|
||||
s.SetPort(p)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
client := ghttp.NewClient()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
|
||||
r, err := client.Get("/pprof/index")
|
||||
Assert(err, nil)
|
||||
Assert(r.StatusCode, 200)
|
||||
r.Close()
|
||||
|
||||
r, err = client.Get("/pprof/cmdline")
|
||||
Assert(err, nil)
|
||||
Assert(r.StatusCode, 200)
|
||||
r.Close()
|
||||
|
||||
//r, err = client.Get("/pprof/profile")
|
||||
//Assert(err, nil)
|
||||
//Assert(r.StatusCode, 200)
|
||||
//r.Close()
|
||||
|
||||
r, err = client.Get("/pprof/symbol")
|
||||
Assert(err, nil)
|
||||
Assert(r.StatusCode, 200)
|
||||
r.Close()
|
||||
|
||||
r, err = client.Get("/pprof/trace")
|
||||
Assert(err, nil)
|
||||
Assert(r.StatusCode, 200)
|
||||
r.Close()
|
||||
})
|
||||
|
||||
}
|
||||
@ -63,18 +63,18 @@ func GetNameByAddr(ipAddress string) (string, error) {
|
||||
}
|
||||
|
||||
// Ip2long converts ip address to an uint32 integer.
|
||||
func Ip2long(ipAddress string) uint32 {
|
||||
ip := net.ParseIP(ipAddress)
|
||||
if ip == nil {
|
||||
func Ip2long(ip string) uint32 {
|
||||
netIp := net.ParseIP(ip)
|
||||
if netIp == nil {
|
||||
return 0
|
||||
}
|
||||
return binary.BigEndian.Uint32(ip.To4())
|
||||
return binary.BigEndian.Uint32(netIp.To4())
|
||||
}
|
||||
|
||||
// Long2ip converts an uint32 integer ip address to its string type address.
|
||||
func Long2ip(properAddress uint32) string {
|
||||
func Long2ip(long uint32) string {
|
||||
ipByte := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(ipByte, properAddress)
|
||||
binary.BigEndian.PutUint32(ipByte, long)
|
||||
return net.IP(ipByte).String()
|
||||
}
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ func Parse(supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
//
|
||||
// The optional parameter <strict> specifies whether stops parsing and returns error if invalid option passed.
|
||||
func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bool) (*Parser, error) {
|
||||
strictParsing := true
|
||||
strictParsing := false
|
||||
if len(strict) > 0 {
|
||||
strictParsing = strict[0]
|
||||
}
|
||||
@ -77,7 +77,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo
|
||||
} else {
|
||||
if parser.isOptionValid(option) {
|
||||
if parser.isOptionNeedArgument(option) {
|
||||
if i+1 < len(args) {
|
||||
if i < len(args)-1 {
|
||||
parser.setOptionValue(option, args[i+1])
|
||||
i += 2
|
||||
continue
|
||||
@ -95,7 +95,7 @@ func ParseWithArgs(args []string, supportedOptions map[string]bool, strict ...bo
|
||||
}
|
||||
i++
|
||||
continue
|
||||
} else {
|
||||
} else if parser.strict {
|
||||
return nil, errors.New(fmt.Sprintf(`invalid option '%s'`, args[i]))
|
||||
}
|
||||
}
|
||||
@ -136,9 +136,6 @@ func (p *Parser) parseOption(argument string) string {
|
||||
}
|
||||
|
||||
func (p *Parser) isOptionValid(name string) bool {
|
||||
if !p.strict {
|
||||
return true
|
||||
}
|
||||
_, ok := p.supportedOptions[name]
|
||||
return ok
|
||||
}
|
||||
@ -174,8 +171,14 @@ func (p *Parser) GetOpt(name string, def ...string) string {
|
||||
}
|
||||
|
||||
// GetOptVar returns the option value named <name> as *gvar.Var.
|
||||
func (p *Parser) GetOptVar(name string, def ...string) *gvar.Var {
|
||||
return gvar.New(p.GetOpt(name, def...))
|
||||
func (p *Parser) GetOptVar(name string, def ...interface{}) *gvar.Var {
|
||||
if p.ContainsOpt(name) {
|
||||
return gvar.New(p.GetOpt(name))
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return gvar.New(def[0])
|
||||
}
|
||||
return gvar.New(nil)
|
||||
}
|
||||
|
||||
// GetOptAll returns all parsed options.
|
||||
@ -184,7 +187,7 @@ func (p *Parser) GetOptAll() map[string]string {
|
||||
}
|
||||
|
||||
// ContainsOpt checks whether option named <name> exist in the arguments.
|
||||
func (p *Parser) ContainsOpt(name string, def ...string) bool {
|
||||
func (p *Parser) ContainsOpt(name string) bool {
|
||||
_, ok := p.parsedOptions[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
@ -60,7 +60,14 @@ func Build(m map[string]string) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Remove deletes a single environment variable.
|
||||
func Remove(key string) error {
|
||||
return os.Unsetenv(key)
|
||||
// Remove deletes one or more environment variables.
|
||||
func Remove(key ...string) error {
|
||||
var err error
|
||||
for _, v := range key {
|
||||
err = os.Unsetenv(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -15,6 +15,9 @@ import (
|
||||
|
||||
// ScanDir returns all sub-files with absolute paths of given <path>,
|
||||
// It scans directory recursively if given parameter <recursive> is true.
|
||||
//
|
||||
// The pattern parameter <pattern> supports multiple file name patterns,
|
||||
// using the ',' symbol to separate multiple patterns.
|
||||
func ScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
|
||||
isRecursive := false
|
||||
if len(recursive) > 0 {
|
||||
@ -33,6 +36,9 @@ func ScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
|
||||
// ScanDirFile returns all sub-files with absolute paths of given <path>,
|
||||
// It scans directory recursively if given parameter <recursive> is true.
|
||||
//
|
||||
// The pattern parameter <pattern> supports multiple file name patterns,
|
||||
// using the ',' symbol to separate multiple patterns.
|
||||
//
|
||||
// Note that it returns only files, exclusive of directories.
|
||||
func ScanDirFile(path string, pattern string, recursive ...bool) ([]string, error) {
|
||||
isRecursive := false
|
||||
|
||||
@ -9,6 +9,7 @@ package gproc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/os/genv"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"io"
|
||||
"os"
|
||||
@ -191,18 +192,23 @@ func SearchBinary(file string) string {
|
||||
array := ([]string)(nil)
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
array = gstr.SplitAndTrim(os.Getenv("Path"), ";")
|
||||
envPath := genv.Get("PATH", genv.Get("Path"))
|
||||
if gstr.Contains(envPath, ";") {
|
||||
array = gstr.SplitAndTrim(envPath, ";")
|
||||
} else if gstr.Contains(envPath, ":") {
|
||||
array = gstr.SplitAndTrim(envPath, ":")
|
||||
}
|
||||
if gfile.Ext(file) != ".exe" {
|
||||
file += ".exe"
|
||||
}
|
||||
default:
|
||||
array = gstr.SplitAndTrim(os.Getenv("PATH"), ":")
|
||||
array = gstr.SplitAndTrim(genv.Get("PATH"), ":")
|
||||
}
|
||||
if len(array) > 0 {
|
||||
path := ""
|
||||
for _, v := range array {
|
||||
path = v + gfile.Separator + file
|
||||
if gfile.Exists(path) {
|
||||
if gfile.Exists(path) && gfile.IsFile(path) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -115,7 +116,9 @@ func (p *Process) Kill() error {
|
||||
if p.Manager != nil {
|
||||
p.Manager.processes.Remove(p.Pid())
|
||||
}
|
||||
p.Process.Release()
|
||||
if runtime.GOOS != "windows" {
|
||||
p.Process.Release()
|
||||
}
|
||||
p.Process.Wait()
|
||||
return nil
|
||||
} else {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -20,7 +20,7 @@ var (
|
||||
// Add unpacks and adds the <content> into the default resource object.
|
||||
// The unnecessary parameter <prefix> indicates the prefix
|
||||
// for each file storing into current resource object.
|
||||
func Add(content []byte, prefix ...string) error {
|
||||
func Add(content string, prefix ...string) error {
|
||||
return defaultResource.Add(content, prefix...)
|
||||
}
|
||||
|
||||
|
||||
@ -9,10 +9,11 @@ package gres
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/encoding/gcompress"
|
||||
"github.com/gogf/gf/internal/utilbytes"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
)
|
||||
|
||||
@ -22,7 +23,7 @@ const (
|
||||
import "github.com/gogf/gf/os/gres"
|
||||
|
||||
func init() {
|
||||
if err := gres.Add(%s); err != nil {
|
||||
if err := gres.Add("%s"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -44,7 +45,8 @@ func Pack(srcPaths string, keyPrefix ...string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
// Gzip the data bytes to reduce the size.
|
||||
return gcompress.Gzip(buffer.Bytes(), 9)
|
||||
}
|
||||
|
||||
// PackToFile packs the path specified by <srcPaths> to target file <dstPath>.
|
||||
@ -73,7 +75,8 @@ func PackToGoFile(srcPath, goFilePath, pkgName string, keyPrefix ...string) erro
|
||||
return err
|
||||
}
|
||||
return gfile.PutContents(
|
||||
goFilePath, fmt.Sprintf(gPACKAGE_TEMPLATE, pkgName, utilbytes.Export(data)),
|
||||
goFilePath,
|
||||
fmt.Sprintf(gPACKAGE_TEMPLATE, pkgName, bytesToHexStr(data)),
|
||||
)
|
||||
}
|
||||
|
||||
@ -83,12 +86,16 @@ func Unpack(path string) ([]*File, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return UnpackContent(gfile.GetBytes(realPath))
|
||||
return UnpackContent(gfile.GetContents(realPath))
|
||||
}
|
||||
|
||||
// UnpackContent unpacks the content to []*File.
|
||||
func UnpackContent(content []byte) ([]*File, error) {
|
||||
reader, err := zip.NewReader(bytes.NewReader(content), int64(len(content)))
|
||||
func UnpackContent(content string) ([]*File, error) {
|
||||
data, err := gcompress.UnGzip(hexStrToBytes(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -98,3 +105,18 @@ func UnpackContent(content []byte) ([]*File, error) {
|
||||
}
|
||||
return array, nil
|
||||
}
|
||||
|
||||
// bytesToHexString converts binary content to hex string content.
|
||||
func bytesToHexStr(b []byte) string {
|
||||
dst := make([]byte, hex.EncodedLen(len(b)))
|
||||
hex.Encode(dst, b)
|
||||
return gconv.UnsafeBytesToStr(dst)
|
||||
}
|
||||
|
||||
// hexStrToBytes converts hex string content to []byte.
|
||||
func hexStrToBytes(s string) []byte {
|
||||
src := gconv.UnsafeStrToBytes(s)
|
||||
dst := make([]byte, hex.DecodedLen(len(src)))
|
||||
hex.Decode(dst, src)
|
||||
return dst
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ func New() *Resource {
|
||||
// Add unpacks and adds the <content> into current resource object.
|
||||
// The unnecessary parameter <prefix> indicates the prefix
|
||||
// for each file storing into current resource object.
|
||||
func (r *Resource) Add(content []byte, prefix ...string) error {
|
||||
func (r *Resource) Add(content string, prefix ...string) error {
|
||||
files, err := UnpackContent(content)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -62,7 +62,7 @@ func (r *Resource) Load(path string, prefix ...string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Add(gfile.GetBytes(realPath), prefix...)
|
||||
return r.Add(gfile.GetContents(realPath), prefix...)
|
||||
}
|
||||
|
||||
// Get returns the file with given path.
|
||||
|
||||
2
os/gres/testdata/data/data.go
vendored
2
os/gres/testdata/data/data.go
vendored
File diff suppressed because one or more lines are too long
2
os/gres/testdata/testdata.go
vendored
2
os/gres/testdata/testdata.go
vendored
File diff suppressed because one or more lines are too long
@ -47,6 +47,14 @@ func Test_NewFromStrFormat(t *testing.T) {
|
||||
t.Error("test fail")
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
t1 := gtime.NewFromStrFormat("2019/2/1", "Y/n/j")
|
||||
gtest.Assert(t1.Format("Y-m-d"), "2019-02-01")
|
||||
|
||||
t2 := gtime.NewFromStrFormat("2019/10/12", "Y/n/j")
|
||||
gtest.Assert(t2.Format("Y-m-d"), "2019-10-12")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_NewFromStrLayout(t *testing.T) {
|
||||
|
||||
@ -16,6 +16,8 @@ import (
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
htmltpl "html/template"
|
||||
)
|
||||
|
||||
// funcDump implements build-in template function: dump
|
||||
@ -85,7 +87,8 @@ func (view *View) funcGe(value, other interface{}) bool {
|
||||
}
|
||||
|
||||
// funcInclude implements build-in template function: include
|
||||
func (view *View) funcInclude(file interface{}, data ...map[string]interface{}) string {
|
||||
// Note that configuration AutoEncode does not affect the output of this function.
|
||||
func (view *View) funcInclude(file interface{}, data ...map[string]interface{}) htmltpl.HTML {
|
||||
var m map[string]interface{} = nil
|
||||
if len(data) > 0 {
|
||||
m = data[0]
|
||||
@ -97,9 +100,9 @@ func (view *View) funcInclude(file interface{}, data ...map[string]interface{})
|
||||
// It will search the file internally.
|
||||
content, err := view.Parse(path, m)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
return htmltpl.HTML(err.Error())
|
||||
}
|
||||
return content
|
||||
return htmltpl.HTML(content)
|
||||
}
|
||||
|
||||
// funcText implements build-in template function: text
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.10.1"
|
||||
const VERSION = "v1.11.3"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user