|
| 1 | +<?php |
| 2 | +namespace codemix\yii2confload; |
| 3 | + |
| 4 | +/** |
| 5 | + * Config helps to build application configuration from a set of config files. |
| 6 | + * It can also initialize the Yii environment from environment vars and help |
| 7 | + * to load the latter from a `.env` file. |
| 8 | + * |
| 9 | + * ```php |
| 10 | + * // Init Yii environment from env vars (or `.env` file) and load configuration |
| 11 | + * // from `config/web.php` or `config/console.php` respectively |
| 12 | + * $config = new Config('/path/to/app')->web(); |
| 13 | + * $config = new Config('/path/to/app')->console(); |
| 14 | + * |
| 15 | + * // Merge a `config/local.php` or `config/console-local.php` with local overrides |
| 16 | + * // This also happens if `ENABLE_LOCALCONF` environment variable is set. |
| 17 | + * $config = new Config('/path/to/app')->web([], true); |
| 18 | + * $config = new Config('/path/to/app')->console([], true); |
| 19 | + * ``` |
| 20 | + * |
| 21 | + * The class normally is used with this set of files: |
| 22 | + * |
| 23 | + * - `.env`: Defines environment variables (usually only used for local development) |
| 24 | + * - `config/web.php`: Web configuration |
| 25 | + * - `config/console.php`: Console configuration |
| 26 | + * |
| 27 | + * A typical example for an `.env` file: |
| 28 | + * |
| 29 | + * ``` |
| 30 | + * YII_DEBUG=1 |
| 31 | + * DB_DSN=mysql:host=db;dbname=web |
| 32 | + * DB_USER=user |
| 33 | + * DB_PASSWORD=secret |
| 34 | + * ``` |
| 35 | + * |
| 36 | + * The `web.php` is loaded in the context of this class, so you can easily access |
| 37 | + * these settings there like: |
| 38 | + * |
| 39 | + * ```php |
| 40 | + * return [ |
| 41 | + * 'components' => [ |
| 42 | + * 'db' => [ |
| 43 | + * 'dsn' => self::env('DB_DSN'), |
| 44 | + * 'username' => self::env('DB_USER'), |
| 45 | + * 'password' => self::env('DB_PASSWORD'), |
| 46 | + * ``` |
| 47 | + * |
| 48 | + * In your `console.php` you can also reuse parts of your web configuration: |
| 49 | + * |
| 50 | + * ``php |
| 51 | + * $web = $this->web(); |
| 52 | + * return [ |
| 53 | + * 'components' => [ |
| 54 | + * 'db' => $web['components']['db'], |
| 55 | + * ``` |
| 56 | + * |
| 57 | + * The initialization of the Yii environment and loading of `.env` file |
| 58 | + * is optional and can also be supressed: |
| 59 | + * |
| 60 | + * ```php |
| 61 | + * $config = new Config('/path/to/app', false)->web(); |
| 62 | + * ``` |
| 63 | + * |
| 64 | + * Vice versa the class can also be used to only initialize the Yii environment |
| 65 | + * and load a `.env` file: |
| 66 | + * |
| 67 | + * ```php |
| 68 | + * Config::initEnv('/path/to/app'); |
| 69 | + * |
| 70 | + * // Get setting from environment variable or .env file |
| 71 | + * $setting = Config::env('MY_SETTING', 'default'); |
| 72 | + * ``` |
| 73 | + */ |
| 74 | +class Config |
| 75 | +{ |
| 76 | + /** |
| 77 | + * @var string the application base directory |
| 78 | + */ |
| 79 | + public $directory; |
| 80 | + |
| 81 | + /** |
| 82 | + * Initialize the app directory path and the Yii environment. |
| 83 | + * |
| 84 | + * If an `.env` file is found in the app directory, it's loaded with `Dotenv`. |
| 85 | + * If a `YII_DEBUG` or `YII_ENV` environment variable is set, the Yii constants |
| 86 | + * are set accordingly. In debug mode the error reporting is also set to `E_ALL`. |
| 87 | + * |
| 88 | + * @param string $directory the application base directory |
| 89 | + * @param bool $initEnv whether to initialize the Yii environment. Default |
| 90 | + * is `true |
| 91 | + * @param string|null $envDir the directory to look for a `.env` file or |
| 92 | + * `null` to not load any environment variables from that file. |
| 93 | + */ |
| 94 | + public function __construct($directory, $initEnv = true) |
| 95 | + { |
| 96 | + $this->directory = $directory; |
| 97 | + |
| 98 | + if ($initEnv) { |
| 99 | + self::initEnv($directory); |
| 100 | + } |
| 101 | + |
| 102 | + } |
| 103 | + |
| 104 | + /** |
| 105 | + * Gets the filename for a config file |
| 106 | + * |
| 107 | + * @param string $name |
| 108 | + * @param bool $required whether the file must exist. Default is `true`. |
| 109 | + * @param string $directory the name of the config directory. Default is `config`. |
| 110 | + * @return string|null the full path to the config file or `null` if $required |
| 111 | + * is set to `false` and the file does not exist |
| 112 | + * @throws \Exception |
| 113 | + */ |
| 114 | + public function getConfigFile($name, $required = true, $directory = 'config') |
| 115 | + { |
| 116 | + $sep = DIRECTORY_SEPARATOR; |
| 117 | + $path = rtrim($this->directory, $sep) . $sep . trim($directory, $sep) . $sep . $name; |
| 118 | + if (!file_exists($path)) { |
| 119 | + if ($required) { |
| 120 | + throw new \Exception("Config file '$path' does not exist"); |
| 121 | + } else { |
| 122 | + return null; |
| 123 | + } |
| 124 | + } |
| 125 | + return $path; |
| 126 | + } |
| 127 | + |
| 128 | + /** |
| 129 | + * Builds the web configuration. |
| 130 | + * |
| 131 | + * If $local is set to `true` and a `local.php` config file exists, it |
| 132 | + * is merged into the web configuration. |
| 133 | + * |
| 134 | + * Alternatively an environment variable `ENABLE_LOCALCONF` can be set |
| 135 | + * to 1. Setting $local to `false` completely disables the local config. |
| 136 | + * |
| 137 | + * @param array $config additional configuration to merge into the result |
| 138 | + * @param bool|null $local whether to check for local configuration overrides. |
| 139 | + * The default is `null`, which will check `ENABLE_LOCALCONF` env var. |
| 140 | + * @return array the web configuration array |
| 141 | + * @throws \Exception |
| 142 | + */ |
| 143 | + public function web($config = [], $local = null) |
| 144 | + { |
| 145 | + $files = [$this->getConfigFile('web.php')]; |
| 146 | + if ($local === null) { |
| 147 | + $local = $this->env('ENABLE_LOCALCONF', false); |
| 148 | + } |
| 149 | + if ($local) { |
| 150 | + $localFile = $this->getConfigFile('local.php', false); |
| 151 | + if ($localFile !==null) { |
| 152 | + $files[] = $localFile; |
| 153 | + } |
| 154 | + } |
| 155 | + return $this->mergeFiles($files, $config); |
| 156 | + } |
| 157 | + |
| 158 | + /** |
| 159 | + * Builds the console configuration. |
| 160 | + * |
| 161 | + * If $local is set to `true` and a `local-console.php` config file exists, |
| 162 | + * it is merged into the console configuration. |
| 163 | + * |
| 164 | + * Alternatively an environment variable `ENABLE_LOCALCONF` can be set |
| 165 | + * to 1. Setting $local to `false` completely disables the local config. |
| 166 | + * |
| 167 | + * @param array $config additional configuration to merge into the result |
| 168 | + * @param bool|null $local whether to check for local configuration overrides. |
| 169 | + * The default is `null`, which will check `ENABLE_LOCALCONF` env var. |
| 170 | + * @return array the web configuration array |
| 171 | + * @throws \Exception |
| 172 | + */ |
| 173 | + public function console($config = [], $local = null) |
| 174 | + { |
| 175 | + $files = [$this->getConfigFile('console.php')]; |
| 176 | + if ($local === null) { |
| 177 | + $local = $this->env('ENABLE_LOCALCONF', false); |
| 178 | + } |
| 179 | + if ($local) { |
| 180 | + $localFile = $this->getConfigFile('local-console.php', false); |
| 181 | + if ($localFile !==null) { |
| 182 | + $files[] = $localFile; |
| 183 | + } |
| 184 | + } |
| 185 | + return $this->mergeFiles($files, $config); |
| 186 | + } |
| 187 | + |
| 188 | + /** |
| 189 | + * Load configuration files and merge them together. |
| 190 | + * |
| 191 | + * The files are loaded in the context of this class. So you can use `$this` |
| 192 | + * and `self` to access instance / class methods. |
| 193 | + * |
| 194 | + * @param array $files list of configuration files to load and merge. |
| 195 | + * Configuration from later files will override earlier values. |
| 196 | + * @param array $config additional configuration to merge into the result |
| 197 | + * @return array the resulting configuration array |
| 198 | + */ |
| 199 | + public function mergeFiles($files, $config = []) |
| 200 | + { |
| 201 | + $configs = array_map(function ($f) { return require($f); }, $files); |
| 202 | + $configs[] = $config; |
| 203 | + return call_user_func_array('yii\helpers\ArrayHelper::merge', $configs); |
| 204 | + } |
| 205 | + |
| 206 | + /** |
| 207 | + * Init the Yii environment from environment variables. |
| 208 | + * |
| 209 | + * If $directory is passed and contains a `.env` file, that file is loaded |
| 210 | + * with `Dotenv` first. |
| 211 | + * |
| 212 | + * @param string|null $directory the directory to check for an `.env` file |
| 213 | + */ |
| 214 | + public static function initEnv($directory = null) |
| 215 | + { |
| 216 | + if ($directory !== null && file_exists($directory . DIRECTORY_SEPARATOR . '.env')) { |
| 217 | + \Dotenv::load($directory); |
| 218 | + } |
| 219 | + |
| 220 | + // Define main Yii environment variables |
| 221 | + if (isset($_ENV['YII_DEBUG'])) { |
| 222 | + define('YII_DEBUG', (bool)$_ENV['YII_DEBUG']); |
| 223 | + if (YII_DEBUG) { |
| 224 | + error_reporting(E_ALL); |
| 225 | + } |
| 226 | + } |
| 227 | + if (isset($_ENV['YII_ENV'])) { |
| 228 | + define('YII_ENV', $_ENV['YII_ENV']); |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + /** |
| 233 | + * Get either an env var or a default value if the var is not set. |
| 234 | + * |
| 235 | + * @param string $name the name of the variable to get |
| 236 | + * @param mixed $default the default value to return if variable is not set. |
| 237 | + * Default is `null`. |
| 238 | + * @param bool $required whether the var must be set. $default is ignored in |
| 239 | + * this case. Default is `false`. |
| 240 | + * @return mixed the content of the environment variable or $default if not set |
| 241 | + */ |
| 242 | + public static function env($name, $default = null, $required = false) |
| 243 | + { |
| 244 | + if ($required) { |
| 245 | + Dotenv::required($name); |
| 246 | + } |
| 247 | + return isset($_ENV[$name]) ? $_ENV[$name] : $default; |
| 248 | + } |
| 249 | + |
| 250 | +} |
0 commit comments