[ Index ]

PHP Cross Reference of WordPress

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 ) ) {
 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 ( $string != ( $newstring = wp_kses( $string, $allowed_html, $allowed_protocols ) ) ) {
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 ) ) {
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 ) ) {
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 ) ) {
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          } // switch
1350  
1351          if ( $working == 0 ) { // not well formed, remove and try again
1352              $attr = wp_kses_html_error( $attr );
1353              $mode = 0;
1354          }
1355      } // while
1356  
1357      if ( $mode == 1 && 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              // The maxlen check makes sure that the attribute value has a length not
1496              // greater than the given value. This can be used to avoid Buffer Overflows
1497              // in WWW clients and various Internet servers.
1498  
1499              if ( strlen( $value ) > $checkvalue ) {
1500                  $ok = false;
1501              }
1502              break;
1503  
1504          case 'minlen':
1505              // The minlen check makes sure that the attribute value has a length not
1506              // smaller than the given value.
1507  
1508              if ( strlen( $value ) < $checkvalue ) {
1509                  $ok = false;
1510              }
1511              break;
1512  
1513          case 'maxval':
1514              // The maxval check does two things: it checks that the attribute value is
1515              // an integer from 0 and up, without an excessive amount of zeroes or
1516              // whitespace (to avoid Buffer Overflows). It also checks that the attribute
1517              // value is not greater than the given value.
1518              // This check can be used to avoid Denial of Service attacks.
1519  
1520              if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
1521                  $ok = false;
1522              }
1523              if ( $value > $checkvalue ) {
1524                  $ok = false;
1525              }
1526              break;
1527  
1528          case 'minval':
1529              // The minval check makes sure that the attribute value is a positive integer,
1530              // and that it is not smaller than the given value.
1531  
1532              if ( ! preg_match( '/^\s{0,6}[0-9]{1,6}\s{0,6}$/', $value ) ) {
1533                  $ok = false;
1534              }
1535              if ( $value < $checkvalue ) {
1536                  $ok = false;
1537              }
1538              break;
1539  
1540          case 'valueless':
1541              // The valueless check makes sure if the attribute has a value
1542              // (like `<a href="blah">`) or not (`<option selected>`). If the given value
1543              // is a "y" or a "Y", the attribute must not have a value.
1544              // If the given value is an "n" or an "N", the attribute must have a value.
1545  
1546              if ( strtolower( $checkvalue ) != $vless ) {
1547                  $ok = false;
1548              }
1549              break;
1550      } // switch
1551  
1552      return $ok;
1553  }
1554  
1555  /**
1556   * Sanitizes a string and removed disallowed URL protocols.
1557   *
1558   * This function removes all non-allowed protocols from the beginning of the
1559   * string. It ignores whitespace and the case of the letters, and it does
1560   * understand HTML entities. It does its work recursively, so it won't be
1561   * fooled by a string like `javascript:javascript:alert(57)`.
1562   *
1563   * @since 1.0.0
1564   *
1565   * @param string   $string            Content to filter bad protocols from.
1566   * @param string[] $allowed_protocols Array of allowed URL protocols.
1567   * @return string Filtered content.
1568   */
1569  function wp_kses_bad_protocol( $string, $allowed_protocols ) {
1570      $string     = wp_kses_no_null( $string );
1571      $iterations = 0;
1572  
1573      do {
1574          $original_string = $string;
1575          $string          = wp_kses_bad_protocol_once( $string, $allowed_protocols );
1576      } while ( $original_string != $string && ++$iterations < 6 );
1577  
1578      if ( $original_string != $string ) {
1579          return '';
1580      }
1581  
1582      return $string;
1583  }
1584  
1585  /**
1586   * Removes any invalid control characters in a text string.
1587   *
1588   * Also removes any instance of the `\0` string.
1589   *
1590   * @since 1.0.0
1591   *
1592   * @param string $string  Content to filter null characters from.
1593   * @param array  $options Set 'slash_zero' => 'keep' when '\0' is allowed. Default is 'remove'.
1594   * @return string Filtered content.
1595   */
1596  function wp_kses_no_null( $string, $options = null ) {
1597      if ( ! isset( $options['slash_zero'] ) ) {
1598          $options = array( 'slash_zero' => 'remove' );
1599      }
1600  
1601      $string = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F]/', '', $string );
1602      if ( 'remove' == $options['slash_zero'] ) {
1603          $string = preg_replace( '/\\\\+0+/', '', $string );
1604      }
1605  
1606      return $string;
1607  }
1608  
1609  /**
1610   * Strips slashes from in front of quotes.
1611   *
1612   * This function changes the character sequence `\"` to just `"`. It leaves all other
1613   * slashes alone. The quoting from `preg_replace(//e)` requires this.
1614   *
1615   * @since 1.0.0
1616   *
1617   * @param string $string String to strip slashes from.
1618   * @return string Fixed string with quoted slashes.
1619   */
1620  function wp_kses_stripslashes( $string ) {
1621      return preg_replace( '%\\\\"%', '"', $string );
1622  }
1623  
1624  /**
1625   * Converts the keys of an array to lowercase.
1626   *
1627   * @since 1.0.0
1628   *
1629   * @param array $inarray Unfiltered array.
1630   * @return array Fixed array with all lowercase keys.
1631   */
1632  function wp_kses_array_lc( $inarray ) {
1633      $outarray = array();
1634  
1635      foreach ( (array) $inarray as $inkey => $inval ) {
1636          $outkey              = strtolower( $inkey );
1637          $outarray[ $outkey ] = array();
1638  
1639          foreach ( (array) $inval as $inkey2 => $inval2 ) {
1640              $outkey2                         = strtolower( $inkey2 );
1641              $outarray[ $outkey ][ $outkey2 ] = $inval2;
1642          }
1643      }
1644  
1645      return $outarray;
1646  }
1647  
1648  /**
1649   * Handles parsing errors in `wp_kses_hair()`.
1650   *
1651   * The general plan is to remove everything to and including some whitespace,
1652   * but it deals with quotes and apostrophes as well.
1653   *
1654   * @since 1.0.0
1655   *
1656   * @param string $string
1657   * @return string
1658   */
1659  function wp_kses_html_error( $string ) {
1660      return preg_replace( '/^("[^"]*("|$)|\'[^\']*(\'|$)|\S)*\s*/', '', $string );
1661  }
1662  
1663  /**
1664   * Sanitizes content from bad protocols and other characters.
1665   *
1666   * This function searches for URL protocols at the beginning of the string, while
1667   * handling whitespace and HTML entities.
1668   *
1669   * @since 1.0.0
1670   *
1671   * @param string   $string            Content to check for bad protocols.
1672   * @param string[] $allowed_protocols Array of allowed URL protocols.
1673   * @return string Sanitized content.
1674   */
1675  function wp_kses_bad_protocol_once( $string, $allowed_protocols, $count = 1 ) {
1676      $string  = preg_replace( '/(&#0*58(?![;0-9])|&#x0*3a(?![;a-f0-9]))/i', '$1;', $string );
1677      $string2 = preg_split( '/:|&#0*58;|&#x0*3a;|&colon;/i', $string, 2 );
1678      if ( isset( $string2[1] ) && ! preg_match( '%/\?%', $string2[0] ) ) {
1679          $string   = trim( $string2[1] );
1680          $protocol = wp_kses_bad_protocol_once2( $string2[0], $allowed_protocols );
1681          if ( 'feed:' == $protocol ) {
1682              if ( $count > 2 ) {
1683                  return '';
1684              }
1685              $string = wp_kses_bad_protocol_once( $string, $allowed_protocols, ++$count );
1686              if ( empty( $string ) ) {
1687                  return $string;
1688              }
1689          }
1690          $string = $protocol . $string;
1691      }
1692  
1693      return $string;
1694  }
1695  
1696  /**
1697   * Callback for `wp_kses_bad_protocol_once()` regular expression.
1698   *
1699   * This function processes URL protocols, checks to see if they're in the
1700   * whitelist or not, and returns different data depending on the answer.
1701   *
1702   * @access private
1703   * @ignore
1704   * @since 1.0.0
1705   *
1706   * @param string   $string            URI scheme to check against the whitelist.
1707   * @param string[] $allowed_protocols Array of allowed URL protocols.
1708   * @return string Sanitized content.
1709   */
1710  function wp_kses_bad_protocol_once2( $string, $allowed_protocols ) {
1711      $string2 = wp_kses_decode_entities( $string );
1712      $string2 = preg_replace( '/\s/', '', $string2 );
1713      $string2 = wp_kses_no_null( $string2 );
1714      $string2 = strtolower( $string2 );
1715  
1716      $allowed = false;
1717      foreach ( (array) $allowed_protocols as $one_protocol ) {
1718          if ( strtolower( $one_protocol ) == $string2 ) {
1719              $allowed = true;
1720              break;
1721          }
1722      }
1723  
1724      if ( $allowed ) {
1725          return "$string2:";
1726      } else {
1727          return '';
1728      }
1729  }
1730  
1731  /**
1732   * Converts and fixes HTML entities.
1733   *
1734   * This function normalizes HTML entities. It will convert `AT&T` to the correct
1735   * `AT&amp;T`, `&#00058;` to `&#58;`, `&#XYZZY;` to `&amp;#XYZZY;` and so on.
1736   *
1737   * @since 1.0.0
1738   *
1739   * @param string $string Content to normalize entities.
1740   * @return string Content with normalized entities.
1741   */
1742  function wp_kses_normalize_entities( $string ) {
1743      // Disarm all entities by converting & to &amp;
1744      $string = str_replace( '&', '&amp;', $string );
1745  
1746      // Change back the allowed entities in our entity whitelist
1747      $string = preg_replace_callback( '/&amp;([A-Za-z]{2,8}[0-9]{0,2});/', 'wp_kses_named_entities', $string );
1748      $string = preg_replace_callback( '/&amp;#(0*[0-9]{1,7});/', 'wp_kses_normalize_entities2', $string );
1749      $string = preg_replace_callback( '/&amp;#[Xx](0*[0-9A-Fa-f]{1,6});/', 'wp_kses_normalize_entities3', $string );
1750  
1751      return $string;
1752  }
1753  
1754  /**
1755   * Callback for `wp_kses_normalize_entities()` regular expression.
1756   *
1757   * This function only accepts valid named entity references, which are finite,
1758   * case-sensitive, and highly scrutinized by HTML and XML validators.
1759   *
1760   * @since 3.0.0
1761   *
1762   * @global array $allowedentitynames
1763   *
1764   * @param array $matches preg_replace_callback() matches array.
1765   * @return string Correctly encoded entity.
1766   */
1767  function wp_kses_named_entities( $matches ) {
1768      global $allowedentitynames;
1769  
1770      if ( empty( $matches[1] ) ) {
1771          return '';
1772      }
1773  
1774      $i = $matches[1];
1775      return ( ! in_array( $i, $allowedentitynames ) ) ? "&amp;$i;" : "&$i;";
1776  }
1777  
1778  /**
1779   * Callback for `wp_kses_normalize_entities()` regular expression.
1780   *
1781   * This function helps `wp_kses_normalize_entities()` to only accept 16-bit
1782   * values and nothing more for `&#number;` entities.
1783   *
1784   * @access private
1785   * @ignore
1786   * @since 1.0.0
1787   *
1788   * @param array $matches `preg_replace_callback()` matches array.
1789   * @return string Correctly encoded entity.
1790   */
1791  function wp_kses_normalize_entities2( $matches ) {
1792      if ( empty( $matches[1] ) ) {
1793          return '';
1794      }
1795  
1796      $i = $matches[1];
1797      if ( valid_unicode( $i ) ) {
1798          $i = str_pad( ltrim( $i, '0' ), 3, '0', STR_PAD_LEFT );
1799          $i = "&#$i;";
1800      } else {
1801          $i = "&amp;#$i;";
1802      }
1803  
1804      return $i;
1805  }
1806  
1807  /**
1808   * Callback for `wp_kses_normalize_entities()` for regular expression.
1809   *
1810   * This function helps `wp_kses_normalize_entities()` to only accept valid Unicode
1811   * numeric entities in hex form.
1812   *
1813   * @since 2.7.0
1814   * @access private
1815   * @ignore
1816   *
1817   * @param array $matches `preg_replace_callback()` matches array.
1818   * @return string Correctly encoded entity.
1819   */
1820  function wp_kses_normalize_entities3( $matches ) {
1821      if ( empty( $matches[1] ) ) {
1822          return '';
1823      }
1824  
1825      $hexchars = $matches[1];
1826      return ( ! valid_unicode( hexdec( $hexchars ) ) ) ? "&amp;#x$hexchars;" : '&#x' . ltrim( $hexchars, '0' ) . ';';
1827  }
1828  
1829  /**
1830   * Determines if a Unicode codepoint is valid.
1831   *
1832   * @since 2.7.0
1833   *
1834   * @param int $i Unicode codepoint.
1835   * @return bool Whether or not the codepoint is a valid Unicode codepoint.
1836   */
1837  function valid_unicode( $i ) {
1838      return ( $i == 0x9 || $i == 0xa || $i == 0xd ||
1839              ( $i >= 0x20 && $i <= 0xd7ff ) ||
1840              ( $i >= 0xe000 && $i <= 0xfffd ) ||
1841              ( $i >= 0x10000 && $i <= 0x10ffff ) );
1842  }
1843  
1844  /**
1845   * Converts all numeric HTML entities to their named counterparts.
1846   *
1847   * This function decodes numeric HTML entities (`&#65;` and `&#x41;`).
1848   * It doesn't do anything with named entities like `&auml;`, but we don't
1849   * need them in the URL protocol whitelisting system anyway.
1850   *
1851   * @since 1.0.0
1852   *
1853   * @param string $string Content to change entities.
1854   * @return string Content after decoded entities.
1855   */
1856  function wp_kses_decode_entities( $string ) {
1857      $string = preg_replace_callback( '/&#([0-9]+);/', '_wp_kses_decode_entities_chr', $string );
1858      $string = preg_replace_callback( '/&#[Xx]([0-9A-Fa-f]+);/', '_wp_kses_decode_entities_chr_hexdec', $string );
1859  
1860      return $string;
1861  }
1862  
1863  /**
1864   * Regex callback for `wp_kses_decode_entities()`.
1865   *
1866   * @since 2.9.0
1867   * @access private
1868   * @ignore
1869   *
1870   * @param array $match preg match
1871   * @return string
1872   */
1873  function _wp_kses_decode_entities_chr( $match ) {
1874      return chr( $match[1] );
1875  }
1876  
1877  /**
1878   * Regex callback for `wp_kses_decode_entities()`.
1879   *
1880   * @since 2.9.0
1881   * @access private
1882   * @ignore
1883   *
1884   * @param array $match preg match
1885   * @return string
1886   */
1887  function _wp_kses_decode_entities_chr_hexdec( $match ) {
1888      return chr( hexdec( $match[1] ) );
1889  }
1890  
1891  /**
1892   * Sanitize content with allowed HTML KSES rules.
1893   *
1894   * This function expects slashed data.
1895   *
1896   * @since 1.0.0
1897   *
1898   * @param string $data Content to filter, expected to be escaped with slashes.
1899   * @return string Filtered content.
1900   */
1901  function wp_filter_kses( $data ) {
1902      return addslashes( wp_kses( stripslashes( $data ), current_filter() ) );
1903  }
1904  
1905  /**
1906   * Sanitize content with allowed HTML KSES rules.
1907   *
1908   * This function expects unslashed data.
1909   *
1910   * @since 2.9.0
1911   *
1912   * @param string $data Content to filter, expected to not be escaped.
1913   * @return string Filtered content.
1914   */
1915  function wp_kses_data( $data ) {
1916      return wp_kses( $data, current_filter() );
1917  }
1918  
1919  /**
1920   * Sanitizes content for allowed HTML tags for post content.
1921   *
1922   * Post content refers to the page contents of the 'post' type and not `$_POST`
1923   * data from forms.
1924   *
1925   * This function expects slashed data.
1926   *
1927   * @since 2.0.0
1928   *
1929   * @param string $data Post content to filter, expected to be escaped with slashes.
1930   * @return string Filtered post content with allowed HTML tags and attributes intact.
1931   */
1932  function wp_filter_post_kses( $data ) {
1933      return addslashes( wp_kses( stripslashes( $data ), 'post' ) );
1934  }
1935  
1936  /**
1937   * Sanitizes content for allowed HTML tags for post content.
1938   *
1939   * Post content refers to the page contents of the 'post' type and not `$_POST`
1940   * data from forms.
1941   *
1942   * This function expects unslashed data.
1943   *
1944   * @since 2.9.0
1945   *
1946   * @param string $data Post content to filter.
1947   * @return string Filtered post content with allowed HTML tags and attributes intact.
1948   */
1949  function wp_kses_post( $data ) {
1950      return wp_kses( $data, 'post' );
1951  }
1952  
1953  /**
1954   * Navigates through an array, object, or scalar, and sanitizes content for
1955   * allowed HTML tags for post content.
1956   *
1957   * @since 4.4.2
1958   *
1959   * @see map_deep()
1960   *
1961   * @param mixed $data The array, object, or scalar value to inspect.
1962   * @return mixed The filtered content.
1963   */
1964  function wp_kses_post_deep( $data ) {
1965      return map_deep( $data, 'wp_kses_post' );
1966  }
1967  
1968  /**
1969   * Strips all HTML from a text string.
1970   *
1971   * This function expects slashed data.
1972   *
1973   * @since 2.1.0
1974   *
1975   * @param string $data Content to strip all HTML from.
1976   * @return string Filtered content without any HTML.
1977   */
1978  function wp_filter_nohtml_kses( $data ) {
1979      return addslashes( wp_kses( stripslashes( $data ), 'strip' ) );
1980  }
1981  
1982  /**
1983   * Adds all KSES input form content filters.
1984   *
1985   * All hooks have default priority. The `wp_filter_kses()` function is added to
1986   * the 'pre_comment_content' and 'title_save_pre' hooks.
1987   *
1988   * The `wp_filter_post_kses()` function is added to the 'content_save_pre',
1989   * 'excerpt_save_pre', and 'content_filtered_save_pre' hooks.
1990   *
1991   * @since 2.0.0
1992   */
1993  function kses_init_filters() {
1994      // Normal filtering
1995      add_filter( 'title_save_pre', 'wp_filter_kses' );
1996  
1997      // Comment filtering
1998      if ( current_user_can( 'unfiltered_html' ) ) {
1999          add_filter( 'pre_comment_content', 'wp_filter_post_kses' );
2000      } else {
2001          add_filter( 'pre_comment_content', 'wp_filter_kses' );
2002      }
2003  
2004      // Post filtering
2005      add_filter( 'content_save_pre', 'wp_filter_post_kses' );
2006      add_filter( 'excerpt_save_pre', 'wp_filter_post_kses' );
2007      add_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
2008  }
2009  
2010  /**
2011   * Removes all KSES input form content filters.
2012   *
2013   * A quick procedural method to removing all of the filters that KSES uses for
2014   * content in WordPress Loop.
2015   *
2016   * Does not remove the `kses_init()` function from {@see 'init'} hook (priority is
2017   * default). Also does not remove `kses_init()` function from {@see 'set_current_user'}
2018   * hook (priority is also default).
2019   *
2020   * @since 2.0.6
2021   */
2022  function kses_remove_filters() {
2023      // Normal filtering
2024      remove_filter( 'title_save_pre', 'wp_filter_kses' );
2025  
2026      // Comment filtering
2027      remove_filter( 'pre_comment_content', 'wp_filter_post_kses' );
2028      remove_filter( 'pre_comment_content', 'wp_filter_kses' );
2029  
2030      // Post filtering
2031      remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
2032      remove_filter( 'excerpt_save_pre', 'wp_filter_post_kses' );
2033      remove_filter( 'content_filtered_save_pre', 'wp_filter_post_kses' );
2034  }
2035  
2036  /**
2037   * Sets up most of the KSES filters for input form content.
2038   *
2039   * First removes all of the KSES filters in case the current user does not need
2040   * to have KSES filter the content. If the user does not have `unfiltered_html`
2041   * capability, then KSES filters are added.
2042   *
2043   * @since 2.0.0
2044   */
2045  function kses_init() {
2046      kses_remove_filters();
2047  
2048      if ( ! current_user_can( 'unfiltered_html' ) ) {
2049          kses_init_filters();
2050      }
2051  }
2052  
2053  /**
2054   * Filters an inline style attribute and removes disallowed rules.
2055   *
2056   * @since 2.8.1
2057   *
2058   * @param string $css        A string of CSS rules.
2059   * @param string $deprecated Not used.
2060   * @return string Filtered string of CSS rules.
2061   */
2062  function safecss_filter_attr( $css, $deprecated = '' ) {
2063      if ( ! empty( $deprecated ) ) {
2064          _deprecated_argument( __FUNCTION__, '2.8.1' ); // Never implemented
2065      }
2066  
2067      $css = wp_kses_no_null( $css );
2068      $css = str_replace( array( "\n", "\r", "\t" ), '', $css );
2069  
2070      $allowed_protocols = wp_allowed_protocols();
2071  
2072      $css_array = explode( ';', trim( $css ) );
2073  
2074      /**
2075       * Filters list of allowed CSS attributes.
2076       *
2077       * @since 2.8.1
2078       * @since 4.4.0 Added support for `min-height`, `max-height`, `min-width`, and `max-width`.
2079       * @since 4.6.0 Added support for `list-style-type`.
2080       * @since 5.0.0 Added support for `background-image`.
2081       * @since 5.1.0 Added support for `text-transform`.
2082       * @since 5.2.0 Added support for `background-position` and `grid-template-columns`
2083       * @since 5.3.0 Added support for `grid`, `flex` and `column` layout properties.
2084       *              Extend `background-*` support of individual properties.
2085       * @since 5.3.1 Added support for gradient backgrounds.
2086       *
2087       * @param string[] $attr Array of allowed CSS attributes.
2088       */
2089      $allowed_attr = apply_filters(
2090          'safe_style_css',
2091          array(
2092              'background',
2093              'background-color',
2094              'background-image',
2095              'background-position',
2096              'background-size',
2097              'background-attachment',
2098              'background-blend-mode',
2099  
2100              'border',
2101              'border-radius',
2102              'border-width',
2103              'border-color',
2104              'border-style',
2105              'border-right',
2106              'border-right-color',
2107              'border-right-style',
2108              'border-right-width',
2109              'border-bottom',
2110              'border-bottom-color',
2111              'border-bottom-style',
2112              'border-bottom-width',
2113              'border-left',
2114              'border-left-color',
2115              'border-left-style',
2116              'border-left-width',
2117              'border-top',
2118              'border-top-color',
2119              'border-top-style',
2120              'border-top-width',
2121  
2122              'border-spacing',
2123              'border-collapse',
2124              'caption-side',
2125  
2126              'columns',
2127              'column-count',
2128              'column-fill',
2129              'column-gap',
2130              'column-rule',
2131              'column-span',
2132              'column-width',
2133  
2134              'color',
2135              'font',
2136              'font-family',
2137              'font-size',
2138              'font-style',
2139              'font-variant',
2140              'font-weight',
2141              'letter-spacing',
2142              'line-height',
2143              'text-align',
2144              'text-decoration',
2145              'text-indent',
2146              'text-transform',
2147  
2148              'height',
2149              'min-height',
2150              'max-height',
2151  
2152              'width',
2153              'min-width',
2154              'max-width',
2155  
2156              'margin',
2157              'margin-right',
2158              'margin-bottom',
2159              'margin-left',
2160              'margin-top',
2161  
2162              'padding',
2163              'padding-right',
2164              'padding-bottom',
2165              'padding-left',
2166              'padding-top',
2167  
2168              'flex',
2169              'flex-basis',
2170              'flex-direction',
2171              'flex-flow',
2172              'flex-grow',
2173              'flex-shrink',
2174  
2175              'grid-template-columns',
2176              'grid-auto-columns',
2177              'grid-column-start',
2178              'grid-column-end',
2179              'grid-column-gap',
2180              'grid-template-rows',
2181              'grid-auto-rows',
2182              'grid-row-start',
2183              'grid-row-end',
2184              'grid-row-gap',
2185              'grid-gap',
2186  
2187              'justify-content',
2188              'justify-items',
2189              'justify-self',
2190              'align-content',
2191              'align-items',
2192              'align-self',
2193  
2194              'clear',
2195              'cursor',
2196              'direction',
2197              'float',
2198              'overflow',
2199              'vertical-align',
2200              'list-style-type',
2201          )
2202      );
2203  
2204      /*
2205       * CSS attributes that accept URL data types.
2206       *
2207       * This is in accordance to the CSS spec and unrelated to
2208       * the sub-set of supported attributes above.
2209       *
2210       * See: https://developer.mozilla.org/en-US/docs/Web/CSS/url
2211       */
2212      $css_url_data_types = array(
2213          'background',
2214          'background-image',
2215  
2216          'cursor',
2217  
2218          'list-style',
2219          'list-style-image',
2220      );
2221  
2222      /*
2223       * CSS attributes that accept gradient data types.
2224       *
2225       */
2226      $css_gradient_data_types = array(
2227          'background',
2228          'background-image',
2229      );
2230  
2231      if ( empty( $allowed_attr ) ) {
2232          return $css;
2233      }
2234  
2235      $css = '';
2236      foreach ( $css_array as $css_item ) {
2237          if ( $css_item == '' ) {
2238              continue;
2239          }
2240  
2241          $css_item        = trim( $css_item );
2242          $css_test_string = $css_item;
2243          $found           = false;
2244          $url_attr        = false;
2245          $gradient_attr   = false;
2246  
2247          if ( strpos( $css_item, ':' ) === false ) {
2248              $found = true;
2249          } else {
2250              $parts        = explode( ':', $css_item, 2 );
2251              $css_selector = trim( $parts[0] );
2252  
2253              if ( in_array( $css_selector, $allowed_attr, true ) ) {
2254                  $found         = true;
2255                  $url_attr      = in_array( $css_selector, $css_url_data_types, true );
2256                  $gradient_attr = in_array( $css_selector, $css_gradient_data_types, true );
2257              }
2258          }
2259  
2260          if ( $found && $url_attr ) {
2261              // Simplified: matches the sequence `url(*)`.
2262              preg_match_all( '/url\([^)]+\)/', $parts[1], $url_matches );
2263  
2264              foreach ( $url_matches[0] as $url_match ) {
2265                  // Clean up the URL from each of the matches above.
2266                  preg_match( '/^url\(\s*([\'\"]?)(.*)(\g1)\s*\)$/', $url_match, $url_pieces );
2267  
2268                  if ( empty( $url_pieces[2] ) ) {
2269                      $found = false;
2270                      break;
2271                  }
2272  
2273                  $url = trim( $url_pieces[2] );
2274  
2275                  if ( empty( $url ) || $url !== wp_kses_bad_protocol( $url, $allowed_protocols ) ) {
2276                      $found = false;
2277                      break;
2278                  } else {
2279                      // Remove the whole `url(*)` bit that was matched above from the CSS.
2280                      $css_test_string = str_replace( $url_match, '', $css_test_string );
2281                  }
2282              }
2283          }
2284  
2285          if ( $found && $gradient_attr ) {
2286              $css_value = trim( $parts[1] );
2287              if ( preg_match( '/^(repeating-)?(linear|radial|conic)-gradient\(([^()]|rgb[a]?\([^()]*\))*\)$/', $css_value ) ) {
2288                  // Remove the whole `gradient` bit that was matched above from the CSS.
2289                  $css_test_string = str_replace( $css_value, '', $css_test_string );
2290              }
2291          }
2292  
2293          // Remove any CSS containing containing \ ( & } = or comments, except for url() useage checked above.
2294          if ( $found && ! preg_match( '%[\\\(&=}]|/\*%', $css_test_string ) ) {
2295              if ( $css != '' ) {
2296                  $css .= ';';
2297              }
2298  
2299              $css .= $css_item;
2300          }
2301      }
2302  
2303      return $css;
2304  }
2305  
2306  /**
2307   * Helper function to add global attributes to a tag in the allowed html list.
2308   *
2309   * @since 3.5.0
2310   * @since 5.0.0 Add support for `data-*` wildcard attributes.
2311   * @access private
2312   * @ignore
2313   *
2314   * @param array $value An array of attributes.
2315   * @return array The array of attributes with global attributes added.
2316   */
2317  function _wp_add_global_attributes( $value ) {
2318      $global_attributes = array(
2319          'aria-describedby' => true,
2320          'aria-details'     => true,
2321          'aria-label'       => true,
2322          'aria-labelledby'  => true,
2323          'aria-hidden'      => true,
2324          'class'            => true,
2325          'id'               => true,
2326          'style'            => true,
2327          'title'            => true,
2328          'role'             => true,
2329          'data-*'           => true,
2330      );
2331  
2332      if ( true === $value ) {
2333          $value = array();
2334      }
2335  
2336      if ( is_array( $value ) ) {
2337          return array_merge( $value, $global_attributes );
2338      }
2339  
2340      return $value;
2341  }


Generated: Sun Jan 19 01:00:03 2020 Cross-referenced by PHPXref 0.7.1