PHP Array to Object - php

Given the following array:
$array = array(
'item_1' => array(
'item_1_1' => array(
'item_1_1_1' => 'Hello',
),
'item_1_2' => 'World',
),
'item_2' => array(),
);
How can I convert that into an Object?
Option 1
$obj = (object) $array;
Or
Option 2
$object = json_decode(json_encode($array), FALSE);
Or something else?
I would like to know the difference in the output between the 2 option and understand the best practice for creating this conversion.

Well you are answering somehow your own question, but if you want to have an object with the attributes like your array you have to cast it, this way an array will remain an array
$obj = (object) $array;
OUTPUT:
object(stdClass)#1 (2) {
["item_1"]=>
array(2) {
["item_1_1"]=>
array(1) {
["item_1_1_1"]=>
string(5) "Hello"
}
["item_1_2"]=>
string(5) "World"
}
["item_2"]=>
array(0) {
}
}
if you are using the json_decode version it will convert arrays to objects too:
object(stdClass)#2 (2) {
["item_1"]=>
object(stdClass)#3 (2) {
["item_1_1"]=>
object(stdClass)#4 (1) {
["item_1_1_1"]=>
string(5) "Hello"
}
["item_1_2"]=>
string(5) "World"
}
["item_2"]=>
array(0) {
}
}
NOTE: just the empty array will be an array here.
To Answer your question: The best practice depends on what YOU need.

It depends, really: if you are working on data that might be an array in one case, and an object the next, it would probably be best to use the json_decode trick, simply because unlike a cast, its result is "recursive". There is one very important thing to keep in mind here, though: numeric indexes can, and probably will cause problems for you at some point in time. Take a look at this bug report
This is documented here, but not in a way that really stands out:
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;
Exampe of the problem:
$data = [
'foo' => 'bar',
123 => 'all is well',
];
$obj = json_decode(json_encode($data));
var_dump($obj->foo);//bar
var_dump($obj->{123});//all is well
$cast = (array) $obj;
var_dump($cast);//shows both keys
var_dump(isset($cast[123]));//FALSE!!!
var_dump(isset($cast['123']));//FALSE
Basically: If you start converting arrays to objects and back again, numeric keys are not reliable anymore. If I were you, I'd simply change the code that is passing the data where possible, or I'd create a value object that can be set using an array or an object, and normalize the data that way.

Related

PHP array cannot get value from key

I got an object that has some private properties that i cannot access.
var_dump($roomType);
// I deleted some of the results of var_dump
object(MPHB\Entities\RoomType)#2003 (6) {
["id":"MPHB\Entities\RoomType":private]=> int(15)
["originalId":"MPHB\Entities\RoomType":private]=> int(15)
["description":"MPHB\Entities\RoomType":private]=> string(0) ""
["excerpt":"MPHB\Entities\RoomType":private]=> string(0) ""
["imageId":"MPHB\Entities\RoomType":private]=> int(406)
["status":"MPHB\Entities\RoomType":private]=> string(7) "publish" }
So I convert the object to array.
$array = (array) $roomType;
print_r($array);
/*
Array (
[MPHB\Entities\RoomTypeid] => 15
[MPHB\Entities\RoomTypeoriginalId] => 15
[MPHB\Entities\RoomTypedescription] =>
[MPHB\Entities\RoomTypeexcerpt] =>
[MPHB\Entities\RoomTypeimageId] => 406
[MPHB\Entities\RoomTypestatus] => publish )
*/
but I still cannot access the values from key like this
var_dump($array["MPHB\Entities\RoomTypeimageId"]); // NULL
The only workaround i got is this :
$array = (array) $roomType;
$array_keys = array_keys($array);
$array_key_id = $array_keys[4];
echo $array[$array_key_id]; // 406
But I am not sure that the key is at the same position all the time, so I want to find an other way.
I escaped the slashes but still the same, any ideas?
Edit :
So I tried to compare the $array_key_id (which is MPHB\Entities\RoomTypeimageId) with the same value (copied from the browser) and it fails.
So I did a loop and pushed the key=>value to the existing $array and now I can get the value.
There must be something like null bytes as BacLuc said.
I would guess that escaping is the problem:
$array["MPHB\Entities\RoomTypeimageId"] -> $array["MPHBEntitiesRoomTypeimageId"] for which there is no value in the array.
But $array["MPHB\\Entities\\RoomTypeimageId"] might work.
Edit:
it's escaping plus on private properties have the class name prepended to the property name, surrounded with null bytes.
Test is here: http://sandbox.onlinephpfunctions.com/code/d218d41f22e86dd861f562de9c040febb011d577
From:
Convert a PHP object to an associative array
https://www.php.net/manual/en/language.types.array.php#language.types.array.casting

Does PHP allow to have duplicate properties inside an stdObject?

