the last couple of weeks i have been working on my own WordPress plugin. I am trying to do a class based codebase.
My plugin works correctly, but i don't really know if this is the correct way to develop. I would like to learn the best practices.
To demonstrate, here is my base plugin file:
<?php
/**
* Blub Hamerhaai plugin
*
* This is the main blub plugin for all sites
*
* @link {github link}
* @since 0.0.2
* @package Blub Hamerhaai
*
* @wordpress-plugin
* Plugin Name: {plugin name}
* Plugin URI: {github link}
* Description: This is the main blub plugin for all sites
* Version: 0.0.4
* Author: Blub
* Author URI: {link}
* License: Copyright Blub media, all rights reserved
* License URI: {link}
* Text Domain: blub-hamerhaai
* Domain Path: /languages
*
* Created by: Me
*/
namespace Blub;
require 'includes/includes.php';
define(__NAMESPACE__ . '\PLUGINROOT', __FILE__);
define(__NAMESPACE__ . '\PLUGINDIR', __DIR__);
if (!class_exists('Hamerhaai')) {
class Hamerhaai
{
public function __construct()
{
register_activation_hook(__FILE__, [$this, 'activation']);
register_deactivation_hook(__FILE__, [$this, 'deactivation']);
add_action('init', [$this, 'init']);
}
public function activation()
{
BlubRole::add();
PluginUpdate::add();
}
public function deactivation()
{
BlubRole::remove();
PluginUpdate::remove();
}
public function init()
{
//Preventing removals in other pages
global $pagenow;
load_textdomain('blub-hamerhaai', PLUGINDIR . '/languages/blub-hamerhaai-nl_NL.mo');
//Things to do for all users
new OptionsPage();
new BlubColors();
new CustomLoginPage();
$widget = new DashboardWidget();
// Things to do for blub user
if (BlubRole::isBlubUser()) {
new AdminText();
new RemoveMenuItems();
new GutenbergEdits();
//Only for dashboard
if ($pagenow === 'index.php') {
$widget->forceShow();
$widget->removeDefault();
}
new LoadFrontendStyle();
}
}
}
}
new Hamerhaai();
The main thing i am insecure about is instantiating all the classes on the plugin init function. I don't think this is the best practice. All the classes are based on a specific functionality.
Can someone explain me if this is the way to go? Or how to improve?
Thanks in advance.
1 Answer 1
1. The constructor is not a good place for actions & filters.
See this very detailed answer from WP.SE or this blog post for some reasoning. As far as I understand, it makes testing much more difficult and in a oop-way doesn't make sense, as it doesn't directly relate to the object's state.
2. Do you really need that state?
Right now, your Hamerhaai
doesn't actually use state. In this case I usually define the methods as static, which makes enqueue/dequeue much simpler because you don't need the original object.
register_activation_hook(__FILE__, [$this, 'activation']);
// ...
public function activation()
{
BlubRole::add();
PluginUpdate::add();
}
would become
register_activation_hook(__FILE__, [__CLASS__, 'activation']);
// ...
public static function activation()
{
BlubRole::add();
PluginUpdate::add();
}
(using __CLASS__
simplifies it a lot so you don't need to watch out for namespaces, see PHP Doc)
3. Don't go half the way.
You're doing modern PHP code. So why require 'includes/includes.php';
? Use composer's autoloading feature and avoid manual include
/ require
statements. In my plugins, all code lives somewhere in a class. And the class is easily autoloaded via composer.
You're in a namespace, so if (!class_exists('Hamerhaai')) {
doesn't actually do what you think it does. Instead you should either check for if (!class_exists('Blub\Hamerhaai')) {
or as you're already using __NAMESPACE__
something like
if (!class_exists(__NAMESPACE__ . '\Hamerhaai')) {
4. Only load code that is actually required
//Things to do for all users
new OptionsPage();
new BlubColors();
new CustomLoginPage();
$widget = new DashboardWidget();
this code lives in your init()
method. So it is called on every request. Most likely you want to check if the user is logged in, has a specific role, etc. before actually calling it.