PHP 基础语法与模板:类型、数组和安全输出
本文目录
原始发布日期保留为 2018-07-02。本文于 2026-10-19 按 PHP 8.x 重整。示例用于学习语法,不是完整 Web 应用;所有外部输入都要先验证,所有动态 HTML 输出都要按上下文编码。
代码在哪里执行
PHP 通常在服务器上执行:服务端运行 PHP,生成 HTML、JSON 或其他响应,再把响应发给浏览器。浏览器收到的不是 PHP 源码。PHP 可以嵌入 HTML,但处理请求和响应头的逻辑应在输出正文之前完成。
<?php
declare(strict_types=1);
$pageTitle = 'PHP 入门';
$serverDate = date('Y-m-d');
?>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title><?= htmlspecialchars($pageTitle, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></title>
</head>
<body>
<h1><?= htmlspecialchars($pageTitle, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></h1>
<p>页面由服务端生成,日期:<?= htmlspecialchars($serverDate, ENT_QUOTES, 'UTF-8') ?></p>
</body>
</html>
使用标准 <?php ... ?> 标签;在 HTML 模板中可以用 <?= ... ?> 输出表达式。不要依赖 short_open_tag,也不要把控制器逻辑、数据库操作、模板和用户输入处理全塞在一个文件里。
输出与调试
echo 是语言结构,可以输出多个表达式,不返回值;print 输出一个表达式并返回 1。普通页面模板里,动态内容应先按输出上下文转义;var_dump()、print_r() 用于本地调试,不应把请求、密码、令牌或数据库对象直接显示给访客。
<?php
$name = '开发者';
echo '你好,', $name, '!', PHP_EOL;
$debugValue = ['count' => 2, 'enabled' => true];
var_dump($debugValue); // 仅用于受控的开发调试
?>
htmlspecialchars() 适用于 HTML 文本和引号包围的普通属性值,不是 SQL、URL、JavaScript 或 CSS 的通用转义。SQL 使用参数化查询;URL 按允许的 scheme/host 校验并正确编码。
常见值类型
PHP 是动态类型语言,但可以通过参数、属性和返回类型声明表达接口约束。不同类型参与运算时可能发生类型转换;需要确定语义时优先显式校验,而不是猜测隐式转换结果。
| 类型 | 示例 | 常见注意点 |
|---|---|---|
int / float | 42、3.14 | 浮点数不适合直接表示精确金额 |
string | 'hello'、"hello" | UTF-8 字符数量不等于字节数 |
bool | true、false | 0、"0"、空字符串等值也可能参与 falsey 判断 |
array | [1, 2]、['id' => 1] | 同时用于列表和键值映射 |
object | new DateTimeImmutable() | 用对象表达有行为和不变量的数据 |
null | null | 表示未提供/无值,与空字符串不是一回事 |
<?php
function formatCount(int $count): string
{
if ($count < 0) {
throw new InvalidArgumentException('数量不能为负数。');
}
return number_format($count);
}
echo formatCount(1280); // 1,280
?>
declare(strict_types=1) 要放在 PHP 文件最前面(注释除外);严格标量类型模式按发起函数调用的文件决定。类型声明能帮助尽早发现错误,但不能替代对 HTTP 字段、数据库数据和业务范围的校验。
项目变大后可用命名空间避免类名冲突。Composer 的 PSR-4 自动加载把命名空间前缀映射到目录,应用启动时加载 vendor/autoload.php,避免在业务代码里散落大量 require。自动加载负责找到类,不会替代依赖版本约束或输入校验。
列表与关联数组
PHP 数组是有序的键值映射。连续数字键常用作列表,字符串键常用作记录;用 foreach 遍历,比手动维护索引更直观。外部输入可能不是数组,传给 foreach 前应先检查类型。
<?php
$tasks = [
['title' => '检查请求字段', 'done' => true],
['title' => '写回归测试', 'done' => false],
];
foreach ($tasks as $index => $task) {
$state = $task['done'] ? '完成' : '待办';
echo ($index + 1), '. ', $task['title'], ':', $state, PHP_EOL;
}
$profile = ['nickname' => '小高', 'level' => 3];
echo $profile['nickname'];
?>
用 isset()、array_key_exists() 和 empty() 前先明确业务问题:它们对缺失键、null、0 与空字符串的判断不同。详见数组键、空值与 falsey 值。
对列表进行纯转换时可用 array_map(),按条件保留元素可用 array_filter();后者会保留原键,若需要从零开始的连续索引,再显式调用 array_values()。回调应尽量无副作用,数据库写入等操作不要藏在遍历回调中。
条件与循环
<?php
$status = 'published';
if ($status === 'published') {
echo '可公开阅读';
} elseif ($status === 'draft') {
echo '仅作者可见';
} else {
echo '未知状态';
}
for ($page = 1; $page <= 3; $page++) {
echo '第 ', $page, ' 页', PHP_EOL;
}
?>
比较用户提供的字符串时通常使用 ===/!==,避免宽松比较带来的意外类型转换。循环应有明确上限;对于分页和批量处理,要设定每次最多读取的记录数,避免一次加载无限数据。
页面模板与外部输入
查询参数、表单字段、Cookie、Header 和上传文件都是外部输入。它们可能不存在、类型错误或被故意构造为数组;不要直接传给数据库或拼进 HTML。
<?php
$rawTitle = $_GET['title'] ?? null;
$title = is_string($rawTitle) ? trim($rawTitle) : '';
if ($title === '' || mb_strlen($title, 'UTF-8') > 120) {
$title = '未命名文章';
}
?>
<h1><?= htmlspecialchars($title, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') ?></h1>
示例中的默认值只表示显示策略,不代表所有业务都可以静默吞掉错误。真正的写操作还要执行 CSRF、身份认证和对象级授权;完整注册与表单校验流程见表单提交与安全注册。
本课练习
- 建立包含“待办标题”和“是否完成”的数组,使用
foreach输出列表。 - 把每个标题放进 HTML 元素时使用上下文转义;尝试传入包含
<、引号和中文的文本。 - 编写带
int参数和string返回值的函数,并对负数输入给出明确处理。 - 分别测试查询参数缺失、空字符串、数组型参数和超长文本,确认它们都不会直接进入页面或数据库。
请求与响应示意
下面的图只说明浏览器请求到服务端响应的概念链路;真实应用还包含路由、认证授权、数据库、缓存、日志和反向代理:

参考资料
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。