Yii Framework Forum: Multiple Environments - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Multiple Environments

#1 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 14 September 2012 - 06:50 AM

*
POPULAR

Are there any plans to add "environment support" to Yii 2.0? Right now one of the first things I do for every project is to add different config files that are merged according to the environment used (development,testing, staging, production etc.) which can easily become messy so I was wondering if this shouldn't be part of the Yii core to make this a bit easier.
I found Laravel's environment support quite interesting. You actually specify an environment in a separate environment config like this

$environments = array(

    'local' => array('http://localhost*', '*.dev'),

);


Meaning everything beginning with "http://localhost*" or ending with ".dev" will use the "local" environment => local config. Any thoughts on this?
7

#2 User is offline   samdark 

  • Having fun
  • Yii
  • Group: Yii Dev Team
  • Posts: 3,353
  • Joined: 17-January 09
  • Location:Russia

Posted 14 September 2012 - 12:21 PM

Yes, there are plans. Ideas are very welcome.
Yii 1.1 Application Development Cookbook

Enjoying Yii? Star us at github: 1.1 and 2.0.
0

#3 User is offline   gbasto 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 70
  • Joined: 28-September 10
  • Location:Portugal

Posted 18 September 2012 - 04:43 AM

Environments is not only a great idea to deal with development/staging/production stages. Is also a great idea to deal, for example, with front/back-end. I already have a project that simulates the environment feature on 1.1.

The file structure:

protected/
config/
main.php
environment/
frontend.php
backend.php
api.php
support.php
modules/
frontend/
backend/
api/
support/

The workflow:

. Loads a main config file.
. Checks the address to see which environment are being requested.
. Loads the corresponding environment module and configuration file, making that module the root one for the request.

With this, I have the following examples:

http://frontend.application.com/ => the frontend default controller
http://backend.appli....com/auth/login => backend/auth/login action
http://support.appli.../tickets/create => support/clients/tickets/create action


Now I'm figuring out the best way to improve this to easily manage with development/staging/production states.

Suggestions are welcome.
0

#4 User is offline   Psih 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 114
  • Joined: 30-June 10

Posted 18 September 2012 - 05:42 AM

In my case I have to deal not only with the environments like local/development/production, but also with the fact that I have a multiple-domain application, so I have a configuration file for each domain.

I'm loading my configuration files based on the domain name - that way I can have any number of environments I like and multi-site application at the same time.

Basically what I do is:

local.example.com usually is my local machine I work on, so the configuration file is named "local.example.com.php"
dev.example.com usually is the testing/development server, so the configuration file is "dev.example.com.php"
example.com is the production server, so the config file is named "example.com" and the "www." part is str_replaced from the domain name so example.com and www.example.com can be used.

Of course I have a global configuration file named "main.php" where most of the configuration options that are identical to all websites and environments reside. The site (environments) options overwrite the global options if necessarily.

My /config folder looks like this:
/config
   /sites
      local.example.com.php
      dev.example.com.php
      example.com.php
      ...
   main.php


If we are working more than one person on the project, we usually prefix our local copies not with "local" but with our nicknames or some other prefix, so it looks like this:
psih.example.com.php
somedude.example.com.php
sodeotherdude.example.com.php

The good thing about that is we can change the configuration files and see the SVN history on those and see who is working on the project too.

My index file is pretty straight forward
// Config base dir
$base = dirname(__FILE__).DIRECTORY_SEPARATOR.'protected'.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR;

// Get the domain
$domain = $_SERVER['SERVER_NAME'];
if (strpos($domain, 'www.') !== false) {
	$domain = str_replace('www.', '', $domain);
}

// You could show here something else, we just use the dev server configuration
// Or even remove this check in production at all - it will save stat operations
if (!file_exists($base.'sites'.DIRECTORY_SEPARATOR.$domain.'.php')) {
	$domain = 'dev.example.com';
}

$main = include($base.'main.php');
$domain_config = include($base.DIRECTORY_SEPARATOR.'sites'.DIRECTORY_SEPARATOR.$domain.'.php');

// Merge configs
$config = CMap::mergeArray($main, $domain_config);

// Create application instance
$app = Yii::createWebApplication($config);


What I don't like in all those examples of making environments in Yii on the internet is that they all rely on some file that is not in source version control system that has to be created and read to identify witch server it is and it takes some syscalls (when you are DDOS'ed with ~10-15k of bots - not a good idea). In my case I don't have to rely on any external file that is not in my source version control system. As a benefit I just made identical domains on my work and home machine for projects and same credentials for MySQL - so I have only one config file and if I change something in it - it is fetched from the source version control system. And because I work rarely at home - I don't have to remember what I have changed and apply that to my home copy by hand and vice-verse.
0

#5 User is offline   gbasto 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 70
  • Joined: 28-September 10
  • Location:Portugal

Posted 18 September 2012 - 09:06 AM

