我正在创建一个集合类,并希望它是我目前使用的数组的替代品.
如何创建一个可以转换为布尔值的类,所以这个类可以是真的还是伪造的?
一个简单的测试表明空类的对象是真实的:
class boolClass {}
$obj = new boolClass();
var_dump( (bool)$obj);
//prints
//bool(true)
Run Code Online (Sandbox Code Playgroud)
但是我需要决定我的班级是真的还是假的.有没有办法告诉PHP引擎如何将我的类转换为布尔值?就像我可以用__toString()做的那样?
背景:
让我们说我写了一个这样的类(这只是一个例子):
class MyCollection implements ArrayAccess, Iterator {
//...
}
Run Code Online (Sandbox Code Playgroud)
我目前大量使用这种模式:
$var = array();
if (empty($var)) {
//array is empty, (or there is no array at all)
// I do something here
}
Run Code Online (Sandbox Code Playgroud)
我希望看起来像:
$var = new MyCollection(array());
Run Code Online (Sandbox Code Playgroud)
并保持其余不变.但是包含MyCollection的$ var总是很简单所以我需要满足以下所有条件:
if ($var->isEmpty()) {
//...
}
Run Code Online (Sandbox Code Playgroud)
但这是不可接受的,因为我的代码库有很多兆字节.
有解决方案吗?
小智 22
经过多次焦虑,失望和黑客攻击后 - 我相信我找到了解决方案.该解决方案不要求任何扩展; 它可以用很少量的PHP样板实现.但是,在自己实施此解决方案之前,请注意这是 - 实际上 - 这是一个巨大的黑客攻击.话虽这么说,这是我发现的:
很沮丧,我花了一些时间查看Booleans的PHP文档.虽然用户创建的类被简单地拒绝了作为布尔值转换的能力,但是一个类 - 奇怪的是 - 提供了这种能力.精明的读者会注意到这个类正是内置的SimpleXmlElement.通过演绎过程,假设SimpleXmlElement的任何子类也将继承其独特的布尔铸造功能似乎是公平的.虽然从理论上讲这种方法似乎是有效的,但围绕SimpleXmlElement的神奇之处还在于消除了这种方法的实用性.要理解这是为什么,请考虑以下示例:
class Truthy extends SimpleXmlElement { }
Run Code Online (Sandbox Code Playgroud)
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!';
Run Code Online (Sandbox Code Playgroud)
实际上,Truthy继承了SimpleXmlElement提供的boolean-casting属性.但是,这种语法很笨拙,并且不太可能从这个类中获得很多实用程序(至少与本机使用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!';
Run Code Online (Sandbox Code Playgroud)
如您所见,尝试在子类上设置属性会立即破坏我们从继承的boolean-casting获得的实用程序.不幸的是,SimpleXmlElement有另一个神奇的功能,打破了我们的惯例.显然,当您设置SimpleXmlElement的属性时,它会修改XML!你自己看:
$xml = new SimpleXmlElement('<element></element>');
xml->test = 'content';
echo $xml->asXML(); // <element><test>content</test></element>
Run Code Online (Sandbox Code Playgroud)
那么我们可以从子类化SimpleXmlElement获得任何实用程序!值得庆幸的是,经过多次黑客攻击,我找到了一种方法来将信息保存到这个子类中,而不会破坏布尔铸造魔法:评论!
$false = new Truthy('<!-- hello world! --><false></false>');
if (!$false) echo 'Great Scott! It worked!';
Run Code Online (Sandbox Code Playgroud)
进展!我们能够在不破坏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);
}
}
Run Code Online (Sandbox Code Playgroud)
为了消除一些笨拙,我添加了一个公共静态工厂方法.现在我们可以创建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'];
}
Run Code Online (Sandbox Code Playgroud)
你有它!在用户定义的类中进行布尔流换的完全hacky(但可用)方法.如果你在生产代码中使用它,请不要起诉我,它会爆炸.
在此页面上,枚举了可以为类定义的魔术方法.
http://www.php.net/manual/en/language.oop5.magic.php#language.oop5.magic.tostring
你证明你已经知道了__toString().
不幸的是,那里没有列出你所要求的神奇方法.所以,我认为现在你唯一的选择是定义一个方法并明确地调用该方法.
您可以查看 PHP运算符扩展,您可以使用它来重载许多运算符,包括 == 和 ===。通过此扩展,理论上您应该能够编写与布尔值相当的类,如下所示:
if($object == true)
Run Code Online (Sandbox Code Playgroud)