performance of array cloning - php

We all know that
$a1 = array('foo');
$a2 = $a1;
$a2[0] = 'bar';
// now $a1[0] is foo, and $a2[0] is bar. The array is copied
However, what I remember reading, but cannot confirm by Googling, is that the array is, internally, not copied until it is modified.
$a1 = array('foo');
$a2 = $a1; // <-- this should make a copy
// but $a1 and $a2 point to the same data internally
$a2[0] = 'bar';
// now $a1[0] is foo, and $a2[0] is bar. The array is really copied
I was wondering if this is true. If so, that would be good. It would increase performance when passing around a big array a lot, but only reading from it anyway (after creating it once).

It may be more than you wanted to know, but this article gives a good description of the way variables work in PHP.
In general, you're correct that variables aren't copied until it's absolutely necessary.

I seem to have confirmed it:
<?php
ini_set('memory_limit', '64M');
function ttime($m) {
global $s;
echo $m.': '.(microtime(true) - $s).'<br/>';
$s = microtime(true);
}
function aa($a) {
return $a;
}
$s = microtime(true);
for ($i = 0; $i < 200000; $i++) {
$array[] = $i;
}
ttime('Create');
$array2 = aa($array); // or $array2 = $array
ttime('Copy');
$array2[1238] = 'test';
ttime('Modify');
Gives:
Create: 0.0956180095673
Copy: 7.15255737305E-6
Modify: 0.0480329990387

I believe you are correct here. I will try to find documentation on this... I can't find anything about this, but I am sure that I read it somewhere. Hopefully someone here will have better luck finding the documentation.

Related

PHP - Stop need to create hundreds of variables

I'm writing a script and it seems like a bit of a ballache so I came on SO to ask for a little help making my script more dynamic so I create a better version of what I'm doing. I've read into variable variables but I'm still stuck on how I'd use them.
I'll obviously shorten this down but my current script is:
$a0 = $tags['items'][0]['snippet']['tags'];
$a1 = $tags['items'][1]['snippet']['tags'];
$a2 = $tags['items'][2]['snippet']['tags'];
if (!is_array($a0)) { $a0 = array(); }
if (!is_array($a1)) { $a1 = array(); }
if (!is_array($a2)) { $a2 = array(); }
$a0 = array_map('strtolower', $a0);
$a1 = array_map('strtolower', $a1);
$a2 = array_map('strtolower', $a2);
array_count_values(array_merge($a0,$a1,$a2));
I'm looking for a way to dynamically create the variables (For example using an index in a while loop rather than creating these variables uniquely. This obviously is fine on a small scale, but i've currently done 50 of these for each and it's causing serious time problems. Any help is much appreciated
Treat the whole $tags variable as an array and you can do this, similar to the strtolower array_map you have already:
$tagItems = [];
foreach($tags['items'] as $item) {
if (!$item['snippet']['tags'] || !is_array($item['snippet']['tags'])) {
continue;
}
foreach($item['snippet']['tags'] as $tag) {
$tag = strtolower($tag);
if (!isset($tagItems[$tag])) {
$tagItems[$tag] = 0;
}
$tagItems[$tag]++;
}
}
As #FranzGleichmann says, try not to use variable variables, which are a smell and potential security risk, but instead rethink how you want to approach the problem.
You should be able to produce the same output that you get from array_count_values with a nested foreach loop.
foreach ($tags['items'] as $x) { // loop over the list of items
foreach ($x['snippet']['tags'] as $tag) { // loop over the tags from each item
$tag = strtolower($tag);
if (!isset($counts[$tag])) $counts[$tag] = 0;
$counts[$tag]++; // increment the tag count
}
}
No need to create 100 variables. That would cause a headache. Instead, use a simple loop function.
$b = array();
for ($n=1; $n<=100; $n++) {
$a = $tags['items']["$n"]['snippet']['tags'];
if (!is_array($a)) { $a = array(); }
$a = array_map('strtolower', $a);
array_count_values(array_merge($b,$a));
}
I hope it works! Have a nice coding
I would write this in a comment but i will a long one,
Variable Variable, is simply the value of the original var assigned as a var name, which means:
$my_1st_var = 'im_1st';
//use $$
$$my_1st_var = 'im_2nd'; //that is the same of $im_1st='im_2nd';
//results
echo $my_1st_var; // >>> im_1st
echo $im_1st; // >>> im_2nd
that means i created a new var and called it the value of the 1st var which is im_1st and that makes the variable name is $im_1st
also you can set multiple values as a var name:
$var0 = 'a';
$var1 = 'b';
$var2 = 'c';
$var3 = '3';
//we can do this
${$var0.$var1} = 'new var 1'; //same as: $ab = 'new var 1';
${$var1.$var2.$var3} = 'im the newest'; //same as: $bc3 = 'im the newest';
//set a var value + text
${$var0.'4'.$var1} = 'new?'; //same as: $a4b = 'new?';
also $GOLBALS[]; is some kind of $$
hope it helps you understanding what is hard for you about $$ ;)
Alright so dynamically creating variables is easy is a script language like PHP.
You could make $a an array, and instead of $a0, $a1, ... use $a[$i] where $i goes from 0 to 50 or more.
Or you could use this nice funky syntax: ${'a'.$i}. For example:
$i = 0;
${'a'.$i} = 'foobar';
echo $a0; // will output foobar
However you shouldn't do any of this.
What you should do is think about what you are trying to achieve and come up with a different algorithm that doesn't require dynamically named variables.
In this case, something like this looks like it would do the job:
$result = [];
foreach ( $tags['items'] as $item ) {
if ( is_array($item['snippet']['tags']) ) {
$result = array_merge($result, array_map('strtolower',$item));
}
}
array_count_values($result);
This is obviously not tested and from the top of my head, but I hope you get the idea. (EDIT: or check the other answers with similarly rewritten algorithms)

