В protected я создал environment.php c такими вот 2я классами:
class Environment
{
/**
*
*/
const PRODUCTION = 10;
/**
*
*/
const STAGING = 20;
/**
*
*/
const TESTING = 30;
/**
*
*/
const DEVELOPMENT = 40;
/**
* @var int
*/
protected static $current;
/**
* @var
*/
private static $currentObj;
/**
*
*/
public static function instance()
{
if(!self::$currentObj)
{
self::$currentObj = new self();
}
return self::$currentObj;
}
/**
* @param int $ENV
* @return bool
*/
public function set($ENV = Environment::DEVELOPMENT)
{
if(self::$current)
{
return false;
}
if(isset($_GET['DEBUG']))
{
$this->setEnvironment($_GET['DEBUG']);
$_SESSION['systemEnvironment'] = $_GET['DEBUG'];
}
elseif(isset($_SESSION['systemEnvironment']) and $_SESSION['systemEnvironment'] !== 'off')
{
$this->setEnvironment($_SESSION['systemEnvironment']);
}
else
{
self::$current = $ENV;
}
return true;
}
/**
* @return int
*/
public static function getCurrent()
{
return self::$current;
}
/**
* @param $level
*/
private function setEnvironment($level)
{
switch($level)
{
case 'PRODUCTION':
self::$current = self::PRODUCTION;
break;
case 'STAGING':
self::$current = self::STAGING;
break;
case 'TESTING':
self::$current = self::TESTING;
break;
case 'DEVELOPMENT':
self::$current = self::DEVELOPMENT;
break;
case 'off':
if(isset($_SERVER['ENVIRONMENT']))
{
self::$current = constant('Environment::' . strtoupper($_SERVER['ENVIRONMENT']));
}
$_SESSION['systemEnvironment'] = null;
break;
}
}
}
/**
* Class EnvironmentUtils
*/
class EnvironmentUtils extends Environment
{
/**
* @param $rootDirectory
* @param $fileName
* @return string
*/
public static function getConfigFile($rootDirectory, $fileName)
{
$fileLink = $rootDirectory . '/config/';
switch(parent::$current)
{
case Environment::DEVELOPMENT:
$fileLink .= 'Development/';
break;
case Environment::PRODUCTION:
$fileLink .= 'Production/';
break;
case Environment::STAGING:
$fileLink .= 'Staging/';
break;
case Environment::TESTING:
$fileLink .= 'Testing/';
break;
}
return $fileLink . $fileName;
}
}
После чего немного изменил структуру файлов в папке config:
¦ +-- API
¦ ¦ +-- Morpher.php
¦ ¦ L-- XmlRpcClient.php
¦ +-- Common
¦ ¦ +-- Daemon.php
¦ ¦ +-- Email.php
¦ ¦ L-- Filtrator.php
¦ +-- console.php
¦ +-- DB
¦ ¦ +-- 1c.php
¦ ¦ L-- BaseConnect.php
¦ +-- main.php
¦ +-- routes.php
+-- Development
¦ +-- cli.php
¦ +-- DB
¦ ¦ +-- 1c.php
¦ ¦ +-- BaseConnect.php
¦ L-- Web.php
+-- Production
¦ +-- cli.php
¦ +-- DB
¦ ¦ +-- 1c.php
¦ ¦ +-- BaseConnect.php
¦ ¦ +-- Sape.php
¦ L-- Web.php
+-- Staging
¦ +-- cli.php
¦ +-- DB
¦ ¦ +-- 1c.php
¦ ¦ +-- BaseConnect.php
¦ L-- Web.php
L-- Testing
+-- cli.php
+-- DB
¦ +-- 1c.php
¦ L-- BaseConnect.php
L-- Web.php
В Base/Web.php находится базовый конфиг. Из серии:
return [
'basePath' => PROTECTED_PATH,
'name' => 'MyApp',
'theme' => 'classic',
'language' => 'ru',
'defaultController' => 'user/login',
// preloading 'log' component
// autoloading model and component classes
'aliases' => [
'bootstrap' => PROTECTED_PATH.'extensions/bootstrap',
// change this if necessary
],
'preload' => [
'log',
'bootstrap'
],
'import' => [
'application.models.*',
]
Тоже самое в cli.php. А вот в Production/Web.php чтоб не копировать постоянно какой то параметр если меняешь, я сделал так:
return CMap::mergeArray(include(PROTECTED_PATH.'config/Base/main.php'),
[
'components'=>
[
'db' => include(dirname(__FILE__) . '/DB/BaseConnect.php'),
'db1c' => include(dirname(__FILE__) . '/DB/1c.php')
]
]
);
Таким образом я просто перезаписываю настройки бд, ну или то, что мне нужно. Теперь самое интересное. Подгрузка. index.php.
P.S.: У меня в проекте ядро yii тянется через composer:
define("PROTECTED_PATH",realpath(dirname(__FILE__)).'/protected/');
if(!file_exists(PROTECTED_PATH.'vendor/autoload.php'))
{
die('autoload.php not found. Composer update!');
}
require_once(PROTECTED_PATH.'vendor/autoload.php');
require_once(PROTECTED_PATH.'environment.php');
if(isset($_SERVER['HTTP_ORIGIN']))
{
if(in_array($_SERVER['HTTP_ORIGIN'],[....]))
{
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Methods: GET, POST, OPTIONS, DELETE, PUT');
header('Access-Control-Max-Age: 1728000');
header('Access-Control-Allow-Credentials: true');
header("Access-Control-Allow-Headers: access-token, expiry, token-type, uid, client");
header("Access-Control-Expose-Headers: access-token, expiry, token-type, uid, client");
}
}
if($_SERVER['REQUEST_METHOD'] == 'OPTIONS')
{
header("HTTP/1.0 204 No Content");
die();
}
Environment::instance()->set(Environment::DEVELOPMENT);
switch(Environment::getCurrent())
{
case Environment::DEVELOPMENT:
define('YII_DEBUG', false);
define('YII_SKIP_AUTH', true);
define('YII_KERNEL_LOG', true);
define('DISPLAY_ERROR_TRACE', true);
define('YII_TRACE_LEVEL', 0);
define('MINIFY_INTERFACE', false);
error_reporting(E_ALL);
ini_set('display_errors','On');
break;
case Environment::PRODUCTION:
define('YII_DEBUG', false);
define('YII_SKIP_AUTH', false);
define('YII_KERNEL_LOG', false);
define('DISPLAY_ERROR_TRACE', false);
define('YII_TRACE_LEVEL', 3);
define('MINIFY_INTERFACE', true);
error_reporting(0);
ini_set('display_errors','Off');
break;
case Environment::STAGING:
define('YII_DEBUG', true);
define('YII_SKIP_AUTH', false);
define('YII_KERNEL_LOG', true);
define('DISPLAY_ERROR_TRACE', true);
define('YII_TRACE_LEVEL', 0);
error_reporting(E_ALL);
ini_set('display_errors','On');
break;
}
/** @noinspection PhpUndefinedClassInspection */
Yii::$enableIncludePath = true; //For init Yii kernel
$config = EnvironmentUtils::getConfigFile(PROTECTED_PATH,'Web.php');
//Other constants
define('DATE_FORMAT', 'Y/m/d');
define('STATUS_NOACTIVE', 0);
define('STATUS_ACTIVE', 1);
date_default_timezone_set("Europe/Kiev");
/** @noinspection PhpUndefinedClassInspection */
Yii::createWebApplication($config)->run();
По аналогии сделано и в cli.php. Система себя отлично зарекомендовала.
Комментарии (17)
getId
17.08.2015 12:36+1Что у вас за странная каша со статическими методами?
Устанавливаете вроде как в экземпляр
Environment::instance()->set(Environment::DEVELOPMENT);
а на самом деле в статику:
private function setEnvironment($level) { switch($level) { case 'PRODUCTION': self::$current = self::PRODUCTION; break;
И все последующие вызовы у вас через статические методы. Зачемprivate static $currentObj;
?
А по делу, есть немного другой подход через конфигурацию из окружения: github.com/vlucas/phpdotenv.
По ссылке объясняют, что в таком подходе хорошего.alexsoft
17.08.2015 12:59Поддерживаю
phpdotenv
.
Очень нравится вариант конфигов в Laravel. Всё, что меняется в зависимости от окружения, выносится в .env файл. Очень удобно!SamDark
17.08.2015 14:12Только не вздумайте в продакшне из файлов читать.
getenv() / setenv()
работают в рамках процесса, а не в рамках реквеста.alexsoft
17.08.2015 14:49bool putenv ( string $setting )
Adds setting to the server environment. The environment variable will only exist for the duration of the current request. At the end of the request the environment is restored to its original state.
Dotenv именно этой функцией устанавливает переменные. И всё работает отлично.
symbix
17.08.2015 16:15Если даже предположить такую редкость, как тредовый SAPI — при нормально организованном деплое приложение разворачивается каждый раз в новый каталог, так что никаких побочных эффектов вызвать не должно. А на каком-нибудь шареде с mpd_php + threaded mpm (а такое бывает?) — ну там и с setlocale аналогичные проблемы.
SamDark
17.08.2015 16:32Проблема не в каталогах, а в том, что это shared-данные, которые пишут-читают-ресетят без лока сразу неколько «клиентов».
symbix
17.08.2015 23:24dotenv сначала пробует $_ENV и $_SERVER, а уже потом getenv, так что проблемы быть не должно даже в таком редком случае — конечно, если пользоваться $loader-> getEnvironmentVariable(), а не напрямую getenv().
SamDark
18.08.2015 13:08Если задавать переменные в окружении — не должно быть. Если читать из файлов — будут обязательно.
symbix
18.08.2015 17:03А какая разница?
SamDark
19.08.2015 11:05symbix
20.08.2015 01:19А, в смысле задавать во внешнем окружении, до запуска реквеста? Так понятнее, а то непонятно — какая разница откуда читать.
Но все равно же, по идее, проблем не будет, если пользоваться $loader-> getEnvironmentVariable(). Там просто не дойдет до нереентерабельного getenv и прочитается из $_ENV (равно как и запишется в него же). В environment будет бардак, но а какая разница, если в $_ENV все на месте?SamDark
20.08.2015 01:29Если именно из $_ENV читать, проблемы нет. Разве что если на сервере более одного проекта, бардак начинается ещё и с именованием этих самых переменных. Но тут была речь про чтение именно из .env-файликов в продакшне, что, при определённых условиях, будет как раз проблемой.
bohdan4ik
17.08.2015 15:24+2Мы пользуем концепцию *local файлов. Пишем конфиг, который повторяется у всех, а потом мерджим в него main-local.php, который уникален для каждого разработчика и сервера. Не dotenv, конечно же, но явно проще, чем нагородил автор.
affka
А по сколько строк в каждом из небазовых (типа Web.php) конфигов? Мы обычно сводим такие настройки в один файл production.php, т.е. на каждый env — свой дополнительный файл, который мержится с base и web/cli.
А вообще в данном аспекте «пули» быть не может, конфигурация сильно зависит от масштабов проекта. Но в одном всегда одинакого — должен быть файл config.php, который лежит в корне и которого нет в гите (на каждой машине — свой). И там идут переопределения настроек индивидуально для машины + пароли для БД/токены/… от продакшена, т.к. хранить пароли в гите — зло :)
Недавно для своего фреймворка делал основу для конфигурация, вот такие примеры получились — github.com/jiisoft/jii-workers/tree/master/examples