In this article I will show you how to use a bloginfo
filter to add markup to your WordPress blog’s title and other metadata, without hurting your SEO.
The problem with the bloginfo filter: context!
The bloginfo() function can, unsurprisingly, be used to retrieve info about your blog! Say you need to show the name of the site in your theme. If you’re anywhere under the body
tag, you might do something like the following:
<h1><?php bloginfo( 'name' ); ?></h1>
Now say you want to use that same title in the HTML headers. Being an avid HTML markup expert, you might be tempted to do something like:
<head> <title><?php bloginfo( 'name' ); ?></title>
What’s the problem with the above? Often nothing.
Until you start using the bloginfo
filter. If you use it to modify the title text of the blog, you might want your modification to be reflected everywhere:
public function filter_bloginfo( $name, $show = null ) { if ( 'name' == $show ) { return "The $name blog"; } else { return $name; } } add_filter( 'bloginfo', 'filter_bloginfo', 10, 2 );
In which case you are fine.
But suppose you are using it to add markup; perhaps you’re writing a plugin that makes the title text bold (and you hate using CSS!). OK, this is a bad example, I know, but there are legitimate cases where you might want to add markup; perhaps you want to add a link onto the site title from your plugin.
In any case, for the sake of simplicity, let’s stick with our “Site title boldifier” plugin! Consider the following code:
public function filter_bloginfo( $name, $show = null ) { if ( 'name' == $show ) { return "<strong>$name</strong>"; } else { return $name; } } add_filter( 'bloginfo', 'filter_bloginfo', 10, 2 );
In this case you would want the strong
tag to show anywhere under the body
tag, but not in the HTML head’s title
or meta
tags. Otherwise you might get something horribly wrong, such as:
<head> <title><strong>The blog's title</strong></title>
Congrats! All your SEO efforts have gone out the window! The strong
tag will now show in the browser window title, in search engine results, in any place where OpenGraph matters (such as Facebook), in any Microdata you might be using, in your sitemap.xml, and generally, in all the wrong places. Not a very pro move!
The solution? Easy. Simply don’t use bloginfo()
in your theme’s meta tags; use get_bloginfo()
instead! Set the second parameter, filter = false
, and the string remains unfiltered.
<head> <title><?php echo get_bloginfo( 'name', false ); ?></title>
This is a good solution, if you, the theme author is also the site owner, and you know that this is what you want. But what if you’re not?
(Incidentally, this is not how the experts at Automattic are doing it. In at least the latest few /^twenty\w+teen$/ themes, the blog name is always filtered, wherever it is used. The Yoast SEO plugin also uses the filtered metadata for OpenGraph and other tags. They are correctly making the assumption that you are using the filter to modify the text content of your meta, not the tags. After all, HTML tags belong in theme templates, right?)
But what if you’re a plugin developer, and you have a legitimate reason for adding markup to the title from your plugin code, and your plugin has to work with any and all themes out there?
And as a theme developer, how do you know whether the modifications that the filter is doing to the blog’s title —or indeed to any bloginfo metadata— is intended to be shown in the site’s metadata or not?
You really don’t!
A possible compromise would be to run the filter in your theme, but then filter out any tags:
<head> <title><?php echo strip_tags( get_bloginfo( 'name' ) ); ?></title>
This would ensure that any text modifications are reflected in the site’s title and meta tags, while any tags will not show in the browser’s title for instance. At least that’s true for the metadata that you control as a theme developer.
But suppose if you’re filtering the title in your plugin to do something using HTML tags, like our <strong>
example above. You have taken extra care so that your theme shows the tags only where it’s supposed to. But the site owner has installed something like Yoast SEO which writes its own metadata in your HTML. You can’t control those meta
tags. And even if you can, you can’t do this for all the plugins out there! Again, you’re in trouble.
Don’t just filter all the things with the bloginfo filter!
If you think about it, what you are lacking is the information of where the result of your filter is to be used. Let’s rectify that:
private $_in_body = false; public function action_wp_head_finished() { $this->_in_body = true; } add_action( 'wp_head', array( &$this, 'action_wp_head_finished' ), PHP_INT_MAX ); public function action_wp_footer_started() { $this->_in_body = false; } add_action( 'wp_footer', array( &$this, 'action_wp_footer_started' ), 0 );
The above OO code maintains a boolean value that can now tell you when you’re in the body
tag of your markup and when you’re not. Notice how by setting our actions’ priorities to PHP_INT_MAX
and 0
, we can ensure that our boolean becomes true only after other actions bound to wp_head
have already run, and becomes false before other actions bound to wp_footer
have run. (PHP_INT_MAX
requires PHP 5.0.5, which shouldn’t be a problem. If it is, use a large integer literal instead.)
Now you, as a plugin developer, know when you’re in the body
tag and when you’re not. So you can reasonably decide whether to filter or not, without any knowledge of how the installed theme works:
public function filter_bloginfo( $name, $show = null ) { if ( 'name' == $show && $this->_in_body ) { return "<strong>$name</strong>"; } else { return $name; } } add_filter( 'bloginfo', array( &$this, 'filter_bloginfo' ), 10, 2 );
Good job! Now you know that your plugin is only adding HTML markup in places where they are valid and can be shown.
Now go filter some metadata!
Alexandros,
How to use your code (the last public function) in a child theme functions.php?
Assuming that you haven’t defined a class in your
functions.php
, you’d just declare normal functions and maybe use a global variable.Try this and let me know:
Make sure to replace “slug” with whatever slug you’re using to identify your child theme.
After removing some errors and some testings, the next code worked:
I commented the
add_action( 'wp_footer', 'twentyseventeen_action_wp_footer_started', 0 );
and this nothing changed. It is needed?You probably don’t need to know when the
body
tag closes, unless your theme or plugins somehow use the bloginfo function afterwards. I just included the footer hook for completeness, in most cases you can get away without it I guess.You can format your code with standard html <pre> and <code> tags. I’ve edited your reply to add those.
As a sidenote, I see that you use
twentyseventeen
as your slug. While this will work OK, keep in mind that this is the slug of your parent theme. Ideally you should have your own slug for your child theme. This is common practice. The idea here is to avoid any naming conflicts with other components, whether they are other plugins or themes or even your parent theme. If there was atwentyseventeen_filter_bloginfo
function in the twentyseventeen theme, you’d be in trouble.Alex, thank you very much! Again, for completeness, this is the final code I have used in my child theme’s functions.php (I hope the tag will work):
$twentyseventeen_child_in_body = false;
function twentyseventeen_child_action_wp_head_finished() {
global $twentyseventeen_child_in_body;
$twentyseventeen_child_in_body = true;
}
add_action( 'wp_head', 'twentyseventeen_child_action_wp_head_finished', PHP_INT_MAX );
function twentyseventeen_child_action_wp_footer_started() {
global $twentyseventeen_child_in_body;
$twentyseventeen_child_in_body = false;
}
add_action( 'wp_footer', 'twentyseventeen_child_action_wp_footer_started', 0 );
function twentyseventeen_child_filter_bloginfo( $name, $show = null ) {
global $twentyseventeen_child_in_body;
if ( 'name' == $show && $twentyseventeen_child_in_body ) {
$name = "<span class="info-style">Info</span>" . "<span class="psi-style">Psi</span>" . "<span class="md-style">.md</span>";
return "$name";
} else {
return $name;
}
}
add_filter( 'bloginfo', 'twentyseventeen_child_filter_bloginfo', 10, 2 );
The <pre> tag don’t worked as I expected, probably it must be used together with the the <code> tag. Anyway, I solved my problem. Thank you!
Lurie,
Glad to be of help.
take care
Can I credit you here?
Thanks 🙂