tags:

views:

64

answers:

5

Basically what I'm trying to achieve is to make the object reference another object of the same class - from inside of a method. It'd be perfect if the following would work:

$this = new self;

But one cannot reassign $this in php.

I of course know, I can return another object from the method and use that, so please do not advise that. The question is:

How can I make $this to be a clone of another object of the same class?

or more specifically,

I want an object to revert to a specific state that was previously saved.


EDIT: some examples where this might be useful.

Say you have an Url object that accepts controller, action and lots more things. You are going to have it fetch a lot of links with the same, say, controller and action, but other properties will differ. I instantiate the object with the common parameters, call a method for it to save its state which it reverts to after outputting a link (IOW after __toString method).

$defaultPath = Url::factory('controller','action1')->extraParams('status',0)->save();

echo $defaultPath->action('action2'); // this has action2 as action and a status 0 as extra params
echo $defaultPath->extraParams('status',2); // this has action1 as action and 2 as status

Another use is I have a CRUD table and I have to configure each column as an object I pass to the main table object. After passing the column I launch a reset method on the column, so I can have code like the following:

    $column->field = 'layouts_id';
    $column->value = $layoutId;
    $dbInput->addColumn($column);

    $column->field = 'pages_id';
    $column->value = $pagesId;
    $dbInput->addColumn($column);

In both situations I save a lot of code and confusion, don't you think?

+1  A: 

What you are asking doesn't make sense in OOP. The this variable (or whatever its equivalent is in the OOP language of your choice) should always point to the current instance and should never be changed. That's whats it is designed for.

Darin Dimitrov
edited the question to reflect the possible, IMO sensible uses for this pattern.
Raveren
A: 

You cannot assign a value to $this in PHP. It will always contain the current object, and that cannot be changed.

It sounds like you are trying to change all references to the current class to point to a different class. There is no way to accomplish this.

The best work around I can think of is to have a $copy member variable in your class. You can then check if $copy is not null and pass through all methods to that object instead. However, this will still leave you with your original object in memory.

Alan Geleynse
+2  A: 

if your hypothetical example $this = new self; did work, it would presumably be intended to effectively reset the object to a clean state.

You can't achieve this the way you've asked, but you could surely do it fairly easily by writing a reset() method that sets all the properties to their intial state and then calls the __constructor() method.

If you want to go further and clone another instance of the class from within the object that would become the clone (as per the last part of your question), that'll be a lot harder. You certainly won't be able to do it in a nice way. You might be able to jury-rig it by writing a method that takes the object to be cloned as a parameter and sets all the current object's properties to the properties of the other object by reference. That would only be possible of course if all the properties were public, but you could try it. Certainly not ideal though. Far far better to simply do it from outside of either of the objects... but you did say not to suggest that... ;)

Spudley
[..] reset() method that sets all the properties to their intial state and then calls the __constructor() method. < but every field I add that has an initial value will have to be added to the `reset()` method, that's not cool. I did not get the `__constructor` part though.
Raveren
$this should never change - and PHP is built this way so you will be in trouble going against the design of the PHP core. `function reset(){foreach((array)$this as $k=>$v)unset($this->$k);}`
Xeoncross
@Xeoncross, regarding the method suggestion, what if some fields are instantiated with a value? `class A {public $b = 'c';}`
Raveren
Doesn't matter, everything will be removed.
Xeoncross
+1  A: 

What I think you're getting at is basically the Flyweight Design Pattern. You don't do it by re-mapping $this, but you create your objects in such a way that they can be re-used without needing to constantly construct and destruct them...

Couple that with the Object Pool Design pattern, and you should be golden...

ircmaxell
No, that's not it, but the pattern is indeed interesting, I'll bear it in mind. Is the question too vague?
Raveren
Well, it's not **what** you're trying to do, but it solves the same problem (if I understand your question in the first place). Make objects cheap and re-usable. Sure, it does it in a different way, but it's still likely applicable. (Oh, and I'm not even going to answer the "you cannot assign a value to `$this`" question, since other people have already answered it quite well)...
ircmaxell
+1  A: 

I see a huge problem with what you're intending:

If you found a way to reassign $this, how do you make sure, that anybody using your class knows how it behaves? While I admit, that it's kinda interesting to do

$column->field = 'layouts_id';
$column->value = $layoutId;
$dbInput->addColumn($column);

$column->field = 'pages_id';
$column->value = $pagesId;
$dbInput->addColumn($column);

What happens, if I need $column twice?

$column->field = 'layouts_id';
$column->value = $layoutId;
$dbInput->addColumn($column);
// log my value
$logger->log($column); // oops, it's empty

...

You would break the expected behaviour and imho make it extremely hard to grasp your code.


But to give some ideas on how to still achieve the aforementioned behaviour:

// clone when passing on
$column->field = 'value_id';
$column->value = $value;
$dbInput->addColumn(clone $column);

// manually (re-)create a new object based on an older
$column->field = 'value_id';
$column->value = $value;
$dbInput->addColumn(new Column($column));


Otherwise the reset() method also sounds feasible:

class Column() {

    public function reset() {
        $this->field = 'standard field id';
        $this->value = 'standard field value';
    }

    public function __construct() {
        $this->reset();
    }

}

This way you could use it like so:

// manually (re-)create a new object based on an older
$column->field = 'value_id';
$column->value = $value;
$dbInput->addColumn($column);
$column->reset();


To overcome the problem of specifying the default values twice (untested):

class Column() {

    public $field = 'standard field id';
    public $value = 'standard field value';

    // keep a static object for resetting
    private static $__blueprint = null;

    public function reset() {
        foreach (self :: $__blueprint as $k => $v)
            $this->$k = $v;
    }

    public function __construct() {
        if (!isset(self :: $__blueprint))
            self :: $__blueprint = clone $this;
    }

}
Cassy
one would clone it before passing it to the addColumn method, or the overwriting can be made optional (eg. `$dbInput->addColumn($column, TRUE);` would not reset the column, that's a snap to implement.)
Raveren
But still, that's not what somebody would expect. When I use an object, I want to deal with it. I wouldn't want a reading method of an unrelated class to clear my object. It feels as if you would lend your book to somebody who erases the letters when he's finished reading. (And I wouldn't want to explicitly tell him, that he shouldn't erase them. As I said, it's not a behaviour you would expect normally)
Cassy
to reflect your edits: I'd prefer no additional steps in the initialization of the CRUD table, even though they are valid (thus, +1). However, the reset method does not keep the code DRY, you have to synchronize initial values in two places and that's just a bug loaded time bomb.
Raveren
The `reset()` method could be your single point of defining defaults. Or, see my edit above.
Cassy
in response to your comment, yeah, but on the other hand you save a line or two for each column, which are not only tedious to retype, but can be forgotten leading to hardly noticeable bugs. Also, the column object has no other function, just to be a container of settings, I could also have used an array - it'd be the same feature wise (not so much documentation wise.)
Raveren
`>The reset() method could be your single point of defining defaults.`this could have been a reason to give a +1, but you already got one from me :) Still, the object states part is unanswered, only the reset() one.
Raveren
actually, the final edit functions very close to what I wanted, thanks, accepted!
Raveren