├── version.txt ├── .gitignore ├── .github ├── CODEOWNERS └── workflows │ └── release-please.yml ├── wp-config.php ├── wp-config.local.php ├── wp-config.staging.php ├── wp-config.production.php ├── wp-config.development.php ├── CHANGELOG.md ├── wp-config.env.php ├── SECURITY.md ├── CONTRIBUTING.md ├── wp-config.default.php ├── CODE_OF_CONDUCT.md ├── wp-config.load.php └── README.md /version.txt: -------------------------------------------------------------------------------- 1 | 2.0.2 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | .DS_Store 3 | .DS_Store? 4 | ._* 5 | .Spotlight-V100 6 | .Trashes 7 | ehthumbs.db 8 | Thumbs.db 9 | /nbproject 10 | /.project 11 | /.settings 12 | /.buildpath 13 | .idea 14 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # See https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners 2 | 3 | # These owners will be the default owners for everything in 4 | # the repo. Unless a later match takes precedence 5 | * @simonrjones -------------------------------------------------------------------------------- /wp-config.php: -------------------------------------------------------------------------------- 1 | 14 | * domain => The domain name 15 | * This can also be an array of multiple domains 16 | * You can also use a wildcard * to indicate all sub-domains at a domain, which is useful when using 17 | * WordPress Multisite. If you use wildcards, set the domain should to a single string, not an array 18 | * path => If WordPress is installed to a sub-folder set it here 19 | * ssl => Whether SSL should be used on this domain 20 | * 21 | * If you don't use any environments, remove them 22 | */ 23 | $env = [ 24 | 'production' => [ 25 | 'domain' => 'domain.com', 26 | 'path' => '', 27 | 'ssl' => false, 28 | ], 29 | 'staging' => [ 30 | 'domain' => 'staging.domain.com', 31 | 'path' => '', 32 | 'ssl' => false, 33 | ], 34 | 'development' => [ 35 | 'domain' => 'domain.local', 36 | 'path' => '', 37 | 'ssl' => false, 38 | ], 39 | ]; 40 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Issues 2 | 3 | If you think that you have found a security issue in Strata, please don't use the issue tracker and don't publish it 4 | publicly. Send urgent or sensitive reports directly to [security@studio24.net](mailto:security@studio24.net) 5 | 6 | ## Resolving Process 7 | 8 | If you submit a report, here’s what will happen: 9 | 10 | * We’ll acknowledge your report. 11 | * We'll confirm the vulnerability and ask any follow up questions. 12 | * The team will work on a fix. 13 | * Once the issue is resolved, we’ll post a security update along with thanks and credit for the discovery. 14 | 15 | Once a patch has been created we will: 16 | 17 | * Publish a release for all maintained versions of Strata. 18 | * Update the public [security advisories database](https://github.com/FriendsOfPHP/security-advisories). 19 | 20 | ### Note 21 | 22 | * While we are working on a patch, please do not reveal the issue publicly. 23 | * The resolution takes anywhere between a couple of days to a month depending on its complexity. 24 | 25 | ## Attribution 26 | 27 | This Security Issues policy is inspired by 28 | [Basecamp security response](https://github.com/basecamp/policies/blob/master/security/vulnerability-response.md) and 29 | [Symfony security issues](https://symfony.com/doc/master/contributing/code/security.html). 30 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We welcome contributions to WordPress Multi-Environment Config. 4 | 5 | All contributors must agree to our [Code of Conduct](CODE_OF_CONDUCT.md). 6 | 7 | WordPress Multi-Environment Config is released under the MIT license and is copyright Studio 24 Ltd. All contributors must accept these license and copyright conditions. 8 | 9 | ## Issues 10 | 11 | Please submit any issues to the [issues log](https://github.com/studio24/wordpress-multi-env-config/issues). You are 12 | welcome to fork the project and create suggested fixes. 13 | 14 | ## Pull Requests 15 | 16 | All contributions must be made on a branch and must be merged into the main branch via Pull Requests. 17 | 18 | All Pull Requests need at least one approval from the Studio 24 development team. 19 | 20 | ## Creating new releases 21 | 22 | This repo uses [Release Please](https://github.com/marketplace/actions/release-please-action) to automatically create releases, based on [semantic versioning](https://semver.org/). 23 | 24 | To create a new release use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) in your commit message. This will automatically create a Release PR, which is kept up to date with further commits and you can merge it to create the new release when you are ready. 25 | 26 | Use the following keywords in your commits: 27 | 28 | * `fix:` this indicates a bug fix and creates a new patch version (e.g. 1.0.1). 29 | * `feat:` this indicates a new feature and creates a new minor version (e.g. 1.1.0). 30 | * To create a new major version (e.g. 2.0.0) either append an exclamation mark to `fix!:` or `feat!:` or add a footer of `BREAKING CHANGE:` with details of what breaking changes there are. 31 | 32 | If the action fails to run you can view the action and select `Re-run all jobs` to re-run it. 33 | -------------------------------------------------------------------------------- /wp-config.default.php: -------------------------------------------------------------------------------- 1 | 8 | */ 9 | 10 | function s24_load_environment_config() { 11 | 12 | /** 13 | * Setup environment 14 | */ 15 | // Set env if set via environment variable 16 | if (getenv('WP_ENV') !== false) { 17 | define('WP_ENV', preg_replace('/[^a-z]/', '', getenv('WP_ENV'))); 18 | } 19 | 20 | // Set env via --env= argument if running via WP-CLI 21 | if (!defined('WP_ENV') && PHP_SAPI == "cli" && defined('WP_CLI_ROOT')) { 22 | 23 | // We need to set $argv as global to be able to access it 24 | global $argv; 25 | 26 | if (isset($argv)) { 27 | foreach ($argv as $arg) { 28 | if (preg_match('/--env=(.+)/', $arg, $m)) { 29 | define('WP_ENV', preg_replace('/[^a-z]/', '', $m[1])); 30 | break; 31 | } 32 | } 33 | } 34 | 35 | // Also support via .env file in config directory 36 | if (!defined('WP_ENV')) { 37 | if (file_exists(__DIR__ . '/.env')) { 38 | $environment = trim(file_get_contents(__DIR__ . '/.env')); 39 | define('WP_ENV', preg_replace('/[^a-z]/', '', $environment)); 40 | } 41 | } 42 | } 43 | 44 | // Define ENV from hostname 45 | if (!defined('WP_ENV')) { 46 | if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) { 47 | $hostname = strtolower(trim(strip_tags($_SERVER['HTTP_X_FORWARDED_HOST']))); 48 | } elseif (!empty($_SERVER['HTTP_HOST'])) { 49 | $hostname = strtolower(trim(strip_tags($_SERVER['HTTP_HOST']))); 50 | } else { 51 | throw new Exception("Hostname is not available."); 52 | } 53 | } 54 | 55 | // Load environments 56 | require __DIR__ . '/wp-config.env.php'; 57 | 58 | if (!isset($env) || !is_array($env)) { 59 | throw new Exception('$env array not detected, you must set this in wp-config.env.php'); 60 | } 61 | 62 | // Set environment constants 63 | if (defined('WP_ENV')) { 64 | if (isset($env[WP_ENV])) { 65 | define('WP_ENV_DOMAIN', $env[WP_ENV]['domain']); 66 | define('WP_ENV_PATH', trim($env[WP_ENV]['path'], '/')); 67 | define('WP_ENV_SSL', (bool) $env[WP_ENV]['ssl']); 68 | } 69 | 70 | } else { 71 | 72 | // Detect environment from hostname 73 | foreach ($env as $environment => $env_vars) { 74 | if (!isset($env_vars['domain'])) { 75 | throw new Exception('You must set the domain value in your environment array, see wp-config.env.php'); 76 | } 77 | $domain = $env_vars['domain']; 78 | 79 | $wildcard = (is_string($domain) && strpos($domain, '*') !== false) ? true : false; 80 | if ($wildcard) { 81 | $match = '/' . str_replace('\*', '([^.]+)', preg_quote($domain, '/')) . '/'; 82 | if (preg_match($match, $hostname, $m)) { 83 | if (!defined('WP_ENV')) { 84 | define('WP_ENV', preg_replace('/[^a-z]/', '', $environment)); 85 | } 86 | define('WP_ENV_DOMAIN', str_replace('*', $m[1], $domain)); 87 | if (isset($env_vars['ssl'])) { 88 | define('WP_ENV_SSL', (bool)$env_vars['ssl']); 89 | } else { 90 | define('WP_ENV_SSL', false); 91 | } 92 | if (isset($env_vars['path'])) { 93 | define('WP_ENV_PATH', trim($env_vars['path'], '/')); 94 | } 95 | 96 | /** 97 | * Define WordPress Site URLs 98 | */ 99 | $protocol = (WP_ENV_SSL) ? 'https://' : 'http://'; 100 | $path = (defined('WP_ENV_PATH')) ? '/' . trim(WP_ENV_PATH, '/') : ''; 101 | 102 | if (!defined('WP_SITEURL')) { 103 | define('WP_SITEURL', $protocol . trim(WP_ENV_DOMAIN, '/') . $path); 104 | } 105 | if (!defined('WP_HOME')) { 106 | define('WP_HOME', $protocol . trim(WP_ENV_DOMAIN, '/') . $path); 107 | } 108 | break; 109 | } 110 | } 111 | if (!is_array($domain)) { 112 | $domain = [$domain]; 113 | } 114 | foreach ($domain as $domain_name) { 115 | if ($hostname === $domain_name) { 116 | if (!defined('WP_ENV')) { 117 | define('WP_ENV', preg_replace('/[^a-z]/', '', $environment)); 118 | } 119 | define('WP_ENV_DOMAIN', $domain_name); 120 | if (isset($env_vars['ssl'])) { 121 | define('WP_ENV_SSL', (bool)$env_vars['ssl']); 122 | } else { 123 | define('WP_ENV_SSL', false); 124 | } 125 | if (isset($env_vars['path'])) { 126 | define('WP_ENV_PATH', trim($env_vars['path'], '/')); 127 | } 128 | 129 | /** 130 | * Define WordPress Site URLs 131 | */ 132 | $protocol = (WP_ENV_SSL) ? 'https://' : 'http://'; 133 | $path = (defined('WP_ENV_PATH')) ? '/' . trim(WP_ENV_PATH, '/') : ''; 134 | 135 | if (!defined('WP_SITEURL')) { 136 | define('WP_SITEURL', $protocol . trim(WP_ENV_DOMAIN, '/') . $path); 137 | } 138 | if (!defined('WP_HOME')) { 139 | define('WP_HOME', $protocol . trim(WP_ENV_DOMAIN, '/') . $path); 140 | } 141 | break; 142 | } 143 | } 144 | } 145 | } 146 | 147 | if (!defined('WP_ENV')) { 148 | throw new Exception("Cannot determine current environment"); 149 | } 150 | if (!defined('WP_ENV_DOMAIN')) { 151 | throw new Exception("Cannot determine current environment domain, make sure this is set in wp-config.env.php"); 152 | } 153 | if (!defined('WP_ENV_SSL')) { 154 | define('WP_ENV_SSL', false); 155 | } 156 | if (WP_ENV_SSL && (!defined('FORCE_SSL_ADMIN'))) { 157 | define('FORCE_SSL_ADMIN', true); 158 | } 159 | 160 | // Define W3 Total Cache hostname 161 | if (defined('WP_CACHE')) { 162 | define('COOKIE_DOMAIN', $hostname); 163 | } 164 | 165 | } 166 | s24_load_environment_config(); 167 | 168 | 169 | /** 170 | * Load config 171 | */ 172 | 173 | // 1st - Load default config 174 | require __DIR__ . '/wp-config.default.php'; 175 | 176 | // 2nd - Load config file for current environment 177 | require __DIR__ . '/wp-config.' . WP_ENV . '.php'; 178 | 179 | // 3rd - Load local config file with any sensitive settings 180 | if (file_exists( __DIR__ . '/wp-config.local.php')) { 181 | require __DIR__ . '/wp-config.local.php'; 182 | } 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Studio 24 WordPress Multi-Environment Config 2 | 3 | This repository contains Studio 24's standard config setup for WordPress, which 4 | loads different config based on current environment. This allows you to have different 5 | site configuration (e.g. debug mode) for different environments (e.g. production and staging). 6 | 7 | Credit is due to FocusLabs [EE Master Config](https://github.com/focuslabllc/ee-master-config) 8 | who gave me the inspiration for the organisation of the config files. 9 | 10 | Please note the current version is v2, if you need to use the older v1 version [please see the v1 release](https://github.com/studio24/wordpress-multi-env-config/releases/tag/v1.0.2). 11 | 12 | ## Contributing 13 | 14 | WordPress Multi-Environment Config is an Open Source project. Find out more about how to [contribute](CONTRIBUTING.md). 15 | 16 | ## Security Issues 17 | 18 | If you discover a security vulnerability within WordPress Multi-Environment Config, please follow our [disclosure procedure](SECURITY.md). 19 | 20 | ## How it works 21 | 22 | The system detects what environment the current website is in and loads the relevant config file for that environment. 23 | 24 | By default the environment is defined by the hostname, though you can also set this as an environment variable. 25 | 26 | Config files are then loaded according to the current environment. There is support for loading a local config file 27 | for sensitive data, which is intended to not be committed to version control. 28 | 29 | ### Config files 30 | 31 | Up to three different config files are loaded: 32 | 33 | 1. **Default configuration** (in `wp-config.default.php`, e.g. shared settings such as `$table_prefix`) 34 | 2. **Environment configuration** (in `wp-config.{ENVIRONMENT}.php`, e.g. any setting specific to the environment such as database name or debug mode) 35 | 3. **Optional local settings** (in `wp-config.local.php`, e.g. any sensitive settings you do not want to commit to version control, e.g. database password) 36 | 37 | ### Environment values 38 | 39 | By default, environment values are: 40 | 41 | * `production` (live website) 42 | * `staging` (test website for client review) 43 | * `development` (local development copy of the website) 44 | 45 | You can add other environment values by adding these to the `wp-config.env.php` file. 46 | 47 | ## Setting the environment 48 | 49 | The current environment is detected in one of three ways: 50 | 51 | ### Environment variable 52 | 53 | You can set an environment variable called `WP_ENV` to set which environment the website uses in your webserver configuration. 54 | 55 | This is commonly done via Apache in your virtual host declaration: 56 | 57 | SetEnv WP_ENV production 58 | 59 | If you don't use Apache consult your webserver documentation. 60 | 61 | ### Server hostname 62 | 63 | The current environment can also be detected from matching the hostname with the domain setup in `wp-config.env.php`. 64 | 65 | ### WP-CLI 66 | If you're using [WP-CLI](http://wp-cli.org/) you can specify your environment using an '.env' file. 67 | 68 | You need to create a file called `.env` and simply write the current environment in this file as a string. 69 | 70 | Example: 71 | 72 | ``` 73 | development 74 | ``` 75 | This file needs to be placed among the wp-config.*.php files (this applies if you keep these files in a sub-folder and the wp-config.php file in the web root). 76 | 77 | It is recommended you do not add this file to version control. 78 | 79 | ## The wp-config.env.php file 80 | 81 | You need to edit the `wp-config.env.php` file to define some settings related to the current environment URL. This needs to 82 | be set regardless of which method is used to set the environment, since all methods set the WordPress URL via settings 83 | contained in this file. 84 | 85 | This file contains a simple array, made up of: 86 | 87 | ``` 88 | environment names => 89 | domain => The domain name. 90 | This can also be an array of multiple domains. 91 | You can also use a wildcard * to indicate all sub-domains at a domain, which is useful when using 92 | WordPress Multisite. If you use wildcards, set the domain should to a single string, not an array. 93 | path => If WordPress is installed to a sub-folder set it here. 94 | ssl => Whether SSL should be used on this domain. If set, this also sets FORCE_SSL_ADMIN to true. 95 | ``` 96 | 97 | Example usage: 98 | 99 | ``` 100 | $env = [ 101 | 'production' => [ 102 | 'domain' => 'domain.com', 103 | 'path' => '', 104 | 'ssl' => false, 105 | ], 106 | 'staging' => [ 107 | 'domain' => 'staging.domain.com', 108 | 'path' => '', 109 | 'ssl' => false, 110 | ], 111 | 'development' => [ 112 | 'domain' => 'domain.local', 113 | 'path' => '', 114 | 'ssl' => false, 115 | ], 116 | ]; 117 | ``` 118 | 119 | If you use localhost for your local test website, just set the development hostname case to `localhost` rather than `domain.local`. 120 | 121 | Example usage when setting a sub-folder, and also serving the live site via SSL: 122 | 123 | ``` 124 | 'production' => [ 125 | 'domain' => 'domain.com', 126 | 'path' => 'blog', 127 | 'ssl' => true, 128 | ], 129 | ``` 130 | 131 | Example usage for using more than one domain for an environment. 132 | 133 | ``` 134 | 'production' => [ 135 | 'domain' => ['domain.com', 'domain2.com'], 136 | 'path' => '', 137 | 'ssl' => false, 138 | ], 139 | ``` 140 | 141 | Example usage when using a wildcard for WordPress multi-site. 142 | 143 | ``` 144 | 'production' => [ 145 | 'domain' => '*.domain.com', 146 | 'path' => '', 147 | 'ssl' => false, 148 | ], 149 | ``` 150 | 151 | ## Installing 152 | 153 | Please note this requires PHP5.4 or above. You should really be on PHP5.6 at a minimum! 154 | 155 | 1. Download the required files via [wordpress-multi-env-config.zip](https://github.com/studio24/wordpress-multi-env-config/releases/latest/download/wordpress-multi-env-config.zip) 156 | 2. First make a backup of your existing `wp-config.php` file. 157 | 3. Copy the following files from this repository to your WordPress installation: 158 | 159 | ``` 160 | wp-config.default.php 161 | wp-config.env.php 162 | wp-config.php 163 | wp-config.load.php 164 | ``` 165 | 166 | 3. Set the correct environments you wish to support via the file `wp-config.env.php`, see the documentation above. 167 | 4. Create one `wp-config.{environment}.php` file for each environment. You can use the sample files provided in this repository: 168 | 169 | ``` 170 | wp-config.development.php 171 | wp-config.production.php 172 | wp-config.staging.php 173 | wp-config.local.php 174 | ``` 175 | 176 | 5. Review your backup `wp-config.php` file and copy config settings to either the default config file or the environment config files as appropriate. It is suggested to: 177 | * If the setting is the same across all environments, add to `wp-config.default.php` 178 | * If the setting is unique to one environment, add to `wp-config.{environment}.php` 179 | * If the setting is sensitive (e.g. database password) add to `wp-config.local.php` 180 | 6. Remember to update the authentication unique keys and salts in `wp-config.default.php` 181 | 7. If you use version control exclude `wp-config.local.php`, an example below for Git: 182 | 183 | ``` 184 | # .gitignore 185 | wp-config.local.php 186 | ``` 187 | 188 | You should now be able to load up the website in each different environment and everything should work just fine! It should now be safe to delete your backup *wp-config.php* file. 189 | 190 | ## Moving your config files outside of the document root 191 | 192 | If you want to store your config files outside of the document root for additional security, this is very easy. 193 | 194 | Simply move all the config files except for `wp-config.php` itself into another folder (which can be outside the doc root). 195 | Next amend the require path for `wp-config.load.php` in `wp-config.php` to point to the new location and everything will work just fine! 196 | 197 | Example directory structure: 198 | 199 | ``` 200 | config/ 201 | wp-config.default.php (Config folder outside of doc root) 202 | wp-config.development.php 203 | wp-config.env.php 204 | wp-config.load.php 205 | wp-config.local.php 206 | wp-config.production.php 207 | wp-config.staging.php 208 | web/ 209 | wp-config.php (Your website doc root, where WordPress is installed) 210 | ``` 211 | 212 | Example `wp-config.php` 213 | 214 | ``` 215 | /** Load the Studio 24 WordPress Multi-Environment Config. */ 216 | require_once(ABSPATH . '../config/wp-config.load.php'); 217 | ``` --------------------------------------------------------------------------------