public/edition/wp-includes/class-wp-widget.php line 360

Open in your IDE?
  1. <?php
  2. /**
  3.  * Widget API: WP_Widget base class
  4.  *
  5.  * @package WordPress
  6.  * @subpackage Widgets
  7.  * @since 4.4.0
  8.  */
  9. /**
  10.  * Core base class extended to register widgets.
  11.  *
  12.  * This class must be extended for each widget, and WP_Widget::widget() must be overridden.
  13.  *
  14.  * If adding widget options, WP_Widget::update() and WP_Widget::form() should also be overridden.
  15.  *
  16.  * @since 2.8.0
  17.  * @since 4.4.0 Moved to its own file from wp-includes/widgets.php
  18.  */
  19. #[AllowDynamicProperties]
  20. class WP_Widget {
  21.     /**
  22.      * Root ID for all widgets of this type.
  23.      *
  24.      * @since 2.8.0
  25.      * @var mixed|string
  26.      */
  27.     public $id_base;
  28.     /**
  29.      * Name for this widget type.
  30.      *
  31.      * @since 2.8.0
  32.      * @var string
  33.      */
  34.     public $name;
  35.     /**
  36.      * Option name for this widget type.
  37.      *
  38.      * @since 2.8.0
  39.      * @var string
  40.      */
  41.     public $option_name;
  42.     /**
  43.      * Alt option name for this widget type.
  44.      *
  45.      * @since 2.8.0
  46.      * @var string
  47.      */
  48.     public $alt_option_name;
  49.     /**
  50.      * Option array passed to wp_register_sidebar_widget().
  51.      *
  52.      * @since 2.8.0
  53.      * @var array
  54.      */
  55.     public $widget_options;
  56.     /**
  57.      * Option array passed to wp_register_widget_control().
  58.      *
  59.      * @since 2.8.0
  60.      * @var array
  61.      */
  62.     public $control_options;
  63.     /**
  64.      * Unique ID number of the current instance.
  65.      *
  66.      * @since 2.8.0
  67.      * @var bool|int
  68.      */
  69.     public $number false;
  70.     /**
  71.      * Unique ID string of the current instance (id_base-number).
  72.      *
  73.      * @since 2.8.0
  74.      * @var bool|string
  75.      */
  76.     public $id false;
  77.     /**
  78.      * Whether the widget data has been updated.
  79.      *
  80.      * Set to true when the data is updated after a POST submit - ensures it does
  81.      * not happen twice.
  82.      *
  83.      * @since 2.8.0
  84.      * @var bool
  85.      */
  86.     public $updated false;
  87.     //
  88.     // Member functions that must be overridden by subclasses.
  89.     //
  90.     /**
  91.      * Echoes the widget content.
  92.      *
  93.      * Subclasses should override this function to generate their widget code.
  94.      *
  95.      * @since 2.8.0
  96.      *
  97.      * @param array $args     Display arguments including 'before_title', 'after_title',
  98.      *                        'before_widget', and 'after_widget'.
  99.      * @param array $instance The settings for the particular instance of the widget.
  100.      */
  101.     public function widget$args$instance ) {
  102.         die( 'function WP_Widget::widget() must be overridden in a subclass.' );
  103.     }
  104.     /**
  105.      * Updates a particular instance of a widget.
  106.      *
  107.      * This function should check that `$new_instance` is set correctly. The newly-calculated
  108.      * value of `$instance` should be returned. If false is returned, the instance won't be
  109.      * saved/updated.
  110.      *
  111.      * @since 2.8.0
  112.      *
  113.      * @param array $new_instance New settings for this instance as input by the user via
  114.      *                            WP_Widget::form().
  115.      * @param array $old_instance Old settings for this instance.
  116.      * @return array Settings to save or bool false to cancel saving.
  117.      */
  118.     public function update$new_instance$old_instance ) {
  119.         return $new_instance;
  120.     }
  121.     /**
  122.      * Outputs the settings update form.
  123.      *
  124.      * @since 2.8.0
  125.      *
  126.      * @param array $instance The settings for the particular instance of the widget.
  127.      * @return string|void Default return is 'noform'.
  128.      */
  129.     public function form$instance ) {
  130.         echo '<p class="no-options-widget">' __'There are no options for this widget.' ) . '</p>';
  131.         return 'noform';
  132.     }
  133.     // Functions you'll need to call.
  134.     /**
  135.      * PHP5 constructor.
  136.      *
  137.      * @since 2.8.0
  138.      *
  139.      * @param string $id_base         Base ID for the widget, lowercase and unique. If left empty,
  140.      *                                a portion of the widget's PHP class name will be used. Has to be unique.
  141.      * @param string $name            Name for the widget displayed on the configuration page.
  142.      * @param array  $widget_options  Optional. Widget options. See wp_register_sidebar_widget() for
  143.      *                                information on accepted arguments. Default empty array.
  144.      * @param array  $control_options Optional. Widget control options. See wp_register_widget_control() for
  145.      *                                information on accepted arguments. Default empty array.
  146.      */
  147.     public function __construct$id_base$name$widget_options = array(), $control_options = array() ) {
  148.         if ( ! empty( $id_base ) ) {
  149.             $id_base strtolower$id_base );
  150.         } else {
  151.             $id_base preg_replace'/(wp_)?widget_/'''strtolowerget_class$this ) ) );
  152.         }
  153.         $this->id_base         $id_base;
  154.         $this->name            $name;
  155.         $this->option_name     'widget_' $this->id_base;
  156.         $this->widget_options  wp_parse_args(
  157.             $widget_options,
  158.             array(
  159.                 'classname'                   => str_replace'\\''_'$this->option_name ),
  160.                 'customize_selective_refresh' => false,
  161.             )
  162.         );
  163.         $this->control_options wp_parse_args$control_options, array( 'id_base' => $this->id_base ) );
  164.     }
  165.     /**
  166.      * PHP4 constructor.
  167.      *
  168.      * @since 2.8.0
  169.      * @deprecated 4.3.0 Use __construct() instead.
  170.      *
  171.      * @see WP_Widget::__construct()
  172.      *
  173.      * @param string $id_base         Base ID for the widget, lowercase and unique. If left empty,
  174.      *                                a portion of the widget's PHP class name will be used. Has to be unique.
  175.      * @param string $name            Name for the widget displayed on the configuration page.
  176.      * @param array  $widget_options  Optional. Widget options. See wp_register_sidebar_widget() for
  177.      *                                information on accepted arguments. Default empty array.
  178.      * @param array  $control_options Optional. Widget control options. See wp_register_widget_control() for
  179.      *                                information on accepted arguments. Default empty array.
  180.      */
  181.     public function WP_Widget$id_base$name$widget_options = array(), $control_options = array() ) {
  182.         _deprecated_constructor'WP_Widget''4.3.0'get_class$this ) );
  183.         WP_Widget::__construct$id_base$name$widget_options$control_options );
  184.     }
  185.     /**
  186.      * Constructs name attributes for use in form() fields
  187.      *
  188.      * This function should be used in form() methods to create name attributes for fields
  189.      * to be saved by update()
  190.      *
  191.      * @since 2.8.0
  192.      * @since 4.4.0 Array format field names are now accepted.
  193.      *
  194.      * @param string $field_name Field name.
  195.      * @return string Name attribute for `$field_name`.
  196.      */
  197.     public function get_field_name$field_name ) {
  198.         $pos strpos$field_name'[' );
  199.         if ( false !== $pos ) {
  200.             // Replace the first occurrence of '[' with ']['.
  201.             $field_name '[' substr_replace$field_name']['$posstrlen'[' ) );
  202.         } else {
  203.             $field_name '[' $field_name ']';
  204.         }
  205.         return 'widget-' $this->id_base '[' $this->number ']' $field_name;
  206.     }
  207.     /**
  208.      * Constructs id attributes for use in WP_Widget::form() fields.
  209.      *
  210.      * This function should be used in form() methods to create id attributes
  211.      * for fields to be saved by WP_Widget::update().
  212.      *
  213.      * @since 2.8.0
  214.      * @since 4.4.0 Array format field IDs are now accepted.
  215.      *
  216.      * @param string $field_name Field name.
  217.      * @return string ID attribute for `$field_name`.
  218.      */
  219.     public function get_field_id$field_name ) {
  220.         $field_name str_replace( array( '[]''['']' ), array( '''-''' ), $field_name );
  221.         $field_name trim$field_name'-' );
  222.         return 'widget-' $this->id_base '-' $this->number '-' $field_name;
  223.     }
  224.     /**
  225.      * Register all widget instances of this widget class.
  226.      *
  227.      * @since 2.8.0
  228.      */
  229.     public function _register() {
  230.         $settings $this->get_settings();
  231.         $empty    true;
  232.         // When $settings is an array-like object, get an intrinsic array for use with array_keys().
  233.         if ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) {
  234.             $settings $settings->getArrayCopy();
  235.         }
  236.         if ( is_array$settings ) ) {
  237.             foreach ( array_keys$settings ) as $number ) {
  238.                 if ( is_numeric$number ) ) {
  239.                     $this->_set$number );
  240.                     $this->_register_one$number );
  241.                     $empty false;
  242.                 }
  243.             }
  244.         }
  245.         if ( $empty ) {
  246.             // If there are none, we register the widget's existence with a generic template.
  247.             $this->_set);
  248.             $this->_register_one();
  249.         }
  250.     }
  251.     /**
  252.      * Sets the internal order number for the widget instance.
  253.      *
  254.      * @since 2.8.0
  255.      *
  256.      * @param int $number The unique order number of this widget instance compared to other
  257.      *                    instances of the same class.
  258.      */
  259.     public function _set$number ) {
  260.         $this->number $number;
  261.         $this->id     $this->id_base '-' $number;
  262.     }
  263.     /**
  264.      * Retrieves the widget display callback.
  265.      *
  266.      * @since 2.8.0
  267.      *
  268.      * @return callable Display callback.
  269.      */
  270.     public function _get_display_callback() {
  271.         return array( $this'display_callback' );
  272.     }
  273.     /**
  274.      * Retrieves the widget update callback.
  275.      *
  276.      * @since 2.8.0
  277.      *
  278.      * @return callable Update callback.
  279.      */
  280.     public function _get_update_callback() {
  281.         return array( $this'update_callback' );
  282.     }
  283.     /**
  284.      * Retrieves the form callback.
  285.      *
  286.      * @since 2.8.0
  287.      *
  288.      * @return callable Form callback.
  289.      */
  290.     public function _get_form_callback() {
  291.         return array( $this'form_callback' );
  292.     }
  293.     /**
  294.      * Determines whether the current request is inside the Customizer preview.
  295.      *
  296.      * If true -- the current request is inside the Customizer preview, then
  297.      * the object cache gets suspended and widgets should check this to decide
  298.      * whether they should store anything persistently to the object cache,
  299.      * to transients, or anywhere else.
  300.      *
  301.      * @since 3.9.0
  302.      *
  303.      * @global WP_Customize_Manager $wp_customize
  304.      *
  305.      * @return bool True if within the Customizer preview, false if not.
  306.      */
  307.     public function is_preview() {
  308.         global $wp_customize;
  309.         return ( isset( $wp_customize ) && $wp_customize->is_preview() );
  310.     }
  311.     /**
  312.      * Generates the actual widget content (Do NOT override).
  313.      *
  314.      * Finds the instance and calls WP_Widget::widget().
  315.      *
  316.      * @since 2.8.0
  317.      *
  318.      * @param array     $args        Display arguments. See WP_Widget::widget() for information
  319.      *                               on accepted arguments.
  320.      * @param int|array $widget_args {
  321.      *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
  322.      *     Default 1.
  323.      *
  324.      *     @type int $number Number increment used for multiples of the same widget.
  325.      * }
  326.      */
  327.     public function display_callback$args$widget_args ) {
  328.         if ( is_numeric$widget_args ) ) {
  329.             $widget_args = array( 'number' => $widget_args );
  330.         }
  331.         $widget_args wp_parse_args$widget_args, array( 'number' => -) );
  332.         $this->_set$widget_args['number'] );
  333.         $instances $this->get_settings();
  334.         if ( isset( $instances$this->number ] ) ) {
  335.             $instance $instances$this->number ];
  336.             /**
  337.              * Filters the settings for a particular widget instance.
  338.              *
  339.              * Returning false will effectively short-circuit display of the widget.
  340.              *
  341.              * @since 2.8.0
  342.              *
  343.              * @param array     $instance The current widget instance's settings.
  344.              * @param WP_Widget $widget   The current widget instance.
  345.              * @param array     $args     An array of default widget arguments.
  346.              */
  347.             $instance apply_filters'widget_display_callback'$instance$this$args );
  348.             if ( false === $instance ) {
  349.                 return;
  350.             }
  351.             $was_cache_addition_suspended wp_suspend_cache_addition();
  352.             if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
  353.                 wp_suspend_cache_additiontrue );
  354.             }
  355.             $this->widget$args$instance );
  356.             if ( $this->is_preview() ) {
  357.                 wp_suspend_cache_addition$was_cache_addition_suspended );
  358.             }
  359.         }
  360.     }
  361.     /**
  362.      * Handles changed settings (Do NOT override).
  363.      *
  364.      * @since 2.8.0
  365.      *
  366.      * @global array $wp_registered_widgets
  367.      *
  368.      * @param int $deprecated Not used.
  369.      */
  370.     public function update_callback$deprecated ) {
  371.         global $wp_registered_widgets;
  372.         $all_instances $this->get_settings();
  373.         // We need to update the data.
  374.         if ( $this->updated ) {
  375.             return;
  376.         }
  377.         if ( isset( $_POST['delete_widget'] ) && $_POST['delete_widget'] ) {
  378.             // Delete the settings for this instance of the widget.
  379.             if ( isset( $_POST['the-widget-id'] ) ) {
  380.                 $del_id $_POST['the-widget-id'];
  381.             } else {
  382.                 return;
  383.             }
  384.             if ( isset( $wp_registered_widgets$del_id ]['params'][0]['number'] ) ) {
  385.                 $number $wp_registered_widgets$del_id ]['params'][0]['number'];
  386.                 if ( $this->id_base '-' $number === $del_id ) {
  387.                     unset( $all_instances$number ] );
  388.                 }
  389.             }
  390.         } else {
  391.             if ( isset( $_POST'widget-' $this->id_base ] ) && is_array$_POST'widget-' $this->id_base ] ) ) {
  392.                 $settings $_POST'widget-' $this->id_base ];
  393.             } elseif ( isset( $_POST['id_base'] ) && $_POST['id_base'] === $this->id_base ) {
  394.                 $num      $_POST['multi_number'] ? (int) $_POST['multi_number'] : (int) $_POST['widget_number'];
  395.                 $settings = array( $num => array() );
  396.             } else {
  397.                 return;
  398.             }
  399.             foreach ( $settings as $number => $new_instance ) {
  400.                 $new_instance stripslashes_deep$new_instance );
  401.                 $this->_set$number );
  402.                 $old_instance = isset( $all_instances$number ] ) ? $all_instances$number ] : array();
  403.                 $was_cache_addition_suspended wp_suspend_cache_addition();
  404.                 if ( $this->is_preview() && ! $was_cache_addition_suspended ) {
  405.                     wp_suspend_cache_additiontrue );
  406.                 }
  407.                 $instance $this->update$new_instance$old_instance );
  408.                 if ( $this->is_preview() ) {
  409.                     wp_suspend_cache_addition$was_cache_addition_suspended );
  410.                 }
  411.                 /**
  412.                  * Filters a widget's settings before saving.
  413.                  *
  414.                  * Returning false will effectively short-circuit the widget's ability
  415.                  * to update settings.
  416.                  *
  417.                  * @since 2.8.0
  418.                  *
  419.                  * @param array     $instance     The current widget instance's settings.
  420.                  * @param array     $new_instance Array of new widget settings.
  421.                  * @param array     $old_instance Array of old widget settings.
  422.                  * @param WP_Widget $widget       The current widget instance.
  423.                  */
  424.                 $instance apply_filters'widget_update_callback'$instance$new_instance$old_instance$this );
  425.                 if ( false !== $instance ) {
  426.                     $all_instances$number ] = $instance;
  427.                 }
  428.                 break; // Run only once.
  429.             }
  430.         }
  431.         $this->save_settings$all_instances );
  432.         $this->updated true;
  433.     }
  434.     /**
  435.      * Generates the widget control form (Do NOT override).
  436.      *
  437.      * @since 2.8.0
  438.      *
  439.      * @param int|array $widget_args {
  440.      *     Optional. Internal order number of the widget instance, or array of multi-widget arguments.
  441.      *     Default 1.
  442.      *
  443.      *     @type int $number Number increment used for multiples of the same widget.
  444.      * }
  445.      * @return string|null
  446.      */
  447.     public function form_callback$widget_args ) {
  448.         if ( is_numeric$widget_args ) ) {
  449.             $widget_args = array( 'number' => $widget_args );
  450.         }
  451.         $widget_args   wp_parse_args$widget_args, array( 'number' => -) );
  452.         $all_instances $this->get_settings();
  453.         if ( -=== $widget_args['number'] ) {
  454.             // We echo out a form where 'number' can be set later.
  455.             $this->_set'__i__' );
  456.             $instance = array();
  457.         } else {
  458.             $this->_set$widget_args['number'] );
  459.             $instance $all_instances$widget_args['number'] ];
  460.         }
  461.         /**
  462.          * Filters the widget instance's settings before displaying the control form.
  463.          *
  464.          * Returning false effectively short-circuits display of the control form.
  465.          *
  466.          * @since 2.8.0
  467.          *
  468.          * @param array     $instance The current widget instance's settings.
  469.          * @param WP_Widget $widget   The current widget instance.
  470.          */
  471.         $instance apply_filters'widget_form_callback'$instance$this );
  472.         $return null;
  473.         if ( false !== $instance ) {
  474.             $return $this->form$instance );
  475.             /**
  476.              * Fires at the end of the widget control form.
  477.              *
  478.              * Use this hook to add extra fields to the widget form. The hook
  479.              * is only fired if the value passed to the 'widget_form_callback'
  480.              * hook is not false.
  481.              *
  482.              * Note: If the widget has no form, the text echoed from the default
  483.              * form method can be hidden using CSS.
  484.              *
  485.              * @since 2.8.0
  486.              *
  487.              * @param WP_Widget $widget   The widget instance (passed by reference).
  488.              * @param null      $return   Return null if new fields are added.
  489.              * @param array     $instance An array of the widget's settings.
  490.              */
  491.             do_action_ref_array'in_widget_form', array( &$this, &$return$instance ) );
  492.         }
  493.         return $return;
  494.     }
  495.     /**
  496.      * Registers an instance of the widget class.
  497.      *
  498.      * @since 2.8.0
  499.      *
  500.      * @param int $number Optional. The unique order number of this widget instance
  501.      *                    compared to other instances of the same class. Default -1.
  502.      */
  503.     public function _register_one$number = -) {
  504.         wp_register_sidebar_widget(
  505.             $this->id,
  506.             $this->name,
  507.             $this->_get_display_callback(),
  508.             $this->widget_options,
  509.             array( 'number' => $number )
  510.         );
  511.         _register_widget_update_callback(
  512.             $this->id_base,
  513.             $this->_get_update_callback(),
  514.             $this->control_options,
  515.             array( 'number' => -)
  516.         );
  517.         _register_widget_form_callback(
  518.             $this->id,
  519.             $this->name,
  520.             $this->_get_form_callback(),
  521.             $this->control_options,
  522.             array( 'number' => $number )
  523.         );
  524.     }
  525.     /**
  526.      * Saves the settings for all instances of the widget class.
  527.      *
  528.      * @since 2.8.0
  529.      *
  530.      * @param array $settings Multi-dimensional array of widget instance settings.
  531.      */
  532.     public function save_settings$settings ) {
  533.         $settings['_multiwidget'] = 1;
  534.         update_option$this->option_name$settings );
  535.     }
  536.     /**
  537.      * Retrieves the settings for all instances of the widget class.
  538.      *
  539.      * @since 2.8.0
  540.      *
  541.      * @return array Multi-dimensional array of widget instance settings.
  542.      */
  543.     public function get_settings() {
  544.         $settings get_option$this->option_name );
  545.         if ( false === $settings ) {
  546.             $settings = array();
  547.             if ( isset( $this->alt_option_name ) ) {
  548.                 // Get settings from alternative (legacy) option.
  549.                 $settings get_option$this->alt_option_name, array() );
  550.                 // Delete the alternative (legacy) option as the new option will be created using `$this->option_name`.
  551.                 delete_option$this->alt_option_name );
  552.             }
  553.             // Save an option so it can be autoloaded next time.
  554.             $this->save_settings$settings );
  555.         }
  556.         if ( ! is_array$settings ) && ! ( $settings instanceof ArrayObject || $settings instanceof ArrayIterator ) ) {
  557.             $settings = array();
  558.         }
  559.         if ( ! empty( $settings ) && ! isset( $settings['_multiwidget'] ) ) {
  560.             // Old format, convert if single widget.
  561.             $settings wp_convert_widget_settings$this->id_base$this->option_name$settings );
  562.         }
  563.         unset( $settings['_multiwidget'], $settings['__i__'] );
  564.         return $settings;
  565.     }
  566. }