WordPress.org

Make WordPress Core

Opened 5 years ago

Last modified 2 hours ago

#33161 accepted enhancement

Create a standard for defining and identifying site environment

Reported by: krogsgard Owned by: SergeyBiryukov
Milestone: 5.5 Priority: normal
Severity: normal Version: 4.2
Component: Options, Meta APIs Keywords: needs-patch
Focuses: Cc:

Description

There are a number of reasons I can think of that would make a site environment identifier quite handy.

What I'm proposing: The ability to potentially define and/or identify a site environment, i.e., local, development, staging, live.

For instance, if it were a constant, then it could be defined:

define('SITE_ENVIRONMENT', 'live');

Third party solutions like Mark Jaquith's WP_LOCAL_DEV are already popular, but this would be in core and include more environments.

I know that adding constants isn't popular, but this could also allow core as well as third party plugins and themes to remove code that attempts, often not that well, to determine the same kind of information. A definition in the site's config could help make that easier and more accurate.

I don't know if a constant is the best route for enabling the definition and identification of the site environment, but I'd like to start a conversation.

Change History (31)

#1 @krogsgard
5 years ago

To be fair upfront, one of the biggest downsides is that this would happen in wp-config.php and that's one of the ideal places to use this information.

It'd be great to more easily define site config constants (like database details) based on what environment is. I don't know the way around that, and that's my biggest question as to whether a constant would be the right route, or if there is a more low lying issue that would need fixing to make this the most valuable.

#2 @Frank Klein
5 years ago

You mention that Core does attempt to find out in what environment the code is running, would you mind pointing me towards an example for this?

In my opinion, your code should not care where it runs. Local, preproduction, production, those are just deploys of the same code base. Meaning the behavior of the code base should not change depending on the environment.

The current way of handling connection details in wp-config.php, and the workarounds like the one that Mark has written about are fine for use cases in which you do not want to put a deployment procedure into place.

However if you are at the point that you have a centralized development version, a staging environment, etc. then the time for poor workarounds like conditional handling of database connection information is gone.

There are solutions for all the problems that people encounter. There is software to automatically provision machines in the different environments to keep the stack the same. The config can be stored in environment variables. Differences in asset handling (unminified in development, minified in production) can be handled through build scripts.

There's a great site about best practices like this: http://12factor.net/

So in short I'm not in favor of this ticket. It encourages outdated development techniques and actually makes things more difficult for developers. Web development has moved on from these techniques, let's move along with it.

#3 @Maelacuna
4 years ago

What about incorporating dotenv?

https://github.com/vlucas/phpdotenv

This would avoid ever having to edit wp-config.php for configuration, like database credentials, as well as giving the ability to define enviroment variables for plugins, themes, etc. if desired. API keys spring to mind as an example. These things can then be kept out of the code base, out of version control, and even out of the document root if desired.

Frank, item 3 of the 12 Factor app is specifically about storing config in the environment and not in the code base, so I'm confused why you reference it and then say you're not in favour?

Here is an article by Eric Barnes highlighting one way that dotenv could be incorporated in to WordPress to help manage environments:

https://dotdev.co/tutorials/secure-wordpress-config-dotenv/

Last edited 4 years ago by Maelacuna (previous) (diff)

#4 @ocean90
13 days ago

#50166 was marked as a duplicate.

#5 @joostdevalk
12 days ago

While @ocean90 just marked my issue as a duplicate, that doesn't bring all the nuance in that issue here. Especially that there's now prior art in three of the biggest plugins out there for doing this, Jetpack, Woo and Yoast. We need to address this.

This ticket was mentioned in Slack in #core-site-health by joostdevalk. View the logs.


12 days ago

#7 @joostdevalk
12 days ago

Moving the context from that duplicate ticket to here:

WooCommerce subscriptions solved this for themselves, as can be seen here. Jetpack has "staging mode". We at Yoast partly fixed it and will need to fix it even better soon. Others _must_ run into this problem, where you don't want to show certain notices on a staging site that you do want to show on a live site.

Would it be a good idea to set an option in the site settings, under Settings -> General, that toggles between three states:

  • development
  • staging
  • live

Where "live" would obviously be the default.

This would allow hosts that create staging sites to switch this toggle when they move a site to a staging environment etc. This way, Yoast SEO for instance could automatically block indexing of development and staging sites, and instead open up indexing on live sites. Other plugins like the Woo Subscriptions case above could also be solved much more uniformly.

#8 @SergeyBiryukov
12 days ago

  • Milestone set to 5.5
  • Owner set to SergeyBiryukov
  • Status changed from new to accepted

