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:
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:
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:
The output looks like this:
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.
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.
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.
Upvote
1