So I have been searching for a while and cannot find the answer to a simple question. Is it possible to have an array of objects in PHP? Such as:
$ar=array();
$ar[]=$Obj1
$ar[]=$obj2
For some reason I have not been able to find the answer anywhere. I assume it is possible but I just need to make sure.
The best place to find answers to general (and somewhat easy questions) such as this is to read up on PHP docs. Specifically in your case you can read more on objects. You can store stdObject and instantiated objects within an array. In fact, there is a process known as 'hydration' which populates the member variables of an object with values from a database row, then the object is stored in an array (possibly with other objects) and returned to the calling code for access.
-- Edit --
class Car
{
public $color;
public $type;
}
$myCar = new Car();
$myCar->color = 'red';
$myCar->type = 'sedan';
$yourCar = new Car();
$yourCar->color = 'blue';
$yourCar->type = 'suv';
$cars = array($myCar, $yourCar);
foreach ($cars as $car) {
echo 'This car is a ' . $car->color . ' ' . $car->type . "\n";
}
Yes.
$array[] = new stdClass;
$array[] = new stdClass;
print_r($array);
Results in:
Array
(
[0] => stdClass Object
(
)
[1] => stdClass Object
(
)
)
Yes, its possible to have array of objects in PHP.
class MyObject {
private $property;
public function __construct($property) {
$this->Property = $property;
}
}
$ListOfObjects[] = new myObject(1);
$ListOfObjects[] = new myObject(2);
$ListOfObjects[] = new myObject(3);
$ListOfObjects[] = new myObject(4);
print "<pre>";
print_r($ListOfObjects);
print "</pre>";
You can do something like this:
$posts = array(
(object) [
'title' => 'title 1',
'color' => 'green'
],
(object) [
'title' => 'title 2',
'color' => 'yellow'
],
(object) [
'title' => 'title 3',
'color' => 'red'
]
);
Result:
var_dump($posts);
array(3) {
[0]=>
object(stdClass)#1 (2) {
["title"]=>
string(7) "title 1"
["color"]=>
string(5) "green"
}
[1]=>
object(stdClass)#2 (2) {
["title"]=>
string(7) "title 2"
["color"]=>
string(6) "yellow"
}
[2]=>
object(stdClass)#3 (2) {
["title"]=>
string(7) "title 3"
["color"]=>
string(3) "red"
}
}
Arrays can hold pointers so when I want an array of objects I do that.
$a = array();
$o = new Whatever_Class();
$a[] = &$o;
print_r($a);
This will show that the object is referenced and accessible through the array.
Another intuitive solution could be:
class Post
{
public $title;
public $date;
}
$posts = array();
$posts[0] = new Post();
$posts[0]->title = 'post sample 1';
$posts[0]->date = '1/1/2021';
$posts[1] = new Post();
$posts[1]->title = 'post sample 2';
$posts[1]->date = '2/2/2021';
foreach ($posts as $post) {
echo 'Post Title:' . $post->title . ' Post Date:' . $post->date . "\n";
}
Although all the answers given are correct, in fact they do not completely answer the question which was about using the [] construct and more generally filling the array with objects.
A more relevant answer can be found in
how to build arrays of objects in PHP without specifying an index number?
which clearly shows how to solve the problem.
Related
I'm trying to create the follwing object in PHP:
$obj= {abc#gmail.com:[usr:130,fname:'Bob',lname:'thekid',news:0,wres:1,SWAGLeaders:0]}
ultimately $obj will have a number of email addresses each with its own array.
Here's what I have so far:
$obj = new stdClass();
$obj->{$user[0]['email']}=[];
where $user[0]['email] contains the email address.
my problem is I don't know how to add the elements to the array
You are on the right path if you really need an object.
$user[0]['email'] = 'test';
$obj = new stdClass();
$obj->{$user[0]['email']} = ['usr' => 130, 'fname' => 'Bob', 'lname' => 'thekid', 'news' => 0, 'wres' => 1, 'SWAGLeaders' => 0];
echo json_encode($obj);
Here's the output.
http://sandbox.onlinephpfunctions.com/code/035266a29425193251b74f0757bdd0a3580a31bf
But, I personally don't see a need for an object, I'd go with an array with a bit simpler syntax.
$user[0]['email'] = 'test';
$obj[$user[0]['email']] = ['usr' => 130, 'fname' => 'Bob', 'lname' => 'thekid', 'news' => 0, 'wres' => 1, 'SWAGLeaders' => 0];
echo json_encode($obj);
http://sandbox.onlinephpfunctions.com/code/13c1b5308907588afc8721c1354f113c641f8788
The same way you assigned the array to the object in the first place.
$user[0]['email'] = "abc#gmail.com";
$obj = new stdClass;
$obj->{$user[0]['email']} = [];
$obj->{$user[0]['email']}[] = "Element 1";
$obj->{$user[0]['email']}[] = "Element 2";
$obj->{$user[0]['email']}[] = "Element 3";
var_dump($obj);
object(stdClass)#1 (1) {
["abc#gmail.com"]=>
array(3) {
[0]=>
string(9) "Element 1"
[1]=>
string(9) "Element 2"
[2]=>
string(9) "Element 3"
}
}
related to : Remove duplicates from an array based on object property?
is there a way to do this exact same thing but consider the order? for example if instead of keeping [2] I wanted to keep [1]
[0]=>
object(stdClass)#337 (9) {
["term_id"]=>
string(2) "23"
["name"]=>
string(12) "Assasination"
["slug"]=>
string(12) "assasination"
}
[1]=>
object(stdClass)#44 (9) {
["term_id"]=>
string(2) "14"
["name"]=>
string(16) "Campaign Finance"
["slug"]=>
string(16) "campaign-finance"
}
[2]=>
object(stdClass)#298 (9) {
["term_id"]=>
string(2) "15"
["name"]=>
string(16) "Campaign Finance"
["slug"]=>
string(49) "campaign-finance-good-government-political-reform"
}
I tried posting a comment but because of my reputation it won't let me so I decided to start a new thread
It is trivial to reverse and array. So before processing the array, call array_reverse() on it:
/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);
Then at the end you can reverse it again if ordering is an issue:
/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);
So put all together looks like this:
class my_obj
{
public $term_id;
public $name;
public $slug;
public function __construct($i, $n, $s)
{
$this->term_id = $i;
$this->name = $n;
$this->slug = $s;
}
}
$objA = new my_obj(23, "Assasination", "assasination");
$objB = new my_obj(14, "Campaign Finance", "campaign-finance");
$objC = new my_obj(15, "Campaign Finance", "campaign-finance-good-government-political-reform");
$array = array($objA, $objB, $objC);
echo "Original array:\n";
print_r($array);
/** flip it to keep the last one instead of the first one **/
$array = array_reverse($array);
/** Answer Code begins here **/
// Build temporary array for array_unique
$tmp = array();
foreach($array as $k => $v)
$tmp[$k] = $v->name;
// Find duplicates in temporary array
$tmp = array_unique($tmp);
// Remove the duplicates from original array
foreach($array as $k => $v) {
if (!array_key_exists($k, $tmp))
unset($array[$k]);
}
/** Answer Code ends here **/
/** flip it back now to get the original order **/
$array = array_reverse($array);
echo "After removing duplicates\n";
echo "<pre>".print_r($array, 1);
produces the following output
Array
(
[0] => my_obj Object
(
[term_id] => 23
[name] => Assasination
[slug] => assasination
)
[1] => my_obj Object
(
[term_id] => 15
[name] => Campaign Finance
[slug] => campaign-finance-good-government-political-reform
)
)
Here is one way to do it. This should keep the first object for each name attribute and remove the rest.
$unique = [];
foreach ($array as $key => $object) {
if (!isset($unique[$object->name])) {
// this will add each name to the $unique array the first time it is encountered
$unique[$object->name] = true;
} else {
// this will remove all subsequent objects with that name attribute
unset($array[$key]);
}
}
I used the object name as a key rather than a value in the $unique array so isset could be used to check for existing names, which should be faster than in_array which would have to be used if the names were added as values.
You can do it with array_reduce:
$names = [];
$withUniqueNames = array_reduce(
[$objA, $objB, $objC],
function ($withUniqueNames, $object) use (&$names) {
if (!in_array($object->name, $names)) {
$names[] = $object->name;
$withUniqueNames[] = $object;
}
return $withUniqueNames;
},
[]
);
Basically, we traverse an array of objects and keep track of used names. If the name was not used yet, we add it to used and add current object to the result.
Here is working demo.
I have a global array in which I am trying to update a value (remain persistent) and then display it.
I get no error but the array never gets updated.
<?php
$anchor = 'bird';
$stuff = array('apple', 'bird', 'frog');
function insert($anchor, $stuff){
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
}
for($x = 0; $x < 2; $x++){
insert($anchor, $stuff);
var_dump($stuff);
}
output:
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(4) "bird"
[2]=>
string(4) "frog"
}
array(3) {
[0]=>
string(5) "apple"
[1]=>
string(4) "bird"
[2]=>
string(4) "frog"
}
expected output:
{'bird' => 674568765}
{'bird' => 986266261}
How do I update an array within a function, so that the changes are reflected globally (persistent)?
Pass the variable $stuff by reference. Note the & in the function parameters.
function insert($anchor, &$stuff){ // note the & mark
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
}
As others have mentioned: by default, PHP function arguments are passed by value, meaning that a value changed inside a function does not change outside of the function.
I suggest returning the new $stuff value from your function:
<?php
$anchor = 'bird';
$stuff = array('apple', 'bird', 'frog');
function insert($anchor, $stuff){
foreach($stuff as $part){
$new_array = array($anchor => rand());
if($part == $anchor){
array_push($stuff, $new_array);
}
}
return $stuff;
}
for($x = 0; $x < 2; $x++){
$stuff=insert($anchor, $stuff);
}
echo"<pre>".print_r($stuff,true)."</pre>";
?>
Array
(
[0] => apple
[1] => bird
[2] => frog
[3] => Array
(
[bird] => 618490127
)
[4] => Array
(
[bird] => 1869073273
)
)
Other solutions propose passing by reference, to which I'm not opposed. But I have definitely run into buggy code in which it wasn't immediately clear that a function was changing a value, and I've been confused by unexpected loop behavior. As a result, I generally prefer arguably more readable/maintainable code that returns a new value.
See When to pass-by-reference in PHP.
Get technical with Sara Golemon.
If you want changes to a variable that you pass to a function to persist after the function ends, pass the variable by reference:
Change:
function insert($anchor, $stuff)
To
function insert($anchor, &$stuff)
I am using an answer from How to get random value out of an array to write a function that returns a random item from the array. I modified it to pass by reference and return a reference.
Unfortunately, it doesn't appear to work. Any modifications to the returned object do not persist. What am I doing wrong?
I'm on PHP 5.4 if that makes a difference (Don't ask).
function &random_value(&$array, $default=null)
{
$k = mt_rand(0, count($array) - 1);
$return = isset($array[$k])? $array[$k]: $default;
return $return;
}
Usage...
$companies = array();
$companies[] = array("name" => "Acme Co", "employees"=> array( "John", "Jane" ));
$companies[] = array("name" => "Inotech", "employees"=> array( "Bill", "Michael" ));
$x = &random_value($companies);
$x["employees"][] = "Donald";
var_dump($companies);
Output...
array(2) {
[0] =>
array(2) {
'name' =>
string(7) "Acme Co"
'employees' =>
array(2) {
[0] =>
string(4) "John"
[1] =>
string(4) "Jane"
}
}
[1] =>
array(2) {
'name' =>
string(7) "Inotech"
'employees' =>
array(2) {
[0] =>
string(4) "Bill"
[1] =>
string(7) "Michael"
}
}
}
I even copied and pasted the examples from the documentation and none of those worked either. They all output null.
The ternary operator creates an implicit copy, which breaks the reference chain. Use an explicit if... else:
function &random_value(&$array, $default=null)
{
$k = mt_rand(0, count($array) - 1);
if (isset($array[$k])) {
return $array[$k];
} else {
return $default;
}
}
As to why, the docs now state:
Note: Please note that the ternary operator is an expression, and that it doesn't evaluate to a variable, but to the result of an expression. This is important to know if you want to return a variable by reference. The statement return $var == 42 ? $a : $b; in a return-by-reference function will therefore not work and a warning is issued in later PHP versions.
See also this bug where the ternary operator actually returns by reference in a foreach context, when it shouldn't.
I'm having trouble wrapping my head around a better function aspect than #bishop as I have just consumed an enormous lunch, however this works:
$x =& $companies[array_rand($companies)];
$x["employees"][] = "Donald";
var_dump($companies);
I am trying to convert the associative array to an array of objects.
$assoc = array (
array(
'prop1'=>'val1',
'prop2'=>'val2',
),
array(
'prop1'=>'val1',
'prop2'=>'val2',
),
)
Here Is the code I have so far:
class Assoc {
public function setObject($assoc) {
$this->assoc[] = new Obj($assoc);
}
}
class Obj {
public function __construct($item) {
foreach ( $item as $property=>$value ) {
$this->{$property} = $value;
}
}
}
$test = New Assoc();
$test->setObject($assoc);
This code will work for a single array but not an array of arrays. If you could help with what I believe to be the loop in the setObject function.
Convert the associative array to an array of objects:
$output = array_map(function($element) {
return (object) $element;
}, $assoc);
Simple enough.
EDIT: If you need to make objects of a specific class:
$output = array_map(function($element) use ($classType) {
return new $classType($element);
}, $assoc);
You can generalize it into just about anything, really.
EDIT for specific object:
To adhere to your existing style as close as possible without messing with array_map voodoo:
class Assoc {
public function setObject($assoc) {
foreach ($assoc as $arr) {
$this->assoc[] = new Obj($arr);
}
}
}
class Obj {
public function __construct($item) {
foreach ( $item as $property=>$value ) {
$this->{$property} = $value;
}
}
}
$test = New Assoc();
$test->setObject($assoc);
Original:
If you just need generic conversion, and not into specific custom objects (not exactly clear in your post?) you can try this:
$new_array = array();
foreach ($assoc as $to_obj)
{
$new_array[] = (object)$to_obj;
}
// Print results
var_dump($new_array);
outputs:
array(2) {
[0]=>
object(stdClass)#1 (2) {
["prop1"]=>
string(4) "val1"
["prop2"]=>
string(4) "val2"
}
[1]=>
object(stdClass)#2 (2) {
["prop1"]=>
string(4) "val1"
["prop2"]=>
string(4) "val2"
}
}
$len = count($assoc);
for($i=0;$i<$len; $i++){
$assoc[$i] = (Object)$assoc[$i];
}
You have an indexed array of associative arrays. If you convert it to json then back to an iterable state with the default behavior of json_decode(), the top level (indexed array) will be cast as array-type while the subarrays will become object-type.
Note that this will conversion will permeate all the way through subsequent levels of data (in case researchers might have deeper data structures). Effectively, indexed arrays remain indexed arrays and associative arrays become objects.
This is such a basic call that I am not sure that creating a wrapper for it is necessary.
Code: (Demo)
$assoc = array (
array(
'prop1'=>'val1',
'prop2'=>'val2',
),
array(
'prop1'=>'val1',
'prop2'=>'val2',
),
);
var_export(
json_decode(json_encode($assoc))
);
Output:
array (
0 =>
(object) array(
'prop1' => 'val1',
'prop2' => 'val2',
),
1 =>
(object) array(
'prop1' => 'val1',
'prop2' => 'val2',
),
)