在PHP中位運算的實際項目當中高級運用操作
2019-08-12
位運算想必軟件相關(guān)專業(yè)的同學(xué)應(yīng)該非常清楚。非科班專業(yè)出身的也不要著急。今天博主就帶著大家一起來回顧一下這些基礎(chǔ)知識,同時也會講位運算在 PHP
實際項目當中的高級運用技巧。
一、位運算知識回顧
在大學(xué)的時候,我們會有一門課程《計算機組成原理》。當然,每所高校的課程名稱不盡相同。這個咱們先別計較。
PHP 手冊當中,專門對位運算及位運算符進行了使用介紹。
https://www.php.net/manual/zh/language.operators.bitwise.php
1) 按位與運算符:&
之所以稱為位運算,指的是在運算過程中,我們會把兩組需要位運算的數(shù)值進行二進制化,然后兩組二進制的數(shù)字從低位向左對齊。這里的位指的是二進制數(shù)字的位置。而按位與運算符指的是兩組數(shù)值每一位相與,同為 1 則 結(jié)果為 1,否則為 0。
看 PHP 示例:
echo 1 & 3; // 輸出:1
為什么會輸出 1 呢?
那是因為:
1 的二進制表示結(jié)果為:01。
3 的二進制表示結(jié)果為:11。
那么這兩組二進制結(jié)果對齊之后的運算流程如下:
01
11
——
01
從右往左進行位運算,都 為 1 則結(jié)果為1,否則為 0。結(jié)果自然是 01。而 01 轉(zhuǎn)換成十進制就是 1 。所以,輸出的結(jié)果為 1。
再看一組示例吧:
echo 2 & 10; // 輸出:2
我們來看看運算過程:
10
1010
——————
0010
0010 轉(zhuǎn)換成十進制就是 2。
一定要記得是向左對齊?;蛘叻Q為低位順位對齊模式。
如果你不會十進制轉(zhuǎn)二進制或二進制轉(zhuǎn)十進制不會。那么,下面這兩個方法能幫助你。
echo decbin(10); // 十進制轉(zhuǎn)二進制。
echo bindec(10); // 二進制轉(zhuǎn)十進制。
2) 按位或運算符:|
這個跟上面的按位與運算符稍微有一點不同:只要兩組數(shù)字當中有只要有一位是 1 則結(jié)果就為 1。
看示例:
echo 2 | 10; // 輸出結(jié)果:10
運算過程:
10
1010
——————
1010
1010 的十進制結(jié)果就是 10。
3) 按位異或運算符:^
這個運算符比較有意思:兩組數(shù)字必須是一組 0 與 1 結(jié)果才為 1。1 與 1 和 0 與1 都是 0。
看示例:
echo 1 ^ 1; // 輸出結(jié)果:0
echo 1 ^ 0; // 輸出結(jié)果:1
1 ^ 1 運算過程:
01
01
——
00
1 ^ 0 運算過程:
01
00
——
01
這個很簡單。沒啥好說的。反正不相同就為 1,相同就為 0。
4) 其他位運算符:~、>>、<<
~ 按位取反運算符、>> 右移運算符、<< 左移運算符。它們的操作數(shù)都是一個。所以,與上面的兩個操作數(shù)的位運算符有著非常明顯的區(qū)別。在 PHP 實際編程當中,運用比較少。權(quán)當知識了解即可。
二、高級動用技巧
我們在上面對位運算符的知識進行了回顧。為的就是要在接下來的實際項目中怎樣運用它解決實際問題。
我們首先來看一個系統(tǒng)中常見的需求:
有一個廣告表,我們要對廣告做顯示控制:
手動上下線。
只允許 VIP 查看。
可能的表結(jié)構(gòu)如下:
CREATE TABLE `finger_ad` (
`ad_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`ad_name` varchar(50) NOT NULL COMMENT '廣告名稱',
`ad_image_url` varchar(255) NOT NULL COMMENT '廣告圖片',
`ad_url` varchar(255) NOT NULL COMMENT '廣告圖片URL跳轉(zhuǎn)地址',
`is_vip` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否僅限 VIP 顯示',
`display` tinyint(1) NOT NULL DEFAULT '1' COMMENT '顯示狀態(tài):1顯示、0隱藏',
PRIMARY KEY (`ad_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='廣告表';
假如后期,我們需求更改了。需要再增加幾種限制:
已登錄用戶
未登錄用戶
30 天內(nèi)未登錄用戶
注冊 30 天的用戶
遇到這種限制條件的需求,開發(fā)同學(xué)是不是很傷腦筋?
可能很多開發(fā)第一反應(yīng)就是在表結(jié)構(gòu)增加這種新增的限制條件字段。一切看來似乎很美好。
的確,這樣添加字段是最快最容易的方式。也能完成我們的需求。
但是,這樣會引來如下毛?。?/span>
每次增加限制條件。我們都要增加字段。這種對數(shù)據(jù)庫的更動能少改就少改。畢竟,無限制的增加字段不可取。
假如廣告表數(shù)據(jù)量很大。大到增加一個字段需要幾分鐘的時候,這會給數(shù)據(jù)庫服務(wù)器造成讀寫壓力。
條件越多,SQL 條件語句就會越來越長。
那么,還有沒有更好的方式解決這些問題呢?
答案:有!
這就是我們今天要講的按位與運算符的高級技巧。
我們把上面的表結(jié)構(gòu)改一下:
DROP TABLE IF EXISTS `finger_ad`;
CREATE TABLE `finger_ad` (
`ad_id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`ad_name` varchar(50) NOT NULL COMMENT '廣告名稱',
`ad_image_url` varchar(255) NOT NULL COMMENT '廣告圖片',
`ad_url` varchar(255) NOT NULL COMMENT '廣告圖片URL跳轉(zhuǎn)地址',
`bit_condition` INT(11) UNSIGNED NOT NULL COMMENT '位運算條件:1-登錄可訪問、2-未登錄可訪問、4-30天注冊可訪問、8-30天未登錄可訪問、16-未消費可訪問、32-VIP可訪問',
`display` tinyint(1) NOT NULL DEFAULT '1' COMMENT '顯示狀態(tài):1顯示、0隱藏',
PRIMARY KEY (`ad_id`)
) ENGINE=InnoDB DEFAULT CHARSET UTF8 COMMENT='廣告表';
我們把所有的條件都去掉了。增加了一個字段: bit_condition
。把所有的條件都組合到一個字段。
那我們此時該如何寫代碼呢?
比如,現(xiàn)在要添加如下限制條件的廣告:
只允許登錄用戶訪問或已注冊 30 天用戶或是 VIP 用戶才允許訪問該廣告。
那么,這個廣告的 bit_condition
該如何設(shè)置值呢?很簡單,把這幾個條件的位值直接相加。此時值為:37。
很多可能會很奇怪。設(shè)置為 37 ,我怎么知道是這幾個值的和呢?如果對 Linux 系統(tǒng)權(quán)限熟悉的同學(xué)就很容易理解這種做法。實際上,這里運用了按位與運算的特性:任意組合相加的值不會重復(fù)。
這個理解起來有一定難度。我三兩句也很難給你梳理明白。大家可以在網(wǎng)上深入挖掘一下這方面兒的知識。你只需要知道這一點特點即可。
那么,現(xiàn)在我們該如何寫 SQL 呢?
示例如下:
SELECT * FROM finger_ad WHERE display = 1 AND bit_condition & 3 = bit_condition
這條 SQL 語句當中的 3 對應(yīng)的是當前用戶針對這么多條件得到的數(shù)值。如果 bit_condition
位值是與 3 按位與與 bit_condition
結(jié)果相同,說明條件符合。
我們通過一個字段解決了所有條件的問題。著實得感謝按位與運算符的特性。同時也對 MySQL
能支持位運算符感到開心。
那么,它有什么缺點呢?
想必有經(jīng)驗的同學(xué)已經(jīng)看出來了。這種寫法只能滿足包含關(guān)系。假如要實現(xiàn)同時滿足 3 個條件才能訪問就不行了?;蛘?,一個滿足另外一個取反。
優(yōu)點明顯,同樣缺點也很明顯。大家要根據(jù)實際情況來選用。