1. Home
  2. Docs
  3. Application Framework
  4. Classes & Patterns
  5. Class Extensibility

Class Extensibility

$ wp mwp add-class GreeterClass

You will notice that new classes created with the WP CLI are initially prefixed with an underscore. This supports an internal feature of the MWP Framework that allows classes to be extended using a special design pattern called the “interior decorator”.

@Interior Decorator Pattern

The interior decorator design pattern allows PSR-4 autoloadable classes to “opt-in” to being extended dynamically by other plugins and themes simply by prefixing their classname with an underscore. When the class is loaded, the MWP Framework will take all registered class extensions (if any) for that class, and chain them together using extends, and then create the originally requested class (the one without the underscore prefix) as the last subclass in that chain.

This leaves the original extensible class (the one with the underscore) as the parent class, and all registered extensions layered on top as decorators, with the intended class sandwiching it all together.

@Code Examples

The base class only needs to opt-in to the extensibility pattern by prefixing with an underscore.

/* File: plugin-dir/classes/GreeterClass.php */

namespace PluginVendor\PluginPackage;

/**
 * Base class (opted in to extensibility).
 */
class _GreeterClass
{
    public function sayHi() {
        return "Hi!";
    }
}

Another plugin or theme creates an extension and registers it.

/* File: theme-dir/extensions/SomeVendorSomePackageGreeterClass.php */

namespace ThemeVendor\ThemePackage\Extensions;

/**
 * Extension class for the targeted base class
 * 
 * The class name should be the fully qualified class name of the target class,
 * with all namespace seperators (backslashes) removed, and it needs to extend 
 * an underscore prefixed class by the same name.
 */
class SomeVendorSomePackageGreeterClass extends _SomeVendorSomePackageGreeterClass
{
    public function sayHi() {
        $hi = parent::sayHi();
        return $hi . " How are you?";
    }
}

/* Register the extension directory, typically in the theme/plugin functions.php */
add_filter( 'mwp_framework_extension_dirs', function( $dirs ) {
    $dirs[] = array(
        'namespace' => 'ThemeVendor\ThemePackage\Extensions',
        'path' => get_stylesheet_directory() . '/extensions',
    );
    return $dirs;
});

The class is used the same as it always is, regardless of if there are any extensions on it or not.

use PluginVendor\PluginPackage\GreeterClass;

$greeter = new GreeterClass();
echo $greeter->sayHi();  // result: "Hi! How are you?"

Limitations

  • Classes declared as final cannot be used with this pattern.
  • Traits cannot be used with this pattern.
  • Classes that need to be used before the after_setup_theme hook cannot be used with this pattern.

@Registration Conventions

To extend any “opted-in” class (prefixed with an underscore) using this pattern, you will need to first register a folder in your plugin or theme as an extensions directory.

For plugins: If you create an /extensions folder in your plugin, it will be automatically registered by the MWP Framework as an extensions directory for your plugin, with the namespace being YourVendor\YourPackage\Extensions.

Register all other extension directories using the mwp_framework_extension_dirs WordPress filter.

function mytheme_mwp_framework_extension_dirs( $dirs ) {
    $dirs[] = [ 
        'namespace' => 'MyTheme\MyPackage\Extensions', 
        'path' => get_stylesheet_directory() . '/extensions' 
    ];
    return $dirs;
}
add_filter( 'mwp_framework_extension_dirs', 'mytheme_mwp_framework_extension_dirs' );
  • The namespace you provide in the registration should be used for every extension in that directory.
  • Once you register an extension directory, you can place as many extensions into it as you need.
  • Every class extension needs to be in its own file.

@Extension Conventions

  1. Create a file in your extensions directory using the full classname of the target class (backslashes removed).
  2. Namespace your file with the same namespace provided when you registered your extension directory.
  3. Create a new class inside your file with the same name as the file in step 1.
  4. Extend a class by the same name, but prefixed with an underscore.

/* File: extensions/SomeVendorSomePackageTargetClass.php */
namespace MyTheme\MyPackage\Extensions;

class SomeVendorSomePackageTargetClass extends _SomeVendorSomePackageTargetClass {

}

When the target class is autoloaded, your extension on it will be mixed into the inheritance chain.

Was this article helpful to you? Yes No

How can we help?