how to create a php class which can be casted to boolean (be truthy or falsy)

On this page, the magic methods that you can define for your classes are enumerated.

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

You demonstrate that you already know about __toString().

Unfortunately, there is no magic method listed there that does what you are asking. So, I think for now your only option is to define a method and call that method explicitly.


After much angst, disappointment, and hacking - I believe I have found a solution. The solution doesn't call for any extensions; it can be implemented with a very small amount of PHP boilerplate. However, before implementing this solution yourself, please take note that this is - in fact - a HUGE hack. That being said, here is what I discovered:

Frustrated, I spent some time looking over the PHP documentation for Booleans. While user-created classes are simply denied the ability to be cast as a boolean, one class - oddly enough - was afforded the capability. The astute reader would notice that this class is none other than the built-in SimpleXmlElement. By process of deduction, it seems fair to assume that any subclass of SimpleXmlElement would also inherit its unique boolean-casting capability. While, in theory this approach seems valid, the magic surrounding SimpleXmlElement also takes away from the utility of this approach. To understand why this is, consider this example:

class Truthy extends SimpleXmlElement { }

Truthy is a subclass of SimpleXmlElement, so we should be able to test if its special boolean-casting property was inherited:

$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!';

Indeed, the boolean-casting property afforded to SimpleXmlElement is inherited by Truthy. However, this syntax is clumsy, and it is highly unlikely that one would get much utility out of this class (at least when compared to using SimpleXmlElement natively). This scenario is where the problems start to come up:

$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!';

As you can see, trying to set a property on our subclass immediately breaks the utility we get from the inherited boolean-casting. Unfortunately for us, the SimpleXmlElement has another magical feature that breaks our convention. Apparently, when you set a property of a SimpleXmlElement, it modifies the XML! See for yourself:

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

Well there goes any utility we would get from subclassing SimpleXmlElement! Thankfully, after much hacking, I found a way to save information into this subclass, without breaking the boolean casting magic: comments!

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

Progress! We were able to get useful information into this class without breaking boolean-casting! Ok, now all we need to do is clean it up, here is my final implementation:

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);
}

}

To remove some of the clumsiness, I added a public static factory method. Now we can create a Truthy object without worrying about the implementation details. The factory lets the caller define any arbitrary set of data, as well as a boolean casting. The data method can then be called at a later time to retrieve a read-only copy of this data:

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

There you have it! A totally hacky (but usable) way to do boolean-casting in user-defined classes. Please don't sue me if you use this in production code and it blows up.


You can take a look at the PHP operator extension which you can use to overload many operators including == and ===. With this extension, you should be theoretically able to write a class comparable to boolean values like this:

if($object == true)

Tags:

Php

Class

Boolean