dealing with other programmers' complex arrays in PHP - php

I am maintaining an OO PHP application that loads everything to $this array. When I do a var dump on $this to find out how to access a value, I get dozens of pages of output. Hunting down the array elements that I need is very time consuming
For example, if I want to find where Customer Territory is stored, I have to figure out the heirarchy of the array using print_r or var_dump and staring [edit: and searching] ]at the output until I figure out the path.
for example:
$this->Billing->Cst->Record['Territory']
Is there a better way to do this, or some tools/techniques that I can use. For instance, is there there quick way to find the path to variable ['Territory'] throughout the array directly?

Krumo is a graphical "var_dump" tool that may make navigation a tiny bit easier. Check out the "examples" section on the project page.
For searching in multi-dimensional arrays, this SO question may help you.

You could probably do ctr+F on the output instead of staring at it?
Just start with ctr+F: "Customer", "Territory" and all other names related to whatever you're searching.

function findInTree($var, $words) {
$words = explode(' ', strtolower($words));
$path = array();
$depth = 0;
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($var), RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $key => $value) {
if ($iterator->getDepth() < $depth) {
unset($path[$depth]);
}
$depth = $iterator->getDepth();
$path[$depth] = $key;
if (is_string($key) && in_array(strtolower($key), $words)) {
echo '<pre>', implode(' -> ', $path), '</pre>';
}
}
}
findInTree($this, 'Customer Territory');
This function will walk through your object and look for any of the given words as a key.

FirePHP/Firebug can give you a detailed structural view of $this and its properties.

Related

How to map array depth?

I'm not sure how to call this, and I can't find what I'm looking for because I must be looking for the wrong keywords, so even the right search term would help me out here:
I have a $map array and a $data array. The $data array is a dynamic array based on a xml that is parsed. Simplified Example:
$data = array('a' => array(0=> array('hello'=>'I want this data')));
$map = array('something' => 'a.0.hello');
Now I'd like to set $test to the value of $data['a']['0']['hello'] by somehow navigating there using $map['something']. The idea behind this is to create a mapping array so that no code changes are required if the xml format is to be changed, only the mapping array. Any help in the good direction is very much appreciated :)
// Set pointer at top of the array
$path = &$data;
// Asume that path is joined by points
foreach (explode('.', $map['something']) as $p)
// Make a next step
$path = &$path[$p];
echo $path; // I want this data
There's not really a good way to programmatically access nested keys in arrays in PHP (or any other language I can think of, actually).
function getDeepValue(&$arr, $keys) {
if (count($keys) === 0)
return $arr;
else
return getDeepValue($arr[$keys[0]], array_slice($keys, 1));
}
$data = ['a' => [0=> ['hello'=>'I want this data']]];
$map = ['something' => getDeepValue($data, ['a', '0', 'hello'])];
var_dump($map);
// array(1) {
// ["something"]=>
// string(16) "I want this data"
// }
If you want to use the string of keys, just use
getDeepValue($arr, explode('.', 'a.0.hello'))
If that's a common operation you have to do, you could add another layer of abstraction that accepts string-based lookup of deep values.

How do I loop through and change values in this PHP object?

