[インデックス 10793] ファイルの概要
このコミットは、Go言語の標準ライブラリである html/template
パッケージに対する変更です。具体的には、以下の2つのファイルが変更されています。
src/pkg/html/template/escape_test.go
:html/template
パッケージのテストファイル。src/pkg/html/template/template.go
:html/template
パッケージの主要な実装ファイル。
変更の目的は、html/template
パッケージの公開APIが text/template
パッケージに直接依存しないようにすることです。
コミット
commit 1402d1a68615d037365158578b0c4861e0fb4157
Author: Rob Pike <r@golang.org>
Date: Wed Dec 14 11:22:17 2011 -0800
html/template: define the FuncMap type locally
This redefinition means that the public signature of html/template
does not refer to text/template.
Fixes #2546.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5487083
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1402d1a68615d037365158578b0c4861e0fb4157
元コミット内容
html/template: define the FuncMap type locally
This redefinition means that the public signature of html/template
does not refer to text/template.
Fixes #2546.
変更の背景
Go言語の html/template
パッケージは、HTML出力の自動エスケープ機能を提供するテンプレートエンジンです。これは、基本的なテンプレート機能を提供する text/template
パッケージの上に構築されています。
このコミットが行われる前は、html/template
パッケージの Funcs
メソッドが text/template.FuncMap
型を直接引数として受け取っていました。これは、html/template
の公開APIが text/template
パッケージの型に直接依存していることを意味します。
ソフトウェア設計において、モジュール間の結合度(Coupling)は低い方が望ましいとされています。結合度が高いと、一方のモジュールが変更された際に、それに依存する他のモジュールも変更が必要になる可能性が高まります。この場合、html/template
が text/template.FuncMap
に直接依存しているため、text/template
の FuncMap
型の定義が変更された場合、html/template
もそれに合わせて変更する必要が生じる可能性があります。
このコミットの背景にあるのは、このような不必要な依存関係を解消し、html/template
パッケージの独立性を高めることです。コミットメッセージにある「public signature of html/template does not refer to text/template」という記述は、この目的を明確に示しています。これにより、html/template
は text/template
の内部実装の詳細からより疎結合になり、将来的な変更に対する堅牢性が向上します。
Fixes #2546
という記述がありますが、このIssueはGoの公開Issueトラッカーでは直接見つかりませんでした。これは、内部的なIssueトラッカーの番号であるか、非常に古いIssueである可能性があります。しかし、コミットメッセージとコードの変更内容から、その目的は明確です。
前提知識の解説
Go言語の text/template
パッケージ
text/template
パッケージは、Go言語におけるテキストベースのテンプレートエンジンを提供します。これは、プレースホルダーを含むテキストを解析し、データ構造から提供された値でそれらを置き換えることができます。主な機能には以下があります。
- テンプレートの解析と実行: テンプレート文字列を解析し、指定されたデータ構造を基に最終的なテキストを生成します。
- アクション:
{{.FieldName}}
のような構文でデータ構造のフィールドにアクセスしたり、{{range .Slice}}...{{end}}
でループ処理を行ったり、{{if .Condition}}...{{end}}
で条件分岐を行ったりできます。 - 関数: テンプレート内でカスタム関数を呼び出すことができます。これらの関数は
FuncMap
を介してテンプレートに登録されます。
Go言語の html/template
パッケージ
html/template
パッケージは、text/template
パッケージを基盤としていますが、HTML出力に特化しています。最も重要な機能は、コンテキストに応じた自動エスケープです。これにより、クロスサイトスクリプティング(XSS)などのセキュリティ脆弱性を防ぐことができます。例えば、ユーザーが入力した文字列をHTMLページに表示する際に、悪意のあるスクリプトが埋め込まれていても、html/template
が自動的にエスケープ処理を行うため、スクリプトが実行されるのを防ぎます。
FuncMap
とは
FuncMap
は、テンプレート内で使用できるカスタム関数を登録するためのマップ型です。キーは関数の名前(文字列)、値はGoの関数(interface{}
型)です。これらの関数は、テンプレートの実行時に呼び出され、動的なコンテンツ生成に利用されます。
text/template
パッケージには text/template.FuncMap
という型が定義されており、これは map[string]interface{}
のエイリアスです。
型のエイリアスと依存関係
Go言語では、type NewType OldType
のようにして既存の型に新しい名前(エイリアス)を付けることができます。このコミットでは、html/template
パッケージ内で FuncMap
という新しい型を定義していますが、その基底型は map[string]interface{}
であり、これは text/template.FuncMap
と同じ基底型です。
重要なのは、html/template
が text/template.FuncMap
を直接参照する代わりに、独自の FuncMap
型を定義することで、html/template
の公開APIから text/template
への直接的な型依存を排除している点です。これにより、html/template
を利用する側は、text/template
パッケージをインポートすることなく、html/template
の Funcs
メソッドを利用できるようになります。
技術的詳細
このコミットの核心は、html/template
パッケージが text/template
パッケージの FuncMap
型に直接依存するのをやめ、独自の FuncMap
型を定義した点にあります。
変更前は、html/template
の Funcs
メソッドのシグネチャは以下のようになっていました。
func (t *Template) Funcs(funcMap template.FuncMap) *Template
ここで template.FuncMap
は text/template
パッケージの FuncMap
を指します。このため、html/template
を利用するユーザーは、Funcs
メソッドを呼び出す際に text/template
をインポートし、その FuncMap
型を使用する必要がありました。
このコミットでは、html/template
パッケージ内に FuncMap
という新しい型を定義しました。
type FuncMap map[string]interface{}
この新しい FuncMap
型は、text/template.FuncMap
と同じ基底型 map[string]interface{}
を持ちます。これにより、html/template
の Funcs
メソッドのシグネチャは以下のように変更されました。
func (t *Template) Funcs(funcMap FuncMap) *Template
この変更により、html/template
の公開APIから text/template
への直接的な型依存がなくなりました。html/template
を利用する側は、html/template
パッケージが提供する FuncMap
型を使用すればよくなり、text/template
を明示的にインポートする必要がなくなります。
しかし、html/template
は内部的には text/template
を利用しています。そのため、html/template
の Funcs
メソッドの実装では、受け取った html/template.FuncMap
型の値を text/template.FuncMap
型に型変換して、内部の text/template.Template
オブジェクトの Funcs
メソッドに渡す必要があります。これは、t.text.Funcs(template.FuncMap(funcMap))
という行で行われています。ここで template.FuncMap(funcMap)
は、html/template
の FuncMap
型の値を text/template
の FuncMap
型に変換しています。Go言語では、基底型が同じであれば、このように型変換を行うことができます。
この変更は、html/template
パッケージのAPI設計をよりクリーンにし、ユーザーが html/template
を使う際に text/template
の詳細を意識する必要をなくすという点で重要です。これは、Goの標準ライブラリがモジュール性と独立性を重視していることの一例と言えます。
コアとなるコードの変更箇所
src/pkg/html/template/escape_test.go
--- a/src/pkg/html/template/escape_test.go
+++ b/src/pkg/html/template/escape_test.go
@@ -654,7 +654,7 @@ func TestEscape(t *testing.T) {
for _, test := range tests {
tmpl := New(test.name)
// TODO: Move noescape into template/func.go
- tmpl.Funcs(template.FuncMap{
+ tmpl.Funcs(FuncMap{
"noescape": func(a ...interface{}) string {
return fmt.Sprint(a...)
},
@@ -792,7 +792,7 @@ func TestEscapeSet(t *testing.T) {
// pred is a template function that returns the predecessor of a
// natural number for testing recursive templates.
- fns := template.FuncMap{"pred": func(a ...interface{}) (interface{}, error) {
+ fns := FuncMap{"pred": func(a ...interface{}) (interface{}, error) {
if len(a) == 1 {
if i, _ := a[0].(int); i > 0 {
return i - 1, nil
このテストファイルでは、tmpl.Funcs()
および fns
の初期化において、template.FuncMap
の代わりに新しく定義された FuncMap
型が使用されるように変更されています。これは、APIの変更に合わせてテストコードも更新されたことを示しています。
src/pkg/html/template/template.go
--- a/src/pkg/html/template/template.go
+++ b/src/pkg/html/template/template.go
@@ -154,12 +154,20 @@ func (t *Template) Name() string {
return t.text.Name()
}
+// FuncMap is the type of the map defining the mapping from names to
+// functions. Each function must have either a single return value, or two
+// return values of which the second has type error. In that case, if the
+// second (error) argument evaluates to non-nil during execution, execution
+// terminates and Execute returns that error. FuncMap has the same base type
+// as template.FuncMap, copied here so clients need not import "text/template".
+type FuncMap map[string]interface{}
+
// Funcs adds the elements of the argument map to the template's function map.
// It panics if a value in the map is not a function with appropriate return
// type. However, it is legal to overwrite elements of the map. The return
// value is the template, so calls can be chained.
-func (t *Template) Funcs(funcMap template.FuncMap) *Template {
- t.text.Funcs(funcMap)
+func (t *Template) Funcs(funcMap FuncMap) *Template {
+ t.text.Funcs(template.FuncMap(funcMap))
return t
}
このファイルが変更の主要な部分です。
-
FuncMap
型の定義追加:type FuncMap map[string]interface{}
という行が追加され、html/template
パッケージ内で独自のFuncMap
型が定義されました。この定義には、その目的とtext/template.FuncMap
との関係を説明するコメントも追加されています。 -
Funcs
メソッドのシグネチャ変更:func (t *Template) Funcs(funcMap template.FuncMap) *Template
がfunc (t *Template) Funcs(funcMap FuncMap) *Template
に変更されました。これにより、Funcs
メソッドがhtml/template
独自のFuncMap
型を引数として受け取るようになりました。 -
Funcs
メソッドの実装変更:t.text.Funcs(funcMap)
がt.text.Funcs(template.FuncMap(funcMap))
に変更されました。これは、html/template
のFuncs
メソッドが受け取ったhtml/template.FuncMap
型の値を、内部で利用しているtext/template
のFuncs
メソッドに渡す前に、text/template.FuncMap
型に明示的に型変換していることを示しています。
コアとなるコードの解説
このコミットのコアとなる変更は、html/template/template.go
における FuncMap
型のローカル定義と、それに伴う Funcs
メソッドのシグネチャおよび実装の変更です。
-
type FuncMap map[string]interface{}
の追加: この行は、html/template
パッケージ内にFuncMap
という新しい型を定義しています。この型はmap[string]interface{}
のエイリアスであり、text/template.FuncMap
と同じ基底型を持ちます。これにより、html/template
はtext/template
のFuncMap
を直接参照することなく、独自のFuncMap
型を公開APIとして提供できるようになります。追加されたコメントは、この型の目的と、クライアントがtext/template
をインポートする必要がないことを明確に説明しています。 -
func (t *Template) Funcs(funcMap FuncMap) *Template
へのシグネチャ変更:Template
型のFuncs
メソッドが、text/template.FuncMap
ではなく、新しく定義されたhtml/template.FuncMap
を引数として受け取るように変更されました。これは、html/template
の公開APIがtext/template
から独立したことを意味します。これにより、html/template
を利用する開発者は、text/template
パッケージを意識することなく、html/template
の機能を利用できるようになります。 -
t.text.Funcs(template.FuncMap(funcMap))
への実装変更:html/template
のTemplate
オブジェクトは、内部にtext/template.Template
オブジェクト (t.text
) を持っています。html/template
のFuncs
メソッドが呼び出された際、その機能は最終的に内部のtext/template.Template
オブジェクトのFuncs
メソッドに委譲されます。 この変更では、html/template.FuncMap
型のfuncMap
引数を、text/template.FuncMap(funcMap)
という形でtext/template.FuncMap
型に明示的に型変換しています。Go言語では、基底型が同じであれば、このように型変換を行うことができます。この変換により、html/template
は自身の公開APIの独立性を保ちつつ、内部的にはtext/template
の機能を引き続き利用することができます。
これらの変更により、html/template
パッケージは、その公開APIにおいて text/template
パッケージへの直接的な依存を解消し、よりクリーンで独立した設計を実現しています。これは、Go言語の標準ライブラリが、パッケージ間の適切な分離と疎結合を重視していることを示す良い例です。
関連リンク
- Go言語の公式ドキュメント:
text/template
パッケージとhtml/template
パッケージに関する詳細な情報が提供されています。 - Go言語のIssueトラッカー: コミットメッセージに
Fixes #2546
とありますが、このIssueは公開されているGoのIssueトラッカーでは直接見つかりませんでした。これは、内部的なIssueであるか、非常に古いIssueである可能性があります。
参考にした情報源リンク
- コミット情報:
/home/orange/Project/comemo/commit_data/10793.txt
- GitHubコミットページ: https://github.com/golang/go/commit/1402d1a68615d037365158578b0c4861e0fb4157
- Go言語のドキュメント (一般的なパッケージ設計と型システムに関する知識)
- Go言語のソースコード (変更されたファイルの内容)