3

I have the following array:

$conf = array(
'db'    => array(
    'server'    =>  'localhost',
    'user'      =>  'root',
    'pass'      =>  'root',
    'name'      =>  'db',
    ),
'path'  =>  array(
    'site_url'  =>  $_SERVER['SERVER_NAME'],
    'site_dir'  => CMS,
    'admin_url' => conf('path.site_url') . '/admin',
    'admin_dir' => conf('path.site_dir') . DS .'admin',
    'admin_paths' => array(
                'assets' => 'path'
                ),
    ),
);

I would like to get a value from this array using a function like so:

/**
 * Return or set a configuration setting from the array.
 * @example
 * conf('db.server')    => $conf['db']['server']
 *
 * @param  string $section the section to return the setting from.
 * @param  string $setting the setting name to return.
 * @return mixed           the value of the setting returned.
 */
function conf($path, $value = null) {
    global $conf;
    // We split each word seperated by a dot character
    $paths = explode('.', $path);

    return $conf[$paths[0]][$paths[1]];
}

But i would like it if the function resolves all dimensions of the array and not just the first two.

Like this

conf('path.admin_paths.assets'); 

would resolve to

=> $conf['path']['admin_paths']['assets']

How would i do this? Also, how would i make this function if it has another param, would set a value rather than return it?

Xees
  • 462
  • 2
  • 5
  • 15

6 Answers6

6

This function works:

function conf($path, $value = null) {
    global $conf;
    // We split each word seperated by a dot character
    $paths = explode('.', $path);
    $result = $conf;
    foreach ($paths as $path) {
        $result = $result[$path];
    }
    return $result;
}

Edit:

Add Set:

function conf($path, $value = null) {
    global $conf;
    // We split each word seperated by a dot character
    $paths = explode('.', $path);
    if ($value === null) {
        // Get
        $result = $conf;
        foreach ($paths as $path) {
            $result = $result[$path];
        }
        return $result;
    }
    // Set
    if (!isset($conf)) $conf = array(); // Initialize array if $conf not set
    $result = &$conf;
    foreach ($paths as $i=>$path) {
        if ($i < count($paths)-1) {
            if (!isset($result[$path])) {
                $result[$path] = array();
            }
            $result = &$result[$path];
        } else {
            $result[$path] = $value;
        }
    }
}
gabrieloliveira
  • 560
  • 3
  • 10
3

I found @Danijel recursive approach a lot cleaner than my initial try. So here's a recursive implementation of the functionality, supporting setting values.

function array_get_by_key(&$array, $key, $value = null) {
    list($index, $key) = explode('.', $key, 2);
    if (!isset($array[$index])) throw new Exception("No such key: " . $index);

    if(strlen($key) > 0)
        return array_get_by_key(&$array[$index], $key, $value);

    $old = $array[$index];
    if ($value !== null) $array[$index] = $value;
    return $old;    
}

function config($key, $value = null) {
    global $CONFIG;
    return array_get_by_key(&$CONFIG, $key, $value);
}

Test run:

$CONFIG = array(
'db'    => array(
    'server'    =>  'localhost',
    'user'      =>  'root',
    'pass'      =>  'root',
    'name'      =>  'db',
    ),
'path'  =>  array(
    'site_url'  =>  'localhost',
    'site_dir'  => 'CMS',
    'admin_url' => 'localhost/admin',
    'admin_dir' => 'localhost/res/admin',
    'admin_paths' => array(
                'assets' => 'path'
                ),
    ),
);

try {
    var_dump(config('db.pass'));
    var_dump(config('path.admin_url', 'localhost/master'));
    var_dump(config('path.admin_url'));
    var_dump(config('path.no_such'));
} catch (Exception $e) {
    echo "Error: trying to access unknown config";
}

// string(4) "root"
// string(15) "localhost/admin"
// string(16) "localhost/master"
// Error: trying to access unknown config
svvac
  • 5,814
  • 3
  • 17
  • 22
2
function conf($path,$value=null) {
    global $conf;
    // We split each word seperated by a dot character
    $paths = explode('.', $path);
    $temp = $conf ;
    $ref = &$conf ;
    foreach ( $paths as $p )
    {
        $ref = &$ref[$p] ; // Register the reference to be able to modify $conf var
        if ( isset($temp[$p]) )
            $temp = $temp[$p] ;
        elseif ( $value !== null ) // This key does not exist, and we have a value : time to modify
            $ref = $value ;
        else // Key does not exist and no value to add
            return false ;
    }
    return $temp ;
}
Pierre Granger
  • 1,993
  • 2
  • 15
  • 21
2
function conf($path, $value = null) {
    global $conf;
    $paths = explode('.', $path);

    $array = &$conf; // Reference to the config array

    foreach ($paths as $k) {
        if (!is_array($array)) throw new Exception("No such key: " . $path);
        if (!isset($array[$k])) throw new Exception("No such key: " . $path);

        // In order to walk down the array, we need to first save the ref in
        // $array to $tmp
        $tmp = &$array;
        // Deletes the ref from $array
        unset($array);
        // Create a new ref to the next item
        $array =& $tmp[$k];
        // Delete the save
        unset($tmp);
    }

    $val = $array

    if ($value !== null) $array = $value;

    return $array
}

(Code inpired by this SO question and Pierre Granger's answer)

Community
  • 1
  • 1
svvac
  • 5,814
  • 3
  • 17
  • 22
1

As I see it, to get any value from the multidimensional array, recursion is the proper way. Also, you can't use conf() function inside the $conf array before the array is initialized ( the global $conf; will be NULL, eg. not set )

function conf( $path ) {
    global $conf;
    return recurse( $conf, explode( '.', $path ) );
}

function recurse( $array, $keys ) {

    if ( isset( $keys[0] ) && isset( $array[$keys[0]] ) ) {

        $array = $array[$keys[0]];
        array_shift( $keys );
        return recurse( $array, $keys );

    } else return is_string( $array ) ? $array : false ;

}


print_r2( conf('path.admin_paths.assets') );    // @str  "path"
print_r2( conf('path.admin_paths') );           // @bool false ( path is invalid )
print_r2( conf('path.admin_url') );             // @str  "/admin" 
print_r2( conf('db.server') );                  // @str  "localhost" 
Danijel
  • 12,408
  • 5
  • 38
  • 54
  • How can i use the conf, function inside the array? I want the array to use other parts of the array itself. – Xees Mar 24 '14 at 19:27
1

I have implemented array access class CompositeKeyArray, that hides away the recursion so you can simply get and set nested keys.

Given that class the original problem can be solved like this:

$conf = new CompositeKeyArray($conf);

var_dump($conf[['path', 'admin_paths', 'assets']]); // => string(4) "path"

If you want you can change the value:

$conf[['path', 'admin_paths', 'assets']] = 'new path';

var_dump($conf[['path', 'admin_paths', 'assets']]); // => string(8) "new path"

Or even unset it:

unset($conf[['path', 'admin_paths', 'assets']]);

var_dump(isset($conf[['path', 'admin_paths', 'assets']])); // => bool(false)

Here is working demo.

sevavietl
  • 3,762
  • 1
  • 14
  • 21