Cloning a PHP object is not working - php

I'm having the same poblem about making a copy of an object without reference in PHP but that soluction is not working.
I want to take all elements of a collection, change one property (add months) and push that new element into a new collection.
So I've used clone in order to get a totally new instance of an Invoice wich should be affected just that instance, but is not.
With this code:
while($thisInvoiceYear <= CURRENT_YEAR) {
$clonedInvoice = clone($thisInvoice);
$this->extendedInvoices->push($clonedInvoice);
$toSaveInvoice = $this->extendedInvoices->last();
$thisInvoiceYear = $this->getCurrentYear($toSaveInvoice);
$toSaveInvoice->Schedule->StartDate = Carbon::parse($toSaveInvoice->Schedule->StartDate)->addMonths($period);
unset($clonedInvoice);
}
Where $thisInvoice is an object of the Invoice collection, $this->extendedInvoices is a method of the class Invoice.
The problem is that every change done in $toSaveInvoice also affects to the original collection element. After 3 iterations, date is the same for all elements of extendedInvoices and should not be:
I am using Laravel 5.2, but I think there aren't helpers to clone an element of a collection.
Do you know why clone() is not working? Thanks a lot for your help.

A good idea is to read the manual first and ask a question on SO later.
From PHP manual:
Creating a copy of an object with fully replicated properties is not always the wanted behavior. A good example of the need for copy constructors, is if you have an object which represents a GTK window and the object holds the resource of this GTK window, when you create a duplicate you might want to create a new window with the same properties and have the new object hold the resource of the new window. Another example is if your object holds a reference to another object which it uses and when you replicate the parent object you want to create a new instance of this other object so that the replica has its own separate copy.
An object copy is created by using the clone keyword (which calls the object's __clone() method if possible). An object's __clone() method cannot be called directly.
$copy_of_object = clone $object;
When an object is cloned, PHP 5 will perform a shallow copy of all of the object's properties. Any properties that are references to other variables will remain references.
Even though the manual describes PHP 5 behavior, I can assure you, from my own experience, that PHP 7 behaves the same way.
If you need to clone with something else inside, implement the __clone method and define what exactly and how the object has to be cloned.

Related

Create an object instance from cache trough the class in PHP

Given a class with some really expensive code, I want to avoid running that code when re-defining an instance.
Best explained with some pseudo-code:
$foo = new Foo('bar');
print $foo->eat_cpu_and_database_resources(); #=> 3.14159
$foo->store_in_cache(); #Uses an existing Memcached and/or caching to store serialized.
#new thread, such as a new HTTP request. Could be days later.
$bar = new Foo('bar');
print $foo->eat_cpu_and_database_resources(); #=> 3.14159
The second $bar should re-initialize the earlier created instance $foo. Inside my actual class, I do several things on eat_cpu_and_database_resources(), which is named get_weighted_tags(): calculate a weighted tagcloud from values in $foo->tags. $foo->tags() was filled with expensive $foo->add_tag() calls. I would like to retrieve the prepared and filled instance from now on, from cache.
I have tried to simply fetch from (serialized) cache on __construct() and assign the retrieved instance to $this, which is not allowed in PHP:
function __construct ($id) {
if ($cached = $this->cache_get($id)) {
$this = $cached
}
else {
#initialize normally
}
}
Is this a proper way? Or should I treat every instance unique and instead apply caching in the eat_cpu_and_database_resources() method, instead of caching the entire instance?
Is there a built-in way in PHP to revive old instances (in a new thread)?
Depending on the size of Foo, you might want to cache the entire object in the cache store Drupal provides. If it's too big for that, see if it makes sense to just cache the result to the expensive method call(s).
If you want to unserialize an object from the PHP internal format, you have to use the corresponding unserialize method and might want to add the magic __wakeup method to do any post re-initializations:
The intended use of __wakeup is to reestablish any database connections that may have been lost during serialization and perform other reinitialization tasks.
Since you have to have the serialized string for that first, you might want to add some facilitiy to encapsulate this logic, like a Factory or Builder pattern or a dedicated FooCache.
Personally I find caching the method call the best option because there's no point in caching the whole object when it's really just the method call that's expensive. That will also save you any additional work checking whether there is a serialized string to start with or building a factory.

In PHP, how can I unserialize into the current object?

This is probably a noob question, so please be kind.
I'm trying to implement a cache on an expensive "activity" object. In the constructor I first check the cache to see if this Activity instance already exists. If not, I do all the queries to build up the object, serialize it and save it to cache. The next time I come in, I check the cache and my object is there, so I unserialize it. Now is my problem, how do I put that object into $this, the current object? I can't just say "$this = unserialize($row[0]);" That fails with the error message, "Cannot re-assign $this in ActivityClass.php". What am I missing?
Thanks a ton!
Mike
If you don't want your construction to leave the class, you can create a factory method:
class Activity
{
public static function Create(/* your params */)
{
// construct cache and key, whatever
$obj = unserialize($cache->get($key));
if ($obj) return $obj;
return new Activity(/* params */);
}
// rest of your stuff
}
You'll have to serialize only your object's internal state, i.e. its parameters (aka "member variables"). In fact, in this instance, serialize() isn't really what you want to do; rather, you want to store your ActivityClass's data to your cache, not the serialization of the entire object. This gets tricky, though, because as you add new parameters later you need to remember to store these in your cache as well.
Alternatively, you can implement a singleton or factory pattern for your ActivityClass. Since you say you're pulling the class from the cache in the constructor, I take it that only one instance of this class is meant to exist at any given time? In this case, you should make your class a singleton, by doing the following:
Make the __construct() method private or protected.
Create a public static method (I tend to call this getInstance()) that will check your cache for the object, or instantiate a new one and then cache it.
Now instead of directly instantiating a new ActivityClass object, you instead write $foo = ActivityClass::getInstance();, which gives you either a new object or unserializes and returns your cached one.
As you noticed, you cannot just override the current object as a whole.
Instead, a possibility would be to store the data you're serializing/unserializing into a property of your object.
This way, you wouldn't serialize your whole object, but only one of its properties -- and only that single property would be overriden when unserializing.
Typically, you wouldn't serialize the connection to the database, which could be another property of your object.
Another possibility would be to not have your object deal with its own (de-)serialization.
Instead, you should :
Use an external class to instanciate your object
With that external class being responsible of either :
Loading data from cache and pushing it into your object,
Or calling the right method of your class, to load data from the database -- and, then, save that object to cache.

PHP sharing objects, do they get updated in another class?

I'm wondering; lets say you start an object in a new class; at the beginning of your app.
If you later pass that object into another class; as a variable and add/modfiy something on it. Will it be updated in the original object and its home class?
Or does it become a 'new' object, a different object in a new class? And will it continue to be the same throughout their uses. In either part of the app?
This is for my own clarification, as opposed to a specific coding question.
objects are passed by reference in php5, so the object is "updated", unless you clone it.
An object doesn't start, it is instantiated, it becomes an instance of a class.
You can't pass an object to a class. You can pass it to a function or a method, with no difference in these 2.
In every language with proper OOP, objects are passed as reference, so any updates anywhere on that object is reflected everywhere. It's an object, you pass it, modify it, and it remains modified.
You should reread what classes are. Obligatory car example ahead:
Imagine a class Car. Now you create a new instance of that abstract concept:
$myCar = new Car();
$myCar->color = "blue";
$yourCar = new Car();
$yourCar->color = "red";
Every object has its own properties. The class may set default values in its constructor functions, but changing one car's color to orange doesn't impact any other car.
In php5 (php4 oo is weird, I wouldn't suggest it to a beginner), every time you assign from a variable that contains an object, the object reference is copied. Therefore, after executing
$ourCar = $yourCar;
$ourCar->color = "yellow";
, $yourCar->color is "yellow".

Cloned object should use single file handle or each one has their own?

I am working on a wrapper that parses a data file for an existing web tool. However, I am using large data files so I cannot load the whole thing into memory so I have to walk through it loading it in small chunks. The existing web tool expects data in a style similar to SimpleXML ($obj->parentnode->childnode->childnode returns a string or a node object of some sort). Thankfully the structure is similar to XML but the syntax is odd. And I can't just translate it to a sane format because of extenuating circumstances. So I have to emulate it on the fly.
As I walk through the file I won't be needing to parse the whole tree, just the sub-node names of the current node. Each sub-node name and associated offset will be stored in the parent node. If contents of a sub-node need to be accessed then the parent-node object will be cloned, offset values will be updated and the sub-node object will begin parsing it's content until it finds the requested subnode.
The questions I have are:
Cloning the parent node object will give child clones the file handle. Should all the clones use the same handle and use fseek to jump around the file if needed (and that is a pretty big if)?
Do I need to close the file? Or will garbage collection at the end of script execution close it? What dangers do I face if I don't?
Will I need to create handles for each clone, or should I stick with them sharing one? If so is there an upper limit?
Is there a way for a cloned object to hold a reference to the original object? If I am putting the handle close in the object destructor I probably shouldn't close it if the object is a clone. And being able to trace upwards may come in handy, possibly.
If you implement the __clone method you can do whatever you want when cloning an object like setting a property flaggin it as a clone and or setting a property that holds the parent.
edit:
public function __clone()
{
$clone = clone $this;
$clone->isCloned = true;
$clone->parent = $this;
$clone->resource = $this->resource; // i dont think resources are copied be default
// additional property transference
return $clone;
}

Tracking PHP Object property changes

I'm trying to track all changes made to a PHP variable. The variable can be an object or array.
For example it looks something like:
$object = array('a', 'b');
This object is then persisted to storage using an object-cache. When php script runs again.
So when the script runs the second time, or another script runs and modifies that object, I want those modifications to be tracked, either as they are being done, or in one go after the script executes.
eg:
$object[] = 'c';
I would like to know that 'c' was added to the object.
Now the actually code looks something like this:
$storage = new Storage();
$storage->object = array('a', 'b');
second load:
$storage = new Storage();
var_dump($storage->object); // array('a', 'b')
$storage->object[] = 'c';
What I want to know is that 'c' was pushed into $storage->object so in the class "Storage" I can set that value to persistent storage.
I have tried a few methods, that work, but have downsides.
1) Wrap all objects in a class "Storable" which tracks changes to the object
The class "Storable" just saves the actual data object as a property, and then provides __get() and __set() methods to access it. When a member/property of the object is modified or added, the "Storable" class notes this.
When a a property is accessed __get() on the Storable class returns the property, wrapped in another Storable class so that changes on that are tracked also, recursively for each new level.
The problem is that the objects are no longer native data types, and thus you cannot run array functions on arrays.
eg:
$storage = new Storage();
var_dump($storage->object); // array('a', 'b')
array_push($storage->object, 'c'); // fails
So instead we'd have to implement these array functions as methods of Storable.
eg:
$storage = new Storage();
var_dump($storage->object); // array('a', 'b')
$storage->object->push('c');
This is all good, but I'd like to know if its possible to somehow use native functions, to reduce the overhead on the library I'm developing, while tracking changes so any changes can be added to persistent storage.
2) Forget about tracking changes, and just update whole object structures
This is the simplest method of keeping the objects in the program synchronized with the objects actually stored in the object-cache (which can be on a different machine).
However, it means whole structures, like an array with 1000 indexes, have to be sent though a socket to the object-cache when a single index changes.
3) Keep a mirror of the object locally
I've also tried cloning the object, and keeping a clone object untouched. Then when all processing is done by the PHP script, compare the clone to the modified object recursively, and submitting changed properties back to the object-cache.
This however requires that the whole object be downloaded in order to use it.
It also requires that the object take up twice as much memory, since it is cloned.
I know this is pretty vague, but there is a quite a bit of code involved. If anyone wants to see the code I can post it, or put it up on an open SVN repo. The project is open source but I haven't set up a public repository yet.
Since your 'object' is really an array, you can't add functionality to it. Your idea of encapsulating it with class methods is the correct approach. Worrying about performance over proper design at this stage is irrelevant and misguided - the overhead you incur with this approach will likely be insignificant to your overall application performance.
You should look into the SPL array classes such as ArrayObject. They provide a complete array-like interface and are easily extendable.
In all honesty I would reconsider what you're doing. You're really trying to turn PHP into something it's not. This is the kind of ORM you see in Java and C# not PHP, which is basically transient in nature (meaning everything, barring memcache/APC/etc, is recreated on each request). This is anathema to sophisticated object caching and change tracking.
That being said, the only way you could do this is wrap everything in something that overloads __get(), __set() and __isset() and implements ArrayAccess.

Categories