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 '&'.
Related
I have an array:
$order = array(2,0,1)
I also have three text variables
$a, $b, $c
they are each represented by a number $a is 0, $b is 1 and $c is 2
I want to add the variables to a text variable ($holder) in the order set by the array
so if the array is (2,0,1) then
$holder = $c.$a.$b
When $order changes I want the order in $holder to change to match.
I can make this work but it seems a really rubbish way of doing it so I wondered if anyone had a better way:
$holder ="";
$order = array(2,0,1)
for($x=0;$x<count($holder);$x++){
switch ($order)
case 0:
$holder .= $a;
break;
case 1:
$holder .=$b;
break;
case 2:
$holder .=$c;
break;
}
thanks
Update
Thanks for the replies they really made me think about the best way to achieve the result. My own solution might work but that's not enough the code needs to be good too. I think it's important to produce good code not just okay code so thanks for the help.
Probably not the fastest, but using some of the array_ functions. This inverts the order to make the order values the keys, then uses array_replace() to take the values from the corresponding point in an array of the parameters. Then just implode() the result...
$order = array(2,0,1);
$a="1";
$b="2";
$c="3";
echo implode(array_replace(array_flip($order), [ $a, $b, $c ]));
gives
312
Normal array contains key starting from 0. You can use that concept and assign your text variable to array based on their representation.
Like $a represent 0, so put its value to index 0 of array and so on.
Now you can easily access these values by accessing array index.
$a = 'X';
$b = 'Y';
$c = 'Z';
$arr = [$a, $b, $c];
$order = [2,0,1];
$text = '';
foreach($order as $key){
$text .= $arr[$key];
}
echo $text;
Output:
ZXY
A short PHP 7.4+ solution, using implode, array_map and arrow functions:
$a = 'a';
$b = 'b';
$c = 'c';
$values = [$a, $b, $c];
$order = [2, 0, 1];
$holder = implode(array_map(fn($index) => $values[$index] ?? null, $order)); // "cab"
Demo
Another version, hope to match with your idea.
$order = array(1,0,2);
$var = array(
0 => "a",
1 => "b",
2 => "c"
);
$holder = '';
foreach( $order as $item ){
$holder .= $var[$item];
}
echo $holder;
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)
Option 1:
$foo = array($obj1, $obj2, $obj3);
for ($i = 0; $i < 1; $i++) {
echo $foo[$i]->attribute;
echo $foo[$i]->attribute2;
}
//shows obj1's attribute and attribute2
Option 2:
$foo = array($obj1, $obj2, $obj3);
$first_foo = array_shift($foo); /* now we only have the first index in the new array */
foreach ($first_foo as $object) {
echo $object->attribute;
echo $object->attribute2;
}
//shows obj1's attribute and attribute2
Option 3:
$foo = array($obj1, $obj2, $obj3);
$first_foo = $foo[0] /* now we just have an object, obj1 */
echo $first_foo->attribute;
echo $first_foo->attribute2;
//shows obj1's attribute and attribute2
I used Option 3, but all of these feel kinda lacking... how would you do this? Is the loop in options 1 and 2 worth it if you feel like easily pulling the first two instead of one later on? I.e. pulling the latest news article vs. pulling the latest two etc.
Why so complicated?
Loops, array_shift(), ... it's all not neccessary.
You gave the solution yourself:
$foo[0]->attribute
Another one would be
reset($foo)->attribute
On your edit:
If you want to write the code so it is flexible later, do
$need = 1; // the variable number of elements you need
for($i = 0; $i < $need; $i++)
echo $foo[$i]->attribute;
The two iterations aren't needed in this case. I would just take your option #3 with a little less code.
$foo = array($obj1, $obj2, $obj3);
echo $foo[0]->attribute;
echo $foo[0]->attribute2;
assuming:
$b = [0,1];
$c = [2,3];
let:
$a = [$b,$c];
$first_elem = array_shift($a);
print_r($first_elem);
This is to get more than 1 from array_shift which returns null after array is empty
$foo = array($obj1, $obj2, $obj3);
while($obj = array_shift($foo))
echo $obj->attribute;
your option 2 can be changed to
$foo = array($obj1, $obj2, $obj3);
foreach ($foo as $object) {
echo $object->attribute;
echo $object->attribute2;
}
why don't use use it that way?
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;
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.