Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"require": {
"php": "^8.4",
"brightnucleus/config": "^0.5",
"cedaro/wp-plugin": "^1.0",
"composer/installers": "^2"
},
"require-dev": {
Expand Down
69 changes: 21 additions & 48 deletions config/defaults.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,75 +2,49 @@
/**
* Plugin configuration file
*
* Translatable labels are closures, so the translation functions only run
* when the labels are resolved during hook callbacks (after init). Calling
* translation functions while this file loads would trigger the
* _load_textdomain_just_in_time notice in WordPress 6.7+.
*
* @package Gamajo\PluginSlug
* @author Gary Jones
* @copyright 2024 Gary Jones
* @copyright 2024-2026 Gary Jones
* @license GPL-2.0-or-later
*/

declare( strict_types = 1 );

namespace Gamajo\PluginSlug;

$plugin_slug_plugin = [
'textdomain' => 'plugin-slug',
'languages_dir' => 'languages',
];

$plugin_slug_settings = [
'submenu_pages' => [
[
'parent_slug' => 'options-general.php',
'page_title' => __( 'Plugin Slug Settings', 'plugin-slug' ),
'menu_title' => __( 'Plugin Slug', 'plugin-slug' ),
'capability' => 'manage_options',
'menu_slug' => 'plugin-slug',
'view' => PLUGIN_SLUG_DIR . 'views/admin-page.php',
'dependencies' => [
'styles' => [],
'scripts' => [
[
'handle' => 'plugin-slug-js',
'src' => PLUGIN_SLUG_URL . 'js/admin-page.js',
'deps' => [ 'jquery' ],
'ver' => '1.2.3',
'in_footer' => true,
'is_needed' => function ( $context ): bool {
if ( $context ) {
return false;
}

return true;
},
'localize' => [
'name' => 'pluginSlugI18n',
'data' => function ( $context ): array {
return [
'test_localize_data' => 'test_localize_value',
'context' => $context,
];
},
],
],
],
'handlers' => [
'scripts' => 'BrightNucleus\Dependency\ScriptHandler',
'styles' => 'BrightNucleus\Dependency\StyleHandler',
],
],
'parent_slug' => 'options-general.php',
'page_title' => static fn(): string => __( 'Plugin Slug Settings', 'plugin-slug' ),
'menu_title' => static fn(): string => __( 'Plugin Slug', 'plugin-slug' ),
'capability' => 'manage_options',
'menu_slug' => 'plugin-slug',
'view' => PLUGIN_SLUG_DIR . 'views/admin-page.php',
],
],
'settings' => [
'setting1' => [
'option_group' => 'pluginslug',
'sanitize_callback' => null,
'sanitize_callback' => static function ( mixed $value ): array {
if ( ! is_array( $value ) ) {
return [];
}

return array_map( 'sanitize_text_field', $value );
},
'sections' => [
'section1' => [
'title' => __( 'My Section Title', 'plugin-slug' ),
'title' => static fn(): string => __( 'My Section Title', 'plugin-slug' ),
'view' => PLUGIN_SLUG_DIR . 'views/section1.php',
'fields' => [
'field1' => [
'title' => __( 'My Field Title', 'plugin-slug' ),
'title' => static fn(): string => __( 'My Field Title', 'plugin-slug' ),
'view' => PLUGIN_SLUG_DIR . 'views/field1.php',
],
],
Expand All @@ -83,7 +57,6 @@
return [
'Gamajo' => [
'PluginSlug' => [
'Plugin' => $plugin_slug_plugin,
'Settings' => $plugin_slug_settings,
],
],
Expand Down
49 changes: 37 additions & 12 deletions plugin-slug.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,27 @@
/**
* Plugin Name
*
* The GitHub Plugin URI header below is read by Git Updater (formerly
* GitHub Updater) to serve plugin updates directly from GitHub.
*
* @package Gamajo\PluginSlug
* @author Gary Jones
* @copyright 2024 Gary Jones
* @copyright 2024-2026 Gary Jones
* @license GPL-2.0-or-later
*
* @wordpress-plugin
* Plugin Name: Plugin Boilerplate
* Plugin URI: https://github.com/garyjones/...
* Plugin URI: https://github.com/GaryJones/plugin-slug
* Description: ...
* Version: 0.1.0
* Author: Gary Jones
* Author URI: https://garyjones.io
* Text Domain: plugin-slug
* License: GPL-2.0-or-later
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
* GitHub Plugin URI: https://github.com/garyjones/...
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* GitHub Plugin URI: https://github.com/GaryJones/plugin-slug
* Requires PHP: 8.4
* Requires WP: 6.9
* Requires at least: 6.9
*/

declare( strict_types = 1 );
Expand All @@ -37,15 +40,37 @@
define( 'PLUGIN_SLUG_DIR', plugin_dir_path( __FILE__ ) );
}

if ( ! defined( 'PLUGIN_SLUG_URL' ) ) {
define( 'PLUGIN_SLUG_URL', plugin_dir_url( __FILE__ ) );
}

// Load Composer autoloader.
if ( file_exists( __DIR__ . '/vendor/autoload.php' ) ) {
require_once __DIR__ . '/vendor/autoload.php';
}

// Initialize the plugin.
$GLOBALS['plugin_slug'] = new Plugin( ConfigFactory::create( __DIR__ . '/config/defaults.php' )->getSubConfig( 'Gamajo\PluginSlug' ) );
$GLOBALS['plugin_slug']->run();
// Initialize the plugin on a hook, rather than at file include time.
add_action(
'plugins_loaded',
static function (): void {
plugin_slug()->run();
}
);

/**
* Get the plugin instance.
*
* Builds and caches the Plugin object on first call, so no work happens
* when this file is merely included.
*
* @since 0.1.0
*
* @return Plugin Plugin instance.
*/
function plugin_slug(): Plugin {
static $plugin = null;

if ( null === $plugin ) {
$plugin = new Plugin(
ConfigFactory::create( __DIR__ . '/config/defaults.php' )->getSubConfig( 'Gamajo\PluginSlug' )
);
}

return $plugin;
}
10 changes: 9 additions & 1 deletion src/Foo.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* @package Gamajo\PluginSlug
* @author Gary Jones
* @copyright 2024 Gary Jones
* @copyright 2024-2026 Gary Jones
* @license GPL-2.0-or-later
*/

Expand All @@ -14,10 +14,16 @@

/**
* Foo class.
*
* @since 0.1.0
*/
class Foo {
/**
* Bar.
*
* @since 0.1.0
*
* @return string Identification of the method being called.
*/
public function bar(): string {
return 'Foo::bar()';
Expand All @@ -26,6 +32,8 @@ public function bar(): string {
/**
* Returns true, always.
*
* @since 0.1.0
*
* @return true
*/
public function is_true(): bool {
Expand Down
88 changes: 76 additions & 12 deletions src/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*
* @package Gamajo\PluginSlug
* @author Gary Jones
* @copyright 2024 Gary Jones
* @copyright 2024-2026 Gary Jones
* @license GPL-2.0-or-later
*/

Expand All @@ -15,7 +15,6 @@
use BrightNucleus\Config\ConfigInterface;
use BrightNucleus\Config\ConfigTrait;
use BrightNucleus\Config\Exception\FailedToProcessConfigException;
use BrightNucleus\Settings\Settings;

/**
* Main plugin class.
Expand All @@ -29,19 +28,9 @@ class Plugin {

use ConfigTrait;

/**
* Static instance of the plugin.
*
* @since 0.1.0
*/
protected static Plugin $instance;

/**
* Instantiate a Plugin object.
*
* Don't call the constructor directly, use the `Plugin::get_instance()`
* static method instead.
*
* @since 0.1.0
*
* @throws FailedToProcessConfigException If the Config could not be parsed correctly.
Expand All @@ -58,5 +47,80 @@ public function __construct( ConfigInterface $config ) {
* @since 0.1.0
*/
public function run(): void {
add_action( 'admin_menu', $this->register_admin_pages( ... ) );
add_action( 'admin_init', $this->register_settings( ... ) );
}

/**
* Register the plugin admin pages from the config.
*
* Runs on the admin_menu hook, which fires after init, so the page and
* menu title closures can be safely resolved to translated strings here.
*
* @since 0.1.0
*/
public function register_admin_pages(): void {
foreach ( $this->getConfigKey( 'Settings', 'submenu_pages' ) as $page ) {
add_submenu_page(
$page['parent_slug'],
$page['page_title'](),
$page['menu_title'](),
$page['capability'],
$page['menu_slug'],
static function () use ( $page ): void {
require $page['view'];
}
);
}
}

/**
* Register settings, sections and fields from the config.
*
* Each field passes a label_for argument that matches the id of the
* input in the field view, so the rendered field title becomes a label
* for the form control.
*
* @since 0.1.0
*/
public function register_settings(): void {
foreach ( $this->getConfigKey( 'Settings', 'settings' ) as $option_name => $setting ) {
register_setting(
$setting['option_group'],
$option_name,
[
'sanitize_callback' => $setting['sanitize_callback'],
]
);

foreach ( $setting['sections'] as $section_id => $section ) {
add_settings_section(
$section_id,
$section['title'](),
static function () use ( $section ): void {
require $section['view'];
},
$setting['option_group']
);

foreach ( $section['fields'] as $field_id => $field ) {
add_settings_field(
$field_id,
$field['title'](),
// $args is used by the view file pulled in via require.
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
static function ( array $args ) use ( $field ): void {
require $field['view'];
},
$setting['option_group'],
$section_id,
[
'label_for' => $field_id,
'option_name' => $option_name,
]
);
}
}
}
}
}
Loading
Loading