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:
- Set
$ver
tonull
when registering and/or enqueueing the stylesheet. - Enqueue fonts in separate files, rather than one.
- Petition WordPress to change the behavior of
wp_parse_str
.
Top comments (1)
Yep. I blew up my site a few times trying to find the solution we used to have with html original version.