[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> kses.php (source)

   1  <?php
   2  /**
   3   * kses 0.2.2 - HTML/XHTML filter that only allows some elements and attributes
   4   * Copyright (C) 2002, 2003, 2005  Ulf Harnhammar
   5   *
   6   * This program is free software and open source software; you can redistribute
   7   * it and/or modify it under the terms of the GNU General Public License as
   8   * published by the Free Software Foundation; either version 2 of the License,
   9   * or (at your option) any later version.
  10   *
  11   * This program is distributed in the hope that it will be useful, but WITHOUT
  12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13   * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  14   * more details.
  15   *
  16   * You should have received a copy of the GNU General Public License along
  17   * with this program; if not, write to the Free Software Foundation, Inc.,
  18   * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19   * http://www.gnu.org/licenses/gpl.html
  20   *
  21   * [kses strips evil scripts!]
  22   *
  23   * Added wp_ prefix to avoid conflicts with existing kses users
  24   *
  25   * @version 0.2.2
  26   * @copyright (C) 2002, 2003, 2005
  27   * @author Ulf Harnhammar <http://advogato.org/person/metaur/>
  28   *
  29   * @package External
  30   * @subpackage KSES
  31   */
  32  
  33  /**
  34   * Specifies the default allowable HTML tags.
  35   *
  36   * Using `CUSTOM_TAGS` is not recommended and should be considered deprecated. The
  37   * {@see 'wp_kses_allowed_html'} filter is more powerful and supplies context.
  38   *
  39   * @see wp_kses_allowed_html()
  40   * @since 1.2.0
  41   *
  42   * @var array[]|bool Array of default allowable HTML tags, or false to use the defaults.
  43   */
  44  if ( ! defined( 'CUSTOM_TAGS' ) ) {
  45      define( 'CUSTOM_TAGS', false );
  46  }
  47  
  48  // Ensure that these variables are added to the global namespace
  49  // (e.g. if using namespaces / autoload in the current PHP environment).
  50  global $allowedposttags, $allowedtags, $allowedentitynames;
  51  
  52  if ( ! CUSTOM_TAGS ) {
  53      /**
  54       * KSES global for default allowable HTML tags.
  55       *
  56       * Can be overridden with the `CUSTOM_TAGS` constant.
  57       *
  58       * @var array[] $allowedposttags Array of default allowable HTML tags.
  59       * @since 2.0.0
  60       */
  61      $allowedposttags = array(
  62          'address'    => array(),
  63          'a'          => array(
  64              'href'     => true,
  65              'rel'      => true,
  66              'rev'      => true,
  67              'name'     => true,
  68              'target'   => true,
  69              'download' => array(
  70                  'valueless' => 'y',
  71              ),
  72          ),
  73          'abbr'       => array(),
  74          'acronym'    => array(),
  75          'area'       => array(
  76              'alt'    => true,
  77              'coords' => true,
  78              'href'   => true,
  79              'nohref' => true,
  80              'shape'  => true,
  81              'target' => true,
  82          ),
  83          'article'    => array(
  84              'align'    => true,
  85              'dir'      => true,
  86              'lang'     => true,
  87              'xml:lang' => true,
  88          ),
  89          'aside'      => array(
  90              'align'    => true,
  91              'dir'      => true,
  92              'lang'     => true,
  93              'xml:lang' => true,
  94          ),
  95          'audio'      => array(
  96              'autoplay' => true,
  97              'controls' => true,
  98              'loop'     => true,
  99              'muted'    => true,
 100              'preload'  => true,
 101              'src'      => true,
 102          ),
 103          'b'          => array(),
 104          'bdo'        => array(
 105              'dir' => true,
 106          ),
 107          'big'        => array(),
 108          'blockquote' => array(
 109              'cite'     => true,
 110              'lang'     => true,
 111              'xml:lang' => true,
 112          ),
 113          'br'         => array(),
 114          'button'     => array(
 115              'disabled' => true,
 116              'name'     => true,
 117              'type'     => true,
 118              'value'    => true,
 119          ),
 120          'caption'    => array(
 121              'align' => true,
 122          ),
 123          'cite'       => array(
 124              'dir'  => true,
 125              'lang' => true,
 126          ),
 127          'code'       => array(),
 128          'col'        => array(
 129              'align'   => true,
 130              'char'    => true,
 131              'charoff' => true,
 132              'span'    => true,
 133              'dir'     => true,
 134              'valign'  => true,
 135              'width'   => true,
 136          ),
 137          'colgroup'   => array(
 138              'align'   => true,
 139              'char'    => true,
 140              'charoff' => true,
 141              'span'    => true,
 142              'valign'  => true,
 143              'width'   => true,
 144          ),
 145          'del'        => array(
 146              'datetime' => true,
 147          ),
 148          'dd'         => array(),
 149          'dfn'        => array(),
 150          'details'    => array(
 151              'align'    => true,
 152              'dir'      => true,
 153              'lang'     => true,
 154              'open'     => true,
 155              'xml:lang' => true,
 156          ),
 157          'div'        => array(
 158              'align'    => true,
 159              'dir'      => true,
 160              'lang'     => true,
 161              'xml:lang' => true,
 162          ),
 163          'dl'         => array(),
 164          'dt'         => array(),
 165          'em'         => array(),
 166          'fieldset'   => array(),
 167          'figure'     => array(
 168              'align'    => true,
 169              'dir'      => true,
 170              'lang'     => true,
 171              'xml:lang' => true,
 172          ),
 173          'figcaption' => array(
 174              'align'    => true,
 175              'dir'      => true,
 176              'lang'     => true,
 177              'xml:lang' => true,
 178          ),
 179          'font'       => array(
 180              'color' => true,
 181              'face'  => true,
 182              'size'  => true,
 183          ),
 184          'footer'     => array(
 185              'align'    => true,
 186              'dir'      => true,
 187              'lang'     => true,
 188              'xml:lang' => true,
 189          ),
 190          'h1'         => array(
 191              'align' => true,
 192          ),
 193          'h2'         => array(
 194              'align' => true,
 195          ),
 196          'h3'         => array(
 197              'align' => true,
 198          ),
 199          'h4'         => array(
 200              'align' => true,
 201          ),
 202          'h5'         => array(
 203              'align' => true,
 204          ),
 205          'h6'         => array(
 206              'align' => true,
 207          ),
 208          'header'     => array(
 209              'align'    => true,
 210              'dir'      => true,
 211              'lang'     => true,
 212              'xml:lang' => true,
 213          ),
 214          'hgroup'     => array(
 215              'align'    => true,
 216              'dir'      => true,
 217              'lang'     => true,
 218              'xml:lang' => true,
 219          ),
 220          'hr'         => array(
 221              'align'   => true,
 222              'noshade' => true,
 223              'size'    => true,
 224              'width'   => true,
 225          ),
 226          'i'          => array(),
 227          'img'        => array(
 228              'alt'      => true,
 229              'align'    => true,
 230              'border'   => true,
 231              'height'   => true,
 232              'hspace'   => true,
 233              'longdesc' => true,
 234              'vspace'   => true,
 235              'src'      => true,
 236              'usemap'   => true,
 237              'width'    => true,
 238          ),
 239          'ins'        => array(
 240              'datetime' => true,
 241              'cite'     => true,
 242          ),
 243          'kbd'        => array(),
 244          'label'      => array(
 245              'for' => true,
 246          ),
 247          'legend'     => array(
 248              'align' => true,
 249          ),
 250          'li'         => array(
 251              'align' => true,
 252              'value' => true,
 253          ),
 254          'map'        => array(
 255              'name' => true,
 256          ),
 257          'mark'       => array(),
 258          'menu'       => array(
 259              'type' => true,
 260          ),
 261          'nav'        => array(
 262              'align'    => true,
 263              'dir'      => true,
 264              'lang'     => true,
 265              'xml:lang' => true,
 266          ),
 267          'p'          => array(
 268              'align'    => true,
 269              'dir'      => true,
 270              'lang'     => true,
 271              'xml:lang' => true,
 272          ),
 273          'pre'        => array(
 274              'width' => true,
 275          ),
 276          'q'          => array(
 277              'cite' => true,
 278          ),
 279          's'          => array(),
 280          'samp'       => array(),
 281          'span'       => array(
 282              'dir'      => true,
 283              'align'    => true,
 284              'lang'     => true,
 285              'xml:lang' => true,
 286          ),
 287          'section'    => array(
 288              'align'    => true,
 289              'dir'      => true,
 290              'lang'     => true,
 291              'xml:lang' => true,
 292          ),
 293          'small'      => array(),
 294          'strike'     => array(),
 295          'strong'     => array(),
 296          'sub'        => array(),
 297          'summary'    => array(
 298              'align'    => true,
 299              'dir'      => true,
 300              'lang'     => true,
 301              'xml:lang' => true,
 302          ),
 303          'sup'        => array(),
 304          'table'      => array(
 305              'align'       => true,
 306              'bgcolor'     => true,
 307              'border'      => true,
 308              'cellpadding' => true,
 309              'cellspacing' => true,
 310              'dir'         => true,
 311              'rules'       => true,
 312              'summary'     => true,
 313              'width'       => true,
 314          ),
 315          'tbody'      => array(
 316              'align'   => true,
 317              'char'    => true,
 318              'charoff' => true,
 319              'valign'  => true,
 320          ),
 321          'td'         => array(
 322              'abbr'    => true,
 323              'align'   => true,
 324              'axis'    => true,
 325              'bgcolor' => true,
 326              'char'    => true,
 327              'charoff' => true,
 328              'colspan' => true,
 329              'dir'     => true,
 330              'headers' => true,
 331              'height'  => true,
 332              'nowrap'  => true,
 333              'rowspan' => true,
 334              'scope'   => true,
 335              'valign'  => true,
 336              'width'   => true,
 337          ),
 338          'textarea'   => array(
 339              'cols'     => true,
 340              'rows'     => true,
 341              'disabled' => true,
 342              'name'     => true,
 343              'readonly' => true,
 344          ),
 345          'tfoot'      => array(
 346              'align'   => true,
 347              'char'    => true,
 348              'charoff' => true,
 349              'valign'  => true,
 350          ),
 351          'th'         => array(
 352              'abbr'    => true,
 353              'align'   => true,
 354              'axis'    => true,
 355              'bgcolor' => true,
 356              'char'    => true,
 357              'charoff' => true,
 358              'colspan' => true,
 359              'headers' => true,
 360              'height'  => true,
 361              'nowrap'  => true,
 362              'rowspan' => true,
 363              'scope'   => true,
 364              'valign'  => true,
 365              'width'   => true,
 366          ),
 367          'thead'      => array(
 368              'align'   => true,
 369              'char'    => true,
 370              'charoff' => true,
 371              'valign'  => true,
 372          ),
 373          'title'      => array(),
 374          'tr'         => array(
 375              'align'   => true,
 376              'bgcolor' => true,
 377              'char'    => true,
 378              'charoff' => true,
 379              'valign'  => true,
 380          ),
 381          'track'      => array(
 382              'default' => true,
 383              'kind'    => true,
 384              'label'   => true,
 385              'src'     => true,
 386              'srclang' => true,
 387          ),
 388          'tt'         => array(),
 389          'u'          => array(),
 390          'ul'         => array(
 391              'type' => true,
 392          ),
 393          'ol'         => array(
 394              'start'    => true,
 395              'type'     => true,
 396              'reversed' => true,
 397          ),
 398          'var'        => array(),
 399          'video'      => array(
 400              'autoplay' => true,
 401              'controls' => true,
 402              'height'   => true,
 403              'loop'     => true,
 404              'muted'    => true,
 405              'poster'   => true,
 406              'preload'  => true,
 407              'src'      => true,
 408              'width'    => true,
 409          ),
 410      );
 411  
 412      /**
 413       * @var array[] $allowedtags Array of KSES allowed HTML elements.
 414       * @since 1.0.0
 415       */
 416      $allowedtags = array(
 417          'a'          => array(
 418              'href'  => true,
 419              'title' => true,
 420          ),
 421          'abbr'       => array(
 422              'title' => true,
 423          ),
 424          'acronym'    => array(
 425              'title' => true,
 426          ),
 427          'b'          => array(),
 428          'blockquote' => array(
 429              'cite' => true,
 430          ),
 431          'cite'       => array(),
 432          'code'       => array(),
 433          'del'        => array(
 434              'datetime' => true,
 435          ),
 436          'em'         => array(),
 437          'i'          => array(),
 438          'q'          => array(
 439              'cite' => true,
 440          ),
 441          's'          => array(),
 442          'strike'     => array(),
 443          'strong'     => array(),
 444      );
 445  
 446      /**
 447       * @var string[] $allowedentitynames Array of KSES allowed HTML entitity names.
 448       * @since 1.0.0
 449       */
 450      $allowedentitynames = array(
 451          'nbsp',
 452          'iexcl',
 453          'cent',
 454          'pound',
 455          'curren',
 456          'yen',
 457          'brvbar',
 458          'sect',
 459          'uml',
 460          'copy',
 461          'ordf',
 462          'laquo',
 463          'not',
 464          'shy',
 465          'reg',
 466          'macr',
 467          'deg',
 468          'plusmn',
 469          'acute',
 470          'micro',
 471          'para',
 472          'middot',
 473          'cedil',
 474          'ordm',
 475          'raquo',
 476          'iquest',
 477          'Agrave',
 478          'Aacute',
 479          'Acirc',
 480          'Atilde',
 481          'Auml',
 482          'Aring',
 483          'AElig',
 484          'Ccedil',
 485          'Egrave',
 486          'Eacute',
 487          'Ecirc',
 488          'Euml',
 489          'Igrave',
 490          'Iacute',
 491          'Icirc',
 492          'Iuml',
 493          'ETH',
 494          'Ntilde',
 495          'Ograve',
 496          'Oacute',
 497          'Ocirc',
 498          'Otilde',
 499          'Ouml',
 500          'times',
 501          'Oslash',
 502          'Ugrave',
 503          'Uacute',
 504          'Ucirc',
 505          'Uuml',
 506          'Yacute',
 507          'THORN',
 508          'szlig',
 509          'agrave',
 510          'aacute',
 511          'acirc',
 512          'atilde',
 513          'auml',
 514          'aring',
 515          'aelig',
 516          'ccedil',
 517          'egrave',
 518          'eacute',
 519          'ecirc',
 520          'euml',
 521          'igrave',
 522          'iacute',
 523          'icirc',
 524          'iuml',
 525          'eth',
 526          'ntilde',
 527          'ograve',
 528          'oacute',
 529          'ocirc',
 530          'otilde',
 531          'ouml',
 532          'divide',
 533          'oslash',
 534          'ugrave',
 535          'uacute',
 536          'ucirc',
 537          'uuml',
 538          'yacute',
 539          'thorn',
 540          'yuml',
 541          'quot',
 542          'amp',
 543          'lt',
 544          'gt',
 545          'apos',
 546          'OElig',
 547          'oelig',
 548          'Scaron',
 549          'scaron',
 550          'Yuml',
 551          'circ',
 552          'tilde',
 553          'ensp',
 554          'emsp',
 555          'thinsp',
 556          'zwnj',
 557          'zwj',
 558          'lrm',
 559          'rlm',
 560          'ndash',
 561          'mdash',
 562          'lsquo',
 563          'rsquo',
 564          'sbquo',
 565          'ldquo',
 566          'rdquo',
 567          'bdquo',
 568          'dagger',
 569          'Dagger',
 570          'permil',
 571          'lsaquo',
 572          'rsaquo',
 573          'euro',
 574          'fnof',
 575          'Alpha',
 576          'Beta',
 577          'Gamma',
 578          'Delta',
 579          'Epsilon',
 580          'Zeta',
 581          'Eta',
 582          'Theta',
 583          'Iota',
 584          'Kappa',
 585          'Lambda',
 586          'Mu',
 587          'Nu',
 588          'Xi',
 589          'Omicron',
 590          'Pi',
 591          'Rho',
 592          'Sigma',
 593          'Tau',
 594          'Upsilon',
 595          'Phi',
 596          'Chi',
 597          'Psi',
 598          'Omega',
 599          'alpha',
 600          'beta',
 601          'gamma',
 602          'delta',
 603          'epsilon',
 604          'zeta',
 605          'eta',
 606          'theta',
 607          'iota',
 608          'kappa',
 609          'lambda',
 610          'mu',
 611          'nu',
 612          'xi',
 613          'omicron',
 614          'pi',
 615          'rho',
 616          'sigmaf',
 617          'sigma',
 618          'tau',
 619          'upsilon',
 620          'phi',
 621          'chi',
 622          'psi',
 623          'omega',
 624          'thetasym',
 625          'upsih',
 626          'piv',
 627          'bull',
 628          'hellip',
 629          'prime',
 630          'Prime',
 631          'oline',
 632          'frasl',
 633          'weierp',
 634          'image',
 635          'real',
 636          'trade',
 637          'alefsym',
 638          'larr',
 639          'uarr',
 640          'rarr',
 641          'darr',
 642          'harr',
 643          'crarr',
 644          'lArr',
 645          'uArr',
 646          'rArr',
 647          'dArr',
 648          'hArr',
 649          'forall',
 650          'part',
 651          'exist',
 652          'empty',
 653          'nabla',
 654          'isin',
 655          'notin',
 656          'ni',
 657          'prod',
 658          'sum',
 659          'minus',
 660          'lowast',
 661          'radic',
 662          'prop',
 663          'infin',
 664          'ang',
 665          'and',
 666          'or',
 667          'cap',
 668          'cup',
 669          'int',
 670          'sim',
 671          'cong',
 672          'asymp',
 673          'ne',
 674          'equiv',
 675          'le',
 676          'ge',
 677          'sub',
 678          'sup',
 679          'nsub',
 680          'sube',
 681          'supe',
 682          'oplus',
 683          'otimes',
 684          'perp',
 685          'sdot',
 686          'lceil',
 687          'rceil',
 688          'lfloor',
 689          'rfloor',
 690          'lang',
 691          'rang',
 692          'loz',
 693          'spades',
 694          'clubs',
 695          'hearts',
 696          'diams',
 697          'sup1',
 698          'sup2',
 699          'sup3',
 700          'frac14',
 701          'frac12',
 702          'frac34',
 703          'there4',
 704      );
 705  
 706      $allowedposttags = array_map( '_wp_add_global_attributes', $allowedposttags );
 707  } else {
 708      $allowedtags     = wp_kses_array_lc( $allowedtags );
 709      $allowedposttags = wp_kses_array_lc( $allowedposttags );
 710  }
 711  
 712  /**
 713   * Filters text content and strips out disallowed HTML.
 714   *
 715   * This function makes sure that only the allowed HTML element names, attribute
 716   * names, attribute values, and HTML entities will occur in the given text string.
 717   *
 718   * This function expects unslashed data.
 719   *
 720   * @see wp_kses_post() for specifically filtering post content and fields.
 721   * @see wp_allowed_protocols() for the default allowed protocols in link URLs.
 722   *
 723   * @since 1.0.0
 724   *
 725   * @param string         $string            Text content to filter.
 726   * @param array[]|string $allowed_html      An array of allowed HTML elements and attributes, or a
 727   *                                          context name such as 'post'.
 728   * @param string[]       $allowed_protocols Array of allowed URL protocols.
 729   * @return string Filtered content containing only the allowed HTML.
 730   */
 731  function wp_kses( $string, $allowed_html, $allowed_protocols = array() ) {
 732      if ( empty( $allowed_protocols ) ) {
 733          $allowed_protocols = wp_allowed_protocols();
 734      }
 735      $string = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
 736      $string = wp_kses_normalize_entities( $string );
 737      $string = wp_kses_hook( $string, $allowed_html, $allowed_protocols );
 738      return wp_kses_split( $string, $allowed_html, $allowed_protocols );
 739  }
 740  
 741  /**
 742   * Filters one HTML attribute and ensures its value is allowed.
 743   *
 744   * This function can escape data in some situations where `wp_kses()` must strip the whole attribute.
 745   *
 746   * @since 4.2.3
 747   *
 748   * @param string $string  The 'whole' attribute, including name and value.
 749   * @param string $element The HTML element name to which the attribute belongs.
 750   * @return string Filtered attribute.
 751   */
 752  function wp_kses_one_attr( $string, $element ) {
 753      $uris              = wp_kses_uri_attributes();
 754      $allowed_html      = wp_kses_allowed_html( 'post' );
 755      $allowed_protocols = wp_allowed_protocols();
 756      $string            = wp_kses_no_null( $string, array( 'slash_zero' => 'keep' ) );
 757  
 758      // Preserve leading and trailing whitespace.
 759      $matches = array();
 760      preg_match( '/^\s*/', $string, $matches );
 761      $lead = $matches[0];
 762      preg_match( '/\s*$/', $string, $matches );
 763      $trail = $matches[0];
 764      if ( empty( $trail ) ) {
 765          $string = substr( $string, strlen( $lead ) );
 766      } else {
 767          $string = substr( $string, strlen( $lead ), -strlen( $trail ) );
 768      }
 769  
 770      // Parse attribute name and value from input.
 771      $split = preg_split( '/\s*=\s*/', $string, 2 );
 772      $name  = $split[0];
 773      if ( count( $split ) == 2 ) {
 774          $value = $split[1];
 775  
 776          // Remove quotes surrounding $value.
 777          // Also guarantee correct quoting in $string for this one attribute.
 778          if ( '' == $value ) {
 779              $quote = '';
 780          } else {
 781              $quote = $value[0];
 782          }
 783          if ( '"' == $quote || "'" == $quote ) {
 784              if ( substr( $value, -1 ) != $quote ) {
 785                  return '';
 786              }
 787              $value = substr( $value, 1, -1 );
 788          } else {
 789              $quote = '"';
 790          }
 791  
 792          // Sanitize quotes, angle braces, and entities.
 793          $value = esc_attr( $value );
 794  
 795          // Sanitize URI values.
 796          if ( in_array( strtolower( $name ), $uris, true ) ) {
 797              $value = wp_kses_bad_protocol( $value, $allowed_protocols );
 798          }
 799  
 800          $string = "$name=$quote$value$quote";
 801          $vless  = 'n';
 802      } else {
 803          $value = '';
 804          $vless = 'y';
 805      }
 806  
 807      // Sanitize attribute by name.
 808      wp_kses_attr_check( $name, $value, $string, $vless, $element, $allowed_html );
 809  
 810      // Restore whitespace.
 811      return $lead . $string . $trail;
 812  }
 813  
 814  /**
 815   * Returns an array of allowed HTML tags and attributes for a given context.
 816   *
 817   * @since 3.5.0
 818   * @since 5.0.1 `form` removed as allowable HTML tag.
 819   *
 820   * @global array $allowedposttags
 821   * @global array $allowedtags
 822   * @global array $allowedentitynames
 823   *
 824   * @param string|array $context The context for which to retrieve tags. Allowed values are 'post',
 825   *                              'strip', 'data', 'entities', or the name of a field filter such as
 826   *                              'pre_user_description'.
 827   * @return array Array of allowed HTML tags and their allowed attributes.
 828   */
 829  function wp_kses_allowed_html( $context = '' ) {
 830      global $allowedposttags, $allowedtags, $allowedentitynames;
 831  
 832      if ( is_array( $context ) ) {
 833          /**
 834           * Filters the HTML that is allowed for a given context.
 835           *
 836           * @since 3.5.0
 837           *
 838           * @param array[]|string $context      Context to judge allowed tags by.
 839           * @param string         $context_type Context name.
 840           */
 841          return apply_filters( 'wp_kses_allowed_html', $context, 'explicit' );
 842      }
 843  
 844      switch ( $context ) {
 845          case 'post':
 846              /** This filter is documented in wp-includes/kses.php */
 847              $tags = apply_filters( 'wp_kses_allowed_html', $allowedposttags, $context );
 848  
 849              // 5.0.1 removed the `<form>` tag, allow it if a filter is allowing it's sub-elements `<input>` or `<select>`.
 850              if ( ! CUSTOM_TAGS && ! isset( $tags['form'] ) && ( isset( $tags['input'] ) || isset( $tags['select'] ) ) ) {
 851                  $tags = $allowedposttags;
 852  
 853                  $tags['form'] = array(
 854                      'action'         => true,
 855                      'accept'         => true,
 856                      'accept-charset' => true,
 857                      'enctype'        => true,
 858                      'method'         => true,
 859                      'name'           => true,
 860                      'target'         => true,
 861                  );
 862  
 863                  /** This filter is documented in wp-includes/kses.php */
 864                  $tags = apply_filters( 'wp_kses_allowed_html', $tags, $context );
 865              }
 866  
 867              return $tags;
 868  
 869          case 'user_description':
 870          case 'pre_user_description':
 871              $tags             = $allowedtags;
 872              $tags['a']['rel'] = true;
 873              /** This filter is documented in wp-includes/kses.php */
 874              return apply_filters( 'wp_kses_allowed_html', $tags, $context );
 875  
 876          case 'strip':
 877              /** This filter is documented in wp-includes/kses.php */
 878              return apply_filters( 'wp_kses_allowed_html', array(), $context );
 879  
 880          case 'entities':
 881              /** This filter is documented in wp-includes/kses.php */
 882              return apply_filters( 'wp_kses_allowed_html', $allowedentitynames, $context );
 883  
 884          case 'data':
 885          default:
 886              /** This filter is documented in wp-includes/kses.php */
 887              return apply_filters( 'wp_kses_allowed_html', $allowedtags, $context );
 888      }
 889  }
 890  
 891  /**
 892   * You add any KSES hooks here.
 893   *
 894   * There is currently only one KSES WordPress hook, {@see 'pre_kses'}, and it is called here.
 895   * All parameters are passed to the hooks and expected to receive a string.
 896   *
 897   * @since 1.0.0
 898   *
 899   * @param string          $string            Content to filter through KSES.
 900   * @param array[]|string  $allowed_html      List of allowed HTML elements.
 901   * @param string[]        $allowed_protocols Array of allowed URL protocols.
 902   * @return string Filtered content through {@see 'pre_kses'} hook.
 903   */
 904  function wp_kses_hook( $string, $allowed_html, $allowed_protocols ) {
 905      /**
 906       * Filters content to be run through kses.
 907       *
 908       * @since 2.3.0
 909       *
 910       * @param string          $string            Content to run through KSES.
 911       * @param array[]|string  $allowed_html      Allowed HTML elements.
 912       * @param string[]        $allowed_protocols Array of allowed URL protocols.
 913       */
 914      return apply_filters( 'pre_kses', $string, $allowed_html, $allowed_protocols );
 915  }
 916  
 917  /**
 918   * Returns the version number of KSES.
 919   *
 920   * @since 1.0.0
 921   *
 922   * @return string KSES version number.
 923   */
 924  function wp_kses_version() {
 925      return '0.2.2';
 926  }
 927  
 928  /**
 929   * Searches for HTML tags, no matter how malformed.
 930   *
 931   * It also matches stray `>` characters.
 932   *
 933   * @since 1.0.0
 934   *
 935   * @global array $pass_allowed_html
 936   * @global array $pass_allowed_protocols
 937   *
 938   * @param string   $string            Content to filter.
 939   * @param array    $allowed_html      Allowed HTML elements.
 940   * @param string[] $allowed_protocols Array of allowed URL protocols.
 941   * @return string Content with fixed HTML tags
 942   */
 943  function wp_kses_split( $string, $allowed_html, $allowed_protocols ) {
 944      global $pass_allowed_html, $pass_allowed_protocols;
 945      $pass_allowed_html      = $allowed_html;
 946      $pass_allowed_protocols = $allowed_protocols;
 947      return preg_replace_callback( '%(<!--.*?(-->|$))|(<[^>]*(>|$)|>)%', '_wp_kses_split_callback', $string );
 948  }
 949  
 950  /**
 951   * Returns an array of HTML attribute names whose value contains a URL.
 952   *
 953   * This function returns a list of all HTML attributes that must contain
 954   * a URL according to the HTML specification.
 955   *
 956   * This list includes URI attributes both allowed and disallowed by KSES.
 957   *
 958   * @link https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
 959   *
 960   * @since 5.0.1
 961   *
 962   * @return string[] HTML attribute names whose value contains a URL.
 963   */
 964  function wp_kses_uri_attributes() {
 965      $uri_attributes = array(
 966          'action',
 967          'archive',
 968          'background',
 969          'cite',
 970          'classid',
 971          'codebase',
 972          'data',
 973          'formaction',
 974          'href',
 975          'icon',
 976          'longdesc',
 977          'manifest',
 978          'poster',
 979          'profile',
 980          'src',
 981          'usemap',
 982          'xmlns',
 983      );
 984  
 985      /**
 986       * Filters the list of attributes that are required to contain a URL.
 987       *
 988       * Use this filter to add any `data-` attributes that are required to be
 989       * validated as a URL.
 990       *
 991       * @since 5.0.1
 992       *
 993       * @param string[] $uri_attributes HTML attribute names whose value contains a URL.
 994       */
 995      $uri_attributes = apply_filters( 'wp_kses_uri_attributes', $uri_attributes );
 996  
 997      return $uri_attributes;
 998  }
 999  
1000  /**
1001   * Callback for `wp_kses_split()`.
1002   *
1003   * @since 3.1.0
1004   * @access private
1005   * @ignore
1006   *
1007   * @global array $pass_allowed_html
1008   * @global array $pass_allowed_protocols
1009   *
1010   * @return string
1011   */
1012  function _wp_kses_split_callback( $match ) {
1013      global $pass_allowed_html, $pass_allowed_protocols;
1014      return wp_kses_split2( $match[0], $pass_allowed_html, $pass_allowed_protocols );
1015  }
1016  
1017  /**
1018   * Callback for `wp_kses_split()` for fixing malformed HTML tags.
1019   *
1020   * This function does a lot of work. It rejects some very malformed things like
1021   * `<:::>`. It returns an empty string, if the element isn't allowed (look ma, no
1022   * `strip_tags()`!). Otherwise it splits the tag into an element and an attribute
1023   * list.
1024   *
1025   * After the tag is split into an element and an attribute list, it is run
1026   * through another filter which will remove illegal attributes and once that is
1027   * completed, will be returned.
1028   *
1029   * @access private
1030   * @ignore
1031   * @since 1.0.0
1032   *
1033   * @param string   $string            Content to filter.
1034   * @param array    $allowed_html      Allowed HTML elements.
1035   * @param string[] $allowed_protocols Array of allowed URL protocols.
1036   * @return string Fixed HTML element
1037   */
1038  function wp_kses_split2( $string, $allowed_html, $allowed_protocols ) {
1039      $string = wp_kses_stripslashes( $string );
1040  
1041      // It matched a ">" character.
1042      if ( substr( $string, 0, 1 ) != '<' ) {
1043          return '&gt;';
1044      }
1045  
1046      // Allow HTML comments.
1047      if ( '<!--' == substr( $string, 0, 4 ) ) {
1048          $string = str_replace( array( '<!--', '-->' ), '', $string );
1049          while ( ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) != $string ) {
1050              $string = $newstring;
1051          }
1052          if ( '' == $string ) {
1053              return '';
1054          }
1055          // Prevent multiple dashes in comments.
1056          $string = preg_replace( '/--+/', '-', $string );
1057          // Prevent three dashes closing a comment.
1058          $string = preg_replace( '/-$/', '', $string );
1059          return "<!--{$string}-->";
1060      }
1061  
1062      // It's seriously malformed.
1063      if ( ! preg_match( '%^<\s*(/\s*)?([a-zA-Z0-9-]+)([^>]*)>?$%', $string, $matches ) ) {
1064          return '';
1065      }
1066  
1067      $slash    = trim( $matches[1] );
1068      $elem     = $matches[2];
1069      $attrlist = $matches[3];
1070  
1071      if ( ! is_array( $allowed_html ) ) {
1072          $allowed_html = wp_kses_allowed_html( $allowed_html );
1073      }
1074  
1075      // They are using a not allowed HTML element.
1076      if ( ! isset( $allowed_html[ strtolower( $elem ) ] ) ) {
1077          return '';
1078      }
1079  
1080      // No attributes are allowed for closing elements.
1081      if ( '' != $slash ) {
1082          return "</$elem>";
1083      }
1084  
1085      return wp_kses_attr( $elem, $attrlist, $allowed_html, $allowed_protocols );
1086  }
1087  
1088  /**
1089   * Removes all attributes, if none are allowed for this element.
1090   *
1091   * If some are allowed it calls `wp_kses_hair()` to split them further, and then
1092   * it builds up new HTML code from the data that `kses_hair()` returns. It also
1093   * removes `<` and `>` characters, if there are any left. One more thing it does
1094   * is to check if the tag has a closing XHTML slash, and if it does, it puts one
1095   * in the returned code as well.
1096   *
1097   * @since 1.0.0
1098   *
1099   * @param string   $element           HTML element/tag.
1100   * @param string   $attr              HTML attributes from HTML element to closing HTML element tag.
1101   * @param array    $allowed_html      Allowed HTML elements.
1102   * @param string[] $allowed_protocols Array of allowed URL protocols.
1103   * @return string Sanitized HTML element.
1104   */
1105  function wp_kses_attr( $element, $attr, $allowed_html, $allowed_protocols ) {
1106      if ( ! is_array( $allowed_html ) ) {
1107          $allowed_html = wp_kses_allowed_html( $allowed_html );
1108      }
1109  
1110      // Is there a closing XHTML slash at the end of the attributes?
1111      $xhtml_slash = '';
1112      if ( preg_match( '%\s*/\s*$%', $attr ) ) {
1113          $xhtml_slash = ' /';
1114      }
1115  
1116      // Are any attributes allowed at all for this element?
1117      $element_low = strtolower( $element );
1118      if ( empty( $allowed_html[ $element_low ] ) || true === $allowed_html[ $element_low ] ) {
1119          return "<$element$xhtml_slash>";
1120      }
1121  
1122      // Split it.
1123      $attrarr = wp_kses_hair( $attr, $allowed_protocols );
1124  
1125      // Go through $attrarr, and save the allowed attributes for this element
1126      // in $attr2.
1127      $attr2 = '';
1128      foreach ( $attrarr as $arreach ) {
1129          if ( wp_kses_attr_check( $arreach['name'], $arreach['value'], $arreach['whole'], $arreach['vless'], $element, $allowed_html ) ) {
1130              $attr2 .= ' ' . $arreach['whole'];
1131          }
1132      }
1133  
1134      // Remove any "<" or ">" characters.
1135      $attr2 = preg_replace( '/[<>]/', '', $attr2 );
1136  
1137      return "<$element$attr2$xhtml_slash>";
1138  }
1139  
1140  /**
1141   * Determines whether an attribute is allowed.
1142   *
1143   * @since 4.2.3
1144   * @since 5.0.0 Add support for `data-*` wildcard attributes.
1145   *
1146   * @param string $name         The attribute name. Passed by reference. Returns empty string when not allowed.
1147   * @param string $value        The attribute value. Passed by reference. Returns a filtered value.
1148   * @param string $whole        The `name=value` input. Passed by reference. Returns filtered input.
1149   * @param string $vless        Whether the attribute is valueless. Use 'y' or 'n'.
1150   * @param string $element      The name of the element to which this attribute belongs.
1151   * @param array  $allowed_html The full list of allowed elements and attributes.
1152   * @return bool Whether or not the attribute is allowed.
1153   */
1154  function wp_kses_attr_check( &$name, &$value, &$whole, $vless, $element, $allowed_html ) {
1155      $name_low    = strtolower( $name );
1156      $element_low = strtolower( $element );
1157  
1158      if ( ! isset( $allowed_html[ $element_low ] ) ) {
1159          $name  = '';
1160          $value = '';
1161          $whole = '';
1162          return false;
1163      }
1164  
1165      $allowed_attr = $allowed_html[ $element_low ];
1166  
1167      if ( ! isset( $allowed_attr[ $name_low ] ) || '' == $allowed_attr[ $name_low ] ) {
1168          /*
1169           * Allow `data-*` attributes.
1170           *
1171           * When specifying `$allowed_html`, the attribute name should be set as
1172           * `data-*` (not to be mixed with the HTML 4.0 `data` attribute, see
1173           * https://www.w3.org/TR/html40/struct/objects.html#adef-data).
1174           *
1175           * Note: the attribute name should only contain `A-Za-z0-9_-` chars,
1176           * double hyphens `--` are not accepted by WordPress.
1177           */
1178          if ( strpos( $name_low, 'data-' ) === 0 && ! empty( $allowed_attr['data-*'] ) && preg_match( '/^data(?:-[a-z0-9_]+)+$/', $name_low, $match ) ) {
1179              /*
1180               * Add the whole attribute name to the allowed attributes and set any restrictions
1181               * for the `data-*` attribute values for the current element.
1182               */
1183              $allowed_attr[ $match[0] ] = $allowed_attr['data-*'];
1184          } else {
1185              $name  = '';
1186              $value = '';
1187              $whole = '';
1188              return false;
1189          }
1190      }
1191  
1192      if ( 'style' == $name_low ) {
1193          $new_value = safecss_filter_attr( $value );
1194  
1195          if ( empty( $new_value ) ) {
1196              $name  = '';
1197              $value = '';
1198              $whole = '';
1199              return false;
1200          }
1201  
1202          $whole = str_replace( $value, $new_value, $whole );
1203          $value = $new_value;
1204      }
1205  
1206      if ( is_array( $allowed_attr[ $name_low ] ) ) {
1207          // There are some checks.
1208          foreach ( $allowed_attr[ $name_low ] as $currkey => $currval ) {
1209              if ( ! wp_kses_check_attr_val( $value, $vless, $currkey, $currval ) ) {
1210                  $name  = '';
1211                  $value = '';
1212                  $whole = '';
1213                  return false;
1214              }
1215          }
1216      }
1217  
1218      return true;
1219  }
1220  
1221  /**
1222   * Builds an attribute list from string containing attributes.
1223   *
1224   * This function does a lot of work. It parses an attribute list into an array
1225   * with attribute data, and tries to do the right thing even if it gets weird
1226   * input. It will add quotes around attribute values that don't have any quotes
1227   * or apostrophes around them, to make it easier to produce HTML code that will
1228   * conform to W3C's HTML specification. It will also remove bad URL protocols
1229   * from attribute values. It also reduces duplicate attributes by using the
1230   * attribute defined first (`foo='bar' foo='baz'` will result in `foo='bar'`).
1231   *
1232   * @since 1.0.0
1233   *
1234   * @param string   $attr              Attribute list from HTML element to closing HTML element tag.
1235   * @param string[] $allowed_protocols Array of allowed URL protocols.
1236   * @return array[] Array of attribute information after parsing.
1237   */
1238  function wp_kses_hair( $attr, $allowed_protocols ) {
1239      $attrarr  = array();
1240      $mode     = 0;
1241      $attrname = '';
1242      $uris     = wp_kses_uri_attributes();
1243  
1244      // Loop through the whole attribute list.
1245  
1246      while ( strlen( $attr ) != 0 ) {
1247          $working = 0; // Was the last operation successful?
1248  
1249          switch ( $mode ) {
1250              case 0:
1251                  if ( preg_match( '/^([-a-zA-Z:]+)/', $attr, $match ) ) {
1252                      $attrname = $match[1];
1253                      $working  = 1;
1254                      $mode     = 1;
1255                      $attr     = preg_replace( '/^[-a-zA-Z:]+/', '', $attr );
1256                  }
1257  
1258                  break;
1259  
1260              case 1:
1261                  if ( preg_match( '/^\s*=\s*/', $attr ) ) { // Equals sign.
1262                      $working = 1;
1263                      $mode    = 2;
1264                      $attr    = preg_replace( '/^\s*=\s*/', '', $attr );
1265                      break;
1266                  }
1267  
1268                  if ( preg_match( '/^\s+/', $attr ) ) { // Valueless.
1269                      $working = 1;
1270                      $mode    = 0;
1271                      if ( false === array_key_exists( $attrname, $attrarr ) ) {
1272                          $attrarr[ $attrname ] = array(
1273                              'name'  => $attrname,
1274                              'value' => '',
1275                              'whole' => $attrname,
1276                              'vless' => 'y',
1277                          );
1278                      }
1279                      $attr = preg_replace( '/^\s+/', '', $attr );
1280                  }
1281  
1282                  break;
1283  
1284              case 2:
1285                  if ( preg_match( '%^"([^"]*)"(\s+|/?$)%', $attr, $match ) ) {
1286                      // "value"
1287                      $thisval = $match[1];
1288                      if ( in_array( strtolower( $attrname ), $uris, true ) ) {
1289                          $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
1290                      }
1291  
1292                      if ( false === array_key_exists( $attrname, $attrarr ) ) {
1293                          $attrarr[ $attrname ] = array(
1294                              'name'  => $attrname,
1295                              'value' => $thisval,
1296                              'whole' => "$attrname=\"$thisval\"",
1297                              'vless' => 'n',
1298                          );
1299                      }
1300                      $working = 1;
1301                      $mode    = 0;
1302                      $attr    = preg_replace( '/^"[^"]*"(\s+|$)/', '', $attr );
1303                      break;
1304                  }
1305  
1306                  if ( preg_match( "%^'([^']*)'(\s+|/?$)%", $attr, $match ) ) {
1307                      // 'value'
1308                      $thisval = $match[1];
1309                      if ( in_array( strtolower( $attrname ), $uris, true ) ) {
1310                          $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
1311                      }
1312  
1313                      if ( false === array_key_exists( $attrname, $attrarr ) ) {
1314                          $attrarr[ $attrname ] = array(
1315                              'name'  => $attrname,
1316                              'value' => $thisval,
1317                              'whole' => "$attrname='$thisval'",
1318                              'vless' => 'n',
1319                          );
1320                      }
1321                      $working = 1;
1322                      $mode    = 0;
1323                      $attr    = preg_replace( "/^'[^']*'(\s+|$)/", '', $attr );
1324                      break;
1325                  }
1326  
1327                  if ( preg_match( "%^([^\s\"']+)(\s+|/?$)%", $attr, $match ) ) {
1328                      // value
1329                      $thisval = $match[1];
1330                      if ( in_array( strtolower( $attrname ), $uris, true ) ) {
1331                          $thisval = wp_kses_bad_protocol( $thisval, $allowed_protocols );
1332                      }
1333  
1334                      if ( false === array_key_exists( $attrname, $attrarr ) ) {
1335                          $attrarr[ $attrname ] = array(
1336                              'name'  => $attrname,
1337                              'value' => $thisval,
1338                              'whole' => "$attrname=\"$thisval\"",
1339                              'vless' => 'n',
1340                          );
1341                      }
1342                      // We add quotes to conform to W3C's HTML spec.
1343                      $working = 1;
1344                      $mode    = 0;
1345                      $attr    = preg_replace( "%^[^\s\"']+(\s+|$)%", '', $attr );
1346                  }
1347  
1348                  break;
1349          } // End switch.
1350  
1351          if ( 0 == $working ) { // Not well-formed, remove and try again.
1352              $attr = wp_kses_html_error( $attr );
1353              $mode = 0;
1354          }
1355      } // End while.
1356  
1357      if ( 1 == $mode && false === array_key_exists( $attrname, $attrarr ) ) {
1358          // Special case, for when the attribute list ends with a valueless
1359          // attribute like "selected".
1360          $attrarr[ $attrname ] = array(
1361              'name'  => $attrname,
1362              'value' => '',
1363              'whole' => $attrname,
1364              'vless' => 'y',
1365          );
1366      }
1367  
1368      return $attrarr;
1369  }
1370  
1371  /**
1372   * Finds all attributes of an HTML element.
1373   *
1374   * Does not modify input.  May return "evil" output.
1375   *
1376   * Based on `wp_kses_split2()` and `wp_kses_attr()`.
1377   *
1378   * @since 4.2.3
1379   *
1380   * @param string $element HTML element.
1381   * @return array|bool List of attributes found in the element. Returns false on failure.
1382   */
1383  function wp_kses_attr_parse( $element ) {
1384      $valid = preg_match( '%^(<\s*)(/\s*)?([a-zA-Z0-9]+\s*)([^>]*)(>?)$%', $element, $matches );
1385      if ( 1 !== $valid ) {
1386          return false;
1387      }
1388  
1389      $begin  = $matches[1];
1390      $slash  = $matches[2];
1391      $elname = $matches[3];
1392      $attr   = $matches[4];
1393      $end    = $matches[5];
1394  
1395      if ( '' !== $slash ) {
1396          // Closing elements do not get parsed.
1397          return false;
1398      }
1399  
1400      // Is there a closing XHTML slash at the end of the attributes?
1401      if ( 1 === preg_match( '%\s*/\s*$%', $attr, $matches ) ) {
1402          $xhtml_slash = $matches[0];
1403          $attr        = substr( $attr, 0, -strlen( $xhtml_slash ) );
1404      } else {
1405          $xhtml_slash = '';
1406      }
1407  
1408      // Split it.
1409      $attrarr = wp_kses_hair_parse( $attr );
1410      if ( false === $attrarr ) {
1411          return false;
1412      }
1413  
1414      // Make sure all input is returned by adding front and back matter.
1415      array_unshift( $attrarr, $begin . $slash . $elname );
1416      array_push( $attrarr, $xhtml_slash . $end );
1417  
1418      return $attrarr;
1419  }
1420  
1421  /**
1422   * Builds an attribute list from string containing attributes.
1423   *
1424   * Does not modify input.  May return "evil" output.
1425   * In case of unexpected input, returns false instead of stripping things.
1426   *
1427   * Based on `wp_kses_hair()` but does not return a multi-dimensional array.
1428   *
1429   * @since 4.2.3
1430   *
1431   * @param string $attr Attribute list from HTML element to closing HTML element tag.
1432   * @return array|bool List of attributes found in $attr. Returns false on failure.
1433   */
1434  function wp_kses_hair_parse( $attr ) {
1435      if ( '' === $attr ) {
1436          return array();
1437      }
1438  
1439      // phpcs:disable Squiz.Strings.ConcatenationSpacing.PaddingFound -- don't remove regex indentation
1440      $regex =
1441      '(?:'
1442      .     '[-a-zA-Z:]+'   // Attribute name.
1443      . '|'
1444      .     '\[\[?[^\[\]]+\]\]?' // Shortcode in the name position implies unfiltered_html.
1445      . ')'
1446      . '(?:'               // Attribute value.
1447      .     '\s*=\s*'       // All values begin with '='.
1448      .     '(?:'
1449      .         '"[^"]*"'   // Double-quoted.
1450      .     '|'
1451      .         "'[^']*'"   // Single-quoted.
1452      .     '|'
1453      .         '[^\s"\']+' // Non-quoted.
1454      .         '(?:\s|$)'  // Must have a space.
1455      .     ')'
1456      . '|'
1457      .     '(?:\s|$)'      // If attribute has no value, space is required.
1458      . ')'
1459      . '\s*';              // Trailing space is optional except as mentioned above.
1460      // phpcs:enable
1461  
1462      // Although it is possible to reduce this procedure to a single regexp,
1463      // we must run that regexp twice to get exactly the expected result.
1464  
1465      $validation = "%^($regex)+$%";
1466      $extraction = "%$regex%";
1467  
1468      if ( 1 === preg_match( $validation, $attr ) ) {
1469          preg_match_all( $extraction, $attr, $attrarr );
1470          return $attrarr[0];
1471      } else {
1472          return false;
1473      }
1474  }
1475  
1476  /**
1477   * Performs different checks for attribute values.
1478   *
1479   * The currently implemented checks are "maxlen", "minlen", "maxval", "minval",
1480   * and "valueless".
1481   *
1482   * @since 1.0.0
1483   *
1484   * @param string $value      Attribute value.
1485   * @param string $vless      Whether the attribute is valueless. Use 'y' or 'n'.
1486   * @param string $checkname  What $checkvalue is checking for.
1487   * @param mixed  $checkvalue What constraint the value should pass.
1488   * @return bool Whether check passes.
1489   */
1490  function wp_kses_check_attr_val( $value, $vless, $checkname, $checkvalue ) {
1491      $ok = true;
1492  
1493      switch ( strtolower( $checkname ) ) {
1494          case 'maxlen':
1495              /*
1496               * The maxlen check makes sure that the attribute value has a length not
1497               * greater than the given value. This can be used to avoid Buffer Overflows
1498               * in WWW clients and various Internet servers.
1499               */
1500  
1501              if ( strlen( $value ) > $checkvalue ) {
1502                  $ok = false;
1503              }
1504              break;
1505  
1506          case 'minlen':
1507              /*
1508               * The minlen check makes sure that the attribute value has a length not
1509               * smaller than the given value.
1510               */
1511  
1512              if ( strlen( $value ) < $checkvalue ) {
1513                  $ok = false;
1514              }
1515              break;
1516  
1517          case 'maxval':
1518              /*
1519               * The maxval check does two things: it checks that the attribute value is
1520               * an integer from 0 and up, without an excessive amount of zeroes or
1521               * whitespace (to avoid Buffer Overflows). It also checks that the attribute
1522               * value is not greater than the given value.
1523               * This check can be used to avoid Denial of Service attacks.
1524               */
1525  
1526              if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
1527                  $ok = false;
1528              }
1529              if ( $value > $checkvalue ) {
1530                  $ok = false;
1531              }
1532              break;
1533  
1534          case 'minval':
1535              /*
1536               * The minval check makes sure that the attribute value is a positive integer,
1537               * and that it is not smaller than the given value.
1538               */
1539  
1540              if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
1541                  $ok = false;
1542              }
1543              if ( $value < $checkvalue ) {
1544                  $ok = false;
1545              }
1546              break;
1547  
1548          case 'valueless':
1549              /*
1550               * The valueless check makes sure if the attribute has a value
1551               * (like `<a href="blah">`) or not (`<option selected>`). If the given value
1552               * is a "y" or a "Y", the attribute must not have a value.
1553               * If the given value is an "n" or an "N", the attribute must have a value.
1554               */
1555  
1556              if ( strtolower( $checkvalue ) != $vless ) {
1557                  $ok = false;
1558              }
1559              break;
1560      } // End switch.
1561  
1562      return $ok;
1563  }
1564  
1565  /**
1566   * Sanitizes a string and removed disallowed URL protocols.
1567   *
1568   * This function removes all non-allowed protocols from the beginning of the
1569   * string. It ignores whitespace and the case of the letters, and it does
1570   * understand HTML entities. It does its work recursively, so it won't be
1571   * fooled by a string like `javascript:javascript:alert(57)`.
1572   *
1573   * @since 1.0.0
1574   *
1575   * @param string   $string            Content to filter bad protocols from.
1576   * @param string[] $allowed_protocols Array of allowed URL protocols.
1577   * @return string Filtered content.
1578   */
1579  function wp_kses_bad_protocol( $string, $allowed_protocols ) {
1580      $string     = wp_kses_no_null( $string );
1581      $iterations = 0;
1582  
1583      do {
1584          $original_string = $string;
1585          $string          = wp_kses_bad_protocol_once( $string, $allowed_protocols );
1586      } while ( $original_string != $string && ++$iterations < 6 );
1587  
1588      if ( $original_string != $string ) {
1589          return '';
1590      }
1591  
1592      return $string;
1593  }
1594  
1595  /**
1596   * Removes any invalid control characters in a text string.
1597   *
1598   * Also removes any instance of the `\0` string.
1599   *
1600   * @since 1.0.0
1601   *
1602   * @param string $string  Content to filter null characters from.
1603   * @param array  $options Set 'slash_zero' => 'keep' when '\0' is allowed. Default is 'remove'.
1604   * @return string Filtered content.
1605   */
1606  function wp_kses_no_null( $string, $options = null ) {
1607      if ( ! isset( $options['slash_zero'] ) ) {
1608          $options = array( 'slash_zero' => 'remove' );
1609      }
1610  
1611      $string = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string );
1612      if ( 'remove' == $options['slash_zero'] ) {
1613          $string = preg_replace( '/\\\\+0+/', '', $string );
1614      }
1615  
1616      return $string;
1617  }
1618  
1619  /**
1620   * Strips slashes from in front of quotes.
1621   *
1622   * This function changes the character sequence `\"` to just `"`. It leaves all other
1623   * slashes alone. The quoting from `preg_replace(//e)` requires this.
1624   *
1625   * @since 1.0.0
1626   *
1627   * @param string $string String to strip slashes from.
1628   * @return string Fixed string with quoted slashes.
1629   */
1630  function wp_kses_stripslashes( $string ) {
1631      return preg_replace( '%\\\\"%', '"', $string );
1632  }
1633  
1634  /**
1635   * Converts the keys of an array to lowercase.
1636   *
1637   * @since 1.0.0
1638   *
1639   * @param array $inarray Unfiltered array.
1640   * @return array Fixed array with all lowercase keys.
1641   */
1642  function wp_kses_array_lc( $inarray ) {
1643      $outarray = array();
1644  
1645      foreach ( (array) $inarray as $inkey => $inval ) {
1646          $outkey              = strtolower( $inkey );
1647          $outarray[ $outkey ] = array();
1648  
1649          foreach ( (array) $inval as $inkey2 => $inval2 ) {
1650              $outkey2                         = strtolower( $inkey2 );
1651              $outarray[ $outkey ][ $outkey2 ] = $inval2;
1652          }
1653      }
1654  
1655      return $outarray;
1656  }
1657  
1658  /**
1659   * Handles parsing errors in `wp_kses_hair()`.
1660   *
1661   * The general plan is to remove everything to and including some whitespace,
1662   * but it deals with quotes and apostrophes as well.
1663   *
1664   * @since 1.0.0
1665   *
1666   * @param string $string
1667   * @return string
1668   */
1669  function wp_kses_html_error( $string ) {
1670      return preg_replace( '/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string );
1671  }
1672  
1673  /**
1674   * Sanitizes content from bad protocols and other characters.
1675   *
1676   * This function searches for URL protocols at the beginning of the string, while
1677   * handling whitespace and HTML entities.
1678   *
1679   * @since 1.0.0
1680   *
1681   * @param string   $string            Content to check for bad protocols.
1682   * @param string[] $allowed_protocols Array of allowed URL protocols.
1683   * @return string Sanitized content.
1684   */
1685  function wp_kses_bad_protocol_once( $string, $allowed_protocols, $count = 1 ) {
1686      $string  = preg_replace( '/(&#0*58(?![;0-9])|&#x0*3a(?![;a-f0-9]))/i', '$1;', $string );
1687      $string2 = preg_split( '/:|&#0*58;|&#x0*3a;|&colon;/i', $string, 2 );
1688      if ( isset( $string2[1] ) && ! preg_match( '%/\?%', $string2[0] ) ) {
1689          $string   = trim( $string2[1] );
1690          $protocol = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols );
1691          if ( 'feed:' == $protocol ) {
1692              if ( $count > 2 ) {
1693                  return '';
1694              }
1695              $string = wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count );
1696              if ( empty( $string ) ) {
1697                  return $string;
1698              }
1699          }
1700          $string = $protocol . $string;
1701      }
1702  
1703      return $string;
1704  }
1705  
1706  /**
1707   * Callback for `wp_kses_bad_protocol_once()` regular expression.
1708   *
1709   * This function processes URL protocols, checks to see if they're in the
1710   * whitelist or not, and returns different data depending on the answer.
1711   *
1712   * @access private
1713   * @ignore
1714   * @since 1.0.0
1715   *
1716   * @param string   $string            URI scheme to check against the whitelist.
1717   * @param string[] $allowed_protocols Array of allowed URL protocols.
1718   * @return string Sanitized content.
1719   */
1720  function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
1721      $string2 = wp_kses_decode_entities( $string );
1722      $string2 = preg_replace( '/\s/', '', $string2 );
1723      $string2 = wp_kses_no_null( $string2 );
1724      $string2 = strtolower( $string2 );
1725  
1726      $allowed = false;
1727      foreach ( (array) $allowed_protocols as $one_protocol ) {
1728          if ( strtolower( $one_protocol ) == $string2 ) {
1729              $allowed = true;
1730              break;
1731          }
1732      }
1733  
1734      if ( $allowed ) {
1735          return "$string2:";
1736      } else {
1737          return '';
1738      }
1739  }
1740  
1741  /**
1742   * Converts and fixes HTML entities.
1743   *
1744   * This function normalizes HTML entities. It will convert `AT&T` to the correct
1745   * `AT&amp;T`, `&#00058;` to `&#58;`, `&#XYZZY;` to `&amp;#XYZZY;` and so on.
1746   *
1747   * @since 1.0.0
1748   *
1749   * @param string $string Content to normalize entities.
1750   * @return string Content with normalized entities.
1751   */
1752  function wp_kses_normalize_entities( $string ) {
1753      // Disarm all entities by converting & to &amp;
1754      $string = str_replace( '&', '&amp;', $string );
1755  
1756      // Change back the allowed entities in our entity whitelist.
1757      $string = preg_replace_callback( '/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string );
1758      $string = preg_replace_callback( '/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string );
1759      $string = preg_replace_callback( '/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string );
1760  
1761      return $string;
1762  }
1763  
1764  /**
1765   * Callback for `wp_kses_normalize_entities()` regular expression.
1766   *
1767   * This function only accepts valid named entity references, which are finite,
1768   * case-sensitive, and highly scrutinized by HTML and XML validators.
1769   *
1770   * @since 3.0.0
1771   *
1772   * @global array $allowedentitynames
1773   *
1774   * @param array $matches preg_replace_callback() matches array.
1775   * @return string Correctly encoded entity.
1776   */
1777  function wp_kses_named_entities( $matches ) {
1778      global $allowedentitynames;
1779  
1780      if ( empty( $matches[1] ) ) {
1781          return '';
1782      }
1783  
1784      $i = $matches[1];
1785      return ( ! in_array( $i, $allowedentitynames, true ) ) ? "&amp;$i;" : "&$i;";
1786  }
1787  
1788  /**
1789   * Callback for `wp_kses_normalize_entities()` regular expression.
1790   *
1791   * This function helps `wp_kses_normalize_entities()` to only accept 16-bit
1792   * values and nothing more for `&#number;` entities.
1793   *
1794   * @access private
1795   * @ignore
1796   * @since 1.0.0
1797   *
1798   * @param array $matches `preg_replace_callback()` matches array.
1799   * @return string Correctly encoded entity.
1800   */
1801  function wp_kses_normalize_entities2( $matches ) {
1802      if ( empty( $matches[1] ) ) {
1803          return '';
1804      }
1805  
1806      $i = $matches[1];
1807      if ( valid_unicode( $i ) ) {
1808          $i = str_pad( ltrim( $i, '0' ), 3, '0', STR_PAD_LEFT );
1809          $i = "&#$i;";
1810      } else {
1811          $i = "&amp;#$i;";
1812      }
1813  
1814      return $i;
1815  }
1816  
1817  /**
1818   * Callback for `wp_kses_normalize_entities()` for regular expression.
1819   *
1820   * This function helps `wp_kses_normalize_entities()` to only accept valid Unicode
1821   * numeric entities in hex form.
1822   *
1823   * @since 2.7.0
1824   * @access private
1825   * @ignore
1826   *
1827   * @param array $matches `preg_replace_callback()` matches array.
1828   * @return string Correctly encoded entity.
1829   */
1830  function wp_kses_normalize_entities3( $matches ) {
1831      if ( empty( $matches[1] ) ) {
1832          return '';
1833      }
1834  
1835      $hexchars = $matches[1];
1836      return ( ! valid_unicode( hexdec( $hexchars ) ) ) ? "&amp;#x$hexchars;" : '&#x' . ltrim( $hexchars, '0' ) . ';';
1837  }
1838  
1839  /**
1840   * Determines if a Unicode codepoint is valid.
1841   *
1842   * @since 2.7.0
1843   *
1844   * @param int $i Unicode codepoint.
1845   * @return bool Whether or not the codepoint is a valid Unicode codepoint.
1846   */
1847  function valid_unicode( $i ) {
1848      return ( 0x9 == $i || 0xa == $i || 0xd == $i ||
1849              ( 0x20 <= $i && $i <= 0xd7ff ) ||
1850              ( 0xe000 <= $i && $i <= 0xfffd ) ||
1851              ( 0x10000 <= $i && $i <= 0x10ffff ) );
1852  }
1853  
1854  /**
1855   * Converts all numeric HTML entities to their named counterparts.
1856   *
1857   * This function decodes numeric HTML entities (`&#65;` and `&#x41;`).
1858   * It doesn't do anything with named entities like `&auml;`, but we don't
1859   * need them in the URL protocol whitelisting system anyway.
1860   *
1861   * @since 1.0.0
1862   *
1863   * @param string $string Content to change entities.
1864   * @return string Content after decoded entities.
1865   */
1866  function wp_kses_decode_entities( $string ) {
1867      $string = preg_replace_callback( '/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string );
1868      $string = preg_replace_callback( '/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string );
1869  
1870      return $string;
1871  }
1872  
1873  /**
1874   * Regex callback for `wp_kses_decode_entities()`.
1875   *
1876   * @since 2.9.0
1877   * @access private
1878   * @ignore
1879   *
1880   * @param array $match preg match
1881   * @return string
1882   */
1883  function _wp_kses_decode_entities_chr( $match ) {
1884      return chr( $match[1] );
1885  }
1886  
1887  /**
1888   * Regex callback for `wp_kses_decode_entities()`.
1889   *
1890   * @since 2.9.0
1891   * @access private
1892   * @ignore
1893   *
1894   * @param array $match preg match
1895   * @return string
1896   */
1897  function _wp_kses_decode_entities_chr_hexdec( $match ) {
1898      return chr( hexdec( $match[1] ) );
1899  }
1900  
1901  /**
1902   * Sanitize content with allowed HTML KSES rules.
1903   *
1904   * This function expects slashed data.
1905   *
1906   * @since 1.0.0
1907   *
1908   * @param string $data Content to filter, expected to be escaped with slashes.
1909   * @return string Filtered content.
1910   */
1911  function wp_filter_kses( $data ) {
1912      return addslashes( wp_kses( stripslashes( $data ), current_filter() ) );
1913  }
1914  
1915  /**
1916   * Sanitize content with allowed HTML KSES rules.
1917   *
1918   * This function expects unslashed data.
1919   *
1920   * @since 2.9.0
1921   *
1922   * @param string $data Content to filter, expected to not be escaped.
1923   * @return string Filtered content.
1924   */
1925  function wp_kses_data( $data ) {
1926      return wp_kses( $data, current_filter() );
1927  }
1928  
1929  /**
1930   * Sanitizes content for allowed HTML tags for post content.
1931   *
1932   * Post content refers to the page contents of the 'post' type and not `$_POST`
1933   * data from forms.
1934   *
1935   * This function expects slashed data.
1936   *
1937   * @since 2.0.0
1938   *
1939   * @param string $data Post content to filter, expected to be escaped with slashes.
1940   * @return string Filtered post content with allowed HTML tags and attributes intact.
1941   */
1942  function wp_filter_post_kses( $data ) {
1943      return addslashes( wp_kses( stripslashes( $data ), 'post' ) );
1944  }
1945  
1946  /**
1947   * Sanitizes content for allowed HTML tags for post content.
1948   *
1949   * Post content refers to the page contents of the 'post' type and not `$_POST`
1950   * data from forms.
1951   *
1952   * This function expects unslashed data.
1953   *
1954   * @since 2.9.0
1955   *
1956   * @param string $data Post content to filter.
1957   * @return string Filtered post content with allowed HTML tags and attributes intact.
1958   */
1959  function wp_kses_post( $data ) {
1960      return wp_kses( $data, 'post' );
1961  }
1962  
1963  /**
1964   * Navigates through an array, object, or scalar, and sanitizes content for
1965   * allowed HTML tags for post content.
1966   *
1967   * @since 4.4.2
1968   *
1969   * @see map_deep()
1970   *
1971   * @param mixed $data The array, object, or scalar value to inspect.
1972   * @return mixed The filtered content.
1973   */
1974  function wp_kses_post_deep( $data ) {
1975      return map_deep( $data, 'wp_kses_post' );
1976  }
1977  
1978  /**
1979   * Strips all HTML from a text string.
1980   *
1981   * This function expects slashed data.
1982   *
1983   * @since 2.1.0
1984   *
1985   * @param string $data Content to strip all HTML from.
1986   * @return string Filtered content without any HTML.
1987   */
1988  function wp_filter_nohtml_kses( $data ) {
1989      return addslashes( wp_kses( stripslashes( $data ), 'strip' ) );
1990  }
1991  
1992  /**
1993   * Adds all KSES input form content filters.
1994   *
1995   * All hooks have default priority. The `wp_filter_kses()` function is added to
1996   * the 'pre_comment_content' and 'title_save_pre' hooks.
1997   *
1998   * The `wp_filter_post_kses()` function is added to the 'content_save_pre',
1999   * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
2000   *
2001   * @since 2.0.0
2002   */
2003  function kses_init_filters() {
2004      // Normal filtering.
2005      add_filter( 'title_save_pre', 'wp_filter_kses' );
2006  
2007      // Comment filtering.
2008      if ( current_user_can( 'unfiltered_html' ) ) {
2009          add_filter( 'pre_comment_content', 'wp_filter_post_kses' );
2010      } else {
2011          add_filter( 'pre_comment_content', 'wp_filter_kses' );
2012      }
2013  
2014      // Post filtering.
2015      add_filter( 'content_save_pre', 'wp_filter_post_kses' );
2016      add_filter( 'excerpt_save_pre', 'wp_filter_post_kses' );
2017      add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
2018  }
2019  
2020  /**
2021   * Removes all KSES input form content filters.
2022   *
2023   * A quick procedural method to removing all of the filters that KSES uses for
2024   * content in WordPress Loop.
2025   *
2026   * Does not remove the `kses_init()` function from {@see 'init'} hook (priority is
2027   * default). Also does not remove `kses_init()` function from {@see 'set_current_user'}
2028   * hook (priority is also default).
2029   *
2030   * @since 2.0.6
2031   */
2032  function kses_remove_filters() {
2033      // Normal filtering.
2034      remove_filter( 'title_save_pre', 'wp_filter_kses' );
2035  
2036      // Comment filtering.
2037      remove_filter( 'pre_comment_content', 'wp_filter_post_kses' );
2038      remove_filter( 'pre_comment_content', 'wp_filter_kses' );
2039  
2040      // Post filtering.
2041      remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
2042      remove_filter( 'excerpt_save_pre', 'wp_filter_post_kses' );
2043      remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
2044  }
2045  
2046  /**
2047   * Sets up most of the KSES filters for input form content.
2048   *
2049   * First removes all of the KSES filters in case the current user does not need
2050   * to have KSES filter the content. If the user does not have `unfiltered_html`
2051   * capability, then KSES filters are added.
2052   *
2053   * @since 2.0.0
2054   */
2055  function kses_init() {
2056      kses_remove_filters();
2057  
2058      if ( ! current_user_can( 'unfiltered_html' ) ) {
2059          kses_init_filters();
2060      }
2061  }
2062  
2063  /**
2064   * Filters an inline style attribute and removes disallowed rules.
2065   *
2066   * @since 2.8.1
2067   *
2068   * @param string $css        A string of CSS rules.
2069   * @param string $deprecated Not used.
2070   * @return string Filtered string of CSS rules.
2071   */
2072  function safecss_filter_attr( $css, $deprecated = '' ) {
2073      if ( ! empty( $deprecated ) ) {
2074          _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented.
2075      }
2076  
2077      $css = wp_kses_no_null( $css );
2078      $css = str_replace( array( "\n", "\r", "\t" ), '', $css );
2079  
2080      $allowed_protocols = wp_allowed_protocols();
2081  
2082      $css_array = explode( ';', trim( $css ) );
2083  
2084      /**
2085       * Filters list of allowed CSS attributes.
2086       *
2087       * @since 2.8.1
2088       * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`.
2089       * @since 4.6.0 Added support for `list-style-type`.
2090       * @since 5.0.0 Added support for `background-image`.
2091       * @since 5.1.0 Added support for `text-transform`.
2092       * @since 5.2.0 Added support for `background-position` and `grid-template-columns`
2093       * @since 5.3.0 Added support for `grid`, `flex` and `column` layout properties.
2094       *              Extend `background-*` support of individual properties.
2095       * @since 5.3.1 Added support for gradient backgrounds.
2096       *
2097       * @param string[] $attr Array of allowed CSS attributes.
2098       */
2099      $allowed_attr = apply_filters(
2100          'safe_style_css',
2101          array(
2102              'background',
2103              'background-color',
2104              'background-image',
2105              'background-position',
2106              'background-size',
2107              'background-attachment',
2108              'background-blend-mode',
2109  
2110              'border',
2111              'border-radius',
2112              'border-width',
2113              'border-color',
2114              'border-style',
2115              'border-right',
2116              'border-right-color',
2117              'border-right-style',
2118              'border-right-width',
2119              'border-bottom',
2120              'border-bottom-color',
2121              'border-bottom-style',
2122              'border-bottom-width',
2123              'border-left',
2124              'border-left-color',
2125              'border-left-style',
2126              'border-left-width',
2127              'border-top',
2128              'border-top-color',
2129              'border-top-style',
2130              'border-top-width',
2131  
2132              'border-spacing',
2133              'border-collapse',
2134              'caption-side',
2135  
2136              'columns',
2137              'column-count',
2138              'column-fill',
2139              'column-gap',
2140              'column-rule',
2141              'column-span',
2142              'column-width',
2143  
2144              'color',
2145              'font',
2146              'font-family',
2147              'font-size',
2148              'font-style',
2149              'font-variant',
2150              'font-weight',
2151              'letter-spacing',
2152              'line-height',
2153              'text-align',
2154              'text-decoration',
2155              'text-indent',
2156              'text-transform',
2157  
2158              'height',
2159              'min-height',
2160              'max-height',
2161  
2162              'width',
2163              'min-width',
2164              'max-width',
2165  
2166              'margin',
2167              'margin-right',
2168              'margin-bottom',
2169              'margin-left',
2170              'margin-top',
2171  
2172              'padding',
2173              'padding-right',
2174              'padding-bottom',
2175              'padding-left',
2176              'padding-top',
2177  
2178              'flex',
2179              'flex-basis',
2180              'flex-direction',
2181              'flex-flow',
2182              'flex-grow',
2183              'flex-shrink',
2184  
2185              'grid-template-columns',
2186              'grid-auto-columns',
2187              'grid-column-start',
2188              'grid-column-end',
2189              'grid-column-gap',
2190              'grid-template-rows',
2191              'grid-auto-rows',
2192              'grid-row-start',
2193              'grid-row-end',
2194              'grid-row-gap',
2195              'grid-gap',
2196  
2197              'justify-content',
2198              'justify-items',
2199              'justify-self',
2200              'align-content',
2201              'align-items',
2202              'align-self',
2203  
2204              'clear',
2205              'cursor',
2206              'direction',
2207              'float',
2208              'overflow',
2209              'vertical-align',
2210              'list-style-type',
2211          )
2212      );
2213  
2214      /*
2215       * CSS attributes that accept URL data types.
2216       *
2217       * This is in accordance to the CSS spec and unrelated to
2218       * the sub-set of supported attributes above.
2219       *
2220       * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url
2221       */
2222      $css_url_data_types = array(
2223          'background',
2224          'background-image',
2225  
2226          'cursor',
2227  
2228          'list-style',
2229          'list-style-image',
2230      );
2231  
2232      /*
2233       * CSS attributes that accept gradient data types.
2234       *
2235       */
2236      $css_gradient_data_types = array(
2237          'background',
2238          'background-image',
2239      );
2240  
2241      if ( empty( $allowed_attr ) ) {
2242          return $css;
2243      }
2244  
2245      $css = '';
2246      foreach ( $css_array as $css_item ) {
2247          if ( '' == $css_item ) {
2248              continue;
2249          }
2250  
2251          $css_item        = trim( $css_item );
2252          $css_test_string = $css_item;
2253          $found           = false;
2254          $url_attr        = false;
2255          $gradient_attr   = false;
2256  
2257          if ( strpos( $css_item, ':' ) === false ) {
2258              $found = true;
2259          } else {
2260              $parts        = explode( ':', $css_item, 2 );
2261              $css_selector = trim( $parts[0] );
2262  
2263              if ( in_array( $css_selector, $allowed_attr, true ) ) {
2264                  $found         = true;
2265                  $url_attr      = in_array( $css_selector, $css_url_data_types, true );
2266                  $gradient_attr = in_array( $css_selector, $css_gradient_data_types, true );
2267              }
2268          }
2269  
2270          if ( $found && $url_attr ) {
2271              // Simplified: matches the sequence `url(*)`.
2272              preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches );
2273  
2274              foreach ( $url_matches[0] as $url_match ) {
2275                  // Clean up the URL from each of the matches above.
2276                  preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces );
2277  
2278                  if ( empty( $url_pieces[2] ) ) {
2279                      $found = false;
2280                      break;
2281                  }
2282  
2283                  $url = trim( $url_pieces[2] );
2284  
2285                  if ( empty( $url ) || wp_kses_bad_protocol( $url, $allowed_protocols ) !== $url ) {
2286                      $found = false;
2287                      break;
2288                  } else {
2289                      // Remove the whole `url(*)` bit that was matched above from the CSS.
2290                      $css_test_string = str_replace( $url_match, '', $css_test_string );
2291                  }
2292              }
2293          }
2294  
2295          if ( $found && $gradient_attr ) {
2296              $css_value = trim( $parts[1] );
2297              if ( preg_match( '/^(repeating-)?(linear|radial|conic)-gradient\(([^()]|rgb[a]?\([^()]*\))*\)$/', $css_value ) ) {
2298                  // Remove the whole `gradient` bit that was matched above from the CSS.
2299                  $css_test_string = str_replace( $css_value, '', $css_test_string );
2300              }
2301          }
2302  
2303          // Remove any CSS containing containing \ ( & } = or comments, except for url() useage checked above.
2304          if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) {
2305              if ( '' != $css ) {
2306                  $css .= ';';
2307              }
2308  
2309              $css .= $css_item;
2310          }
2311      }
2312  
2313      return $css;
2314  }
2315  
2316  /**
2317   * Helper function to add global attributes to a tag in the allowed html list.
2318   *
2319   * @since 3.5.0
2320   * @since 5.0.0 Add support for `data-*` wildcard attributes.
2321   * @access private
2322   * @ignore
2323   *
2324   * @param array $value An array of attributes.
2325   * @return array The array of attributes with global attributes added.
2326   */
2327  function _wp_add_global_attributes( $value ) {
2328      $global_attributes = array(
2329          'aria-describedby' => true,
2330          'aria-details'     => true,
2331          'aria-label'       => true,
2332          'aria-labelledby'  => true,
2333          'aria-hidden'      => true,
2334          'class'            => true,
2335          'id'               => true,
2336          'style'            => true,
2337          'title'            => true,
2338          'role'             => true,
2339          'data-*'           => true,
2340      );
2341  
2342      if ( true === $value ) {
2343          $value = array();
2344      }
2345  
2346      if ( is_array( $value ) ) {
2347          return array_merge( $value, $global_attributes );
2348      }
2349  
2350      return $value;
2351  }


Generated : Wed Apr 8 08:20:02 2020 Cross-referenced by PHPXref