Because I can have multiple domains to the same environment, I don't do that job on index.php, but on what I called AddressManager that is an preloaded CApplicationComponent that checks the domain on a database table. Something like this:

class AddressManager extends CApplicationComponent
{
	const FRONTEND = 1;
	const BACKEND = 2;
	const API = 3;
	const SUPPORT = 4;

	public function init() {
		if (!$address = Yii::app()->cache->get('host.' . $_SERVER['HTTP_HOST'])) {
			$address = Yii::app()->db->createCommand()
				->select('environment')
				->from('addresses')
				->where('address RLIKE :host', array(':host' => $_SERVER['HTTP_HOST']))
				->queryRow();

			Yii::app()->cache->set('host.' . $_SERVER['HTTP_HOST'], $address);
		}

		switch ($address['environment']) {
			case AddressManager::FRONTEND:
				Yii::app()->configure(FrontendModule::config());
				break;
			case AddressManager::BACKEND:
				Yii::app()->configure(BackendModule::config());
				break;
			case AddressManager::API:
				Yii::app()->configure(ApiModule::config());
				break;
			case AddressManager::SUPPORT:
				Yii::app()->configure(SupportModule::config());
                                break;
		}
	}
}


Simple but so far so good.
0

#6 User is offline   wisp 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 192
  • Joined: 04-February 11

Posted 10 October 2012 - 07:37 AM

My idea:

Have the following folder structure:

Posted Image

In main config, create an 'env' property that accepts environments as key/value pairs:

Posted Image

I would suggest at least 4 methods of determining the current environment:
- uri: if one of the supplied strings matches the current domain: $_SERVER['SERVER_NAME']
- docRoot: if one of the supplied strings (partially) matches the current docroot: $_SERVER['DOCUMENT_ROOT']
- envVar: if one of the supplied variables exists as environment var, in this case getenv('myvar')=='prod'
- serverIp: if one of the supplied strings matches the current server IP: $_SERVER['SERVER_ADDR']

On loading page, the environments are looped, if the first doesn't match, go to the next one, etc. You could also provide a key without a value that is automatically matched. For example, have an 'development' at the end of the list, as a fallback if other conditions are not found.

If an environment matches, include it and merge it with the current config. Store the environment in constant YII_ENV.
2

#7 User is offline   Hyprion 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 20
  • Joined: 07-October 09
  • Location:Netherlands

Posted 10 October 2012 - 08:49 AM

I really like the idea of wisp, especially that you could configure all default settings in main.php and only overwrite the environment specific options.

Little addition: I would like it if an environment-config would not HAVE to be in the "env"-folder but you should also be able to set a path to the file. That way I don't need all my environment-configs in source control and I can overwrite the entire application with a new version.
0

#8 User is offline   Haensel 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 444
  • Joined: 14-January 11
  • Location:Vienna (Austria)

Posted 10 October 2012 - 08:50 AM

Nice approach. To take this even further:

What about using folders named like the corresponding environment? This way all the config arrays of a single environment could be automatically merged. I personally like dividing configs into smaller ones (like extension configurations etc.). The environments.php file in the main folder would just hold all the environment stuff and main.php would be used as the default config if no environment is used.

config
--production
----main.php
----params.php
----extensions.php
--testing
----main.php
----params.php
----extensions.php
--staging
----main.php
----params.php
----extensions.php
main.php
environments.php


So if using the production environment all files within the production folder would get merged
0

#9 User is offline   wisp 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 192
  • Joined: 04-February 11

Posted 10 October 2012 - 09:32 AM

What about the following setup? You could expand each environment with some additional stuff like custom imports and basic access rules

'env' => array(
    'dev' => array(
        'match' => array(
            'uri' => array('example.com', 'test.com', 'admin.*'),
            'docRoot' => '/www/mysite'
        ),
        'import' => 'application.config.env.dev',
        'debug' => true,
    ),

    'staging' => array(
        'match' => array(
            'docRoot' => '/www/mysite'
        ),
        'import' => 'application.config.staging',
        'whitelist' => array(
            'ip' => array('127.0.0.1', '31.22.155.12'),
            'message' => 'This is a testing server, ask info@example.com for access.'
        ),
       'debug' => true,
    ),

    'production' => array(
        'match' => array(
            'docRoot' => '/www/mysite',
            'envVar' => array('myvar' => 'prod')
        ),
        'import' => array(
            'application.config.staging',
            'application.config.extensions.*',
        ),
        'blacklist' => array(
            'ip' => array('127.0.0.1', '31.22.155.12'),
            'redirect' => '/site/notallowed/'
        ),
    )
),


Please tell me what you guys think..
1

#10 User is offline   Rodrigo Coelho 

  • Master Member
  • PipPipPipPip
  • Yii
  • Group: Members
  • Posts: 664
  • Joined: 05-August 10
  • Location:Rio de Janeiro, Brazil

Posted 21 October 2012 - 10:32 AM

