isset / unset / empty
概述
isset()、unset() 和 empty() 是 PHP 中处理变量的三个核心语言结构(不是函数)。它们用于检查变量是否存在、销毁变量以及检查变量是否为"空值"。理解它们的区别和正确用法是编写健壮 PHP 代码的基础。
PHP 版本说明
isset()和empty()是语言结构,不是函数,不能用可变函数调用- PHP 7.0 引入
??(null 合并运算符),可以部分替代isset()检查 - PHP 7.4 引入
??=(null 合并赋值运算符) isset()支持多参数自 PHP 5 起可用
基础概念
三个语言结构对比
| 语言结构 | 功能 | 对未定义变量 | 对 null | 返回值类型 |
|---|---|---|---|---|
isset($var) | 检查变量是否已设置且非 null | 返回 false(不报错) | 返回 false | bool |
unset($var) | 销毁变量 | 不报错 | 销毁变量 | void |
empty($var) | 检查变量是否为空 | 返回 true(不报错) | 返回 true | bool |
语言结构 vs 函数
isset、unset、empty 是语言结构(language construct),不是函数。它们不能被可变函数调用:$func = 'isset'; $func($var); 会导致错误。
语法与代码
isset() 详解
isset() 检查变量是否已设置且不为 null。如果变量存在且值不为 null,返回 true,否则返回 false。
php
<?php
declare(strict_types=1);
// 已定义且非 null
$name = 'Alice';
echo isset($name) ? 'set' : 'not set'; // set
// 已定义但为 null
$age = null;
echo isset($age) ? 'set' : 'not set'; // not set
// 未定义的变量
echo isset($undefined) ? 'set' : 'not set'; // not set(不报错)
// 检查数组键
$data = ['name' => 'Alice', 'email' => 'alice@example.com'];
echo isset($data['name']) ? 'set' : 'not set'; // set
echo isset($data['phone']) ? 'set' : 'not set'; // not set(不报错)
// 多参数 isset(全部设置才返回 true)
$a = 1;
$b = 2;
$c = null;
var_dump(isset($a, $b)); // true
var_dump(isset($a, $b, $c)); // false($c 为 null)isset 与直接比较
isset($var) 等价于 array_key_exists('key', $array) && $array['key'] !== null。对于数组,如果需要区分"键不存在"和"值为 null",应使用 array_key_exists()。
unset() 详解
unset() 销毁指定的变量,使其不再存在。
php
<?php
declare(strict_types=1);
// 销毁变量
$name = 'Alice';
echo $name . "\n"; // Alice
unset($name);
echo isset($name) ? 'set' : 'not set'; // not set
// echo $name; // Undefined variable
// 销毁数组元素
$user = ['name' => 'Alice', 'age' => 30, 'email' => 'alice@example.com'];
unset($user['email']);
print_r($user);
// Array ( [name] => Alice [age] => 30 )
// 销毁多个变量
$a = 1;
$b = 2;
$c = 3;
unset($a, $b, $c);
// unset 对引用的影响
$x = 'hello';
$ref = &$x; // $ref 是 $x 的引用
unset($x); // 只销毁 $x,不影响 $ref
echo $ref . "\n"; // hello($ref 仍然存在)
// 销毁全局变量(在函数内部)
$globalVar = 'I am global';
function destroyGlobal(): void
{
// global $globalVar;
// unset($globalVar); // 只销毁局部引用,不影响全局变量
unset($GLOBALS['globalVar']); // 销毁真正的全局变量
}
destroyGlobal();
echo isset($globalVar) ? 'exists' : 'destroyed'; // destroyedempty() 详解
empty() 检查变量是否被视为"空"。以下值被认为是空的:
''(空字符串)0(整数 0)0.0(浮点数 0)'0'(字符串 '0')nullfalse[](空数组)$undefinedVar(未定义变量)
php
<?php
declare(strict_types=1);
// 被视为空的值
$emptyValues = [
'' => 'empty string',
0 => 'integer zero',
0.0 => 'float zero',
'0' => 'string zero',
null => 'null',
false => 'false',
[] => 'empty array',
];
foreach ($emptyValues as $value => $description) {
echo empty($value) ? "[EMPTY] {$description}\n" : "[NOT EMPTY] {$description}\n";
}
// 不被视为空的值
$nonEmptyValues = [
'hello' => 'non-empty string',
1 => 'non-zero integer',
true => 'true',
[0] => 'non-empty array',
'false' => 'string "false"',
];
foreach ($nonEmptyValues as $value => $description) {
echo empty($value) ? "[EMPTY] {$description}\n" : "[NOT EMPTY] {$description}\n";
}
// empty 不会产生 Undefined variable 警告
echo empty($undefinedVar) ? 'empty' : 'not empty'; // empty(安全)'0' 被视为空
字符串 '0' 被 empty() 视为空值,这在处理表单输入和 API 参数时需要特别注意。建议使用更精确的比较来判断。
与 null 比较的区别
php
<?php
declare(strict_types=1);
$var = null;
// isset vs === null
var_dump(isset($var)); // false
var_dump($var === null); // true
var_dump($var ?? 'default'); // 'default'
// 未定义的变量
// var_dump($undefined === null); // Undefined variable(报错)
var_dump(isset($undefined)); // false(不报错)
var_dump($undefined ?? 'default'); // 'default'(不报错)
// empty vs === null
$zero = 0;
var_dump(empty($zero)); // true(0 被视为空)
var_dump($zero === null); // false(0 不是 null)
var_dump($zero ?? 'default'); // 0(0 不是 null,所以返回 0)
// 关键区别总结
// isset($var) → 变量存在且不为 null
// $var === null → 变量值是 null(变量必须存在)
// empty($var) → 变量为假值(包括 0, '', '0', false, null, [], 未定义)
// $var ?? 'default' → 变量存在且不为 null,否则返回默认值$var ?? 'default' 替代 isset
PHP 7.0+ 的 null 合并运算符 ?? 是 isset() 检查的简洁替代。
php
<?php
declare(strict_types=1);
// 旧写法
$name = isset($_GET['name']) ? $_GET['name'] : 'Anonymous';
// 新写法(PHP 7.0+)
$name = $_GET['name'] ?? 'Anonymous';
// 多层嵌套
// 旧写法
$user = isset($data['user']) ? (isset($data['user']['name']) ? $data['user']['name'] : 'Unknown') : 'Unknown';
// 新写法
$user = $data['user']['name'] ?? 'Unknown'; // 如果 $data['user'] 不存在也不会报错
// null 合并赋值(PHP 7.4+)
$config = [];
$config['debug'] ??= false; // 相当于: $config['debug'] = $config['debug'] ?? false
$config['cache'] ??= true;
$config['timeout'] ??= 30;
print_r($config);
// Array ( [debug] => false [cache] => true [timeout] => 30 )详细说明
isset 的短路行为
isset() 是短路运算:如果第一个参数就不满足条件,不会继续检查后续参数。
php
<?php
declare(strict_types=1);
$data = ['user' => null];
// isset 短路:$data['user'] 为 null,不会访问 $data['user']['name']
var_dump(isset($data['user'], $data['user']['name'])); // false
// 如果 $data['user'] 不为 null,才会检查第二个参数
$data['user'] = ['name' => 'Alice'];
var_dump(isset($data['user'], $data['user']['name'])); // true
// 空合并运算符也有短路行为
$result = $data['user']['name'] ?? $data['user']['email'] ?? 'Unknown';
echo $result . "\n"; // Alice数组操作中的 isset vs array_key_exists
php
<?php
declare(strict_types=1);
$arr = ['a' => 1, 'b' => null, 'c' => 0];
// isset vs array_key_exists
var_dump(isset($arr['a'])); // true
var_dump(array_key_exists('a', $arr)); // true
var_dump(isset($arr['b'])); // false(值为 null)
var_dump(array_key_exists('b', $arr)); // true(键存在)
var_dump(isset($arr['d'])); // false(键不存在)
var_dump(array_key_exists('d', $arr)); // false(键不存在)
// PHP 8.0+ 可以使用 $arr['key'] ?? null 替代 array_key_exists(但语义略有不同)
var_dump($arr['b'] ?? null); // null(键存在但值为 null,返回 null,无法区分)
var_dump($arr['d'] ?? null); // null(键不存在,同样返回 null)选择建议
- 需要区分"键不存在"和"值为 null"时,使用
array_key_exists() - 只关心值是否可用(非 null)时,使用
isset()或??
实战示例
安全的请求数据获取
php
<?php
declare(strict_types=1);
function getRequestParam(array $params, string $key, mixed $default = null): mixed
{
return $params[$key] ?? $default;
}
function getIntParam(array $params, string $key, int $default = 0): int
{
$value = $params[$key] ?? null;
if ($value === null) {
return $default;
}
return is_numeric($value) ? (int) $value : $default;
}
function getStringParam(array $params, string $key, string $default = ''): string
{
$value = $params[$key] ?? null;
if ($value === null) {
return $default;
}
return is_string($value) ? trim($value) : $default;
}
// 模拟请求参数
$_GET = ['page' => '2', 'search' => ' php ', 'sort' => ''];
$page = getIntParam($_GET, 'page', 1);
$search = getStringParam($_GET, 'search', '');
$sort = getStringParam($_GET, 'sort', 'date');
$limit = getIntParam($_GET, 'limit', 20);
echo "page: {$page}, search: '{$search}', sort: '{$sort}', limit: {$limit}\n";
// page: 2, search: 'php', sort: 'date', limit: 20条件性清理变量
php
<?php
declare(strict_types=1);
function processLargeData(array &$data): void
{
$result = [];
foreach ($data as $key => $value) {
$result[$key] = processItem($value);
}
// 清理原始数据释放内存
$data = [];
// 处理完成,可以 unset 临时变量
unset($result);
}
function processItem(mixed $item): mixed
{
return $item;
}
$largeData = array_fill(0, 100000, 'data');
processLargeData($largeData);
echo empty($largeData) ? 'cleared' : 'still has data'; // still has data(函数内 unset 只影响局部引用)注意事项
常见陷阱
- empty('0') 返回 true
php
<?php
declare(strict_types=1);
$statusCode = '0';
// 错误:empty('0') 返回 true
if (empty($statusCode)) {
echo "Status is empty\n"; // 实际执行了这里
}
// 正确:使用精确比较
if ($statusCode === '') {
echo "Status is empty string\n";
}- unset 不影响引用链中的其他变量(只解除当前变量)
php
<?php
declare(strict_types=1);
$a = 'hello';
$b = &$a;
$c = &$a;
unset($a);
// $b 和 $c 仍然存在且值为 'hello'
echo $b . "\n"; // hello
echo $c . "\n"; // hello- 函数参数默认不会检查 isset
php
<?php
declare(strict_types=1);
function process(string $name): void
{
// 如果传入未定义的变量会报错
echo $name . "\n";
}
// $undefined 未定义
// process($undefined); // Undefined variable
// 安全方式
$undefined ??= 'default';
process($undefined ?? 'default');最佳实践
1. 使用 ?? 替代 isset + 三元运算符
php
<?php
declare(strict_types=1);
// 推荐
$name = $data['name'] ?? 'Anonymous';
$age = $data['age'] ?? 0;
$active = $data['active'] ?? true;
// 不推荐
$name = isset($data['name']) ? $data['name'] : 'Anonymous';2. empty 要谨慎使用,优先精确判断
php
<?php
declare(strict_types=1);
// 不推荐:empty 太宽泛
if (!empty($value)) { ... }
// 推荐:精确判断
if ($value !== null && $value !== '') { ... }
if ($value !== '' && $value !== 0) { ... }3. 及时释放不再需要的变量
php
<?php
declare(strict_types=1);
function processFile(string $path): array
{
$content = file_get_contents($path);
$result = json_decode($content, true);
unset($content); // 处理完后及时释放
return $result;
}