视图模板
源文件 app/Views/*.tpl,首次渲染时编译为纯 PHP 缓存到 runtime/views/*.php,之后直接 include(零运行时解析开销),源文件更新自动重编译。
{{ $title }} <!-- 自动 htmlspecialchars 转义,防 XSS -->
{!! $rawHtml !!} <!-- 原样输出,不转义 -->
{{-- 这是注释,编译后完全移除,不会出现在 HTML 中 --}}
@if ($count > 0)
<p>共 {{ $count }} 条</p>
@elseif ($count > 10)
<p>很多条</p>
@else
<p>暂无数据</p>
@endif
@foreach ($items as $it)
<li>#{{ $loop->iteration }}/{{ $loop->count }} {{ $it['name'] }}</li>
@endforeach
@forelse ($items as $it)
<li>{{ $it['name'] }}</li>
@empty
<li>集合为空时走这里</li>
@endforelse
@switch ($status)
@case('ok') <p>正常</p> @break
@default <p>未知</p>
@endswitch
@include('partial.footer', ['year' => 2026]) <!-- 内联渲染子模板,可追加数据 -->
设计取向(重要):模板引擎追求的是调用简单(指令少、语义直观),但功能向主流框架看齐(Blade 风格,思想借鉴、独立实现)。下面 §9.2 给出完整指令速查;真正用到时再查即可,日常 90% 场景只需要
@extends/@section/@yield与{{ }}。
字面量
@转义:正文里若想原样显示@foreach、@if这类字样(而非当成指令),写成@@foreach、@@if即可。@@在编译结束后会被还原成单个@,且不会被解析为指令。这一规则同样适用于注释、组件、资源栈等所有指令所在的文本位置。
// 控制器中
return view('home', ['title' => 'mtphp', 'items' => $list]);
9.1 布局模板(@extends / @section / @yield / @parent)
多页共用同一套页面骨架(头部、导航、页脚)时,用布局模板避免重复。思路:写一个布局用 @yield 占位,子页面用 @extends 继承它、用 @section 填内容。
布局 app/Views/layouts/app.tpl(骨架 + 占位):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>@yield('title', 'mtphp 应用')</title> <!-- 第二参数是默认内容 -->
</head>
<body>
<header><h1>mtphp 站点</h1></header>
<aside>@yield('sidebar', '默认侧边栏')</aside> <!-- 子页可覆盖/追加 -->
<main>@yield('content')</main> <!-- 主体内容占位 -->
<footer>© 武汉麦麦心选网络科技有限公司 · @yield('year', '2026')</footer>
</body>
</html>
子页面 app/Views/home.tpl(继承布局、填充区块):
@extends('layouts.app') <!-- 继承上面的布局(点号等价于目录斜杠) -->
@section('title')
mtphp 首页
@endsection
@section('content')
<h2>{{ $title }}</h2>
<p>本页内容由 @section('content') 定义,布局用 @yield('content') 输出。</p>
@endsection
控制器照常 return view('home', $data) 即可,框架会自动完成「收集子页区块 → 渲染布局并注入」的两遍渲染。
@parent(在布局默认内容上追加):若子页区块里写 @parent,会被替换成布局同名 @yield 的默认内容,便于“保留默认 + 追加”:
@section('sidebar')
@parent <!-- 先输出布局 sidebar 默认内容 -->
<ul><li>子模板追加的菜单项 A</li></ul> <!-- 再追加本页内容 -->
@endsection
说明与限制(这里的“简单”指调用/使用简单,而功能对标主流框架——指令少、语义直观,但能力向 Blade 风格看齐(思想借鉴、独立实现),详见 §9.2): - 区块语法仅支持块形式
@section('name') ... @endsection;不支持内联@section('name','默认')。 - 当前支持一层继承(子页 → 布局);布局本身不再二次@extends。 - 布局默认内容(@yield 第二参数)里不要出现字面量@parent,否则会被原样输出。 - 模板名里的.等价于目录分隔符:layouts.app→app/Views/layouts/app.tpl。
9.2 指令速查(Blade 风格)
下面列出模板引擎支持的全部指令。日常大多数页面只需用到 {{ }} 与 §9.1 的布局指令;其余指令在需要时查阅即可。
输出(转义 / 原样)
| 指令 | 作用 |
|---|---|
{{ $var }} | 输出变量,自动 htmlspecialchars 转义(ENT_QUOTES),防 XSS |
{!! $var !!} | 原样输出,不转义(仅用于受信任的 HTML) |
{{-- 注释 --}} | Blade 风格注释,编译后彻底移除,不出现在产物中 |
条件 / 分支
| 指令 | 作用 |
|---|---|
@if / @elseif / @else / @endif | 条件分支 |
@unless (expr) ... @endunless | 条件取反(if (!expr)) |
@isset($v) ... @endisset | 变量是否存在 |
@empty($v) ... @endempty | 变量是否为空 |
@switch($v) / @case(expr) / @default / @endswitch | 多分支(注意:@case/@default 行不要缩进也能正常工作,引擎已处理) |
循环
| 指令 | 作用 |
|---|---|
@foreach ($items as $it) ... @endforeach | 遍历,自动注入 $loop 循环变量 |
@forelse ($items as $it) ... @empty ... @endforelse | 集合为空时走 @empty 分支 |
@for (...) ... @endfor | 计数循环 |
@while (expr) ... @endwhile | 条件循环 |
@continue / @break | 循环控制;支持条件写法 @continue($i>3)、@break($i>3) |
$loop循环变量(Blade 风格):$loop->index(从 0)、$loop->iteration(从 1)、$loop->count(总数)、$loop->first/$loop->last、$loop->even/$loop->odd、$loop->remaining(剩余项数)、$loop->depth(嵌套深度)、$loop->parent(外层$loop)。
片段 / 组件
| 指令 | 作用 |
|---|---|
@include('tpl'[, $data]) | 内联渲染子模板,自动继承当前数据,可用第二参数追加 |
@includeIf('tpl') / @includeWhen($cond,'tpl') / @includeUnless($cond,'tpl') | 条件包含 |
@includeFirst(['a','b']) | 依次尝试,渲染第一个存在的模板 |
@each('row', $items, 'item'[, 'emptyTpl']) | 一行遍历集合;空集合走空模板 |
@component('comp'[, $props]) ... @endcomponent | 渲染组件视图 |
@slot('name') ... @endslot | 在 @component 内定义具名插槽(未命名的默认插槽即 $slot) |
@props(['a' => 1]) | 组件内声明带默认值的属性(仅当未传入时才用默认值) |
资源栈(注入 CSS / JS)
| 指令 | 作用 |
|---|---|
@push('scripts') ... @endpush | 向某资源栈追加内容 |
@prepend('scripts') ... @endprepend | 向某资源栈前插内容 |
@stack('scripts') | 输出该栈累积的全部内容(通常放在 <head> 或 </body> 前) |
布局辅助(运行期判断区块)
| 指令 | 作用 |
|---|---|
@hasSection('name') ... @else ... @endif | 子模板是否定义了某区块 |
@sectionMissing('name') ... @else ... @endif | 子模板是否未定义某区块 |
输出助手
| 指令 | 作用 |
|---|---|
@json($data) | 输出 json_encode(JSON_UNESCAPED_UNICODE / 不转义斜杠) |
@checked($cond) / @selected($cond) / @disabled($cond) | 条件输出 checked/selected/disabled 属性 |
@class(['a' => $cond, 'b']) | 条件拼接 class:['a'=>true,'b'] → a b;['active'=>false,'base'] → base |
其它
| 指令 | 作用 |
|---|---|
@php ... @endphp | 原样 PHP 块(可多行,也可内联 @php echo $x; @endphp) |
@verbatim ... @endverbatim | 原样保留块内 {{ }} 与 @ 指令不编译,适合放 Vue / Angular 等前端模板 |
@@指令名 | 字面量转义:正文想显示 @if 字样时写 @@if,编译后还原为 @if 且不被解析 |
布局相关指令
@extends/@section/@yield/@parent见 §9.1。