stdClass dynamic member - memory exhausted - php

The problem is simple:
I have an object like
{a:'A', b:'B'}
and i want it to be like
{a:'A', new_a:'A', b:'B', new_b:'B'}
The code used is:
<?php
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
foreach($obj as $field=>$value)
{
$obj->{'new_'.$field} = $value;
}
The output is:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 21290 bytes) in /path/to on line 9
Line 9 is this:
$obj->{'new_'.$field} = $value
I do have alternatives to solve the problem, but i don't understand why this particular code generates this error.
Does anybody know the reason for this error, eventually an explanation.
Thank you!

The problem is, as you're iterating the source object properties, you're adding more properties to the source object, so the foreach loop can never end.
You can see this happening by adding a var_dump call to the loop:
foreach ($obj as $field => $value)
{
$obj->{'new_'.$field} = $value;
var_dump($obj);
}
Results in:
object(stdClass)#1 (29) {
["a"]=>
string(1) "A"
["b"]=>
string(1) "B"
["new_a"]=>
string(1) "A"
["new_b"]=>
string(1) "B"
["new_new_a"]=>
string(1) "A"
["new_new_b"]=>
string(1) "B"
["new_new_new_a"]=>
...
Instead, create a new object and replace the old one:
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$newObj = new stdClass();
// Copy old properties
foreach ($obj as $field => $value) {
$newObj->{$field} = $value;
}
// Create new properties
foreach($obj as $field => $value) {
$newObj->{'new_' . $field} = $value;
}
Or even:
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$newObj = new stdClass();
// Copy old properties and create new
foreach ($obj as $field => $value) {
$newObj->{$field} = $value;
$newObj->{'new_' . $field} = $value;
}

I'm not sure why there are so many suggestions to create a second object to work around this. In my opinion, the simplest thing to do would just be to grab the fields before the loop with get_object_vars, so that you're only iterating over the initial fieldset:
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$fields = get_object_vars($obj);
foreach ($fields as $field => $value) {
$obj->{'new_'.$field} = $value;
}
See https://eval.in/937616

You are appending more data unto the same object you are Looping through, using also the properties of the same Object.... To counter that, you can just do something like this:
<?php
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$obj2 = clone $obj;
foreach($obj as $field=>$value)
{
$key = 'new_'.$field;
$obj2->{$key} = $value;
}
// IF YOU STILL WISH TO HAVE $obj AS YOUR MAIN OBJECT,
// YOU CAN AS WELL RE-CLONE $obj2 INTO $obj AND DELETE $obj2 LIKE SO:
$obj = $obj2;
unset($obj2);
var_dump($obj);
var_dump($obj2);
UPDATE
If you wish to be consistent (ie: without Cloning) and also for reasons that #Dan has mentioned, You could simply use stdClass() like so:
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$obj2 = new stdClass();
foreach($obj as $field=>$value)
{
$key = 'new_'.$field;
$obj2->{$field} = $value;
$obj2->{$key} = $value;
}
$obj = clone $obj2;
unset($obj2);
var_dump($obj);
// YIELDS:
object(stdClass)[3]
public 'a' => string 'A' (length=1)
public 'new_a' => string 'A' (length=1)
public 'b' => string 'B' (length=1)
public 'new_b' => string 'B' (length=1)

AS told you have to create another $obj
instead of using the object inside the loop
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$obj2 = new stdClass();
foreach($obj as $field=>$value)
{
$obj2->{$field} = $value;
$obj2->{'new_'.$field} = $value;
}
$obj = $obj2;
echo '<pre>';
var_dump($obj);

your code is stuck in a infinite loop because if you have:
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
foreach($obj as $field=>$value)
{
$obj->{'new_'.$field} = $value;
}
In each iteration, you add a new position into the object, and this position must be iterated by the foreach.
You can clone the original object and iterate the clone, into the iteration you can add the new position to the original object.
$obj = new stdClass();
$obj->a = 'A';
$obj->b = 'B';
$objcopy = clone $obj;
foreach($objcopy as $field=>$value)
{
$obj->{'new_'.$field} = $value;
}

Related

Unsetting properties in array of objects where parent is a clone

