Search code examples
phpmultidimensional-arrayevalvariable-variables

Referencing a multidimensional array's elements based on a string without eval


Alright I'm working with a large multidimensional array which has more information in it than I need and I want to loop through it to filter the data which I'm interested in. Sadly this multidimensional array is produced dynamically and doesn't always contain the data I want, so I have to use logic like:

if(isset($ar['b']['ba']['baa'])){
echo '<h3>'.$ar['b']['ba']['baa'].'</h3>';
}
if(isset($ar['b']['ba']['baba'])){
echo '<h3>'.$ar['b']['ba']['baba'].'</h3>';
}
if(isset($ar['b']['ba']['babb'])){
echo '<h3>'.$ar['b']['ba']['babb'].'</h3>';
}

The above works great but its a bit messy-looking so I converted the above to:

$refAr=array();
$refAr[]='b->ba->baa';//3
$refAr[]='b->ba->bab->baba';//4
$refAr[]='b->ba->bab->babb';//5

The above looks a lot more nice and neat and is how I want to control the script in case I need to reference different keys in the future. The problem I am having is trying to use the above format to actually reference the array. I thought variable variables would work but apparently it fails. My second attempt using eval worked but I'm not very happy with my solution. This is where I need you guys to come in, is there a better way to do this? Here's my attempt below:

<?php

$ar=array(
    'a'=>array('aa'=>1,'ab'=>2),
    'b'=>array(
        'ba'=>array('baa'=>3,'bab'=>array('baba'=>4,'babb'=>5),'bac'=>array('baca'=>6,'bacb'=>7)),
    )
);


$refAr=array();
$refAr[]='b->ba->baa';//3
$refAr[]='b->ba->bab->baba';//4
$refAr[]='b->ba->bab->babb';//5
foreach($refAr as $ref)
{
    $r=explode('->',$ref);
    $r="\$ar['".implode("']['",$r)."']";
    echo '<h1>'.$r.'</h1>';
    echo '<h3>'.$$r.'</h3>';//undefined
    eval('$t='.$r.';');
    echo "<h3>$t</h3>";//works but uses eval, is there a better way?

}

Solution

  • I decided to answer my own question. This is what I wound up using:

    <?php
    
    //Sample PHP representation of dynamically-pulled JSON data from an untrusted source
    $ar=array(
        'a'=>array('aa'=>1,'ab'=>2),
        'b'=>array(
            'ba'=>array('baa'=>3,'bab'=>array('baba'=>4,'babb'=>5),'bac'=>array('baca'=>6,'bacb'=>7)),
        )
    );
    
    //Reusable function
    function resolveReference($ar,&$ref)
    {
        $t = $ar;
        foreach ( explode('->',$ref) as $v )
        {
            if (!array_key_exists($v,$t)){$ref=null;return false;}
            $t = $t[$v];
        }
        $ref=$t;
        return true;
    }
    
    //The references I'm interested in but don't know if my dynamic data will contain these keys every time
    $refAr=array();
    $refAr[]='b->ba->baa';
    $refAr[]='b->ba->bab->baba';
    $refAr[]='b->ba->bab->babb';
    $refAr[]='b->doesnt->exist';
    
    
    foreach($refAr as $ref)
    {
        echo '<h1>'.$ref.'</h1>';
        if(resolveReference($ar,$ref))
        {
            echo '<h3><span style="color:blue;">'.$ref.'</span></h3>';
        }else{
            echo '<h3><span style="color:red;">Alternative text for non-existent expected reference</span></h3>';
        }
    }