Module interface and classes

Module is the PHP interface that every ProcessWire module must implement

It requires only a className() method — all other methods are optional and called by ProcessWire when present. See wire/core/Module/Module.php for the full interface definition.

Minimal module class
<?php namespace ProcessWire;

class HelloWorld extends WireData implements Module {

    public static function getModuleInfo() {
        return [
            'title'   => 'Hello World',
            'version' => 1,
            'summary' => 'Demonstrates a minimal module.',
        ];
    }
}

Modules should extend WireData (or a recognized base type — see Module types below). The className() method is inherited from Wire, so no need to implement it yourself.

Module info

ProcessWire needs basic information about each module. Provide it via one of:

1. Static getModuleInfo() method:

public static function getModuleInfo() {
    return [
        'title'    => 'Hello World',
        'version'  => 1,
        'summary'  => 'One sentence description.',
        'author'   => 'Your Name',
        'href'     => 'https://example.com/module-info',
        'autoload' => false,
        'singular' => true,
        'requires' => ['OtherModule>=1.0.0', 'PHP>=8.0', 'ProcessWire>=3.0.200'],
        'installs' => ['SubModuleA', 'SubModuleB'],
    ];
}

2. ModuleName.info.php file (populates $info array, same structure as above):

$info = [
    'title' => 'Hello World',
    'version' => 1,
    /* ... */
];

3. ModuleName.info.json file:

{
  "title": "Hello World",
  "version": 1
}

Should you use getModuleInfo() or ModuleName.info.php (or json)?

In cases where the module has PHP or ProcessWire version requirements, or other module dependencies in the requires module info property, it is preferable to use a ModuleName.info.php or ModuleName.info.json file rather than a static getModuleInfo() method. This is because with a ModuleName.info.php (or json) file, the dependencies can be determined before loading the actual module file.

For example, the CliModule interface was added in ProcessWire 3.0.259 — if a user tried to install a CliModule in a version of ProcessWire prior to that, they would get an "unknown class: CliModule" error as soon as ProcessWire reads the module file. Using a ModuleName.info.php (or json) file ensures that these dependencies can be determined and resolved before ProcessWire attempts to read the module file.

If a module does not need to populate anything to the requires module info property, then it is fine to use a static getModuleInfo() method.

Module info properties

