Basically a place that Chris can post solutions to problems so he can easily find them later https://cjhaas.com Just another WordPress site Fri, 25 Oct 2019 09:28:57 +0000 en-US hourly 1 https://wordpress.org/?v=5.2.4 mySQL Recursive CTE https://cjhaas.com/2019/10/25/mysql-recursive-cte/?utm_source=rss&utm_medium=rss&utm_campaign=mysql-recursive-cte https://cjhaas.com/2019/10/25/mysql-recursive-cte/#respond Fri, 25 Oct 2019 09:28:14 +0000 https://cjhaas.com/?p=947 Almost 10 years ago I asked this question on Stack Overflow about how to find the deepest nodes in a recursive table using a Common Table Expression (CTE). Fast forward to today, and I need a mySQL equivalent, specifically for MariaDB. Please read the original question to get the gist, below is the table, records […]

The post mySQL Recursive CTE appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Almost 10 years ago I asked this question on Stack Overflow about how to find the deepest nodes in a recursive table using a Common Table Expression (CTE). Fast forward to today, and I need a mySQL equivalent, specifically for MariaDB.

Please read the original question to get the gist, below is the table, records and CTE converted to mySQL (tested against MariaDB 10.2.22).

Create the table

CREATE TABLE Employees
(
   EmployeeId int NOT NULL AUTO_INCREMENT,
   ParentEmployeId int NULL,
   Name varChar(255),
   PRIMARY KEY (EmployeeId),
   CONSTRAINT FOREIGN KEY (ParentEmployeId) REFERENCES Employees(EmployeeId)
)

Insert rows

INSERT INTO Employees VALUES (1, NULL, 'Company President 1');
INSERT INTO Employees VALUES (2, NULL, 'Company President 2');
INSERT INTO Employees VALUES (3, 1, 'Company President 1 - VP');
INSERT INTO Employees VALUES (4, 2, 'Company President 2 - VP');
INSERT INTO Employees VALUES (5, 3, 'Company President 1 - VP - Secretary');
INSERT INTO Employees VALUES (6, 4, 'Company President 2 - VP - Secretary');
INSERT INTO Employees VALUES (7, 5, 'Company President 1 - VP - Secretary - Sandwich Delivery');

Query

WITH RECURSIVE EmployeeRec AS
(
    SELECT
        EmployeeId AS Master,
        EmployeeId,
        ParentEmployeId,
        Name,
        1 as Level
    FROM
        Employees
    WHERE
        ParentEmployeId IS NULL
        
    UNION ALL

    SELECT
         R.Master,
         E.EmployeeId,
         E.ParentEmployeId,
         E.Name,
         R.Level + 1
    FROM
         Employees E
    INNER JOIN
         EmployeeRec R
    ON
         E.ParentEmployeId = R.EmployeeId
)
SELECT
    er.Master,
    er.EmployeeId,
    er.ParentEmployeId,
    er.Name,
    m.Level
FROM
    EmployeeRec er
INNER JOIN
    (
      SELECT
            Master,
            MAX(Level) AS Level
      FROM
            EmployeeRec
      GROUP BY
            Master
   ) m
ON
    m.Master = er.Master
   AND
    m.Level = er.Level

Notes

  • For comparison sake, I left the typo of ParentEmployeId in, even though that messed me up several times as I typed it
  • In the result set, Master represents the root parent, and Level represents the depth (total number of nodes in the specific chain, including this node)

The post mySQL Recursive CTE appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/10/25/mysql-recursive-cte/feed/ 0
WPDB Insert automatically changing string format to number https://cjhaas.com/2019/10/16/wpdb-insert-automatically-changing-string-format-to-number/?utm_source=rss&utm_medium=rss&utm_campaign=wpdb-insert-automatically-changing-string-format-to-number https://cjhaas.com/2019/10/16/wpdb-insert-automatically-changing-string-format-to-number/#respond Wed, 16 Oct 2019 14:22:35 +0000 https://cjhaas.com/?p=941 The third parameter to wpdb::insert() is an array of formats that defaults to %s unless they are overridden in wpdb::$field_types. This is last part is very important. When WordPress boots, it statically sets that list to fields that should always be numbers unless someone specifies an explicit format as the third parameter. Currently there these […]

