tags:

views:

61

answers:

2

Hi guys,

Basic setup. I have a classA instance which is a subclass of classX ... On construction (or sometime else), I want it to load another class classB which is also a subclass of classX and replace itself with classB in situ. Sort of like a factory, but one that replaces itself transparently. If need be, I can wrap classB with classA if there is a way to (at runtime) change the subclass of an object.

Currently I am using classA::__call() to emulate MRO magic, but is seems very inelegant. This needs to be done transparently to the caller of classA/B so that to the outside world, it isn't aware that classA has replaced itself with classB after construction (or anywhere else for that matter).

I know PHP can be a bit thin when doing things like this ... I am suspecting we can't, but it would be handy for my situation.

Also, 5.3 but ideally (yuck) 5.2/x

Thanks in advance (wishing I was coding in Python)

+1  A: 

This is not currently possible in PHP...

Without doing stupid things.

If every instance of the object is a reference, and those references can be found in $GLOBALS, and the object knows what every one of those instances is called, you could replace each and every reference of the old object with your new object. The outside world won't know the difference.

This is, however, a spectacularly awful idea. Using __call magic is probably the least insane way of accomplishing your goal.

Edit: There's always runkit, which will let you do things like add and remove methods from classes. However, it's a PECL extension and might not even work correctly...

Charles
+1, thanks for your input. I don't want to slag PHP off too much, but it is inferior :P
Aiden Bell
Indeed, PHP doesn't do interesting and advanced things like metaprogramming very well, if at all. Also, I've updated my answer with another option.
Charles
@Charles -- Runkit seems reeeaallly dirty. *sighs*, thanks though :)
Aiden Bell
@Aiden I get the impression PHP dev team wants to keep it that way, and for very good reason. You use PHP primarily to script HTML output, not to design satellites. With each year, PHP grows to accommodate more niches, such as OOP, CLI, memory, graphics, etc, and eventually .NET. Browsing through their bug database and RFC's, you get the impression they're starting to hate programmers coming in from other languages demanding more advanced features, forcing them to essentially become some massive genetically modified monstrosity absorbing the functionality and pitfalls of all languages.
bob-the-destroyer
@Charles Do you mean like `$GLOBALS['a'] = new b();` within class a's `__construct()`? If so, at least in 5.3, testing this does not change $a in any way. Almost as if the engine completely ignores this, probably because it's still busy making an object of class a and doesn't have time for such nonsense. Unless right after `$a = new a()` in another scope, you call `$b = new a();` Only then will $a be officially an object of class b. You're right - coding like this is getting bad.
bob-the-destroyer
@bob-the-destroyer - I agree with your statements about the languages intent. I just think that for the amount of libraries (and support it has) and how prolific it is, without advanced features it is just a dumb template language ... a toy ... and IMHO that doesn't warrant such popularity; These features can be added without altering the 'basic' syntax and semantics if done right. So all it is is lazyness.
Aiden Bell
@bob-the-destroyer - Also, I also wouldn't mind if their simple setup was tight ... but the API is sloppy to say the least ... which boils down to not-very-much being done well.
Aiden Bell
@bob-the-destroyer: I suspect it might work *after* construction. Will poke at it, though I see you've already done so.
Charles
+1  A: 

Ok, I've taken an interest in this question because I've reached the point where I'd like to discover the exact limits of PHP, including little hacks like this. Hopefully, I'll make sense this late at night and a few beers in me. Because of the ugly hackishness, I'm actually expecting to be downvoted.

Obviously, you can't do $this = $that. You also can't change the global variable you're currently trying to make into an object while it's being constructed, and attempting to do so will be ignored. As Charles said earlier, this can't be reasonably done. Not with clone, not serialize(), nothing within __construct().

So, unreasonably if you want $a to first become an object of class A, then convert mid-creation to class B, try this pseudo method: You'll have to call __construct of class A twice in a row. First time to handle construction of class A. Second time to complete the object converting it to class B. Class A handles the first half of construction, and class B the second half:


class A {

    function __construct() {
        $args = func_get_args();  // just to tell us the first round of __construct already occured
        if (array_key_exists(0, $args) AND $args[0]) {
            $GLOBALS['a'] = new B($GLOBALS['a']);
            // stop because "reconstruction" has stopped.  Nothing else you can do to $a in this scope.
            $this->aprop2 = "yay";
            // Seriously, stop.  Don't bother putting more code at this point, you're wasting your time.  Consider $a 'converted and returned' already.
            }
        // build on an object of class a here
        }
    }

class B {

    function __construct($var) {
        // maybe you'd like to do something with old $a?  If so, here's $var for you
        // continue constructing where A left off.
        }

    }


$a = new A(); // object of class A
$a->__construct(true);  // object of class B

Maybe instead make another method of class A named more importantly sounding, which does the same thing to convert the global $a into object of class B, just so it doesn't look so stupid as my example. In other words, you're probably already doing it as best as PHP allows.

Edit: Really though, the above is nothing more than $a = new A(); $a = new B($a);. For better code readability and maintainability, you may want not to use my example and instead opt to implement a factory or handler class that creates and juggles these objects for you. I found a brief and insightful www.ibm.com article explaining the concept of factories how they are applied in PHP. Maybe conceptually, you want a static class that acts like a cd changer, and this is where Return by Reference - to work with a variable reference to the object in any scope - and Variable Objects (ref: midir's comments on that page) - to dynamically set or work with the object - comes in handy.

bob-the-destroyer
+1 Might give this a go and a test. It is quite dirty, but definitely feasible.
Aiden Bell