WordPress.org

Make WordPress Core

Opened 8 years ago

Closed 7 years ago

#2866 closed enhancement (duplicate)

WP can't be used from a single source installation

Reported by: RuddO Owned by:
Milestone: Priority: normal
Severity: normal Version:
Component: General Keywords:
Focuses: Cc:

Description (last modified by masquerade)

In summary, WordPress can't be used in a single source installation.

To achieve this, I'm using a technique which involves having WP in a master directory, plus a "phantom" directory for each different blog - where wp-config.php resides, and the other WP files are symlinks to the master directory's contents. I thus, for each blog, set the Apache DocumentRoot to the blog's phantom directory (which contains that blog's wp-config.php) rather than to the master directory.

This would work okay, except for the fact that, on wp-blog-header.php, if ( !file_exists( dirname(__FILE__) . '/wp-config.php') ) { does not work, because FILE points to the *real* wp-blog-header.php instead of to the wp-blog-header.php on the phantom directory. Of course, this results in wp-config.php "not existing".

Since, for all general executions of the blog engine, dirname(FILE) in wp_blog_header.php should equal the results of getcwd(), I'm changing this. Attached is the patch that performs this fix on WordPress 2.0.3 (plus nonces fixes).

{{{All plugins that use this method are broken. BRO-KEN. By design. In other words, it's a really dumb idea.

Why? Because str_replace(dirnameFILE with siteurl) can't be assumed to yield the right URL.

  • odd WP installation paths or Apache mappings can make the replacement useless - }}}

No, any string should be replaced by any string fine. Now, please, before you go on telling us where this is so wrong, a use case outside of your single hack of trying to make WordPress-MU out of a normal install. I'd like to be accomodating to your attempts, but suggesting that every plugin is broken in a place where we have never heard anyone complain that they are broken in a real environment despite hundreds of thousands of testers for the past few years is a bit of insanity.

Attachments (4)

wp-blog-header-phantom-fix.diff (1.1 KB) - added by RuddO 8 years ago.
fix for running multiple WP blogs with a single source dir
supplementary-phantom-fix.diff (2.0 KB) - added by RuddO 8 years ago.
Supplementary fixes for some files
wp-widgets-phantom-fix.diff (1.2 KB) - added by RuddO 8 years ago.
Fix for WordPress widgets naivete
mirrorlinks-1.0.0.tar.gz (1.7 KB) - added by RuddO 8 years ago.
mirrorlinks, used to easily create phantom WP installs

Download all attachments as: .zip

Change History (22)

RuddO8 years ago

fix for running multiple WP blogs with a single source dir

comment:1 RuddO8 years ago

  • Type changed from defect to enhancement

comment:2 RuddO8 years ago

Yes, I can confirm that this works even in wp-admin/

comment:3 RuddO8 years ago

I'm adding a second patch which fixes other files - got this while setting up a fourth blog.

RuddO8 years ago

Supplementary fixes for some files

comment:4 RuddO8 years ago

I know that this is not probably the best place, but I have another patch, this time for the Widgets plugin.

The Widgets plugin in its naiveté assumes that ABSPATH and dirname(FILE). Wrong when using phantom directories to run WordPress. The attached patch fixes it.

RuddO8 years ago

Fix for WordPress widgets naivete

comment:5 matt8 years ago

This is unlikely to change in core, as just like the widgets plugin there are probably dozens of plugins that use the same method we use for determining directories.

comment:6 ryanscheuermann8 years ago

I'm just looking over this ticket and I understand what you're trying to accomplish, but why not replace wp-config.php in the actual root with a file that just includes a "wp-config-phantom.php" (using getcwd())? That way you don't have to modify all the WP core files that include wp-config.php using the FILE method, including plugins.... Can you follow me?

I think that will work, but I'm not positive. Let me know if it does.

I don't think changing the WP core to use getcwd() is a good idea, as matt mentions, more plugins than you can imagine are using this method.

comment:7 RuddO8 years ago

OK. Let's recap. This bug touches two different (and sensitive issues):

ISSUE 1: THE WP-CONFIG ISSUE IN WP CORE

say we have A: a pristine WP source dir
say we have B, C, D: three different Apache DocumentRoots, targets for WP

How can we work with a single source dir, instead of having to manually propagate changes from B into A, C, D?

Answer: UNIX symlinks. Make B, C and D be full with folders and symlinks to A's contents, as an exact mirror. Then place a wp-config.php file on B, C and D.

Results? Edit a file in A, and B C D see the updates. The same applies in all other directions. Add one level of indirection (say, make A a mirror of a WP 2.0.3 source tree) and a bit of care, and you're set for nearly atomic WP updates (zero downtime) because you can later on swap A for A', which may be a symlink mirror of WP 2.1 source.

(the actual case is actually much, much more complicated -- but fully automated now -- since I'm integrating disparate wp-contents, wp-content/themes and wp-content/plugins for each DocumentRoot, but this is the fundamental principle. I only mention this to show that it isn't actually possible to build a conditional wp-config.php because the three sites must have different contents and some of those contents come from a disparate branch checkouts, but some of those contents are shared, including pristine WP core and a base of free plugins which undergo constant updates and bug fixes by me. No, I can't start checking wp-configs.php into my SVN repos - that would be asking for real trouble with my customers in a few days - plus wp-config.php is CONFIG, not APP code - it doesn't just belong in an app repo.)

The fact that WP includes wp-config.php using FILE which does not work when the WP source directory is a tree of symlinks, because for some reason FILE will point to the symlink target instead of the symlink. Since FILE points to the real file, dirname(FILE) . "/../" usually points to the file's parent directory instead of the "symlink mirror dir" with the actual wp-config.php. Obviously, wp-config.php does not exist in the actual source drop which is taken as a model to build the symlink mirror. The *only* way to get wp-config.php is to look at the output of getcwd(), which apart from being faster than dirname() calls, actually uses the CWD (current working dir) - which is correct for symlink shadow copies.

The tree of symlinks is used over here *a lot* to avoid duplication of files and work on single source checkouts (and it should also be used in large hosting farms, because this lets people upgrade their WP atomically - a big boon in large hosting farms again). This in practice means that as I update one site's code, the others automatically get the fix, without me needing to manually merge changes in N repositories.

Of course, with a tree of symlinks, five or ten different Web sites can run from a single source checkout. Imagine making N WP's running off N databases, without actually having to manage separate source drops... BTW, If someone is reading this and wants to save a huge amount of disk space and money in his big, big WP deployment, sure, e-mail me (rudd-o at rudd-o dot com) and I'll do it for you at fair rates ;-). I built the software to create and automate this task.