php - how to sort all elements of array except one

I am looking for elegant way to sort two of three values stored in one array. The third one can be ignored but i dont want to loose it (so unseting is not an option).
Imagine such array:
$r = Array("tree_type" => 1, "tree_height" = 5, "tree_age" = 2);
If tree_height is bigger then it's age i want to swap tree height with tree_age, so tree height is always smaller number then age.
Now I am sorting it like this:
if ( $r['tree_height'] > $r['tree_age'] ) {
$tmp = $r['tree_height'];
$r['tree_height'] = $r['tree_age'];
$r['tree_age'] = $tmp;
}
It works perfectly fine, but i am looking for more elegant way. The best solution would be sth like this:
fname($r, $r['tree_height'], $r['tree_age']);
fname would always swap second argument with third if it's bigger then third, otherwise would do nothing.
Any advice would be appreciated.
Kalreg.
ANSWER:
The shortest answer, without condition is:
$tmp = Array($r['tree_height'], $r['tree_age'])
sort($tmp);
You could just encase the code into a function:
function swap(&$arr){ //NOTE SIGN & meaning passing by reference.
if ($arr['tree_height'] > $arr['tree_age']){
$cache_age = $arr['tree_age'] ;
$arr['tree_age'] = $arr['tree_height'] ;
$arr['tree_height'] = $cache_age ;
}
}
$r = array("tree_type" => 1, "tree_height" => 5, "tree_age" => 2);
swap($r);
var_dump($r);
Another elegant solution: (based on Is there a PHP function for swapping the values of two variables?)
function swap(&$arr){ //NOTE SIGN & meaning passing by reference.
if ($arr['tree_height'] > $arr['tree_age']){
list($arr['tree_height'], $arr['tree_age']) = array($arr['tree_age'], $arr['tree_height']);
}
}
This might work (not tested):
function fname(&$a, &$b) {
if ($a <= $b)
return;
$t = $a;
$a = $b;
$b = $t;
}
fname($r['tree_height'], $r['tree_age']);
EDIT: Maybe You wanted something like this?
function fname(&$array, $name1, $name2) {
if ($array[$name1] <= $array[$name2])
return;
$t = $array[$name1];
$array[$name1] = $array[$name2];
$array[$name2] = $t;
}
fname($r, 'tree_height', 'tree_age');

PHP unreference

