Using Environment variables in Wordpress wp-config - php

I'm using phpfog.com for hosting and github.com for issue tracking, etc.
I have two remotes setup, one to phpfog.com, and the other to github.
In the back-end admin of phpfog you can define Environment Variables. I did so there and want to use them in my wp-config file.
Here's the code I used:
/** Hardened Salts for use on github.com, phpfog.com, etc.*/
$AUTH_KEY = getenv('AUTH_KEY');
$SECURE_AUTH_KEY = getenv('SECURE_AUTH_KEY');
$LOGGED_IN_KEY = getenv('LOGGED_IN_KEY');
$NONCE_KEY = getenv('NONCE_KEY');
$AUTH_SALT = getenv('AUTH_SALT');
$SECURE_AUTH_SALT = getenv('SECURE_AUTH_SALT');
$LOGGED_IN_SALT = getenv('LOGGED_IN_SALT');
$NONCE_SALT = getenv('NONCE_SALT');
define('AUTH_KEY', $AUTH_KEY);
define('SECURE_AUTH_KEY', $SECURE_AUTH_KEY);
define('LOGGED_IN_KEY', $LOGGED_IN_KEY);
define('NONCE_KEY', $NONCE_KEY);
define('AUTH_SALT', $AUTH_SALT);
define('SECURE_AUTH_SALT', $SECURE_AUTH_SALT);
define('LOGGED_IN_SALT', $LOGGED_IN_SALT);
define('NONCE_SALT', $NONCE_SALT);
There must be a cleaner way of doing this…

You could make it half as long by passing the function result as a constant value without intermediate variable:
define('AUTH_KEY', getenv('AUTH_KEY'));
Or do that in a loop:
$vars = array('AUTH_KEY', 'SECURE_AUTH_KEY', ...);
foreach ($vars as $var) {
define($var, getenv($var));
}

From WordPress 5.5.0
WordPress has added a new function for the environment variables with 3 different possible values.
You can use wp_get_environment_type() function to get the current environment.
Usage example:
If(wp_get_environment_type() === 'development') {
// do something
} else {
// do something
}
By default, if WP_ENVIRONMENT_TYPE is empty or invalid ( anything except development, staging & production), production is returned.
You can define development or staging environment through the wp-config.php file.
define( 'WP_ENVIRONMENT_TYPE', 'development' );

I prefer to use this approach below:
<?php
//GET HOSTNAME INFO
$hostname = $_SERVER['SERVER_NAME'];
//VERIFY WHICH ENVIRONMENT THE APP IS RUNNING
switch ($hostname) {
case 'development.dev':
define('WP_ENV', 'development');
define('WP_DEBUG', true);
break;
case 'staging.mywebsite.com':
define('WP_ENV', 'staging');
define('WP_DEBUG', true);
break;
case 'www.mywebsite.com':
define('WP_ENV', 'production');
define('WP_DEBUG', false);
break;
default:
define('WP_ENV', 'production');
define('WP_DEBUG', false);
}
?>

