* WordPress Customize Control classes
if ( ! defined( 'ABSPATH' ) ) {
* Customize Control class.
#[AllowDynamicProperties]
class WP_Customize_Control {
* Incremented with each new class instantiation, then stored in $instance_number.
* Used when sorting two instances whose priorities are equal.
protected static $instance_count = 0;
* Order in which this instance was created in relation to other instances.
* @var WP_Customize_Manager
* All settings tied to the control.
* The primary setting for the control (if there is one).
* @var string|WP_Customize_Setting|null
public $setting = 'default';
* Capability required to use this control.
* Normally this is empty and the capability is derived from the capabilities
* of the associated `$settings`.
* Order priority to load the control in Customizer.
* Section the control belongs to.
* Description for the control.
public $description = '';
* List of choices for 'radio' or 'select' type controls, where values are the keys, and labels are the values.
public $choices = array();
* List of custom input attributes for control output, where attribute names are the keys and values are the values.
* Not used for 'checkbox', 'radio', 'select', or 'dropdown-pages' control types.
public $input_attrs = array();
* Show UI for adding new content, currently only used for the dropdown-pages control.
public $allow_addition = false;
* @deprecated It is better to just call the json() method
* @see WP_Customize_Control::active()
* @var callable Callback is called with one argument, the instance of
* WP_Customize_Control, and returns bool to indicate whether
* the control is active (such as it relates to the URL
* currently being previewed).
public $active_callback = '';
* Supplied `$args` override class property defaults.
* If `$args['settings']` is not defined, use the `$id` as the setting ID.
* @param WP_Customize_Manager $manager Customizer bootstrap instance.
* @param string $id Control ID.
* Optional. Array of properties for the new Control object. Default empty array.
* @type int $instance_number Order in which this instance was created in relation
* @type WP_Customize_Manager $manager Customizer bootstrap instance.
* @type string $id Control ID.
* @type array $settings All settings tied to the control. If undefined, `$id` will
* @type string $setting The primary setting for the control (if there is one).
* @type string $capability Capability required to use this control. Normally this is empty
* and the capability is derived from `$settings`.
* @type int $priority Order priority to load the control. Default 10.
* @type string $section Section the control belongs to. Default empty.
* @type string $label Label for the control. Default empty.
* @type string $description Description for the control. Default empty.
* @type array $choices List of choices for 'radio' or 'select' type controls, where
* values are the keys, and labels are the values.
* @type array $input_attrs List of custom input attributes for control output, where
* attribute names are the keys and values are the values. Not
* used for 'checkbox', 'radio', 'select', or 'dropdown-pages'
* control types. Default empty array.
* @type bool $allow_addition Show UI for adding new content, currently only used for the
* dropdown-pages control. Default false.
* @type array $json Deprecated. Use WP_Customize_Control::json() instead.
* @type string $type Control type. Core controls include 'text', 'checkbox',
* 'textarea', 'radio', 'select', and 'dropdown-pages'. Additional
* input types such as 'email', 'url', 'number', 'hidden', and
* 'date' are supported implicitly. Default 'text'.
* @type callable $active_callback Active callback.
public function __construct( $manager, $id, $args = array() ) {
$keys = array_keys( get_object_vars( $this ) );
foreach ( $keys as $key ) {
if ( isset( $args[ $key ] ) ) {
$this->$key = $args[ $key ];
$this->manager = $manager;
if ( empty( $this->active_callback ) ) {
$this->active_callback = array( $this, 'active_callback' );
self::$instance_count += 1;
$this->instance_number = self::$instance_count;
if ( ! isset( $this->settings ) ) {
if ( is_array( $this->settings ) ) {
foreach ( $this->settings as $key => $setting ) {
$settings[ $key ] = $this->manager->get_setting( $setting );
} elseif ( is_string( $this->settings ) ) {
$this->setting = $this->manager->get_setting( $this->settings );
$settings['default'] = $this->setting;
$this->settings = $settings;
* Enqueues control related scripts/styles.
public function enqueue() {}
* Checks whether control is active to current Customizer preview.
* @return bool Whether the control is active to the current preview.
final public function active() {
$active = call_user_func( $this->active_callback, $this );
* Filters response of WP_Customize_Control::active().
* @param bool $active Whether the Customizer control is active.
* @param WP_Customize_Control $control WP_Customize_Control instance.
$active = apply_filters( 'customize_control_active', $active, $control );
* Default callback used when invoking WP_Customize_Control::active().
* Subclasses can override this with their specific logic, or they may
* provide an 'active_callback' argument to the constructor.
* @return true Always true.
public function active_callback() {
* Fetches a setting's value.
* Grabs the main setting by default.
* @param string $setting_key
* @return mixed The requested setting's value, if the setting exists.
final public function value( $setting_key = 'default' ) {
if ( isset( $this->settings[ $setting_key ] ) ) {
return $this->settings[ $setting_key ]->value();
* Refreshes the parameters passed to the JavaScript via JSON.
public function to_json() {
$this->json['settings'] = array();
foreach ( $this->settings as $key => $setting ) {
$this->json['settings'][ $key ] = $setting->id;
$this->json['type'] = $this->type;
$this->json['priority'] = $this->priority;
$this->json['active'] = $this->active();
$this->json['section'] = $this->section;
$this->json['content'] = $this->get_content();
$this->json['label'] = $this->label;
$this->json['description'] = $this->description;
$this->json['instanceNumber'] = $this->instance_number;
if ( 'dropdown-pages' === $this->type ) {
$this->json['allow_addition'] = $this->allow_addition;
* Gets the data to export to the client via JSON.
* @return array Array of parameters passed to the JavaScript.
* Checks if the user can use this control.
* Returns false if the user cannot manipulate one of the associated settings,
* or if one of the associated settings does not exist. Also returns false if
* the associated section does not exist or if its capability check returns
* @return bool False if theme doesn't support the control or user doesn't have the required permissions, otherwise true.
final public function check_capabilities() {
if ( ! empty( $this->capability ) && ! current_user_can( $this->capability ) ) {
foreach ( $this->settings as $setting ) {
if ( ! $setting || ! $setting->check_capabilities() ) {
$section = $this->manager->get_section( $this->section );
if ( isset( $section ) && ! $section->check_capabilities() ) {
* Gets the control's content for insertion into the Customizer pane.
* @return string Contents of the control.
final public function get_content() {
return trim( ob_get_clean() );
* Checks capabilities and render the control.
* @uses WP_Customize_Control::render()
final public function maybe_render() {
if ( ! $this->check_capabilities() ) {
* Fires just before the current Customizer control is rendered.
* @param WP_Customize_Control $control WP_Customize_Control instance.
do_action( 'customize_render_control', $this );
* Fires just before a specific Customizer control is rendered.
* The dynamic portion of the hook name, `$this->id`, refers to
* @param WP_Customize_Control $control WP_Customize_Control instance.
do_action( "customize_render_control_{$this->id}", $this );
* Renders the control wrapper and calls $this->render_content() for the internals.
protected function render() {
$id = 'customize-control-' . str_replace( array( '[', ']' ), array( '-', '' ), $this->id );
$class = 'customize-control customize-control-' . $this->type;
printf( '<li id="%s" class="%s">', esc_attr( $id ), esc_attr( $class ) );
* Gets the data link attribute for a setting.
* @since 4.9.0 Return a `data-customize-setting-key-link` attribute if a setting is not registered for the supplied setting key.
* @param string $setting_key
* @return string Data link parameter, a `data-customize-setting-link` attribute if the `$setting_key` refers
* to a pre-registered setting, and a `data-customize-setting-key-link` attribute if the setting
public function get_link( $setting_key = 'default' ) {
if ( isset( $this->settings[ $setting_key ] ) && $this->settings[ $setting_key ] instanceof WP_Customize_Setting ) {
return 'data-customize-setting-link="' . esc_attr( $this->settings[ $setting_key ]->id ) . '"';
return 'data-customize-setting-key-link="' . esc_attr( $setting_key ) . '"';
* Renders the data link attribute for the control's input element.
* @uses WP_Customize_Control::get_link()
* @param string $setting_key Default 'default'.
public function link( $setting_key = 'default' ) {
echo $this->get_link( $setting_key );
* Renders the custom attributes for the control's input element.
public function input_attrs() {
foreach ( $this->input_attrs as $attr => $value ) {
echo $attr . '="' . esc_attr( $value ) . '" ';
* Renders the control's content.
* Allows the content to be overridden without having to rewrite the wrapper in `$this::render()`.
* Supports basic input types `text`, `checkbox`, `textarea`, `radio`, `select` and `dropdown-pages`.
* Additional input types such as `email`, `url`, `number`, `hidden` and `date` are supported implicitly.
* Control content can alternately be rendered in JS. See WP_Customize_Control::print_template().
protected function render_content() {
$input_id = '_customize-input-' . $this->id;
$description_id = '_customize-description-' . $this->id;
$describedby_attr = ( ! empty( $this->description ) ) ? ' aria-describedby="' . esc_attr( $description_id ) . '" ' : '';
<span class="customize-inside-control-row">