位运算符
概述
位运算符允许在整数的二进制位级别进行操作。PHP 提供了完整的位运算符集,包括按位与、或、异或、取反以及左移和右移。位运算在权限管理、IP 地址处理、数据压缩等领域有广泛的应用。
适用范围
位运算符的操作数会被转换为整数(int)。对于浮点数会先丢弃小数部分,对于字符串会按其 ASCII 码值进行位运算,但此行为在 PHP 7.x 后已弃用。
基础概念
运算符一览
| 运算符 | 名称 | 说明 |
|---|---|---|
$a & $b | 按位与(AND) | 两个位都为 1 时结果为 1 |
$a | $b | 按位或(OR) | 任一位为 1 时结果为 1 |
$a ^ $b | 按位异或(XOR) | 两个位不同时结果为 1 |
~$a | 按位取反(NOT) | 将所有位取反 |
$a << $b | 左移 | 将 $a 的位向左移动 $b 位(等同于乘以 2 的 $b 次方) |
$a >> $b | 右移 | 将 $a 的位向右移动 $b 位(等同于除以 2 的 $b 次方取整) |
二进制基础
理解位运算的关键是二进制表示。以下以 8 位为例:
5 的二进制: 0000 0101
3 的二进制: 0000 0011
5 & 3 = 1: 0000 0001 (对应位都为1才为1)
5 | 3 = 7: 0000 0111 (对应位有1就为1)
5 ^ 3 = 6: 0000 0110 (对应位不同才为1)
~5 = -6: 1111 1010 (所有位取反,有符号整数)语法与代码示例
基本位运算
php
<?php
declare(strict_types=1);
$a = 5; // 二进制: 0101
$b = 3; // 二进制: 0011
// 按位与 AND
var_dump($a & $b); // int(1) -> 0001
// 按位或 OR
var_dump($a | $b); // int(7) -> 0111
// 按位异或 XOR
var_dump($a ^ $b); // int(6) -> 0110
// 按位取反 NOT
var_dump(~$a); // int(-6) -> 有符号整数取反
// 左移
var_dump(5 << 1); // int(10) -> 乘以 2
var_dump(5 << 2); // int(20) -> 乘以 4
var_dump(1 << 10); // int(1024)
// 右移
var_dump(20 >> 1); // int(10) -> 除以 2
var_dump(20 >> 2); // int(5) -> 除以 4位运算的优先级陷阱
php
<?php
declare(strict_types=1);
// 陷阱:& 的优先级低于 ==
$flag = 3;
$mask = 1;
// 这实际是 $flag & (MASK == 1) = 3 & true = 3 & 1 = 1
$wrong = $flag & $mask == 1;
var_dump($wrong); // int(1)
// 正确:使用括号
$right = ($flag & $mask) == 1;
var_dump($right); // bool(true)位移运算与乘除法
php
<?php
declare(strict_types=1);
// 左移等价于乘以 2 的 n 次方
var_dump(5 << 3); // int(40) = 5 * 8
var_dump(1 << 8); // int(256) = 256
// 右移等价于除以 2 的 n 次方(向零取整)
var_dump(40 >> 3); // int(5) = 40 / 8
var_dump(256 >> 4); // int(16) = 256 / 16
// 位移比乘除法更快(编译器通常自动优化)
// 但代码中建议使用乘除法,可读性更好
// 位移主要用于位掩码操作
// 右移是有符号的
var_dump(-8 >> 1); // int(-4) —— 符号位保留
var_dump(-1 >> 1); // int(-1)详细说明
按位取反的补码特性
PHP 中整数使用补码表示,因此 ~$a 的结果等于 -$a - 1:
php
<?php
declare(strict_types=1);
// 按位取反
var_dump(~5); // int(-6) = -(5) - 1
var_dump(~(-5)); // int(4) = -(-5) - 1
// 利用此特性可以快速求相反数减一
$a = 10;
var_dump(~$a + 1); // int(-10) —— 等价于取反加一(即取负)无符号右移的处理
PHP 没有无符号右移运算符。如果需要无符号右移:
php
<?php
declare(strict_types=1);
// 模拟无符号 32 位右移
function unsignedRightShift(int $a, int $b): int
{
if ($b >= 32) {
return 0;
}
return ($a >> $b) & ((1 << (32 - $b)) - 1);
}
var_dump(unsignedRightShift(-1, 1)); // int(2147483647)
var_dump(unsignedRightShift(-1, 16)); // int(65535)实战示例
权限管理系统
php
<?php
declare(strict_types=1);
// 定义权限常量
class Permission
{
public const READ = 1 << 0; // 1
public const WRITE = 1 << 1; // 2
public const DELETE = 1 << 2; // 4
public const EXECUTE = 1 << 3; // 8
public const ADMIN = 1 << 4; // 16
}
// 添加权限
function addPermission(int &$current, int $permission): void
{
$current |= $permission;
}
// 移除权限
function removePermission(int &$current, int $permission): void
{
$current &= ~$permission;
}
// 检查权限
function hasPermission(int $current, int $permission): bool
{
return ($current & $permission) === $permission;
}
// 使用示例
$role = Permission::READ | Permission::WRITE; // 3
var_dump(hasPermission($role, Permission::READ)); // bool(true)
var_dump(hasPermission($role, Permission::DELETE)); // bool(false)
addPermission($role, Permission::DELETE);
var_dump(hasPermission($role, Permission::DELETE)); // bool(true)
removePermission($role, Permission::WRITE);
var_dump(hasPermission($role, Permission::WRITE)); // bool(false)
// 检查多个权限
$admin = Permission::READ | Permission::WRITE | Permission::DELETE | Permission::ADMIN;
var_dump(hasPermission($admin, Permission::READ | Permission::WRITE)); // bool(true)IP 地址处理
php
<?php
declare(strict_types=1);
// 将 IPv4 地址转换为整数
function ipToLong(string $ip): int
{
return sprintf('%u', ip2long($ip));
}
// 将整数转换回 IPv4 地址
function longToIp(int $long): string
{
return long2ip($long);
}
// 检查 IP 是否在子网内
function isInSubnet(string $ip, string $subnet, int $prefix): bool
{
$ipLong = ipToLong($ip);
$subnetLong = ipToLong($subnet);
$mask = $prefix === 0 ? 0 : (~0 << (32 - $prefix));
return ($ipLong & $mask) === ($subnetLong & $mask);
}
// 使用示例
var_dump(isInSubnet('192.168.1.100', '192.168.1.0', 24)); // bool(true)
var_dump(isInSubnet('192.168.2.100', '192.168.1.0', 24)); // bool(false)
// 计算子网掩码
function getSubnetMask(int $prefix): string
{
$mask = $prefix === 0 ? 0 : (~0 << (32 - $prefix));
return longToIp($mask);
}
var_dump(getSubnetMask(24)); // string(13) "255.255.255.0"
var_dump(getSubnetMask(16)); // string(11) "255.255.0.0"特征位标记
php
<?php
declare(strict_types=1);
// 使用位标志表示商品特征
class ProductFlags
{
public const IS_NEW = 1 << 0; // 1
public const IS_SALE = 1 << 1; // 2
public const IS_FEATURED = 1 << 2; // 4
public const IS_FREE_SHIPPING = 1 << 3; // 8
public const IS_LIMITED = 1 << 4; // 16
}
function setFlag(int &$flags, int $flag, bool $value): void
{
if ($value) {
$flags |= $flag;
} else {
$flags &= ~$flag;
}
}
function toggleFlag(int &$flags, int $flag): void
{
$flags ^= $flag;
}
// 使用
$flags = ProductFlags::IS_NEW;
setFlag($flags, ProductFlags::IS_SALE, true);
setFlag($flags, ProductFlags::IS_FREE_SHIPPING, true);
var_dump($flags); // int(11) = 1 | 2 | 8
toggleFlag($flags, ProductFlags::IS_NEW);
var_dump($flags); // int(10) = 0 | 2 | 8注意事项
- 位运算仅适用于整数:对浮点数操作会先转换为整数,对字符串的位运算在 PHP 7.1 后产生
Deprecated警告 - 位运算与比较运算混用:
&的优先级低于==,必须使用括号 - 左移溢出:左移超出整数位宽时,结果取决于平台
- 有符号右移:PHP 的右移保留符号位,负数右移仍为负数
- 32 位 vs 64 位:PHP 整数大小取决于平台(32 位:4 字节,64 位:8 字节)
最佳实践
- 使用常量定义位标志:如
const READ = 1 << 0,提高可读性 - 位运算表达式使用括号:特别是与比较运算符混用时
- 权限检查使用
===:($flags & $mask) === $mask确保完全匹配 - 使用枚举(PHP 8.1+)管理位标志:
enum Permission: int更安全 - 位运算替代布尔数组:当需要存储大量布尔标志时,一个整数可存储 64 个标志
进阶用法
调试与测试技巧
php
<?php
declare(strict_types=1);
// 单元测试辅助函数
function createTestResource(): mixed
{
return match (true) {
default => new stdClass(),
};
}
// 调试输出函数
function debugOutput(mixed , string = ''): void
{
= ? ": " : '';
.= print_r(, true);
fwrite(STDERR, . "\n");
}
// 性能基准测试
function benchmark(callable , int = 1000): float
{
= hrtime(true);
for ($i = 0; $i < $iterations; $i++) {
$fn();
}
return (hrtime(true) - $start) / 1e9;
}日志记录实践
php
<?php
declare(strict_types=1);
/**
* 简易日志记录器
*/
class SimpleLogger
{
private string $logFile;
private string $level = 'INFO';
public function __construct(string $logFile)
{
$this->logFile = $logFile;
}
public function info(string $message, array $context = []): void
{
$this->log('INFO', $message, $context);
}
public function warning(string $message, array $context = []): void
{
$this->log('WARNING', $message, $context);
}
public function error(string $message, array $context = []): void
{
$this->log('ERROR', $message, $context);
}
private function log(string $level, string $message, array $context): void
{
$timestamp = date('Y-m-d H:i:s');
$contextStr = $context ? ' ' . json_encode($context, JSON_UNESCAPED_UNICODE) : '';
$line = "[{$timestamp}] [{$level}] {$message}{$contextStr}\n";
file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX);
}
}配置与环境检测
php
<?php
declare(strict_types=1);
// 环境检测工具
class EnvironmentChecker
{
public static function checkRequirements(array $requirements): array
{
$results = [];
foreach ($requirements as $name => $check) {
$results[$name] = is_callable($check) ? $check() : false;
}
return $results;
}
public static function getSystemInfo(): array
{
return [
'php_version' => PHP_VERSION,
'os' => PHP_OS,
'sapi' => PHP_SAPI,
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'loaded_extensions' => get_loaded_extensions(),
];
}
}常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 连接超时 | 网络问题/配置错误 | 检查配置,增加超时时间 |
| 权限不足 | 文件/目录权限 | 使用 chmod/chown 修正 |
| 性能下降 | 索引缺失/数据量大 | 添加索引,优化查询 |
| 数据不一致 | 并发冲突/事务残留 | 使用锁机制和事务 |
| 内存溢出 | 大数据集/未释放资源 | 增大内存限制,分批处理 |
故障排除步骤
- 检查错误日志和异常信息
- 确认配置和环境是否正确
- 使用调试工具逐步排查
- 参考官方文档查找已知问题
版本兼容性说明
| 功能 | 最低版本 | 说明 |
|---|---|---|
| 基础功能 | PHP 8.1 | 本文档基准版本 |
| 只读属性 | PHP 8.1 | public readonly 修饰符 |
| 枚举类型 | PHP 8.1 | enum 类型和 match 表达式 |
| Fiber | PHP 8.1 | 协程/轻量级并发 |
| 命名参数 | PHP 8.0 | foo(arg_name: value) |
| 联合类型 | PHP 8.0 | `int |
| Null 安全运算符 | PHP 8.0 | $obj?->method() |
| 析构器 promotion | PHP 8.0 | __construct(public $x) |
php
<?php
declare(strict_types=1);
// 版本兼容性检测
function ensureVersion(string $minVersion): void
{
if (version_compare(PHP_VERSION, $minVersion, '<')) {
throw new RuntimeException(
sprintf('需要 PHP %s+, 当前版本: %s', $minVersion, PHP_VERSION)
);
}
}
ensureVersion('8.1.0');