Giter Site home page Giter Site logo

beporter / cakephp-envawareness Goto Github PK

View Code? Open in Web Editor NEW
10.0 3.0 0.0 9.96 MB

A sample project demonstrating how to make CakePHP load configuration values based on the environment.

License: Other

ApacheConf 0.36% PHP 79.37% Shell 1.72% Ruby 3.83% CSS 13.55% Batchfile 1.17%

cakephp-envawareness's Introduction

CakePHP Environment Awareness Demo

A sample CakePHP application that will load configuration information based on an environment flag value.

It is meant to accompany this presentation: Running a CakePHP App in Different Operating Environments

The Cake 3 demo app lives in app-cake3/.

The Short Version

A web app most likely has to connect to a different database when being developed on a developer's workstation or virtual machine compared to the production server(s). This repo demonstrates how to set up a CakePHP project to load different configuration values based on a value unique to each operating environment, such as an environment variable.

In Apache, this can be accomplished using SetEnv:

# my_apache_vhost.conf
<VirtualHost *:80>
    ServerName stagingsite.com
    SetEnv APP_ENV staging
</VirtualHost>

On the command line you can export an environment variable (for use with Cake Shells):

# ~/.profile or ~/.bash_profile
export APP_ENV=staging

With Cake 3, you can load additional configuration files quickly in config/bootstrap.php:

// config/bootstrap.php

// After loading the stock config file,
// load the environment config file
// and the local config file (when present.)
try {
	$env = getenv('APP_ENV');
	Configure::load("app-{$env}", 'default');
} catch (\Exception $e) {
	// It is not an error if this file is missing.
}
try {
	Configure::load('app-local', 'default');
} catch (\Exception $e) {
	// It is not an error if this file is missing.
}

The additional code above will load values from the file config/app-staging.php.

Assuming the config files contained the following:

// config/app.php
return [
	'debug' => false,
    'App' => [
    	'FancyName' => 'Wonderful Application',
    	'EnvSignalColor' => '#ffffff', // White admin background in production.
    ],
];
// config/app-stage.php
return [
	'debug' => true, // Turn debug on in the staging environment.
    'App' => [
    	// (Note that we don't change the [FancyName] key.)
    	'EnvSignalColor' => '#77cccc', // Red admin background in staging.
    ],
];

In your app, you can access a consistently named key and obtain a value appropriate for the current environment. Take src/Template/Layout/default.ctp for example:

<!-- src/Template/Layout/default.ctp -->
<head>
	<style>
		.navBackgroundColor {
			background-color: <?php echo Configure::read('App.EnvSignalColor'); ?>;
		}
	</style>
</head>

That's a much better alternative than this:

<!-- src/Template/Layout/default.ctp -->
<?php if ($_SERVER['SERVER_NAME'] === 'productionsite.com') {
	$bgColor = '#ffffff'; // white in production
} elseif ($_SERVER['SERVER_NAME'] === 'stagingsite.com') {
	$bgColor = '#cccc77'; // yellow in staging
} else {
	$bgColor = '#77cccc'; // red in development
} ?>
<head>
	<style>
		.navBackgroundColor {
			background-color: <?php echo $bgColor; ?>;
		}
	</style>
</head>

CakePHP 2.x and 1.x

This same principle can be applied to Cake 2.x and 1.x apps, although there are some things to keep in mind.

  • Where Cake 3 unifies all configurations into a single file, config/app.php. Cakes 1 & 2 uses multiple config files such as Config/database.php, Config/email.php and Config/core.php. This repo has an app-cake2/ folder that demonstrates how to adapt the Email and Database configurations to load from Configure, making them automatically "environment-aware" and bringing them in-line with Cake 3.
  • Configure::load() behaves very differently in Cake 1. It will overwrite keys wholesale, whereas Cakes 2 and 3 will merge keys deeply using Hash::merge(). This can have unexpected results in both cases.

There is a Cake 2.x demo app available that demonstrates the necessary changes.

Feedback?

This is GitHub: Feel free to open an issue.

License

Brian Porter, 2015

CC BY-SA 4.0

Code released under the MIT license.

cakephp-envawareness's People

Contributors

beporter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

cakephp-envawareness's Issues

Cache Duration in Cake 2.x

Hi Brian,

I just noticed a bug in your 2.x core.php. It jumped out at me because I had the same bug in my production app until a few months ago!

Line 25:
Configure::write("debug", 0);

Line 334:

// In development mode, caches should expire quickly.
$duration = '+999 days';
if (Configure::read('debug') > 0) {
    $duration = '+10 seconds';
}

Environment overrides are not called until later in the file. Therefore, when the value of debug is checked, it is always zero. As a result, dev environments will have long caches.

I am working on implementing your system into our app at the moment and my solution to this is simply to define Cache.Duration as a configuration key in core-{$env} and move the app override code further up core.php.

