Determine infinite nested array in PHP - php

SO,
I have an issue with determining recursion in arrays in PHP. Assume that I have a dynamic-generated array, which finally can looks like:
$rgData = [
['foo', 'bar', $rgTemp=[7, 31, true]],
[-5, &$rgData, 'baz']
];
(links to variables here are provided dynamically and may refer to an array itself). Another sample:
$rgData = [
['foo', 'bar', $rgTemp=[7, 31, true]],
[-5, &$rgTemp, 'baz']
];
Both arrays have some references inside, but second looks good since it's reference is not cycled. Later, due to my logic, I have to handle array via recursive functions (something like array_walk_recursive) - and, of cause, I got Fatal error due to infinite nested array recursion in case of first sample above.
My question is - how to determine if an array has infinite recursion. Note, that this question is more complicated than simple search link from inside array to itself, because we can have something like:
$rgOne = [
['foo', 'bar'],
['baz']
];
$rgTwo = [6, 'test', &$rgOne];
$rgOne[1][] = &$rgTwo;
i.e. more complicated recursion. I found that PHP can resolve this somehow in var_dump and similar functions - for example, dump of the third sample will look like:
array(2) {
[0]=>
array(2) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
}
[1]=>
array(2) {
[0]=>
string(3) "baz"
[1]=>
&array(3) {
[0]=>
int(6)
[1]=>
string(4) "test"
[2]=>
&array(2) {
[0]=>
array(2) {
[0]=>
string(3) "foo"
[1]=>
string(3) "bar"
}
[1]=>
*RECURSION*
}
}
}
}
i.e. *RECURSION* was caught. Currently, I've tried to resolve a matter via function:
function isLooped(&$rgData, &$rgCurrent=null)
{
foreach($rgData as $mKey=>$mValue)
{
if(is_array($mValue) && isset($rgCurrent) &&
$rgCurrent===$rgData /* that's where, it seems, I need help*/)
{
return true;
}
elseif(is_array($mValue) && isLooped($mValue, $rgData))
{
return true;
}
}
return false;
}
but with no success (and I know why - that comparison is invalid). The only idea I have now is to do weird thing, like:
function isLooped(&$rgData)
{
$rgTemp = #var_export($rgData, 1);
return preg_match('/circular references/i', error_get_last()['message']);
}
but that is sad since I need at least copy my array data to some temporary storage (and, besides, all of this looks like a glitch, not a proper solution). So, may be there are some ideas of how to do that normal way?
Update: I've found a solution via changing key in the array and then looking for it in recursive loop. This is much better than var_* functions, but still not exactly what I want. Is there a way to do this without var_* functions or changing original array?

The problem is that PHP doesn't have a mechanism to tell you whether two variables reference the same zval (internal data type representing the actual held data). This rules out keeping track of the variables you have traversed so far as a solution.
Without this feature or modifying elements (pin method) it's unfortunately not possible to detect a recursive data structure except for var_dump() or print_r() hacks.

Related

Issue passing array as argument [to InvokeArg()] in php - var_dump are the same but only one works?

