HTML entities. $title = preg_replace( '/&.+?;/', '', $title ); $title = str_replace( '.', '-', $title ); $title = preg_replace( '/[^%a-z0-9 _-]/', '', $title ); $title = preg_replace( '/\s+/', '-', $title ); $title = preg_replace( '|-+|', '-', $title ); $title = trim( $title, '-' ); return $title; } /** * Ensures a string is a valid SQL 'order by' clause. * * Accepts one or more columns, with or without a sort order (ASC / DESC). * e.g. 'column_1', 'column_1, column_2', 'column_1 ASC, column_2 DESC' etc. * * Also accepts 'RAND()'. * * @since 2.5.1 * * @param string $orderby Order by clause to be validated. * @return string|false Returns $orderby if valid, false otherwise. */ function sanitize_sql_orderby( $orderby ) { if ( preg_match( '/^\s*(([a-z0-9_]+|`[a-z0-9_]+`)(\s+(ASC|DESC))?\s*(,\s*(?=[a-z0-9_`])|$))+$/i', $orderby ) || preg_match( '/^\s*RAND\(\s*\)\s*$/i', $orderby ) ) { return $orderby; } return false; } /** * Sanitizes an HTML classname to ensure it only contains valid characters. * * Strips the string down to A-Z,a-z,0-9,_,-. If this results in an empty * string then it will return the alternative value supplied. * * @todo Expand to support the full range of CDATA that a class attribute can contain. * * @since 2.8.0 * * @param string $classname The classname to be sanitized. * @param string $fallback Optional. The value to return if the sanitization ends up as an empty string. * Default empty string. * @return string The sanitized value. */ function sanitize_html_class( $classname, $fallback = '' ) { // Strip out any percent-encoded characters. $sanitized = preg_replace( '|%[a-fA-F0-9][a-fA-F0-9]|', '', $classname ); // Limit to A-Z, a-z, 0-9, '_', '-'. $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $sanitized ); if ( '' === $sanitized && $fallback ) { return sanitize_html_class( $fallback ); } /** * Filters a sanitized HTML class string. * * @since 2.8.0 * * @param string $sanitized The sanitized HTML class. * @param string $classname HTML class before sanitization. * @param string $fallback The fallback string. */ return apply_filters( 'sanitize_html_class', $sanitized, $classname, $fallback ); } /** * Strips out all characters not allowed in a locale name. * * @since 6.2.1 * * @param string $locale_name The locale name to be sanitized. * @return string The sanitized value. */ function sanitize_locale_name( $locale_name ) { // Limit to A-Z, a-z, 0-9, '_', '-'. $sanitized = preg_replace( '/[^A-Za-z0-9_-]/', '', $locale_name ); /** * Filters a sanitized locale name string. * * @since 6.2.1 * * @param string $sanitized The sanitized locale name. * @param string $locale_name The locale name before sanitization. */ return apply_filters( 'sanitize_locale_name', $sanitized, $locale_name ); } /** * Converts lone & characters into `&` (a.k.a. `&`) * * @since 0.71 * * @param string $content String of characters to be converted. * @param string $deprecated Not used. * @return string Converted string. */ function convert_chars( $content, $deprecated = '' ) { if ( ! empty( $deprecated ) ) { _deprecated_argument( __FUNCTION__, '0.71' ); } if ( str_contains( $content, '&' ) ) { $content = preg_replace( '/&([^#])(?![a-z1-4]{1,8};)/i', '&$1', $content ); } return $content; } /** * Converts invalid Unicode references range to valid range. * * @since 4.3.0 * * @param string $content String with entities that need converting. * @return string Converted string. */ function convert_invalid_entities( $content ) { $wp_htmltranswinuni = array( '€' => '€', // The Euro sign. '' => '', '‚' => '‚', // These are Windows CP1252 specific characters. 'ƒ' => 'ƒ', // They would look weird on non-Windows browsers. '„' => '„', '…' => '…', '†' => '†', '‡' => '‡', 'ˆ' => 'ˆ', '‰' => '‰', 'Š' => 'Š', '‹' => '‹', 'Œ' => 'Œ', '' => '', 'Ž' => 'Ž', '' => '', '' => '', '‘' => '‘', '’' => '’', '“' => '“', '”' => '”', '•' => '•', '–' => '–', '—' => '—', '˜' => '˜', '™' => '™', 'š' => 'š', '›' => '›', 'œ' => 'œ', '' => '', 'ž' => 'ž', 'Ÿ' => 'Ÿ', ); if ( str_contains( $content, '' ) ) { $content = strtr( $content, $wp_htmltranswinuni ); } return $content; } /** * Balances tags if forced to, or if the 'use_balanceTags' option is set to true. * * @since 0.71 * * @param string $text Text to be balanced * @param bool $force If true, forces balancing, ignoring the value of the option. Default false. * @return string Balanced text */ function balanceTags( $text, $force = false ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid if ( $force || (int) get_option( 'use_balanceTags' ) === 1 ) { return force_balance_tags( $text ); } else { return $text; } } /** * Balances tags of string using a modified stack. * * @since 2.0.4 * @since 5.3.0 Improve accuracy and add support for custom element tags. * * @author Leonard Lin * @license GPL * @copyright November 4, 2001 * @version 1.1 * @todo Make better - change loop condition to $text in 1.2 * @internal Modified by Scott Reilly (coffee2code) 02 Aug 2004 * 1.1 Fixed handling of append/stack pop order of end text * Added Cleaning Hooks * 1.0 First Version * * @param string $text Text to be balanced. * @return string Balanced text. */ function force_balance_tags( $text ) { $tagstack = array(); $stacksize = 0; $tagqueue = ''; $newtext = ''; // Known single-entity/self-closing tags. $single_tags = array( 'area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source', 'track', 'wbr' ); // Tags that can be immediately nested within themselves. $nestable_tags = array( 'article', 'aside', 'blockquote', 'details', 'div', 'figure', 'object', 'q', 'section', 'span' ); // WP bug fix for comments - in case you REALLY meant to type '< !--'. $text = str_replace( '< !--', '< !--', $text ); // WP bug fix for LOVE <3 (and other situations with '<' before a number). $text = preg_replace( '#<([0-9]{1})#', '<$1', $text ); /** * Matches supported tags. * * To get the pattern as a string without the comments paste into a PHP * REPL like `php -a`. * * @see https://html.spec.whatwg.org/#elements-2 * @see https://html.spec.whatwg.org/multipage/custom-elements.html#valid-custom-element-name * * @example * ~# php -a * php > $s = [paste copied contents of expression below including parentheses]; * php > echo $s; */ $tag_pattern = ( '#<' . // Start with an opening bracket. '(/?)' . // Group 1 - If it's a closing tag it'll have a leading slash. '(' . // Group 2 - Tag name. // Custom element tags have more lenient rules than HTML tag names. '(?:[a-z](?:[a-z0-9._]*)-(?:[a-z0-9._-]+)+)' . '|' . // Traditional tag rules approximate HTML tag names. '(?:[\w:]+)' . ')' . '(?:' . // We either immediately close the tag with its '>' and have nothing here. '\s*' . '(/?)' . // Group 3 - "attributes" for empty tag. '|' . // Or we must start with space characters to separate the tag name from the attributes (or whitespace). '(\s+)' . // Group 4 - Pre-attribute whitespace. '([^>]*)' . // Group 5 - Attributes. ')' . '>#' // End with a closing bracket. ); while ( preg_match( $tag_pattern, $text, $regex ) ) { $full_match = $regex[0]; $has_leading_slash = ! empty( $regex[1] ); $tag_name = $regex[2]; $tag = strtolower( $tag_name ); $is_single_tag = in_array( $tag, $single_tags, true ); $pre_attribute_ws = isset( $regex[4] ) ? $regex[4] : ''; $attributes = trim( isset( $regex[5] ) ? $regex[5] : $regex[3] ); $has_self_closer = str_ends_with( $attributes, '/' ); $newtext .= $tagqueue; $i = strpos( $text, $full_match ); $l = strlen( $full_match ); // Clear the shifter. $tagqueue = ''; if ( $has_leading_slash ) { // End tag. // If too many closing tags. if ( $stacksize <= 0 ) { $tag = ''; // Or close to be safe $tag = '/' . $tag. // If stacktop value = tag close value, then pop. } elseif ( $tagstack[ $stacksize - 1 ] === $tag ) { // Found closing tag. $tag = ''; // Close tag. array_pop( $tagstack ); --$stacksize; } else { // Closing tag not at top, search for it. for ( $j = $stacksize - 1; $j >= 0; $j-- ) { if ( $tagstack[ $j ] === $tag ) { // Add tag to tagqueue. for ( $k = $stacksize - 1; $k >= $j; $k-- ) { $tagqueue .= ''; --$stacksize; } break; } } $tag = ''; } } else { // Begin tag. if ( $has_self_closer ) { /* * If it presents itself as a self-closing tag, but it isn't a known single-entity self-closing tag, * then don't let it be treated as such and immediately close it with a closing tag. * The tag will encapsulate no text as a result. */ if ( ! $is_single_tag ) { $attributes = trim( substr( $attributes, 0, -1 ) ) . "> 0 && ! in_array( $tag, $nestable_tags, true ) && $tagstack[ $stacksize - 1 ] === $tag ) { $tagqueue = ''; --$stacksize; } $stacksize = array_push( $tagstack, $tag ); } // Attributes. if ( $has_self_closer && $is_single_tag ) { // We need some space - avoid
and prefer
. $pre_attribute_ws = ' '; } $tag = '<' . $tag . $pre_attribute_ws . $attributes . '>'; // If already queuing a close tag, then put this tag on too. if ( ! empty( $tagqueue ) ) { $tagqueue .= $tag; $tag = ''; } } $newtext .= substr( $text, 0, $i ) . $tag; $text = substr( $text, $i + $l ); } // Clear tag queue. $newtext .= $tagqueue; // Add remaining text. $newtext .= $text; while ( $x = array_pop( $tagstack ) ) { $newtext .= ''; // Add remaining tags to close. } // WP fix for the bug with HTML comments. $newtext = str_replace( '< !--', '