Following my last two posts (in portuguese, here and here) about class juggling in PHP I started getting some google hits on “cast class in PHP”.
A couple of days after I just have to duh! myself in the head a couple of times for whining about it and not thinking around the problem: I found a really simple, quick and dirty solution.
First things first, if want to know how to convert integers to booleans and other primitive juggling, go read the excelent manual. You will even find some fine examples on how to convert an object into another class, quite interesting stuff if you’re into the loose and weak PHP way (no pun intended).
So moving on. I’ve been dreaming about class cast (not conversions) but it ain’t possible in PHP. I’d love to have it in place because it would really help those programmers that use doc-blocks and smart auto-doc IDE’s autocompleting. It would also allow a more elegant and cleaner style, namely by going more EAFP and less LBYL.
The solution
So you can’t write (Whatever)$obj->method() in PHP right?
Right! But what about to_Whatever($obj)->method() would it work?
Yep! By wrapping an object in a transparent function call you can:
- tell the IDE what class you are dealing with, and he’ll tell you back the members on Ctrl+Space
- throw a catchable error if you pass an incompatible class
/**
* cast to whatever
*
* @param Whatever $obj
*
* @return Whatever
*/
function to_whatever(Whatever $obj)
{
return $obj;
}
So whenever you have an object that the IDE is not aware of it’s class (example: it was returned by some generic function, like your get() method in that handy Registry class you keep asking for things) but you know which class, superclass or interface it should be… just throw it at the cast function!
Complete solution
So, even better, why not create a “static” class that does all the needed casts?
/**
* "static" class performs all kinds of "casts"
*/
class Cast
{
/**
* cast to Db
*
* @param Db $obj
*
* @return Db
*/
public static function Db(Db $obj)
{
return $obj;
}
...more cast methods...
}
The function above accepts instances of Db or any Db subclass. It does not “cast” to any particular subclass, remember that’s not the point. That would be (as mentioned previously) destroying the benefits of polymorphism. But it does validate that the object is of the right type. And it also puts it back into context!
Let’s se how to use it:
// fetch db from registry
$db = Registry->get('dbconnection');
try
{
// cast
$db = Cast::Db($db)
// use it
$db->query( ... );
}
catch (Exception $e)
{
...
}
So, now your IDE knows that $db should be an instance of Db, regardless of begin DbMysql, DbMysqli or any other subclass of Db. It will show you, connect(), query(), etc.. when you ask for autocomplete.
Also, because your Registry class could be somehow mistaken, these code will validate that you got a valid Db instance from Registry::get(), instead of something totally absurd like a Widget or XML data.
Catchable fatal error: Argument 1 passed to Cast::Db() must be an instance of Db, instance of Whatever given
Beware that wrapping everything in 1 single try {} catch {} you will also catch any exception thrown in the query() method. You should test the exception type (you should throw your own custom exceptions inside of these methods anyway) or wrap the cast call in a single try {} catch {} block.
So that’s it! I know it’s not that elegant, and it does require you to implement a function for every casts you are using in a certain application or framework. Which is not that bad since it helps you track the mess you are into. Perfect would be to create on Cast class per Package, but namespaces are also not available (whinnnnner!)…
Hope you liked the idea and wish it makes your life easier. It’s already working for me.



2 Responses to “Type cast in PHP: doing the trick beyond the language”