Compare commits

..

17 Commits

Author SHA1 Message Date
c07c4d7217 version updates 2020-01-16 21:26:34 +08:00
b867b2a0bc add return parameter name for function Cas of gtype;improve Response.Redirect* functions by adding optional parameter code 2020-01-16 21:04:28 +08:00
872d674182 fix issue in database 'time' type support in package gdb 2020-01-15 21:23:40 +08:00
4682abafdf fix concurrent issue in gdb.Model.Count 2020-01-15 10:38:02 +08:00
b7d194cf52 improva gcmd.Parser/gres 2020-01-15 09:36:58 +08:00
edf2366296 improve gzip feature for gcompress; add gzip compression for package gres 2020-01-15 00:15:56 +08:00
22af5be71f rename parameter name for gipv4.Ip2Long/Long2Ip 2020-01-13 14:50:06 +08:00
f662ff8051 add pprof unit testing case for ghttp; reame updates 2020-01-12 22:26:07 +08:00
8c51121b3b version updates 2020-01-11 10:40:13 +08:00
e9a0805801 add function Schema for gdb 2020-01-10 23:48:19 +08:00
afadbc6621 improve genv.Remove/gproc.Kill 2020-01-10 22:32:07 +08:00
ca546fc30b why the goland auto removed my genv import? 2020-01-10 09:39:54 +08:00
7c7c168c3d improve gproc.SearchBinary 2020-01-09 23:05:03 +08:00
16f0bb96db improve strict parsing feature for gcmd 2020-01-08 23:06:01 +08:00
33a899d32e add As function for gdb.Model; improve string quote handling for gdb 2020-01-08 21:24:33 +08:00
f3a208f02f fix issue in router value retrieving for ghttp.Request 2020-01-08 20:00:42 +08:00
81fd3d06bb make parser default unstrict for gcmd 2020-01-08 19:30:56 +08:00
49 changed files with 529 additions and 287 deletions

View File

@ -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)
}

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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%`开源免费。

View File

@ -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

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)

View File

@ -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 += "=?"
}
}

View File

@ -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)
}

View 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)
}

View File

@ -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:

View File

@ -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)

View File

@ -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(), "")
})
}

View File

@ -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"])

View File

@ -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
}

View 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
}

View File

@ -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 {

View 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
}

View File

@ -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()
}

View File

@ -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]

View File

@ -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.

View File

@ -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) {

View File

@ -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{

View 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()
})
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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

View File

@ -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...)
}

View File

@ -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
}

View File

@ -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.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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) {

View File

@ -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

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.10.1"
const VERSION = "v1.11.3"
const AUTHORS = "john<john@goframe.org>"