* Callback function used by preg_replace.
* @param array $matches Populated by matches to preg_replace.
* @return string The text returned after esc_html if needed.
function wp_pre_kses_less_than_callback( $matches ) {
if ( false === strpos( $matches[0], '>' ) ) {
return esc_html( $matches[0] );
* Remove non-allowable HTML from parsed block attribute values when filtering
* @param string $string Content to be run through KSES.
* @param array[]|string $allowed_html An array of allowed HTML elements
* and attributes, or a context name
* @param string[] $allowed_protocols Array of allowed URL protocols.
* @return string Filtered text to run through KSES.
function wp_pre_kses_block_attributes( $string, $allowed_html, $allowed_protocols ) {
* `filter_block_content` is expected to call `wp_kses`. Temporarily remove
* the filter to avoid recursion.
remove_filter( 'pre_kses', 'wp_pre_kses_block_attributes', 10 );
$string = filter_block_content( $string, $allowed_html, $allowed_protocols );
add_filter( 'pre_kses', 'wp_pre_kses_block_attributes', 10, 3 );
* WordPress implementation of PHP sprintf() with filters.
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.
* @link https://www.php.net/sprintf
* @param string $pattern The string which formatted args are inserted.
* @param mixed ...$args Arguments to be formatted into the $pattern string.
* @return string The formatted string.
function wp_sprintf( $pattern, ...$args ) {
$len = strlen( $pattern );
while ( $len > $start ) {
// Last character: append and break.
if ( strlen( $pattern ) - 1 == $start ) {
$result .= substr( $pattern, -1 );
// Literal %: append and continue.
if ( '%%' === substr( $pattern, $start, 2 ) ) {
// Get fragment before next %.
$end = strpos( $pattern, '%', $start + 1 );
$fragment = substr( $pattern, $start, $end - $start );
// Fragment has a specifier.
if ( '%' === $pattern[ $start ] ) {
// Find numbered arguments or take the next one in order.
if ( preg_match( '/^%(\d+)\$/', $fragment, $matches ) ) {
$index = $matches[1] - 1; // 0-based array vs 1-based sprintf() arguments.
$arg = isset( $args[ $index ] ) ? $args[ $index ] : '';
$fragment = str_replace( "%{$matches[1]}$", '%', $fragment );
$arg = isset( $args[ $arg_index ] ) ? $args[ $arg_index ] : '';
* Filters a fragment from the pattern passed to wp_sprintf().
* If the fragment is unchanged, then sprintf() will be run on the fragment.
* @param string $fragment A fragment from the pattern.
* @param string $arg The argument.
$_fragment = apply_filters( 'wp_sprintf', $fragment, $arg );
if ( $_fragment != $fragment ) {
$fragment = sprintf( $fragment, (string) $arg );
// Append to result and move to next fragment.
* Localize list items before the rest of the content.
* The '%l' must be at the first characters can then contain the rest of the
* content. The list items will have ', ', ', and', and ' and ' added depending
* on the amount of list items in the $args parameter.
* @param string $pattern Content containing '%l' at the beginning.
* @param array $args List items to prepend to the content and replace '%l'.
* @return string Localized list items and rest of the content.
function wp_sprintf_l( $pattern, $args ) {
if ( '%l' !== substr( $pattern, 0, 2 ) ) {
* Filters the translated delimiters used by wp_sprintf_l().
* Placeholders (%s) are included to assist translators and then
* removed before the array of strings reaches the filter.
* Please note: Ampersands and entities should be avoided here.
* @param array $delimiters An array of translated delimiters.
/* translators: Used to join items in a list with more than 2 items. */
'between' => sprintf( __( '%1$s, %2$s' ), '', '' ),
/* translators: Used to join last two items in a list with more than 2 times. */
'between_last_two' => sprintf( __( '%1$s, and %2$s' ), '', '' ),
/* translators: Used to join items in a list with only 2 items. */
'between_only_two' => sprintf( __( '%1$s and %2$s' ), '', '' ),
$result = array_shift( $args );
if ( count( $args ) == 1 ) {
$result .= $l['between_only_two'] . array_shift( $args );
// Loop when more than two args.
$arg = array_shift( $args );
$result .= $l['between_last_two'] . $arg;
$result .= $l['between'] . $arg;
return $result . substr( $pattern, 2 );
* Safely extracts not more than the first $count characters from HTML string.
* UTF-8, tags and entities safe prefix extraction. Entities inside will *NOT*
* be counted as one character. For example & will be counted as 4, < as
* @param string $str String to get the excerpt from.
* @param int $count Maximum number of characters to take.
* @param string $more Optional. What to append if $str needs to be trimmed. Defaults to empty string.
* @return string The excerpt.
function wp_html_excerpt( $str, $count, $more = null ) {
$str = wp_strip_all_tags( $str, true );
$excerpt = mb_substr( $str, 0, $count );
// Remove part of an entity at the end.
$excerpt = preg_replace( '/&[^;\s]{0,6}$/', '', $excerpt );
if ( $str != $excerpt ) {
$excerpt = trim( $excerpt ) . $more;
* Add a Base url to relative links in passed content.
* By default it supports the 'src' and 'href' attributes. However this can be
* changed via the 3rd param.
* @global string $_links_add_base
* @param string $content String to search for links in.
* @param string $base The base URL to prefix to links.
* @param array $attrs The attributes which should be processed.
* @return string The processed content.
function links_add_base_url( $content, $base, $attrs = array( 'src', 'href' ) ) {
$_links_add_base = $base;
$attrs = implode( '|', (array) $attrs );
return preg_replace_callback( "!($attrs)=(['\"])(.+?)\\2!i", '_links_add_base', $content );
* Callback to add a base url to relative links in passed content.
* @global string $_links_add_base
* @param string $m The matched link.
* @return string The processed link.
function _links_add_base( $m ) {
// 1 = attribute name 2 = quotation mark 3 = URL.
return $m[1] . '=' . $m[2] .
( preg_match( '#^(\w{1,20}):#', $m[3], $protocol ) && in_array( $protocol[1], wp_allowed_protocols(), true ) ?
WP_Http::make_absolute_url( $m[3], $_links_add_base )
* Adds a Target attribute to all links in passed content.
* This function by default only applies to `<a>` tags, however this can be
* modified by the 3rd param.
* *NOTE:* Any current target attributed will be stripped and replaced.
* @global string $_links_add_target
* @param string $content String to search for links in.
* @param string $target The Target to add to the links.
* @param string[] $tags An array of tags to apply to.
* @return string The processed content.
function links_add_target( $content, $target = '_blank', $tags = array( 'a' ) ) {
global $_links_add_target;
$_links_add_target = $target;
$tags = implode( '|', (array) $tags );
return preg_replace_callback( "!<($tags)((\s[^>]*)?)>!i", '_links_add_target', $content );
* Callback to add a target attribute to all links in passed content.
* @global string $_links_add_target
* @param string $m The matched link.
* @return string The processed link.
function _links_add_target( $m ) {
global $_links_add_target;
$link = preg_replace( '|( target=([\'"])(.*?)\2)|i', '', $m[2] );
return '<' . $tag . $link . ' target="' . esc_attr( $_links_add_target ) . '">';
* Normalize EOL characters and strip duplicate whitespace.
* @param string $str The string to normalize.
* @return string The normalized string.
function normalize_whitespace( $str ) {
$str = str_replace( "\r", "\n", $str );
$str = preg_replace( array( '/\n+/', '/[ \t]+/' ), array( "\n", ' ' ), $str );
* Properly strip all HTML tags including script and style
* This differs from strip_tags() because it removes the contents of
* the `<script>` and `<style>` tags. E.g. `strip_tags( '<script>something</script>' )`
* will return 'something'. wp_strip_all_tags will return ''
* @param string $string String containing HTML tags
* @param bool $remove_breaks Optional. Whether to remove left over line breaks and white space chars
* @return string The processed string.
function wp_strip_all_tags( $string, $remove_breaks = false ) {
$string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string );
$string = strip_tags( $string );
$string = preg_replace( '/[\r\n\t ]+/', ' ', $string );
* Sanitizes a string from user input or from the database.
* - Checks for invalid UTF-8,
* - Converts single `<` characters to entities
* - Removes line breaks, tabs, and extra whitespace
* @see sanitize_textarea_field()
* @see wp_check_invalid_utf8()
* @see wp_strip_all_tags()
* @param string $str String to sanitize.
* @return string Sanitized string.
function sanitize_text_field( $str ) {
$filtered = _sanitize_text_fields( $str, false );
* Filters a sanitized text field string.
* @param string $filtered The sanitized string.
* @param string $str The string prior to being sanitized.
return apply_filters( 'sanitize_text_field', $filtered, $str );
* Sanitizes a multiline string from user input or from the database.
* The function is like sanitize_text_field(), but preserves
* new lines (\n) and other whitespace, which are legitimate
* input in textarea elements.
* @see sanitize_text_field()
* @param string $str String to sanitize.
* @return string Sanitized string.
function sanitize_textarea_field( $str ) {
$filtered = _sanitize_text_fields( $str, true );
* Filters a sanitized textarea field string.
* @param string $filtered The sanitized string.
* @param string $str The string prior to being sanitized.
return apply_filters( 'sanitize_textarea_field', $filtered, $str );
* Internal helper function to sanitize a string from user input or from the db
* @param string $str String to sanitize.
* @param bool $keep_newlines Optional. Whether to keep newlines. Default: false.
* @return string Sanitized string.
function _sanitize_text_fields( $str, $keep_newlines = false ) {
if ( is_object( $str ) || is_array( $str ) ) {
$filtered = wp_check_invalid_utf8( $str );
if ( strpos( $filtered, '<' ) !== false ) {
$filtered = wp_pre_kses_less_than( $filtered );
// This will strip extra whitespace for us.
$filtered = wp_strip_all_tags( $filtered, false );
// Use HTML entities in a special case to make sure no later
// newline stripping stage could lead to a functional tag.
$filtered = str_replace( "<\n", "<\n", $filtered );
if ( ! $keep_newlines ) {
$filtered = preg_replace( '/[\r\n\t ]+/', ' ', $filtered );
$filtered = trim( $filtered );
while ( preg_match( '/%[a-f0-9]{2}/i', $filtered, $match ) ) {
$filtered = str_replace( $match[0], '', $filtered );
// Strip out the whitespace that may now exist after removing the octets.
$filtered = trim( preg_replace( '/ +/', ' ', $filtered ) );
* i18n friendly version of basename()
* @param string $path A path.
* @param string $suffix If the filename ends in suffix this will also be cut off.
function wp_basename( $path, $suffix = '' ) {
return urldecode( basename( str_replace( array( '%2F', '%5C' ), '/', urlencode( $path ) ), $suffix ) );
// phpcs:disable WordPress.WP.CapitalPDangit.Misspelled, WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid -- 8-)
* Forever eliminate "Wordpress" from the planet (or at least the little bit we can influence).
* Violating our coding standards for a good function name.
* @param string $text The text to be modified.
* @return string The modified text.
function capital_P_dangit( $text ) {
// Simple replacement for titles.
$current_filter = current_filter();
if ( 'the_title' === $current_filter || 'wp_title' === $current_filter ) {
return str_replace( 'Wordpress', 'WordPress', $text );
// Still here? Use the more judicious replacement.
$dblq = _x( '“', 'opening curly double quote' );
array( ' Wordpress', '‘Wordpress', $dblq . 'Wordpress', '>Wordpress', '(Wordpress' ),
array( ' WordPress', '‘WordPress', $dblq . 'WordPress', '>WordPress', '(WordPress' ),
* @param string $mime_type Mime type
* @return string Sanitized mime type