I'm stuck with a very weird bug. I have an object called $row that looks like this:
stdClass Object
(
[title] => Some Title
[body] => My body
[topic] => Topic
[dataType] => Survey
[csvrownum] => 1
)
I'm just trying to print out the title property in the following way:
print_r($row->title);
However for some reason that doesn't output anything.
Then I've tried to manually set the title property and print it right after, something like this:
$row->title = 'My Title';
print_r($row->title);
Surprisingly it worked but why? To make this more strange I decided to var_dump the object after set the title variable by hand:
$row->title = 'My Title';
var_dump($row);
And this is what I've got:
class stdClass#391 (6) {
public $title =>
string(3) "Some title"
public $body =>
string(7) "My body"
public $topic =>
string(6) "Topic"
public $dataType =>
string(17) "Survey"
public $csvrownum =>
int(1)
public $title =>
string(8) "My title"
}
Notice the title key is duplicated with different values. Is there any condition under this could happen?
No, PHP does not allow an object to have duplicate property names, because objects in PHP are implemented just like arrays. They are both implemented as ordered hashmaps. In a hashmap, two things that have the same hash, overwrite each other.
You likely just have unprintible characters in your object property name. You can see this more clearly by doing something like the following for debug purposes...
foreach($row as $key => $value) {
var_dump($key);
}
If we had an object like this, for example, you'd see it gets overwritten.
$row = new stdClass;
$row->title = "First";
$row->title = "Second";
But something like this might be more deceptive...
$row = new stdClass;
$row->{"title\0"} = "First";
$row->title = "Second";
Output from the foreach using var_dump on the key, would reveal this...
string(6) "title"
string(5) "title"
Notice one is string of length 6 and the other is a string of length 5.
Grain of salt
It's always better to use var_dump when attempting to debug variables than using something like print_r, as var_dump was specifically designed for debug purposes, whereas print_r is just a recursive print (hence the name). Printing values like null, false, or empty strings, gives you no useful information for debug purposes, but var_dump does.

Adding variable for the context on a view, on Laravel 4

Lets say that I have a View that uses 3 variables, while developing on Laravel 4:
<ul>
<li>Name: {{name}}</li>
<li>Surname: {{surname}}</li>
<li>Age: {{age}}</li>
<ul>
In my controller, I could be adding the data to the view the following way.
$context = array('name' => $name, 'surname' => $surname, 'age' => $age);
return View::make('myview');
But this code isnt completely DRY, since I am writing twice the name of the variable. It would be optimal if I could do:
$context = array($name, $surname, $age);
return View::make('myview', $context);
This code seems tighter. But unfortunately, the view will give me an error "Undefined variable: name".
Is there any way to achieve this with Laravel?
UPDATE: Another option, as stated in some answers is using compact.
$context = compact('name', 'surname', 'age')
return View::make('myview', $context);
But the problem here is that you have to create all your context variable in one go. And programming is not always so convenient sadly. Sometimes you will want to be adding your elements to the context, as you go progressing through the function.
For instance:
$context = array($name, $surname, $age);
if ($age > 18){
$adult = "He is adult";
array_push($context, $adult);
}
return View::make('myview', $context);
So as you see, if I could use arrays, it wouldnt be a problem because I could push new elements into the $context with array_push. But I can not use arrays that way, unfortunately. And compact wouldn't allow me to push elements inside the context, would it?
You use compact(), a native PHP function (see docs):
compact — Create array containing variables and their values
For each of these, compact() looks for a variable with that name in the current symbol table and adds it to the output array such that the variable name becomes the key and the contents of the variable become the value for that key. In short, it does the opposite of extract().
Any strings that are not set will simply be skipped.
For example, to create an array with keys of name, surname, age, with their values respectively:
$name = 'foo';
$surname = 'bar';
$age = 99;
$context = compact("name", "surname", "age");
return View::make('myview', $context);
If you var_dump($context) you would get:
array(3) {
["name"] => string(3) "foo"
["surname"] => string(3) "bar"
["age"] => int(99)
}
Which is what you want to pass into View::make().
Update:
To respond the part of question about using array_push(). I don't think you can use array_push() regardless of compact() anyway. The View::make() still requires a two-dimensional array with key => values. Say you have this array:
$context = array(
'name' => 'foo',
'surname' => 'bar',
'age' => 99,
);
Doing an array_push($context, $adult) would give you:
array(4) {
["name"] => string(3) "foo"
["surname"] => string(3) "bar"
["age"] => int(99)
[0] => string(11) "He is adult"
}
You get [0] => string(11) "He is adult" instead of ["adult"] => string(11) "He is adult". You would not get your expected result in your view using array_push()
To answer "Sometimes you will want to be adding your elements to the context, as you go progressing through the function.", I say there are two ways:
1st approach: Append $context as you go. Similar to your example:
$context = compact("name", "surname", "age");
if ($age > 18) {
$context['adult'] = "He is adult"; // You need to do this way because array_push() does not support setting your own key.
}
return View::make('myview', $context);
Pros: No need to worry whether you have added your variable to the compact() yet.
Cons: To know what is being passed to your view, you need to scan your whole method for key/value that you appended to $context.
2nd approach: Prepare all data then compact them into $context only right before sending to the view.
$name = 'foo';
$surname = 'bar';
$age = 99;
if ($age > 18){
$adult = "He is adult";
}
$context = compact("name", "surname", "age", "adult");
return View::make('myview', $context);
Pros: Single point of compiling view context. You can easily see all variables that are compacted and sent to the view.
Cons: Misspellings are harder to detect. compact() can become really long if you are passing a lot of variables.

