如何创建一个可以转换为布尔值的php类(be truthy或falsy)

时间:2011-05-24 16:06:19

标签: php class boolean

我正在创建一个集合类,并希望它是我目前使用的数组的替代品。

如何创建一个可以转换为布尔值的类,所以该类可以是真正的还是虚假的?

一个简单的测试表明,空类的对象是真实的:

class boolClass {}
$obj = new boolClass();
var_dump( (bool)$obj);
//prints 
//bool(true)

但我需要决定我的班级是真的还是假的。有没有办法告诉PHP引擎如何将我的类转换为布尔值?就像我可以用__toString()?

背景:

让我说我写了一个这样的课(这只是一个例子):

class MyCollection implements ArrayAccess, Iterator {
    //...
}

我目前大量使用这种模式:

$var = array();

if (empty($var)) {
   //array is empty, (or there is no array at all)
   // I do something here
}

我希望看起来像这样:

$var = new MyCollection(array());

并保持其余部分不变。但是包含MyCollection的$ var总是很简单所以我需要满足以下所有条件:

if ($var->isEmpty()) {
    //...
}

但这是不可接受的,因为我的代码库有很多兆字节。

这里有解决方案吗?

6 个答案:

答案 0 :(得分:21)

经过多次焦虑,失望和黑客攻击后 - 我相信我找到了解决方案。该解决方案不要求任何扩展;它可以用很少量的PHP样板实现。但是,在自己实施此解决方案之前,请注意这是 - 实际上 - 这是一个巨大的黑客攻击。话虽这么说,这是我发现的:

感到沮丧,我花了一些时间查看PHP documentation for Booleans。虽然用户创建的类被简单地拒绝了作为布尔值转换的能力,但是一个类 - 奇怪的是 - 提供了这种能力。精明的读者会注意到这个类只不过是内置的SimpleXmlElement。通过演绎过程,假设SimpleXmlElement的任何子类也将继承其独特的布尔强制转换功能似乎是公平的。虽然从理论上讲这种方法似乎是有效的,但围绕SimpleXmlElement的神奇之处还在于消除了这种方法的实用性。要理解这是为什么,请考虑以下示例:

class Truthy extends SimpleXmlElement { }

Truthy是SimpleXmlElement的子类,所以我们应该能够测试它是否继承了它的特殊boolean-casting属性:

$true = new Truthy('<true>1</true>'); // XML with content eval's to TRUE
if ($true) echo 'boolean casting is happening!'; 
$false = new Truthy('<false></false>'); // empty XML eval's to FALSE
if (!$false) echo 'this is totally useful!';

实际上,Truthy继承了SimpleXmlElement提供的boolean-cast属性。但是,这种语法很笨拙,并且不太可能从这个类中获得很多实用程序(至少与本机使用SimpleXmlElement相比)。这种情况是问题开始出现的地方:

$false = new Truthy('<false></false>'); // empty XML eval's to FALSE
$false->reason = 'because I said so'; // some extra info to explain why it's false
if (!$false) echo 'why is this not false anymore?!?';
else echo 'because SimpleXMLElements are magical!';

正如您所看到的,尝试在子类上设置属性会立即破坏我们从继承的boolean-casting获得的实用程序。不幸的是,SimpleXmlElement有另一个神奇的功能,打破了我们的惯例。显然,当您设置SimpleXmlElement的属性时,它会修改XML!亲眼看看:

$xml = new SimpleXmlElement('<element></element>');
xml->test = 'content';
echo $xml->asXML(); // <element><test>content</test></element>

那么我们可以从子类化SimpleXmlElement获得任何实用程序!值得庆幸的是,经过多次黑客攻击后,我找到了一种方法来将信息保存到这个子类中,而不会破坏布尔铸造魔法:评论!

$false = new Truthy('<!-- hello world! --><false></false>');
if (!$false) echo 'Great Scott! It worked!';

进展!我们能够在不破坏boolean-casting的情况下将有用的信息输入到这个类中!好的,现在我们需要做的就是清理它,这是我最后的实现:

class Truthy extends SimpleXMLElement {

public function data() {

    preg_match("#<!\-\-(.+?)\-\->#", $this->asXML(), $matches);

    if (!$matches) return null;

    return unserialize(html_entity_decode($matches[1]));


}

public static function create($boolean, Serializable $data = null) {
    $xml  = '<!--' . htmlentities(serialize($data)) . "-->";
    $xml .= $boolean ? '<truthy>1</truthy>' : '<truthy/>';
    return new Truthy($xml);
}

}

为了消除一些笨拙,我添加了一个公共静态工厂方法。现在我们可以创建Truthy对象而不必担心实现细节。工厂允许调用者定义任意数据集,以及布尔转换。然后可以在稍后调用数据方法以检索此数据的只读副本:

$false = Truthy::create(false, array('reason' => 'because I said so!'));
if (!$false) {
   $data = $false->data();
   echo 'The reason this was false was ' . $data['reason'];
}

你有它!在用户定义的类中进行布尔流换的完全hacky(但可用)方法。如果你在生产代码中使用它,请不要起诉我,它会爆炸。

答案 1 :(得分:7)

在此页面上,枚举了您可以为类定义的魔术方法。

http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring

您证明您已经了解__toString()

不幸的是,那里没有列出你所要求的魔法。所以,我认为现在你唯一的选择是定义一个方法并明确地调用该方法。

答案 2 :(得分:4)

您可以查看PHP operator extension,您可以使用它来重载许多运算符,包括==和===。有了这个扩展,理论上你应该能够编写一个类似于布尔值的类:

if($object == true)

答案 3 :(得分:1)

你做不到。

在PHP中,转换为bool时的对象始终生成true。没有办法改变这一点。

答案 4 :(得分:0)

如果您的代码大小为“很多兆字节”,则问题可能不在于您的命名方案过于冗长。我会寻找重复并尝试抽象代码。

OTOH,为什么代码的大小是个大问题?尝试最小化每次运行php时所包含的代码。未使用的杂散代码没有任何区别。如果 包含大量代码,请考虑使用缓存软件,例如MMCache。

回答你原来的问题,AFAIK没有办法在PHP类中添加类型强制。实例将始终评估为true。

答案 5 :(得分:0)

如果你不想实现自己的方法,那么就可以进入__toString,如下所示:

class foo
{
    public function __toString()
    {
        return strval(false);
    }
}

$foo = new foo();

if (strval($foo) == false) // non-strict comparison
{
   echo '$foo is falsy';
}

echo (bool) strval($foo); // false