This concept mod is inspired by the Wedge plugin system. It adds plugin support to SMF.
Plugins are standalone modifications that do not need to be installed or removed through the Package Manager. They don't make changes to SMF files and run entirely on hooks.
The entry point of each plugin is plugin.php with an anonymous class inside. Also in the directory of each plugin should be a file plugin-info.xml, which contains the key data of the plugin:
* name
* description
* plugin version
* author name
* link to the author's website (optional)
* email of the author (optional)
* the license used
* link to the plugin site
Plugins are turned on and off at the touch of a button. To install, simply place the plugin folder with the correct structure in the Plugins directory.
The list of currently active plugins is stored in the global variable $plugins in the Settings.php file. To disable a problem plugin, just remove its name from the $plugins variable, or rename the plugin folder, or rename the plugin.php file of the plugin.
- The plugin manager reads metadata from each plugin's plugin-info.xml file. If the XML is empty or invalid, that plugin is skipped in the list and is not enabled automatically until the file is fixed.
- Enabling a plugin adds its directory name to $plugins in Settings.php. If plugin-info.xml declares a
<database>script, that script is executed when the plugin is enabled. - Disabling a plugin removes its directory name from $plugins in Settings.php. The plugin files stay on disk, so it can be enabled again later.
- Removing a plugin deletes its directory from Plugins. Removal is allowed only for disabled plugins; enabled plugins must be disabled first.
example_plugin/
images/
example.png
index.php
languages/
english.php
index.php
russian.php
sources/
index.php
plugin.php
templates/
index.php
Example.template.php
scripts/
index.php
example.js
styles/
index.php
example.css
index.php
license.txt
plugin-info.xml
<?xml version="1.0" standalone="yes" ?>
<plugin id="Author:Example">
<name>Example</name>
<description>
<english>Description...</english>
<russian>Описание...</russian>
</description>
<version>0.1</version>
<author email="noreply@site.com" url="https://author-site.com">Author</author>
<license url="https://license-site.com">License name</license>
<website>https://plugin-site.com</website>
<settings>
<setting name="key1" type="text" default="" />
<setting name="key2" type="large_text" default="" />
<setting name="key3" type="check" default="1" />
<setting name="key4" type="int" default="1" />
</settings>
</plugin>Plugins that require creation of tables in the database for their work must contain a node <database>file_name.php</database> in plugin-info.xml. In the specified file, you can place a script to create the necessary tables when the plugin is enabled, if they have not yet been created.
<?php
/**
* @package Example
* @link https://plugin-site.com
* @author Author https://author-site.com
* @copyright 2026 Author
* @license https://opensource.org/licenses/MIT The MIT License
*/
use Bugo\PluginLoader\Attributes\Hook;
use Bugo\PluginLoader\Plugin;
if (!defined('SMF'))
die('No direct access...');
return new class extends Plugin
{
public const NAME = 'example';
#[Hook('integrate_load_theme')]
public function loadTheme(): void
{
// Your code
// Use language strings
// $this->loadLanguage();
// var_dump($this->txt['key'])
// Use template
// $this->loadTemplate('Example'); // will be loaded /templates/Example.template.php
// Use other source file of the same plugin
// $this->loadSource('other); // will be loaded /sources/other.php
// Use CSS file
// $this->loadCSS('test'); // will be loaded /styles/test.css
// Use JS file
// $this->loadJS('test'); // will be loaded /scripts/test.js
// Use plugin settings
// var_dump($this->getSettings());
}
#[Hook('integrate_menu_buttons')]
public function menuButtons($buttons): void
{
// var_dump($buttons);
}
};As you can see, all hooks required by the plugin are defined using the Hook attribute.
<?php
return [
'key1' => 'Text 1',
'key2' => 'Text 2',
];The following methods are provided to work within plugin classes:
loadLanguage($lang_name)- plugging PHP language file$lang_namefrom subdirectorylanguagesof the current plugin (by default$lang_name = $context['user']['language'])loadTemplate($template_name)- plugging PHP template file$template_namefrom subdirectorytemplatesof the current pluginloadCSS($css_name)- plugging CSS file$css_namefrom subdirectorystylesof the current pluginloadJS($js_name)- plugging JS-file$js_namefrom subdirectoryscriptsof the current pluginloadSource($source_name)- plugging PHP file$source_namefrom subdirectorysourcesof the current plugingetUrl($sub_directory = '')- returns URL to the directory of the current plugin, including$sub_directory(if specified)