The best way to use environment variables to control your WP environment is by using DotEnv ( https://github.com/vlucas/phpdotenv )
This approach is laid out in a blog post: https://m.dotdev.co/secure-your-wordpress-config-with-dotenv-d939fcb06e24
The basic approach is to create an .env file in the root of your site with the environment variables.
However there are a few problems with the blog post as DotEnv version 5 no longer uses environment variables by default.
So instead of the code used in the blog post, use this at the top of your wp-config.php file...
$app_env = getenv("APP_ENV");
$file = $app_env == null ? ".env" : ".env.".$app_env;
if(file_exists(__DIR__.'/'.$file))
{
require_once(__DIR__ . '/vendor/autoload.php');
(Dotenv\Dotenv::createUnsafeImmutable(__DIR__,$file))->load();
error_log("Environment loaded from ".$file);
} else {
error_log("*WARNING* environment file not found: ".$file);
}
The .env file looks like this...
# MySQL settings
DB_NAME=wpbench
DB_USER=wpuser
DB_PASSWORD=password
DB_HOST=localhost
DB_CHARSET=utf8
DB_COLLATE=
Defining the constants in the wp-config.php file looks like this...
/** The name of the database for WordPress */
define( 'DB_NAME', getenv('DB_NAME'));
/** MySQL database username */
define( 'DB_USER', getenv('DB_USER'));
/** MySQL database password */
define( 'DB_PASSWORD', getenv('DB_PASSWORD'));
/** MySQL hostname */
define( 'DB_HOST', getenv('DB_HOST'));
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', getenv('DB_CHARSET'));
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', getenv('DB_COLLATE'));
Use the APP_ENV variable to switch between variable sets. For example create .env.production and .env.staging files. If the .env file does not exist then the values are pulled from the environment which works well for cloud deployment.

Related

PHP Dotenv causing wordpress configuration to run twice

I have a relatively complex laravel project, within it I have a wordpress installation for a blog on the side which is installed with composer.
In my wp-config.php I use an include to a file in my config directory called application.php (for organisation purposes). I have various cases of define('XXX', 'config stuff'); and similar in the file. When 'config stuff' is hard coded the site works perfectly, but recently I'm trying to use dotenv installed by composer to pull in values from my .env with getenv()
require_once(LARAVEL_PATH . '/vendor/autoload.php');
$dotenv = new Dotenv\Dotenv(APP_ROOT_DIR);
$dotenv->load();
When I var_dump my getenv('example_env_constant') it gives me the correct values absolutely fine. So I went about setting these throughout my application.php file.
But now when I load up the site I get a large number of
Notice: Constant XXX_XXXX_XXX already defined in /path/to/application.php on line X
One for every single define('XXX', value);
And also a
Cannot modify header information
On testing I've found that my wp-config.php file is being run once. But somehow my application.php is being run twice. The 1st run is from my include call in wp-config.php as it should be. The 2nd run which is triggering the errors is happening within wp-settings.php on line 326
do_action( 'plugins_loaded' );
I have no idea how this is happening.
If I remove the dotenv code from application.php and instead put it directly into my wp-config.php the behaviour is exactly the same, with wp-config.php running once and application.php running twice.
Now if I remove application.php and put all of my code into wp-config.php as wp config is traditionally done. Then I get the exact same issue again and it references the deleted file... this of course indicates a cache issue at this point although I don't think cache is the cause of the original issue. Running a cache flush using the wp cli doesn't work as it actually manages to hit the same errors from application.php when flushing. Never mind that caching is disabled in the first place anyway. This isn't a browser cache issue here either as a new incognito chrome instance and hard refreshing makes no difference.
This is a long read so sorry about that, I hope I was clear enough. I'm quite confused as to how this is happening and any help or tips for debugging this would be great. Maybe I've missed something very obvious as it appears that using dotenv for my wp config thoroughly breaks everything in ways that I've never seen before. Worst comes to worst I'll go back to hardcoding the wp-config file
Update:
I was mistake about deleting the application.php file not stopping the related bugs. It stops the require bugs but I get others instead. If I just delete the contents of application.php then I have no difference.
Something is majorly wrong, if anyone simply has any debugging advice it would be highly appreciated
Files:
public/help-advice/wp-config.php
config/application.php
wordpress installation is in public/help-advice/wp
non composer generated wp files are in public/help-advice/app or
public/help-advice
Pasted code if you can't use pastebin:
This is the config.php
<?php
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the
* installation. You don't have to use the web site, you can
* copy this file to "wp-config.php" and fill in the values.
*
* This file contains the following configurations:
*
* * MySQL settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* #link https://codex.wordpress.org/Editing_wp-config.php
*
* #package WordPress
*/
/*
* Caching
*/
define('WP_CACHE', true);
define('LARAVEL_PATH', dirname(__FILE__) . '/../..'); // Make sure this is pointed to same server
require_once(LARAVEL_PATH . '/vendor/autoload.php');
require_once(LARAVEL_PATH . '/config/application.php');
require_once(ABSPATH . 'wp-settings.php');
This is the application.php
<?php
//This file pulls in data for WP and is included in the wp-config.php file within help-advice
/*
* Base paths
*/
define('APP_ROOT_DIR', dirname(__DIR__));
// $dotenv = new Dotenv\Dotenv(APP_ROOT_DIR);
// $dotenv->load();
// this one above works but causes this file to run twice causing errors, the one below errors
// if (file_exists(APP_ROOT_DIR . '/.env')) {
// Dotenv\Dotenv::load(APP_ROOT_DIR);
// }
define('APP_PUBLIC_DIR', APP_ROOT_DIR . '/public/help-advice');
define('APP_STORAGE_DIR', APP_ROOT_DIR . '/storage');
define('APP_LOG_DIR', APP_STORAGE_DIR . '/logs');
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'redacted');
/** MySQL database username */
define('DB_USER', 'redacted');
/** MySQL database password */
define('DB_PASSWORD', 'redacted');
/** MySQL hostname */
define('DB_HOST', '127.0.0.1');
/** Database Charset to use in creating database tables. */
define('DB_CHARSET', 'utf8mb4');
/** The Database Collate type. Don't change this if in doubt. */
define('DB_COLLATE', '');
/**##+
* Authentication Unique Keys and Salts.
*
* Change these to different unique phrases!
* You can generate these using the {#link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
*
* #since 2.6.0
*/
define('AUTH_KEY', 'redacted');
define('SECURE_AUTH_KEY', 'redacted');
define('LOGGED_IN_KEY', 'redacted');
define('NONCE_KEY', 'redacted');
define('AUTH_SALT', 'redacted');
define('SECURE_AUTH_SALT', 'redacted');
define('LOGGED_IN_SALT', 'redacted');
define('NONCE_SALT', 'redacted');
/*
* Debugging/errors
*/
define('APP_DEBUG', (boolean) getenv('APP_DEBUG'));
// Always log errors
ini_set('log_errors', 1);
ini_set('error_log', APP_LOG_DIR . '/wp_debug.log');
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', false);
define('WP_DEBUG_DISPLAY', APP_DEBUG);
define('SCRIPT_DEBUG', APP_DEBUG);
/*
* URLs
*/
define('WP_SITEURL', 'http://' . $_SERVER['HTTP_HOST'].'/help-advice/wp');
define('WP_HOME', 'http://' . $_SERVER['HTTP_HOST'].'/help-advice');
/*
* Custom Content Directory (/public/help-advice/app)
*/
define('CONTENT_DIR', '/app');
define('WP_CONTENT_DIR', APP_PUBLIC_DIR . CONTENT_DIR);
define('WP_CONTENT_URL', WP_HOME . CONTENT_DIR);
//google analytics
define('GA_PROPERTY_ID',getenv('GA_PROPERTY_ID'));
/**##-*/
/**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';
/* That's all, stop editing! Happy blogging. */
/** Absolute path to the WordPress directory. */
if ( !defined('ABSPATH') )
define('ABSPATH', dirname(__FILE__) . '/../public/help-advice/wp/');
I have no idea why it runs twice or is triggered by the do_action( 'plugins_loaded' ); on the 2nd run.
Since this is a fairly unique build/situation and I don't expect this to be easily fixed or for more than a couple of people to ever need this. So instead of continuing to tear my hair out I added a check to every definition in my application.php to see if it already existed, if so don't re-define. example: if (!defined('WP_DEBUG')) {define('WP_DEBUG', true);}
I was also getting errors from advanced-cache.php missing as a result of the dotenv being added to application.php, no idea why, especially since I never installed or had anything to do with advanced-cache. So I simply disabled this define('WP_CACHE', false);

