基础不牢,地动山摇!PHP判断函数是每个开发者必须精通的基本功
在日常开发中,我们几乎每行代码都在做判断。变量是否存在?数据是否合法?条件是否满足?这些看似简单的判断,却是构建稳定应用的基石。
据不完全统计,PHP项目中近30%的bug都与判断逻辑不当有关。今天,我们就系统梳理PHP的所有判断函数,帮你打好扎实的基础!
一、变量存在性判断:isset vs empty vs is_null
1.1 三者的根本区别
// 测试数据
$var1 = null;
$var2 = '';
$var3 = 0;
$var4 = false;
$var5 = '0';
$var6 = [];
$var7 = 'Hello';
// 对比结果
echo "变量 | isset | empty | is_null\n";
echo "null | " . (isset($var1) ? 'true' : 'false') . " | " . (empty($var1) ? 'true' : 'false') . " | " . (is_null($var1) ? 'true' : 'false') . "\n";
echo "空字符串 | " . (isset($var2) ? 'true' : 'false') . " | " . (empty($var2) ? 'true' : 'false') . " | " . (is_null($var2) ? 'true' : 'false') . "\n";
echo "数字0 | " . (isset($var3) ? 'true' : 'false') . " | " . (empty($var3) ? 'true' : 'false') . " | " . (is_null($var3) ? 'true' : 'false') . "\n";
执行结果:
变量 | isset | empty | is_null
null | false | true | true
空字符串 | true | true | false
数字0 | true | true | false
false | true | true | false
'0' | true | true | false
空数组 | true | true | false
'Hello' | true | false | false
1.2 实际应用场景
// 表单数据处理
if (isset($_POST['username'])) {
// 用户提交了用户名
$username = trim($_POST['username']);
if (!empty($username)) {
// 用户名非空,进行进一步处理
processUsername($username);
} else {
// 用户名为空
throw new Exception('用户名不能为空');
}
} else {
// 用户名字段不存在
throw new Exception('缺少必要参数');
}
// 配置项读取
$config = [
'debug' => true,
'cache_time' => 3600
];
// 安全读取配置,提供默认值
$debugMode = isset($config['debug']) ? $config['debug'] : false;
$cacheTime = $config['cache_time'] ?? 3600; // PHP 7+ 空合并运算符
二、数据类型判断:让代码类型安全
2.1 基本类型判断函数
// 数值类型判断
$var = 42;
is_int($var); // true
is_integer($var); // true - is_int的别名
is_long($var); // true - 同样是别名
$var = 3.14;
is_float($var); // true
is_double($var); // true - is_float的别名
// 字符串判断
$var = "Hello";
is_string($var); // true
// 布尔值判断
$var = true;
is_bool($var); // true
2.2 复合类型判断
// 数组判断
$var = [1, 2, 3];
is_array($var); // true
// 对象判断
$var = new stdClass();
is_object($var); // true
// 可调用判断
$var = function() { return 'hello'; };
is_callable($var); // true
// 迭代器判断(PHP 7.1+)
$var = new ArrayIterator([1,2,3]);
is_iterable($var); // true
2.3 特殊类型判断
// 资源判断
$file = fopen('test.txt', 'r');
is_resource($file); // true
fclose($file);
// NULL判断
$var = null;
is_null($var); // true
// 标量判断(单值类型)
is_scalar(123); // true - 整数是标量
is_scalar('hello'); // true - 字符串是标量
is_scalar([1,2]); // false - 数组不是标量
is_scalar(new stdClass()); // false - 对象不是标量
三、比较运算符:== 与 === 的终极对决
3.1 松散比较(==)的隐式转换
// 经典陷阱案例
echo "松散比较结果:\n";
echo "'123' == 123: " . ('123' == 123 ? 'true' : 'false') . "\n"; // true
echo "'123abc' == 123: " . ('123abc' == 123 ? 'true' : 'false') . "\n"; // true - 注意!
echo "'0' == false: " . ('0' == false ? 'true' : 'false') . "\n"; // true
echo "'' == false: " . ('' == false ? 'true' : 'false') . "\n"; // true
echo "0 == false: " . (0 == false ? 'true' : 'false') . "\n"; // true
3.2 严格比较(===)的类型安全
echo "\n严格比较结果:\n";
echo "'123' === 123: " . ('123' === 123 ? 'true' : 'false') . "\n"; // false
echo "'0' === false: " . ('0' === false ? 'true' : 'false') . "\n"; // false
echo "0 === false: " . (0 === false ? 'true' : 'false') . "\n"; // false
3.3 最佳实践建议
// 数据库ID比较
$userIdFromDb = '123'; // 数据库返回的字符串
$userIdFromInput = 123; // 用户输入的整数
// 错误做法
if ($userIdFromDb == $userIdFromInput) {
// 这里会执行,但可能不是你想要的结果
}
// 正确做法
if ((int)$userIdFromDb === $userIdFromInput) {
// 明确类型转换后比较
}
// 或者
if ($userIdFromDb === (string)$userIdFromInput) {
// 统一类型后比较
}
四、字符串判断:PHP 8新特性革命
4.1 传统字符串判断方法
$email = 'user@example.com';
$url = 'https://www.example.com';
$filename = 'document.pdf';
// 判断是否包含子串
if (strpos($email, '@') !== false) {
echo "有效的邮箱地址\n";
}
// 判断是否以某字符串开头
if (strpos($url, 'https://') === 0) {
echo "使用HTTPS协议\n";
}
// 判断是否以某字符串结尾
if (substr($filename, -4) === '.pdf') {
echo "PDF文件\n";
}
4.2 PHP 8新函数的优雅写法
// 更直观的字符串判断
if (str_contains($email, '@')) {
echo "有效的邮箱地址\n";
}
if (str_starts_with($url, 'https://')) {
echo "使用HTTPS协议\n";
}
if (str_ends_with($filename, '.pdf')) {
echo "PDF文件\n";
}
4.3 大小写不敏感判断
$text = "Hello World";
// 传统方法
if (stripos($text, 'hello') !== false) {
echo "包含hello(不区分大小写)\n";
}
// 如果需要使用新函数,可以结合strtolower
if (str_contains(strtolower($text), 'hello')) {
echo "包含hello\n";
}
五、数组判断:避免Undefined offset警告
5.1 数组键值存在性检查
$user = [
'name' => '张三',
'age' => 25,
'email' => 'zhangsan@example.com'
];
// 检查键是否存在
if (array_key_exists('email', $user)) {
echo "邮箱字段存在\n";
}
// 检查值是否存在
if (in_array('张三', $user)) {
echo "值'张三'存在于数组中\n";
}
// 搜索值并返回键
$key = array_search(25, $user);
if ($key !== false) {
echo "值25的键是: $key\n";
}
5.2 多维数组判断
$users = [
'user1' => [
'name' => '张三',
'profile' => [
'age' => 25,
'city' => '北京'
]
]
];
// 安全访问多维数组
if (isset($users['user1']['profile']['age'])) {
$age = $users['user1']['profile']['age'];
echo "用户年龄: $age\n";
}
// 使用null合并运算符的链式调用(PHP 7+)
$city = $users['user1']['profile']['city'] ?? '未知';
echo "用户城市: $city\n";
六、文件系统判断:避免文件操作错误
6.1 文件存在性判断
$filename = 'config.php';
// 判断文件是否存在
if (file_exists($filename)) {
echo "文件存在\n";
// 判断是否为普通文件
if (is_file($filename)) {
echo "是普通文件\n";
}
// 判断是否为目录
if (is_dir($filename)) {
echo "是目录\n";
}
// 判断是否可读
if (is_readable($filename)) {
echo "文件可读\n";
}
// 判断是否可写
if (is_writable($filename)) {
echo "文件可写\n";
}
// 判断是否可执行
if (is_executable($filename)) {
echo "文件可执行\n";
}
} else {
echo "文件不存在\n";
}
6.2 文件上传判断
// 处理文件上传
if (isset($_FILES['avatar'])) {
$uploadFile = $_FILES['avatar'];
// 检查上传是否成功
if ($uploadFile['error'] === UPLOAD_ERR_OK) {
// 检查是否为上传文件(安全重要!)
if (is_uploaded_file($uploadFile['tmp_name'])) {
// 检查文件类型
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (in_array($uploadFile['type'], $allowedTypes)) {
// 安全处理文件
move_uploaded_file($uploadFile['tmp_name'], 'uploads/avatar.jpg');
}
}
}
}
七、实战案例:完整的表单验证类
class FormValidator
{
public static function validateRegistration(array $data): array
{
$errors = [];
// 用户名验证
if (!isset($data['username']) || empty(trim($data['username']))) {
$errors['username'] = '用户名不能为空';
} else {
$username = trim($data['username']);
if (!is_string($username)) {
$errors['username'] = '用户名必须是字符串';
} elseif (strlen($username) < 3) {
$errors['username'] = '用户名至少3个字符';
} elseif (strlen($username) > 20) {
$errors['username'] = '用户名不能超过20个字符';
}
}
// 邮箱验证
if (!isset($data['email']) || empty(trim($data['email']))) {
$errors['email'] = '邮箱不能为空';
} else {
$email = trim($data['email']);
if (!is_string($email)) {
$errors['email'] = '邮箱必须是字符串';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors['email'] = '邮箱格式不正确';
}
}
// 年龄验证
if (isset($data['age'])) {
if (!is_numeric($data['age'])) {
$errors['age'] = '年龄必须是数字';
} else {
$age = (int)$data['age'];
if ($age < 0 || $age > 150) {
$errors['age'] = '年龄必须在0-150之间';
}
}
}
// 兴趣验证(数组验证)
if (isset($data['interests'])) {
if (!is_array($data['interests'])) {
$errors['interests'] = '兴趣必须是一个数组';
} elseif (count($data['interests']) > 5) {
$errors['interests'] = '兴趣不能超过5个';
}
}
return $errors;
}
}
// 使用示例
$formData = [
'username' => '张三',
'email' => 'zhangsan@example.com',
'age' => '25', // 字符串形式的数字
'interests' => ['编程', '阅读', '运动']
];
$errors = FormValidator::validateRegistration($formData);
if (empty($errors)) {
echo "表单验证通过!\n";
} else {
echo "验证错误:\n";
print_r($errors);
}
八、性能优化与最佳实践
8.1 判断顺序优化
// 性能较差的写法
if (is_array($data) && count($data) > 0) {
// 先判断类型,再判断长度
}
// 性能更好的写法
if (!empty($data) && is_array($data)) {
// empty()对于非数组返回false,利用短路求值
}
// 实际性能测试对比
$testData = [];
$iterations = 1000000;
// 测试1:先is_array再count
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
if (is_array($testData) && count($testData) > 0) {
// 空操作
}
}
$time1 = microtime(true) - $start;
// 测试2:先empty再is_array
$start = microtime(true);
for ($i = 0; $i < $iterations; $i++) {
if (!empty($testData) && is_array($testData)) {
// 空操作
}
}
$time2 = microtime(true) - $start;
echo "方法1耗时: {$time1}秒\n";
echo "方法2耗时: {$time2}秒\n";
8.2 使用匹配函数提高可读性
// 复杂的条件判断
if (($status === 'active' && $role === 'admin') ||
($status === 'pending' && $role === 'editor') ||
($status === 'active' && $permissions['can_access'])) {
// 复杂的条件逻辑
}
// 使用匹配函数提高可读性
function canAccess($status, $role, $permissions) {
return match(true) {
($status === 'active' && $role === 'admin') => true,
($status === 'pending' && $role === 'editor') => true,
($status === 'active' && $permissions['can_access']) => true,
default => false
};
}
if (canAccess($status, $role, $permissions)) {
// 清晰的逻辑
}
九、常见坑点及解决方案
9.1 strpos函数的0值问题
// 经典错误
$text = "Hello world";
if (strpos($text, 'Hello')) {
// 这里不会执行!因为strpos返回0,0在条件判断中视为false
echo "找到Hello\n";
}
// 正确写法
if (strpos($text, 'Hello') !== false) {
echo "找到Hello\n"; // 这会执行
}
9.2 自动类型转换的陷阱
// 令人困惑的比较结果
echo "陷阱比较:\n";
echo "'0' == false: " . ('0' == false ? 'true' : 'false') . "\n"; // true
echo "'' == false: " . ('' == false ? 'true' : 'false') . "\n"; // true
echo "0 == false: " . (0 == false ? 'true' : 'false') . "\n"; // true
echo "null == false: " . (null == false ? 'true' : 'false') . "\n"; // true
// 解决方案:始终使用严格比较
echo "\n严格比较:\n";
echo "'0' === false: " . ('0' === false ? 'true' : 'false') . "\n"; // false
echo "0 === false: " . (0 === false ? 'true' : 'false') . "\n"; // false
十、总结:PHP判断函数的最佳实践
- 变量检查:优先使用
isset()
检查存在性,empty()
检查是否为空值 - 类型安全:始终使用
===
和!==
进行严格比较 - 字符串操作:PHP 8+ 项目优先使用
str_contains()
等新函数 - 数组访问:使用
array_key_exists()
或 null合并运算符安全访问 - 性能优化:利用短路求值特性,合理安排判断顺序
- 代码可读性:复杂条件判断封装成函数或使用match表达式
记住: 好的判断逻辑是程序稳定性的基石。每次严谨的判断,都是在为代码质量投资!