2013-05-04 58 views
10

我想創建一個類並擴展一個PHP類FilesystemIterator,如下面的代碼所示。我定義了一個hasFlag()方法並測試它是否包含一個標誌(我希望它像其他PHP函數,例如glob),但結果總是與預期不同。那麼我該如何解決這個問題呢?如何使用功能標誌?

class c extends FilesystemIterator 
{ 
    /* These are parent constants */ 
    const CURRENT_AS_FILEINFO = 0 ; 
    const KEY_AS_PATHNAME  = 0 ; 
    const CURRENT_AS_SELF  = 16 ; 
    const CURRENT_AS_PATHNAME = 32 ; 
    const CURRENT_MODE_MASK = 240 ; 
    const KEY_AS_FILENAME  = 256 ; 
    const NEW_CURRENT_AND_KEY = 256 ; 
    const FOLLOW_SYMLINKS  = 512 ; 
    const KEY_MODE_MASK  = 3840 ; 
    const SKIP_DOTS   = 4096 ; 
    const UNIX_PATHS   = 8192 ; 

    public function __construct($flags) { 
     $this->flags = $flags; 
    } 
    public function hasFlag($flag) { 
     //How do I test $this->flags it contains a $flag??? 
     return ($this->flags & $flag) ? true : false; 
    } 
} 

$c = new c(
    c::CURRENT_AS_FILEINFO | 
    c::KEY_AS_PATHNAME | 
    c::CURRENT_AS_SELF | 
    c::CURRENT_AS_PATHNAME | 
    c::CURRENT_MODE_MASK | 
    c::KEY_AS_FILENAME | 
    c::NEW_CURRENT_AND_KEY | 
    c::FOLLOW_SYMLINKS | 
    c::KEY_MODE_MASK | 
    c::SKIP_DOTS | 
    c::UNIX_PATHS 
); 

var_dump($c->hasFlag(c::CURRENT_AS_FILEINFO)); 

EDIT 1

爲什麼如此??

var_dump(((0 | 16 | 32 | 240 | 3840) & 0) == 0); //true 
var_dump(((0 | 16 | 32 | 240 | 3840) & 32) == 32); //true 
var_dump(((0 | 16 | 32 | 240 | 3840) & 240) == 240); //true 
var_dump(((0 | 16 | 32 | 240 | 3840) & 1024) == 1024); //true?? 
var_dump(((0 | 16 | 32 | 240 | 3840) & 2048) == 2048); //true?? 
var_dump(((0 | 16 | 32 | 240 | 3840) & 3840) == 3840); //true 
+0

我知道我只是告訴你 – Jasper 2013-05-04 14:31:21

+0

啊好吧,不是很確定 – hakre 2013-05-04 14:32:04

+0

'$ c-> hasFlag(c :: CURRENT_AS_FILEINFO)'預計爲'false'。 – 2013-05-04 14:41:57

回答

1

只需使用:

(($this->flags & $flag) === $flag) 

實施例:

class Test 
{ 
    /* These are parent constants */ 
    const FLAG_A = 1; // binary 01 
    const FLAG_B = 2; // binary 10 

    public function __construct($flags) { 
     $this->flags = $flags; 
    } 
    public function hasFlag($flag) { 
     return (($this->flags & $flag) === $flag) ? true : false; 
    } 
} 


$t = new Test(Test::FLAG_A | Test::FLAG_B); 
var_dump($t->hasFlag(Test::FLAG_A)); # bool(true) 
var_dump($t->hasFlag(Test::FLAG_A)); # bool(true) 

$t = new Test(Test::FLAG_A); 
var_dump($t->hasFlag(Test::FLAG_A)); # bool(true) 
var_dump($t->hasFlag(Test::FLAG_B)); # bool(false) 

$t = new Test(Test::FLAG_B); 
var_dump($t->hasFlag(Test::FLAG_A)); # bool(false) 
var_dump($t->hasFlag(Test::FLAG_B)); # bool(true) 

說明:

想象這是的$flags的二進制表示被當前設置爲CURRENT_AS_PATHNAME | CURRENT_AS_SELF(二進制表示縮短):

...110000 

您現在嘗試檢查標誌CURRENT_AS_SELF是否處於活動狀態。 CURRENT_AS_SELF看起來像這樣的二進制形式:

...010000 

如果你現在適用的是在兩個操作數設置logical AND運營商&只有位將結果進行設置。什麼給你:

...010000 

這是一樣的國旗。這就是爲什麼=== $flag

還要注意從@mario我二進制標誌枝條價值0沒有意義

+0

'$ this-> flags&$ flag === $ flag':) – sectus 2013-05-04 14:05:57

+0

'?真:假'附錄已經是多餘的。 '==='如何提供幫助? – mario 2013-05-04 14:06:10

+0

