開発プラクティス

インストール

Latte をインストールする最良の方法は Composer を使用することです:

composer require latte/latte

サポートされている PHP バージョン(最新のマイナーバージョンの Latte に適用):

バージョン PHP との互換性
Latte 3.0 PHP 8.0 – 8.2

テンプレートをレンダリングする方法

テンプレートをレンダリングするにはどうすればよいですか?この簡単なコードで十分です:

$latte = new Latte\Engine;
// キャッシュディレクトリ
$latte->setTempDirectory('/path/to/tempdir');

$params = [ /* テンプレート変数 */ ];
// または $params = new TemplateParameters(/* ... */);

// 出力にレンダリング
$latte->render('template.latte', $params);
// 変数にレンダリング
$output = $latte->renderToString('template.latte', $params);

パラメータは配列、またはオブジェクトである方が望ましいです。これにより、型チェックとエディタでの補完が保証されます。

使用例は Latte examples リポジトリにもあります。

パフォーマンスとキャッシュ

Latte のテンプレートは非常に高速です。Latte はそれらを直接 PHP コードにコンパイルし、ディスク上のキャッシュに保存します。したがって、純粋な PHP で書かれたテンプレートと比較して追加のオーバーヘッドはありません。

ソースファイルを変更するたびに、キャッシュは自動的に再生成されます。したがって、開発中は Latte テンプレートを快適に編集し、変更をすぐにブラウザで確認できます。この機能は本番環境で無効にして、パフォーマンスを少し節約できます:

$latte->setAutoRefresh(false);

本番サーバーにデプロイする場合、特に大規模なアプリケーションでは、最初のキャッシュ生成に少し時間がかかることがあります。Latte には「キャッシュスタンピード」に対する組み込みの防止策があります:https://en.wikipedia.org/wiki/Cache_stampede。 これは、Latte を起動する多数の同時リクエストが発生し、キャッシュがまだ存在しないため、すべてが同時に生成を開始する状況です。これにより、サーバーに過度の負荷がかかります。 Latte は賢く、複数の同時リクエストがある場合、最初のスレッドのみがキャッシュを生成し、他のスレッドは待機して後でそれを使用します。

クラスとしてのパラメータ

テンプレートに変数を配列として渡すよりも、クラスを作成する方が良いです。型安全な記述IDEでの快適な補完、およびフィルタ関数を登録するためのパスが得られます。

class MailTemplateParameters
{
	public function __construct(
		public string $lang,
		public Address $address,
		public string $subject,
		public array $items,
		public ?float $price = null,
	) {}
}

$latte->render('mail.latte', new MailTemplateParameters(
	lang: $this->lang,
	subject: $title,
	price: $this->getPrice(),
	items: [],
	address: $userAddress,
));

変数の自動エスケープの無効化

変数に HTML 文字列が含まれている場合、Latte が自動的に(したがって二重に)エスケープしないようにマークできます。これにより、テンプレートで |noescape を指定する必要がなくなります。

最も簡単な方法は、文字列を Latte\Runtime\Html オブジェクトでラップすることです:

$params = [
	'articleBody' => new Latte\Runtime\Html($article->htmlBody),
];

Latte はさらに、Latte\HtmlStringable インターフェースを実装するすべてのオブジェクトをエスケープしません。したがって、__toString() メソッドが自動的にエスケープされない HTML コードを返す独自のクラスを作成できます:

class Emphasis extends Latte\HtmlStringable
{
	public function __construct(
		private string $str,
	) {
	}

	public function __toString(): string
	{
		return '<em>' . htmlspecialchars($this->str) . '</em>';
	}
}

$params = [
	'foo' => new Emphasis('hello'),
];

__toString メソッドは正しい HTML を返し、パラメータのエスケープを保証する必要があります。そうしないと、XSS 脆弱性が発生する可能性があります!

フィルタ、タグなどで Latte を拡張する方法

カスタムフィルタ、関数、タグなどを Latte に追加するにはどうすればよいですか?これについては、Latte の拡張の章で説明します。 変更をさまざまなプロジェクトで再利用したり、他の人と共有したりしたい場合は、拡張機能を作成する必要があります。