Backstory: Creating function to handle mysqli and binding data. All code is in the scope of a single function. Using ReflectionClass to programmatically invoke mysqli_stmt_bind_param function (as number of arguments vary).
Problem: I am having an issue passing an array I built up programmatically ($refArr). When I compare the var_dump of this array (with a sample array I created directly), the two arrays are identical. The invokeArg() method runs with the sample array ($refArr_sample) but not with $refArr.
Here is the output for the code shown below:
array(3) { [0]=> string(2) "si" [1]=> string(5) "user1" [2]=> int(0) } - output of refArr
array(3) { [0]=> string(2) "si" [1]=> string(5) "user1" [2]=> int(0) } - output of refArr_sample
$refArr_sample = array("si", "user1", 0);
// var_dump are equal in type and length
var_dump($refArr);
var_dump($refArr_sample);
$ref = new ReflectionClass('mysqli_stmt');
$method = $ref->getMethod("bind_param");
$method->invokeArgs($res,$refArr); // Doesn't Work ?????????
//$method->invokeArgs($res,$refArr_sample); // Works ?????????
$res->execute();
I am unsure how to fix the problem (as I've no clue what the problem is). I don't think this is an issue with references. Once the var_dump is the same I thought it wouldn't have mattered. I also did a === comparison between the two arrays which came back true. At this point I am lost as to why it isn't working.
I can link the complete code (self contained function) if required.
You should note that the values in the passed array must be references (as of PHP 5.3), whereas your $refArr is built dynamically.
...
//assign references
foreach ($refArr as $key => $value) {
$refArr[$key] =& $refArr[$key];
}
$method->invokeArgs($res,$refArr);
...

Dealing with php array whose content may or may not have another array

I am dealing with arrays whose structure is different depending on the number or items in the array.
For example, the following is the array with one item in it.
// Case #1
["Assignment"]=>
object(stdClass)#29 (9) {
["Id"]=> string(10) "1234567890"
..
}
However if there are more than 1 item in the array,
// Case #2
["Assignment"]=>
array(2) {
[0]=>
object(stdClass)#28 (9) {
["Id"]=> string(10) "1234567890"
..
}
[1]=>
object(stdClass)#28 (9) {
["Id"]=> string(10) "1234567890"
..
}
}
Notice that the contents are in another array for this. No matter of how many items there are, I want to access the Id. $array->Id will work for one case but won't work for the other with the error saying, Trying to get property of non-object.
I could come up with an inefficient way by counting the # of contents in the array like this:
// say the arrays above are declared as $assignment
if($numOfAssignment > 1) {
foreach($assignment as $key) {
echo $key->Id;
}
}
else {
echo $assignment->Id;
}
But if the code was a bit lengthy, I feel it is too repetitive and inefficient.
Is there a way to do this in one effective phrase no matter of the number of the contents inside the array? Let me know if anything is unclear. Thanks!
What you can do is change the non-array into an array with a single element, then you can process it consistently.
if (!is_array($assignment)) {
$assignment = array($assignment);
}
foreach ($assignment as $key) {
echo $key=>Id;
}

PHP Convert array element to string prints "Array"

