Creating a simple module

To create a module, you simply create PHP class that extends one of ProcessWire’s core classes, and also implements the Module interface. This Module interface doesn't actually have any required methods, but it serves as a way for ProcessWire to recognize PHP classes that are intended to be modules, and it provides guidance on how to implement various optional methods. If creating a module that isn't of a predefined type, it should extend the ProcessWire WireData class, and this is a good place to start (we'll get into predefined types later). Here's an example to summarize the above:

Save the class that you create in a file with the same name as the class, but with the extension “.module” or “.module.php” (either works). For example, a module with class Foo (like above) would be in a file named Foo.module or Foo.module.php. The file should be placed in /site/modules/. Though we recommend placing it in a directory within that, having the same name as the module class, like /site/modules/Foo/Foo.module.

Telling ProcessWire about your module

One of the first things that a module should provide is information about itself, so that it can be identified in ProcessWire. This information can be provided via static getModuleInfo() method in the class, or a separate ModuleName.info.php or ModuleName.info.json file. We'll use a static method for now, so that we can keep everything in one file. The method returns an array of information about the module. The minimum information that ProcessWire needs is to know the module’s title, a short text summary, and the version number:

With the above, you've now got the minimum amount of information that ProcessWire needs to install your module. So go ahead and install it. Since you just creating the file, ProcessWire might not be able to see your module yet, so you'll want to login to your admin and then go to Modules > Refresh. ProcessWire should detect your module, and you'll see it on the Site tab of your Modules screen. Click the “Install” button.

Your module may now be installed, but it doesn't actually do anything. That's okay, we'll get to that next.

Making the module do something

Continuing our examle above, lets make the module do something. In this case, lets add a hi() method to the module that returns the string "Hello name", where "name" would be the current user's name:

Now you've got a module that can do something. You can call upon this module from any of your site template files and use it. For example, here's how you might call upon it from our /site/templates/basic-page.php file:

The output would be <h1>Hi there guest</h1> or whatever the current user’s name is.

So now we've got a module that actually does something. If you are building a module to provide a library of reusable functions or the like, then this might be all you need. But what if you want to create a module that performs actions automatically, without having to be loaded manually? Read on…

Automatically loading modules

ProcessWire supports something called autoload modules. These are modules that are loaded automatically when ProcessWire boots. Autoload modules are especially useful when you want to hook into anything within ProcessWire. We'll get into hooks in a bit, but first we'll tell you how to define an autoload module. It's as simple as just adding 'autoload' => true to the information returned by your getModuleInfo() method or file:

After you've added that autoload property, you'll again want to refresh your modules in the admin (Modules > Refresh), so that ProcessWire will reload all the module information. Once you've done that, ProcessWire will see that your module is now an autoload module.

An autoload module isn't particulary useful unless it also does something automatically. So lets update our earlier example to display our “Hi there” message to the user while they are in the admin. To do this, we will add a ready() method to our module. This is a method that ProcessWire automatically calls on all autoload modules as soon as the API is ready.

If you go into your admin, you should now see a "Hi there name" notification at the top of your screen, on every page in the admin.

Note in the above example that we don't display our “Hi there” message unless the user happens to be in the admin (which we determine by checking the current page's template). While it wouldn't hurt anything if we did, messages/notifications aren't displayed to users on the front-end, so there would be no point.

Pro Tip: If you only want your module to autoload in the admin, change your 'autoload' => true in your getModuleInfo() method to instead return 'autoload' => 'template=admin', which essentially says "autoload only if the current page template is admin." If you do this, then you can remove if() statement from the ready() method above, since you are now asking ProcessWire to autoload your module only in the admin. Remember to Modules > Refresh, after making this change.

Adding hooks to autoload modules

Modules may add hooks to methods in ProcessWire's core or to other modules. Nearly every key component and action in ProcessWire is hookable. A hookable method is identified by the presence of three underscores before its definition, i.e. public function ___someMethod() { ... }. You can hook any such methods from your autoload modules.

Modules can schedule designated methods to be executed before or after any hookable method. They can manipulate the values provided to (or returned from) any hookable method. Modules may also add new methods to existing classes or object instances in ProcessWire.

To demonstrate hooking a method, lets take our example above and make it display our “Hi there” message to the user after every page is rendered. This will make a <p>Hi there user</p> appear at the bottom of every page rendered in ProcessWire, so… maybe don't do this on a live site.

The $event argument that the hook function receives is a HookEvent object. Since we are hooking after an existing method, it provides us the return value in $event->return, which we then modify by appending some more markup to it. It also provides all of the arguments the method received in $event->arguments(), and each argument can be retrieved by zero-based index, or by name. Were we using a before hook, we could also modify those arguments before they are received by the method we have hooked. For more about hooks and events, please see our Hooks documentation.

Using an autoload module to add new methods to existing classes

Before we wrap things up, lets show you how to add a new method to an existing class in ProcessWire. In this case we'll add a new summarize() method to the Page class. This method will auto-generate a summary of the “body” text, of a specified length. This is something that might actually be useful, unlike the previous examples.

With the new summarize() method added to all Page instances via this hook, now we can call summarize() on any Page object. For instance, we might use this when rendering a list of pages or search results:

Another example

Before we finish, lets take a look at one more module example. In this case, the module displays a message to the user every time they save a page. If most of this example makes sense to you, then you are ready to move forward with module development. There is one thing a little different here though, lets see if you can spot it. I'll tell you more after the example.

What's a little different in this example relative to our previous examples is the way that we are adding the hook in our ready() method. We are adding the hook with $this->pages->addHookAfter() rather than $this->addHookAfter(). Note also the way we specify the method we are hooking, which is just saved rather than Pages::saved.

What we are doing here is adding this hook to 1 specific object instance $this->pages rather than ALL instances of a class. In this case, it does not matter because there is only ever one instance of the Pages class, identified by $this->pages. But for classes that can have any number of instances (like Page) it would matter, as $this->addHook('Page::method', ...) would hook all instances of a Page and $page->addHook('method', ...) would only add the hook to that one $page.

When it comes to module development, it's more likely you'll be hooking all instances of a class rather than just a single instance, so we won't dive deeper into it here. But if you are interested, be sure to read more in our hooks documentation. This completes our basic introduction to development of a simple module, but there's lots more to learn if you are interested…

Next steps

Take a look at the HelloWorld module that’s included with the core. You can find it in your /site/modules/HelloWorld/ directory.

New post: In this week’s post, we’ll take a look a look at the new website and focus on some parts of it and how they were built. Then we’ll dive into the latest version of ProcessWire on the dev branch, version 3.0.124— More

Latest news

ProcessWire Weekly #245In the 245th issue of ProcessWire Weekly we're going to focus on some new documentation additions made just this week, cover a couple of new third party modules from Bernhard Baumrock, and introduce a magnificent new travel website called Uncover Colombia. Read on!

Weekly.pw / 19 January 2019

ProcessWire 3.0.124 and new website launchedIn this week’s post, we’ll take a look a look at the new website and focus on some parts of it and how they were built. Then we’ll dive into the latest version of ProcessWire on the dev branch, version 3.0.124.

Blog / 11 January 2019

Subscribe to weekly ProcessWire news

“Indeed, if ProcessWire can be considered as a CMS in its own right, it also offers all the advantages of a CMF (Content Management Framework). Unlike other solutions, the programmer is not forced to follow the proposed model and can integrate his/her ways of doing things.﻿” —Guy Verville, Spiria Digital Inc.