Skip to content

位运算符

概述

位运算符允许在整数的二进制位级别进行操作。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 字节)

最佳实践

  1. 使用常量定义位标志:如 const READ = 1 << 0,提高可读性
  2. 位运算表达式使用括号:特别是与比较运算符混用时
  3. 权限检查使用 ===($flags & $mask) === $mask 确保完全匹配
  4. 使用枚举(PHP 8.1+)管理位标志enum Permission: int 更安全
  5. 位运算替代布尔数组:当需要存储大量布尔标志时,一个整数可存储 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 修正
性能下降索引缺失/数据量大添加索引,优化查询
数据不一致并发冲突/事务残留使用锁机制和事务
内存溢出大数据集/未释放资源增大内存限制,分批处理

故障排除步骤

  1. 检查错误日志和异常信息
  2. 确认配置和环境是否正确
  3. 使用调试工具逐步排查
  4. 参考官方文档查找已知问题

版本兼容性说明

功能最低版本说明
基础功能PHP 8.1本文档基准版本
只读属性PHP 8.1public readonly 修饰符
枚举类型PHP 8.1enum 类型和 match 表达式
FiberPHP 8.1协程/轻量级并发
命名参数PHP 8.0foo(arg_name: value)
联合类型PHP 8.0`int
Null 安全运算符PHP 8.0$obj?->method()
析构器 promotionPHP 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');

参考链接