Not sure this should be a UI option. Let's start with a filterable function, wp_get_environment_type() or something like that. If someone has ideas for a better name, please share :)

#9 @SergeyBiryukov
12 days ago

  • Keywords needs-patch added

#10 @nathanrice
12 days ago

+1 from me. Would definitely be useful for us at StudioPress/WP Engine.

#11 @grierson
11 days ago

This would be very helpful for other areas of sites looking to use things like Elasticsearch as well (specifically on our Jetpack Search feature). Big benefit if you can see what state the site is in and not accidentally start indexing large amounts of data on a testing site.

Last edited 11 days ago by grierson (previous) (diff)

#12 @jchristopher
11 days ago

Yes, definitely in favor of something proper here.

Adding to the use cases: conditionally plugging wp_mail() in non-production environments to as to not inadvertently send n automated/transaction emails. I heard this happened to a friend once and it was a bad day for my friend.

Last edited 11 days ago by jchristopher (previous) (diff)

#13 @Clorith
11 days ago

I've had this come up a couple times as well of late, and made some thoughts on the approach, heck, I've made a quick code-draft with my thoughts on how to approach it, I don't want it to be a dashboard setting, but I see the value in flexibility.

System environment variables are worht their weight in gold for those who have dedicated stage servers, while constants are fixed values that can be used to override this in one-off situations, or where system environment variables are not available for whatever reason.

And of course, filters, you may have a plugin to handle this in various cases.

It's also important to limit the outputs, or at least to an extent, to ensure the response is an expected one.

I'm not 100% sold on filtering the available approved environments or not, but I put it in as a thought to not disregard it straight away though.

<?php
function get_environment() {
        $approved_environments = array(
                'development',
                'stage',
                'production',
        );
        
        /*
         * Filter specs for allowing other environment types  for very unique setups.
         */
        $approved_environments = apply_filters( 'wp_approved_environments', $approved_environments );

        $current_env = '';

        // Check if a environment variable has been set for max flexibility, if `getenv` is available on the system.
        if ( function_exists( 'getenv' ) ) {
                $has_env = getenv( 'WP_ENVIRONMENT' );
                if ( false !== $has_env ) {
                        $current_env = $has_env;
                }
        }
        
        // Fetch the environment from a constant, this overrides global system variables for consistency.
        if ( defined( 'WP_ENVIRONMENT' ) ) {
                $current_env = WP_ENVIRONMENT;
        }
        
        /*
         * Filter specs for setting the environment programatically.
         */
        $current_env = apply_filters( 'wp_current_environment', $current_env );
        
        // Make sure the environment is an allowed one, and not accidentalyl set to an invalid value to try and avoid breaking stuff relying on this.
        if ( ! in_array( $current_env, $approved_environments ) ) {
                $current_env = 'production';
        }
        
        return $current_env;
}

#14 follow-up: @davidvee
11 days ago

To echo @nathanrice... I did a little checking with folks here at WP Engine / Genesis / Flywheel / Local WP and we can think of a good number of use cases where this would be helpful for our customers & products. As with other plugin authors, we have our home-brew ways of dealing with this today. Having a standard would be helpful.

#15 in reply to: ↑ 14 @jackfungi
11 days ago

+1 we see this all the time. For example, Jetpack is crazy popular and doesn't run well on staging or local environments. Support for hosts is hard-coded into the plugin itself: https://github.com/Automattic/jetpack/blob/5faa321993c4ca955df871f5a77cd89fa411bca5/packages/status/src/class-status.php#L91

Local would benefit greatly from something like this!

Replying to davidvee:

To echo @nathanrice... I did a little checking with folks here at WP Engine / Genesis / Flywheel / Local WP and we can think of a good number of use cases where this would be helpful for our customers & products. As with other plugin authors, we have our home-brew ways of dealing with this today. Having a standard would be helpful.

#16 @johnbillion
11 days ago

I'd like to hear a bit more about specific use cases so we can more clearly identify the problem before proposing solutions.

Assuming core implements functionality that allows the environment to be determined as one of development, staging, or production, what does this enable?

#17 follow-up: @johnbillion
11 days ago

The reason I'd like to hear specifics is because every time I encounter this problem, the logic that alters behaviour based on the environment happens really early, in wp-config.php, meaning a filterable function will trigger too late for a lot of per-environment logic.

#18 @tabrisrp
11 days ago

We have this need too in WP Rocket since recently.

A use case is for example our RocketCDN service integration. Users are able to subscribe to the service from the plugin itself, but we don't want them to do that when it's a local or staging site.

