WordPress.org

Make WordPress Core

Opened 23 months ago

Last modified 16 months ago

#37646 new enhancement

Make wp-settings.php a series of do_actions()

Reported by: johnjamesjacoby Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Bootstrap/Load Keywords: 2nd-opinion dev-feedback
Focuses: Cc:

Description (last modified by johnjamesjacoby)

Now that #36819 is in, my master plan for wp-settings.php can begin.


Problem

wp-settings.php makes many assumptions, many on purpose, others by necessity, some on consequence, and a few by accident. It is somewhat poorly named for what it is, and it's a mishmash of globals, function calls, class instantiations, and do_action() calls.


Solution

do_action() all of the things.

Make wp-settings.php a series of action calls. One for setting versions, one for initial constants, one for environmental setup, translations, database, plugins, themes, users, template output, and so on...

Introduce a file named wp-includes/default-actions.php that serves 2 purposes:

  • Includes a bunch of new functions that wrap up sections of what's already in wp-settings.php
  • Hooks those new functions into the new actions in wp-settings.php

Why do we do this?

As more robust and sophisticated plugins, themes, APIs, and systems start to use, rely on, and bend WordPress to their will, the need to override more & more pieces becomes apparent. While WordPress comes with a very handy set of default post types, taxonomies, APIs, helpers, wrappers, and tools, it may be desirable to unhook (or never load) certain pieces so that other pieces can take their place.

In the past, this is done only with great intent, with strategic actions & filters in places where specific needs are being addressed. This is good in that it's predictable, but bad in that it's impossible for anyone to truly know what action or hook is *best* to perform any given subsequent action.

By breaking wp-settings.php up into many clearly named do_action() calls, it becomes clearly obvious what actions perform what duties, while also introducing literally maximum flexibility in the entire system for new and exciting things to happen around WordPress itself.

Imagine something like:

// Load versions
do_action( 'wp_settings_load_versions' );

// Load constants
do_action( 'wp_settings_load_constants' );

// Load translations
do_action( 'wp_settings_load_translations' );

// Load environment
do_action( 'wp_settings_load_environment' );

// Load early WordPress settings
do_action( 'wp_settings_load_early' );

// Load database
do_action( 'wp_settings_load_database' );

// and on, and on...

Epilogue

This is a huge idea, easily scoffed at, and introduces code-churn like whoa. It would mean doubling down on WordPress's actions API, trusting it implicitly to load all of WordPress's core pieces & parts. It would open many doors to many unforeseen oddities while developers start dissecting all the ways things are tied together. It would also enable really cool external tools, like REST API drop-ins that can SHORTINIT WordPress if auth is missing, or WP CLI commands that can die() literally anywhere in the stack after they've done what they need to do.

This is something I've wanted in WordPress since 2006 having seen similar in other libraries, and even old BackPress & bbPress gave nods and hints to back in the day.

I'm also happy to give this a first patch if it's helpful to see visually the destruction it causes, or guide someone else along my vision for this if someone is willing and able to see it through before I am. <3

Change History (10)

#1 @johnjamesjacoby
23 months ago

  • Description modified (diff)

I used the incorrect formatting for the code block.

This ticket was mentioned in Slack in #core by pento. View the logs.


23 months ago

#3 @lgedeon
22 months ago

With appropriate warnings in the PHPdoc for each action and everywhere these are ever mentioned, this could be really awesome!

This ticket was mentioned in Slack in #core by jorbin. View the logs.


22 months ago

#5 follow-up: @jorbin
22 months ago

  • Keywords dev-feedback added; needs-patch removed

Discussed this a bit in the linked bug scrub. Some thoughts:

This is both an exciting and scary proposal.

My biggest worry with tying all includes to actions is that it would make all of WordPress pluggable and that feels like a great way to increase breakage on updates. Image a plugin that decides to include it’s own version of src/wp-includes/category-template.php. Core could never add a new function to that file again since it can’t count on that file existing. The Autoloader being worked on in #36335 will make it so that we don't need to worry about this. This seems like a dependency before I would want to include more actions in the bootstrap process.

We need to consider a lot of "what if" situations with this change, let's try to brainstorm those out here while we continue to explore this issue.

I'm removing needs-patch because this needs more discussion before we write code.

#6 in reply to: ↑ 5 ; follow-up: @DrewAPicture
22 months ago

Replying to jorbin:

My biggest worry with tying all includes to actions is that it would make all of WordPress pluggable and that feels like a great way to increase breakage on updates. Image a plugin that decides to include it’s own version of src/wp-includes/category-template.php. Core could never add a new function to that file again since it can’t count on that file existing. The Autoloader being worked on in #36335 will make it so that we don't need to worry about this. This seems like a dependency before I would want to include more actions in the bootstrap process.

We need to consider a lot of "what if" situations with this change, let's try to brainstorm those out here while we continue to explore this issue.

I'm not so sure we really do need to consider the what-ifs so much. If we make it clear that messing with how core loads is effectively "voiding the warranty" I'm not sure core support should necessarily extend beyond how it's intended to work.

There's an immense upside in enabling proverbial forks of WordPress to do what they wish with how WordPress is loaded, and very little downside in enabling that kind of flexibility, in my opinion – again, with the caveat that hooks as-is and as-hooked is how core expects it to work. Any deviation is uncharted territory.

