baxtian/wp_settings

Class to be inherite to create settings

Maintainers

Package info

bitbucket.org/baxtian/wp_settings

pkg:composer/baxtian/wp_settings

Transparency log

Statistics

Installs: 80

Dependents: 0

Suggesters: 0

0.1.15 2026-08-02 15:35 UTC

This package is auto-updated.

Last update: 2026-08-02 15:36:27 UTC


README

Class to be inherited to create a WP Settings.

Mantainers

Juan SebastiΓ‘n Echeverry baxtian.echeverry@gmail.com

πŸš€ Usage Guide

This library requires a minimum of two configuration files: one to define the Page and one to define a Section within that page.

1. Defining the Settings Page

The page configuration file sets up the main menu item, its location, and basic properties within the WordPress dashboard.

PropertyDescriptionRequired?Example Value
slugThe unique identifier (slug) for this settings page in WordPress.Yesmy_plugin_options
page_titleThe title displayed at the top of the settings screen.YesMy Plugin Settings
sectionsAn array containing instances of the defined Section classes.YesSee examples below.
menu_titleThe text displayed for the menu/sub-menu item in the dashboard.YesPlugin Options
parent_slugThe slug of the existing parent menu this page belongs to.Yesoptions-general.php

Parent Slugs for Standard WordPress Menus:

  • Appearance: themes.php
  • Tools: tools.php
  • Settings: options-general.php
  • (Use a custom slug for a new top-level menu.)
<?php
namespace My_Plugin\Settings;

use My_Plugin\Settings\Section\Style;
use Baxtian\WP_Settings;

/**
 * Configuration page
 */
class My_Plugin extends WP_Settings
{
	use \Baxtian\SingletonTrait;

	protected function __construct()
	{
		$this->slug        = 'my_plugin';
		$this->parent_slug = 'options-general.php';
		$this->sections    = [
			new Style()
		];

		add_action('init', [$this, 'init']);

		parent::__construct();
	}

	public function init()
	{
		$this->page_title = __('My Plugin Settings', 'my_plugin');
		$this->menu_title = __('My Plugin', 'my_plugin');
	}

}

Filename: src/Settings/My_Plugin.php

2. Defining a Section

The section configuration defines a tab or logical grouping of settings within the Page.

PropertyDescriptionRequired?Example Value
slugThe unique identifier (slug) for this section within the page.Yesgeneral_settings_tab
titleThe title displayed for the section's tab.YesGeneral Settings
subsectionsAn array containing the definitions for each subsection.YesSee Subsection Properties below.

3. Defining Subsections and Fields

The subsections array holds definitions for the fields that will actually store data, grouped logically for display.

Subsection Properties

Each item in the subsections array defines a block of related fields:

PropertyDescriptionNotes
slugThe unique identifier (slug) for this subsection within the section.Used internally for grouping fields.
titleThe displayed title for this subsection block.Set to false to suppress the title display.
descriptionDescriptive text displayed below the title.Set to false to suppress the description.
fieldsAn array defining all input fields for this subsection.See Field Properties below.

Field Properties

Each item in the fields array defines a single input control:

PropertyDescriptionDefaultAvailable Types
nameThe unique option name used to retrieve this field's stored value.
labelThe descriptive label displayed next to the field.
typeThe type of input field to render.texttext, checkbox, dropdown, number, password
defaultThe fallback value used if the option has not yet been saved by the user.
descriptionHelp text displayed beneath the input field.Set to false to hide.
classCustom CSS classes to apply to the input element.
editable_ifA callable evaluated (with no arguments) on every save. When it returns false, the submitted value for this field is discarded and the previously stored value is restored β€” enforced server-side in the sanitize_callback, independent of whether the field is rendered readonly/disabled in the browser.null (always editable)
<?php
namespace My_Plugin\Settings\Section;

use Baxtian\WP_Settings\Section;

/**
 * Section of the configuration
 */
class Style extends Section
{
	public function __construct()
	{
		$this->slug = 'style';
		add_action('init', [$this, 'init']);

		parent::__construct();
	}

	public function init()
	{
		$this->title = __('Style', 'my_plugin');

		$this->subsections = [
			[
				'slug'        => 'colors',
				'title'       => __('Colors', 'my_plugin'),
				'description' => false,
				'fields'      => [
					[
						'name'        => 'text_color',
						'label'       => __('Text color', 'my_plugin'),
						'class'       => false,
						'description' => false,
						'default'     => 'black',
						'type'        => 'string',
					],
					[
						'name'        => 'text_background',
						'label'       => __('Text background color', 'my_plugin'),
						'class'       => false,
						'description' => false,
						'default'     => 'silver',
						'type'        => 'string',
					],
				],
			],
		];
	}
}

