DEV Community

Josh Nederveld
Josh Nederveld

Posted on

WordPress enqueues don't support Google Fonts with more than one font-family.

The current Google Fonts stylesheet URLs syntax looks like this:

https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,700;1,400;1,700&family=Montserrat:wght@700;800&display=swap

If you use more than one font-family, the "family" parameter is reused. If you're using wp_register_style and wp_enqueue_style to add this stylesheet link to your site, you'll get this:

https://fonts.googleapis.com/css2?family=Montserrat:wght@700;800&display=swap&ver=5.4.1

The code that adds the version query string parameter uses parse_str, which returns code like this:

<?php
$str = "family=Lora:ital,wght@0,400;0,700;1,400;1,700&family=Montserrat:wght@700;800&display=swap";

parse_str($str, $output);
echo $output['family'];  // Montserrat:wght@700;800
echo $output['display']; // swap

I'm not sure why add_query_arg has to deconstruct the entire query string and rebuild it in order to add another query string parameter. In my opinion, WordPress also does a bad job of giving devs the opportunity to prevent this behavior with hooks. Consider this snippet from do_item in class.wp-styles.php:

$href = $this->_css_href( $src, $ver, $handle );
....
$tag = apply_filters( 'style_loader_tag', $tag, $handle, $href, $media );

The original $src variable is never accessible through hooks after being changed into $href. Consider _css_href, which do_item calls. This is where the query string arg is added:

if ( ! empty( $ver ) ) {
    $src = add_query_arg( 'ver', $ver, $src );
}

$src = apply_filters( 'style_loader_src', $src, $handle );
return esc_url( $src );

We're given a filter, but it doesn't include the original $src, because it's been overwritten by add_query_arg. If the original variables were preserved and passed in these hooks, then we'd be able to prevent this behavior.

Another way to do it is to change the behavior of wp_parse_str. There's a good comment on php.net outlining the issue from Evan K from 12 years ago:

It bears mentioning that the parse_str builtin does NOT process a query string in the CGI standard way, when it comes to duplicate fields. If multiple fields of the same name exist in a query string, every other web processing language would read them into an array, but PHP silently overwrites them:

<?php
# silently fails to handle multiple values
parse_str('foo=1&foo=2&foo=3');

# the above produces:
$foo = array('foo' => '3');
?>

Instead, PHP uses a non-standards compliant practice of including brackets in fieldnames to achieve the same effect.

<?php
# bizarre php-specific behavior
parse_str('foo[]=1&foo[]=2&foo[]=3');

# the above produces:
$foo = array('foo' => array('1', '2', '3') );
?>

This can be confusing for anyone who's used to the CGI standard, so keep it in mind.

But of course, changing the Google Fonts URL to https://fonts.googleapis.com/css2?family[]=Lora:ital,wght@0,400;0,700;1,400;1,700&family[]=Montserrat:wght@700;800&display=swap results in a 404, so there's no easy fix there.

How to Fix

There are a few ways to fix this. I grabbed Evan's proper_parse_str function and plugged it into wp_parse_str and it worked fine for this case, but I don't know where all else wp_parse_str is used, so it makes me hesitant to propose that as a solution.

For the meantime, we can:

  1. Set $ver to null when registering and/or enqueueing the stylesheet.
  2. Enqueue fonts in separate files, rather than one.
  3. Petition WordPress to change the behavior of wp_parse_str.

Top comments (1)

Collapse
 
jilljj profile image
Jill Johnson

Yep. I blew up my site a few times trying to find the solution we used to have with html original version.