$arr = array(array(array()));
foreach($arr as $subarr)
{
$subarr[] = 1;
}
var_dump($arr);
Output:
array(1) {
[0]=>
array(1) {
[0]=>
array(0) {
}
}
}
But for object,it's reference:
class testclass {
}
$arr = array(new testclass());
foreach($arr as $subarr)
{
$subarr->new = 1;
}
var_dump($arr);
Output:
array(1) {
[0]=>
object(testclass)#1 (1) {
["new"]=>
int(1)
}
}
Why treat array different from object?
PHP passes all objects by reference. (PHP5?)
PHP passes all arrays by value.
Originally PHP passed both objects and arrays by value, but in order to cut down on the number of objects created, they switch objects to automatically pass by reference.
There is not really a logical reason why PHP does not pass arrays by reference, but that is just how the language works. If you need to it is possible to iterate over arrays by value but you have to declare the value explicitly by-reference:
foreach ( $myArray as &$val ){
$val = 1; //updates the element in $myArray
}
Thanks to Yacoby for the example.
Frankly I prefer arrays to be passed by value because arrays are a type of basic data structure, while objects are more sophisticated data structures. The current system makes sense, at least to me.
Foreach copies the iterated array because it allows you to modify the original array while inside the foreach, and it's easier to use that way. Wouldn't it be the case, your first example would blow up. There is no way to keep PHP from copying the array inside a foreach. Even when using the pass-item-by-reference syntax foreach($foo as &$bar), you'll still work on a copy of the array, one that contains references instead of actual values.
Objects, on the other hand, are expected from most object-oriented developers to always be passed by reference. This became the case in PHP 5. And when you copy an array that contains objects, you actually copy the references to objects; so even though you're working on an copy of the array, you're working on the same objects.
If you want to copy an object, use the clone operator. As far as I know, there is no way to have certain objects always be passed by value.
$foo = new Foo;
$bar = clone $foo;
Why would array and object be treated the same?
PHP simply passes objects by reference (->), and passes all arrays by value.
If all objects were passed by value, the script would make many copies of the same class, thereby using more memory.
Related
As I understand, when you pass a variable to a function, and if you don't use reference sign (&) , it means any changes inside your function will not affect to your variable outside the function. In other words, it means the compiler will make a copy of the outside variable to use inside function, doesn't it?
But when I run these testing code, it does not happen like that.
Can anyone explain me what I miss here? Thank you
My test code: the expected result should be 3, but it becomes 1?
function test($arr2) {
foreach($arr2 as &$item) {
$item = 1;
}
}
$arr = array(2);
foreach($arr as &$item2) {
$item2 = 3;
}
test($arr);
print_r($arr);
This issue has been solved a few times before you've asked this (#1). The issue is due to the fact that:
Reference of a $value and the last array element remain even after the
foreach loop. It is recommended to destroy it by unset().
Reference: PHP foreach()
You need to unset the last $item2 after your foreach:
foreach ($arr as &$item2) {
$item2 = 3;
}
unset($item2);
This is quite interesting, it seems like the behavior of array are the same as objects in php in which new array still holds the copy of the members identifier (which points to the same value as the array it was copied from).
As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.
PHP Manual - Objects and references
Even though you are not passing $arr as Reference variable, you are still accessing $arr elements as References in function test(). So anything that changes in function will effect outside function too.
If you are looking to change $arr ( which has been passed as $arr2 in test function ) only in test function, then remove &from $item
I've noticed something quite strange with PHP Objects and can't find a documented cause of it.
The following code demonstrates the behaviour
<?php
$a = (object) array( 0 => 1 );
foreach($a as $b => $c) {
$a->$b = ++$c; //I'm expecting the key to be overwritten here
}
var_dump($a);
$var = 0;
var_dump($a->$var);
$var = "0";
var_dump($a->$var);
and the output
object(stdClass)#1 (2) {
[0]=>
int(1)
["0"]=>
int(2)
}
int(2)
int(2)
Is the numeric part of the class inaccessible using -> syntax?
When you perform an (object) cast on an array you promote that array as the internal property list of an anonymous object (i.e. stdClass).
The way properties are indexed in an object is slightly different than that of an array; specifically, object property names are always treated as strings whereas array indices are looked up based on the intended type (e.g. numeric strings are seen as integers).
The above behaviour doesn't affect foreach loops because there's no hashing involved there; as far as PHP is concerned, a regular array is being iterated.
To answer your question, yes, the numeric keys from your original array can't be accessed using the -> operator. To avoid this you should remove the numeric indices from your array before the cast is performed.
It's hard to find this behaviour in the documentation, but a hint of it can be found here:
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 ...
FYI
In this particular case you can circumvent the issue by using references; this is not recommended, please follow the earlier advise of not using numeric property names:
foreach ($a as &$c) {
++$c;
}
unset($c);
Update
2014-11-26: I've updated the documentation; the live pages will be updated this Friday - commit.
stdClass handles data terribly loosely, since it's an object representation of an internal array (thus the ability of of casting without problems).
$stdClassObject->property = "value";
The property is handled as a string, but upon casting, the property type doesn't change (which is somehow understandable, as if you cast to an object and then to an array again, you'd have lost all the integer indexes).
I don't think they could do better than that, but you can create your own alternative to stdClass :-)
I'm a newbie in the PHP area, so please bear with my question.
Basically I have/will have a pretty big json file, and I need to query the file to get a single entry based on the key provided. An example would be as follow:
{
"key1" : {value1},
"key2" : {value2},
...,
"keyn" : {valuen}
}
I will need to retrieve only one value at any one request, and hope to get a better performance.
The basic way to deal with this sort of handling in PHP from my search is to use json_decode() and then foreach.
However, this approach seems like need to iterate through the whole file based on the order of the key and what the key I am looking for. So if I am looking for keyn, then essentially I have to read from top to bottom of the large file. (Yep, I can use some sort algorithm to get a better result)
But from my understanding, JSON is basically another form of HashMap, so given HashMap can get easily and fast, is there a similar way in PhP to get the best performance out of it?
Well, given the structure you provided you definitely don't need to loop through the entire object.
If you're looking for keyn, you would just do:
$obj = json_decode($input);
echo $obj->keyn;
Maybe I'm missing something obvious. If you want to prevent having to json_decode the entire object, your question makes a bit more sense though... but that's not what you're asking.
From JSON.org
JSON is built on two structures:
A collection of name/value pairs. In various languages, this is realized as an object, >record, struct, dictionary, hash table, keyed list, or associative array.
An ordered list of values. In most languages, this is realized as an array, vector, list, or >sequence.
You can't just interact with json without first using json_decode() to turn it into a usable object. But if you know the keys, after running json_decode() you can interact with it (because it's now an object). for example:
<?php
$string = '{"foo": "bar", "cool": "attr"}';
$result = json_decode($string);
// Result: object(stdClass)#1 (2) { ["foo"]=> string(3) "bar" ["cool"]=> string(4) "attr" }
var_dump($result);
// Prints "bar"
echo $result->foo;
// Prints "attr"
echo $result->cool;
?>
In situations like this, var_dump() and print_r() are your friends.
There really isn't any magical way to find the value without using any kind of loop
I haven't benchmarked this:
This is how I would approach the problem without having to finish the iterating over the whole tree if and when a match is foudn
$key = 'keyn'
$obj = json_decode(file_get_contents('path/to/your/file'), true);
$len = count($obj);
$match = false;
for($ii = 0; $ii < $len; $ii++){
$curr = $obj[$ii];
if($curr == $key) {
$match = $curr;
}
break;
}
In PHP you can use function: file_get_contents to parse JSON file. You have to go through each and every key-value pairs.
I have a PHP class called "book": I've made various instances of it and placed them in an array called $books. I know this is not the best approach in PHP but I've learned OOP in JavaScript and that's how I usually do this.
var_dump of $books will produce
array(2) { [0]=> object(book)#3 (6) { // some properties }
[1]=> object(book)#4 (6) { // some properties } }
I've always been able to iterate in array of objects with foreach($books as $book) but this time I get the error:
Cannot use object of type book as array
Var dump says it is an array: error reporting says is an object. Why is this?
$books is created with $books = array(); and objects inside of it are added with array_push(). $books IS an array and not an object: is PHP crazy?
Your issue looks as you are trying to iterate a Book object instead of an array of Book objects, check well your array and vars; probably your are assigning an object to your array var instead append it to your array
Are you sure $books is an array? It looks like you're trying to iterate over a book object.
There is no $books.length notation in PHP. If anything it should be $books->length but since $books is an array it's count($books). Arrays are not Objects in PHP, unlike JavaScript.
I just read this answer by Bill Karwin.
Note that the $node is pass by reference.
I always thought that variable created there is only ever temporary and exists only until the end of the loop (or maybe it remains set to the last iteration - I have not tested it).
So, what are the advantages of making it pass by reference?
I'm sure there is one, but I can't figure it out yet.
It has nothing to do with being faster or more efficient.
PHP implements copy-on-write so a variable may refer to the same area of memory until you change its value. Then when you change the variable, PHP decides whether or not to create a copy depending on whether you're accessing it by value or by reference.
Access by value -- changes to a variable creates a copy:
$a = array("abc");
foreach ($a as $element) {
$element = "def";
}
print_r($a);
Array
(
[0] => abc
)
Access by reference -- changes to a variable affect the original copy:
foreach ($a as &$element) {
$element = "def";
}
print_r($a);
Array
(
[0] => def
)
Note that all objects in PHP 5 are accessed by reference without using the & operator.
This is a good reason to use objects!
Be very careful using references in PHP, because they can have confusing effects.
Be sure to read this blog: Do not use PHP references by PHP core developer Johannes Schlüter
The advantage is that it lets you manipulate the array directly.
foreach ($array as $key => $node) {
$array[$key]['foo'] = 'bar';
}
can be made shorter and more efficient like this:
foreach ($array as &$node) {
$node['foo'] = 'bar';
}
In this case $node is not a copy of the contents of the array entry, but a reference to the actual array entry; whatever you do to it, you do to the array.