@mario它不是*多餘的,因爲返回值是一個'int',但是'(bool)($ this-> flags&$ flag)'就足夠了。 – PointedEars 2013-05-04 14:10:03

2

你的問題是在這裏聲明的答案:

const CURRENT_AS_FILEINFO = 0 

當定義你的位標誌值爲零,它永遠不可能出現在位掩碼上。

+0

從來沒有或永遠,這是問題:) – hakre 2013-05-04 14:21:24

11

關於違約和口罩

有在FilesystemIterator,被稱爲口罩使用了一些特殊標誌;他們將多個相關的標誌(或者在這種情況下是互斥的)組合在一起,並且而不是作爲常規標誌傳遞;下面是他們的二進制表示:

000x00xx0000 
+--++--+ 
    | | 
    | +---- CURRENT_MODE_MASK 
    | 
    +-------- KEY_MODE_MASK 

,這些標誌位來確定是否應使用默認key()current()方法。兩種方法的默認值定義如下:

const CURRENT_AS_FILEINFO = 0 ; 
const KEY_AS_PATHNAME  = 0 ; 

下面的代碼演示瞭如何爲它進行測試:

if ($flags & CURRENT_MODE_MASK == 0) { 
    // CURRENT_AS_FILEINFO is used 
} else { 
    // either CURRENT_AS_PATHNAME, CURRENT_AS_SELF or CURRENT_AS_PATHNAME is used 
} 

if ($flags & KEY_MODE_MASK == 0) { 
    // KEY_AS_PATHNAME is used 
} else { 
    // KEY_AS_FILENAME is used 
} 

具有如->hasFlag()功能的問題是,你不能區分兩個默認值,即你想測試CURRENT_AS_FILEINFO還是KEY_AS_PATHNAME?你將不得不重新思考邏輯。

關於相互排斥的標誌

有不能一起使用,因爲它們會導致不確定的行爲幾個標誌;例如:

const CURRENT_AS_SELF  = 16 ; 
const CURRENT_AS_PATHNAME = 32 ; 

不能定義兩種行爲進行current(),要麼應該使用它們(或默認)之一。一組兼容的標誌可能是這樣的:

$c = new c(
    c::CURRENT_AS_SELF | 
    c::KEY_AS_FILENAME | 
    c::FOLLOW_SYMLINKS | 
    c::SKIP_DOTS | 
    c::UNIX_PATHS 
); 

關於擴展類

假設你的構造是一樣的家長,您可以完全刪除你的構造:

+0

有沒有辦法測試標誌包含0,240,3840?怎麼做? – Jasper 2013-05-04 22:12:06

+0

@joe你是什麼意思?要測試'0',你只需使用'$ this-> flags == 0';對於'240'或'3840',你可以使用'($ this-> flags&240)== 240'。 – 2013-05-05 08:45:39

+0

但爲什麼是結果?看到我的編輯1請 – Jasper 2013-05-05 13:38:07

0

你試圖用假設他們是兩個冪的方式來使用不是兩個冪的東西。

0xf00 = (0x800 + 0x400 + 0x200 + 0x100) 

爲什麼0xf00 & 0x400的== 0x400的是事實,就變得很明顯:

一旦你意識到你感到困惑可以重新寫成十六進制爲

var_dump(((0 | 0x10 | 0x20 | 0xf0 | 0xf00) & 0x400) == 0x400); //true?? 
var_dump(((0 | 0x10 | 0x20 | 0xf0 | 0xf00) & 0x800) == 0x800); //true?? 

的線條。你不能使用那些標誌,比如你現在如何做簡單的測試,看它們是否有效。

我想你應該只檢查確切的標誌,因爲它們被定義,而不是針對任意數字。

編輯

嗯 - 我明白你的意思。似乎有一些標誌不兼容,因爲它們會與例如衝突

const KEY_AS_FILENAME  = 256 ; 
const NEW_CURRENT_AND_KEY = 256 ; 

所以不能設置和獨立測試 - 這是非常奇怪的。

我試過閱讀FileIterator的源代碼 - 它在https://github.com/php/php-src/blob/master/ext/spl/spl_directory.c。然而,它不是很容易閱讀。

我想可能在那個類之外使用那些標誌可能不是一個好主意。

+0

我只重寫那些標誌嗎?有可能不重寫它來測試它嗎?如果不得不重寫它爲什麼父類的FilesystemIterator可以被測試呢? – Jasper 2013-05-05 22:17:08

+0

你應該先理解然後重寫需要的東西。 – Danack 2013-05-05 22:18:03

+0

你的意思是?我必須重寫標誌嗎? – Jasper 2013-05-05 22:27:07

6

我感覺你並不真正瞭解你正在使用的bitwise operators|&。它所做的就是比較位級和改變位的輸入。所以爲了理解,你需要把所有的值都放在二進制格式中並檢查它。 的|操作員將一個位設置爲1時,無論是位中的一個是1,且將&其設置爲1時兩個位都是1

