I am creating a collection class and would like it to be drop-in-replacement for arrays, which I use currently.
How to create a class which could be casted
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)
If you have a code size of "many megabytes" the issue is probably not that your naming scheme is too verbose. I would instead look for duplication and try to abstract code.
OTOH, why is the size of the code a big issue? Try to minimize the code you are including each time php runs. Stray code that isn't used makes no difference. If you have to include a lot of code, consider using caching software, such as MMCache.
And to answer your original question, AFAIK there is no way to add type coercion to PHP classes. an instance will always evaluate to true.
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.
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.
If you don't want to implement your own method you could hack your way into __toString
, like this:
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
You cannot.
In PHP an object when cast to bool
always produces true
. There is no way of changing that.