Escaping strings in WordPress

What is escaping, and why escape?

Put simply, escaping is making sure that data that is output to end users is rendered securely. Escaped data has had defensive measures applied to it, to prevent injection attacks (see the OWASP for a more in-depth reference here).

The reason we have to escape is that we can never assume user input is safe, never assume anything from the database is safe, never assume anything from third party sources is safe.

In the context of this article, escaping and encoding are both considered as escaping, however there is a difference. The OWASP specifically defines escaping as when you add special characters prior to specific characters to prevent misinterpretation, whereas encoding is where special characters are converted into another form to make sure they cannot be interpreted as dangerous.

Key principle: Escape as late as possible

Content should be escaped as late as possible – so ideally just prior to being ‘echoed’ or printed on a screen, for example. If escaping happens too early, then the escaped (or encoded) information could be misunderstood. Depending on what is happening to the data that could mean being double escaped for example.

What examples are covered in this post

There are multiple escaping functions within WordPress, listed here. This post doesn’t intend to cover many, but instead cover the principles / basics as they would apply to most.

The examples in this post assume all strings are being sent for / prepared to be able to be sent for translation. It also assumes the text we’re printing is being output on a page. However in expandable sections below each main example there are further examples without echoing or translating.

Please note as well, this article covers escaping specifically, not validation or sanitization.

Adding variables to the string

$adjective = 'awesome';

echo esc_html( sprintf( __( 'Hello %s World', 'text-domain' ), $adjective ); 

//Prints out 'Hello awesome World'
String not being echoed (click to view example):
$adjective = 'awesome';

$string = esc_html( sprintf( __( 'Hello %s World', 'text-domain' ), $adjective ); 

// $string now holds the value of the string, ready to be echoed separately.
String not being translated (click to view example):
$adjective = 'awesome';

echo esc_html( sprintf( 'Hello %s World' ), $adjective ); 

// Prints out 'Hello awesome World'

Factoring in plurals

$number_visitors = 4;

 echo esc_html( sprintf( _n( '%s person is here', '%s people are here', $number_visitors, 'text-domain' ), $number_visitors ) );

// Prints out '4 people are here'.
String not being echoed (click to view example):
$number_visitors = 4;

$string = esc_html( sprintf( _n( '%s person is here', '%s people are here', $number_visitors, 'text-domain' ), $number_visitors ) );

// $string now holds the value of the string, ready to be echoed separately.
String not being translated (click to view example):
$number_visitors = 4;

echo esc_html( sprintf( '%s people are here', $number_visitors );

// Prints out '4 people are here' - _n is not needed at all in this case: https://developer.wordpress.org/reference/functions/_n/

Adding multiple variables to a string

While the principles follow the examples above in most cases, it is also a good idea to consider argument swapping when using sprintf. Given the example sprintf( __( 'The %s said, hello %s world', 'text-domain' ), $animal, $adjective ); , we’ll have a problem if the arguments change position, such as sprintf( __( 'The %s said, hello %s world', 'text-domain' ), $adjective, $animal );. We would end up with a string such as The beautiful said, hello dog World.

The solution is to to include the placeholders in one of the following formats:

  • sprintf( __( 'The %1$s said, hello %2$s world', 'text-domain' ), $animal, $adjective );
  • sprintf( __( 'The %1$s said, hello %2$d world', 'text-domain' ), $animal, $adjective );
  • sprintf( __( "The %1\$s said, hello %2\$s world", 'text-domain' ), $animal, $adjective );

The final example is escaping the dollar sign with a backslash if the string is enclosed in double quotes.

Including HTML

$type_of_gift = 'birthday';

echo wp_kses( 
   sprintf( 
      __( 'Please <a target="_blank" href="https://example.com/login" >log in to your account</a> to view your %s gifts.', 'text-domain' ), esc_url( $type_of_gifts) ),
      array(
         'a'      => array(
            'href'   => array(),
         )
      )
   );

// Prints out 'Please log in to your account to view your birthday gifts.' (with 'log in to your account' being a link).
String not being echoed (click to view example):
$type_of_gift = 'birthday';

$string = wp_kses( 
   sprintf( 
      __( 'Please <a target="_blank" href="https://example.com/login" >log in to your account</a> to view your %s gifts.', 'text-domain' ), esc_url( $type_of_gifts) ),
      array(
         'a'      => array(
            'href'   => array(),
         )
      )
   );

// $string now holds the value of the string, ready to be echoed separately.
String not being translated (click to view example):
$type_of_gift = 'birthday';

echo wp_kses( 
   sprintf( 'Please <a target="_blank" href="https://example.com/login" >log in to your account</a> to view your %s gifts.', esc_url( $type_of_gifts) ),
      array(
         'a'      => array(
            'href'  => array(),
         )
      )
   );

// Prints out 'Please log in to your account to view your birthday gifts.' (with 'log in to your account' being a link).

Not adding variables to the string

esc_html_e( 'Hello World', 'text-domain'  );
// Prints out 'Hello World'

This is the same as echo esc_html( __( 'Hello World', 'text_domain' ) );, and echo esc_html( __( 'Hello World', 'text_domain' ) ); but just more concise.

Including HTML

echo wp_kses(
   __( 'Hello <strong>World!</strong>', 'text-domain'  ),
   array(
      'strong'      => array()
   )
);
// Prints out Hello World! (with 'World' being in bold)

Additional thoughts

There are multiple types of escaping functions available in WordPress. Following the general principles above, using the more appropriate escaping functions given the context should still work. As just one additional example:

$site_url = 'https://example.com';
echo esc_url( sprintf( __( 'The URL you are looking for is: %s', 'text-domain' ), $site_url ); 
//Prints out 'The URL you are looking for is: https://example.com'

Single or double quotes?

When including a string for escaping, the same best practices apply as elsewhere in WordPress when working with PHP – use single quotes quotes if you’re not evaluating anything in the string, double quotes otherwise. If you have an apostrophe that you want to keep in the string, then of course use double quotes so the quote doesn’t appear to be the end of the string, and renders properly.

Translations

An extra note: if you’re working on a larger project and your strings are being submitted for translation, for strings with placeholders always make sure the comment for translators is on the line just above the string in question (though if you’re using the WordPress Code Sniffer package that will be flagged otherwise). Example:

/* translators: %s is replaced with a location string */
$location = 'Scotland';
echo esc_html( sprintf( __( 'We are located in %s' ), $location ); 

//Prints out 'We are located in Scotland'

So there you go – several types of examples covering escaping most kinds of strings in WordPress.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Karen Attfield

Subscribe now to keep reading and get access to the full archive.

Continue reading