ISSUE 2: PLUGINS USING THE FILE METHOD

Of course, fixing wpcore was the easy part (a few greps and seds, and that's it). But I later discovered that the Widgets plugin was not working on my newly setup blogs. I couldn't drag or drop any sidebar widgets. I took the time to inspect the source of the admin page, and, lo and behold, script language=javascript src=/home/rudd-o/Projects/Websites/...

I said "what the HELL? How can a plugin be issuing an HTML resource request with an absolute path?

The culprit was the source lines I changed on the plugin (you can see the diff). What was going on? Simple. The widgets plugin was attempting to determine its wp-content public directory (to load the JS files) by str_replacing dirname(FILE) with get_settings(siteurl). Forgetting for a moment that this is hackish (to say the least) and it might lead to problems under certain setups, I posit the following:

All plugins that use this method are broken. BRO-KEN. By design. In other words, it's a really dumb idea.

Why? Because str_replace(dirnameFILE with siteurl) can't be assumed to yield the right URL.

  • odd WP installation paths or Apache mappings can make the replacement useless
  • *of course* FILE may very well be a symlink, but FILE will point to the actual file. If you have your WP source at /home/r/wpsource and you have your DocumentRoot be a symlink shadow, the replacement won't work at all.

Basically the Widgets plugin as it currently stands punts the issue, completely ignoring the fact that a file can have two completely ifferent paths on UNIX, and still be the same file. PHP compounds the problem, of course.

The fix I submitted for the Widgets plugin was the most reasonable compromise I could find. It still dynamically computes its installation directory (while it should actually be traversing *up* the dir hier to find wp-content and then use the children dirs - a technique I've seen successfully used in countless plugins, and a technique that actually yields much better results than the one used in Widgets).

Matt, what you just said about "other countless plugins" using the same technique... dude, that theory doesn't hold water. The theory that countless other plugins are broken (and in my personal supercharged 3rdparty plugin mishmash, so far I haven't found any that uses this technique) does NOT matter ZILCH in the face of the fact that Widgets also needs fixing. I respect your opinion, but the "other boys do the same brokenness as well" argument just doesn't fly with me :-)

Ryan: your proposed solution can't work either because (if I understood you correctly) the actual source dir can't have a wp-config.php - it's a pristine source directory.

I actually don't see a problem with the changeset I submitted (at least for the WP core changes). It's short, it does not touch large amounts of files, the changes it introduces are localized... I've tested it throughly and no bugs seemed to have arisen at all.

If anything, advanced sysadmins have gained a new choice (choice that of course why should be denied?). As I already stated, using FILE in plugins to generate relative URLs in plugins is broken code that should not be relied upon and is asking for trouble. They can of course just use getcwd() which throws correct results in all situations (except made for the wp-admin/ case where of course this can be tested for in a short one-liner or simply hard-coded depending on what the plugin wants to do). By correct, I mean "what the sysadmin configured as DocumentRoot".

Sorry for such a longish reply. I felt I had to explain the motivations in deep detail. I also meant to call the attention of you guys to this seemingly okay FILE issue (most of the uses of FILE in WP are fine and dandy, and that's why you didn't see me changing them all - I just changed the ones pertaining to wp-config and of course the widgets plugin).

comment:8 masquerade8 years ago

  • Description modified (diff)

Matt, what you just said about "other countless plugins" using the same technique... dude, that theory doesn't hold water. The theory that countless other plugins are broken (and in my personal supercharged 3rdparty plugin mishmash, so far I haven't found any that uses this technique) does NOT matter ZILCH in the face of the fact that Widgets also needs fixing. I respect your opinion, but the "other boys do the same brokenness as well" argument just doesn't fly with me :-)

