Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 353 lines (336 sloc) 14.384 kb
e7f3c31 GWoo going lithium
gwoo authored
1 <?php
2 /**
3 * Lithium: the most rad php framework
4 *
14de7bf GWoo Happy 2012!
gwoo authored
5 * @copyright Copyright 2012, Union of RAD (http://union-of-rad.org)
e7f3c31 GWoo going lithium
gwoo authored
6 * @license http://opensource.org/licenses/bsd-license.php The BSD License
7 */
8
9 namespace lithium\core;
10
6fb3f64 Michael Nitschinger change _use lithium_ commands and remove the prefixed slash
daschl authored
11 use lithium\util\Set;
e7f3c31 GWoo going lithium
gwoo authored
12
f4dd741 Alexander Morland added @package to action, core and data classes
alkemann authored
13 /**
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
14 * The `Environment` class allows you to manage multiple configurations for your application,
15 * depending on the context within which it is running, i.e. development, test or production.
f4dd741 Alexander Morland added @package to action, core and data classes
alkemann authored
16 *
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
17 * While those three environments are the most common, you can create any arbitrary environment
18 * with any set of configuration, for example:
19 *
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
20 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetAndGetCurrentEnvironment(1-3)}}}
21 *
22 * You can then retrieve the configurations using the key name. The correct configuration is
23 * returned, automatically accounting for the current environment:
24 *
25 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetAndGetCurrentEnvironment(15-15)}}}
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
26 *
27 * `Environment` also works with subclasses of `Adaptable`, allowing you to maintain separate
28 * configurations for database servers, cache adapters, and other environment-specific classes, for
29 * example:
30 * {{{
31 * Connections::add('default', array(
32 * 'production' => array(
33 * 'type' => 'database',
34 * 'adapter' => 'MySql',
35 * 'host' => 'db1.application.local',
36 * 'login' => 'secure',
37 * 'password' => 'secret',
38 * 'database' => 'app-production'
39 * ),
40 * 'development' => array(
41 * 'type' => 'database',
42 * 'adapter' => 'MySql',
43 * 'host' => 'localhost',
44 * 'login' => 'root',
45 * 'password' => '',
46 * 'database' => 'app'
47 * )
48 * ));
49 * }}}
50 *
51 * This allows the database connection named `'default'` to be connected to a local database in
52 * development, and a production database in production. You can define environment-specific
53 * configurations for caching, logging, even session storage, i.e.:
54 * {{{
55 * Cache::config(array(
56 * 'userData' => array(
57 * 'development' => array('adapter' => 'File'),
58 * 'production' => array('adapter' => 'Memcache')
59 * )
60 * ));
61 * }}}
62 *
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
63 * When the cache configuration is accessed in the application's code, the correct configuration is
64 * automatically used:
65 * {{{
66 * $user = User::find($request->id);
67 * Cache::write('userData', "User.{$request->id}", $user->data(), '+5 days');
68 * }}}
69 *
70 * In this configuration, the above example will automatically send cache writes to the file system
71 * during local development, and to a [ memcache](http://memcached.org/) server in production.
72 *
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
73 * When writing classes that connect to other external resources, you can automatically take
74 * advantage of environment-specific configurations by extending `Adaptable` and implementing
75 * your resource-handling functionality in adapter classes.
76 *
77 * In addition to managing your environment-specific configurations, `Environment` will also help
78 * you by automatically detecting which environment your application is running in. For additional
79 * information, see the documentation for `Environment::is()`.
80 *
81 * @see lithium\core\Adaptable
f4dd741 Alexander Morland added @package to action, core and data classes
alkemann authored
82 */
e7f3c31 GWoo going lithium
gwoo authored
83 class Environment {
84
85 protected static $_configurations = array(
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
86 'production' => array(),
87 'development' => array(),
88 'test' => array()
e7f3c31 GWoo going lithium
gwoo authored
89 );
90
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
91 /**
92 * Holds the name of the current environment under which the application is running. Set by
93 * passing a `Request` object or `$_SERVER` or `$_ENV` array into `Environment::set()` (which
94 * in turn passes this on to the _detector_ used to determine the correct environment). Can be
95 * tested or retrieved using `Environment::is()` or `Environment::get()`.
96 *
0e3af10 Gavin Davies Minor documentation correction
gavD authored
97 * @see lithium\core\Environment::set()
98 * @see lithium\core\Environment::is()
99 * @see lithium\core\Environment::get()
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
100 * @var string
101 */
102 protected static $_current = '';
e7f3c31 GWoo going lithium
gwoo authored
103
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
104 /**
105 * If `Environment::is()` is used to assign a custom closure for environment detection, a copy
106 * is kept in `$_detector`. Otherwise, `$_detector` is `null`, and the hard-coded detector is
107 * used.
108 *
109 * @see lithium\core\Environment::_detector()
110 * @see lithium\core\Environment::is()
111 * @var object
112 */
e7f3c31 GWoo going lithium
gwoo authored
113 protected static $_detector = null;
114
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
115 /**
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
116 * Resets the `Environment` class to its default state, including unsetting the current
117 * environment, removing any environment-specific configurations, and removing the custom
118 * environment detector, if any has been specified.
119 *
8b2050b Simon JAILLET Allow `lithium\Environment::reset()` to remove a specific env only for t...
jails authored
120 * @param $env If set, delete the defined environment only.
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
121 */
8b2050b Simon JAILLET Allow `lithium\Environment::reset()` to remove a specific env only for t...
jails authored
122 public static function reset($env = null) {
123 if ($env) {
124 unset(static::$_configurations[$env]);
125 return;
126 }
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
127 static::$_current = '';
128 static::$_detector = null;
129 static::$_configurations = array(
130 'production' => array(),
131 'development' => array(),
132 'test' => array()
133 );
134 }
135
136 /**
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
137 * A simple boolean detector that can be used to test which environment the application is
138 * running under. For example `Environment::is('development')` will return `true` if
139 * `'development'` is, in fact, the current environment.
140 *
141 * This method also handles how the environment is detected at the beginning of the request.
7b422f4 Nate Abele Implementing host mapping in `\core\Environment::is()`.
nateabele authored
142 *
143 * #### Custom Detection
144 *
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
145 * While the default detection rules are very simple (if the `'SERVER_ADDR'` variable is set to
146 * `127.0.0.1`, the environment is assumed to be `'development'`, or if the string `'test'` is
147 * found anywhere in the host name, it is assumed to be `'test'`, and in all other cases it
148 * is assumed to be `'production'`), you can define your own detection rule set easily using a
149 * closure that accepts an instance of the `Request` object, and returns the name of the correct
150 * environment, as in the following example:
7b422f4 Nate Abele Implementing host mapping in `\core\Environment::is()`.
nateabele authored
151 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testCustomDetector(1-9) }}}
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
152 *
153 * In the above example, the user-specified closure takes in a `Request` object, and using the
154 * server data which it encapsulates, returns the correct environment name as a string.
155 *
7b422f4 Nate Abele Implementing host mapping in `\core\Environment::is()`.
nateabele authored
156 * #### Host Mapping
157 *
158 * The most common use case is to set the environment depending on host name. For convenience,
159 * the `is()` method also accepts an array that matches host names to environment names, where
160 * each key is an environment, and each value is either an array of valid host names, or a
161 * regular expression used to match a valid host name.
162 *
163 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testDetectionWithArrayMap(1-5) }}}
164 *
165 * In this example, a regular expression is being used to match local domains
166 * (i.e. `localhost`), as well as the built-in `.console` domain, for console requests. Note
167 * that in the console, the environment can always be overridden by specifying the `--env`
168 * option.
169 *
170 * Then, one or more host names are matched up to `'test'` and `'staging'`, respectively. Note
171 * that no rule is present for production: this is because `'production'` is the default value
172 * if no other environment matches.
173 *
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
174 * @param mixed $detect Either the name of an environment to check against the current, i.e.
175 * `'development'` or `'production'`, or a closure which `Environment` will use
7b422f4 Nate Abele Implementing host mapping in `\core\Environment::is()`.
nateabele authored
176 * to determine the current environment name, or an array mapping environment names
177 * to host names.
4c10d97 Nate Abele Adding documentation and small refactoring to `core\Environment`.
nateabele authored
178 * @return boolean If `$detect` is a string, returns `true` if the current environment matches
179 * the value of `$detect`, or `false` if no match. If used to set a custom detector,
180 * returns `null`.
181 */
182 public static function is($detect) {
e7f3c31 GWoo going lithium
gwoo authored
183 if (is_callable($detect)) {
184 static::$_detector = $detect;
185 }
7b422f4 Nate Abele Implementing host mapping in `\core\Environment::is()`.
nateabele authored
186 if (!is_array($detect)) {
187 return (static::$_current == $detect);
188 }
189 static::$_detector = function($request) use ($detect) {
190 if ($request->env || $request->command == 'test') {
191 return ($request->env) ? $request->env : 'test';
192 }
193 $host = method_exists($request, 'get') ? $request->get('http:host') : '.console';
194
195 foreach ($detect as $environment => $hosts) {
196 if (is_string($hosts) && preg_match($hosts, $host)) {
197 return $environment;
198 }
199 if (is_array($hosts) && in_array($host, $hosts)) {
200 return $environment;
201 }
202 }
203 return "production";
204 };
e7f3c31 GWoo going lithium
gwoo authored
205 }
206
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
207 /**
208 * Gets the current environment name, a setting associated with the current environment, or the
209 * entire configuration array for the current environment.
210 *
211 * @param string $name The name of the environment setting to retrieve, or the name of an
212 * environment, if that environment's entire configuration is to be retrieved. If
213 * retrieving the current environment name, `$name` should not be passed.
214 * @return mixed If `$name` is unspecified, returns the name of the current environment name as
215 * a string (i.e. `'production'`). If an environment name is specified, returns that
216 * environment's entire configuration as an array.
217 */
218 public static function get($name = null) {
a90a34d Nate Abele Adding semantics in `\core\Environment` for automatically using the curr...
nateabele authored
219 $cur = static::$_current;
220
221 if (!$name) {
222 return $cur;
223 }
224 if ($name === true) {
225 return isset(static::$_configurations[$cur]) ? static::$_configurations[$cur] : null;
e7f3c31 GWoo going lithium
gwoo authored
226 }
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
227 if (isset(static::$_configurations[$name])) {
bed760f Howard Lince III Environment: Added dot path fetching for Environment::get, this utilizes...
Howard3 authored
228 return static::_processDotPath($name, static::$_configurations);
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
229 }
bed760f Howard Lince III Environment: Added dot path fetching for Environment::get, this utilizes...
Howard3 authored
230 if (!isset(static::$_configurations[$cur])) {
231 return static::_processDotPath($name, static::$_configurations);
e7f3c31 GWoo going lithium
gwoo authored
232 }
a90a34d Nate Abele Adding semantics in `\core\Environment` for automatically using the curr...
nateabele authored
233
bed760f Howard Lince III Environment: Added dot path fetching for Environment::get, this utilizes...
Howard3 authored
234 return static::_processDotPath($name, static::$_configurations[$cur]);
235 }
236
237 protected static function _processDotPath($path, &$arrayPointer) {
238 if (isset($arrayPointer[$path])) {
239 return $arrayPointer[$path];
240 }
241 if (strpos($path, '.') === false) {
242 return null;
243 }
244 $pathKeys = explode('.', $path);
245 foreach ($pathKeys as $pathKey) {
9652522 Valery Vishnevskiy Fix for _processDotPath in Environment class. Fatal error if we try to g...
v-v-vishnevskiy authored
246 if (!is_array($arrayPointer) || !isset($arrayPointer[$pathKey])) {
bed760f Howard Lince III Environment: Added dot path fetching for Environment::get, this utilizes...
Howard3 authored
247 return false;
248 }
249 $arrayPointer = &$arrayPointer[$pathKey];
250 }
251 return $arrayPointer;
e7f3c31 GWoo going lithium
gwoo authored
252 }
253
254 /**
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
255 * Creates, modifies or switches to an existing environment configuration. To create a new
256 * configuration, or to update an existing configuration, pass an environment name and an array
257 * that defines its configuration:
258 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(1-1) }}}
e7f3c31 GWoo going lithium
gwoo authored
259 *
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
260 * You can then add to an existing configuration by calling the `set()` method again with the
261 * same environment name:
262 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(6-6) }}}
263 *
264 * The settings for the environment will then be the aggregate of all `set()` calls:
265 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testModifyEnvironmentConfig(7-7) }}}
266 *
73a07d8 Nate Abele Allowing `Environment::set()` to assign to multiple environments.
nateabele authored
267 * By passing an array to `$env`, you can assign the same configuration to multiple
268 * environments:
269 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetMultipleEnvironments(5-7) }}}
270 *
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
271 * The `set()` method can also be called to manually set which environment to operate in:
272 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testSetAndGetCurrentEnvironment(5-5) }}}
273 *
73a07d8 Nate Abele Allowing `Environment::set()` to assign to multiple environments.
nateabele authored
274 * Finally, `set()` can accept a `Request` object, to automatically detect the correct
275 * environment.
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
276 *
277 * {{{ embed:lithium\tests\cases\core\EnvironmentTest::testEnvironmentDetection(9-10) }}}
278 *
279 * For more information on defining custom rules to automatically detect your application's
280 * environment, see the documentation for `Environment::is()`.
281 *
73a07d8 Nate Abele Allowing `Environment::set()` to assign to multiple environments.
nateabele authored
282 * @see lithium\action\Request
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
283 * @see lithium\core\Environment::is()
73a07d8 Nate Abele Allowing `Environment::set()` to assign to multiple environments.
nateabele authored
284 * @param mixed $env The name(s) of the environment(s) you wish to create, update or switch to
285 * (string/array), or a `Request` object or `$_SERVER` / `$_ENV` array used to
286 * detect (and switch to) the application's current environment.
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
287 * @param array $config If creating or updating a configuration, accepts an array of settings.
288 * If the environment name specified in `$env` already exists, the values in
289 * `$config` will be recursively merged with any pre-existing settings.
290 * @return array If creating or updating a configuration, returns an array of the environment's
291 * settings. If updating an existing configuration, this will be the newly-applied
292 * configuration merged with the pre-existing values. If setting the environment
293 * itself (i.e. `$config` is unspecified), returns `null`.
e7f3c31 GWoo going lithium
gwoo authored
294 */
295 public static function set($env, $config = null) {
73a07d8 Nate Abele Allowing `Environment::set()` to assign to multiple environments.
nateabele authored
296 if ($config === null) {
297 if (is_object($env) || is_array($env)) {
298 static::$_current = static::_detector()->__invoke($env);
299 } elseif (isset(static::$_configurations[$env])) {
300 static::$_current = $env;
301 }
302 return;
303 }
304 if (is_array($env)) {
305 foreach ($env as $name) {
306 static::set($name, $config);
e7f3c31 GWoo going lithium
gwoo authored
307 }
308 return;
309 }
a90a34d Nate Abele Adding semantics in `\core\Environment` for automatically using the curr...
nateabele authored
310 $env = ($env === true) ? static::$_current : $env;
e8b36b8 Nate Abele Adding documentation to `core\Environment`, fixing issue in `Environment...
nateabele authored
311 $base = isset(static::$_configurations[$env]) ? static::$_configurations[$env] : array();
312 return static::$_configurations[$env] = Set::merge($base, $config);
e7f3c31 GWoo going lithium
gwoo authored
313 }
314
45d74c1 Nate Abele Adding documentation to `core\Environment`, refactoring `Environment::ge...
nateabele authored
315 /**
316 * Accessor method for `Environment::$_detector`. If `$_detector` is unset, returns the default
317 * detector built into the class. For more information on setting and using `$_detector`, see
318 * the documentation for `Environment::is()`. The `_detector()` method is called at the
319 * beginning of the application's life-cycle, when `Environment::set()` is passed either an
320 * instance of a `Request` object, or the `$_SERVER` or `$_ENV` array. This object (or array)
321 * is then passed onto `$_detector`, which returns the correct environment.
322 *
323 * @see lithium\core\Environment::is()
324 * @see lithium\core\Environment::set()
325 * @see lithium\core\Environment::$_detector
326 * @return object Returns a callable object (anonymous function) which detects the application's
327 * current environment.
328 */
e7f3c31 GWoo going lithium
gwoo authored
329 protected static function _detector() {
330 return static::$_detector ?: function($request) {
49080ac Richard McIntyre Removed duplication and line length offensive code
mackstar authored
331 $isLocal = in_array($request->env('SERVER_ADDR'), array('::1', '127.0.0.1'));
e7f3c31 GWoo going lithium
gwoo authored
332 switch (true) {
f12057f GWoo changing environment check for CLI.
gwoo authored
333 case (isset($request->env)):
334 return $request->env;
335 case ($request->command == 'test'):
427ee64 Richard McIntyre Added environment recognition for the console
mackstar authored
336 return 'test';
d41ee98 David Persson Adding `'PLATFORM'` environment variable to console request.
davidpersson authored
337 case ($request->env('PLATFORM') == 'CLI'):
427ee64 Richard McIntyre Added environment recognition for the console
mackstar authored
338 return 'development';
ffd5429 Simon JAILLET Fixes test environment detection.
jails authored
339 case (preg_match('/^\/test/', $request->url) && $isLocal):
cd0f9dc Richard McIntyre Added a default detector for routes beginning with /test
mackstar authored
340 return 'test';
49080ac Richard McIntyre Removed duplication and line length offensive code
mackstar authored
341 case ($isLocal):
e7f3c31 GWoo going lithium
gwoo authored
342 return 'development';
343 case (preg_match('/^test/', $request->env('HTTP_HOST'))):
344 return 'test';
345 default:
346 return 'production';
347 }
348 };
349 }
350 }
351
0e3af10 Gavin Davies Minor documentation correction
gavD authored
352 ?>
Something went wrong with that request. Please try again.