基础语法
这一课你将能做到
- 用正确的变量、字符串、数组语法写出结构清晰的 PHP 脚本
- 熟练使用 if/match/foreach 等控制流处理一组数据并输出结果
- 用类型声明和 strict_types 让 PHP 写出接近静态语言的严谨风格
为什么要学
变量、数组、控制流、类型声明是任何语言的「地基四件套」。PHP 的特殊之处在于:它是动态类型语言,但 PHP 7 之后引入了完整的类型声明体系,PHP 8 又补上了 match 表达式、命名参数等现代语法。今天企业级 PHP 项目的规范(如 Laravel 框架源码、PSR 标准)都大量使用类型声明——只会写无类型的「老式 PHP」,在现代代码库里寸步难行。
人话:这节课就是把「会打字」练到「会写文章」。学完你就能独立写一个处理数组的统计脚本、按条件分流的业务判断,这是后面函数、面向对象、数据库操作的全部前提。
先建立图像
PHP 变量的心智模型:带 $ 前缀的标签,贴在值上。
$price ──► 19.9 (float)
$name ──► "Spring" (string)
$tags ──► [0]=>'php' (array,有序键值对容器)
[1]=>'web'
和 Java 最大的区别:变量本身没有类型,值才有类型;把字符串赋给 $price 也不报错。但开启类型声明后,你可以在函数签名、类属性上重新找回静态检查。
跟着做
步骤 1:变量与字符串
新建 vars.php:
<?php
declare(strict_types=1);
$name = 'Spring';
$price = 19.9;
$active = true;
// 双引号支持变量插值,单引号是纯字面量
echo "框架名:{$name},价格:{$price}\n";
echo '框架名:{$name}\n'; // 原样输出 {$name}
echo $name . ' Boot' . PHP_EOL; // 点号拼接字符串
执行 php vars.php。记住两条铁律:变量以 $ 开头、双引号才插值。
步骤 2:类型声明初体验
<?php
declare(strict_types=1);
function add(int $a, int $b): int {
return $a + $b;
}
echo add(2, 3), PHP_EOL; // 5
echo add(2.5, 3); // TypeError!严格模式下浮点数不能传给 int
declare(strict_types=1) 必须是文件第一条语句,只对当前文件生效。开了它,传参类型不符直接抛 TypeError;不开,PHP 会做宽松转换(如 "5" 转成 5),隐患更深。
步骤 3:数组——PHP 的万能容器
<?php
declare(strict_types=1);
// 索引数组
$stack = ['PHP', 'Go', 'Java'];
$stack[] = 'Rust'; // 追加元素
echo count($stack), PHP_EOL; // 4
echo $stack[0], PHP_EOL; // PHP
// 关联数组(类似 Java 的 Map)
$user = ['id' => 1, 'name' => '张三', 'roles' => ['admin', 'dev']];
echo $user['name'], PHP_EOL; // 张三
echo $user['roles'][1], PHP_EOL; // dev
// 遍历
foreach ($user as $key => $value) {
if (is_array($value)) {
echo $key, ': ', implode(',', $value), PHP_EOL;
} else {
echo $key, ': ', $value, PHP_EOL;
}
}
PHP 数组 = 索引数组 + 字典二合一,底层是有序哈希表。写惯 Java 的人注意:不存在单独的 Map 类型,关联数组就是 Map。
步骤 4:控制流
<?php
declare(strict_types=1);
$scores = ['php' => 92, 'mysql' => 78, 'nginx' => 85];
foreach ($scores as $subject => $score) {
// match 表达式(PHP 8+):有返回值、严格比较、不需要 break
$level = match (true) {
$score >= 90 => '优秀',
$score >= 80 => '良好',
$score >= 60 => '及格',
default => '不及格',
};
printf("%-6s %d 分,评级 %s\n", $subject, $score, $level);
}
// ?: 三元与 ?? 空合并:处理可能不存在的键
$page = (int) ($_GET['page'] ?? 1); // CLI 下 $_GET 不存在,取默认值 1
echo "当前页码:{$page}\n";
match(true) 配合条件写法,可读性远超嵌套 if-else;?? 专门对付「键可能不存在」的场景,比 isset() ? : 简洁得多。
讲清楚要点
==宽松比较是历史大坑:0 == "foo"在 PHP 8 前为 true。PHP 8 修复了字符串与数字比较的规则,但面试和遗留代码里它仍是高频坑点。规则很简单:一律用===,除非明确要做弱比较。- strict_types 按文件声明:只约束「调用发生在这个文件里」的传参行为,不全局生效。团队项目应在所有源码文件顶部统一声明。
- 数组是有序哈希表:键顺序即插入顺序,
foreach按插入顺序遍历;数字键会自动重排(array_values),字符串键保留。unset($arr[1])不会让后面的键前移。 - 类型声明金字塔:参数类型 → 返回类型 → 可空类型
?int→ 联合类型int|string(PHP 8.0)→ 交叉类型Countable&Traversable(PHP 8.1)。按项目规范从参数和返回值开始加,收益最大。
你可能会懵的地方
$a和&$a有什么区别?$a是值拷贝(写时复制,实际开销不大),&$a是引用传递,函数内修改会影响外部。foreach ($arr as &$v)也用引用修改原数组,但循环后必须unset($v),否则后续复用$v会误改最后一个元素。- 关联数组的键有什么限制?
整型和字符串作键;浮点数键会被截断成整型;
true/false/null会转成1/0/""。数字字符串键如"8"会自动转成数字键8。 - 单引号和双引号到底怎么选?
需要插值或转义符(
\n、\t)用双引号;纯字面量用单引号更快更安全。字符串里带$时务必想清楚是否会被当成变量解析。 switch和match该用哪个? 新代码统一用match:它是表达式(有返回值)、用===严格比较、不需要break、漏分支直接抛异常而不是静默掉过。switch只在维护遗留代码时阅读即可。- 为什么我的 foreach 改了元素但数组没变?
foreach ($arr as $v)中$v是拷贝。要改原数组用引用:foreach ($arr as &$v) { $v = $v * 2; },循环后unset($v)。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
if ($status == '1') 弱比较 | "1"、1、true 混淆 | 统一用 === 严格比较 |
单引号里写 "$name" | 不插值,原样输出 | 改用双引号或点号拼接 |
array(1, 2, 3) 老式写法 | 冗长过时 | 用短语法 [1, 2, 3] |
| 未声明 strict_types 就依赖隐式转换 | "abc" == 0 类隐患 | 文件顶部 declare(strict_types=1) |
函数里 if($a){return A;}else{return B;} | 嵌套冗长 | 提前 return 或用 match 表达式 |
foreach 引用循环后不 unset | 后续误改最后一项 | 循环结束立即 unset($v) |
卡点急救
Parse error: syntax error, unexpected ...:多半是少了分号、括号没闭合、把.写成+(PHP 字符串拼接用点号)。按报错行号检查上一行结尾。Undefined array key "page"警告:访问了不存在的键。用$arr['key'] ?? 默认值或先isset()/array_key_exists()判断。- 数组明明有值却取不到:检查键类型——数字键
0和字符串键"0"是同一个键,但" 0"(带空格)不是;用var_dump($arr)看真实结构。
面试常问
==和===的区别?==先做类型转换再比较(PHP 8 收紧了数字与字符串比较规则),===同时比较类型和值。生产代码规范要求一律===,避免0 == "abc"这类历史陷阱。- PHP 数组的底层实现是什么? 有序哈希表(HashTable):通过哈希表实现 O(1) 键查找,同时维护插入顺序支持索引语义;数字键和字符串键统一处理。这也是 PHP 数组既是 list 又是 map 的原因。
- strict_types 的作用范围是什么?
declare(strict_types=1)只对声明它的文件生效,影响该文件内发起的函数调用与方法调用的参数/返回值类型检查;不含该声明的文件调用同一函数仍是宽松模式,因此团队必须在编码规范里强制统一。
自测三问
match表达式相比switch有哪四个优点?- 如何安全地读取一个可能不存在的数组键并给默认值?
- PHP 关联数组等价于 Java 的哪个结构?底层如何做到有序?
小结一句话
PHP 基础语法 = $ 变量 + 万能数组 + 现代控制流(match/??),再用 strict_types 和类型声明把动态语言管出静态纪律。
延伸
- PHP 官方手册:类型
- 下一课:函数与命名空间——从脚本堆砌走向可复用、可自动加载的工程代码。