Jump to content

Maintain separate configs for live/dev like a boss :)


bernhard
 Share

Recommended Posts

UPDATE 2022

Simple and powerful - put this at the end of your config.php file:

$localConfig = __DIR__ . "/config-local.php";
if (is_file($localConfig)) include $localConfig;

Then put all local development settings into config-local.php:

<?php

namespace ProcessWire;

/** @var Config $config */
$config->debug = true;
$config->advanced = true;
$config->dbName = 'db';
$config->dbUser = 'db';
$config->dbPass = 'db';
$config->dbHost = 'db';
$config->userAuthSalt = '1234';
$config->tableSalt = '1234';
$config->httpHosts = ['xyz.ddev.site'];

// this prevents logout when switching between
// desktop and mobile in chrome devtools
$config->sessionFingerprint = false;

// RockFrontend
$config->livereload = 1;

// RockMigrations
// $config->filesOnDemand = 'https://your-live.site/';
$config->rockmigrations = [
  'syncSnippets' => true,
];

// tracy config for ddev development
$config->tracy = [
  'outputMode' => 'development',
  'guestForceDevelopmentLocal' => true,
  'forceIsLocal' => true,
  'localRootPath' => '/Users/xyz/code/yourproject/',
  'numLogEntries' => 100, // for RockMigrations
];

$config->rockpagebuilder = [
  "createView" => "latte",
];

--------------------

Have you ever come across the situation where you needed to change your config.php file for local development? Have you ever come across this warning?

5aaa5dc1bd4d7_2018-03-1512_48_50-EditPage_Homehrd_w4y1.baumrock_com.png.36e7826396df488c67d6a672c7279712.png

Say goodbye to this little annoying tasks :)

Some of you might know that you can have a separate config-dev.php file for local development. This is nice, but also needs some setup (for example excluding this file for git upload) and there is still some danger of uploading this file to a place where it should not be... The other option was to keep the database settings for your live and dev server in sync - also not easy and maybe not the best...

My solution is as simple, fast and I think it should be even safer than the other options. Just put these lines at the end of your config.php file:

if(strpos($config->paths->root, 'C:/www/') === 0) {
  $config->dbUser = 'root';
  $config->dbPass = '';
  $config->httpHosts = [$_SERVER[HTTP_HOST]];
  $config->debug = true;
}
elseif(strpos($config->paths->root, '/var/www/vhosts/') === 0) {
  $config->httpHosts = [];
  $config->dbUser = 'XXX';
  $config->dbPass = 'XXX';
  $config->debug = false;
  
  /**
   * Redirect to HTTPS
   * ATTENTION: This will NOT work with ProCache enabled! Use .htaccess instead
   */
  if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == "off"){
    $redirect = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    header('HTTP/1.1 301 Moved Permanently');
    header('Location: ' . $redirect);
    exit();
  }
}
else die('wrong config.php');

Having a separate config-dev.php file also makes it necessary to keep both files in sync. That was my first idea: To include the original config and then overwrite settings. But it's just simpler the other way round: Using the default config.php file and overwriting settings there.

  • Like 7
  • Thanks 1
Link to comment
Share on other sites

Hi everybody ?