Filename: src/Settings/Section/Style.php

4. Extending Sections from Another Plugin

WP_Settings::__construct() applies the filter wp_settings_sections_{slug} (where {slug} is the transformed slug β€” for the page-owning class from section 1 above, $this->slug = 'my_plugin' yields 'config_my_plugin') right after computing $this->slug and before linking sections to it. This lets a separate plugin add its own Section to an existing settings page without modifying its source:

<?php
namespace Other_Plugin\Settings;

class Sections
{
    use \Baxtian\SingletonTrait;

    protected function __construct()
    {
        add_filter('wp_settings_sections_config_my_plugin', [$this, 'add_section']);
    }

    public function add_section($sections)
    {
        $sections[] = new Section\MySection();
        return $sections;
    }
}

Computing the filter name: the slug passed to the filter is str_replace('-', '_', sanitize_title('Config-' . $original_slug)). For $this->slug = 'my_plugin' this yields config_my_plugin.

Load-order guarantee: the wp_settings_sections_{slug} filter is applied on the init action (priority 20) β€” not in the constructor, and not lazily on first render. This means the page-owning class can be constructed eagerly, at plugin load time, even by a theme that extends it: themes finish loading after every plugin, so a filter added from functions.php would otherwise always be registered too late for a filter applied in the constructor. init runs after every plugin/theme's normal loading has registered its add_filter() call, but β€” importantly β€” before admin_init, which is when each Section registers its fields via add_settings_section()/add_settings_field(). A section added by the filter still needs to exist before admin_init fires for its fields to register; resolving any later (e.g. on first render) would miss that window entirely.

Reading values from outside the owning plugin: a Section added via this filter still stores its values in the page-owning class's option (e.g. config_my_plugin, not one scoped to the extending plugin/theme). If you're that extending plugin/theme and need to read a value elsewhere in your own code (not from your Section, which already gets option_name for free β€” see below), read the option directly with WordPress's native get_option('config_my_plugin', [])['field_name'] ?? $default, not by instantiating the page-owning class (My_Plugin\Settings\My_Plugin::get_instance()->get_option(...), the class from section 1 that declared $this->slug = 'my_plugin'). Two reasons:

  • Decoupling. The option name is the real, stable contract between the two β€” it's what your own Section already writes to and what the page's installer/activation code already reads/writes. The class that owns the page is an internal implementation detail of that plugin; depending on its name/namespace couples you to something it's free to restructure without warning.
  • No exception path. WP_Settings::get_option() throws if the field isn't registered in the resolved sections or has no default β€” an unnecessary failure mode for code (e.g. a front-end template) that should just fall back to a default instead of fataling.

Code that lives inside the page-owning plugin (alongside the WP_Settings subclass itself) doesn't need this β€” calling its own class's get_option() there is a normal in-package dependency, not a cross-boundary one.

5. Retrieving an Option Value

To retrieve a field's value, call the instance of the settings class and use the get_option() method. If the field has not been configured by the user, the system automatically returns the defined default value. If the field is not registered or does not have a default value, the system generates an exception.

use My_Plugin\Settings\My_Plugin as Settings;
.
.
.
$settings = Settings::get_instance();
$text_color = $settings->get_option('text_color');
$text_background = $settings->get_option('text_background');

Changelog

0.1.14

  • πŸ› fix: resolve sections on init (not lazily on first render/save/get_option() as in 0.1.13) β€” 0.1.13 broke every section's fields, because each Section::__construct() hooks its own admin_init to register fields, and resolving as late as first render meant admin_init had already fired by the time a filter-added section was constructed. init still runs after every plugin/theme has registered its add_filter() call, but early enough for the resulting sections' admin_init hooks to fire normally.

0.1.13

  • πŸ› fix: defer the wp_settings_sections_{slug} filter from the constructor to first use, so a settings page constructed eagerly at plugin load time doesn't resolve its sections before a theme (which always loads after every plugin) has had the chance to register its own add_filter() call.

0.1.12

  • ✨ feat: add editable_if to restrict field editing by permission.

0.1.11

  • ✨ feat(settings): keep the tab active after saving the settings

0.1.9

  • Add filter wp_settings_sections_{slug} in WP_Settings::__construct(), allowing other plugins to extend an existing settings page with their own sections.

0.1.8

  • Add type image.
  • Sanitize checkbox.
  • Use toggler for checkboxes.

0.1.5

  • Add type dropdown.

0.1.4

  • Keep opened last selected tab.

0.1.3

  • Add type textarea.

0.1.2

  • Documentation.

0.1.1

  • First stable release.