我正在创建一个集合类,并希望它是我目前使用的数组的替代品。
如何创建一个可以转换为布尔值的类,所以该类可以是真正的还是虚假的?
一个简单的测试表明,空类的对象是真实的:
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()) {
//...
}
但这是不可接受的,因为我的代码库有很多兆字节。
这里有解决方案吗?
答案 0 :(得分:21)
感到沮丧,我花了一些时间查看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