It should float very well, but I hate how you assume that because some plugin does what the WP core does with no issue on a regular blog, it is therefore "broken". I'm not sure where you found your definition of broken, but they've been working pretty darn good for us for the past few months.

Your solution is twice as hackish than anything in the core right now, and is fundamentally broken a few ways. First plugin that does chdir() breaks everything. chrdir() is disabled on many servers for security reasons.

  1. First plugin or auto-prepended script to do chdir breaks everything.
  2. getcwd()'s value changes to be unusable with its context, for example during shutdown, where its value becomes "/"
  3. I still fail to

comment:9 RuddO8 years ago

We seem to diverge on this issue.

Here are the technical reasons why I think your last post is flawed. Note that I said "I think".

In this context, "broken"'s definition is simply "assuming things that aren't necessarily true". In this context again, the untrue assumption is that files can only have ONE path name, which is evidently FALSE on UNIX.

You said: "First plugin or auto-prepended script to do chdir breaks everything."

No, no plugin can break anything in WP core by issuing a chdir(), since the getcwd() calls are executed before plugin execution - you can easily see the getcwd() calls are invoked right at the beginning of core engine load. It also doesn't break the patch I submitted for widgets, since the Widgets plugin just uses siteurl, not getcwd().


You said: "getcwd()'s value changes to be unusable with its context, for example during shutdown, where its value becomes "/" "

Now, this isn't affected by WP core + my patches. As I said earlier, plugins should not use FILE to determine public URLs to submit to the browser, and this has nothing to do, absolutely, to what you just said. I also DO NOT advocate to use the getcwd() technique to determine public URLs, as you can see from my patch to the Widgets plugin. In summary, both FILE and getcwd() are broken.

You said "I still fail to"

Of course, I can't reply to this. Your brush seems to be running out of pai

(the last sentence is a half-hearted joke ;-)

comment:10 RuddO8 years ago

Moreover, this discussion is getting EXTREMELY technically detailed. I'm attaching the software I use to create the phantom directories.

RuddO8 years ago

