Extension:Shared codebase

From MediaWiki.org
Jump to: navigation, search
The "official homepage" for this initiative is at http://bmearns.net/wwk/view/MediaWiki_shared_codebase. Check there more updates and more info.

The Shared CodeBase Initiative (let's call it SCBI or "skibby", that'll be nice) is a project to make it easy to run multiple instances of MediaWiki on a server, with all instances sharing a common code base. This will have several benefits:

  • Reduces space (a little) required on the server: instead of having the entire codebase present for each instance, we only need the codebase installed in one master location, and then a few configuration files for each instance.
  • Changes to the codebase can be rolled out more easily: just edit the files in the master codebase and all the instances on the server will automatically get it.

In addition, it is desirable that each instance can independently override any functionality locally, without the change propagating to the other instances (if the change is desired to be global, it can be made in the master codebase, instead).

This is actually fairly easy to do in general, but there's probably a lot of hidden things that you we won't realize are broken right away. The basic idea is to configure the include_path (in each instance) to look for included files in the local directories (for that instance) first, and if it can't find them, then it goes to the master codebase. This also involves an override of the __autoload function, which is used to automatically include the correct file for a specified class.

The main problem is that a lot of the include/require statements in the existing code base use the $IP variable as a path prefix, which creates an absolute path, so our include_path is no good. Similarly, a lot of them use . and .. as prefixes for the code path. Even though these look like relative paths, they're only relative to the current directory, not to the include path, so once again, our include path is of no use.

So the primary effort for this is to find all the places in the code that do this, and just remove the prefixes that make them absolute paths. The only real downside to this (besides the work), is that it will take just a bit longer to include/require a relative path then an absolute path, because PHP needs to decide where to get the file from.

After downloading a pristine copy of mediawiki (version 1.11.0), I made of the code base, and in one copy, made all the changes that I've (so far) found to be necessary for this to work. Then I diffed the original codebase and my mods, so anyone interested in doing the same can just follow the diff to make the changes. The complete log of the diff, with a few of my comments (prefixed with # on the beginning of the line), is at /diff_1.11.0

Contents

[edit] Overriding functionality

Sadly, neither PHP nor the MediaWiki codebase is set up real well to accomodate the sort of thing we're trying to do here (not this I really think this was a foreseeable demand), but it is possible, you just have to get your hands dirty a little.

What we're basically going to do to override functionality locally, is to rename the existing class (and the file it's defined in) to suffix it with _Base. This is where all the master functionality is implemented. To override it, we'll create a subclass defined in our local directories, and just override the functions we want to. However, so we don't have to create a local subclass if we don't want to, we'll create a default subclass in the master codebase, which just doesn't override anything.

So here's how it will work. Let's take the Article class as an example. Straight out of the "box", it's defined in the file includes/Article.php. In the master code base, we're going to rename this file includes/Article_Base.php, and inside the file, we'll simply rename the class from Article to Article_Base.

Next, we'll create a new includes/Article.php file, which looks like this:

<?php

require_once("includes/Article_Base.php");
class Article extends Article_Base {

}

?>

So what happens when somewhere in the code, we try to access the Article class. Well, at this point, our autoloader won't find the includes/Article.php file in our local instance directories, only in the master codebase, so it will include that one, which we showed above. The Article class defined in that file is what we created above, it's just an empty subclass of Article_Base. So if it tries to call a function of the Article class, it will just get the implementation of that function from the Article_Base class, which is the original class from the original codebase, so everything's fine.

Now we want to override some functionality locally, which was our whole purpose for doing this. So back in the local instance, we create a new file includes/Article.php. To start with, we can just copy the contents of the same file from the master code base (not the original Article.php file, which we renamed to Article_Base.php, but the new one we created as the default subclass). So what happens now when some code trys to access the Article class? The autoloader will look for the includes/Article.php file locally, and this time it will find it. So this file will include the includes/Article_Base.php file (which is only in the master codebase, so it will get that one), and then define an empty subclass of the Article_Base class, called Article. In otherwords, it will do the same thing as before we copied it locally.

Now, however, we have the Article class defined locally, with default (superclass) implementation defined in the master codebase. So now in the local Article.php file, we can simply override any method of the Article_Base class that we want. When this method get's called, it will get our local implementation. However, any other instances that we have on the server that share this master codebase, will not get the new implementation, because they don't have their own Article.php file, so they get the one from the master codebase which doesn't override anything.

[edit] Statics

One little complication with this is the static fields. In our case, the static fields are supposed to belong to the Article class, but they're now defined in the Article_Base class, which means we can't access them. So we'll just have to cut them out of the Article_Base class, and paste them into all Article.php files. That means in our master codebase, and when we copy it down to a local instance for overriding. That's another reason why it's best for overriding to just copy the default subclass from the master codebase directly, to make sure you get all the static fields. Finally, you need to make sure that the base class (Article_Base in this case) can access any static fields it needs to (probably all of them), which means they can't be private.

[edit] Setting up an instance

[edit] Install script

Bash shell script to setup a new instance, run from the top level directory of the instance:

#!/bin/sh

if [$# -lt 1 ]
then
        echo Requires one argument, the codebase directory.
        exit 1
fi

CODEBASE_DIR="$1"

mkdir config
cp -v $CODEBASE_DIR/config/index.php? config/
cp -rv $CODEBASE_DIR/serialized .
ln -s $CODEBASE_DIR/index.php index.php
ln -s $CODEBASE_DIR/mb_entryPoint.php mb_entryPoint.php

echo "Done."

Pass in the path to the master codebase. Windoze users will have to copy index.php and mb_entryPoint.php, instead of linking. That just means you won't get any updates made to those files without manually rolling them out.

[edit] mb_entryPoint.php

This file should be in the master code base, and then you can link to it from the top level directory of all the instances (as done in the install script above). This should be included at the top of any files that are entry points to the wiki, like index.php and config/index.php initially. Make sure to set the $GMB_CODEBASE and $GMB_CODEBASE_URL as appropriate (absolute paths).

<?php
/**
 * Define the location and url for the master codebase, and some
 * helper functions for getting the path to a file or URL to
 * a resource, trying the local instance first, then going to
 * the codebase if neccessary. Also updates the include_path
 * so the instance directories come first, then the master
 * codebase dir.
 */

$GMB_CODEBASE = "/var/www/content/wiki/master/current";
$GMB_CODEBASE_URL = "/content/wiki/master/current";

$IP = getcwd();

#We put the local instance directories first on the list, then the master code base directories
$path = array( "$IP/", "$IP/includes/", "$IP/languages/", "$GMB_CODEBASE/", "$GMB_CODEBASE/includes/", "$GMB_CODEBASE/languages/" );
set_include_path( implode( PATH_SEPARATOR, $path ) . PATH_SEPARATOR . get_include_path() );

function mb_getResource($relPath, $instanceScriptPath)
/**
 * Get a URL for a resource (like an icon or something). Tries to find it
 * in the local instance, if it can't, it use $gCodeBaseUrl as a prefix.
 */
{
        global $GMB_CODEBASE_URL;

        if(file_exists($relPath))
                return $instanceScriptPath. "/" . $relPath;
        else
                return $GMB_CODEBASE_URL . "/" . $relPath;
}
//end function mb_getResource

function mb_getFile($relPath)
{
        global $GMB_CODEBASE;

        if(file_exists($relPath))
                return $relPath;
        else
                return $GMB_CODEBASE . "/" . $relPath;
}
//end function mb_getFile


?>
Personal tools
Namespaces

Variants
Actions
Navigation
Support
Download
Development
Communication
Print/export
Toolbox