Here is a fairly big object dumped using print_r.
https://docs.google.com/document/d/175RLhWlMQcyhGR6ffGSsoJGS3RyloEqo4EEHCL2H2vg/edit?usp=sharing
I am trying to change the values of the uploaded_files.
Towards the end of that object you'll see something like
[uploaded_files] => Array
(
[attachment] => /home2/magician/public_html/development/testing/wp-content/uploads/wpcf7_uploads/Central-Coast-Montessori-logo.jpg
[attachment2] => /home2/magician/public_html/development/testing/wp-content/uploads/wpcf7_uploads/Andrew.jpg )
My code
// move the attachments to wpcf7ev temp folder
foreach ($cf7ev_object['uploaded_files'] as $key => $uploaded_file_path) {
$new_filepath = WPCF7EV_UPLOADS_DIR . '/' . basename($uploaded_file_path);
wpcf7ev_debug("New file path is {$new_filepath}");
rename($uploaded_file_path, $new_filepath);
wpcf7ev_debug("'{$key}'is the KEY for {$uploaded_file_path}");
wpcf7ev_debug($cf7ev_object['uploaded_files']);
$cf7ev_object['uploaded_files'][$key] = $new_filepath; // this is not updating
}
To loop through it I have been using
foreach ($cf7ev_object->uploaded_files as $key => $uploaded_file_path) {
and this has worked.
But shouldn't it be
foreach ($cf7ev_object['uploaded_files'] as $key => $uploaded_file_path) {
? As '->' is for accessing methods?
And specifically I want to update the values of those uploaded_files, so to do that I need to do
$cf7ev_object['uploaded_files'][$key] = $new_filepath; // this is not updating
? But this doesn't seem to be working.
I think I need to be clear on how to access values in an object.
Thanks.
First of all, regarding the single arrow "->" that is how you reference an objects values. But I won't get into that. Since you say it works, $cf7ev_object is obviously an object.
You say you want to "access the values in the object".
var_dump($cf7ev_object);
This will spit out what is in that object. I gather you are a bit of a newbie, so I will try to help you out best I can with the limited data you provided (you may want to expand your question.
Looping is not a one-shot deal. You can have nested loops and nested loops inside of those. However, it is a resource hog if you're not careful. Here is an exercise that might help you.
$new_array = array();
foreach($cf7ev_object->uploaded_files as $key => $value) {
$new_value = $value;//do something to the $value here
$new_array[$key] = $new_value;
}
//take a look at your work now:
print_r($new_array);
I hope this helps. Note: your google doc is restricted, public can't see it.. And your question is too vague. Let me know if I can help more.
If you want to change the object array values instantly you just set it equal to the above loop result:
$cf7ev_object->uploaded_files = $new_array;

detecting infinite array recursion in PHP?

i've just reworked my recursion detection algorithm in my pet project dump_r()
https://github.com/leeoniya/dump_r.php
detecting object recursion is not too difficult - you use spl_object_hash() to get the unique internal id of the object instance, store it in a dict and compare against it while dumping other nodes.
for array recursion detection, i'm a bit puzzled, i have not found anything helpful. php itself is able to identify recursion, though it seems to do it one cycle too late. EDIT: nvm, it occurs where it needs to :)
$arr = array();
$arr[] = array(&$arr);
print_r($arr);
does it have to resort to keeping track of everything in the recursion stack and do shallow comparisons against every other array element?
any help would be appreciated,
thanks!
Because of PHP's call-by-value mechanism, the only solution I see here is to iterate the array by reference, and set an arbitrary value in it, which you later check if it exists to find out if you were there before:
function iterate_array(&$arr){
if(!is_array($arr)){
print $arr;
return;
}
// if this key is present, it means you already walked this array
if(isset($arr['__been_here'])){
print 'RECURSION';
return;
}
$arr['__been_here'] = true;
foreach($arr as $key => &$value){
// print your values here, or do your stuff
if($key !== '__been_here'){
if(is_array($value)){
iterate_array($value);
}
print $value;
}
}
// you need to unset it when done because you're working with a reference...
unset($arr['__been_here']);
}
You could wrap this function into another function that accepts values instead of references, but then you would get the RECURSION notice from the 2nd level on. I think print_r does the same too.
Someone will correct me if I am wrong, but PHP is actually detecting recursion at the right moment. Your assignation simply creates the additional cycle. The example should be:
$arr = array();
$arr = array(&$arr);
Which will result in
array(1) { [0]=> &array(1) { [0]=> *RECURSION* } }
As expected.
Well, I got a bit curious myself how to detect recursion and I started to Google. I found this article http://noteslog.com/post/detecting-recursive-dependencies-in-php-composite-values/ and this solution:
function hasRecursiveDependency($value)
{
//if PHP detects recursion in a $value, then a printed $value
//will contain at least one match for the pattern /\*RECURSION\*/
$printed = print_r($value, true);
$recursionMetaUser = preg_match_all('#\*RECURSION\*#', $printed, $matches);
if ($recursionMetaUser == 0)
{
return false;
}
//if PHP detects recursion in a $value, then a serialized $value
//will contain matches for the pattern /\*RECURSION\*/ never because
//of metadata of the serialized $value, but only because of user data
$serialized = serialize($value);
$recursionUser = preg_match_all('#\*RECURSION\*#', $serialized, $matches);
//all the matches that are user data instead of metadata of the
//printed $value must be ignored
$result = $recursionMetaUser > $recursionUser;
return $result;
}

RecursiveIteratorIterator Blank Page

I'm trying to use RecursiveIteratorIterator and RecursiveDirectoryIterator.
I want get all file inside my c:\ folder. But i don't know why i can't get the result but a blank page.
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('c:/' ));
foreach( $it as $file ) {
$all[] = $file->getRealPath();
}
print_r($all);
but if i use this code, it's work
foreach( $it as $key=>$file )
{
echo $key."=>".$file."\n";
}
Most likely because your PHP interpreter does not have access to that folder.
It's because PHPs StdObj-class cannot be used as an array because it's an associative array. This is wrong by the words but I cannot describe it better. The PHP object gets casted or something like that.
If $data is an object then this is required if you want to access $value as an object.
foreach($data as $property => $value){
echo $value->r;
}
Edit: This is a good question btw, I've spent a couple of hours myself figuring this out.

while(list($key, $value) = each($array)) vs. foreach($array as $key => $value)?

Recently I experienced this weird problem:
while(list($key, $value) = each($array))
was not listing all array values, where replacing it with...
foreach($array as $key => $value)
...worked perfectly.
And, I'm curious now.. what is the difference between those two?
Had you previously traversed the array? each() remembers its position in the array, so if you don't reset() it you can miss items.
reset($array);
while(list($key, $value) = each($array))
For what it's worth this method of array traversal is ancient and has been superseded by the more idiomatic foreach. I wouldn't use it unless you specifically want to take advantage of its one-item-at-a-time nature.
array each ( array &$array )
Return the current key and value pair from an array and advance the array cursor.
After each() has executed, the array cursor will be left on the next element of the array, or past the last element if it hits the end of the array. You have to use reset() if you want to traverse the array again using each.
(Source: PHP Manual)
Well, one difference is that each() will only work on arrays (well only work right). foreach will work on any object that implements the traversable interface (Which of course includes the built in array type).
There may be a micro-optimization in the foreach. Basically, foreach is equivilant to the following:
$array->rewind();
while ($array->valid()) {
$key = $array->key();
$value = $array->current();
// Do your code here
$array->next();
}
Whereas each basically does the following:
$return = $array->valid() ? array($array->key(), $array->current()) : false;
$array->next();
return $return;
So three lines are the same for both. They are both very similar. There may be some micro-optimizations in that each doesn't need to worry about the traversable interface... But that's going to be minor at best. But it's also going to be offset by doing the boolean cast and check in php code vs foreach's compiled C... Not to mention that in your while/each code, you're calling two language constructs and one function, whereas with foreach it's a single language construct...
Not to mention that foreach is MUCH more readable IMHO... So easier to read, and more flexible means that -to me- foreach is the clear winner. (that's not to say that each doesn't have its uses, but personally I've never needed it)...
Warning! Foreach creates a copy of the array so you cannot modify it while foreach is iterating over it. each() still has a purpose and can be very useful if you are doing live edits to an array while looping over it's elements and indexes.
// Foreach creates a copy
$array = [
"foo" => ['bar', 'baz'],
"bar" => ['foo'],
"baz" => ['bar'],
"batz" => ['end']
];
// while(list($i, $value) = each($array)) { // Try this next
foreach($array as $i => $value) {
print $i . "\n";
foreach($value as $index) {
unset($array[$index]);
}
}
print_r($array); // array('baz' => ['end'])
Both foreach and while will finish their loops and the array "$array" will be changed. However, the foreach loop didn't change while it was looping - so it still iterated over every element even though we had deleted them.
Update: This answer is not a mistake.
I thought this answer was pretty straight forward but it appears the majority of users here aren't able to appreciate the specific details I mention here.
Developers that have built applications using libdom (like removing elements) or other intensive map/list/dict filtering can attest to the importance of what I said here.
If you do not understand this answer it will bite you some day.
If you passed each an object to iterate over, the PHP manual warns that it may have unexpected results.
What exactly is in $array

Categories