CakePHP: Change database connection for all models globally based on Configure value

I've 3 copies of my code - on development workstation, a test server and a production server.
Accordingly, I have 3 separate DB configs in the database.php file named dev, test & production.
I'm trying to figure out a way to switch the connection based on a value written to the Configure class. The way I'm trying to accomplish this out is to require a file called instance.php in the last line of bootstrap.php.
require( APP . 'Config/instance.php' );
My instance.php looks like:
<?php
// Configure instance type
Configure::write( 'InstanceType', 'Development' );
//Configure::write( 'InstanceType', 'Test' );
//Configure::write( 'InstanceType', 'Production' );
On the development workstation, the first line will be uncommented. Likewise on Test and Production the 2nd and 3rd lines will remain uncommented.
What I need is a method (perhaps in AppModel) that'll get executed before any other Model loads and will set the correct DB config. using the following code:
// Set DB Config
switch( Configure::read( 'InstanceType' ) ) {
case 'Development':
//default:
$this->useDbConfig = 'default';
break;
case 'Test':
$this->useDbConfig = 'test';
break;
case 'Production':
$this->useDbConfig = 'production';
break;
}
OR (condensed version)
$this->useDbConfig = Configure::read( 'InstanceType' ) == 'Development' ? 'default' : strtolower( Configure::read( 'InstanceType' ) );
I tried to put this into a __construct() method inside AppModel but that started throwing a bunch of errors about missing arguments to __construct.
The outcome is to have:
All models utilize the given DB connection.
Not mess around with the DB specifier individually in each model.
Not having to maintain 3 different copies of database.php on the 3 platforms.
How can I make this work?
Thank you.
You are on the right track. However, the code can't be placed in the construct for AppController.php.
Instead, add it to class DATABASE_CONFIG in /app/Config/database.php:
public function __construct() {
// Set DB Config
switch( Configure::read( 'InstanceType' ) ) {
case 'Development':
//default:
//do nothing, as $this->default holds the right config
break;
case 'Test':
$this->default = $this->test;
break;
case 'Production':
$this->default = $this->production;
break;
}
}
Option 2: Symlinks
A different approach is to create 3 different versions of your database.php file, each holding the right database config for each server, which you maintain with the rest of your code.
/app/Config/database-development.php
/app/Config/database-test.php
/app/Config/database-production.php
These three files are copied over to all of your servers when you update the repositories.
You then create a symbolic link on each server pointing to the right config file:
// Development Server
ln -s app/Config/database-development.php app/Config/database.php
// Test Server
ln -s app/Config/database-test.php app/Config/database.php
// Production Server
ln -s app/Config/database-production.php app/Config/database.php
Make sure this symlink is ignored in .gitignore, so it won't be overwritten (default CakePHP behaviour).
You still have to maintain 3 different database config files, but you do it locally. You can also get rid of Config/instance.php.