PHP - Access value from previously-defined key during array initialization

I'm looking to see if it's possible to access the value of a key I previously defined within the same array.
Something like:
$test = array(
'foo' => 1,
'bar' => $test['foo']
);
I know I can always do so after initialization, I am just wondering if it's possible during initialization?
No, $test doesn't exist until the full constructor is evaluated.
For example: http://codepad.viper-7.com/naUprJ
Notice: Undefined variable: test..
array(2) { ["foo"]=> int(1) ["bar"]=> NULL }
It's probably for the best. Imagine of this worked:
$test = array('foo' => $test['foo']); // mwahaha
If you need to do this a lot, you could create a class that takes keys of a particular format that flag to the class constructor that it should be parsed until all relevant keys are evaluated.

Create Multi-Dimensional Array With Algorithm Using Data in Single-Dimensional Array

I have an single-dimensional array of PHP objects. Each object has two attributes, one attribute is the object's unique ID and the other is the unique ID of another object in the array that is its parent. For example:
array(3) {
[0]=>
object(stdClass)#1 (2) {
["ID"]=>
int(1)
["parentID"]=>
int(0)
}
[1]=>
object(stdClass)#2 (2) {
["ID"]=>
int(3)
["parentID"]=>
int(2)
}
[2]=>
object(stdClass)#3 (2) {
["ID"]=>
int(2)
["parentID"]=>
int(1)
}
}
I need to convert this single-dimensional array into a multi-dimensional array. I have taken a few stabs at this but I can't find a way to get it done without having a loop for each level of nesting. The algorithm needs to be able to adapt to hypothetically infinite levels of nesting. I've tried using some recursion techniques but I've never gotten it quite right.
To add a bit of complexity, the objects in the array that I am getting are not always in a sensical order. I tried to replicate this in my example above; you'll notice that the object with the ID of 3 comes in the array before the object with the ID of 2. So their will probably be a sorting algorithm involved as well.
Ideally the example above would turn out something like this:
Array
(
[0] => Array
(
[ID] => 1
[parentID] => 0
[0] => Array
(
[ID] => 2
[parentID] => 1
[0] => Array
(
[ID] => 3
[parentID] => 2
)
)
)
)
Try this algorithm:
// sort objects by parentID
function cmpNodes($a, $b) {
return $a->parentID - $b->parentID;
}
usort($objects, 'cmpNodes');
// define first node as root of tree
$tree = (array) array_shift($objects);
// lookup table for direct jumps
$idTable = array($tree['ID'] => &$tree);
foreach ($objects as $object) {
$node = (array) $object;
// test if parent node exists
if (!isset($idTable[$node['parentID']])) {
// Error: parent node does not exist
break;
}
// append new node to the parent node
$idTable[$node['parentID']][] = $node;
// set a reference in the lookup table to the new node
$idTable[$node['ID']] = &$idTable[$node['parentID']][count($idTable[$node['parentID']])-3];
}
// unset($idTable);
var_dump($tree);
I used a lookup table ($idtable) for the IDs to jump directly to the nodes.
So, just as a precursor - I do not know php, really at all. I am primarily a c-style language developer (aka c, Objective c and Java). So some of this may be harder to do in php, but here is the attempt I would make:
//the original input array
oldArray;
//the output array
array[] newArray = new array[];
foreach (element : oldArray) {
//if the element is at the top, put it at the top of the array
if (element.parentId == 0) {
newArray.add(element);
} else {
//otherwise, find it's parent and put it in the child array of the parent
for (potentialParent : oldArray) {
if (potentialParent.id = element.parentId) {
potentialParent.array.add(element);
break;
}
}
}
}
A couple of notes: I am assuming you are passing everything around with pointers. If you are making copies of the objects, it is harder, but not impossible. I am also assuming you can change the size of the array dynamically. Again, I am not too aware of php - if you cannot do that, then you would need a procedural way to do this behavior. In Java I would use a list type, or just copy the array and reset it again.
The key to this algorithm working is that the children are placed under their parent - wherever they are - in one pass. This means that, no matter the order, the hierarchy will be created in that single pass. If you need the wrapper array around the parent that you show in your example, you can simply add something like this to the end of the code:
finalArray = new array[];
finalArray[0] = newArray;
Hope this helps.

Categories