Serialized object coming out with internal value references - php

This is by far the strangest thing i have seen in PHP, but there surely is some sort of explanation.
Using serialize() i am storing some objects. At a later point, i revive them using unserialize().
Today i discovered a problem with an object that has been unserialized. Picture this scenario:
object__product_bundle Object (
[collateralValue] =>
[collateralGroup] =>
)
Now imagine $obj to be an instance of object__product_bundle as shown above.
When i did:
$obj->collateralValue = 10;
And checked the object variables, i was shown:
object__product_bundle Object (
[collateralValue] => 10
[collateralGroup] => 10
)
Mindboggling!
I spent an hour smashing my head against the table, as this didn't make sense. But when i started using var_dump() on the object, before making changes to it, i saw this:
object(object__product_bundle)#28 (15) {
["collateralValue"] => &NULL
["collateralGroup"] => &NULL
}
Apparently these properties/variables were somehow linked. I researched &NULL and all i found was this question which told me i am dealing with some sort of references.
But how?
My object comes from a serialized string.
Now, taking a look at the serialized string i found this:
s:15:"collateralValue";N;s:15:"collateralGroup";R:15;
What is R:15 ?
Can it be the issue?
How can this problem be addressed and where does it come from?
EDIT
After digging deeper, i found the culprit.
Orientiation:
The objects (as described above) are stored into a property of another object, which is the item of a shop cart.
class shopCart {
public $storage;
}
$cart->storage[] = new shopCart_item();
class shopCart_item {
public $object;
}
$object is where the products (object__product_*) are stored.
Upon placing an order, with the aim of being repeated (subscription), this entire shopCart is stored into the database as a blob.
Whenever a subscription order is scheduled, an automated task then grabs the old shopCart and generates a new order from it.
And here i found the culprit - i added the properties (collateralValue etc.) later during development, but there had already been stored orders.
Now during debugging i found that this is where PHP starts creating references, although i do not understand why.
Simply put:
static public function generateOrderFromSubscription() {
[...]
$order = new object__webShop_order();
var_dump($subscription->cart); // <-- no references are in here at all
$order->cart = serialize($subscription->cart);
var_dump($order->cart); // <-- suddenly, here i have the references
}
Apparantely, i use __sleep() for each object__product_* - which returns those variable names (including collateralValue and so on).
The question now becomes then: Why does PHP create references, when it is dealing with new properties for objects that were asleep but whose structure has changed in the meantime?
Very confusing!
EDIT #2
Finally some hope.
My __sleep() function basically returned a hardcoded array of variable names, as there were a ton of others i never wanted to store in the database. This approach apparently led to the current problem described in this question.
I still do not know why PHP creates references for variables in objects that were awoken without having those variables at all, but with those variables being returned in __sleep().
The only sensible solution to me, seemed to be to adapt __sleep(). I now do this:
public function __sleep(){
$vars=array(
'dbId',
'title',
'articleId',
'price_per_unit',
);
if(isset($this->collateralValue))
$vars[]='collateralValue';
if(isset($this->collateralGroup))
$vars[]='collateralGroup';
}
This way, __sleep() will not return (any of those two new) variable names (collateralValue, collateralGroup) which are not in use in the current object.

Well let's analyse your serialized string:
s:15:"collateralValue";N;s:15:"collateralGroup";R:15;
First property (key):
s:15:"collateralValue"
s just means it is a string
15 is the size of the string
collateralValue is the string itself the value (And if you look the string is 15 characters long)
First property (value):
N
N just mean NULL
Second property (key):
s:15:"collateralGroup"
s just means it is a string
15 is the size of the string
collateralGroup is the string itself the value (And if you look the string is 15 characters long)
Second property (value):
R:15
R means reference
15 means to the 15 value. So here the 15 value is probably the property collateralValue, which means if you change the value of it it also changes the value of the collateralGroup property
For more information see: http://www.phpinternalsbook.com/classes_objects/serialization.html

Related

Can a POST value ever be an instanceof something in PHP?

I've come across PHP code with the a check which tests if a POST value is an instanceof a class:
if ($_POST['something'] instanceof SomeClass) {
// do something
}
This seems odd to me, because I wouldn't think that the check can ever be true. A POST value is a string after all, and a string isn't an instance of a class.
I tried passing the serialized version of an instance (O:9:"SomeClass":0:{}), but that doesn't work (which makes sense, as it's still a string, not an object).
Am I correct in thinking that this check can never be true? Or am I missing something here?
I think this is not a simple question. I think theoretically "yes".
As a $_POST is a array of variables, and there is no limitation what the elements of a array can be, you can for example create a array of objects.
$array[] = new stdClass;
$array[0]->variable = value;
etc ...
You see the code author is checking if $_POST['something'], that is a element of the array, and that easily can be a object, is a instance of a class.
Now I have not tested it, but theoretically one could put a object of a class in a array and send it via $_POST nicely encoded.