Override core Magento DB connection using environment variables

I am trying to use environment variables that are loaded into my Magento application to override the default database connection credentials.
I've managed to 'almost' get this working using the getenv('MY_CUSTOM_VAR'), however I am trying to use this same method to override the database credentials, so this sensitive data can be stored within the htaccess.
e.g
# .htaccess file
SetEnv DB_USERNAME root
SetEnv DB_PASSWORD password123
SetEnv DB_HOST localhost
My App.php (within app/code/local/Mage/Core/Model/App.php - a copy of the one from the core pool)
// my function that overrides the core one
protected function _initBaseConfig()
{
Varien_Profiler::start('mage::app::init::system_config');
$this->_config->loadBase();
/* Read DB connection config from environment variables */
$connection = $this->_config->getNode('global/resources/default_setup/connection');
$connection->setNode('host', getenv('DB_HOST'));
$connection->setNode('username', getenv('DB_USERNAME'));
$connection->setNode('password', getenv('DB_PASSWORD'));
Varien_Profiler::stop('mage::app::init::system_config');
return $this;
}
I want the $connection just created to be the default global database connection used site-wide, this code should appear to do that but if i enter pure random entries for DB_HOST/DB_PASSWORD etc.. it still connects to the database which suggests that it isn't overriding the default database settings configured with the Magento setup.
Any ideas on how to get this $connection to override and become the 'global' database connection?
P.S Apologies in advance if this question is similar to my previous one, the original question was a bit of a general question whereas this one is much more focused to particular section.
Update...
I have open the local.xml within the app/etc directory and set the 'default setup' database connection to inactive (by changing the active node value to 0) and this as expected returns an error. It would appear the overriding function doesn't seem to like to override the initial db connection.. any ideas guys?
For MySQL db you can do next:
Copy file lib/Zend/Db/Adapter/Mysqli.php to app/code/local/Zend/Db/Adapter/Mysqli.php
Open app/code/local/Zend/Db/Adapter/Mysqli.php and find _connect() function.
In this function near line 317 you'll see:
$_isConnected = #mysqli_real_connect(
$this->_connection,
$this->_config['host'],
$this->_config['username'],
$this->_config['password'],
$this->_config['dbname'],
$port
);
Redefine params:
$this->_config['host'] = getenv('DB_HOST');
$this->_config['username'] = getenv('DB_USERNAME');
$this->_config['password'] = getenv('DB_PASSWORD');
$_isConnected = #mysqli_real_connect(
$this->_connection,
$this->_config['host'],
$this->_config['username'],
$this->_config['password'],
$this->_config['dbname'],
$port
);
Clear cache and restart MySQL. For other databases check files in lib/Zend/Db/Adapter folder (DB2.php, Oracle.php, Sqlsrv.php).

Integrate phpgrid with codeigniter