テンプレート内の任意のコード {php ...}

{do} タグ内では PHP 式のみを記述できます。したがって、if ... else などの構文やセミコロンで終わるステートメントを挿入することはできません。

ただし、{php ...} タグを追加する RawPhpExtension 拡張機能を登録できます。これにより、任意の PHP コードを挿入できます。サンドボックスモードのルールは適用されないため、使用はテンプレート作成者の責任となります。

$latte->addExtension(new Latte\Essential\RawPhpExtension);

生成されたコードのチェック

Latte はテンプレートを PHP コードにコンパイルします。もちろん、生成されたコードが構文的に有効であることを保証します。ただし、サードパーティの拡張機能または RawPhpExtension を使用する場合、Latte は生成されたファイルの正確性を保証できません。 また、PHP では構文的に正しいが禁止されているコード(例えば、変数 $this への値の代入)を記述することができ、PHP コンパイルエラーが発生します。 このような操作をテンプレートに記述すると、生成された PHP コードにも含まれます。PHP には約 200 の異なる禁止された操作があるため、Latte はそれらを検出することを目指していません。通常、レンダリング時に PHP 自体がそれらを警告しますが、これは通常問題ありません。

ただし、テンプレートのコンパイル時に PHP コンパイルエラーが含まれていないことを知りたい場合があります。特に、テンプレートをユーザーが編集できる場合、またはサンドボックスを使用している場合です。このような場合は、コンパイル時にテンプレートをチェックさせてください。 この機能は Engine::enablePhpLint() メソッドで有効にします。チェックには PHP バイナリを呼び出す必要があるため、そのパスをパラメータとして渡します:

$latte = new Latte\Engine;
$latte->enablePhpLinter('/path/to/php');

try {
	$latte->compile('home.latte');
} catch (Latte\CompileException $e) {
	// Latte のエラーと PHP のコンパイルエラーをキャッチします
	echo 'Error: ' . $e->getMessage();
}

ロケール

Latte を使用すると、数値、日付、および並べ替えの書式設定に影響を与えるロケールを設定できます。これは setLocale() メソッドを使用して設定されます。ロケール識別子は、PHP 拡張機能 intl が使用する IETF 言語タグ標準に従います。これは、言語コードと、場合によっては国コードで構成されます。例:米国の英語の場合は en_US、ドイツのドイツ語の場合は de_DE など。

$latte = new Latte\Engine;
$latte->setLocale('cs');

ロケール設定は、フィルタ localDatesortnumber、および bytes に影響します。

PHP 拡張機能 intl が必要です。Latte の設定は PHP のグローバルロケール設定には影響しません。

厳格モード

厳格な解析モードでは、Latte は閉じ HTML タグが欠落していないかチェックし、変数 $this の使用も禁止します。次のように有効にします:

$latte = new Latte\Engine;
$latte->setStrictParsing();

declare(strict_types=1) ヘッダーを持つテンプレートの生成は、次のように有効にします:

$latte = new Latte\Engine;
$latte->setStrictTypes();

テンプレートでの翻訳

TranslatorExtension 拡張機能を使用すると、テンプレートにタグ {_...}{translate}、およびフィルタ translate を追加できます。これらは、値またはテンプレートの一部を他の言語に翻訳するために使用されます。パラメータとして、翻訳を実行するメソッド(PHP callable)を指定します:

class MyTranslator
{
	public function __construct(private string $lang)
	{}

	public function translate(string $original): string
	{
		// $original から $this->lang に従って $translated を作成します
		return $translated;
	}
}

$translator = new MyTranslator($lang);
$extension = new Latte\Essential\TranslatorExtension(
	$translator->translate(...), // PHP 8.0 では [$translator, 'translate']
);
$latte->addExtension($extension);

トランスレータは、テンプレートのレンダリング時に実行時に呼び出されます。ただし、Latte はテンプレートのコンパイル中にすべての静的テキストを翻訳できます。これにより、各文字列が 1 回だけ翻訳され、結果の翻訳がコンパイルされた形式に書き込まれるため、パフォーマンスが節約されます。したがって、キャッシュディレクトリには、言語ごとに 1 つずつ、複数のコンパイル済みバージョンのテンプレートが作成されます。これを行うには、言語を 2 番目のパラメータとして指定するだけです:

$extension = new Latte\Essential\TranslatorExtension(
	$translator->translate(...),
	$lang,
);

静的テキストとは、例えば {_'hello'}{translate}hello{/translate} のようなものを意味します。{_$foo} のような非静的テキストは、引き続き実行時に翻訳されます。

トランスレータには、{_$original, foo: bar} または {translate foo: bar} を使用してテンプレートから追加のパラメータを渡すこともできます。これらは配列 $params として取得されます:

public function translate(string $original, ...$params): string
{
	// $params['foo'] === 'bar'
}

デバッグと Tracy

Latte は開発をできるだけ快適にしようとします。デバッグ目的のために、3 つのタグ {dump}{debugbreak}{trace} があります。

さらに優れたデバッグツール Tracyをインストールし、Latte アドオンを有効にすると、最高の快適さが得られます:

// Tracy を有効にします
Tracy\Debugger::enable();

$latte = new Latte\Engine;
// Tracy の拡張機能を有効にします
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);

これで、すべてのエラーが、行と列が強調表示されたテンプレートのエラーを含め、明確な赤い画面に表示されます(ビデオ)。 同時に、右下のいわゆる Tracy Bar に Latte のタブが表示され、レンダリングされたすべてのテンプレートとその相互関係(テンプレートまたはコンパイル済みコードにクリックして移動する可能性を含む)および変数が明確に表示されます:

Latte はテンプレートを読みやすい PHP コードにコンパイルするため、IDE で快適にステップ実行できます。

Linter:テンプレート構文の検証

Linter ツールは、すべてのテンプレートを調べて、構文エラーが含まれていないかチェックするのに役立ちます。コンソールから起動します:

vendor/bin/latte-lint <path>

--strict パラメータは厳格モードを有効にします。

カスタムタグを使用している場合は、独自のバージョンの Linter を作成します。例:custom-latte-lint

#!/usr/bin/env php
<?php

// autoload.php ファイルへの実際のパスを指定します
require __DIR__ . '/vendor/autoload.php';

$path = $argv[1] ?? '.';

$linter = new Latte\Tools\Linter;
$latte = $linter->getEngine();
// ここに個々の拡張機能を追加します
$latte->addExtension(/* ... */);

$ok = $linter->scanDirectory($path);
exit($ok ? 0 : 1);

あるいは、カスタム Latte\Engine オブジェクトを Linter に渡すこともできます:

$latte = new Latte\Engine;
// ここで $latte オブジェクトを設定します
$linter = new Latte\Tools\Linter(engine: $latte);

文字列からのテンプレートの読み込み

テスト目的などで、ファイルではなく文字列からテンプレートを読み込む必要がありますか?StringLoader が役立ちます:

$latte->setLoader(new Latte\Loaders\StringLoader([
	'main.file' => '{include other.file}',
	'other.file' => '{if true} {$var} {/if}',
]));

$latte->render('main.file', $params);

例外ハンドラ

予期される例外に対して独自のハンドラを定義できます。{try} 内およびサンドボックス内で発生した例外が渡されます。

$loggingHandler = function (Throwable $e, Latte\Runtime\Template $template) use ($logger) {
	$logger->log($e);
};

$latte = new Latte\Engine;
$latte->setExceptionHandler($loggingHandler);

自動レイアウト検索

{layout} タグを使用して、テンプレートはその親テンプレートを指定します。レイアウトを自動的に検索させることも可能です。これにより、テンプレートに {layout} タグを含める必要がなくなるため、テンプレートの記述が簡素化されます。

これは次の方法で実現されます:

$finder = function (Latte\Runtime\Template $template) {
	if (!$template->getReferenceType()) {
		// レイアウトファイルへのパスを返します
		return 'automatic.layout.latte';
	}
};

$latte = new Latte\Engine;
$latte->addProvider('coreParentFinder', $finder);

テンプレートにレイアウトがない場合は、{layout none} タグで示します。

バージョン: 3.0