位运算在PHP错误级别中的运用

首先,我们要解决的第一个问题是

什么是位运算?

按照wikipedia中的说法,位运算有如下的含义

  1. 位运算操作的是二进制数
  2. 对二进制数在位级别上进行运算
  3. 效率通常比普通的加减乘除快(为什么快的问题有待解决)

其次,我们要解决的问题是

什么是PHP的错误级别?

根据错误产生的原因,PHP将错误分成若干类,参见文档。每一种错误对应一个全局常量,可以用来组合,得到的结果作为参数传递给error_reporting()函数,PHP就能知道哪些错误应该被忽略,哪些错误应该被显示或记录日志。接下来,又引出一个问题

表示每一种错误的全局常量是如何被组合的?

在配置文件中的配置项error_reporting或者传递给error_reporting()的参数中,通过对表示错误的常量进行位运算来组合错误种类,从而决定要处理哪些错误。

假定有符号整型在机器上占4个字节,32位。我们先看看表示错误的全局常量和其对应值的十进制与二进制表示

常量 十进制 二进制
E_ERROR 1 00000000 00000000 00000000 00000001
E_WARNING 2 00000000 00000000 00000000 00000010
E_PARSE 4 00000000 00000000 00000000 00000100
E_NOTICE 8 00000000 00000000 00000000 00001000
E_CORE_ERROR 16 00000000 00000000 00000000 00010000
E_CORE_WARNING 32 00000000 00000000 00000000 00100000
E_COMPILE_ERROR 64 00000000 00000000 00000000 01000000
E_COMPILE_WARNING 128 00000000 00000000 00000000 10000000
E_USER_ERROR 256 00000000 00000000 00000001 00000000
E_USER_WARNING 512 00000000 00000000 00000010 00000000
E_USER_NOTICE 1024 00000000 00000000 00000100 00000000
E_STRICT 2048 00000000 00000000 00001000 00000000
E_RECOVERABLE_ERROR 4096 00000000 00000000 00010000 00000000
E_DEPRECATED 8192 00000000 00000000 00100000 00000000
E_USER_DEPRECATED 16384 00000000 00000000 01000000 00000000
E_ALL 32767 00000000 00000000 01111111 11111111

知道了要通过位运算组合错误种类,接下来的问题便是

如何通过位运算来组合出我们想要的错误种类?

首先,我们看看位运算符有哪些,参见文档

例子 名称 结果
$a & $b And(按位与) 将把 $a 和 $b 中都为 1 的位设为 1。
$a | $b Or(按位或) 将把 $a 和 $b 中任何一个为 1 的位设为 1。
$a ^ $b Xor(按位异或) 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。
~ $a Not(按位取反) 将 $a 中为 0 的位设为 1,反之亦然。
$a << $b Shift left(左移) 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。
$a >> $b Shift right(右移) 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。

利用这些运算符和预定义常量,就能对错误种类进行组合。假如,我们希望处理以下几种错误组合

  1. 所有错误都处理
  2. 处理E_ALL 中除了E_NOTICE类型错误
  3. 只处理E_ERRORE_WARNING类型错误

第一个,显然要对E_ALL取反,即~E_ALL,二进制值运算式

1~ 00000000 00000000 01111111 11111111

结果为

111111111 11111111 10000000 00000000

所有类型错误对应的位均被置0,表示所有错误将不会被显示。

例程如下

 1<?php
 2ini_set('display_errors', true);
 3error_reporting(~E_ALL);
 4
 5
 6// E_NOTICE
 7echo $var;
 8
 9// E_USER_DEPRECATED
10trigger_error ('deprecated error triggered', E_USER_DEPRECATED);
11
12// E_WARNING
13$a = 2048/0;
14
15// E_ERROR
16func();

运行,没有任何输出。

第二个,转换成位运算表示为E_ALL & ~E_NOTICE,对应的二进制运算式

1  00000000 00000000 01111111 11111111
2& 11111111 11111111 11111111 11110111

结果为

100000000 00000000 01111111 11110111

除了E_NOTICE对应的位为0,其他类型错误对应的位均为1,表示只有E_NOTICE不会被显示。

例程如下:

 1<?php
 2ini_set('display_errors', true);
 3error_reporting(E_ALL & ~E_NOTICE);
 4
 5
 6// E_NOTICE
 7echo $var;
 8
 9// E_USER_DEPRECATED
10trigger_error ('deprecated error triggered', E_USER_DEPRECATED);
11
12// E_WARNING
13$a = 2048/0;
14
15// E_ERROR
16func();

运行,输出

1Deprecated: deprecated error triggered in /usr/local/var/www/test/index.php on line 10
2Warning: Division by zero in /usr/local/var/www/test/index.php on line 13
3Fatal error: Uncaught Error: Call to undefined function func() in /usr/local/var/www/test/index.php:16

除了E_NOTICE级别的错误,E_WARNINGE_ERRORE_USER_DEPRECATED级别的错误均被显示。

第三个,转换成位运算表示为E_ERROR | E_WARNING,对应的二进制运算式

1  00000000 00000000 00000000 00000001
2| 00000000 00000000 00000000 00000010

结果为

100000000 00000000 00000000 00000011

E_ERRORE_WARNING对应的位为1,其他类型错误对应的位为0,表示只有E_ERRORE_WARNING类型的错误将被显示。

例程如下:

 1<?php
 2ini_set('display_errors', true);
 3error_reporting(E_ERROR | E_WARNING);
 4
 5
 6// E_NOTICE
 7echo $var;
 8
 9// E_USER_DEPRECATED
10trigger_error ('deprecated error triggered', E_USER_DEPRECATED);
11
12// E_WARNING
13$a = 2048/0;
14
15// E_ERROR
16func();

运行,输出

1PHP Warning:  Division by zero in /usr/local/var/www/test/index.php on line 13
2PHP Fatal error:  Uncaught Error: Call to undefined function func() in /usr/local/var/www/test/index.php:16

除了E_NOTICEE_USER_DEPRECATED级别的错误,E_WARNINGE_ERROR级别的错误均被显示。

以上,便是位运算在PHP错误级别中的运用

其他需要探究的问题

  1. PHP为什么使用位运算处理错误级别的组合?
  2. 有没有其他类似的位运算应用?
  3. 为什么位运算速度快?