Developing Your First Plugin¶
This chapter provides a step-by-step guide for creating a functional LabPress plugin from scratch. It explains the required plugin structure, the header comment format, how to register actions and filters, how to add an admin page, and how to handle activation and uninstallation.
The chapter assumes that you have read the Plugin System Introduction and are familiar with the hook mechanism and plugin lifecycle. If you have not yet reviewed that material, it is recommended that you do so first.
1. Overview¶
Creating a LabPress plugin involves the following steps:
- Create a plugin directory under
plugins/. - Create a
plugin.phpfile with a valid header comment. - Register callbacks on actions and filters.
- Optionally register an admin page for configuration.
- Optionally create plugin language files.
- Scan, activate, and test the plugin from the admin panel.
This guide creates a simple plugin called My First Plugin. The plugin adds a custom message to the frontend footer, modifies news titles through a filter, and registers a basic admin page.
2. Directory and Plugin Header¶
Every plugin must reside in its own directory under plugins/. The directory name is the plugin slug and must be unique across the installation.
Create the following directory:
Inside that directory, create a file named plugin.php. This is the main plugin file and must begin with a header comment that LabPress uses to identify the plugin.
The header comment contains the plugin name, description, version, and author.
Example header:
<?php
/**
* Plugin Name: My First Plugin
* Description: Adds a custom footer message and modifies news titles.
* Version: 1.0.0
* Author: Your Name
*/
The plugin scanner parses these fields when the plugin is discovered through the admin panel.
3. Registering Actions and Filters¶
All plugin callbacks should be registered on the init action hook. This ensures that core services are available when the callbacks are defined.
The following skeleton registers an action and a filter on init:
<?php
/**
* Plugin Name: My First Plugin
* Description: Adds a custom footer message and modifies news titles.
* Version: 1.0.0
* Author: Your Name
*/
LabPress_Hooks::addAction('init', function() {
// Add a custom message to the frontend footer.
LabPress_Hooks::addAction('footer_scripts', function() {
echo '<p style="text-align:center;">Powered by My First Plugin</p>';
});
// Modify all news titles.
LabPress_Hooks::addFilter('news_list_title', function($title) {
return '[My Plugin] ' . $title;
});
});
Explanation:
- The outer
initaction is fired after all active plugins have been loaded. - Inside the
initcallback, the plugin registers its own action and filter. - The
footer_scriptsaction is triggered by the core footer template. - The
news_list_titlefilter is applied to every news title rendered in the public news list.
4. Adding an Admin Page¶
Plugins can register their own admin page using LabPress::registerAdminPage().
The method requires a page slug, title, callback, and permission. In this example, the plugin registers a page that displays a simple message.
Modify the plugin code as follows:
<?php
/**
* Plugin Name: My First Plugin
* Description: Adds a custom footer message and modifies news titles.
* Version: 1.0.0
* Author: Your Name
*/
LabPress_Hooks::addAction('init', function() {
LabPress_Hooks::addAction('footer_scripts', function() {
echo '<p style="text-align:center;">Powered by My First Plugin</p>';
});
LabPress_Hooks::addFilter('news_list_title', function($title) {
return '[My Plugin] ' . $title;
});
// Register an admin page.
LabPress::registerAdminPage(
'my-first-plugin',
'My First Plugin',
function() {
echo '<div class="card">';
echo '<h3>My First Plugin</h3>';
echo '<p>This page is provided by the plugin.</p>';
echo '</div>';
},
'all'
);
});
The admin page appears in the sidebar under the title My First Plugin. Only users with the all permission can access it.
5. Handling Activation and Uninstallation¶
Plugins can use lifecycle hooks to create and remove database tables or other persistent data.
5.1 Activation Hook¶
The plugin_activation action is fired when the plugin is activated. The callback receives the plugin slug.
In the following example, the plugin creates a simple table for storing custom messages.
LabPress_Hooks::addAction('plugin_activation', function($slug) {
if ($slug !== 'my-first-plugin') return;
$db = LabPress::db();
$db->exec("CREATE TABLE IF NOT EXISTS `my_plugin_messages` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`message` TEXT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
});
This hook should be registered outside the init callback so that it is available before plugin initialization when activation occurs. In this example, place it near the top of plugin.php, after the header comment.
5.2 Uninstall Hook¶
The plugin_uninstalled action is fired before the plugin directory is removed. Use it to delete the table.
LabPress_Hooks::addAction('plugin_uninstalled', function($slug) {
if ($slug !== 'my-first-plugin') return;
$db = LabPress::db();
$db->exec("DROP TABLE IF EXISTS `my_plugin_messages`");
});
As with the activation hook, this registration should occur outside the init callback.
6. Adding Plugin Language Files¶
Plugins can provide their own language files to translate interface strings.
Create the following file:
with content:
And en_US.php with:
<?php
return [
'plugin_title' => 'My First Plugin',
'plugin_description' => 'Displays a custom footer message and modifies news titles.',
];
To load these strings inside the plugin, use explicit plugin language loading:
This approach prevents key conflicts with core language packs.
7. Complete Example Plugin¶
Below is a complete plugin.php for the example plugin, combining the components described above.
<?php
/**
* Plugin Name: My First Plugin
* Description: Adds a custom footer message and modifies news titles.
* Version: 1.0.0
* Author: Your Name
*/
// Activation hook: create table.
LabPress_Hooks::addAction('plugin_activation', function($slug) {
if ($slug !== 'my-first-plugin') return;
$db = LabPress::db();
$db->exec("CREATE TABLE IF NOT EXISTS `my_plugin_messages` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`message` TEXT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
});
// Uninstall hook: drop table.
LabPress_Hooks::addAction('plugin_uninstalled', function($slug) {
if ($slug !== 'my-first-plugin') return;
$db = LabPress::db();
$db->exec("DROP TABLE IF EXISTS `my_plugin_messages`");
});
// Register callbacks on init.
LabPress_Hooks::addAction('init', function() {
LabPress_Hooks::addAction('footer_scripts', function() {
echo '<p style="text-align:center;">Powered by My First Plugin</p>';
});
LabPress_Hooks::addFilter('news_list_title', function($title) {
return '[My Plugin] ' . $title;
});
LabPress::registerAdminPage(
'my-first-plugin',
'My First Plugin',
function() {
echo '<div class="card">';
echo '<h3>' . __('plugin_title', 'plugin:my-first-plugin') . '</h3>';
echo '<p>' . __('plugin_description', 'plugin:my-first-plugin') . '</p>';
echo '</div>';
},
'all'
);
});
8. Installing and Activating the Plugin¶
After creating the plugin files, the plugin must be registered and activated through the admin panel.
- Upload or place the plugin directory under
plugins/. - Log in to the admin panel.
- Go to Plugins > Installed Plugins.
- Click Scan for New Plugins.
- Locate My First Plugin in the plugin list.
- Click Activate.
After activation:
- The footer will display the custom message on the frontend.
- News titles will include the
[My Plugin]prefix. - The My First Plugin page will appear in the admin sidebar.
9. Debugging Common Issues¶
9.1 Plugin Not Detected¶
If the plugin does not appear after scanning:
- Verify that the directory name is correct and matches the expected slug.
- Ensure that
plugin.phpexists in the directory. - Check that the header comment is present and formatted correctly.
9.2 Activation Fails or No Effect¶
- Confirm that the plugin is active in the Installed Plugins list.
- Check that all callbacks are registered inside
initfor normal frontend hooks. - Lifecycle hooks should be registered outside
init.
9.3 Admin Page Missing¶
- Ensure that
LabPress::registerAdminPage()is called inside theinitcallback. - Verify that the current user has the required permission.
9.4 Translation Not Loading¶
- Ensure the plugin language file exists at
plugins/my-first-plugin/languages/{locale}.php. - Use
plugin:my-first-pluginas the second parameter of__(). - Check that the language keys in the plugin file match the keys used in the code.
10. Next Steps¶
After building your first plugin, consider exploring the following resources:
- Hooks Reference – the full list of core actions and filters available to plugins.
- Plugin Examples – real-world examples included with LabPress.
- Plugin System Introduction – review the architecture and lifecycle in more detail.
- Plugin Management – understand how plugins are managed from the admin panel.
- Core Language Packs – learn how to structure and load plugin language files.
- Dynamic Multi-Language Plugin – study a complete plugin that uses filters and a database table.