mirrorlinks, used to easily create phantom WP installs

comment:11 RuddO8 years ago

I've attached mirrorlinks. All usual disclaimers regarding warranty or fitness apply.

Guys, I'm practically bending over to get you to see my point. Please don't close yourselves up to the idea that my proposed patches may actually get WP one more use case, without breaking anything. And of course, if you feel there's room for improvement, do suggest! I'm here to help, not to bicker.

(BTW see definition of broken above)

comment:12 RuddO8 years ago

http://rudd-o.com/projects/mirrorlinks/ has the scoop on what mirrorlinks does, in case you hate plain text readmes like me

comment:13 darkfate8 years ago

Is it me, or is this basically WordPress MU.

comment:14 westi8 years ago

Why can you not use hardlinks instead of symlinks sure then all your problems disappear.

Afterall I believe this is the method that webapp-config on gentoo uses sucessfully to achive the same solution without needing any changes to core files.

comment:15 mdawaffe8 years ago

  • Resolution set to worksforme
  • Status changed from new to closed

In the past, I've used a custom wp-config.php to do something similar. There may be some webserver configuration issues.

wp-config.php

<?php
define('DB_USER', 'username');
define('DB_PASSWORD', 'password');
define('DB_HOST', 'host');

define('ABSPATH', dirname(__FILE__).'/');
define ('WPLANG', '');

if ( 0 === strpos($_SERVER['HTTP_HOST'], 'one.example.com') ) :
        define('DB_NAME', 'one');
        $table_prefix  = 'wp_';
        define('CACHE_PATH', ABSPATH . 'wp-content/cache1/');
elseif ( 0 === strpos($_SERVER['HTTP_HOST'], 'one-more.example.com') ) :
        define('DB_NAME', 'one');
        $table_prefix  = 'wp_more_';
        define('CACHE_PATH', ABSPATH . 'wp-content/cache1more/');
elseif ( 0 === strpos($_SERVER['HTTP_HOST'], 'two.example.com') ) :
        define('DB_NAME', 'two');
        $table_prefix  = 'wp_';
        define('CACHE_PATH', ABSPATH . 'wp-content/cache2/');
else :
        define('DB_NAME', 'main');
        $table_prefix  = 'wp_';
endif;

require_once(ABSPATH.'wp-settings.php');
?>

You can also define the cookie and custom table constants where appropriate. I think there may have been a couple other caveats such as making sure the upload directories were distinct.

Then have a web root dir one

.htaccess
index.php
wordpress@ -> /wherever/wordpress/

with an index.php

<?php
/* Short and sweet */
define('WP_USE_THEMES', true);
require('./wordpress/wp-blog-header.php');
?>

/wherever/wordpress/ contains a standard WP install with that custom wp-config.php.

Set WordPress URL = "http://one.example.com/wordpress"

Set Blog URL = "http://one.example.com"

No core mods necessary.

But, since WP isn't really designed with this in mind, I'm going to close the ticket.

comment:16 RuddO7 years ago

  • Resolution worksforme deleted
  • Status changed from closed to reopened

The solution you posted is definitely not what I was looking for. I'm NOT running ONE single WP source install in three different servers, but three different source installs which are phantom installs to separate subversion checkouts, of different branches. A single wp-config.php won't alleviate that.

People, though it's a valuable goal and my patches certainly help to get there, I'm NOT asking for the ability to run a single WP source installation in several web hosts (even if it is from different databases). What I want is to use symlinks (no, hard links don't work, because that messes up subversion workflow) to create custom drops of WP, and have those drops pick up the correct wp-config.php file.

comment:17 RuddO7 years ago

westi: hardlinks can't be used because Subversion nukes them. Confirmed behavior.

Anyway, I submitted this as a different bug because the newer bug is more to the point and the patch is more concise and doesn't have the drawbacks some pointed on this bug. Feel free to nuke this bug.

comment:18 foolswisdom7 years ago

  • Resolution set to duplicate
  • Status changed from reopened to closed

Ask and you shall receive.

Closing bug as DUPLICATE.

Continued in ticket:3349

Note: See TracTickets for help on using tickets.