"The app must perform all environment-specific logic at that point only." Confusing

Ref: https://github.com/beporter/CakePHP-EnvAwareness/blob/a83170ddc7c6a8970471db04bac6a4f40d6867e2/slides/env-aware-cake-apps.md#env-specific-settings-are-checked-and-loaded-once

You say it should only perform all environment specific logic at that point only, but really everything is environment specific as defined by the APP_ENV, what you really mean is the application should figure out it's environment in one place and only one place.

Does Configure::load() throw an exception and prevent loading of local file?

@garethellis36 / @garethellis mentioned to me at CakeFest that there might be an issue with this code snippet :

try {
    $env = getenv('APP_ENV');
    Configure::load("app-{$env}", 'default'); // If this file doesn't exist (say in production)...
    Configure::load('app-local', 'default'); // ...does this still execute?
} catch (\Exception $e) {}

The answer is: I'm not sure-- I don't think I've tested it. If it does, it could be an issue specifically in production situations, where an APP_ENV var isn't meant to even be explicitly required or set. The solution is straightforward though:

try {
    $env = getenv('APP_ENV');
    Configure::load("app-{$env}", 'default');
} catch (\Exception $e) {}
try {
    Configure::load('app-local', 'default');
} catch (\Exception $e) {}

Not quite as succinct, but probably safer. Has anyone tested this? It shouldn't be too difficult using the demo vagrant box in this repo.

Multiple "environments" on the same machine?

I'm responding to Gareth's questions via Twitter (here and here) in an issue because it's a little easier without a character limit and because I like having this discussion right here with the code and slides.

Quoting from above tweets:

Hey Brian, regarding your environment awareness stuff from #Cakefest, any suggestions on how to handle 2 envs on same server? We have staging & demo environments on same server :-) IIS env variables can handle HTTP, but shell might be tricky?

Windows makes this a little harder than *nix does, since on linux, Mac, etc, you can run individual commands with a specific env var using this style:

# In this example, APP_ENV will be set to "demo"
# for this **single** execution of bin/cake
$ APP_ENV=demo bin/cake some_shell arg1 arg2

This approach is a little harder in Windows. That said, the there are a couple things you can do here:

First, I'd recommend choosing one of the environments as "primary" (if that's applicable) and setting a machine-wide global command line environment variable for that. (The thinking here is that if you ever forget to manually set the environment, the "important" one should be in effect regardless. I like making it as hard as possible to "forget" something like this, although there are probably cases where "neither" is a better option but ask me about that separately.) Then you can use a batch script to override that environment var and put you in a CLI for the "other" environment. You'll have to use that script to set up the proper secondary environment, but that's kind of unavoidable when you have two envs on a single machine.

In this kind of situation, the most important goal (in my opinion) is to make as easy as possible to put yourself into each environment reliably and repeatably, so even if you end up just placing two shortcuts on your Desktop that open a command prompt, set the proper env var and cd to the proper webroot for you, that should get the job done. And really, this is actually pretty similar to the "shortcuts" you use to access the different environments on that machine from a browser (via different URLs).

A third option would be to maintain two different login accounts for each environment, with per-user env vars set for each, but that's getting a little extreme and probably more complicated than it's worth.

Like I said: Just try to give yourself quick, reliable ways to get to each env. If anyone has better suggestions, I'm definitely all ears.

Question - how to get app URL from CLI?

Hi Brian,

I have another question for you, this time about how you get the correct app URL when in the CLI. Our app sends emails from shell scripts , and these emails contain links to pages in the app.

Our current environment detection system is pretty clunky and probably breaks a bunch of the rules you outlined in your talk!

First of all, it checks if $_SERVER["HTTP_HOST"] is set. If not, it does some further file_exists() calls to look for files which identify which server it's on (because I hadn't heard of environment variables when I wrote this!). Based on the results of that, HTTP_HOST is then set. i.e. if file_exists("this_is_production") returns true then $_SERVER["HTTP_HOST"] is set to the production URL.

Then follows a switch block which switches on the value of $_SERVER["HTTP_HOST"] and sets environment vars for each possible URL.

Finally, it checks if FULL_BASE_URL is defined (app is still on Cake 2.2 before this was deprecated), and if not, defines it as being equal to $_SERVER["HTTP_HOST"]. This gives the app access to the correct app URL whether running as HTTP or CLI.

How would you handle this with your method? The only thing I can think of is to set the URL with the Configure class in app-{$env}?

Using "must not wrap retrieved config values" seems too restrictive.

Very good talk today. I only had one comment.

The app must not wrap retrieved config values in conditionals.

I agree with what you are saying in general (you clarify it in the next sentence), but there are cases where you want to ensure they are defined, like Google.Analytics.tracking_id. And it seems there must be cases where the logic gets too complex to avoid an if/else or switch dependent on config.

So "must not wrap" might be better worded as "should avoid wrapping".

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.