Just realized that redirecting to HTTPS does not work when ProCache is enabled (of course it does not work, but I didn't think of it...). The problem with redirecting via the default .htaccess rule is that it will also redirect to https on your dev environment. But you can easily exclude any domains from this redirect rule like this:

  # -----------------------------------------------------------------------------------------------
  # 9. If you only want to allow HTTPS, uncomment the RewriteCond and RewriteRule lines below.
  # -----------------------------------------------------------------------------------------------
  RewriteCond %{HTTPS} off
  RewriteCond %{HTTP_HOST} !.*\.test$ [NC]
  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Maybe that helps someone else as well.

  • Like 2
Link to comment
Share on other sites

Another possibility is to use different names for htaccess files for local development and for live servers.

In your local httpd.conf you can set for AccessFilename something like

AccessFileName .htaccess.test

or

AccessFileName .htaccess.local

where you can define different settings for local development by keeping the .htaccess file untouched for online server settings.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

It only loads the defined .htaccess.local files. The regular .htaccess files are completly ignored. 

Yes, you may need to copy all locally required parts from regular to local AccessFiles. But I locally work with the pw distri htaccess file. I don‘t want to have settings included for caching or for ProCache. Also rewrites for different domain names to one final domain etc. only stays in the online file. Nothing of that is needed or explicitly must be avoided locally. 

Maintaining the .htaccess for online deploy exclusivly and add .htaccess.local to the .gitignore. ?

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Hi @bernhard

Here is my solution which uses the server name instead of a directory structure. So you are safe if you are changing paths or the OS where paths are different.

$base_url = $_SERVER['SERVER_NAME'];
$config->base_url = $base_url;
switch ($base_url) {
  case "p-jentschura.localhost":
  case "p-jentschura.acemanpc":
  case "p-info.localhost":
  case "p-info.acemanpc":
    $config->dbHost = 'localhost';
    $config->dbName = 'dbname';
    $config->dbUser = 'root';
    $config->dbPass = 'dbpass';
    $config->dbPort = '3306';
    $config->debug = false;
    $config->httpHosts = array(
      'p-jentschura.localhost',
      'p-jentschura.acemanpc',
      '192.168.178.16',
      '192.168.178.23',
      'localhost.p-jentschura'
    );
    break;
  default:
    // LIVE CONFIG
    $config->dbHost = '127.0.0.1';
    $config->dbName = 'dbname';
    $config->dbUser = 'dbuser';
    $config->dbPass = 'dbpass';
    $config->dbPort = '3306';
    $config->debug = false;
    $config->httpHosts = array(
      'p-jentschura.com',
      'www.p-jentschura.com',
      'p-jentschura.de',
      'www.p-jentschura.de',
      'p-jentschura.nl',
      'www.p-jentschura.nl',
      'p-jentschura.shop'
    );
}

 

  • Like 2
Link to comment
Share on other sites

Thx for sharing! I wonder why you don't use dev as default and only list the live server name as one case statement. Then you would not need to list all dev domains and could even create new ones without changing the config ?

Link to comment
Share on other sites

LOL. This is because I have far more domains on the live server and didn't want to write them all. Most of my dev server settings could also be omitted; I only use one dev domain ATM.

Link to comment
Share on other sites

Nice thinking @bernhard!

I have one config.php with reads a config.ini file using "parse_ini_file" which is one level up from /public/. No passwords in my Version Control and the same config file for all installs. 

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

  • 1 month later...

I've now came up with this setup which works really well for me:

/site/config.php (can safely be committed to GIT)

<?php
if(!defined("PROCESSWIRE")) die();

/*** SITE CONFIG *************************************************************************/

$config->debug = false;
$config->prependTemplateFile = '_init.php';
$config->appendTemplateFile = '_main.php';

/*** INSTALLER CONFIG ********************************************************************/

$config->dbHost = 'localhost';
$config->dbPort = '3306';
$config->chmodDir = '0755'; // permission for directories created by ProcessWire
$config->chmodFile = '0644'; // permission for files created by ProcessWire 
$config->timezone = 'Europe/Vienna';

// include config file from outside of the repo
include('../config.php');

../config.php DEV (one file for all laragon hosted sites on my local machine)

<?php
$config->dbUser = 'root';
$config->dbPass = '';
$config->httpHosts = [$_SERVER['HTTP_HOST']];
$config->debug = true;

switch($_SERVER['SERVER_NAME']) {
  case 'www.XXX.test':
    $config->dbName = 'XXX';
    $config->userAuthSalt = 'XXX';
    $config->installed = 123456789;
    break;
  [...]
}

../config.php LIVE (one file for each vhost)

<?php
$config->dbName = 'XXX';
$config->dbUser = 'XXX';
$config->dbPass = 'XXX';
$config->httpHosts = ['www.XXX.com'];
$config->userAuthSalt = 'XXX';
$config->installed = 123456789;
setlocale(LC_ALL,'en_US.UTF-8');

Thx for that ideas @arjen and @jens.martsch!

  • Like 2
  • Thanks 1
Link to comment
Share on other sites

In regards to dev vs live htaccess, you can also use htaccess's "if" conditional for blocks of code you want to selectively enable/disable depending on the site being loaded.  By doing so, you don't need two separate htaccess files to maintain.

When using ProCache combined with it's htaccess recommendations at the top, I wrap it inside a conditional like this:

#################################################################################################
# ProcessWire ProCache recommended .htaccess tweaks for expires and gzip performance settings. 
# PWPC-3.0 Keep this line here for ProCache identification purposes. 
#################################################################################################
<If "%{HTTP_HOST} == 'my-live-website.com'">
  ...the code here...
</If>

 

  • Like 6
  • Thanks 2
Link to comment
Share on other sites

  • 1 month later...

I use an IDE and found it stressful to have to work-around deploying config.php

This is how I found your thread. Thank you!

I integrated your solution into my own workflow and came up with this to share back here:

/site/config.php (like @bernhard except last line) 

// include config file from outside of the repo
$client = 'config-' . end(explode('/',__FILE__, -2));
include("../$client.php");

 The PHP end() function and the minus 2 in the explode(), always gets the URI domain base. (As long as PW keeps it there.)

Since my usual deployment flow is from ABC.dev -> test.mybox.com/ABC -> ABC.com

...in my root folder, safely out of reach (like @bernhard's concept), I then create 3 files each containing config deployment differences.

../config-ABC.dev.php  and  ../config-ABC.php  and ../config-ABC.com.php

For those also using PHPStorm, since these files are located out of project, I would add them in Favorites. (And add it's root folders in Deployment Mappings).

I can then easily make use of IDE features like "Compare Two Files", "Sync with Deployed to", etc. Removing doubts about what config property I may have missed across the board. No more stress.

  • Like 4
Link to comment
Share on other sites

  • 1 month later...

Little tweak that you can add to your config.php on DEV to support bootstrapping of PW (in that case there is no SERVER_NAME set and you need to define it manually):

<?php
if(!defined("PROCESSWIRE")) die();

// make bootstrapping possible
$const = get_defined_constants(true);
$host = $const['user']['host'] ?: $_SERVER['SERVER_NAME'];

$config->dbUser = 'root';
$config->dbPass = '';
$config->httpHosts = [$_SERVER[HTTP_HOST]];
$config->debug = true;

switch($host) {
  case 'www.foo.bar':
    $config->dbName = 'foo';
    $config->userAuthSalt = 'bar';
    break;
}

Then you can execute any script like this:

<?php namespace ProcessWire;
define('host', 'www.foo.bar');
include('index.php');
foreach($pages->find('limit=100') as $p) {
  echo $p->path . "\n";
}

 

Link to comment
Share on other sites

  • 2 years later...
On 5/2/2019 at 6:26 AM, dotnetic said:

Hi @bernhard

Here is my solution which uses the server name instead of a directory structure. So you are safe if you are changing paths or the OS where paths are different.

$base_url = $_SERVER['SERVER_NAME'];
$config->base_url = $base_url;
switch ($base_url) {
  case "p-jentschura.localhost":
  case "p-jentschura.acemanpc":
  case "p-info.localhost":
  case "p-info.acemanpc":
    $config->dbHost = 'localhost';
    $config->dbName = 'dbname';
    $config->dbUser = 'root';
    $config->dbPass = 'dbpass';
    $config->dbPort = '3306';
    $config->debug = false;
    $config->httpHosts = array(
      'p-jentschura.localhost',
      'p-jentschura.acemanpc',
      '192.168.178.16',
      '192.168.178.23',
      'localhost.p-jentschura'
    );
    break;
  default:
    // LIVE CONFIG
    $config->dbHost = '127.0.0.1';
    $config->dbName = 'dbname';
    $config->dbUser = 'dbuser';
    $config->dbPass = 'dbpass';
    $config->dbPort = '3306';
    $config->debug = false;
    $config->httpHosts = array(
      'p-jentschura.com',
      'www.p-jentschura.com',
      'p-jentschura.de',
      'www.p-jentschura.de',
      'p-jentschura.nl',
      'www.p-jentschura.nl',
      'p-jentschura.shop'
    );
}

 

Watch out with this. If you're running a cli script or cron job, the SERVER_NAME will not be set and you may end up running it on your live database instead of your development!

  • Like 1
Link to comment
Share on other sites

  • 2 months later...

Just wanted to share the new approach we've started using for our configs.

Instead of multiple config files, we are using a single shared /site/config.php plus a custom /site/config-env.php file that is unique for every environment the site is deployed (or developed) in. The config-env.php file simply contains one $env variable with a string that defines which environment we're working in (e.g. "production" "staging" "dev") and we include this file within the usual /site/config.php file. The config-env.php file is excluded from git (gitignore) and from our deployment scripts, so it won't be overwritten or touched after it's set up.

Using a file to determine the environment rather than the domain or server variables means that it will work regardless of where or how it's invoked (cli vs browser). Here's the basic format:

Example /site/config-env.php

<?php

$env = 'production'; // A string identifying the current environment

 

Example /site/config.php

<?php

if(!defined("PROCESSWIRE")) die();
setlocale(LC_ALL,'en_US.UTF-8');
ini_set('session.gc_probability', 1);
ini_set("log_errors_max_len",0);

/*** SITE CONFIG *************************************************************************/

/**
 * Set up all your defaults and settings that are shared by all environements below
 */

$config->debug = false;

$config->adminEmail = 'example@example.com';
$config->advanced = true;
$config->templateCompile = false;
$config->uploadTmpDir = dirname(__FILE__) . '/assets/uploads/';

$config->chmodDir = '0775';
$config->chmodFile = '0664';

$config->timezone = 'America/New_York';

$config->defaultAdminTheme = 'AdminThemeUikit';

$config->installed = 1527694349;



/**
 * Environment-specific configs
 */

if(!file_exists(__DIR__ . '/config-env.php')) {
	echo 'No /site/config-env.php file could be located. Please create a /site/config-env.php file with a single $env variable containing a string identifying the current environment. This file should NOT be committed to Git (add it to the /.gitignore file) and must be excluded from deployment.';
	exit;
}
require(__DIR__ . '/config-env.php');
if(!isset($env)) {
	echo 'No $env variable found in /site/config-env.php';
	exit;
}

switch($env) {

	/**
	 * Production
	 */
	case 'production':

		$config->dbHost = '...';
		$config->dbName = '...';
		$config->dbUser = '...';
		$config->dbPass = '...';
		$config->dbPort = '3306';
		$config->dbEngine = 'InnoDB';

		$config->httpHosts = array('...');

		break;

	/**
	 * Staging
	 */
	case 'staging':

		$config->dbHost = '...';
		$config->dbName = '...';
		$config->dbUser = '...';
		$config->dbPass = '...';
		$config->dbPort = '3306';
		$config->dbEngine = 'InnoDB';

		$config->httpHosts = array('...');

		break;

	/**
	 * Developer One's Dev environment
	 */
	case 'dev1':
    
    	$config->debug = true;

		$config->dbHost = '...';
		$config->dbName = '...';
		$config->dbUser = '...';
		$config->dbPass = '...';
		$config->dbPort = '3306';
		$config->dbEngine = 'InnoDB';

		$config->httpHosts = array('...');

		break;

	/**
	 * Developer Two's Dev environment
	 */
	case 'dev2':
    
    	$config->debug = true;

		$config->dbHost = '...';
		$config->dbName = '...';
		$config->dbUser = '...';
		$config->dbPass = '...';
		$config->dbPort = '3306';
		$config->dbEngine = 'InnoDB';

		$config->httpHosts = array('...');

		break;

	default:
		echo "No configuration found in /site/config.php for the environment string \"$env\"";
		exit;
}

If you wanted, you could make the $env variable $config->env instead, which would allow you to access it from anywhere else on the site using wire('config')->env

  • Like 2
Link to comment
Share on other sites

3 minutes ago, bernhard said:

@thetuningspoon doesn't that mean that you put all your database secrets into git?

You don't have to. For example, you could put the username and passwords into the config-env.php instead, and still have the rest of the advantages of sharing the config file. But if you manage your own git server securely then it shouldn't be a problem. I'm interested in hearing your thoughts if you think otherwise, though.

In your example, are you not including the config file in git? I'm confused as to why you would be including both the dev and prod settings in a single config file if you aren't.

Link to comment
Share on other sites

11 hours ago, thetuningspoon said:

You don't have to. For example, you could put the username and passwords into the config-env.php instead, and still have the rest of the advantages of sharing the config file. But if you manage your own git server securely then it shouldn't be a problem. I'm interested in hearing your thoughts if you think otherwise, though.

Don't think so, I was just curious ? 

This is what I'm using nowadays:

<?php namespace ProcessWire;
if(!defined("PROCESSWIRE")) die();
/** @var Config $config */

$config->debug = false;
$config->useFunctionsAPI = false;
$config->useMarkupRegions = false;
$config->usePageClasses = true;
$config->prependTemplateFile = '_init.php';
$config->appendTemplateFile = '_main.php';
$config->defaultAdminTheme = 'AdminThemeUikit';

$config->dbHost = 'localhost';
$config->dbPort = '3306';
$config->chmodDir = '0755';
$config->chmodFile = '0644';
$config->timezone = 'Europe/Vienna';
$config->dbEngine = 'InnoDB';
$config->installed = 123456789;

setlocale(LC_ALL, 'de_AT.utf8', 'de_DE.utf8', 'de_AT', 'de_DE');

// try to load config
foreach([
  "/path/to/your/live/config.php",
  __DIR__."/config-local.php",
  ] as $c) {
  if(!is_file($c)) continue;
  return include($c);
}
die("no config found");

This file is under git, so everything that is the same for all environments comes into config.php; Everything else comes into the live or dev config:

<?php namespace ProcessWire;
/** @var Config $config */
$config->debug = true;
$config->dbName = 'db';
$config->dbUser = 'db';
$config->dbPass = 'db';
$config->dbHost = 'db';
$config->userAuthSalt = 'abcdefg';
$config->httpHosts = ['my.ddev.site'];
$config->filesOnDemand = 'https://staging.mysite.com';

? 

  • Like 1
Link to comment
Share on other sites

I didn't read the whole topic, so hope I guess corretly what is it about. I add my approach to having different configs for each environment. I did it very simple, just used environment variables, so I have one configurable config for all ?

(I am using docker on localhost so it is easy to work with environment variables)

<?php namespace ProcessWire;

/**
 * ProcessWire Configuration File
 *
 * Site-specific configuration for ProcessWire
 *
 * Please see the file /wire/config.php which contains all configuration options you may
 * specify here. Simply copy any of the configuration options from that file and paste
 * them into this file in order to modify them.
 *
 * SECURITY NOTICE
 * In non-dedicated environments, you should lock down the permissions of this file so
 * that it cannot be seen by other users on the system. For more information, please
 * see the config.php section at: https://processwire.com/docs/security/file-permissions/
 *
 * This file is licensed under the MIT license
 * https://processwire.com/about/license/mit/
 *
 * ProcessWire 3.x, Copyright 2019 by Ryan Cramer
 * https://processwire.com
 *
 */

if(!defined("PROCESSWIRE")) die();

/*** SITE CONFIG *************************************************************************/

/** @var Config $config */

/**
 * Allow core API variables to also be accessed as functions?
 *
 * Recommended. This enables API varibles like $pages to also be accessed as pages(),
 * as an example. And so on for most other core variables.
 *
 * Benefits are better type hinting, always in scope, and potentially shorter API calls.
 * See the file /wire/core/FunctionsAPI.php for details on these functions.
 *
 * @var bool
 *
 */
$config->useFunctionsAPI = true;


/*** INSTALLER CONFIG ********************************************************************/


/**
 * Installer: Database Configuration
 *
 */
$config->dbHost = getenv("DB_HOST");
$config->dbName = getenv("DB_NAME");
$config->dbUser = getenv("DB_USER");
$config->dbPass = getenv("DB_PASS");
$config->dbPort = getenv("DB_PORT");
$config->dbEngine = 'InnoDB';

/**
 * Installer: User Authentication Salt
 *
 * This value was randomly generated for your system on 2021/12/14.
 * This should be kept as private as a password and never stored in the database.
 * Must be retained if you migrate your site from one server to another.
 * Do not change this value, or user passwords will no longer work.
 *
 */
$config->userAuthSalt = getenv("USER_AUTH_SALT");

/**
 * Installer: Table Salt (General Purpose)
 *
 * Use this rather than userAuthSalt when a hashing salt is needed for non user
 * authentication purposes. Like with userAuthSalt, you should never change
 * this value or it may break internal system comparisons that use it.
 *
 */
$config->tableSalt = getenv("TABLE_SALT");

/**
 * Installer: File Permission Configuration
 *
 */
$config->chmodDir = '0755'; // permission for directories created by ProcessWire
$config->chmodFile = '0644'; // permission for files created by ProcessWire

/**
 * Installer: Time zone setting
 *
 */
$config->timezone = 'Europe/Prague';

/**
 * Installer: Admin theme
 *
 */
$config->defaultAdminTheme = 'AdminThemeUikit';

/**
 * Installer: Unix timestamp of date/time installed
 *
 * This is used to detect which when certain behaviors must be backwards compatible.
 * Please leave this value as-is.
 *
 */
$config->installed = 1639521804;


/**
 * Installer: HTTP Hosts Whitelist
 *
 */
$config->httpHosts = preg_split('/\s*,\s*/', getenv('HTTP_HOSTS'));


/**
 * Installer: Debug mode?
 *
 * When debug mode is true, errors and exceptions are visible.
 * When false, they are not visible except to superuser and in logs.
 * Should be true for development sites and false for live/production sites.
 *
 */
$config->debug = filter_var(getenv("DEBUG"), FILTER_VALIDATE_BOOLEAN);

$config->advanced = true;

 

  • Like 2
Link to comment
Share on other sites

@dotnetic Hi I use docker containers everywhere. I my localhost develepoment environment I have docker stack yaml defining the containers and also the environment variables (can be included from external file). And in production I use AWS ECS, where you can specify env vars in many ways. If need more details just ask.

  • Like 1
Link to comment
Share on other sites

  • 1 year later...

So, today I learned that you can use a config-dev.php file which takes precedence over the normal config.php file if it's present.

... unfortunately I discovered this by accidentally uploading a config-dev file to a live site. I'd got two config files for live and staging in my project folder and just renamed them when I wanted to update a setting - I'd coincidently named the file for the staging details as config-dev.php and accidently uploaded it to production. Luckily this just meant that the content from the staging site got displayed which wasn't too out of date so hopefully no one noticed....

Now I look into it, I can see that's it been around for ever and there's been lots of chat about it, but hey, I didn't know about it, so thought I'd stick it in this thread just in case anyone else was a daft as me.

 

  • Like 4
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...