经过长时间的烦恼、失望和破解,我相信我已经找到了一个解决方案。这个解决方案不需要任何扩展;它可以通过一小段PHP模板来实现。但是,在您自己实施此解决方案之前,请注意,这实际上是一个非常大的黑客攻击。话虽如此,以下是我发现的内容:
感到沮丧后,我花了一些时间查看PHP布尔类型文档。尽管用户创建的类被简单地拒绝为布尔类型强制转换,但有一个类却奇怪地被赋予了这种能力。敏锐的读者会注意到,这个类就是内置的SimpleXmlElement。据此推断,任何SimpleXmlElement子类也将继承其独特的布尔类型强制转换功能。虽然从理论上讲这种方法似乎是有效的,但是SimpleXmlElement周围的魔法也削弱了这种方法的实用性。要理解为什么,请考虑以下示例:
class Truthy extends SimpleXmlElement { }
Truthy是SimpleXmlElement的一个子类,因此我们应该能够测试它是否继承了其特殊的布尔转换属性:
Truthy是SimpleXmlElement的一个子类,因此我们应该能够测试它是否继承了其特殊的布尔转换属性:
$true = new Truthy('<true>1</true>');
if ($true) echo 'boolean casting is happening!';
$false = new Truthy('<false></false>');
if (!$false) echo 'this is totally useful!';
事实上,SimpleXmlElement所提供的布尔强制属性也被Truthy继承。但是,这种语法很笨拙,并且与原生使用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!';
正如你所看到的,试图在我们的子类上设置属性会立即破坏我们从继承的布尔强制转换中获得的效用。不幸的是,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!';
进展顺利!我们成功地将有用的信息传递到这个类中,而不会破坏布尔强制转换!好的,现在我们只需要清理一下,这是我的最终实现:
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'];
}
到此为止!这是一种非常草率(但可用)的方法,可以在用户定义的类中进行布尔转换。如果您在生产代码中使用它并且出现问题,请不要起诉我。