2.1 变量与常量
变量基础
PHP 变量以 $ 符号开头,无需声明类型即可使用。变量名区分大小写。
<?php
// 变量无需声明,直接赋值即可
$name = "张三";
$age = 28;
$price = 99.5;
$isActive = true;
// 变量名规则:以字母或下划线开头,后跟字母、数字、下划线
$_privateVar = "合法";
$camelCase = "推荐的命名风格";
// 可变变量(少用,但要知道)
$varName = "hello";
$$varName = "world"; // 等价于 $hello = "world"
echo $hello; // 输出: world
常量
常量一旦定义不可修改。推荐使用 const(编译时)或 define()(运行时)。
<?php
// 方式一:const(推荐,编译时定义,可用于类中)
const APP_VERSION = "2.0.0";
const MAX_RETRY = 3;
// 方式二:define()(运行时定义,可用于条件语句中)
define('DB_HOST', 'localhost');
define('FEATURES', ['login', 'register', 'profile']); // 数组常量
// 使用常量(不需要 $ 符号)
echo APP_VERSION; // 2.0.0
echo DB_HOST; // localhost
// 魔术常量(双下划线开头和结尾)
echo __FILE__; // 当前文件完整路径
echo __LINE__; // 当前行号
echo __DIR__; // 当前文件所在目录
echo __FUNCTION__; // 当前函数名
echo __CLASS__; // 当前类名
变量作用域
<?php
$globalVar = "我是全局变量";
function testScope(): void {
// echo $globalVar; // ❌ 报错!函数内无法直接访问全局变量
// 方式一:global 关键字
global $globalVar;
echo $globalVar; // ✅ 可以了
// 方式二:$GLOBALS 超全局数组
echo $GLOBALS['globalVar']; // ✅ 也可以
// 局部变量
$localVar = "我是局部变量";
// 静态变量:函数调用结束后保留值
static $count = 0;
$count++;
echo "调用次数: $count";
}
testScope(); // 调用次数: 1
testScope(); // 调用次数: 2
testScope(); // 调用次数: 3
🔄 与 Python / JavaScript 对比
| 特性 | PHP | Python | JavaScript |
|---|---|---|---|
| 变量前缀 | $name |
name |
let name |
| 需要声明 | 否 | 否 | 是(let/const/var) |
| 常量 | const / define() |
无原生常量(约定大写) | const |
| 函数作用域 | 默认隔离全局 | 可读全局,写需 global |
闭包/块级作用域 |
2.2 数据类型
类型一览
| 分类 | 类型 | 示例 | 说明 |
|---|---|---|---|
| 标量 | int |
42, 0xFF, 0b1010, 1_000_000 |
整数,支持十六进制、二进制、下划线分隔 |
float |
3.14, 1.2e3 |
浮点数(双精度) | |
string |
"hello", 'world' |
字符串 | |
bool |
true, false |
布尔值(大小写不敏感) | |
| 复合 | array |
[1, 2, 3] |
有序映射(兼具列表和字典功能) |
object |
new stdClass |
类的实例 | |
| 特殊 | null |
null |
空值,变量未赋值时的默认值 |
| 可调用 | callable |
fn($x) => $x * 2 |
函数、闭包或可调用对象 |
类型检测与转换
<?php
$val = "42";
// 检测类型
echo gettype($val); // string
echo get_debug_type($val); // string(PHP 8.0+,更精确)
var_dump($val); // string(2) "42"
// 类型判断函数
is_int($val); // false
is_string($val); // true
is_numeric($val); // true(字符串 "42" 是数字)
is_null($val); // false
is_array($val); // false
// 类型转换(强制)
$intVal = (int) "42"; // 42
$floatVal = (float) "3.14"; // 3.14
$strVal = (string) 100; // "100"
$boolVal = (bool) ""; // false
$arrVal = (array) "hello"; // ["hello"]
// intval() / floatval() / strval() 函数形式
$num = intval("0xFF", 16); // 255,第二参数指定进制
PHP 8.x 类型声明
<?php
// 基本类型声明
function add(int $a, int $b): int {
return $a + $b;
}
// 可空类型(参数可以传 null)
function findUser(?int $id): ?array {
if ($id === null) {
return null;
}
return ['id' => $id, 'name' => '张三'];
}
// 联合类型(PHP 8.0+)
function formatId(int|string $id): string {
return "ID: $id";
}
// 交叉类型(PHP 8.1+)
function process(Iterator&Countable $collection): void {
foreach ($collection as $item) {
echo $item;
}
}
// DNF 类型(PHP 8.2+,组合联合与交叉)
function handle((Iterator&Countable)|null $data): void {
// ...
}
// 返回 never(PHP 8.1+,函数永不返回)
function throwError(string $msg): never {
throw new RuntimeException($msg);
}
// 返回 void(无返回值)
function logMessage(string $msg): void {
echo "[LOG] $msg\n";
}
// strict_types 严格模式(文件级别)
declare(strict_types=1);
// 开启后 add("3", "4") 将抛出 TypeError(不再自动转换)
2.3 字符串操作
单引号 vs 双引号
<?php
$name = "张三";
$age = 25;
// 双引号:支持变量插值和转义序列
echo "你好,$name!\n"; // 你好,张三!
echo "年龄:{$age}岁\n"; // 年龄:25岁
echo "花括号用于复杂表达式:{$user['name']}\n";
// 单引号:原样输出(性能略好)
echo '你好,$name!\n'; // 你好,$name!\n(原样输出)
// 字符串拼接用 .(点号),不是 +
$greeting = "你好," . $name . "!";
$greeting .= " 欢迎光临。"; // .= 追加赋值
Heredoc 与 Nowdoc
<?php
$name = "张三";
// Heredoc:类似双引号,支持变量插值
$html = <<<HTML
<div class="card">
<h2>{$name} 的主页</h2>
<p>欢迎来到我的网站</p>
</div>
HTML;
// Nowdoc:类似单引号,不解析变量
$template = <<<'TPL'
Hello, $name!
这里不会做变量替换。
TPL;
echo $html; // 变量被替换
echo $template; // 原样输出 $name
常用字符串函数
<?php
$str = " Hello, PHP World! ";
// 长度与查找
echo strlen($str); // 23(含空格)
echo mb_strlen("你好世界"); // 4(多字节安全)
echo strpos($str, "PHP"); // 9(返回位置,0 起始)
echo str_contains($str, "PHP"); // true(PHP 8.0+)
echo str_starts_with($str, " He"); // true(PHP 8.0+)
echo str_ends_with($str, "! "); // true(PHP 8.0+)
// 截取
echo substr($str, 9, 3); // "PHP"
echo mb_substr("你好世界", 0, 2); // "你好"
// 分割与合并
$parts = explode(",", "a,b,c,d"); // ["a", "b", "c", "d"]
$joined = implode(" | ", $parts); // "a | b | c | d"
// 替换
echo str_replace("PHP", "Laravel", $str); // " Hello, Laravel World! "
echo str_ireplace("php", "Laravel", $str); // 不区分大小写替换
// 格式化
echo sprintf("用户: %s, 年龄: %d, 余额: %.2f", "张三", 25, 1234.5);
// 用户: 张三, 年龄: 25, 余额: 1234.50
// 清理
echo trim($str); // "Hello, PHP World!"(去除两端空白)
echo ltrim($str); // 去除左侧
echo rtrim($str); // 去除右侧
// 大小写转换
echo strtolower("HELLO"); // "hello"
echo strtoupper("hello"); // "HELLO"
echo ucfirst("hello world"); // "Hello world"
echo ucwords("hello world"); // "Hello World"
// 其他常用
echo str_repeat("=-", 20); // 重复字符串
echo str_pad("42", 5, "0", STR_PAD_LEFT); // "00042"
echo nl2br("第一行\n第二行"); // "第一行<br />\n第二行"
💡 多字节字符串
处理中文等多字节字符时,务必使用 mb_ 前缀的函数(如 mb_strlen、mb_substr),否则可能得到错误的长度或截断结果。确保 php.ini 中设置了 mbstring.internal_encoding = UTF-8。
2.4 数组(PHP 最核心的数据结构)
PHP 数组是有序映射(ordered map),它同时充当了列表、字典、集合、栈、队列等多种数据结构的角色。这是 PHP 最重要也是使用最频繁的类型。
🔄 与 Python / JavaScript 对比
PHP 的 array ≈ Python 的 list + dict 的合体。一个 PHP 数组既可以当索引列表用([1, 2, 3]),也可以当关联字典用(['name' => '张三']),甚至可以混用。JavaScript 中类似的概念是 Array + Object,但 PHP 数组的功能更统一。
创建数组
<?php
// 索引数组(类似 Python list)
$fruits = ["苹果", "香蕉", "橙子"];
$numbers = [1, 2, 3, 4, 5];
// 关联数组(类似 Python dict)
$user = [
'name' => '张三',
'age' => 28,
'email' => 'zhangsan@example.com',
]; // 末尾逗号是允许的(推荐写法)
// 多维数组
$matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
// 复杂嵌套
$users = [
['name' => '张三', 'age' => 28, 'skills' => ['PHP', 'MySQL']],
['name' => '李四', 'age' => 32, 'skills' => ['Go', 'Docker']],
];
// 访问元素
echo $fruits[0]; // "苹果"
echo $user['name']; // "张三"
echo $users[0]['skills'][1]; // "MySQL"
// 修改与添加
$fruits[] = "葡萄"; // 追加到末尾
$fruits[0] = "红苹果"; // 修改第一个
$user['phone'] = '13800138000'; // 添加新键
// 删除元素
unset($fruits[1]); // 删除索引 1(注意:不会重新索引)
$fruits = array_values($fruits); // 重新索引
解构赋值
<?php
// 索引数组解构
[$a, $b, $c] = [10, 20, 30];
echo $b; // 20
// 跳过元素
[, , $third] = [10, 20, 30];
echo $third; // 30
// 关联数组解构
$user = ['name' => '张三', 'age' => 28, 'city' => '北京'];
['name' => $name, 'age' => $age] = $user;
echo "$name, $age"; // 张三, 28
// 在 foreach 中解构
$points = [['x' => 1, 'y' => 2], ['x' => 3, 'y' => 4]];
foreach ($points as ['x' => $x, 'y' => $y]) {
echo "($x, $y) "; // (1, 2) (3, 4)
}
Spread 运算符
<?php
// 合并数组(PHP 7.4+)
$first = [1, 2, 3];
$second = [4, 5, 6];
$merged = [...$first, ...$second]; // [1, 2, 3, 4, 5, 6]
// 关联数组 spread(PHP 8.1+)
$defaults = ['color' => 'blue', 'size' => 'M'];
$custom = ['size' => 'L', 'weight' => '200g'];
$config = [...$defaults, ...$custom];
// ['color' => 'blue', 'size' => 'L', 'weight' => '200g']
// 用于函数参数展开
function sum(int ...$nums): int {
return array_sum($nums);
}
$numbers = [1, 2, 3, 4, 5];
echo sum(...$numbers); // 15
常用数组函数
<?php
$arr = [3, 1, 4, 1, 5, 9, 2, 6];
// ---- 基础操作 ----
echo count($arr); // 8(元素个数)
echo in_array(5, $arr); // true(是否存在)
echo array_search(4, $arr); // 2(查找位置)
$user = ['name' => '张三', 'age' => 28];
echo array_key_exists('name', $user); // true
$keys = array_keys($user); // ['name', 'age']
$vals = array_values($user); // ['张三', 28]
// ---- 增删改 ----
array_push($arr, 7); // 末尾添加 → [..., 7]
array_pop($arr); // 弹出末尾
array_unshift($arr, 0); // 头部添加
array_shift($arr); // 弹出头部
$sliced = array_slice($arr, 2, 3); // 从索引2取3个
array_splice($arr, 2, 1, [40, 50]); // 替换:删1个,插入2个
// ---- 合并与组合 ----
$merged = array_merge([1, 2], [3, 4]); // [1, 2, 3, 4]
$combined = array_combine(['a', 'b'], [1, 2]); // ['a'=>1, 'b'=>2]
$unique = array_unique([1, 2, 2, 3, 3]); // [1, 2, 3]
$flipped = array_flip(['a' => 1, 'b' => 2]); // [1 => 'a', 2 => 'b']
// ---- 排序(原地修改) ----
$nums = [3, 1, 4, 1, 5];
sort($nums); // [1, 1, 3, 4, 5] 值排序,重新索引
rsort($nums); // [5, 4, 3, 1, 1] 值逆序
asort($nums); // 值排序,保留键名
ksort($nums); // 按键排序
// 自定义排序
$items = ['banana', 'apple', 'cherry'];
usort($items, fn($a, $b) => strcmp($a, $b)); // 字母序
函数式操作
<?php
$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// array_map:对每个元素执行操作,返回新数组
$doubled = array_map(fn($n) => $n * 2, $numbers);
// [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// array_filter:过滤,保留回调返回 true 的元素
$evens = array_filter($numbers, fn($n) => $n % 2 === 0);
// [2, 4, 6, 8, 10](注意:保留原始键名)
// array_reduce:归约
$sum = array_reduce($numbers, fn($carry, $n) => $carry + $n, 0);
// 55
// array_walk:遍历并修改(原地)
$prices = ['apple' => 5, 'banana' => 3, 'cherry' => 8];
array_walk($prices, function(&$price, $name) {
$price = $price * 1.1; // 加价 10%
});
// array_column:从多维数组中提取一列
$users = [
['id' => 1, 'name' => '张三', 'dept' => '技术'],
['id' => 2, 'name' => '李四', 'dept' => '产品'],
['id' => 3, 'name' => '王五', 'dept' => '技术'],
];
$names = array_column($users, 'name'); // ['张三', '李四', '王五']
$nameById = array_column($users, 'name', 'id'); // [1=>'张三', 2=>'李四', 3=>'王五']
2.5 控制流
条件判断
<?php
$score = 85;
// if / elseif / else
if ($score >= 90) {
echo "优秀";
} elseif ($score >= 70) {
echo "良好";
} elseif ($score >= 60) {
echo "及格";
} else {
echo "不及格";
}
// 三元运算符
$status = $score >= 60 ? "通过" : "未通过";
// 短三元(Elvis 运算符)
$displayName = $username ?: "匿名用户"; // $username 为假值时用默认值
// null 合并运算符(后面详细讲)
$name = $_GET['name'] ?? '游客';
match 表达式(PHP 8.0+)
match 是 switch 的现代替代品,使用严格比较(===),且是表达式(有返回值)。
<?php
$statusCode = 404;
// match 表达式:简洁、严格比较、有返回值
$message = match($statusCode) {
200 => '成功',
301 => '永久重定向',
302 => '临时重定向',
400 => '错误请求',
401 => '未授权',
403 => '禁止访问',
404 => '页面未找到',
500 => '服务器错误',
default => '未知状态码',
};
echo $message; // "页面未找到"
// 多值匹配
$lang = 'zh-CN';
$greeting = match($lang) {
'zh-CN', 'zh-TW' => '你好',
'en-US', 'en-GB' => 'Hello',
'ja' => 'こんにちは',
default => 'Hi',
};
// 无参数 match(替代 if-elseif 链)
$score = 85;
$grade = match(true) {
$score >= 90 => 'A',
$score >= 80 => 'B',
$score >= 70 => 'C',
$score >= 60 => 'D',
default => 'F',
};
💡 match vs switch
match 使用严格比较 ===,不需要 break,并且是表达式可以赋值。如果没有匹配且没有 default,会抛出 UnhandledMatchError。建议在 PHP 8+ 项目中优先使用 match 替代 switch。
循环
<?php
// for 循环
for ($i = 0; $i < 5; $i++) {
echo $i; // 0 1 2 3 4
}
// while 循环
$count = 0;
while ($count < 3) {
echo $count++;
}
// do-while(至少执行一次)
$input = '';
do {
$input = readline("输入 'quit' 退出: ");
} while ($input !== 'quit');
// foreach —— PHP 中最常用的循环
$colors = ['red', 'green', 'blue'];
// 遍历值
foreach ($colors as $color) {
echo $color;
}
// 遍历键值对
$user = ['name' => '张三', 'age' => 28, 'city' => '北京'];
foreach ($user as $key => $value) {
echo "$key: $value\n";
}
// 通过引用修改元素
$prices = [10, 20, 30];
foreach ($prices as &$price) {
$price *= 1.1; // 每个价格加 10%
}
unset($price); // ⚠️ 务必 unset 引用变量,否则后续代码可能出错
// 嵌套遍历多维数组
$users = [
['name' => '张三', 'skills' => ['PHP', 'MySQL']],
['name' => '李四', 'skills' => ['Go', 'Redis']],
];
foreach ($users as $user) {
echo "{$user['name']} 的技能: ";
foreach ($user['skills'] as $skill) {
echo "$skill ";
}
echo "\n";
}
// 循环控制
for ($i = 0; $i < 10; $i++) {
if ($i === 3) continue; // 跳过 3
if ($i === 7) break; // 到 7 停止
echo $i; // 0 1 2 4 5 6
}
2.6 运算符
算术与赋值运算符
<?php
// 算术
$a = 10;
$b = 3;
echo $a + $b; // 13 加
echo $a - $b; // 7 减
echo $a * $b; // 30 乘
echo $a / $b; // 3.33 除
echo $a % $b; // 1 取模
echo $a ** $b; // 1000 幂运算
// 复合赋值
$x = 10;
$x += 5; // $x = $x + 5 → 15
$x -= 3; // 12
$x *= 2; // 24
$x /= 4; // 6
$x %= 4; // 2
$x **= 3; // 8
$x .= "px"; // "8px"(字符串拼接赋值)
// 自增自减
$i = 5;
echo $i++; // 5(先返回再自增)
echo ++$i; // 7(先自增再返回)
比较运算符
⚠️ 必须掌握:== 与 === 的区别
PHP 的 ==(宽松比较)会做类型转换,这导致了许多令人困惑的行为。强烈建议始终使用 ===(严格比较)。
<?php
// == 宽松比较(会类型转换,各种坑)
var_dump(0 == "foo"); // PHP 8: false(PHP 7 是 true!)
var_dump("" == false); // true
var_dump("0" == false); // true
var_dump(null == false); // true
var_dump("" == null); // true
var_dump(0 == null); // true
var_dump("1" == true); // true
var_dump("123" == 123); // true
// === 严格比较(类型和值都必须相同)
var_dump(0 === false); // false(int vs bool)
var_dump("" === false); // false(string vs bool)
var_dump("123" === 123); // false(string vs int)
var_dump(null === false); // false(null vs bool)
// ✅ 经验法则:永远用 === 和 !==
逻辑运算符
<?php
$a = true;
$b = false;
echo $a && $b; // false 与(短路求值)
echo $a || $b; // true 或(短路求值)
echo !$a; // false 非
// and / or 优先级低于 && / ||,不建议混用
// 推荐始终使用 && || !
Null 相关运算符
<?php
// null 合并运算符 ??(PHP 7.0+)
// 左侧为 null 或未定义时使用右侧值
$name = $_GET['name'] ?? '游客';
$config = $settings['timeout'] ?? $defaults['timeout'] ?? 30;
// null 合并赋值 ??=(PHP 7.4+)
$data['count'] ??= 0; // 等价于 $data['count'] = $data['count'] ?? 0;
// null 安全运算符 ?->(PHP 8.0+)
// 对象为 null 时不报错,直接返回 null
$user = getUser(42); // 可能返回 null
$city = $user?->getAddress()?->getCity();
// 等价于:$city = ($user !== null && $user->getAddress() !== null)
// ? $user->getAddress()->getCity() : null;
太空船运算符
<?php
// 太空船运算符 <=>(PHP 7.0+)
// 返回 -1(小于)、0(等于)、1(大于)
echo 1 <=> 2; // -1
echo 2 <=> 2; // 0
echo 3 <=> 2; // 1
// 常用于排序回调
$users = [
['name' => '张三', 'age' => 28],
['name' => '李四', 'age' => 22],
['name' => '王五', 'age' => 35],
];
usort($users, fn($a, $b) => $a['age'] <=> $b['age']);
// 按年龄升序:李四(22) → 张三(28) → 王五(35)
// 多字段排序
usort($users, fn($a, $b) =>
$a['age'] <=> $b['age'] ?: $a['name'] <=> $b['name']
);
2.7 错误处理
try / catch / finally
<?php
try {
$result = riskyOperation();
echo "结果: $result";
} catch (InvalidArgumentException $e) {
echo "参数错误: " . $e->getMessage();
} catch (RuntimeException $e) {
echo "运行时错误: " . $e->getMessage();
} catch (Exception $e) {
echo "其他异常: " . $e->getMessage();
} finally {
echo "无论是否异常都会执行(清理资源等)";
}
// 联合捕获(PHP 8.0+)
try {
processInput($data);
} catch (TypeError | ValueError $e) {
echo "输入错误: " . $e->getMessage();
}
// 仅捕获不使用变量(PHP 8.0+)
try {
json_decode($json, flags: JSON_THROW_ON_ERROR);
} catch (JsonException) {
echo "JSON 解析失败";
}
异常层次结构
<?php
// PHP 内置异常层次
// Throwable
// ├── Error (引擎级错误,通常不捕获)
// │ ├── TypeError
// │ ├── ValueError (PHP 8.0+)
// │ ├── ArithmeticError
// │ │ └── DivisionByZeroError
// │ └── ...
// └── Exception (应用级异常)
// ├── RuntimeException
// ├── InvalidArgumentException
// ├── LogicException
// ├── OverflowException
// └── ...
// 自定义异常
class BusinessException extends RuntimeException
{
public function __construct(
string $message,
private readonly string $errorCode,
int $code = 0,
?\Throwable $previous = null,
) {
parent::__construct($message, $code, $previous);
}
public function getErrorCode(): string
{
return $this->errorCode;
}
}
class InsufficientBalanceException extends BusinessException {}
// 使用自定义异常
function withdraw(float $amount, float $balance): float
{
if ($amount <= 0) {
throw new InvalidArgumentException("取款金额必须大于 0");
}
if ($amount > $balance) {
throw new InsufficientBalanceException(
"余额不足",
errorCode: 'INSUFFICIENT_BALANCE',
);
}
return $balance - $amount;
}
try {
$newBalance = withdraw(500, 100);
} catch (InsufficientBalanceException $e) {
echo "{$e->getMessage()} (错误码: {$e->getErrorCode()})";
// 余额不足 (错误码: INSUFFICIENT_BALANCE)
}
全局错误处理
<?php
// 将传统 PHP 错误转为异常
set_error_handler(function (int $severity, string $message, string $file, int $line): bool {
if (!(error_reporting() & $severity)) {
return false;
}
throw new ErrorException($message, 0, $severity, $file, $line);
});
// 全局异常处理器(兜底)
set_exception_handler(function (Throwable $e): void {
error_log("未捕获的异常: {$e->getMessage()} 在 {$e->getFile()}:{$e->getLine()}");
if (php_sapi_name() === 'cli') {
echo "致命错误: {$e->getMessage()}\n";
} else {
http_response_code(500);
echo "<h1>服务器内部错误</h1>";
}
});
// 关闭函数(捕获致命错误)
register_shutdown_function(function (): void {
$error = error_get_last();
if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR])) {
error_log("致命错误: {$error['message']}");
}
});
2.8 本章要点
💲 变量与常量
- • 变量用
$前缀,无需声明类型 - •
const定义编译时常量,define()定义运行时常量 - • 函数内默认无法访问全局变量(需
global)
🏷️ 类型系统
- • PHP 8.x 支持完整类型声明:联合类型、交叉类型、nullable
- •
declare(strict_types=1)启用严格模式 - • 多字节字符串用
mb_系列函数
📦 数组
- • PHP 数组 = 有序映射,兼具列表和字典功能
- •
array_map、array_filter、array_reduce实现函数式操作 - • Spread 运算符
...用于合并和展开
🔀 控制流
- •
match表达式替代switch,严格比较有返回值 - •
foreach是遍历数组的首选 - • 引用遍历后务必
unset引用变量
⚖️ 运算符
- • 始终用
===而不是== - •
??null 合并、?->null 安全调用 - •
<=>太空船运算符简化排序
🛡️ 错误处理
- •
try/catch/finally+ 自定义异常层次 - • PHP 8: 联合捕获
catch (A | B $e) - •
set_error_handler将传统错误转为异常
掌握了基础语法后,下一章将学习 函数与面向对象编程 —— PHP 8.x 的类系统功能丰富,是现代 PHP 开发的基石。