The post WPDB Insert automatically changing string format to number appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
The third parameter to wpdb::insert() is an array of formats that defaults to %s unless they are overridden in wpdb::$field_types. This is last part is very important. When WordPress boots, it statically sets that list to fields that should always be numbers unless someone specifies an explicit format as the third parameter. Currently there these 34 in the file (I was tripped up by object_id which my system is storing as a string).

    'post_author'      => '%d',
    'post_parent'      => '%d',
    'menu_order'       => '%d',
    'term_id'          => '%d',
    'term_group'       => '%d',
    'term_taxonomy_id' => '%d',
    'parent'           => '%d',
    'count'            => '%d',
    'object_id'        => '%d',
    'term_order'       => '%d',
    'ID'               => '%d',
    'comment_ID'       => '%d',
    'comment_post_ID'  => '%d',
    'comment_parent'   => '%d',
    'user_id'          => '%d',
    'link_id'          => '%d',
    'link_owner'       => '%d',
    'link_rating'      => '%d',
    'option_id'        => '%d',
    'blog_id'          => '%d',
    'meta_id'          => '%d',
    'post_id'          => '%d',
    'user_status'      => '%d',
    'umeta_id'         => '%d',
    'comment_karma'    => '%d',
    'comment_count'    => '%d',
    // multisite:
    'active'           => '%d',
    'cat_id'           => '%d',
    'deleted'          => '%d',
    'lang_id'          => '%d',
    'mature'           => '%d',
    'public'           => '%d',
    'site_id'          => '%d',
    'spam'             => '%d',

You might be tempted to do something like wpdb->insert('table', $values, ['object_id' => '%s']) but that won’t work, either. The logic for looking up a format by columns is only used for the core fields, which you can see here.

If everything to be inserted is a string (or should be inserted as a string) then you can just pass %s or [%s] as the third parameter (the former gets converted to the latter) and WordPress will use it for items.

Unfortunately, if anything else needs to be inserted in a non-string way, you need to explicitly set each placeholder.

