加入收藏 | 设为首页 | 会员中心 | 我要投稿 源码门户网 (https://www.92codes.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

PHP字符串变量 template 全面讲解

发布时间:2022-09-30 04:03:26 所属栏目:PHP教程 来源:网络
导读: 什么是go - template
你可以把它理解为Java的jsp,理解为js的ejs,vue的template,等等就是一个模版,所以就是需要了解一些模版的语法,以及渲染机制等,本文基本覆盖的很全面,模版其实核心

什么是go - template

你可以把它理解为Java的jsp,理解为js的ejs,vue的template,等等就是一个模版,所以就是需要了解一些模版的语法,以及渲染机制等,本文基本覆盖的很全面,模版其实核心是理解:1、基本语法,2、自定义func使用和内置func使用,3、变量的使用,4、模版复用等机制。

如果你掌握以上我说的,那么开发一个模版工具,是很轻松的,比如orm通用代码,其他工具等。

以下就是个demo:

func TestTemplateString(t *testing.T) {
    tmpl := "my name is {{.}}"
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, "anthony")
}
// my name is anthony

所以需要模板渲染的部分都需要加入{{}} , 有些部分操作需要加入{{end}} 作为标识符等。

简单语法学习{{.}}

可以展示任何数据,各种类型的数据,可以理解为接收类型是interface{}

func TestTemplateString(t *testing.T) {
    tmpl := "my name is {{.}}"
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, []string{"hao", "dong"})
}
=== RUN   TestTemplateString
my name is [hao dong]--- PASS: TestTemplateString (0.00s)
PASS

{{.Field}}

func TestTemplateStruct(t *testing.T) {
    tmpl := `my name is "{{.Name}}"`
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, struct{ Name string }{Name: "anthony"})
}

{{/* a comment */}}

? 给go文件加入注释

func TestTemplateCommon(t *testing.T) {
    tmpl := `{{/* a comment */}}`
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, struct{ Name string }{Name: "anthony"})
}
// 什么也不输出

{{-content-}}

去除前后空格,超级方便好用

func TestTemplateTrim(t *testing.T) {
    tmpl := `
             {{- . -}}             `
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, "hello")
}
//=== RUN   TestTemplateTrim
//hello--- PASS: TestTemplateTrim (0.00s)

如果不去会是这样子

func TestTemplateTrim(t *testing.T) {
    tmpl := `
             {{ . }}             `
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, "hello")
}
//=== RUN   TestTemplateTrim
//
//             hello             --- PASS: TestTemplateTrim (0.00s)
//PASS

条件语句{{ifcondition}}do{{else}}doelse{{end}}

这个语意是如果true,则do,否则do-else,必须最后申明{{end}} , 跟我的ifelse 一模一样的

doelse的条件为 : false、0、任意 nil 指针、接口值、数组、切片、字典和空字符串""(长度为 0 的字符串)。

func TestTemplateIf(t *testing.T) {
    tmpl := `{{if .flag -}}
             The flag=true
             {{- else -}}
             The flag=false
             {{- end}}
`
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, _map{
        "flag": true,
    })
    _ = parse.Execute(os.Stdout, _map{
        "flag": false,
    })
}
=== RUN   TestTemplateIf
The flag=true
The flag=false
--- PASS: TestTemplateIf (0.00s)
PASS

循环语句{{range content}}T1{{end}}

? 语意很简单,就是遍历内容

func TestRange(t *testing.T) {
    tmpl := `{{range .Array}}
             {{- . -}},
             {{- end}}
`
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, struct {
        Array []string
    }{Array: []string{"a", "b", "c"}})
}
//=== RUN   TestRange
//a,b,c,
//--- PASS: TestRange (0.00s)

{{range content}}T1{{else}}T0{{end}}

? 如果数组为空,输出else的东西

func TestRangeEmpty(t *testing.T) {
    tmpl := `{{range .Array}}
             {{- . -}},
             {{else -}}
              array null
             {{- end}}
`
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, struct {

        Array []string
    }{Array: []string{}})
}
//=== RUN   TestRangeEmpty
//array null
//--- PASS: TestRangeEmpty (0.00s)

{{range $index,$value:=pipeline}}T1{{end}}

? {{range $key,$value :=pipeline }}T1 {{end}} 也适用,用于遍历

? 关于$key,这个属于变量操作,后面会讲到

func TestRange(t *testing.T) {
    tmpl := `{{range $key, $value:=.Array}}
             {{- $key}}:{{$value}},
             {{- end}}
`
    parse, _ := template.New("demo").Parse(tmpl)
    _ = parse.Execute(os.Stdout, struct {
        Array map[string]string
    }{Array: map[string]string{"a": "1", "b": "2", "c": "3"}})
}
//=== RUN   TestRange
//a:1,b:2,c:3,
//--- PASS: TestRange (0.00s)

{{ range pipeline }} T1 {{ end }}
// 这个 else 比较有意思,如果 pipeline 的长度为 0 则输出 else 中的内容
{{ range pipeline }} T1 {{ else }} T0 {{ end }}
// 获取容器的下标
{{ range $index, $value := pipeline }} T1 {{ end }}

FuncMap(很重要)1、自定义函数

type FuncMapmap[string]interface{}

? 它有一个比较好的功能就是PHP字符串变量,自定义函数

func TestFuncMap(t *testing.T) {
    tem, _ := template.New("").Funcs(map[string]interface{}{
        "ReplaceAll": func(src string, old, new string) string {
            return strings.ReplaceAll(src, old, new)
        },
    }).Parse(`func replace: {{ReplaceAll .content "a" "A"}}`)
    tem.Execute(os.Stdout, map[string]interface{}{
        "content": "aBC",
    })
}
=== RUN   TestFuncMap
func replace: ABC--- PASS: TestFuncMap (0.00s)
PASS

