Core Framework¶
The Core module provides the foundational infrastructure for all Woodev-based plugins. This document covers the bootstrap process, plugin base class, and lifecycle management.
Overview¶
The Core module handles:
- Plugin bootstrap and version management
- Plugin lifecycle (installation, upgrades, activation, deactivation)
- Dependency checking (PHP extensions, functions, settings)
- Text domain loading and internationalization
- Logging and debugging utilities
- Hook deprecation management
Key Classes¶
| Class | File | Purpose |
|---|---|---|
Woodev_Plugin_Bootstrap |
bootstrap.php |
Singleton that loads the highest framework version |
Woodev_Plugin |
class-plugin.php |
Abstract base class for all plugins |
Woodev_Lifecycle |
class-lifecycle.php |
Handles install/upgrade/activation routines |
Woodev_Plugin_Dependencies |
class-woodev-plugin-dependencies.php |
PHP dependency checker |
Woodev_Hook_Deprecator |
class-woodev-hook-deprecator.php |
Manages deprecated hooks |
Bootstrap Process¶
How Bootstrap Works¶
The Woodev_Plugin_Bootstrap class ensures only one version of the framework is loaded:
<?php
class Woodev_Plugin_Bootstrap {
protected static $instance = null;
protected array $registered_plugins = [];
protected array $active_plugins = [];
private function __construct() {
add_action( 'plugins_loaded', [ $this, 'load_plugins' ] );
add_action( 'admin_init', [ $this, 'maybe_deactivate_framework_plugins' ] );
}
public static function instance(): self {
return self::$instance ??= new self();
}
public function register_plugin(
string $framework_version,
string $plugin_name,
string $path,
callable $callback,
array $args = []
) {
$this->registered_plugins[] = [
'version' => $framework_version,
'plugin_name' => $plugin_name,
'path' => $path,
'callback' => $callback,
'args' => $args,
];
}
}
Bootstrap Flow¶
1. Plugin A calls register_plugin('1.4.0', ...)
2. Plugin B calls register_plugin('1.3.0', ...)
3. plugins_loaded (priority 0)
4. Sort plugins by version (highest first)
5. Load class-plugin.php from Plugin A (1.4.0)
6. Call each plugin's callback
7. All plugins share framework 1.4.0
Registration Example¶
<?php
add_action( 'plugins_loaded', 'init_my_plugin', 0 );
function init_my_plugin() {
Woodev_Plugin_Bootstrap::instance()->register_plugin(
'1.4.0', // Framework version
'My Plugin',
__FILE__,
'my_plugin_init',
[
'minimum_wc_version' => '8.0',
'minimum_wp_version' => '5.9',
'backwards_compatible' => '1.4.0',
]
);
}
function my_plugin_init() {
final class My_Plugin extends Woodev_Plugin {}
return My_Plugin::instance();
}
Woodev_Plugin Base Class¶
Class Structure¶
<?php
abstract class Woodev_Plugin {
const VERSION = '1.4.1';
protected static $instance;
private $id;
private $version;
private $plugin_path;
private $plugin_url;
private $template_path;
private $supported_features;
private $logger;
protected $license;
private $message_handler;
private $text_domain;
private $active_plugins = [];
private $dependency_handler;
private $hook_deprecator;
protected $lifecycle_handler;
private $admin_notice_handler;
protected $rest_api_handler;
protected $setup_wizard_handler;
protected Woodev_Blocks_Handler $blocks_handler;
public function __construct( string $id, string $version, array $args = [] ) {
// Initialization
}
// Abstract methods (must be implemented)
abstract protected function get_file();
abstract public function get_plugin_name();
abstract public function get_download_id();
}
Constructor¶
<?php
public function __construct( string $id, string $version, array $args = [] ) {
$this->id = $id;
$this->version = $version;
$args = wp_parse_args( $args, [
'text_domain' => '',
'dependencies' => [],
'supported_features' => [
'hpos' => false,
'blocks' => [
'cart' => false,
'checkout' => false,
],
],
] );
$this->text_domain = $args['text_domain'];
$this->supported_features = $args['supported_features'];
// Load required files
$this->includes();
// Initialize handlers
$this->init_dependencies( $args['dependencies'] );
$this->init_admin_message_handler();
$this->init_admin_notice_handler();
$this->init_license_handler();
$this->init_hook_deprecator();
$this->init_lifecycle_handler();
$this->init_rest_api_handler();
$this->init_blocks_handler();
$this->init_setup_wizard_handler();
// Load admin pages and license fields
$this->load_admin_pages();
$this->load_license_settings_fields();
// Add hooks
$this->add_hooks();
}
Required Methods¶
Every plugin must implement these three methods:
<?php
class My_Plugin extends Woodev_Plugin {
/**
* Get the main plugin file path
*/
protected function get_file() {
return __FILE__;
}
/**
* Get the human-readable plugin name
*/
public function get_plugin_name() {
return __( 'My Plugin', 'my-plugin' );
}
/**
* Get the EDD download ID for licensing
* Return 0 if plugin doesn't use licensing
*/
public function get_download_id() {
return 0;
}
}
Plugin Properties¶
Access plugin information:
<?php
$plugin = My_Plugin::instance();
// Get plugin ID
echo $plugin->get_id(); // 'my-plugin'
echo $plugin->get_id_dasherized(); // 'my-plugin'
echo $plugin->get_id_underscored(); // 'my_plugin'
// Get version
echo $plugin->get_version(); // '1.0.0'
// Get paths and URLs
echo $plugin->get_plugin_path(); // /path/to/plugin
echo $plugin->get_plugin_url(); // https://example.com/wp-content/plugins/my-plugin
// Get framework paths
echo $plugin->get_framework_path(); // /path/to/plugin/woodev
echo $plugin->get_framework_assets_path(); // /path/to/plugin/woodev/assets
Lifecycle Management¶
Woodev_Lifecycle Class¶
Handles plugin installation, upgrades, and deactivation:
<?php
class Woodev_Lifecycle {
protected $upgrade_versions = [];
private $milestone_version;
private $plugin;
public function __construct( Woodev_Plugin $plugin ) {
$this->plugin = $plugin;
$this->add_hooks();
}
protected function add_hooks() {
add_action( 'admin_init', [ $this, 'handle_activation' ] );
add_action( 'deactivate_' . $this->get_plugin()->get_plugin_file(), [ $this, 'handle_deactivation' ] );
if ( is_admin() && ! wp_doing_ajax() ) {
add_action( 'wp_loaded', [ $this, 'init' ] );
add_action( 'init', [ $this, 'add_admin_notices' ] );
}
add_action(
'woodev_' . $this->get_plugin()->get_id() . '_milestone_reached',
[ $this, 'trigger_milestone' ],
10,
3
);
}
}
Lifecycle Hooks¶
<?php
// After installation
add_action( 'woodev_my-plugin_installed', function() {
// First install logic
} );
// After upgrade
add_action( 'woodev_my-plugin_updated', function( string $old_version ) {
// Upgrade logic
}, 10, 1 );
// After activation
add_action( 'woodev_my-plugin_activated', function() {
// Activation logic
} );
// After deactivation
add_action( 'woodev_my-plugin_deactivated', function() {
// Deactivation logic
} );
Custom Lifecycle Handler¶
Override the init_lifecycle_handler() method (not get_lifecycle_handler(), which is just a getter):
<?php
class My_Plugin extends Woodev_Plugin {
protected function init_lifecycle_handler() {
$this->lifecycle_handler = new class( $this ) extends Woodev_Lifecycle {
protected function upgrade( string $installed_version ) {
parent::upgrade( $installed_version );
// Run upgrade routines
if ( version_compare( $installed_version, '1.1.0', '<' ) ) {
$this->upgrade_to_1_1_0();
}
if ( version_compare( $installed_version, '1.2.0', '<' ) ) {
$this->upgrade_to_1_2_0();
}
}
private function upgrade_to_1_1_0() {
// Migrate old options
$old_value = get_option( 'my_plugin_old_setting' );
if ( $old_value ) {
update_option( 'my_plugin_new_setting', $old_value );
delete_option( 'my_plugin_old_setting' );
}
}
private function upgrade_to_1_2_0() {
// Database changes
global $wpdb;
$wpdb->query(
"ALTER TABLE {$wpdb->prefix}my_table ADD COLUMN new_column VARCHAR(255)"
);
}
};
}
}
Install Routine¶
<?php
class My_Lifecycle extends Woodev_Lifecycle {
protected function install() {
// Set default options
update_option( 'my_plugin_version', $this->get_plugin()->get_version() );
update_option( 'my_plugin_installed', time() );
// Create database tables
$this->create_tables();
// Schedule events
wp_schedule_event( time(), 'daily', 'my_plugin_daily_event' );
}
private function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'my_plugin_table';
$sql = "CREATE TABLE $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql );
}
}
Dependency Management¶
Woodev_Plugin_Dependencies¶
Handles PHP extension, function, and settings checks:
<?php
class Woodev_Plugin_Dependencies {
protected $php_extensions = [];
protected $php_functions = [];
protected $php_settings = [];
protected $plugin;
public function __construct( Woodev_Plugin $plugin, $args = [] ) {
$this->plugin = $plugin;
$dependencies = $this->parse_dependencies( $args );
$this->php_extensions = (array) $dependencies['php_extensions'];
$this->php_functions = (array) $dependencies['php_functions'];
$this->php_settings = (array) $dependencies['php_settings'];
$this->add_hooks();
}
protected function add_hooks() {
add_action( 'admin_init', [ $this, 'add_admin_notices' ] );
}
}
Declaring Dependencies¶
<?php
parent::__construct(
'my-plugin',
'1.0.0',
[
'dependencies' => [
'php_extensions' => [ 'curl', 'json', 'mbstring' ],
'php_functions' => [ 'gzinflate', 'base64_decode' ],
'php_settings' => [
'allow_url_fopen' => '1',
'max_execution_time' => '30',
],
],
]
);
Checking Dependencies Manually¶
<?php
$plugin = My_Plugin::instance();
$dependency_handler = $plugin->get_dependency_handler();
// Get missing extensions
$missing_extensions = $dependency_handler->get_missing_php_extensions();
if ( ! empty( $missing_extensions ) ) {
echo 'Missing extensions: ' . implode( ', ', $missing_extensions );
}
// Get missing functions
$missing_functions = $dependency_handler->get_missing_php_functions();
if ( ! empty( $missing_functions ) ) {
echo 'Missing functions: ' . implode( ', ', $missing_functions );
}
// Get incompatible settings
$incompatible_settings = $dependency_handler->get_incompatible_php_settings();
if ( ! empty( $incompatible_settings ) ) {
echo 'Incompatible settings detected';
}
Note: The methods
get_missing_extension_dependencies(),get_missing_function_dependencies(), andget_incompatible_php_settings()onWoodev_Pluginare deprecated since 1.1.8. Use theWoodev_Plugin_Dependencieshandler via$plugin->get_dependency_handler()instead.
Logging¶
Using the Logger¶
<?php
$plugin = My_Plugin::instance();
// Log info message
$plugin->log( 'Processing order #' . $order_id );
// Log error
$plugin->log( 'API request failed: ' . $error_message );
// Log with context
$plugin->log( sprintf(
'Shipping rate calculated: %s kg = %s RUB',
$weight,
$rate
) );
Accessing WC_Logger¶
The logger() method is protected, so it is only available within the plugin class itself or subclasses. External code should use the public log() method instead:
<?php
// From within your plugin class (logger() is protected)
$this->logger()->add( $this->get_id(), 'Information message' );
// From external code, use the public log() method
$plugin->log( 'Information message' );
$plugin->log( 'Error message' );
// View logs: WooCommerce > Status > Logs
Custom Log File¶
<?php
class My_Plugin extends Woodev_Plugin {
public function log( $message, $log_id = null ) {
if ( is_null( $log_id ) ) {
$log_id = $this->get_id();
}
$this->logger()->add( $log_id, $message );
}
}
Hook Deprecator¶
Managing Deprecated Hooks¶
<?php
class My_Plugin extends Woodev_Plugin {
protected function init_hook_deprecator() {
$deprecated_hooks = array_merge(
$this->get_framework_deprecated_hooks(),
[
'my_plugin_old_action' => [
'removed' => false,
'replacement' => 'my_plugin_new_action',
'map' => true,
],
'my_plugin_removed_filter' => [
'removed' => true,
'replacement' => 'my_plugin_replacement_filter',
'map' => true,
],
]
);
$this->hook_deprecator = new Woodev_Hook_Deprecator(
$this->get_plugin_name(),
$deprecated_hooks
);
}
}
Hook Deprecation Format¶
<?php
$deprecated_hooks = [
'old_hook_name' => [
'removed' => false, // true if hook is removed
'replacement' => 'new_hook_name',
'map' => true, // true to map old to new
],
];
Template Loading¶
Loading Templates¶
<?php
$plugin = My_Plugin::instance();
// Load template with variables
$plugin->load_template( 'emails/shipping-notice.php', [
'order' => $order,
'tracking_number' => $tracking,
] );
Template Hierarchy¶
Templates are loaded from:
wp-content/themes/your-theme/woocommerce/my-plugin/wp-content/uploads/woocommerce/my-plugin/your-plugin/templates/
Creating Templates¶
<?php
// templates/emails/shipping-notice.php
defined( 'ABSPATH' ) || exit;
/**
* @var WC_Order $order
* @var string $tracking_number
*/
?>
<h2><?php esc_html_e( 'Your order has shipped!', 'my-plugin' ); ?></h2>
<p>
<?php printf(
esc_html__( 'Order #%d has been shipped.', 'my-plugin' ),
$order->get_id()
); ?>
</p>
<p>
<?php printf(
esc_html__( 'Tracking number: %s', 'my-plugin' ),
$tracking_number
); ?>
</p>
Helper Methods¶
Plugin Information¶
<?php
$plugin = My_Plugin::instance();
// Get plugin URLs
echo $plugin->get_settings_url(); // Settings page URL
echo $plugin->get_documentation_url(); // Documentation URL
echo $plugin->get_support_url(); // Support URL
// Check if on settings page
if ( $plugin->is_general_configuration_page() ) {
// On settings page
}
File Operations¶
<?php
// Get uploads path
$upload_path = $plugin->get_woocommerce_uploads_path();
// Load class from file
$plugin->load_class( 'class-my-class.php', 'My_Class' );
Compatibility Checks¶
<?php
// Check if TLS 1.2 is available
if ( $plugin->is_tls_1_2_available() ) {
// Use TLS 1.2
}
// Require TLS 1.2 for requests
if ( $plugin->require_tls_1_2() ) {
add_action( 'http_api_curl', [ $api, 'set_tls_1_2_request' ], 10, 3 );
}
Complete Example¶
<?php
/**
* Plugin Name: My Plugin
* Version: 1.0.0
*/
defined( 'ABSPATH' ) || exit;
// Include bootstrap
if ( ! class_exists( 'Woodev_Plugin_Bootstrap' ) ) {
require_once plugin_dir_path( __FILE__ ) . 'woodev/bootstrap.php';
}
// Register plugin
add_action( 'plugins_loaded', 'init_my_plugin', 0 );
function init_my_plugin() {
Woodev_Plugin_Bootstrap::instance()->register_plugin(
'1.4.0',
'My Plugin',
__FILE__,
'my_plugin_init',
[
'minimum_wc_version' => '8.0',
'minimum_wp_version' => '5.9',
]
);
}
// Initialize plugin
function my_plugin_init() {
require_once plugin_dir_path( __FILE__ ) . 'includes/class-my-plugin.php';
return My_Plugin::instance();
}
<?php
// includes/class-my-plugin.php
defined( 'ABSPATH' ) || exit;
final class My_Plugin extends Woodev_Plugin {
private static $instance;
public static function instance(): self {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
public function __construct() {
parent::__construct(
'my-plugin',
'1.0.0',
[
'text_domain' => 'my-plugin',
'dependencies' => [
'php_extensions' => [ 'curl', 'json' ],
],
'supported_features' => [
'hpos' => true,
'blocks' => [
'cart' => true,
'checkout' => true,
],
],
]
);
}
protected function get_file() {
return __FILE__;
}
public function get_plugin_name() {
return __( 'My Plugin', 'my-plugin' );
}
public function get_download_id() {
return 0;
}
protected function init_lifecycle_handler() {
$this->lifecycle_handler = new class( $this ) extends Woodev_Lifecycle {
protected function install() {
parent::install();
update_option( 'my_plugin_installed', time() );
}
};
}
}
Best Practices¶
1. Use Singleton Pattern¶
<?php
final class My_Plugin extends Woodev_Plugin {
private static $instance;
public static function instance(): self {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
}
2. Mark Class as Final¶
<?php
// Prevent inheritance that could break the framework
final class My_Plugin extends Woodev_Plugin {}
3. Declare All Dependencies¶
<?php
parent::__construct(
'my-plugin',
'1.0.0',
[
'dependencies' => [
'php_extensions' => [ 'curl', 'json' ],
'php_functions' => [ 'gzinflate' ],
],
]
);
4. Implement Lifecycle Handlers¶
<?php
protected function init_lifecycle_handler() {
$this->lifecycle_handler = new class( $this ) extends Woodev_Lifecycle {
protected function upgrade( string $installed_version ) {
parent::upgrade( $installed_version );
// Your upgrade logic
}
};
}
5. Use Logging¶
<?php
public function process_order( int $order_id ) {
$this->log( "Processing order #{$order_id}" );
try {
// Process order
} catch ( Exception $e ) {
$this->log( "Error: {$e->getMessage()}" );
throw $e;
}
}
Related Documentation¶
- Getting Started — Installation and setup
- Helpers — Utility functions
- Admin Module — Admin pages and notices
- Utilities — Background processing
For more information, see README.md.