(Okay, technically, if your first parameter is a string, you only need to provide formats up till the last non-string, because the logic for missing placeholders is to use the first provided one, but that’s just begging to either break or get yourself punched, possibly by your future self.

The post WPDB Insert automatically changing string format to number appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/10/16/wpdb-insert-automatically-changing-string-format-to-number/feed/ 0
WP 5.3 – Breaking changes https://cjhaas.com/2019/10/09/wp-5-3-breaking-changes/?utm_source=rss&utm_medium=rss&utm_campaign=wp-5-3-breaking-changes https://cjhaas.com/2019/10/09/wp-5-3-breaking-changes/#respond Wed, 09 Oct 2019 13:31:20 +0000 https://cjhaas.com/?p=935 This is pretty big, and I’m guessing that a good number of people that have WP_DEBUG_LOG turned on (like me, sorry Scott) might see it filling up fast when 5.3 lands, and there might be some fatal errors, too. The discussion is here: https://core.trac.wordpress.org/ticket/47678 The gist is that they are introducing some modern PHP-isms (where […]

The post WP 5.3 – Breaking changes appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
This is pretty big, and I’m guessing that a good number of people that have WP_DEBUG_LOG turned on (like me, sorry Scott) might see it filling up fast when 5.3 lands, and there might be some fatal errors, too.

The discussion is here:

https://core.trac.wordpress.org/ticket/47678

The gist is that they are introducing some modern PHP-isms (where “modern” is 5.6 from 2014) and although they fix some performance issues, they do break compatibility with some code.

The fix is overall pretty trivial, basically any code that is overriding core’s code that is changing will need to update function signatures (and it appears that there were about 50 cases of this).

For instance, the old signature might be:

function walk( $elements, $max_depth )

And the new signature would be:

function walk( $elements, $max_depth, ...$args )

No actual logic needs to be changed, just the function/method signature. This is similar to when PHP enforced the __construct() method in 7.0.

The most problematic change is a fatal error (not a warning) with the Walker class which WordPress uses for a lot of different things.

The recommendation is to grep for something like:

find /var/www/ -type f -name "*.php" | grep -v "/wp-includes/" | xargs grep -PHns "function (?:paged_)?walk\s*\(\s*\\$"

Ignore anything from core and only focus on plugins and themes. There will false-positives (specifically there’s something in ACF that matches this but isn’t affected) but you are generally looking for something that has two parameters, $elements and $max_depth. Then just update with the signature above.

For other warnings no related to the Walker class you might just need to poke around and see what’s changed.

The post WP 5.3 – Breaking changes appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/10/09/wp-5-3-breaking-changes/feed/ 0
Turn on spellcheck for any web page https://cjhaas.com/2019/07/09/turn-on-spellcheck-for-any-web-page/?utm_source=rss&utm_medium=rss&utm_campaign=turn-on-spellcheck-for-any-web-page https://cjhaas.com/2019/07/09/turn-on-spellcheck-for-any-web-page/#respond Tue, 09 Jul 2019 13:38:51 +0000 https://cjhaas.com/?p=930 Bring up the console and enter these two: document.body.contentEditable='true';document.body.spellcheck='true';

The post Turn on spellcheck for any web page appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Bring up the console and enter these two:

document.body.contentEditable='true';document.body.spellcheck='true';

The post Turn on spellcheck for any web page appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/07/09/turn-on-spellcheck-for-any-web-page/feed/ 0
MySQL DUAL table https://cjhaas.com/2019/05/21/mysql-dual-table/?utm_source=rss&utm_medium=rss&utm_campaign=mysql-dual-table https://cjhaas.com/2019/05/21/mysql-dual-table/#respond Tue, 21 May 2019 22:03:44 +0000 https://cjhaas.com/?p=925 Here’s code that should work on any MySQL server regardless of databases and tables: SELECT 1 FROM DUAL Apparently they copied it from Oracle.

The post MySQL DUAL table appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Here’s code that should work on any MySQL server regardless of databases and tables:

SELECT 1 FROM DUAL

Apparently they copied it from Oracle.

The post MySQL DUAL table appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/21/mysql-dual-table/feed/ 0
PHP Include Path Surprises https://cjhaas.com/2019/05/21/php-include-path-surprises/?utm_source=rss&utm_medium=rss&utm_campaign=php-include-path-surprises https://cjhaas.com/2019/05/21/php-include-path-surprises/#respond Tue, 21 May 2019 19:36:00 +0000 https://cjhaas.com/?p=916 While diagnosing a potential unconfirmed problem with a certain popular WordPress plugin I did something that every good developer should do every once in a while, which is to humble themselves and read the most basic and obvious documentation for the simplest parts of a language or framework that you know by heart. If you […]

The post PHP Include Path Surprises appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
While diagnosing a potential unconfirmed problem with a certain popular WordPress plugin I did something that every good developer should do every once in a while, which is to humble themselves and read the most basic and obvious documentation for the simplest parts of a language or framework that you know by heart.

If you aren’t a programmer, what I mean is the equivalent of reading a driver’s education book even though you’ve been driving for years or decades. And I don’t necessarily mean “what’s the yellow blinking arrow mean”, I mean basic like “how do I make the car go left?”

And when I say read, I don’t mean skim, I really mean thoroughly read. For instance, to make a car go left, you don’t “turn the wheel left” but instead, when going forward you turn the wheel counter/anti-clockwise! The top of the wheel goes left but the bottom of the wheel goes right. Stupid and obvious, I know. But how about when going backward. Puzzle that through, I think there’s actually a two-part answer there (for how most people use reverse at least). And then, just for fun, work through the scenario with a trailer (although I don’t think they teach that in driver’s ed).

Anyhow, back to code.

I’ve always known that there have been peculiarities about PHP’s include system, and I’ve always tried to use absolute paths just to avoid the peculiarities, but I never really dove into what the specific nuances were until this morning.

The below code is talking about include however it applies to require and require_once just the same.

From the documentation:

Files are included based on the file path given or, if none is given, the include_path specified. If the file isn’t found in the include_path, include will finally check in the calling script’s own directory and the current working directory before failing

That might be a “duh” moment for some people, and maybe you’ll stop reading this because you think I’m an idiot for writing about obvious stuff.

But here’s the problem: what do they mean by “file path” exactly? Going to the next paragraph in the documentation it says:

If a path is defined — whether absolute (starting with a drive letter or \ on Windows, or / on Unix/Linux systems) or relative to the current directory (starting with . or ..)

Do you get where I’m going yet?

Let’s look at a common bit of code:

require_once 'file.php';

The second quote above basically says that for the argument to this code to be considered path-based, the string must begin with either a slash (be absolute) or one or two periods (relative). If these neither of these two conditions are met, the current folder will not be looked at until after the include path is checked.

Read that again. Or let me say it again. If the argument to require/include doesn’t start with a slash or dot, multiple directories will be (potentially) scanned before the current directory.

Let’s make a quick proof-of-concept demo:

<?php

//Make a subfolder if it doesn't exist
if(!is_dir('sub_folder')){mkdir('sub_folder');}

//The PHP script that we'll write to each file, just echoing the script's directory
$contents = '<?php echo __DIR__ . "\n";';

//Create two files, one "here" and one in a sub folder
file_put_contents('sub_folder/file.php', $contents);
file_put_contents('file.php', $contents);

//Unless you have this file in your own path, these two should be the same
require 'file.php';
require './file.php';

//Change the current include path
set_include_path(__DIR__ . '/sub_folder');

//Re-run the exact same code as above, the first result is now different than the second!
require 'file.php';
require './file.php';

You can save that somewhere as runner.php and then run php -f runner.php from that directory.

What do you get when you run it? Here’s what I get:

/home/demo_user
/home/demo_user
/home/demo_user/sub_folder
/home/demo_user

Did you notice that third one? That’s the call to just require 'file.php' which, because it doesn’t start with a slash or a period, searched the include path first looking for a match, not the current folder!

Now, if you’re running in your own private code base and server, this might not be a big deal. The perf problems of this are probably very negligible, too, to the point that I don’t think you’d ever notice.

But if you’re creating code for other people to consume, you cannot guarantee that they don’t have an include path set, and you also cannot guarantee that they don’t have a file with your exact structure in that path. Probably not, or, hopefully not.

But the fix is actually easy. Just always use absolute or relative paths.

If you want a file in the current directory, just always prepend with ./. (However, scroll to the additional problem to see why this breaks, too!)

Or, better yet, in whatever you think should be your “entrance file(s)”, define a constant that holds the value of __DIR__ for that file and use that with concatenation for your files.

//In your entrance file
define('MY_APP_ROOT', __DIR__);

//In your other files
require_once MY_APP_ROOT . '/path/to/my/file.php';

That’s exactly what WordPress does.

Even better than all of this is to avoid require and include in general and always use composer to handle things on your behalf, but that’s not always an option.

A thought from a security perspective

As I write this, I’m thinking about a pretty interesting exploit that would be hard-ish to debug. First, you’d need to get code onto someone’s server and second, you’d need to get it into the execution path. We’ll wave our hand and pretend that’s done somehow. Once that happens, that code could set the include path to include its own code just by naming files the same as other code that gets included with a true path. This exploit code could even then re-require the legitimate code, effectively making it a trojan that no one notices (depending on what the payload does).

Once again, they’d need to have elevation to start with, but once in, this would be almost impossible to notice if the payload is silent enough, like logging passwords or something.

An additional problem

I’m going to take the initial code same I gave and tweak it a little bit. This time, we’re going to create an extra sub folder that has the same file.php with the exact same contents as the others. Also in that folder we’re going to create one additional file with the only job of running our main original code via require.

(Also, I’m using better ABS paths for creating things because with the jumping around for demo purposes things get weird otherwise. The actual spirit of the original demo is still here, just more specific.)

 <?php

//Save this file as runner.php

//We're going to use two sub folders
$FOLDER_ALPHA  = __DIR__ . '/sub_folder';
$FOLDER_RUNNER = __DIR__ . '/sub_runner';

//Make them if they don't exist
if(!is_dir($FOLDER_ALPHA)){mkdir($FOLDER_ALPHA);}
if(!is_dir($FOLDER_RUNNER)){mkdir($FOLDER_RUNNER);}

//The PHP script that we'll write to each file, just echo'ing the script's directory
$contents = '<?php echo __DIR__ . "\n";';

//Create three files, one "here" and one in each sub folder
file_put_contents("${FOLDER_ALPHA}/file.php",    $contents);
file_put_contents("${FOLDER_RUNNER}/file.php",   $contents);
file_put_contents('file.php',                    $contents);

$second_runner_contents = '<?php require dirname(__DIR__) . "/runner.php";';
file_put_contents("${FOLDER_RUNNER}/runner.php", $second_runner_contents);

//Unless you have this file in your own path, these two should be the same
require 'file.php';
require './file.php';

//Change the current include path
set_include_path($FOLDER_ALPHA);

//Re-run the exact same code as above, the first result is now different than the second!
require 'file.php';
require './file.php';

Now, from a command line where you saved the above run php -f runner.php and you’ll get something like:

/home/demo_user
/home/demo_user
/home/demo_user/sub_folder
/home/demo_user

No surprises, the previous code talked about this. From that same folder now run php -f sub_runner/runner.php and you’ll get:

/home/demo_user
/home/demo_user
/home/demo_user/sub_folder
/home/demo_user

Once again, still the same.

Lastly, we’re going to cd into our new inner folder and run that runner:

cd sub_runner/
php -f runner.php

Which now gives:

/home/demo_user/sub_runner
/home/demo_user/sub_runner
/home/demo_user/sub_folder
/home/demo_user/sub_runner

Wow, that’s new. We’re invoking almost the exact same code, just in a slightly different way. This time, we’re tripping a different rule from the include documentation (same as above, sub-setted below):

include will finally check in the calling script’s own directory and the current working directory before failing

I’m not actually sure the documentation is correct here, or, more specifically, I can’t get “calling scripts own directory” to be any different than “current working directory”. If someone can get me an actual working difference I’d love to see that.

Back to the results, because are working directory was changed, and because there was a new magic-named file in that folder, our main script used that file for the non-path version. But also notice that the explicitly relative paths also broke!

The path ./file.php isn’t relative to the file it is written in, it is relative to the current working directory! This basically means that the only absolutely safe method to include files is to always use the absolute path format, unless you have 100% absolute control over everything.

The post PHP Include Path Surprises appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/21/php-include-path-surprises/feed/ 0
Drupal Status Color Codes https://cjhaas.com/2019/05/16/drupal-status-color-codes/?utm_source=rss&utm_medium=rss&utm_campaign=drupal-status-color-codes https://cjhaas.com/2019/05/16/drupal-status-color-codes/#respond Thu, 16 May 2019 18:51:13 +0000 https://cjhaas.com/?p=910 This has bugged me for a long time. If you visit any project issue page on Drupal.org you’ll find a bunch of different color-coded table rows. What the heck do the colors mean!!! It has always bothered me, but today I decided to finally dig in. This process wasn’t hard in any way, I just […]

The post Drupal Status Color Codes appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
This has bugged me for a long time. If you visit any project issue page on Drupal.org you’ll find a bunch of different color-coded table rows.

Example of multiple issues on Drupal.org's website for the Feeds module showing many different colored rows, where the specific usage of color for the row isn't obvious.

What the heck do the colors mean!!!

It has always bothered me, but today I decided to finally dig in. This process wasn’t hard in any way, I just really wanted a key somewhere to understand the rationality behind the order. Below I present that.

1Active#f9f9f9
13Needs work#ffece8
14Reviewed & tested by the community#f1ffe8
15Patch (to be ported)#f1ffe8
16Postponed (maintainer needs more info)#eff1fe
17Closed (outdated)#eff1fe
18Closed (cannot reproduce)#fddddd
2Fixed#d7ffd8
3Closed (duplicate)#eff1fe
4Postponed#eff1fe
5Closed (won’t fix)#eff1fe
6Closed (works as designed)#eff1fe
7Closed (fixed)#fddddd
8Needs review#ffffdd
  • Generally speaking, green-like status represent done or mostly done things
  • Yellow means a review is needed
  • Blue-ish/purple-ish is “not going to fix” for many different reasons
  • Red-ish is either fixed or confusingly, needs to be worked on (the latter has a different shade of red but it is hard to tell unless it is side-by-side with the former)
  • Grey is an active bug

To be clear, this isn’t a judgment of any of the statuses, this is just a key which I needed.

The post Drupal Status Color Codes appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/16/drupal-status-color-codes/feed/ 0
You have requested a non-existent service “media_entity.cli” https://cjhaas.com/2019/05/07/you-have-requested-a-non-existent-service-media_entity-cli/?utm_source=rss&utm_medium=rss&utm_campaign=you-have-requested-a-non-existent-service-media_entity-cli https://cjhaas.com/2019/05/07/you-have-requested-a-non-existent-service-media_entity-cli/#respond Tue, 07 May 2019 18:58:42 +0000 https://cjhaas.com/?p=907 We upgraded a Drupal site to 8.7 that had used media_entity previously and we needed to get it converted over to core’s media module. We followed all of the directions but we kept running into the error: You have requested a non-existent service "media_entity.cli" After a bunch of trial and error we decided to just […]

The post You have requested a non-existent service “media_entity.cli” appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
We upgraded a Drupal site to 8.7 that had used media_entity previously and we needed to get it converted over to core’s media module. We followed all of the directions but we kept running into the error:

You have requested a non-existent service "media_entity.cli"

After a bunch of trial and error we decided to just hack the module by adding a test for the existence of the service before consuming it.

In the file media_entity/media_entity.drush.inc before the line the contains \Drupal::service('media_entity.cli') we added:

if(!\Drupal::hasService('media_entity.cli.cli')){
return TRUE;
}

The post You have requested a non-existent service “media_entity.cli” appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2019/05/07/you-have-requested-a-non-existent-service-media_entity-cli/feed/ 0
WordPress CSV upload problems https://cjhaas.com/2018/12/18/wordpress-csv-upload-problems/?utm_source=rss&utm_medium=rss&utm_campaign=wordpress-csv-upload-problems https://cjhaas.com/2018/12/18/wordpress-csv-upload-problems/#respond Tue, 18 Dec 2018 17:46:00 +0000 http://cjhaas.com/?p=899 The post WordPress CSV upload problems appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
Just an FYI, the WordPress 5.0.1/4.9.9 (and others back to 3.7.x) security update broke uploads for CSV files under certain circumstances.

The trac ticket is here and the workaround is here.

This note says that it will be addressed in 5.0.3 which will probably correlate to 4.9.10 (unless 5.0.2 also includes security updates).

The post WordPress CSV upload problems appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
https://cjhaas.com/2018/12/18/wordpress-csv-upload-problems/feed/ 0
I hate Font Awesome like I hate jQuery https://cjhaas.com/2018/12/07/i-hate-font-awesome-like-i-hate-jquery/?utm_source=rss&utm_medium=rss&utm_campaign=i-hate-font-awesome-like-i-hate-jquery Fri, 07 Dec 2018 18:20:41 +0000 http://cjhaas.com/?p=888 The post I hate Font Awesome like I hate jQuery appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>
First off, if you don’t know me then you might be offended or enraged by that statement. Please, keep reading.

Font Awesome is a truly great collection of icons that can be easily deployed with a simple CSS or JS include. A ton of work has gone into it to make it as small as possible and they’ve even encouraged use of things like subresource integrity which is just great to see. Font Awesome is truly awesome.

I guess to be fair, I don’t actually hate Font Awesome, I hate that people use it blindly.

Once again, if you don’t know me, I’m a penny pincher when it comes to HTTP requests, DNS lookups and bytes crossing the wire. When someone says “pull the data in with AJAX” I say “can you just inline the data on the server-side in a JS block?” When someone else says “here’s how to do something with jQuery” I’ll always come back with the vanilla JS version. Just load the jQuery from a CDN because the user might have it cached already? Nope, I’d rather host myself. One less DNS lookup and TLS handshake to worry about, not to mention a potential point of failure.

Back to Font Awesome, including it the way that most people blindly use it you’ll end up with a minimum of two calls, one for the CSS and one for one of the font files. But, how I usually see it is that someone wants a Twitter icon, and exclamation mark, and a check mark, which, if you don’t already know actually causes three font files to download! The Twitter logo is part of the brand fonts, the exclamation mark is solid fonts and the check mark is part of the regular fonts. In total you’ve added 150 KB to the page, just for those three icons that could have been inlined for probably 2 or 3 KB (and that’s not counting compression.)

Now, if you don’t care about 150 KB on your initial request, that’s fine, I’m not judging you. But 150 KB for X plus another 100 KB for Y starts to add up over time. And sites never shrink over time, they always grow. Tomorrow or next week or next year you’ll add a new feature which adds another 100 KB for feature Z.

My further hatred of how people use it, and you’ll see this very often in the WordPress world, is that every plugin requests their own copy of Font Awesome, some on version 4, some on version 5, so you end up with multiple downloads of Font Awesome. On a recent site we audited we found 500 KB of Font Awesome data on the home page! That’s half of a megabyte! For icons!

Anyone still reading? Probably not.

I just had to say something, it was bugging me. Font Awesome is a great way to get started for fast designs. It is also a great tool if you have dynamic or unknown data entering your site that you need to support. And further, if you defer or lazy load things you can ease the burden even more. But just don’t use it blindly.

On the off-chance that someone actually reads this I turned comments off because I don’t care, I just wanted to rant.

The post I hate Font Awesome like I hate jQuery appeared first on Basically a place that Chris can post solutions to problems so he can easily find them later.

]]>