其中,不能写{{ReplaceAll.content a A}} ,因为go不识别 a 是一个字符串,所以必须加引号

如果理解了这个,其实对于一些内置函数会理解很多,其中对于函数的要求是:

// 写法错误:不允许有两个参数返回值,如果是两个返回值,第二个必须是error
"ReplaceAll": func(src string, old, new string) (string, string) {
  return strings.ReplaceAll(src, old, new), "111"
},
// 写法正确: 如果两个返回类型,第二个必须是error,顺序不能颠倒
"ReplaceAll": func(src string, old, new string) (string, error) {
  return strings.ReplaceAll(src, old, new), nil
},
// 写法正确: 如果是一个返回类型,直接返回就行了
"ReplaceAll": func(src string, old, new string) string {
  return strings.ReplaceAll(src, old, new)
},
// 写法正确:参数可以不传递,但是必须有返回值的(其实可以理解,没有返回值,你渲染啥)
tem, _ := template.New("").Funcs(map[string]interface{}{
  "Echo": func() string {
    return "hello world"
  },
}).Parse(`func echo : {{Echo}}`)
// 写法错误:不允许没有返回参数,直接panic
"ReplaceAll": func(src string, old, new string)  {
   strings.ReplaceAll(src, old, new)
},

? 其实理解了funcmap,你就理解了内置函数如何玩的,接下来就会说的,不用死记硬背。

2、内置函数

内置函数本质上也是 FuncMap,所以如果掌握了如何使用FuncMap,其实就会这个了。

eq

func TestEQ(t *testing.T) {
    parse, _ := template.New("").Parse(`{{eq .content1 .content2}}`)
    parse.Execute(os.Stdout, _map{
        "content1": "a",
        "content2": "b",
    })
}
=== RUN   TestEQ
false--- PASS: TestEQ (0.00s)
PASS

call

func TestCall(t *testing.T) {
    parse, _ := template.New("").Parse(`{{call .fun .param}}
`)
    parse.Execute(os.Stdout, _map{
        "fun":   func(str string) string { return strings.ToUpper(str) },
        "param": "abc",
    })
}
=== RUN   TestCall
ABC
--- PASS: TestCall (0.00s)
PASS

其实就是这么简单,对于call函数来说,它必须要求返回参数格式是1或2个,其中如果是两个则必须一个是error

func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error)
func safeCall(fun reflect.Value, args []reflect.Value) (val reflect.Value, err error) {

    defer func() {
        if r := recover(); r != nil {
            if e, ok := r.(error); ok {
                err = e
            } else {
                err = fmt.Errorf("%v", r)
            }
        }
    }()
    ret := fun.Call(args)
  // 结果如果两个,则必须有一个是error
    if len(ret) == 2 && !ret[1].IsNil() {
        return ret[0], ret[1].Interface().(error)
    }
    return ret[0], nil
}

变量

? 变量通常适用于定义了某个值,申明很简单,和写php一样,变量前面需要加入一个$ ,注意的是需要渲染的部分全部需要用{{}} 包起来

func TestVariable(t *testing.T) {
    tmpl, err := template.New("test").Parse(`{{$a := "anthony"}} hello {{$a}}`)
    if err != nil {
        panic(err)
    }
    err = tmpl.Execute(os.Stdout, nil)
    if err != nil {
        panic(err)
    }
}

其实跟我的go预发基本一致。

模版嵌套

? 自定义内嵌的模板{{define "template_name"}}template_content {{end}},其中就是一种模版复用的机制。 那么如何模版引用呢{{template"template_name""args"}} ,记得传入两个参数就行,一个模版的名称(记住申明模版名称需要加上""),一个是你的模版需要的参数。

func TestTemplateInternal(t *testing.T) {
    parse, _ := template.New("").Parse(`
        {{define "print"}}my name is {{.}} {{end}}
        {{- template "print" .name}}
    `)
    parse.Execute(os.Stdout, _map{
        "name": "hao dong",
    })
}
=== RUN   TestTemplateInternal
        my name is hao dong 
    --- PASS: TestTemplateInternal (0.00s)
PASS

format 工具

? 我们知道,go是有个fmt插件,可以帮助format 文件,所以在这里go 也提供了format

func TestFormat(t *testing.T) {
    parse, _ := template.New("").Parse(`
        package main
        import  "fmt"
        func main(){
            fmt.Println("{{.}}")
        }
    `)
    parse.Execute(os.Stdout, _map{"data":"1111"})
}
// 结果:是不是很乱,但是别慌
=== RUN   TestFormat
        package main
        import  "fmt"
        func main(){
            fmt.Println("map[data:1111]")
        }
    --- PASS: TestFormat (0.00s)
PASS

如何使用format呢 ?

func TestFormat(t *testing.T) {
    parse, _ := template.New("").Parse(`
        package main
        import  "fmt"
        func main(){
            fmt.Println("{{.}}")
        }
    `)
    buffer := &bytes.Buffer{}
    parse.Execute(buffer, _map{"data":"1111"})
    source, _ := format.Source(buffer.Bytes())
    fmt.Printf("%s",source)
}
=== RUN   TestFormat
package main
import "fmt"
func main() {
    fmt.Println("map[data:1111]")
}
--- PASS: TestFormat (0.00s)
PASS

是不是变得很整齐。

总结

go 原生的提供了模版机制,对于开发者相当友好,尽管第三方包有很多模版工具,但是原生是最好的,因为不需要引入依赖。

模版通常用于生成一些复用的代码,比如 protobuf文件生成, 比如orm框架的model,dao等,都是需要改进的

参考

(编辑:源码门户网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!