Right now we have our own function to check for that https://github.com/wp-media/wp-rocket/blob/c7a020448d6639ca3cb4cddf72195f9d9c18fbec/inc/functions/api.php#L85 but a core solution would make it unnecessary, as we could rely on the hosts/local solutions to provide that information to WP for the most part instead.

#19 @knutsp
11 days ago

Something like the idea behind this little plugin of mine?
https://github.com/Nettvendt/wp-local-dev

#20 @Clorith
11 days ago

I'd love to walk through it (since I made a very tangible proposal).

Firstly, it's a function used for grabbing the current environment, this is to ensure everyone fetches the values the same way (to avoid using different terms and such).

As you mentioned, the function would need to be loaded fairly early, probably alongside the same time as the fatal error handler and similar things, to make it usable in most scenarios.

As for the filtering possibilities, although possible to circumvent by running code directly instead of through hooks, _most_ (based on my own experience at least) actions that fire would come from another hook or later down the call stack.

Some examples where diverting based on environment would be useful:

Outbound emails could be filtered, and if get_environment() isn't set to production then send them to the developer, allowing for legitimate email tests in stage setups without worrying about emails reaching end users.

API endpoints where you want to use a test endpoint when not in production to prevent data pollution.

Providing a wp-admin notice for non-production sites so it's much clearer that what you are doing now will/won't affect the site your business relies on.

All of those examples fire so late that plugins would have had a chance to kick in a filter.

As for why they might want to filter, you may use a "staging-plugin" to change environments on your site on the fly for testing purposes, or possibly conditionally set the environment "the core way" on a legacy project which has been doing it in some custom way without a good way to change that, but wanting to ensure compatibility with other plugins/themes moving forward.

#21 in reply to: ↑ 17 @ev3rywh3re
11 days ago

Replying to johnbillion:

The reason I'd like to hear specifics is because every time I encounter this problem, the logic that alters behaviour based on the environment happens really early, in wp-config.php, meaning a filterable function will trigger too late for a lot of per-environment logic.

I was actually looking to see if there were comments beyond the dev, staging, and live usage. For a specific use how about a temp site built on a local vm used for quick and dirty host/domain migrations? I’ve done this with a hacked site in the past as well for content restoration.

#22 @joostdevalk
9 days ago

@johnbillion with the patch proposed above you could set WP_ENVIRONMENT before wp-config even loads and fix it.

#23 @Rastaban
7 days ago

I really like @Clorith's excellent suggestion.

We are adding our own custom environment vars here at Pantheon so plugin authors have the option of doing this but it would be so nice to have this in core so plugin authors don't need to write host specific code.

The original proposal defined local as a possible option. Is there a need for a local definition or does using development for local give the same outcome for plugin authors?

Last edited 7 days ago by Rastaban (previous) (diff)

#24 @danielbachhuber
7 days ago

One suggestion on how to possibly make this a user-facing enhancement: different admin styling (colors or otherwise) for staging and local environments.

#25 @joostdevalk
7 days ago

One suggestion on how to possibly make this a user-facing enhancement: different admin styling (colors or otherwise) for staging and local environments.

I like that idea, would also prefer changing the favicon.

But: I don't want *anything* to block shipping the bare bones version of this ASAP :)

#26 @johnbillion
7 days ago

Yeah there's a few options we can do there, one that I've done on WordPress.com VIP is to use an environment indicator in the admin toolbar but I don't think that's anything we should do for the first version in core.

This ticket was mentioned in Slack in #hosting-community by mike. View the logs.


7 days ago

#28 follow-ups: @SergeyBiryukov
7 days ago

This seems to have consensus, my only concern is that get_environment() function name seems too general, I would think it returns something like phpinfo() or Site Health info. I also think we'd want to add a wp_ prefix to avoid a potential clash with any custom functions.

How about:

  • wp_get_environment_type() for the function and filter name.
  • WP_ENVIRONMENT_TYPE for the constant name?

#29 in reply to: ↑ 28 @joostdevalk
7 days ago

How about:

  • wp_get_environment_type() for the function and filter name.
  • WP_ENVIRONMENT_TYPE for the constant name?

Sounds good to me.

This ticket was mentioned in Slack in #hosting-community by jadonn. View the logs.


6 days ago

#31 in reply to: ↑ 28 @audrasjb
2 hours ago

Replying to SergeyBiryukov:

How about:

  • wp_get_environment_type() for the function and filter name.
  • WP_ENVIRONMENT_TYPE for the constant name?

Indeed, in this case I think it's really necessary to use wp_ prefix, as there may be a lot of existing implementations.

Note: See TracTickets for help on using tickets.