I need to clone an object, then remove some properties from the clone. Using unset() on the cloned object works fine, but not on the cloned objects array of objects. I have simplified the object as it has quite a few more properties but the premise is the same.
$testobject = new stdClass();
$testobject->propertya = 'banana';
$testobject->propertyb = 'orange';
$testobject->propertyc = 'apple';
$testobject->childarray = array();
$testobject->childarray[] = new stdClass();
$testobject->childarray[0]->childpropertya = 'cola';
$testobject->childarray[0]->childpropertyb = 'bread';
$testobject->childarray[0]->childpropertyc = 'pasta';
echo "Original object:\n";
print_r($testobject);
$cloneobject = clone $testobject;
unset($cloneobject->propertyb);
foreach ($cloneobject->childarray as $index => $data) {
unset ($data->childpropertya);
}
unset($cloneobject->childarray['childpropertyc']);
echo "Original object expected to be the same but is NOT!:\n";
print_r($testobject);
I expect the $testobject not to change, but it does. Why?!
I have re-created the format in a 3v4l here
Solved, thanks for the tip #nice_dev
$testobject = new stdClass();
$testobject->propertya = 'banana';
$testobject->propertyb = 'orange';
$testobject->propertyc = 'apple';
$testobject->childarray = array();
$testobject->childarray[] = new stdClass();
$testobject->childarray[0]->childpropertya = 'cola';
$testobject->childarray[0]->childpropertyb = 'bread';
$testobject->childarray[0]->childpropertyc = 'pasta';
echo "Original object \n";
print_r($testobject);
$cloneobject = unserialize(serialize($testobject));
unset($cloneobject->propertyb);
foreach ($cloneobject->childarray as $index => $data) {
unset ($data->childpropertya);
}
unset($cloneobject->childarray['childpropertyc']);
echo "Original object is the same!\n";
print_r($testobject);
echo "Copied object is now different than $testobject!\n";
print_r($cloneobject);

How to trim object properties in PHP?

I have an object $obj as
$obj->{' Property1'} = " value1";
$obj->{'Property2 '} = "value2 ";
I want to get this object $obj as
$obj->{'Property1'} = "value1";
$obj->{'Property2'} = "value2";
I am able to trim all the values using
foreach($obj as $prop => &$val)
{
$val = trim($val);
}
but doing this (below) causing an error
foreach($obj as &$prop => &$val)
{
$prop = trim($prop);
$val = trim($val);
}
Please tell a solution.
Thanks in advance.
You can't reference a key.
What you have to do is unset it, and set the trimmed version like this:
<?php
$obj = new stdClass;
$obj->{' Property1'} = " value1";
foreach($obj as $prop => $val)
{
unset($obj->{$prop});
$obj->{trim($prop)} = trim($val);
}
var_dump($obj);
A little comment to Daan's answer. In his case script will fall into infinite loop if $obj have more than one property. So a working code looks like this.
<?php
$obj = new stdClass;
$obj->{' Property1'} = " value1";
$obj->{'Property2 '} = "value2 ";
$newObj = new stdClass;
foreach($obj as $prop => $val)
{
$newObj->{trim($prop)} = trim($val);
}
$obj = $newObj;
unset($newObj);
var_dump($obj);
Because you're trying to trim the property of the object. You can't do that.
That would work for an array, but not for an object. If you need to alter the properties of an object you need to change the properties of the class.

PHP: Set object properties inside a foreach loop

Is it possible to set property values of an object using a foreach loop?
I mean something equivalent to:
foreach($array as $key=>$value) {
$array[$key] = get_new_value();
}
EDIT: My example code did nothing, as #YonatanNir and #gandra404 pointed out, so I changed it a little bit so it reflects what I meant
You can loop on an array containing properties names and values to set.
For instance, an object which has properties "$var1", "$var2", and "$var3", you can set them this way :
$propertiesToSet = array("var1" => "test value 1",
"var2" => "test value 2",
"var3" => "test value 3");
$myObject = new MyClass();
foreach($propertiesToSet as $property => $value) {
// same as $myObject->var1 = "test value 1";
$myObject->$property = $value;
}
Would this example help at all?
$object = new stdClass;
$object->prop1 = 1;
$object->prop2 = 2;
foreach ($object as $prop=>$value) {
$object->$prop = $object->$prop +1;
}
print_r($object);
This should output:
stdClass Object
(
[prop1] => 2
[prop2] => 3
)
Also, you can do
$object = new stdClass;
$object->prop1 = 1;
$object->prop2 = 2;
foreach ($object as $prop=>&$value) {
$value = $value + 1;
}
print_r($object);
You can implement Iterator interface and loop through the array of objects:
foreach ($objects as $object) {
$object->your_property = get_new_value();
}
Hacked away at this for a few hours and this is what i finally used. Note the parameters passed by reference in two places. One when you enter the method and the other in the foreach loop.
private function encryptIdsFromData(&$data){
if($data == null)
return;
foreach($data as &$item){
if(isset($item["id"]))
$item["id"] = $this->encrypt($item["id"]);
if(is_array($item))
$this->encryptIdsFromData($item);
}
}

Add property to stdClass at the top of the object in php

