1

For a metering project, I have a class MeterReadings, with a constructor that takes a previous reading as basis for all of its property values except the timestamp. In the example, the Object has only 3 properties, in real life there are many more. I could line by line for each property X do

$this->propertyX=$prevMS->propertyX

but that quickly becomes tedious.

What is best practice for copying all property values from the $prevMS object?

<?php class MeterReadings{
    function __construct($prevMS=NULL){
        if($prevMS===NULL){
            $this->gas=0;
            $this->water=0;
            $this->electricity=0;
        }else{
          //PHP can duplicate an object by assignment,
          //so I expected to be able to create a copy of $prevMS 
          //but cannot $this=$prevMS;
          //and cannot $this= clone $that;
          //which both throw error 'Cannot re-assign $this'
          foreach($prevMS as $key => $value){
            //logs "Undefined property: MeterReadings::$0"  
            print "$key => $value\n";   
            $this->$key = $prevMS->$value;
          }
        }
        $this->date=time();//set timestamp for this object
    }
}
$test= new MeterReadings();
$test2 = new MeterReadings($test);
print_r($test);
print_r($test2);
?>gets me

gas => 0
water => 0
electricity => 0
date => 1434448706
MeterReadings Object
 (
    [gas] => 0
    [water] => 0
    [electricity] => 0
    [date] => 1434448706
)
MeterReadings Object
(
    [gas] => 
    [water] => 
    [electricity] => 
    [date] => 1434448706
)

1 Answer 1

2

You can use clone and the __clone Magic Method to manage what get's cloned.

For example:

class Test
{
    public $value1;
    public $value2;
    public $timestamp;

    public function __clone()
    {
        $this->timestamp = null;
    }
}

$test = new Test();
$test->value1 = 1;
$test->value2 = 2;
$test->timestamp = time();

$test2 = clone $test;

print_r($test2);

// Test Object
// (
//     [value1] => 1
//     [value2] => 2
//     [timestamp] => 
// )

Alternative:

To keep things as in the __constuct as specified the comment below:

class Test
{
    public $value1;
    public $value2;
    public $timestamp;

    public function __construct (Test $copy = null)
    {
        if($copy) {
            foreach ($copy as $key => $value) {
                $this->$key = $value;
            }
        }
        $this->timestamp = 'whoop';
    }
}

$test = new Test();
$test->value1 = 1;
$test->value2 = 2;
$test->timestamp = 'whatev\'s';

$test2 = new Test($test);

print_r($test2);

// Test Object
// (
//     [value1] => 1
//     [value2] => 2
//     [timestamp] => 'whoop'
// )

This will work for objects that are not itterable and for properties that are protected (but not private). For objects that are itterable and return something else, you can use get_object_vars(), documentation here. For stuff that's private (are you sure you need private, that's unusual) you'll need to create getters. They can be protected if you don't want them externally available.

Sign up to request clarification or add additional context in comments.

5 Comments

DanielM, thanks for URLing the Magic Method. I'm trying to keep everything within the __construct method, but $this = clone ($prevMS); is not allowed. The hope is to keep all values private to the class. I adapt the question to this end.
Objects in PHP are stored as references. Even if you could do $this = $object you'd have to references to the same object. As an alternative you can do foreach($object as $key => $value) { $this->$key = $value; }. The object does not need to be itterable for this to work. If the object is itterable, then you can use get_object_vars()
Thanks. We have concurrently edited question and answer. I see my error; shoudln't refer to $prevMS->$value but to $value only.
I think you mean "you'd have tWo references". It is working now, thanks a bunch!
Yup. I'm really bad for not reading my posts before hitting submit. :p I'm probably in the top 5% of repeat editors for that reason. :p

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.