PropertyTypeRequiredDescription
titlestringyesHuman-readable module name
versionint or stringyesVersion number — integer preferred (e.g. 101 = 1.0.1
summarystringyesOne-sentence description
authorstringAuthor name(s)
hrefstringURL for more information
autoloadbool/string/callable/intLoad at boot? See autoload values below. (default=false)
singularboolSingle instance? (default=auto-detected from base type)
requiresarray or stringRequired modules, PHP or ProcessWire versions (CSV or array)
installsarray or stringModules this module installs and uninstalls (CSV or array)
permissionstringPermission required to execute this module
permissionsarrayPermissions to auto-install: ['perm-name' => 'Description']
iconstringFont Awesome icon name, without the "fa-" prefix
permanentboolWhen true, module cannot be uninstalled (core modules only)
searchablestringImplement SearchableModule; value is the search result group name

autoload values

ValueMeaning
trueAlways load at boot
falseLoad only when requested via $modules->get() (default)
selector stringLoad only when the current page matches the selector, e.g. template=admin
callableLoad only when the callable returns true
int ≥ 2Load at boot before other autoload modules (higher = earlier)

requires version syntax

May be an array…

'requires' => [
    'OtherModule>=2.0.0',    // module with minimum version
    'PHP>=8.1',              // PHP version
    'ProcessWire>=3.0.200',  // ProcessWire version
],

…or a selector string:

'requires' => 'OtherModule>=2.0.0, PHP>=8.1, ProcessWire>=3.0.259',
Module methods

All methods are optional. ProcessWire calls them when present:

MethodCalled when
__construct()Module instantiated — config data not yet populated. Must not have any required arguments.
wired()Called after dependency injection (instance wired to ProcessWire's API)
init()After config data populated — good place to attach hooks
ready()API fully ready — autoload modules only; $page is available
install()Module installed — typically hookable ___install()
uninstall()Module uninstalled — typically hookable ___uninstall()
upgrade($fromVersion, $to)Version change detected — typically hookable ___upgrade()
isSingular()Is module a singleton? Overrides singular info property when present
isAutoload()Does module autoload at boot? Overrides autoload info property when present
public function init() {
    // called after config populated — attach hooks here
    $this->addHookAfter('Pages::saved', $this, 'onPageSaved');
}

public function ready() {
    // autoload modules only — $page is available here
    if($this->wire()->page->template == 'admin') { ... }
}

public function ___install() {
    // create fields, templates, pages, etc.
    // throw WireException if install cannot proceed
}

public function ___uninstall() {
    // undo everything install() did
    // throw WireException if uninstall cannot proceed
}

public function ___upgrade($fromVersion, $toVersion) {
    // migrate data or settings between versions
}

When a module is instianted, the method call order is: __construct(), wired(), init() and ready() (when applicable).

Module configuration

Configurable modules have their configuration values populated directly to the module automatically. This happens after __construct() and before init() and ready() (when applicable).

To be configurable, a module must:

  1. Implement the ConfigurableModule interface and extend the WireData class (or another module base class):
class MyModule extends WireData implements Module, ConfigurableModule {
  1. Establish default values for its configurable settings in the constructor (optional but recommended):
public function __construct() {
    parent::__construct();
    $this->set('greeting', 'Hello'); // set default value
}
  1. Provide the form fields for collecting input by implementing a getModuleConfigInputfields()method (option A), or a getModuleConfigArray() method (option B), or a ModuleName.config.php file with a class that extends the ModuleConfig class (option C). Each of these options is outlined below.

Option A — getModuleConfigInputfields() method (most common method):

public function getModuleConfigInputfields(InputfieldWrapper $inputfields) {
    $f = $inputfields->InputfieldText;
    // or $f = $this->wire()->modules->get('InputfieldText');
    $f->attr('name', 'greeting');
    $f->label = 'Greeting';
    $f->val($this->greeting); // current value
    $inputfields->add($f);

    $f = $inputfields->InputfieldText;
    $f->name = 'first_name'; // alternate syntax, attr assumed
    $f->label = 'First name';
    $f->value = $this->first_name; // current value (alternate syntax)
    $inputfields->add($f);
}

Option B — getModuleConfigArray() method (shorthand array format):

public static function getModuleConfigArray() {
    return [
        'greeting' => [
            'type'  => 'text', // 'InputfieldText' or just 'text'
            'label' => 'Greeting',
            'value' => 'Hello', // default value
        ],
        'first_name' => [
            'type'  => 'text', // 'InputfieldText' or just 'text'
            'label' => 'First name',
            'value' => '', // default value
        ],
    ];
}

Option C — external ModuleName.config.php file implementing a ModuleConfig-based class.

class ModuleNameConfig extends ModuleConfig {
    public function __construct() {
        parent::__construct();
        $this->add([
            [
                'type' => 'text', // 'InputfieldText' or just 'text'
                'name' => 'greeting',
                'label' => 'Greeting',
                'value' => 'Hello' // default value
            ], [
                'type' => 'text',
                'name' => 'first_name',
                'label' => 'First name',
                'value' => ''  // default value
            ]
        ]);
    }
}
  1. Document your configuration properties (optional but recommended):
/**
 * My Module
 *
 * @property string $greeting The greeting text
 * @property string $first_name First name
 *
 */
class MyModule extends WireData implements Module, ConfigurableModule {

Module config data is also retrievable at runtime via $modules->getConfig('ModuleName'). See Modules API for getConfig() / saveConfig() usage.

Module types

Extend a base type instead of plain WireData when your module fits a recognized pattern. Base types set defaults for singular, autoload, and provide additional structure.

Base class (A-Z)LocationPurpose
AdminThemeFrameworkwire/core/Admin/AdminThemeFramework.phpAdmin interface themes (autoload, singular)
Fieldtypewire/core/Fieldtype/Fieldtype.phpField value storage and retrieval
FieldtypeMultiwire/core/Fieldtype/FieldtypeMulti.phpMulti-value field value storage and retrieval
FileValidatorModulewire/core/Module/FileValidatorModule/FileValidatorModule.phpValidates file uploads of specified types
Inputfieldwire/core/Inputfield/Inputfield.phpInput UI for field values (non-singular)
ModuleJSwire/core/Module/ModuleJS/ModuleJS.phpModules that load JS/CSS assets in admin
Processwire/core/Module/Process/Process.phpAdmin applications; execute via URL segments
Textformatterwire/core/Module/Textformatter/Textformatter.phpOutput text formatting (singular)
Tfawire/core/Module/Tfa/Tfa.phpTwo-factor (or multi-factor) auth modules
WireSessionHandlerwire/core/Session/WireSessionHandler.phpCustom session storage backends
WireMailwire/core/WireMail/WireMail.phpEmail delivery adapters

In addition, modules may implement these interfaces for optional behaviors:

InterfaceLocationPurpose
CliModulewire/core/Module/CliModule/CliModule.phpExpose commands via php index.php <command>
SearchableModulewire/core/Module/SearchableModule/SearchableModule.phpParticipate in the admin search engine
ConfigurableModulewire/core/Module/ConfigurableModule.phpEnables PW to recognize configurable modules

Process modules

Process modules are admin applications accessed via a page in the admin tree. They respond to URL segments via execute*() methods:

<?php namespace ProcessWire;

class ProcessHello extends Process {

    public static function getModuleInfo() {
        return [
            'title'   => 'Hello',
            'version' => 1,
            'summary' => 'A simple Process module.',
            'page'    => ['name' => 'hello', 'parent' => 'setup', 'title' => 'Hello'],
            'nav' => [ // optional navigation for admin theme dropdowns:
                ['url' => '',       'label' => 'Hello', 'icon' => 'smile-o'],
                ['url' => 'world/', 'label' => 'World', 'icon' => 'globe'],
            ],
        ];
    }

    // Handles /setup/hello/  (default)
    public function ___execute() {
        return '<p>Hello! See the <a href="./world/">world</a></p>';
    }

    // Handles /setup/hello/world/
    public function ___executeWorld() {
        return '<p>Here is the world: 🌐</p>';
    }
}

The page info property causes ProcessWire to auto-create the admin page on install and remove it on uninstall. See wire/core/Module/Process/Process.php for the full Process API.

CliModule interface

Modules implementing CliModule expose commands runnable from the command line:

class MyModule extends WireData implements Module, CliModule {

    public static function getModuleInfo() {
        return [
            'title' => 'My Module',
            'version' => 1,
            'summary' => '...',
            'cli' => 'foobar'
        ];
    }

    // called via: php index.php foobar [args...]
    public function executeCli(array $args) {
        if(empty($args)) return;
        if($args[0] === 'run') {
            if(isset($args[1]) && $args[1] === '--dry-run') {
                echo "Preview of main task";
            } else {
                echo "Running main task";
            }
        } else if($args[0] === 'hi') {
            echo "Hello there!";
        }
    }

    public function getCliCommands() {
        return [
            'run' => 'Run the main task',
            'run --dry-run' => 'Preview without making changes',
            'hi' => 'Say hello',
        ];
    }
}
Notes
  • A module's __construct() fires before config data is populated — use init() for anything requiring config values.
  • ready() is only called for autoload modules. Non-autoload modules do not have a ready() method because the API is already assumed to be ready in their init() method.
  • Modules that attach hooks should generally be singular => true to avoid duplicate hooks.
  • Autoload modules are almost always also singular — an autoload non-singular module would create a new instance on every $modules->get() call while still loading at boot.
  • If your install() or uninstall() method needs to call the parent class version (e.g. for a Process module that uses the page auto-install feature), do so explicitly: parent::___install() / parent::___uninstall().
API reference: methods, properties, hooks

FileCompiler modules must use the name format: FileCompiler[Name].module For example, FileCompilerTags.module

This file is licensed under the MIT license


Click any linked item for full usage details and examples. Hookable methods are indicated with the icon. In addition to those shown below, the FileCompilerModule class also inherits all the methods and properties of: WireData and Wire.

Show class?     Show args?       Only hookable?    

Properties

NameReturnSummary 
FileCompilerModule::runOrder int Order that the module executes in relative to other FileCompiler modules.  

Additional methods and properties

In addition to the methods and properties above, FileCompilerModule also inherits the methods and properties of these classes:

API reference based on ProcessWire core version 3.0.259