1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Not Planned Use Environment Variables For Configuration Options

Discussion in 'Closed Suggestions' started by ibnesayeed, Jan 12, 2016.

  1. ibnesayeed

    ibnesayeed Well-Known Member

    In a setup where the application is deployed using a private repository revision control system, it is not a good idea to keep the credentials and other config options hard coded in the config.php file and commit it to the repository. One common practice in this scenario is to optionally allow system environment variables for configuration. This will also make XenForo more friendly to containerize (such as using Docker), where a single docker image can be used in production and test instances just by passing different environment variable values at run time (to connect to a different database for instance). Currently we can do this by mounting an external config file in the container, but it is a hassle.

    One option to support environment variable base configuration is to do something like this in the config.php:
    PHP:
    $config['db']['username'] = '' ?: $_ENV['XF_db_username'];
    However, this approach will only allow overwriting configurations that are already listed in the config.php file with additional code to fallback to the ENV var. To make it generic and allow arbitrary config variables to be added, something needs to be done where the defaults are set. The order of precedence should be like this:
    Code:
    from_config_file ?: from_env_var ?: default
    So, I looked into the code where config variables are being set to see how it's working. Based on my understanding, I wrote a function to filter relevant ENV vars and parse them to load the config options. I assumed that any ENV var that starts with "XF_" is a relevant one and config nesting is flattened using "_" as the delimiter. Here is my code:
    PHP:
    <?php

    // Filter and parse ENV Vars to update default config
    function getEnvConfig($prefix 'XF')
    {
        
    $envConfig = array();
        foreach (
    $_ENV AS $key => $value)
        {
            
    $parts explode('_'$key);
            
    $first array_shift($parts);
            if (
    $first == $prefix && count($parts))
            {
                
    $target =& $envConfig;
                foreach (
    $parts AS $part)
                {
                    
    $target =& $target[$part];
                }
                
    $target $value;
            }
        }
        return 
    $envConfig;
    }

    // Test the function
    $_ENV = array(
        
    'XF_externalDataPath'  => '/tmp/data',
        
    'XF_db_username'       => 'test',
        
    'XF_db_password'       => 'secret',
        
    'XF_db_dbname'         => 'testdb',
        
    'XF_addOn_custom_conf' => 'testing',
        
    'XF'                   => 'edge_condition',
        
    'OTHER_ENV'            => 'ignored'
    );
    print(
    "\n# Environment variables\n");
    print_r($_ENV);
    $envConfig getEnvConfig();
    print(
    "\n# Filtered and parsed config variables\n");
    print_r($envConfig);
    The output looks like this:
    PHP:
    # Environment variables
    Array
    (
        [
    XF_externalDataPath] => /tmp/data
        
    [XF_db_username] => test
        
    [XF_db_password] => secret
        
    [XF_db_dbname] => testdb
        
    [XF_addOn_custom_conf] => testing
        
    [XF] => edge_condition
        
    [OTHER_ENV] => ignored
    )

    # Filtered and parsed config variables
    Array
    (
        [
    externalDataPath] => /tmp/data
        
    [db] => Array
            (
                [
    username] => test
                
    [password] => secret
                
    [dbname] => testdb
            
    )

        [
    addOn] => Array
            (
                [
    custom] => Array
                    (
                        [
    conf] => testing
                    
    )

            )

    )
    I believe, if the return value of the above method "getEnvConfig()" is used in the "loadConfig()" method of the Application.php file as following, it will give the desired functionality.
    PHP:
    $envConfig getEnvConfig();
    $outputConfig->merge($defaultConfig)
        ->
    merge($envConfig// Merge after the default config
        
    ->merge(new Zend_Config($config))
        ->
    setReadOnly();
    I don't write PHP very often and this code is for the proof of concept purpose only. As such it can only handle string values, but it should be possible to allow passing numbers, boolean, or comma separated lists (to explode into an array) as values.
     
    HWS likes this.
  2. Mike

    Mike XenForo Developer Staff Member

    I believe that if you need to read configuration from the environment, it would be best to make it explicit by doing it in your config.php file directly. You would know your setup and would know what you're explicitly expecting to be in the environment. If you make something magic, it adds an additional layer of confusion when trying to debug. Essentially, I'm recommending just pulling from $_ENV directly in config.php in the locations where you want to use it (perhaps throwing an error if expected elements aren't there). You could even use your demo code in config.php if you do want to keep the magic.

    The vast majority of our customers are likely in a setup where they don't actually even control the environment variables. If you are and prefer to pull stuff like this from the environment, I think just setting it up for your needs directly (and IMO, explicitly) is the best approach.
     
    ibnesayeed likes this.

Share This Page