In my apps I do the following:

  • Add an environment variable in the Web server's virtual host configuration. In nginx: fastcgi_param APP_ENV "development";
  • In the bootstrap file index.php (and in the file yiic.php for the console app), I check for the environment variable and the set the debug mode if needed and merge the common and the environment-specific configuration files.


Additional thoughts:

Besides the environment name, I believe that it could be useful to have an environment behavior (not Yii behaviors).
With these behaviors, multiple environments could have the same configuration without the need for multiple files with the exact same contents.
A use case is for the "production" and "demo" environments, which could have mostly the same configuration.

I found it easy merging everything but the URL rules. I still have to work on it.

For the local configuration, I use a main-local.php.template file and each developer has a main-local.php file, which is based off the template and is ignored by the SCM.
0

#11 User is offline   lubosdz 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 115
  • Joined: 25-July 10
  • Location:Slovakia, Bratislava

Posted 25 October 2012 - 09:48 AM

Environment are tightly related to development & deployment workflow (product cycle).
For example - small projects are suitable to deploy directly from localhost to online.
Bigger projects need more staged workflow of deployment, e.g. localhost -> testing -> production.

On most projects, we use following environments:

phase 1: -> local development (devel environment)
phase 2: -> test server 1 (local) (testing environment)
phase 3: -> test server 2 (production server copy) (testing environment)
phase 4: -> production server (production environment)



Since Yii 1.x does not assume multiple environments, we solved the issue via loading config.php files in a cascading style - meaning following configuration file can overwrite any previously loaded configuration setting. This actually the same logic like in other frameworks, e.g. ZF1.

Here is example:

(1) in /config/main.php:

...
// some production config code (always loaded first)
...
if(!Config::isProduction() && is_file(dirname(__FILE__).'/main-test.php')){
	require(dirname(__FILE__).'/main-test.php');
}



(2) in /config/main-test.php:

...
// some testing config code (overrides some configuration from main.php)
...
if(Config::isDevelopment() && is_file(dirname(__FILE__).'/main-development.php')){
	require(dirname(__FILE__).'/main-development.php');
}



(3) in /config/main-development.php:

...
// some development config code shared amongst all developers 
// (overrides configuration from main.php and main-test.php)
...
if(is_file(dirname(__FILE__).'/main-developer.php')){
	require(dirname(__FILE__).'/main-developer.php');
}



(4) in /config/main-developer.php:

...
// setting specific to particular developer, this file is never committed 
// (overrides configuration from main.php, main-test.php and main-development.php)
...




The Config object is simply check for specific IPs e.g.:


class Config{

   public static function isProduction(){
      // return TRUE if current server is running on domain "*.mydomain.com"
      return false !== stripos($_SERVER['SERVER_NAME'], 'mydomain.com');
   }

   public static function isDevelopment(){
      // return TRUE if current server is in intranet with IP 10.20.30.*
      return false !== strpos($_SERVER['SERVER_NAME'], '10.20.30.');
   }

}



Perhaps this gives some ideas on solving the issue with multiple environments.
Unfortunatelly, it is impossible to unify deployment workflows - so in the end it is questionable whether framework can solve such a specific need...

Cheers.
Lubos
Yii extension: Captcha Extended

Greatest discoveries in 22nd century will be about the gravitation. | http://www.synet.sk | http://ipdf.sk
0

#12 User is offline   Gustavo 

  • Master Member
  • Yii
  • Group: Moderators
  • Posts: 916
  • Joined: 27-July 10
  • Location:Curitiba - Brasil

Posted 25 April 2013 - 01:38 PM

This is something that I miss
Something like Zend does works for me, using enviroments as sections
We would have to adapt to accept an array instead of an ini file, something like:

return array(
 'production'=>array(
  //..main config
),
 'dev:production'=>array(
  //merges with production if enviroment is dev
 ),
 'console'=>array(
 //console config
 ),
 'dev:console'=>array(
  //dev console config
 ),
);


and init it like:
Yii::createWebApplication('config', 'application enviroment');

or
Yii::createConsoleApplication('config', 'console enviroment');

--
Extensions:
translate modue - module to handle translations
multiActiveRecord - db selection in models
redisCache - redis cache component
mpCpanel - interact with cpanel api
mUploadify - use uploadify uploader in your application

Gustavo Salomé Silva
0

#13 User is offline   Onman 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 175
  • Joined: 26-December 09
  • Location:The Netherlands

Posted 25 April 2013 - 03:02 PM

I use a combination of some of the above.

I have a separate bootstrap.php which, like the config file, returns an array like this one:

return array(
    'debug' => true,
    'traceLevel' => 3,
    'frameworkPath' => '/path/to/Yii/framework',
    'configFile' => '/path/to/config/main.php',
);



In my index.php I include this bootstrap array and use its values to start the application. For smaller projects I just change the values by hand when I need a different environment, but obviously any value (like 'debug') can also be set based on a certain condition (like the ip address or domain name).

Next to this I have a separate config file for each environment.
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users