How can I import a class privately into a plugin?

I’ve written a WordPress plugin that makes use of a simple controller/templating library I have authored, to separate out business logic from presentation. I have a (slightly theoretical) query about how to import this “privately” into a plugin in a way that doesn’t clash with other plugin usages.

The library, called TemplateSystem, is a submodule in my git repo for the plugin, and is defined internally thus:

<?php

// Don't define this if it's already been defined
if (!class_exists('TemplateSystem'))
{
    abstract class TemplateSystem
    {
        ...
    }
}

Thus, if another plugin makes use of TemplateSystem, the loaded copy will be used, rather than creating an error by redefining it. That is fine normally, but now TemplateSystem has had a backwards-incompatible change introduced.

So, I have two versions of the same library that need to be loaded at the same time. As it stands, either the newer library will break the older plugin, or the older library will break the newer plugin, depending on what order the plugins are loaded by WP.

So, here’s one potential change to TemplateSystem that solves the problem:

<?php

// The 'Change2' part would change with backwards incompatible changes,
// rather than just version numbers (although that would be fine tbh)
namespace TemplateSystem\Change2;

// Don't define this if it's already been defined
if (!class_exists('TemplateSystem\Change2\TemplateSystem'))
{
    abstract class TemplateSystem
    {
        ...
    }
}

To avoid name clashes in my use case, I can do this:

<?php
use TemplateSystem\Change2\TemplateSystem as VersionedCommentsTemplateSystem;

class VersionedCommentsController extends VersionedCommentsTemplateSystem
{
    ...
}

That works fine alongside the older (non-namespaced) version. Now, I mentioned that this question is theoretical, since I’m the author of the templating library and (although it is publicly available) I don’t think anyone else is using it. So, one solution is to ignore this problem; however, it would be good to know how to deal with it properly!

Second possibility: the namespace solution above will work fine, except that namespaces require PHP 5.3, and – as various answers on this site show – WP ain’t moving from a deprecated version soon. Should I ignore that and require PHP 5.3, even if core isn’t moving? That would of course prevent any plugin based on my work from operating on an older server.

Or, if you think that plugins (and their libraries) are best requiring just 5.2.4, what is the alternative fix? I was considering a bundled PHP console “build” script that just does some trivial search and replaces, such that the library name is replaced by a custom name for each plugin; thus, the TemplateSystem.php file is copied and the copy’s class definition renamed internally as VersionedCommentsTemplateSystem (i.e. according to the use case). That’s rather hacky, but would at least work with more installations.

Last possibility, add the change or version number into the class name:

<?php

// Don't define this if it's already been defined
if (!class_exists('TemplateSystem2'))
{
    abstract class TemplateSystem2
    {
        ...
    }
}

That would work with 5.2.4+, but is my least preferred solution; my inner purist baulks at the corruption of the class name!

What approach would experienced plugin-authors go towards?

1
1

Use namespaces, but check the PHP version on the server side and deactivate the plugin with a useful error message if the requirements aren’t met.

If you look at the official statistics, you can see how many installations are still running on outdated WordPress versions. But almost all new plugins require the latest WordPress version, and nobody complains about that. I think there is a large number of abandoned or poorly managed installations out there. They will probably never use your plugin anyway.

That leaves you with a rather small number of installations that are up to date but still on PHP 5.2. They can upgrade. All web hosts offer this nowadays, they couldn’t run Symfony or most other popular scripts otherwise.

The other option is to move the library to a separate plugin and to require that library to be installed first. Then hook into library_loaded to start your specialized plugin’s code.

Leave a Comment