This ticket was mentioned in Slack in #core-restapi by chopinbach. View the logs.


20 months ago

This ticket was mentioned in Slack in #core by nickduncan. View the logs.


20 months ago

#9 in reply to: ↑ 6 @MikeSchinkel
16 months ago

Replying to jorbin:

My biggest worry with tying all includes to actions is that it would make all of WordPress pluggable and that feels like a great way to increase breakage on updates.

I definitely can see @jorbin's perspective. Making WordPress easier to break has the potential for rogue plugin author to break sites for users unable to fix the problem or even understand the problem, and that makes WordPress look bad, in users eyes and in the eyes of a press who likes to sensationalize problems.

Replying to DrewAPicture:

There's an immense upside in enabling proverbial forks of WordPress to do what they wish with how WordPress is loaded

And yet I definitely see @DrewAPicture's perspective, because I am usually in this position of wanting to get WordPress to work they way I need it to work for a highly customized site yet I am stymied because of some limitation baked into core. For those of us building custom sites for businesses, this lack of flexibility turns out to be a huge downside and perpetuates the "WordPress is just a blog" mentality among IT professionals and those business people who parrot them.

What's the middle ground?

I've often thought that there should be something that a plugin can't to and a theme can't do but that a custom site builder could do to turn on "unsafe" features, such as having a constant defined in wp-config.php that, once defined, allows custom site builders to shoot themselves in the foot, if we so desire. But themes and plugins could not do it unless they get the their end-users to manually update wp-config.php, which most won't want to do because of the bad user experience required. OTOH I've never before proposed it because I always feared that too many people would see it as not "the WordPress way." FWIW.

#10 in reply to: ↑ description @MikeSchinkel
16 months ago

Replying to johnjamesjacoby:

An interesting proposal. On the surface I like it.

OTOH I see several causes for concern that I don't think you covered. (If you did, blame my lack of reading comprehension on longish tickets. :-) )

  1. The first problem is chicken-and-egg. Assuming you are not talking about wholesale reorganization of wp-settings.php then most of your do_action() calls occur before mu-plugins are loaded, so where are you could to call add_action()? advanced-cache.php is called early, but not early enough for your example.

And do we want to pollute advanced-cache.php with site-custom add-action calls? Minially you'd need bootstrap.php file early, but even it could not be called before constants are defined unless it was to be placed in the site root where ABSPATH points, and not good if you moved WordPress core into a subfolder for Composer sake.

  1. While I do like the idea of hooking, your approach is problematic IMO. It is equivalent to approaches I have seen with some themes, like (early) Thesis (when I still used it) that caused emmense heartaches. And in admin-filter.php, not an approach I think we should emulate.

Basically with approach you posted a reader of the code cannot know what default code will be executed without looking in another file, or they must run it under a debugger to be able to tell. That makes static analysis of the code more difficult and makes it much harder to learn.

Better would be something like this:

require WP_CONTENT_DIR . '/bootstrap.php';

add_action( 'wp_settings_load_versions',     'wp_settings_load_versions' );
add_action( 'wp_settings_load_constants',    'wp_settings_load_constants' );
add_action( 'wp_settings_load_translations', 'wp_settings_load_translations' );
add_action( 'wp_settings_load_environment',  'wp_settings_load_environment' );
add_action( 'wp_settings_load_early',        'wp_settings_load_early' );
add_action( 'wp_settings_load_database',     'wp_settings_load_database' );

if ( apply_filters( 'do_bootstrap', true ) ) {
	do_action( 'wp_settings_load_versions' );
	do_action( 'wp_settings_load_constants' );
	do_action( 'wp_settings_load_translations' );
	do_action( 'wp_settings_load_environment' );
	do_action( 'wp_settings_load_early' );
	do_action( 'wp_settings_load_database' );
}

Or even better:

require WP_CONTENT_DIR . '/bootstrap.php';

$bootstraps = array(
	'wp_settings_load_versions',     
	'wp_settings_load_constants',    
	'wp_settings_load_translations', 
	'wp_settings_load_environment',  
	'wp_settings_load_early',        
	'wp_settings_load_database',     
);
foreach( $bootstraps as $bootstrap ) {
	add_action( $bootstrap, $bootstrap );
	if ( apply_filters( 'do_bootstrap', true, $bootstrap ) ) {
		do_action( $bootstrap );
	}
}

Then elsewhere:

function wp_settings_load_versions() {
	// Load versions
}
function wp_settings_load_constants() {
	// Load constants
}
function wp_settings_load_translations() {
	// Load translations
}
function wp_settings_load_environment() {
	// Load environment
}
function wp_settings_load_early() {
	// Load early
}
function wp_settings_load_database() {
	// Load database
}

With the above any reader of WordPress core code could more easily determine what is being called by default during bootstrap rather than having to hope they have found all the appropriate needles in the haystack that is the WordPress core codebase.

  1. I assume you are aware of the <a href="http://becircle.com/drupal_7_line_by_line_part_2_bootstrap">prior art in Drupal</a> with it's bootstrap process? Not saying their's is an approach to emulate but maybe they have learned some things we don't want to repeat that might be available to learn from their public writings on the topic?
Note: See TracTickets for help on using tickets.