periklis Posted January 4, 2008 Share Posted January 4, 2008 Hi everyone, I've been using SPL Iterators for some time and I've run onto this problem: I use nested arrays for tree-like data representations. For example, take this "family tree": $array = array('id' => 1, 'name' => 'john', 2 => array('id' => 2, 'name' => 'michael'), 32 => array('id' => 32, 'name' => 'mary'), 14 => array('id' => 14, 'name' => 'george',75 => array('id' => 5, 'name' => 'chris'),9 => array('id' => 9, 'name' => 'chris'))); I'm trying to unset a specific tree node, using the code below: $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator :: SELF_FIRST); //Create recursive iterator for the array $iterator -> rewind(); //Initialize the iterator while ($iterator -> valid() && $iterator -> key() != 75) { $iterator -> next(); //Advance the iterator until you reach the designated node } $iterator -> offsetUnset('id'); //Unset the 'id' array member Now, if we print the subiterator, the array member with key 'id' is unset: print_r($iterator -> getSubIterator()); //Does not contain the 'id' entry But it is not unset from the array itself: print_r($array); //The array is the same as before, nothing has changed Any ideas why is that happening or what else can I do? Thanx in advance! Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/ Share on other sites More sharing options...
deadimp Posted January 5, 2008 Share Posted January 5, 2008 The only thing I can think of based off of your code is that you're not passing by reference. Your unset function may just be altering a copy of a part of that array, not the array itself. Just look into references on the manual and see if that can help you. Random examples: //Function arguments function by_value($value) { $value=5; } function by_ref(&$value) { $value=5; } $x=12; by_value($x); //$x is 12 by_ref($x); //$x is 5 //Function return global $test='Hello'; function return_value() { global $test; return $test; } function& return_ref() { global $test; return $test; } $x=return_value(); $x='World'; //$x is 'World', $test is still 'Hello' $x=return_ref(); $x='People'; //$x and $test are 'People' Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-430870 Share on other sites More sharing options...
periklis Posted January 8, 2008 Author Share Posted January 8, 2008 Hi and thanks for your reply. Ok, using references seems a good idea, but where would I put the reference? I mean, the iterator has an internal pointer to the array, I don't think I have any ability to interfere with it (especially since I don't use any custom function to traverse the nested arrays). Any help would be appreciated! Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-433393 Share on other sites More sharing options...
aschk Posted January 9, 2008 Share Posted January 9, 2008 Try the following : $array =& array('id' => 1, ... This will set $array as a reference to the array. Now i'm guessing that RecursiveArrayIterator accepts a reference, and hopefully this will resolve your problem. Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-434401 Share on other sites More sharing options...
periklis Posted January 9, 2008 Author Share Posted January 9, 2008 Nope, this doesn't work, it throws a parse error (reasonable enough since you can't do $array = & array() just as you wouldn't do $temp = &1; I guess that even if RecursiveArrayIterator uses a reference for the array, it reinitializes the inner iterator each time it traverses a subarray and does not use references deeper (which seems to explain why it works when unseting values of the "root" array, but not subarrays). Thanks alot anyway, I guess I'll just have to figure out a "counter-intuitive awful-looking impossible-to-maintain" workaround for this... Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-434411 Share on other sites More sharing options...
aschk Posted January 9, 2008 Share Posted January 9, 2008 ok well seeing as you can't create a reference to a constant (1 or an array) although an array isn't a constant, do this instead $array = array(...); $refArray =& $array; then use $refArray in your recursiveArrayIterator Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-434420 Share on other sites More sharing options...
periklis Posted January 9, 2008 Author Share Posted January 9, 2008 Nope, this doesn't work either. But isn't it the same? I mean, what's the difference in using $refArray instead of $array? I think they are the same variable. Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-434422 Share on other sites More sharing options...
periklis Posted January 9, 2008 Author Share Posted January 9, 2008 Well, after many unsuccesful attempts, I decided to go for that countr-intuitive-blah-blah solution, which was to find the subarray ancestors (parents) and use eval() to unset the subarray value. Thank you all for your time! Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-434498 Share on other sites More sharing options...
deadimp Posted January 10, 2008 Share Posted January 10, 2008 Sorry, I was thinking that you had designed the iterator object... I'm not sure how you can change the code so it directly references to your original array variable. What you might want to do is just see if you can get the data from the iterator, something similar to getSubIterator()... Maybe current() or getArrayCopy(). I'm looking at the reference here. Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-435145 Share on other sites More sharing options...
periklis Posted January 10, 2008 Author Share Posted January 10, 2008 Yes, I've read through this reference (along with many other, the best one beeing http://www.phpro.org/tutorials/Introduction-to-SPL.html Last night however, I figured out a way to do it: You have to declare all arrays and suvbarrays as RecursiveArrayIterator objects new RecursiveArrayIterator(array('id' => 1, 'name' => 'john', 2 => new RecursiveArrayIterator(array('id' => 2, 'name' => 'michael')), etc... This way it seems to work, but I can't tell because apache keeps crashing everytime I try to print the array after the offsetUnset() call... I'll look into it more thoroughly and keep you up to date PS: The same problem holds not for just unsetting, but for performing any update on the subarrays data (for example, you have no way to change the 'name' property of a subarray through the iterator) Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-435396 Share on other sites More sharing options...
periklis Posted January 10, 2008 Author Share Posted January 10, 2008 Well, finally I came up with a solution: you must declare each subarray to be ArrayObject: $array = new RecursiveArrayIterator(array('id' => 1, 'name' => 'john', 2 => new ArrayObject(array('id' => 2, 'name' => 'michael')), 32 => new ArrayObject(array('id' => 32, 'name' => 'mary')), 14 => new ArrayObject(array('id' => 14, 'name' => 'george', 75 => new ArrayObject(array('id' => 5, 'name' => 'chris')), 9 => new ArrayObject(array('id' => 9, 'name' => 'john'))) ))); This way, unsetting a value through the iterator works (and without crashing the web server ) Thanks for all you help once more guys! Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-435406 Share on other sites More sharing options...
deadimp Posted January 11, 2008 Share Posted January 11, 2008 Well, not sure what more I can add, so I'll just state why it works for further understanding. You probably already know this, but I'm just avoiding doing homework now. When arrays are copied (i.e. assigning $test=$array), it goes through the array and copies all primitive data over. Primitive data types are numbers, strings, references, object references, and arrays (fairly certain that they are - subarrays should be copied). Since they are copied and not referenced, changing the primitive data in one array will not affect the other copy. Note: Object references only refer to objects, and they are the only way to refer to objects. When an object reference is copied, it does not copy/clone the object. That has to be done explicitly. [Applies to PHP 5 only] Now, if you change data that is referenced in one array, say something in an object, that change will be visible through the reference in the other array, since they're both references. That's what you are doing with ArrayObject - the iterator returns references to the original object instantiated in the original array, therefore the change is visible through the reference in that array. Here's just some random examples: //Basics $array=array(5,15, array(12,7,"pie"), "biscuits", new stdClass()); $test=$array; //All primitives copied $test[1]=18; //was 15 - This does not change the original value in $array $test[2][1]++; //8 $test[3].=" are delicious"; $test[4]->name="Bob"; //Now print out the arrays. All primitive data in $array should remain unchanged. The only different thing will be the stdClass, which will now have the 'name' variable print_r($array); echo "<br>"; print_r($test); echo "<br><br>"; //Try a different approach, same operations $ref=&$array; //referenced $ref[1]=18; //Just wait for it... $ref[2][1]++; $ref[3].=" are delicious"; $ref[4]->color="red"; //Print out - all data should be the same print_r($array); echo "<br>"; print_r($ref); echo "<br><br>"; //And just to screw around $x=15; $array=array($x,"apple",&$x,new stdClass(), array("sub",new stdClass()) ); $test=$array; //Old data in $test left to garbage collector $test[0]=12; $test[1].=" cake"; $test[2]+=58; $test[3]->name="Timmy"; $test[4][0].="array"; $test[4][1]->data=array("good",2,"go"); //Print echo "$x<br>"; print_r($array); echo "<br>"; print_r($test); Output: Array ( [0] => 5 [1] => 15 [2] => Array ( [0] => 12 [1] => 7 [2] => pie ) [3] => biscuits [4] => stdClass Object ( [name] => Bob ) ) Array ( [0] => 5 [1] => 18 [2] => Array ( [0] => 12 [1] => 8 [2] => pie ) [3] => biscuits are delicious [4] => stdClass Object ( [name] => Bob ) ) Array ( [0] => 5 [1] => 18 [2] => Array ( [0] => 12 [1] => 8 [2] => pie ) [3] => biscuits are delicious [4] => stdClass Object ( [name] => Bob [color] => red ) ) Array ( [0] => 5 [1] => 18 [2] => Array ( [0] => 12 [1] => 8 [2] => pie ) [3] => biscuits are delicious [4] => stdClass Object ( [name] => Bob [color] => red ) ) 73 Array ( [0] => 15 [1] => apple [2] => 73 [3] => stdClass Object ( [name] => Timmy ) [4] => Array ( [0] => sub [1] => stdClass Object ( [data] => Array ( [0] => good [1] => 2 [2] => go ) ) ) ) Array ( [0] => 12 [1] => apple cake [2] => 73 [3] => stdClass Object ( [name] => Timmy ) [4] => Array ( [0] => subarray [1] => stdClass Object ( [data] => Array ( [0] => good [1] => 2 [2] => go ) ) ) ) Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-436182 Share on other sites More sharing options...
periklis Posted January 11, 2008 Author Share Posted January 11, 2008 Well, I don't know how you did on your homework, but the explanation is really useful Quote Link to comment https://forums.phpfreaks.com/topic/84481-solved-spl-recursive-iterators-offsetunset/#findComment-436307 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.