[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/customize/ -> class-wp-customize-custom-css-setting.php (source)

   1  <?php
   2  /**
   3   * Customize API: WP_Customize_Custom_CSS_Setting class
   4   *
   5   * This handles validation, sanitization and saving of the value.
   6   *
   7   * @package WordPress
   8   * @subpackage Customize
   9   * @since 4.7.0
  10   */
  11  
  12  /**
  13   * Custom Setting to handle WP Custom CSS.
  14   *
  15   * @since 4.7.0
  16   *
  17   * @see WP_Customize_Setting
  18   */
  19  final class WP_Customize_Custom_CSS_Setting extends WP_Customize_Setting {
  20  
  21      /**
  22       * The setting type.
  23       *
  24       * @since 4.7.0
  25       * @var string
  26       */
  27      public $type = 'custom_css';
  28  
  29      /**
  30       * Setting Transport
  31       *
  32       * @since 4.7.0
  33       * @var string
  34       */
  35      public $transport = 'postMessage';
  36  
  37      /**
  38       * Capability required to edit this setting.
  39       *
  40       * @since 4.7.0
  41       * @var string
  42       */
  43      public $capability = 'edit_css';
  44  
  45      /**
  46       * Stylesheet
  47       *
  48       * @since 4.7.0
  49       * @var string
  50       */
  51      public $stylesheet = '';
  52  
  53      /**
  54       * WP_Customize_Custom_CSS_Setting constructor.
  55       *
  56       * @since 4.7.0
  57       *
  58       * @throws Exception If the setting ID does not match the pattern `custom_css[$stylesheet]`.
  59       *
  60       * @param WP_Customize_Manager $manager Customizer bootstrap instance.
  61       * @param string               $id      A specific ID of the setting.
  62       *                                      Can be a theme mod or option name.
  63       * @param array                $args    Setting arguments.
  64       */
  65  	public function __construct( $manager, $id, $args = array() ) {
  66          parent::__construct( $manager, $id, $args );
  67          if ( 'custom_css' !== $this->id_data['base'] ) {
  68              throw new Exception( 'Expected custom_css id_base.' );
  69          }
  70          if ( 1 !== count( $this->id_data['keys'] ) || empty( $this->id_data['keys'][0] ) ) {
  71              throw new Exception( 'Expected single stylesheet key.' );
  72          }
  73          $this->stylesheet = $this->id_data['keys'][0];
  74      }
  75  
  76      /**
  77       * Add filter to preview post value.
  78       *
  79       * @since 4.7.9
  80       *
  81       * @return bool False when preview short-circuits due no change needing to be previewed.
  82       */
  83  	public function preview() {
  84          if ( $this->is_previewed ) {
  85              return false;
  86          }
  87          $this->is_previewed = true;
  88          add_filter( 'wp_get_custom_css', array( $this, 'filter_previewed_wp_get_custom_css' ), 9, 2 );
  89          return true;
  90      }
  91  
  92      /**
  93       * Filters `wp_get_custom_css` for applying the customized value.
  94       *
  95       * This is used in the preview when `wp_get_custom_css()` is called for rendering the styles.
  96       *
  97       * @since 4.7.0
  98       *
  99       * @see wp_get_custom_css()
 100       *
 101       * @param string $css        Original CSS.
 102       * @param string $stylesheet Current stylesheet.
 103       * @return string CSS.
 104       */
 105  	public function filter_previewed_wp_get_custom_css( $css, $stylesheet ) {
 106          if ( $stylesheet === $this->stylesheet ) {
 107              $customized_value = $this->post_value( null );
 108              if ( ! is_null( $customized_value ) ) {
 109                  $css = $customized_value;
 110              }
 111          }
 112          return $css;
 113      }
 114  
 115      /**
 116       * Fetch the value of the setting. Will return the previewed value when `preview()` is called.
 117       *
 118       * @since 4.7.0
 119       *
 120       * @see WP_Customize_Setting::value()
 121       *
 122       * @return string
 123       */
 124  	public function value() {
 125          if ( $this->is_previewed ) {
 126              $post_value = $this->post_value( null );
 127              if ( null !== $post_value ) {
 128                  return $post_value;
 129              }
 130          }
 131          $id_base = $this->id_data['base'];
 132          $value   = '';
 133          $post    = wp_get_custom_css_post( $this->stylesheet );
 134          if ( $post ) {
 135              $value = $post->post_content;
 136          }
 137          if ( empty( $value ) ) {
 138              $value = $this->default;
 139          }
 140  
 141          /** This filter is documented in wp-includes/class-wp-customize-setting.php */
 142          $value = apply_filters( "customize_value_{$id_base}", $value, $this );
 143  
 144          return $value;
 145      }
 146  
 147      /**
 148       * Validate a received value for being valid CSS.
 149       *
 150       * Checks for imbalanced braces, brackets, and comments.
 151       * Notifications are rendered when the customizer state is saved.
 152       *
 153       * @since 4.7.0
 154       * @since 4.9.0 Checking for balanced characters has been moved client-side via linting in code editor.
 155       * @since 5.9.0 Renamed `$css` to `$value` for PHP 8 named parameter support.
 156       * @since 7.0.0 Only restricts contents which risk prematurely closing the STYLE element,
 157       *              either through a STYLE end tag or a prefix of one which might become a
 158       *              full end tag when combined with the contents of other styles.
 159       *
 160       * @see WP_REST_Global_Styles_Controller::validate_custom_css()
 161       *
 162       * @param string $value CSS to validate.
 163       * @return true|WP_Error True if the input was validated, otherwise WP_Error.
 164       */
 165  	public function validate( $value ) {
 166          // Restores the more descriptive, specific name for use within this method.
 167          $css = $value;
 168  
 169          $validity = new WP_Error();
 170  
 171          $length = strlen( $css );
 172          for (
 173              $at = strcspn( $css, '<' );
 174              $at < $length;
 175              $at += strcspn( $css, '<', ++$at )
 176          ) {
 177              $remaining_strlen = $length - $at;
 178              /**
 179               * Custom CSS text is expected to render inside an HTML STYLE element.
 180               * A STYLE closing tag must not appear within the CSS text because it
 181               * would close the element prematurely.
 182               *
 183               * The text must also *not* end with a partial closing tag (e.g., `<`,
 184               * `</`, … `</style`) because subsequent styles which are concatenated
 185               * could complete it, forming a valid `</style>` tag.
 186               *
 187               * Example:
 188               *
 189               *     $style_a = 'p { font-weight: bold; </sty';
 190               *     $style_b = 'le> gotcha!';
 191               *     $combined = "{$style_a}{$style_b}";
 192               *
 193               *     $style_a = 'p { font-weight: bold; </style';
 194               *     $style_b = 'p > b { color: red; }';
 195               *     $combined = "{$style_a}\n{$style_b}";
 196               *
 197               * Note how in the second example, both of the style contents are benign
 198               * when analyzed on their own. The first style was likely the result of
 199               * improper truncation, while the second is perfectly sound. It was only
 200               * through concatenation that these two styles combined to form content
 201               * that would have broken out of the containing STYLE element, thus
 202               * corrupting the page and potentially introducing security issues.
 203               *
 204               * @see https://html.spec.whatwg.org/multipage/parsing.html#rawtext-end-tag-name-state
 205               */
 206              $possible_style_close_tag = 0 === substr_compare(
 207                  $css,
 208                  '</style',
 209                  $at,
 210                  min( 7, $remaining_strlen ),
 211                  true
 212              );
 213              if ( $possible_style_close_tag ) {
 214                  if ( $remaining_strlen < 8 ) {
 215                      $validity->add(
 216                          'illegal_markup',
 217                          sprintf(
 218                              /* translators: %s is the CSS that was provided. */
 219                              __( 'The CSS must not end in "%s".' ),
 220                              esc_html( substr( $css, $at ) )
 221                          )
 222                      );
 223                      break;
 224                  }
 225  
 226                  if ( 1 === strspn( $css, " \t\f\r\n/>", $at + 7, 1 ) ) {
 227                      $validity->add(
 228                          'illegal_markup',
 229                          sprintf(
 230                              /* translators: %s is the CSS that was provided. */
 231                              __( 'The CSS must not contain "%s".' ),
 232                              esc_html( substr( $css, $at, 8 ) )
 233                          )
 234                      );
 235                      break;
 236                  }
 237              }
 238          }
 239  
 240          if ( ! $validity->has_errors() ) {
 241              $validity = parent::validate( $css );
 242          }
 243          return $validity;
 244      }
 245  
 246      /**
 247       * Store the CSS setting value in the custom_css custom post type for the stylesheet.
 248       *
 249       * @since 4.7.0
 250       * @since 5.9.0 Renamed `$css` to `$value` for PHP 8 named parameter support.
 251       *
 252       * @param string $value CSS to update.
 253       * @return int|false The post ID or false if the value could not be saved.
 254       */
 255  	public function update( $value ) {
 256          // Restores the more descriptive, specific name for use within this method.
 257          $css = $value;
 258  
 259          if ( empty( $css ) ) {
 260              $css = '';
 261          }
 262  
 263          $r = wp_update_custom_css_post(
 264              $css,
 265              array(
 266                  'stylesheet' => $this->stylesheet,
 267              )
 268          );
 269  
 270          if ( is_wp_error( $r ) ) {
 271              return false;
 272          }
 273  
 274          $post_id = $r->ID;
 275  
 276          // Cache post ID in theme mod for performance to avoid additional DB query.
 277          if ( $this->manager->get_stylesheet() === $this->stylesheet ) {
 278              set_theme_mod( 'custom_css_post_id', $post_id );
 279          }
 280  
 281          return $post_id;
 282      }
 283  }


Generated : Wed Jul 1 08:20:12 2026 Cross-referenced by PHPXref