PHP objecture structure chainability

I am trying to build a huge object structure that depends on hundreds of queries. To make it performant i did the following approach:
Each query result object is build into an object. For example a result set from table "apples" is build into an apple object. An orange result is build into an orange object.
Each object is saved in the registry, where the registry is a global array with functions like isExistent($type, $id), add($type, $obj) and get($type, $id). I want to store each object from point 1 once here to access it later without needing to call another query for the object (tons of objects are needed several times at different places).
I want objects to be chained together so i can do something like $tree->getFruits()->getApple()->getPlace()->getName(). I also want objects in my hierarchy to be linked in both directions for later use.
Why do i need this? For example the "apple" object with id 3 will be used once when the $tree object is instantiated, once when the $vitamins object is created, etc. I don't want to sent another query() each time to the database this object is needed. So i check something like:
Registry::isExistent("APPLE", $id)
and then get it from the Registry if it is there, and otherwise call it from db and then additionally add it to registry.
Problem: I am getting infinite loops for obvious reasons, which i could easily fix, but i don't know what's the best approach here.
Actual Code (not example stuff like apples and oranges from above):
$mask = new Mask($row["maskId"], $row["maskName"]);
foreach (Fields::getFieldsFor($mask->getId()) as $field) {
$mask->addField($field);
}
Registry::add("MASK", $mask);
and the getter for a field of a mask:
$mask = Registry::receive("MASK", $row["maskId"]);
$category = Registry::receive("FIELDTYPE", $row["fieldCategory"]);
$field = new Field($row["fieldId"], $category, $row["fieldQuestion"],
$row["fieldDescription"], $row["fieldPosition"], null, null, null, $mask);
Registry::add("FIELD", $field);
This will produce an infinite loop. A mask consists of several fields. When building the $mask object i want to have each field linked in the $mask array. When building each $field object i want to parent $mask linked to it. This will obviously fail because i only add to registry, AFTER they are done building which will obviously fail because before that they will already have a look at the registry.
There are 2 solutions i can think of:
Throw away my chainability and simply store the linked ids in my objects. So instead of storing a reference to the parent mask object in each field i would simply store the id and later retreive the actual object from the registry. This would be the easiest fix, but would throw away the chainability.
Split up the primitive object instantiation and the complex attributes. Just add the primitives via the constructor and add the complex like the actual $mask object to each field after the constructor AND after the Registry::add() via a setter.
What would be best to do here? Is something wrong in my general approach?
My goal simply is to build an easy to work with object structure (will be hundreds of objects) and to reduce redundant queries(). This really needs to be fast. I also only want each different object once and then linked accordingly in the complex object structure.
Thanks and sorry for a long description.
well, i've done something similar for one project few years ago using PHP Magic Method __get(). What i've done actually is storing the Getter Object inside a variable called Registry.get; something like: $this->get = new GetterObject($this);. Now, notice that i passed the Registry as a variable to my GetterObject, you might ask "why?" simply this is:
How it Works
The GetterObject gets initialized with the Registry as a variable (unless Registery as a global absolute object then no need for this!).
You "get" a ORM-object using something like Registery::get->fruits;.
The GetterObject will check the Registry array of objects to find fruits ORM-object.
The GetterObject will return a new instance of itself with some additional variables defining at which step you are; something like $this->path = "/fruits/";.
Now, when you "get" the second object, say, Registery::get->fruits->apples;, the GetterObject will simple check which was the last $this->path piece, on which it will depend to find the child element. Note that this step depends highly on how you structured your own ORM-objects array.
The GetterObject will repeat steps 4, and 5 until you end up with the object of desire and do something like ->return_values(); which is a defined function that returns the actual ORM-object you are looking for, not the GetterObject you were using to navigate through your ORM-objects.
As a small note again, this might not be the best practice for all the Registry-based platforms, so, make sure you really need something this complicated before doing so. Otherwise, a simple __get() definition might all what you need. Best of Luck.

Malformed field names in Symfony JsonResponse