Lets say i have this code:
$val = 1;
$arr = Array();
$arr['val'] =& $val;
$val = 2;
echo $arr['val'];
This will print out 2, because $val was passed to $arr by reference.
My question is : if i passed a value to an array by reference, is there a way to remove that reference later on, making it a simple copied value ?
To make it clearer, i would like something like this:
$val = 1;
$arr = Array();
$arr['val'] =& $val;
$arr['val'] = clone $arr['val'];
// Or better yet:
$arr = clone $arr;
$val = 2;
echo $arr['val'];
And this should print out 1 (because the array was cloned, before the referenced variable changed).
Howerver, clone does not work with arrays, it only works with objects.
Any ideas? I really have no clue how to do this. I tried writing a recursive copy function, but that didn't work.
You could unset the index and then reassign by-value instead of by reference.
unset($arr['val']);
$arr['val'] = $val;

References and Arrays in PHP

I'm new to programming in general and references in particular. I want to manipulate individual objects in an array by reference, so that I'm not working on mere copies of the objects I wanted to stick into the array. My question is how to do that.
For example, suppose I have these lines of code:
$obj0 = blah;
$obj1 = blah;
$obj2 = blah;
$myArray = array($obj0, $obj1, $obj2);
When I now access and modify $myArray[1], will this be the same as modifying $obj1? Or would I have to be modifying &$myArray[1] instead?
As it stands, no you will not alter the initial variables.
If you wish to do it by reference, you should put those ampersands when you setup the array like so.
$myArray = array(&$obj0, &$obj1, &$obj2);
Code:
$a = "cat"; $a1 = "cat";
$b = "dog"; $b1 = "dog";
$arrRef = array(&$a, &$b); $arrCopy = array($a, $b);
$arrRef[0] .= "food"; $arrCopy[0] .= "food";
$arrRef[1] .= "house"; $arrCopy[1] .= "house";
echo "a: $a b: $b <br />";
echo "a1: $a1 b1: $b1 <br />";
Output:
a: catfood b: doghouse
a1: cat b1: dog
No, modifying $myArray[1] is not the same as modifying $obj1. However, if you are operating on the object that they both point to, that object will be changed.
In other words:
// Does not affect $obj1, but it does affect $obj1->foo
$myArray[1]->foo = "bar";
// Does not affect $obj1, which continues to point to the same object
$myArray[1] = null;
In PHP5 objects are called "by reference" by default so there is no need to prepend $myArray[1] with '&'.

How to get numeric key of new pushed item in PHP?

$arr[] = $new_item;
Is it possible to get the newly pushed item programmatically?
Note that it's not necessary count($arr)-1:
$arr[1]=2;
$arr[] = $new_item;
In the above case,it's 2
end() do the job , to return the value ,
if its help to you ,
you can use key() after to petch the key.
after i wrote the answer , i see function in this link :
http://www.php.net/manual/en/function.end.php
function endKey($array){
end($array);
return key($array);
}
max(array_keys($array)) should do the trick
The safest way of doing it is:
$newKey = array_push($array, $newItem) - 1;
You can try:
max(array_keys($array,$new_item))
array_keys($array,$new_item) will return all the keys associated with value $new_item, as an array.
Of all these keys we are interested in the one that got added last and will have the max value.
You could use a variable to keep track of the number of items in an array:
$i = 0;
$foo = array();
$foo[++$i] = "hello";
$foo[++$i] = "world";
echo "Elements in array: $i" . PHP_EOL;
echo var_dump($foo);
if it's newly created, you should probably keep a reference to the element. :)
You could use array_reverse, like this:
$arr[] = $new_item;
...
$temp = array_reverse($arr);
$new_item = $temp[0];
Or you could do this:
$arr[] = $new_item;
...
$new_item = array_pop($arr);
$arr[] = $new_item;
If you are using the array as a stack, which it seems like you are, you should avoid mixing in associative keys. This includes setting $arr[$n] where $n > count($arr). Stick to using array_* functions for manipulation, and if you must use indexes only do so if 0 < $n < count($arr). That way, indexes should stay ordered and sequential, and then you can rely on $arr[count($arr)-1] to be correct (if it's not, you have a logic error).

Categories