I query a database to obtain an array of results.
$usersArray = $db->getAllUsers(); // db-Query
If I print out the array's var_dump, its content is structured in form of other arrays:
array(9) { [0]=> **array**(1) { ["column"]=> string(20) "..." } [1]=> **array**(1) { ["column"] (remaining 8 are the same).
Now I need these values (which are correct, so far) to be casted as strings, so that:
array(9) { [0]=> **string**(1) { ["column"]=> string(20) "..." } [1]=> **string**(1) { ["column"] ....
There are several answers to this here and elsewhere, such as
-array_map: here I can actually cast the content as string, but- it prints "Array" instead the value. It tried then getting the content via
$users = array_map('strval',implode( $usersArray));
$users = array_map('strval', print_r($usersArray));
Neither of those worked.
Is there a method through which I could cast the content as string and get the content ? Or should I rewrite the query to format the result as strings ?
You have a wrong understanding of types or at least this:
array(9) { [0]=> **string**(1) { ["column"]=> string(20) "..." } [1]=> **string**(1) { ["column"] ....
doesn't make any sense. You believe you want elements to be of type string but yet contain array data which really doesn't work.
What you actually want is a different array structure but you are heading in the wrong direction for that.
You basically have two options:
Modify the getAllUsers() method in a way that returns your data in a structure you actually need.
Modify the data after you have received it. Obviously there's no builtin function convert_data_to_how_i_want_them() - so a basic understanding of arrays is required.
Basically you create a new array and copy those values you need to the position you need them at.
Something like this should do the trick in this case:
$out = array();
foreach($in as => $value) {
$out[] = $value['column'];
}

Convert multidimensional array

How can I convert the below array to look like the one right below. I am trying to use array_map but i am confused on how to write the function.
array(3) {
["award_year"]=>
array(2) {
[0]=>
string(7) "1999-01"
[1]=>
string(7) "2010-02"
}
["award_title_user"]=>
array(2) {
[0]=>
string(1) "2"
[1]=>
string(2) "tt"
}
["award_description_user"]=>
array(2) {
[0]=>
string(1) "2"
[1]=>
string(3) "ddd"
}
}
This what i am trying to achieve:
array(2) {
[0]=>
array(3) {
["award_year"]=>
string(7) "2010-02"
["award_title_user"]=>
string(2) "tt"
["award_description_user"]=>
string(3) "ddd"
}
[1]=>
array(3) {
["award_year"]=>
string(7) "1999-01"
["award_title_user"]=>
string(1) "2"
["award_description_user"]=>
string(1) "2"
}
}
$newArr = [];
foreach($oldArr as $key=>$row) {
foreach($row as $index=>$item) {
$newArr[$index][$key] = $item;
}
}
this will solve it, but no checks if data is valid as you mentioned
First things first, #tzook-bar-noy's answer is a better answer in this case, and I am not even advocating the approach I am going to detail here.
When dealing with arrays, I always try to avoid generic loops (ie: foreach()) if I can, instead using the more functional-programming approach that you mention in your question: using functions like array_map(). However the functional-programming functions are very focused in what they do (which is their benefit: they make your code Cleaner), so to use them: you kinda gotta be wanting to do the specific operation they offer.
array_map() has a drawback for your purposes here, in that the mapped array has the same keys in the same order as the original array, which is not what you want here. You need to both turn your original array "inside out", but the ordering of the resultant array you want is the reverse of the original data. Not a good fit for array_map().
It's doable with array_map(), but it's like using a flathead screwdriver when you really need a Philips.
Here's an example:
<?php
$awards = [
"award_year" => ["1999-01", "2010-12"],
"award_title_user" => ["2", "tt"],
"award_description_user" => ["2", "ddd"]
];
$remappedAwards = array_map(function($year, $title, $description){
return [
"award_year" => $year,
"award_title_user" => $title,
"award_description_user" => $description
];
}, $awards["award_year"], $awards["award_title_user"], $awards["award_description_user"]);
$remappedAwards = array_reverse($remappedAwards);
echo "<pre>";
var_dump($remappedAwards);
echo "</pre>";
Obviously I've hardcoded the key names in here too which is less than ideal. One could contrive a generic approach, but by then we'd be so far beyond the actual intent of aaray_map() that the code complexity would be getting daft.
In other languages wherein these array-iteration functions are a bit better implemented (say JS or CFML) one might be able to come up with a half-decent answer with a .reduce() (JS, CFML) kind of operation. However PHP's array_reduce() (and its other array-iteration methods) are hamstrung by their poor implementation as they only pass the value to the callback. They really need to pass at least the index as well, and (less-often-useful, but handy sometimes ~) the array itself as additional arguments to make them anything more than a proof-of-concept. IMO, obviously (I'm biased, I do more JS & CFML than I do PHP, so my expectations are set higher).
Bottom line: array_map() was not a good fit for your requirement here, but I applaud your efforts for thinking to us it as the function-programming approach to array manipulation is definitely a better approach than generic looping where the requirement matches the functionality.

Get value from a array

update
how can I retrieve this value? I need to do that if I will write the value to my database.
array(3) {
[1]=> NULL
[2]=> array(2) {
[123]=>
int(123)
[122]=>
int(0)
}
[3]=> NULL
}
There is something missing in your output. I assume it looks something like:
// var_dump($array);
array(1) {
[0]=>
string(2) "39"
}
so you can access the value with $array[0]. Simple array access.
As arrays are the most important data structure in PHP, you should learn how to deal with them.
Read PHP: Arrays.
Update:
Regarding your update, which value do you want? You have a multidimensional array. This is what you will get:
$array[1] // gives null
$array[2] // gives an array
$array[2][123] // gives the integer 123
$array[2][122] // gives the integer 0
$array[3] // gives null
Maybe you also want (have) to loop over the inner array to get all values:
foreach($array[2] as $key => $value) {
// do something with $key and $value
}
As I said, read the documentation, it contains everything you need to know. Accessing arrays in PHP is not much different than in other programming languages.
The PHP manual contains a lot of examples, it is a pretty could documentation. Use it!
If your array is referenced as $myArray, you can get the string 39 via $myArray[0], i.e., this zeroth item.

Categories