I am looping through an object and i try to duplicate one of the items while changing one of it's variables.
But when i copy the original and then change the title in the new one, the old one changes along with it. Which it shouldn't, since i did not initialised it as a reference.
$calendar = array(
(object)[
'id' => 1,
'title' => 'original 1',
],
(object)[
'id' => 2,
'title' => 'original 2',
],
(object)[
'id' => 3,
'title' => 'original 3',
],
);
foreach ($calendar AS $key => $item){
if($item->id == 2){
$item->title = 'new 2';
array_splice($calendar, $key, 0, [1]);
$calendar[$key] = $item;
}
}
echo "<pre>";
print_r($calendar);
die();
I would expect the output of this to keep original 2 intact. But it changes it along with it.
(
[0] => stdClass Object
(
[id] => 1
[title] => original 1
)
[1] => stdClass Object
(
[id] => 2
[title] => new 2
)
[2] => stdClass Object
(
[id] => 2
[title] => new 2
)
[3] => stdClass Object
(
[id] => 3
[title] => original 3
)
)
Even if i make a new object and use that one to make the changes, it still changes the orignial.
foreach ($calendar AS $key => $item){
if($item->id == 2){
$new_item = $item;
$new_item->title = 'new 2';
array_splice($calendar, $key, 0, [1]);
$calendar[$key] = $new_item;
}
}
Now i could probably fix this by just making a new object from scratch and copy the values one by one in it. But where's the fun in that?
So my question is...Why does this happen? Even though i didn't cast $item as &$item
Assignment or clone ?
Because, you're using objects, the problem is that $new_item = $item; doesn't create a new object, it creates a new reference of $item, named $new_item.
In the following example, $a and $b are the same object:
$a = new stdclass;
$b = $a;
var_dump($a, $b);
Output is:
object(stdClass)#1 (0) {...} // same object #1
object(stdClass)#1 (0) {...} // same object #1
Clone
You could use the keyword clone to create a new instance:
$a = new stdclass;
$b = clone $a; // Clone the object
var_dump($a, $b);
Output:
object(stdClass)#1 (0) {...} // object #1
object(stdClass)#2 (0) {...} // new object #2
So, in your case, you could use:
if ($item->id == 2) {
$clone = clone $item; // << Create a COPY of $item
$clone->title = 'new 2'; // Update the copy, not the reference
$calendar[$key] = $clone; // Add this copy to final array
// ...
}
A note about parameters
When you're using objects as parameters for some functions, objects are reference, so, the given object can be updated in that function. Here is a simple example (demo):
function updateObject(object $object): void {
$object->newProperty = true;
}
$obj = new stdClass;
var_dump($obj);
updateObject($obj);
var_dump($obj);
The code above gives the following (condensed) output:
object(stdClass)#1 (0) { }
object(stdClass)#1 (1) { ["newProperty"]=> bool(true) }
Further reading : Objects and references
As simple PHP Object assignment does not create a new object. It simply creates a new pointer to the same object.
I think you want to use the clone keyword:
foreach ($calendar AS $key => $item){
if($item->id == 2){
$new_item = clone $item;
$new_item->title = 'new 2';
array_splice($calendar, $key, 0, [1]);
$calendar[$key] = $new_item;
}
}
Related
I'm trying to write to a variable inside an object and I can't find how to do it.
Array
(
[0] => stdClass Object
(
[id] => 3
[rota_name] => Tea and coffee
[rota_owner_name] => 9
[rota_notes] =>
[rota_entry] => {"rota_entry0":{"person":"8","rota_assignment_date":"2018-04-01 20:17:48","rota_role":""},"rota_entry1":{"person":"7","rota_assignment_date":"2018-04-08 20:17:48","rota_role":""},"rota_entry2":{"person":"11","rota_assignment_date":"2018-04-15 20:17:48","rota_role":""},"rota_entry3":{"person":"7","rota_assignment_date":"2018-04-22 20:17:48","rota_role":""},"rota_entry4":{"person":"10","rota_assignment_date":"2018-04-29 20:17:48","rota_role":""},"rota_entry5":{"person":"3","rota_assignment_date":"2018-05-06 20:18:20","rota_role":""},"rota_entry6":{"person":"11","rota_assignment_date":"2018-05-13 20:18:23","rota_role":""}}
[rota_advance_email_days] =>
[rota_reminder_sent] =>
)
I want to change person 8 to person 9
So I think that I need to get the rota_entry (using foreach) and then use Json_decode to get an array and then something but my brain now hurts :( I and don't know how to reset it back up to put into the database again.
I can find lots that talks about simple JSON decode or simple array parsing but not something to help with this
This code assumes $obj = the first entry in your array you show.
So $obj = Array[0]
$json = json_decode($obj->rota_entry);
$json->rota_entry0->person = 9;
$obj->rota_entry = json_encode($json);
This code changes rota_entry0 person 8 to 9
// Your original array
$array = [
0 => (object) [
'id' => 3,
'rota_name' => 'Tea and coffee',
'rota_owner_name' => 9,
'rota_notes' => '',
'rota_entry' =>' {"rota_entry0":{"person":"8","rota_assignment_date":"2018-04-01 20:17:48","rota_role":""},"rota_entry1":{"person":"7","rota_assignment_date":"2018-04-08 20:17:48","rota_role":""},"rota_entry2":{"person":"11","rota_assignment_date":"2018-04-15 20:17:48","rota_role":""},"rota_entry3":{"person":"7","rota_assignment_date":"2018-04-22 20:17:48","rota_role":""},"rota_entry4":{"person":"10","rota_assignment_date":"2018-04-29 20:17:48","rota_role":""},"rota_entry5":{"person":"3","rota_assignment_date":"2018-05-06 20:18:20","rota_role":""},"rota_entry6":{"person":"11","rota_assignment_date":"2018-05-13 20:18:23","rota_role":""}}',
'rota_advance_email_days' => '',
'rota_reminder_sent' => '',
]
];
// Create an empty object to replace the rota_entry key in the array
$rotaEntry = (object) [];
// Iterate through the original rota_entry and replace "person"
foreach (json_decode($array[0]->rota_entry) as $key => $value) {
// You can set whatever logic you want here
// For example: if ($key == "rota_entry4") {$value->person = 4;}
// I'm hardcoding "9"
$value->person = 9;
$rotaEntry->$key = $value;
}
// Assign the newly created (and modified) rotaEntry back to the original array
$array[0]->rota_entry = $rotaEntry;
Try this:
$array = (array) $object;
foreach($array as &$value){
$json = json_encode($value['rota_entry']);
$json -> rota_entry0 -> person = 9;
$value['rota_entry'] = json_encode($json);
}
$array = (object) $array;
good luck.
I have two objects like this.
$array1
stdClass Object (
[BellId] => 2
[BellCode] => BP001
[BellDescription] => SPI SPEED ABNORMAL,CHK BELT
[ControllerId] => 3
[CreatedBy] => 1
[CreatedOn] => 2016-08-19 15:09:25
[ModifiedBy] =>
[ModifiedOn] =>
)
$array2
stdClass Object (
[BellId] => 1
[BellCode] => BP002
[BellDescription] => MCB TRIPPED,CHK MTR SHORT,O/L.
[ControllerId] => 3
[CreatedBy] => 1
[CreatedOn] => 2016-08-19 15:09:25
[ModifiedBy] =>
[ModifiedOn] =>
)
I need to compare this object and get the difference in these two objects only.
I have checked the below links but no use.
Comparing two stdClass Objects
Comparing 2 objects PHP
My Sample code is as follows
function recursive_array_diff($a1, $a2) {
$r = array();
foreach ($a1 as $k => $v) {
if (array_key_exists($k, $a2)) {
if (is_array($v)) {
$rad = recursive_array_diff($v, $a2[$k]);
if (count($rad)) {
$r[$k] = $rad;
}
} else {
if ($v != $a2[$k]) {
$r[$k] = $v;
}
}
} else {
$r[$k] = $v;
}
}
return $r;
}
Can someone help me with the code.
Use array_diff_assoc(); e.g:
<?php
$foo = new stdClass();
$foo->BellId = 1;
$foo->BellDescription = 'foo';
$foo->CreatedBy = 1;
$bar = new stdClass();
$bar->BellId = 2;
$bar->BellDescription = 'bar';
$bar->CreatedBy = 1;
$diff = array_diff_assoc((array) $foo, (array) $bar);
print_r($diff);
array_diff_assoc performs a diff of arrays with additional index check. In your case this is required because you want to perform a key/value diff, not a diff on the values alone.
The above code yields:
Array
(
[BellId] => 1
[BellDescription] => foo
)
Note: you can transparently cast an instance of stdClass() to an array and vice versa:
$arr = ['id' => 1];
$obj = (object) $arr;
$arr = (array) $obj;
// etc.
Hope this helps :)
First convert both objects to arrays:
$arrayA = (array)$objectA;
$arrayB = (array)$objectB;
then just use array_diff to get the difference between arrays
$difference = array_diff($arrayA, $arrayB);
This will return an array containing keys from the first array ($arrayA) and their value, which gives an indication as to what fields are different between the two objects
foreach($difference as $key => $diff) {
echo $objectA->$key;
echo $objectB->$key;
// the above two values will be different
}
Note: array_diff can be used if you know the order of fields in both objects are the same, however it's probably best to use array_diff_assoc here, as this offers an additional index check.
With array_udiff_assoc you can compare the items as you like using a callback. Of course, you need to cast the objects to arrays:
$d = array_udiff_assoc((array)$array1, (array)$array2, function ($x, $y) {
if (! (is_scalar($x) && is_scalar($y))) {
trigger_error("skipping non-scalar members!", E_USER_WARNING);
// you might want to handle this in the app-specific way
}
if (is_numeric($x) && is_numeric($y))
return $x - $y;
return strcmp($x, $y);
});
var_dump($d);
where $x and $y are the items from the arrays being compared.
Sample output
array(3) {
["BellId"]=>
int(2)
["BellCode"]=>
string(5) "BP001"
["BellDescription"]=>
string(27) "SPI SPEED ABNORMAL,CHK BELT"
}
This is a very flexible way. You can put your own comparison logic into the callback. For instance, you might want to compare instances of classes:
static $date_fmt = 'YmdHis';
if ($x instanceof DateTime)
$x = $x->format($date_fmt);
if ($y instanceof DateTime)
$y = $y->format($date_fmt);
My code:
Scenario 1
$newarray = array();
foreach($rows as $k => $v)
{
$newarray[$k] = $v;
$newarray['newitem'] = 'Add this to existing object';
}
echo json_encode($newarray);
I tried this as well:
Scenario 2
$newarray = array();
foreach($rows as $k => $v)
{
$newarray[$k] = $v;
$newarray[$k]['newitem'] = 'Add this to existing object';
}
echo json_encode($newarray);
In scenario 1, key value pair gets added for after 1st object only. It doesn't loop through.
In scenario 2, i get HTTP ERROR 500
What am I doing wrong here ?
Update:
Existing Output:
[{"pid":"123","date":"2016-08-23 08:08:40","post_title":"AHHH"},{"pid":"223","date":"2016-08-23 08:08:40","post_title":"wwwwAHHH"},{"pid":"223","date":"2016-08-23 08:08:40","post_title":"xxxAHHH"}]
Required Output:
[{"pid":"123","date":"2016-08-23 08:08:40","post_title":"AHHH","newkey":"new value1"},{"pid":"223","date":"2016-08-23 08:08:40","post_title":"wwwwAHHH","newkey":"new value2"},{"pid":"223","date":"2016-08-23 08:08:40","post_title":"xxxAHHH","newkey":"new value3"}]
You can make an temporary array containing what you want and then add it to the newarray, so create a temporary array, add the new data and the existing data, then add that to your newarray
$newarray = array();
foreach($rows as $k => $v)
{
$t = $v;
$t['newitem'] = 'Add this to existing object';
$newarray[$k] = $t;
}
echo json_encode($newarray);
EDIT:
Now I know what is in $rows is a JSON String this is the way to do what you want. You cannot foreach over a JSON string, you must first convert it to a PHP data structure, then foreach over it adding the new data, then convert it back to a JSON String
$rows = '[{"pid":"123","date":"2016-08-23 08:08:40","post_title":"AHHH"},{"pid":"223","date":"2016-08-23 08:08:40","post_title":"wwwwAHHH"},{"pid":"223","date":"2016-08-23 08:08:40","post_title":"xxxAHHH"}]';
$objArray = json_decode($rows);
$newarray = array();
foreach($objArray as $v)
{
$v->newitem = 'Add this to existing object';
$newarray[] = $v;
}
print_r($newarray);
echo json_encode($newarray);
The $newArray is now
Array
(
[0] => stdClass Object
(
[pid] => 123
[date] => 2016-08-23 08:08:40
[post_title] => AHHH
[newitem] => Add this to existing object
)
[1] => stdClass Object
(
[pid] => 223
[date] => 2016-08-23 08:08:40
[post_title] => wwwwAHHH
[newitem] => Add this to existing object
)
[2] => stdClass Object
(
[pid] => 223
[date] => 2016-08-23 08:08:40
[post_title] => xxxAHHH
[newitem] => Add this to existing object
)
)
And the new JSON String will be
[ {"pid":"123",
"date":"2016-08-23 08:08:40",
"post_title":"AHHH",
"newitem":"Add this to existing object"
},
{"pid":"223",
"date":"2016-08-\u200c\u200b23 08:08:40",
"post_title":"wwwwAHHH",
"newitem":"Add this to existing object"
},
{"pid":"223",
"date":"2016\u200c\u200b-08-23 08:08:40",
"post_title":"xxxAHHH",
"newitem":"Add this to existing object"
}
]
In Scenario 1, you are assigning a different value, $v, to the same array key each time so it is just being overridden (and would eventually end up being the value of the last row passed to the foreach):
$newarray['newitem'] = 'Add this to existing object';
Scenario 2 is on the right lines but needs changing slightly. In Scenario 2 you are adding an array containing $v but then overriding it straight away:
$newarray[$k] = $v;
$newarray[$k]['newitem'] = 'Add this to existing object';
You need to give the items separate keys so a solution could be to use an 'olditem' key to assign the initial value to:
$newarray[$k]['olditem'] = $v;
$newarray[$k]['newitem'] = 'Add this to existing object';
There are many solutions you could do depending on how you want the array to be structured but the key principle is to ensure the values have separate keys so they do not override each other.
I have keys and values as follows :
[store_id] => 1
[store_name] => StarShop
[store_phone] => 62-22-8383838
[store_email] => admin#starshop.com
I use this to make objects with $this from the above arrays
foreach($above_array as $key => $value){
$this->$key = $value;
}
echo $this->store_name; // this does not work, please help
You cannot use $this->, if you are not inside a class:
<?php
$a = array(
'store_id' => 1,
'store_name' => 'StarShop',
'store_phone' => '62-22-8383838',
'store_email] => admin#starshop.com',
);
$obj = new StdClass; // create new object
foreach($a as $key => $value){ // iterate as before
$obj->$key = $value; // add properties to object
}
echo $obj->store_name;
?>
Demo
In PHP, you can initialize arrays with values quickly using the following notation:
$array = array("name" => "member 1", array("name" => "member 1.1") ) ....
is there any way to do this for STDClass objects?
I don't know any shorter way than the dreary
$object = new STDClass();
$object->member1 = "hello, I'm 1";
$object->member1->member1 = "hello, I'm 1.1";
$object->member2 = "hello, I'm 2";
You can use type casting:
$object = (object) array("name" => "member 1", array("name" => "member 1.1") );
I also up-voted Gumbo as the preferred solution but what he suggested is not exactly what was asked, which may lead to some confusion as to why member1o looks more like a member1a.
To ensure this is clear now, the two ways (now 3 ways since 5.4) to produce the same stdClass in php.
As per the question's long or manual approach:
$object = new stdClass;
$object->member1 = "hello, I'm 1";
$object->member1o = new stdClass;
$object->member1o->member1 = "hello, I'm 1o.1";
$object->member2 = "hello, I'm 2";
The shorter or single line version (expanded here for clarity) to cast an object from an array, ala Gumbo's suggestion.
$object = (object)array(
'member1' => "hello, I'm 1",
'member1o' => (object)array(
'member1' => "hello, I'm 1o.1",
),
'member2' => "hello, I'm 2",
);
PHP 5.4+ Shortened array declaration style
$object = (object)[
'member1' => "hello, I'm 1",
'member1o' => (object)['member1' => "hello, I'm 1o.1"],
'member2' => "hello, I'm 2",
];
Will both produce exactly the same result:
stdClass Object
(
[member1] => hello, I'm 1
[member1o] => stdClass Object
(
[member1] => hello, I'm 1o.1
)
[member2] => hello, I'm 2
)
nJoy!
From a (post) showing both type casting and using a recursive function to convert single and multi-dimensional arrays to a standard object:
<?php
function arrayToObject($array) {
if (!is_array($array)) {
return $array;
}
$object = new stdClass();
if (is_array($array) && count($array) > 0) {
foreach ($array as $name=>$value) {
$name = strtolower(trim($name));
if (!empty($name)) {
$object->$name = arrayToObject($value);
}
}
return $object;
}
else {
return FALSE;
}
}
Essentially you construct a function that accepts an $array and iterates over all its keys and values. It assigns the values to class properties using the keys.
If a value is an array, you call the function again (recursively), and assign its output as the value.
The example function above does exactly that; however, the logic is probably ordered a bit differently than you'd naturally think about the process.
You can use :
$object = (object)[]; // shorter version of (object)array();
$object->foo = 'bar';
I use a class I name Dict:
class Dict {
public function __construct($values = array()) {
foreach($values as $k => $v) {
$this->{$k} = $v;
}
}
}
It also has functions for merging with other objects and arrays, but that's kinda out of the scope of this question.
You could try:
function initStdClass($thing) {
if (is_array($thing)) {
return (object) array_map(__FUNCTION__, $thing);
}
return $thing;
}
from this answer to a similar question:
As of PHP7, we have Anonymous Classes which would allow you to extend a class at runtime, including setting of additional properties:
$a = new class() extends MyObject {
public $property1 = 1;
public $property2 = 2;
};
echo $a->property1; // prints 1
It's not as succinct as the initializer for array. Not sure if I'd use it. But it is another option you can consider.
Another option for deep conversion is to use json_encode + json_decode (it decodes to stdClass by default). This way you won't have to repeat (object) cast in each nested object.
$object = json_decode(json_encode(array(
'member1' => "hello, I'm 1",
'member1o' => array(
'member1' => "hello, I'm 1o.1",
),
'member2' => "hello, I'm 2",
)));
output:
php > print_r($object);
stdClass Object
(
[member1] => hello, I'm 1
[member1o] => stdClass Object
(
[member1] => hello, I'm 1o.1
)
[member2] => hello, I'm 2
)