When creating a object in php used to return JSON is it possible to add a property and force it to go at the top? I'd like this since the object is exposed via an API and it is nice to have ids at the top.
For example:
$obj = new stdClass();
$obj->name = 'John';
$obj->age = 26;
$obj->id = 3645;
When you json_encode() this, it turns into:
{
"name": "John",
"age": 26,
"id": 3645
}
Is there a way to force id at the top of the object even though it is added last? Note, I can't simply just add id before adding name and age because of other dependent code.
It's easily possible if you use an associative array instead of an object, i.e.
$x = ['name' => 'john', 'age' => 26]; // or: $x = (array)$obj
$x = ['id' => 123] + $x;
echo json_encode($x);
// {"id":123,"name":"john","age":26}
However, it's important to note that in JSON property ordering is not defined and should not be relied upon. If what you currently have works, this change would be rather useless in fact.
Not very elegant but...
$obj = new stdClass();
$obj->name = 'John';
$obj->age = 26;
$obj->id = 3645;
$name = $obj->name;
$age = $obj->age;
unset($obj->name);
unset($obj->age);
$obj->name = $name;
$obj->age = $age;
echo json_encode($obj);
Hmm, nice question!
It is not possible to add a property and force it to go at the top.
You have to sort the object properties or the array keys.
Some nitpicking here: JSON is unordered by definition, but the browsers respect the insertion order. More: https://code.google.com/p/v8/issues/detail?id=164
JSON
4.3.3 Object An object is a member of the type Object. It is an unordered collection of properties each of which contains a
primitive value, object, or function. A function stored in a property
of an object is called a method.
Check this out: http://ideone.com/Hb4rGQ
<?php
function array_reorder_keys($array, $keynames){
if(empty($array) || !is_array($array) || empty($keynames)) return;
if(!is_array($keynames)) $keynames = explode(',',$keynames);
if(!empty($keynames)) $keynames = array_reverse($keynames);
foreach($keynames as $n){
if(array_key_exists($n, $array)){
$newarray = array($n=>$array[$n]); //copy the node before unsetting
unset($array[$n]); //remove the node
$array = $newarray + array_filter($array); //combine copy with filtered array
}
}
return $array;
}
$obj = new stdClass();
$obj->name = 'John';
$obj->age = 26;
$obj->id = 3645;
function get_json_sorted($object, $array) {
return json_encode(array_reorder_keys(get_object_vars($object), $array));
}
var_dump(get_json_sorted($obj, array('id', 'name', 'age')));
This is a solution. Turn the object into an assoc array. Get the last item (both key and value) off of the array (I'm assuming id is the last element) and move it to the front. Finally convert the assoc array back into an object.
$data_array = json_decode(json_encode($obj), true);
if(is_array($data_array)) {
end($data_array);
$data_array = array_merge(array(key($data_array) => array_pop($data_array)), $data_array);
$data = json_decode(json_encode($data_array), false);
}
This is a similar answer to Jacks' answer ( https://stackoverflow.com/a/24900322/6312186 ) but that caused a fatal error for me. I had to tweak it a bit by casting to array, using array_merge() and cast back to object, but this worked nicely for me:
$obj = (object) array_merge( (array)$obj2, (array)$obj);
This code is more generic and should work for all versions of PHP, including strict mode. Full code here
$obj = new stdClass(); // create new object
$obj->name = 'john';
$obj->age = '26';
$obj2 = new stdClass(); // we want to add this object to the top of $obj
$obj2->id = 'uid2039';
$obj = (object) array_merge( (array)$obj2, (array)$obj);
var_dump($obj);
// object(stdClass)#8700 (3) { ["id"]=> string(7) "uid2039" ["name"]=> string(4) "john" ["age"]=> string(2) "26" }
If you are json_encodeing this, you don't need to cast it back to an object again before encoding it:
$arr = ['name' => 'John', 'age' => '26'];
echo json_encode($arr);
// {"name":"john","age":"26"}
// is the same as:
$obj = (object)$arr;
echo json_encode($obj );
// {"name":"john","age":"26"}

PHP Rename object property while maintaining its position

How can I rename an object property without changing the order?
Example object:
$obj = new stdClass();
$obj->k1 = 'k1';
$obj->k2 = 'k2';
$obj->k3 = 'k3';
$obj->k4 = 'k4';
Example rename by creating new property:
$obj->replace = $obj->k3;
unset($obj->k3);
Result:
( k1=>k1, k2=>k2, k4=>k4, replace=>k3 )
See the problem?
Simply recreating the object property causes the order to change.
I had a similar issue with arrays and came up with this solution:
Implemented solution for arrays
function replaceKey(&$array, $find, $replace)
{
if(array_key_exists($find, $array)){
$keys = array_keys($array);
$i = 0;
$index = false;
foreach($array as $k => $v){
if($find === $k){
$index = $i;
break;
}
$i++;
}
if ($index !== false) {
$keys[$i] = $replace;
$array = array_combine($keys, $array);
}
}
}
Looking for something similar to this but a function that will work on objects.
Can't seem to find any documentation on object property position indexes.
So, readd your properties
$obj = new YourClass();
$backup = [];
foreach ($obj as $key => $value)
{
$backup[$key] = $value;
unset($obj->$key);
}
replaceKey($backup, $find, $replace);
foreach ($backup as $key => $value)
{
$obj->$key = $value;
}
Since you have a function for your arrays, why dont you do something like :
$array = (array) $obj;
$replacedKeyArray = replaceKey($array);
$newObject = (object) $replacedKeyArray;

Categories