2013-05-04 53 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