我們使用正則表達式,熟練掌握各種功能和結構只是手段,解決實際的問題才是真正的目的。要解決真正的問題,就必須有解決問題的思路,正則表達式的功能,說到底,可以歸納為三種邏輯,為了表述方便,我們分別稱為與、或、非。
最近用CI在做個人工資管理系統的時候,需要驗證用戶是否登錄和使用特定的功能,用到了正則表達式-非。需求如下:
路徑/user, /user/login, /user/register不需要進行攔截,其實諸如/profile, /company, /work的路徑都要攔截,然後檢查session中是否存在user_id,沒有的話就跳轉到/user/login
初步觀察:
只要對/user進行匹配就行了。初步代碼如下:
01
<?php
02
class Acl {
03
04
private $CI;
05
06
public function __construct() {
07
$this->CI = &get_instance();
08
}
09
10
public function auth() {
11
if (!preg_match('/^user.*$/', uri_string())) {
12
$user_id = $this->CI->session->userdata('user_id');
13
if(empty($user_id)) {
14
redirect('/user');
15
return;
16
}
17
}
18
}
19
}
測試的時候才發現漏了檢測user/change_password,這個功能是需要客戶首先登錄的,這裡就要在user.*中排除user/change_password的,也就是前面說的要用到正則中的“非”。
“非”是正則表達式中最難處理的邏輯關系。因為沒有直接對應的結構,“非”的處理比較吃力。
最簡單的“非”,意思是此處不能出現某個字符,這一點通常很直觀,似乎用排除型字符組[^…]就可以解決。比如雙引號字符串的匹配,首尾兩個雙引號很容易匹配,其中的內容肯定不是雙引號(暫時不考慮轉義的情況),所以可以用[^"]表示即可,其長度不確定,所以用*來限定,所以整個表達式就是"[^"]*",非常簡單。
但是,事情果真都如此簡單嗎?我們舉cat和cut的例子,如果希望匹配c開頭,t結尾的單詞,但不希望匹配cut,可以寫成c[^u]t,是否就可以了?
這個表達式的意思是:最開頭的字母是c,之後是一個不為u的字符,之後是t。沒錯,它確實不會匹配cut,也可以匹配cat。但是,chart、conduct、court等等,它也沒法匹配,因為[^u]的意思是:匹配一個不是u的字符。
那麼,把[^u]改成[^u]+好了,這樣應該就可以解決問題了。但是真的如此嗎?[^u]+的意思是,一個或若干(最多到無窮)個字符,但每一個字符都不能是u。所以,盡管c[^u]+t能匹配cat和chart,卻不能匹配conduct和court。
其實cut和我的路徑匹配問題可以用順序否定環視功能來解決。
(?!cut)就是用來進行這種判斷的,它判斷之後的字符串能不能由cut匹配,也不會移動“當前位置”。所以我們將它放在表達式的最開頭,得到(?!cut)c[a-z]+t。這個表達式的邏輯是:只有在當前位置右側字符串不能由cut匹配的情況下,才從這裡開始,向右嘗試用c[a-z]+t。
如果我們更進一步,需要排除掉cat和cut,可以把否定順序環視改為(?!c[au]t)。這樣就能保證,匹配到的肯定不是cat或者cut。www.2cto.com
回到最上面的問題。直接上代碼吧,代碼如下:
01
<?php
02
class Acl {
03
04
private $CI;
05
06
public function __construct() {
07
$this->CI = & get_instance();
08
}
09
10
public function auth() {
11
if (!preg_match('/^(?!user\/change_password)user.*$/', uri_string())) {
12
$user_id = $this->CI->session->userdata('user_id');
13
if(empty($user_id)) {
14
redirect('/user');
15
return;
16
}
17
}
18
}
19
}
作者:kxt