Im trying to integrate phpgrid with my codeigniter program.
I need a few clarifications on how to use the library function.
I have added the phpgrid files to the application/libraries path and I have loaded the libray using $this->load->library('phpGrid');
Below is the code in the phpgrid conf.php file.
<?php
// mysql example
define('DB_HOSTNAME','localhost'); // database host name
define('DB_USERNAME', 'admin'); // database user name
define('DB_PASSWORD', 'pop3'); // database password
define('DB_NAME', xtra); // database name
define('DB_TYPE', 'mysql'); // database type
define('DB_CHARSET','utf8'); // ex: utf8(for mysql),AL32UTF8 (for oracle), leave blank to use the default charset
define('SERVER_ROOT', '/grid');
/******** DO NOT MODIFY ***********/
require_once('phpGrid.php');
/**********************************/
?>
Can someone please help me how can I reference the define and require_once files to my library path?
Or is there any other way that I can include the phpgrid files to my CI project?
I've just looked at the PHPgrid source files and it appears that they are encrypted so you are limited as to how much you can fully "integrate" with Codeigniters MVC framework.
To work with an external library like this, here's what I would generally do:
Store a separate config.php file (that looks like the PHPgrid one) that defines the db connection constants in the root directory.
Then require this in your Codeigniter config/database.php file and use the constants to set the Codeigniter settings as well. So your Codeigniter database.php would look like:
require_once('config.php');
$db['default']['hostname'] = DB_HOSTNAME;
$db['default']['username'] = DB_USERNAME;
$db['default']['password'] = DB_PASSWORD;
$db['default']['database'] = DB_NAME;
You don't want to be storing database connection details all over the place.
Then include config.php at the top of your phpgrid/conf.php file, and use the constants to fill the details the same way, obviously fill out the other phpgrid constants as well.
Put all the PHPgrid files in a subdir of application/libraries. Now create a new file in your application/libraries file that is called something like ci_phpgrid.php and in it create a new class like below:
<?php
require_once('phpgrid/conf.php');
class CI_phpgrid {
public function example_method($val = '')
{
$dg = new C_DataGrid("SELECT * FROM Orders", $val, "Orders");
return $dg;
}
}
You can now use this to communicate with php grid, leaving the original files intact.
In you controller you would just do something like:
$this->load->library('ci_phpgrid');
$data['phpgrid'] = $this->ci_phpgrid->example_method(3)
$this->load->view('home_page',$data);
And then in the view you can display the table using:
$phpgrid->display()
As I mentioned, I've not used PHPgrid and you'll need to include all the relevant JS for sorting etc but this is generally the way you want to approach external libraries in CI.

How can I refer to different environment's inside Symfony's ProjectConfiguration.class.php

I have a staging and development environment on the same machine. I would like to configure a different memcached port in ProjectConfiguration.class.php depending on my environment. I imagine its not safe to use $SERVER['HTTP_HOST'] inside of the ProjectConfiguration file because that won't account for tasks run from the command line.
What would be the best way to accomplish what this bit of code intends:
public function configureDoctrine(Doctrine_Manager $manager) {
if ($_SERVER['HTTP_HOST'] == 'dev.example.com') {
$memcached_port = 11211;
} else {
$memcached_port = 11212;
}
$servers = array(
'host' => '127.0.0.1',
'port' => $memcached_port,
'persistent' => true);
}
There are 2 ways to accomplish this.
To start off, you should be setting your environment separately in index.php. What we usually do is set it to prod, and then check in the file, and manually change it to dev on our dev servers and local machines in order to make deployment easier. I also recommend put this file in .gitignore so it never accidentally gets changed.
Use the app.yml configuration file. In your app.yml config, set up a key called 'memcache_port' and set the value separately for each environment. The yaml file would look something like this:
prod:
memcached_port: 11211
dev:
memcached_port: 11212
Then to access this value in the code you would say:
$memcached_port = sfConfig::get('app_memcached_port');
Get the environment name manually through the symfony context
$env = sfContext::getInstance()->getConfiguration()->getEnvironment();
if($env == 'dev') {
$memcached_port = 11211;
} else if($env == 'prod') {
$memcached_port = 11212;
}
I personally prefer option 1, but it's totally up to you how you want to handle this.
Note: When I refer to the environment, I am referring to the symfony environment defined in the index.php file.
Hope this helps.
I use http host in the web controller to detect the environment and pass it to the project configuration instance, as per here:
<?php
require_once(dirname(__FILE__).'/../config/ProjectConfiguration.class.php');
$env = ProjectConfiguration::getEnvironment();
if (!in_array($env, array('dev'))) throw new Exception('Not Allowed');
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', $env, true);
sfContext::createInstance($configuration)->dispatch();
This is from a frontend_dev.php - so it also ensures that you can't access the dev controller anywhere but dev.
My project configuration class contains the referenced method, which does the work:
public function getEnvironment() {
if ($_SERVER ['HTTP_HOST'] == 'dev.example.com') {
return 'dev';
} else {
return 'prod';
}
}
As you rightly say, there are also command line tasks to consider - but almost all symfony tasks will take --env=xxx arguments. I use those. They all default to dev anyway, which is where I do most of my work, so its not as arduous as it sounds.
I'd then use if (sfConfig::get('sf_environment') == 'dev') in your if statement, rather than the HTTP_HOST directly, which will work from both cmd line and web.

Categories