Plugin Development
Extend Scriptlog's functionality with custom plugins using the hook-based architecture.
Overview
Scriptlog plugin system uses a hook-based architecture. Plugins register hooks using the clip() function, and those hooks are executed at specific points in the application.
Plugin Lifecycle
Understanding the plugin lifecycle helps you build plugins that integrate seamlessly with scriptLog.
ZIP Upload
Plugin is uploaded and installed through the admin panel.
PluginController::installPlugin()
- Validates ZIP structure
- Extracts to
admin/plugins/ - Saves info to
tbl_plugin - Status: Disabled
Enable Plugin
Plugin is activated and ready to use.
PluginController::enablePlugin($id)
- Executes
schema.sqlif exists - Loads plugin PHP class
- Registers hooks via
clip() - Sets status = 'Y'
Disable Plugin
Plugin is deactivated but files remain.
PluginController::disablePlugin($id)
- Hooks are unregistered
- Files remain on disk
- Database record preserved
- Sets status = 'N'
Remove Plugin
Plugin is completely removed from the system.
PluginController::remove($id)
- Deletes plugin directory
- Removes database record
- Cleanup complete
Quick Reference
Activation Methods
activate()- Called on activationdeactivate()- Called on deactivationuninstall()- Called before deletion
Database Status
plugin_status = 'Y'- Enabledplugin_status = 'N'- Disabled- Record in
tbl_plugin
Creating a Plugin
Required Directory Structure
admin/plugins/[plugin-name]/
├── plugin.ini # REQUIRED - Plugin configuration
├── YourClassFile.php # REQUIRED - Main plugin class
├── functions.php # OPTIONAL - Helper functions
└── schema.sql # OPTIONAL - Database schema
Step 1: Create plugin.ini
[INFO]
plugin_name = "My Plugin Name"
plugin_description = "Description of what the plugin does"
plugin_level = "administrator"
plugin_version = "1.0.0"
plugin_author = "Your Name"
plugin_loader = "MyPlugin"
plugin_action = "my-plugin"
| Field | Required | Description |
|---|---|---|
plugin_name | Yes | Display name |
plugin_description | Yes | What the plugin does |
plugin_level | Yes | "administrator" or "manager" |
plugin_version | Yes | Semantic version (e.g., 1.0.0) |
plugin_author | Yes | Author name |
plugin_loader | Yes | PHP class filename (without .php) |
plugin_action | Yes | Action identifier for routing |
Step 2: Create Main Plugin Class
<?php defined('SCRIPTLOG') || die("Direct access not permitted");
class MyPlugin
{
private $pluginDir;
public function __construct()
{
$this->pluginDir = dirname(__FILE__);
$this->registerHooks();
}
private function registerHooks()
{
clip('clip_my_plugin', null, function($content = '') {
return $this->frontendContent($content);
});
clip('clip_my_plugin_admin', null, function() {
return $this->adminPage();
});
}
public function activate()
{
// Runs on activation - create tables, set options
return true;
}
public function deactivate()
{
// Runs on deactivation - cleanup temporary data
return true;
}
public function uninstall()
{
// Runs on deletion - remove all plugin data
return true;
}
public function adminPage()
{
return '<div class="box">
<div class="box-header"><h3>My Plugin</h3></div>
<div class="box-body">
<p>Plugin is active!</p>
</div>
</div>';
}
public function frontendContent($content = '')
{
return $content . '<div class="my-plugin">Hello from my plugin!</div>';
}
}
Step 3: Create Helper Functions (Optional)
Helper functions provide a cleaner API for your plugin:
<?php defined('SCRIPTLOG') || die("Direct access not permitted");
function my_plugin_instance()
{
static $instance = null;
if (null === $instance) {
$instance = new MyPlugin();
}
return $instance;
}
function my_plugin_display($content = '')
{
return my_plugin_instance()->frontendContent($content);
}
Step 4: Create Database Schema (Optional)
If your plugin needs database tables, create schema.sql:
-- Create tables for your plugin
CREATE TABLE IF NOT EXISTS tbl_my_plugin (
ID BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Add DROP statement for uninstall (commented by default)
-- DROP TABLE IF EXISTS tbl_my_plugin;
Hook System Reference
The clip() Function
clip('hook_name', $value, $callback);
Usage:
// Register a hook (attach callback)
clip('my_hook', null, function($value) {
return $value . ' modified';
});
// Execute all callbacks attached to hook
$result = clip('my_hook', 'original');
Available Hooks
Frontend Hooks
| Hook Name | Description | Value Passed |
|---|---|---|
clip_[plugin_name] | Main plugin hook | Content string |
clip_content | Content filter | Post content |
clip_footer | Footer section | - |
clip_header | Header section | - |
Admin Hooks
| Hook Name | Description |
|---|---|
clip_[plugin_name]_admin | Admin page content |
clip_plugin_menu | Plugin navigation |
Invoking Plugins
From Code
// Check if plugin is enabled
if (is_plugin_enabled('my-plugin')) {
$content = invoke_plugin('my-plugin', $content);
}
Available Plugin Functions
// Check if plugin exists in database
is_plugin_exist('plugin-name');
// Check if plugin is enabled
is_plugin_enabled('plugin-name');
// Execute plugin hook
invoke_plugin('plugin-name', $args);
// Set plugin navigation in admin sidebar
set_plugin_navigation('plugin-name');
Common Issues
| Issue | Solution |
|---|---|
| Plugin not appearing in list | Check plugin.ini syntax |
| Plugin loads but no output | Ensure hooks are registered in constructor |
| Activation fails | Check schema.sql syntax |
| Hook not firing | Verify clip() call exists and plugin is enabled |
Best Practices
Follow these guidelines for secure and maintainable plugins:
Security First
Always check defined('SCRIPTLOG') at the top of PHP files to prevent direct access.
Unique Hook Names
Always use unique hook names with your plugin prefix (e.g., clip_myplugin_) to avoid conflicts.
Input Sanitization
Always sanitize user input in your plugin using built-in sanitization functions.
Use Hooks
Avoid modifying core files - use hooks (clip()) instead to extend functionality.
Documentation
Document your plugin's hooks, functions, and configuration options for future reference.
Version Control
Version your plugin using semantic versioning (e.g., 1.0.0) and test on development first.
Example: Complete Social Share Plugin
This example demonstrates a complete plugin implementation with all components.
Directory Structure
admin/plugins/social-share/
├── plugin.ini
├── SocialSharePlugin.php
└── functions.php
plugin.ini
[INFO]
plugin_name = "Social Share"
plugin_description = "Add social sharing buttons to posts"
plugin_level = "administrator"
plugin_version = "1.0.0"
plugin_author = "Developer Name"
plugin_loader = "SocialSharePlugin"
plugin_action = "social-share"
SocialSharePlugin.php
<?php defined('SCRIPTLOG') || die("Direct access not permitted");
class SocialSharePlugin
{
private $pluginDir;
public function __construct()
{
$this->pluginDir = dirname(__FILE__);
$this->registerHooks();
}
private function registerHooks()
{
clip('clip_social_share', null, function($content = '') {
return $this->addSocialButtons($content);
});
clip('clip_social_share_admin', null, function() {
return $this->adminPage();
});
}
public function activate()
{
return true;
}
public function deactivate()
{
return true;
}
public function addSocialButtons($content = '')
{
$buttons = '<div class="social-share" style="margin-top:20px; padding:15px; border-top:1px solid #eee;">
<h4>Share this post</h4>
<a href="#" class="btn btn-primary">Facebook</a>
<a href="#" class="btn btn-info">Twitter</a>
<a href="#" class="btn btn-danger">Google+</a>
</div>';
return $content . $buttons;
}
public function adminPage()
{
return '<div class="box box-primary">
<div class="box-header"><h3>Social Share Settings</h3></div>
<div class="box-body">
<p>Configure your social share buttons here.</p>
</div>
</div>';
}
}
functions.php
<?php defined('SCRIPTLOG') || die("Direct access not permitted");
function social_share_plugin()
{
static $instance = null;
if (null === $instance) {
$instance = new SocialSharePlugin();
}
return $instance;
}
function social_share_buttons($content = '')
{
return social_share_plugin()->addSocialButtons($content);
}
Testing Your Plugin
Follow this checklist to ensure your plugin works correctly through its entire lifecycle.
Manual Testing Checklist
Debug Tips
Add debugging to your plugin during development:
public function __construct()
{
$this->pluginDir = dirname(__FILE__);
// Debug: Log plugin initialization
error_log('MyPlugin: Initialized at ' . date('Y-m-d H:i:s'));
$this->registerHooks();
}