I have a strange problem with Symfony's JsonResponse that I cannot seem to figure out. I have the following action in my controller:
public function loadTemplateAction($id)
{
$repository = $this->getDoctrine()->getRepository('AppBundle:Host');
$template = $repository->find($id);
return new JsonResponse((array)$template);
}
It's supposed to find the given template in my repository by the passed id. I want to use the returned data in an ajax call. It does what I want, but it seems to "prefix" all the field names with an asterisk. So it returns a response like this:
I can't figure out why it's putting those asterisks in front of the field names (they are obviously not named that way in my datasource). Does anybody have a clue what could be causing this type of behaviour?
First of all, see http://php.net/manual/en/language.types.array.php:
If an object is converted to an array, the result is an array whose
elements are the object's properties. The keys are the member variable
names, with a few notable exceptions: integer properties are
unaccessible; private variables have the class name prepended to the
variable name; protected variables have a '*' prepended to the
variable name. These prepended values have null bytes on either side.
This can result in some unexpected behaviour:
You probably should not just typecast your object to arrays and JSON encode them. Have a look at some of the serialization solutions that exist:
http://symfony.com/doc/current/cookbook/serializer.html
http://jmsyst.com/libs/serializer
These libraries offer great control on how to serialize your objects to different formats, including JSON.
If you need less control on how your objects are serialized to JSON, you could just implement the JsonSerializable interface.

How can I use a reference only and make no copies of the object argument in php

I have a function that requires information to be passed to it. The information is contained within an object. Therefore I must pass that object as one of the function arguments. The object is very large however, and I would like to reduce the overhead involved in making copies every time it is passed. Here is an example of
My function Call:
1 myFunction($myObject1);
and the function:
2 function myFunction($myObject2){
3 //do stuff
4 }
I understand there is more to it in php than just pass-by-reference vs pass-by-value. Correct me if I am wrong, but I believe on line 1 there is only a reference to the object made, but on line 2 the object is copied. To avoid this copy I have replaced ($myObject2) with (&$myObject2). I still refer to the object within the function definition as $myObject2 and everything seems to work. I believe I am now using a reference only and therefore making no copies of the object (which was my goal). Is my thinking correct? If not not why?
In PHP5, "objects" are not values. The value of the variables $myObject1, $myObject2 are object references (i.e. pointers to objects). You cannot get "the object itself"; objects can only be manipulated through these pointers.
Assignment and passing by value only copy values. Since objects are not values, they cannot ever be cloned through assignment, passing, etc. The only way to duplicate an object is to use the clone operator.
Putting & on a variable makes it pass or assign by reference, instead of by value without the &. Passing by reference allows you to modify the variable passed. Since the value of a variable cannot be an object, this has nothing to do with objects.

Why would one want to pass primitive-type parameters by reference in PHP?

One thing that's always bugged me (and everyone else, ever) about PHP is its inconsistency in function naming and parameters. Another more recent annoyance is its tendency to ask for function parameters by reference rather than by value.
I did a quick browse through the PHP manual, and found the function sort() as an example. If I was implementing that function I'd take an array by value, sort it into a new array, and return the new value. In PHP, sort() returns a boolean, and modifies the existing array.
How I'd like to call sort():
$array = array('c','a','b');
$sorted_array = sort($array);
How PHP wants me to call sort():
$array = array('c','a','b');
sort($array);
$sorted_array = $array;
And additionally, the following throws a fatal error: Fatal error: Only variables can be passed by reference
sort(array('c','a','b');
I'd imagine that part of this could be a legacy of PHP's old days, but there must have been a reason things were done this way. I can see the value in passing an object by reference ID like PHP 5+ does (which I guess is sort of in between pass by reference and pass by value), but not in the case of strings, arrays, integers and such.
I'm not an expert in the field of Computer Science, so as you can probably gather I'm trying to grasp some of these concepts still, and I'm curious as to whether there's a reason things are set up this way, or whether it's just a leftover.
The main reason is that PHP was developed by C programmers, and this is very much a C-programming paradigm. In C, it makes sense to pass a pointer to a data structure you want changed. In PHP, not so much (Among other things, because references are not the same as a pointer).
I believe this is done for speed-reason.
Most of the time you need the array you are working on to be sorted, not a copy.
If sort should have returned a new copy of the array then for each time you call sort(); the PHP engine should have copied the array into new one (lowering speed and increasing space cost) and you would have no way to control this behaviour.
If you need the original array to be not sorted (and this doesn't happen so often) then just do:
$copy = $yourArray;
sort($yourArray);

Categories