採取數字2和4.使用|

2: 0010 
4: 0100 
-------- 
6: 0110 

使用&

2: 0010 
4: 0100 
-------- 
0: 0000 
在構造函數

所以,你只是將所有的數字加起來,$this->flags將包含一個整數

c::CURRENT_AS_FILEINFO | 
c::KEY_AS_PATHNAME | 
c::CURRENT_AS_SELF | 
c::CURRENT_AS_PATHNAME | 
c::CURRENT_MODE_MASK | 
c::KEY_AS_FILENAME | 
c::NEW_CURRENT_AND_KEY | 
c::FOLLOW_SYMLINKS | 
c::KEY_MODE_MASK | 
c::SKIP_DOTS | 
c::UNIX_PATHS 

將轉化爲

0 : 00000000000000 
0 : 00000000000000 
16 : 00000000010000 
240 : 00000011110000 //notice this one. It doesnt change 1 bit, but multiple. 
256 : 00000100000000 
256 : 00000100000000 
512 : 00001000000000 
3840 : 00111100000000 //notice this one. It doesnt change 1 bit, but multiple. 
4096 : 01000000000000 
8192 : 10000000000000 
--------------------- 
16368: 11111111110000 

所以你$this->flags包含16368

現在爲您var_dump測試,當我離開了所有的確切數據位,但你正在是這樣的:

var_dump(((0 | 16 | 32 | 240 | 3840) & 0) == 0); //true 
var_dump(((4080) & 0) == 0); //true 

4080: 111111110000 
0 : 000000000000 
------------------ & 
0 : 000000000000 //nowhere are both bits a 1 so the output is 0 

所以在結果你的var_dump statesment變成:

var_dump((4080 & 0) == 0); 
var_dump((4080 & 32) == 32); 
var_dump((4080 & 240) == 240); 
var_dump((4080 & 1024) == 1024); 
var_dump((4080 & 2048) == 2048); 
var_dump((4080 & 3840) == 3840); 

    //which is also.. 
var_dump(0 == 0); 
var_dump(32 == 32); 
var_dump(240 == 240); 
var_dump(1024 == 1024); 
var_dump(2048 == 2048); 
var_dump(3840 == 3840); 

    //which obvisouly is true on all accounts. 

現在回到您的hasFlag功能。請記住,您的$this->flags包含1636811111111110000。現在,如果你採取正確的10000只有位,你將有16號 如果添加1到左側和更改所有其他位爲0,你會得到100000000000000翻譯爲16384

的結果:16至16384之間的任何數字轉換爲二進制數,在同一點上至少有一個1,其中旗幟有一個1。所以return ($this->flags & $flag)將適用於所有這些數字。

由於您不能在父級中創建標誌,因此您需要使用其他方法來檢查標誌是否爲真。在這種情況下,您需要確保$this->flags & $flag的結果與該標誌相匹配。因爲只有這樣的結果是真因此,這將成爲

return ($this->flags & $flag) == $flag; 

僅供參考:

如果你可以自己設置標誌和沒有發現任何複合的標誌,你可以把你所有的標誌電源2和所有不同。這樣每個標誌將對應於二進制格式的單個位置,因此具有是/否設置。

const CURRENT_AS_FILEINFO = 2 ; 
const KEY_AS_PATHNAME  = 4 ; 
const CURRENT_AS_SELF  = 8 ; 
const CURRENT_AS_PATHNAME = 16 ; 
const CURRENT_MODE_MASK = 32 ; 
const KEY_AS_FILENAME  = 64 ; 
const NEW_CURRENT_AND_KEY = 128 ; 
const FOLLOW_SYMLINKS  = 256 ; 
const KEY_MODE_MASK  = 512 ; 
const SKIP_DOTS   = 1024 ; 
const UNIX_PATHS   = 2048 ; 

或者以二進制形式,所以你可以看到每一位都有自己的位置(注意,您可能已經開始與1,2,4,等也使用右邊的第一位):

const CURRENT_AS_FILEINFO = 000000000010; 
const KEY_AS_PATHNAME  = 000000000100; 
const CURRENT_AS_SELF  = 000000001000; 
const CURRENT_AS_PATHNAME = 000000010000; 
const CURRENT_MODE_MASK  = 000000100000; 
const KEY_AS_FILENAME  = 000001000000; 
const NEW_CURRENT_AND_KEY = 000010000000; 
const FOLLOW_SYMLINKS  = 000100000000; 
const KEY_MODE_MASK   = 001000000000; 
const SKIP_DOTS    = 010000000000; 
const UNIX_PATHS   = 100000000000; 

現在您可以像創建它一樣使用標誌功能。因爲只有實際上已經用構造函數設置了一個標誌,它纔會被接受。

+0

優秀的答案 – Jason 2013-05-15 14:13:16