Debugging WordPress and PHP 8.1: a chicken-and-egg conundrum

If you’re a WordPress developer, trying to debug code on a server that’s running PHP 8.1, you may have noticed an absurd number of deprecation notices overwhelming your efforts to get anything done.

After trying in vain to resolve the issue by updating the value for error_reporting in my server’s php.ini file, I discovered why that doesn’t work, courtesy of a StackExchange answer.

WordPress sets its own value for error_reporting when you turn on WP_DEBUG, ignoring the php.ini value. It kind of has to do this. (Well, not “kind of.”) That’s the only way for WordPress to display more — or less — error output than what’s configured at the server level.

The problem is, when you turn on WP_DEBUG, WordPress shows you everything. Normally that would be desirable, but PHP 8.1 has introduced an unusually large number of deprecation notices in anticipation of PHP 9 imposing strict rules on things that have been generously allowed in earlier PHP versions.

OK, so we know what’s going on. But since a lot of the deprecated code is in WordPress core, or in third-party plugins, there’s not really anything a developer like me can do about fixing these issues. (Sure, I could fix it and submit a pull request, but I’m not currently a WordPress core developer and I am not sure I want to take that on, even in an extremely peripheral way.)

So… uh… how do I just make it stop? That is definitely easier said than done, and the reason is the sequential nature of how code is loaded and executed. “Everything everywhere all at once” is not how it works. WordPress loads one file, that loads another file, that loads several other files, each containing a mix of procedural and object-oriented code, and functions. The way WordPress lets you hook into that flow and insert modifications is… well.. hooks.

Hooks are great, but a) the hook you want has to exist, and b) your code that uses the hook needs to be loaded before WordPress processes the hook. Oh, and of course, c) hooks themselves are functions, so you can’t use them until those functions have been defined.

Hence our problem. The code that tells WordPress to show you all the stuff — errors, warnings, deprecation notices — happens pretty early in the sequence. Specifically (as of WordPress 6.1.1) it is in line 460 of wp-includes/load.php in a function called wp_debug_mode(). By that point, yes, the add_action() and add_filter() functions have been defined. But, WordPress hasn’t actually loaded any plugins yet (even “must-use” plugins in the mu-plugins folder). So if you write a plugin to modify the error_reporting value, it might work, but only on deprecation notices that are generated after your plugin has been loaded, and the ones we’re concerned with are all in WordPress core and get triggered before plugin loading starts.

Realizing this, I thought I might solve the problem by putting my filter into the wp-config.php file, a.k.a. the only “early” file you’re allowed to edit. But nope, can’t do that: the add_filter() function doesn’t exist until wp-includes/plugin.php gets loaded at line 49 of wp-settings.php, which itself gets loaded at the very end of wp-config.php.

Since wp_debug_mode() runs at line 80 of wp-settings.php, that means the only way to do what we’re trying to accomplish is to get it to fire off somewhere within those 31 lines of code inside wp-settings.php. Those lines consist of calls to a handful of low-level functions. I checked the source code of each of them for any hooks — not that it would be correct to use the hooks in any of those functions for this purpose, if they existed — but merely to see if it would even be possible.

There is only one hook in the entire lot, and it’s inside wp_debug_mode() itself. It’s called enable_wp_debug_mode_checks. I wrote my own filter function that leverages that hook to modify error_reporting, and it would work, except for the fact that there’s nowhere to put it. I can’t write any custom code in a plugin or theme to call that filter, because it wouldn’t be loaded yet by the time the filter is applied in wp_debug_mode(). And I can’t put it in wp-config.php because, as noted above, the add_filter() function isn’t even defined yet at that point.

So… there are only two place you can put this code to get it to work: either in wp-settings.php just before line 80, or by just editing the wp_debug_mode() function itself in wp-includes/load.php. And you very much are not supposed to do either of these things, because your changes will get overwritten the next time a WordPress core update runs.

But… what else are you going to do? Well… after going through all of the emotions on my wide spectrum from frustration to rage, I read the comments at the top of wp_debug_mode() that start with a pretty unambiguous statement:

This filter runs before it can be used by plugins. It is designed for non-web runtimes.

OK then.

Also inside the comment is a code example, mirrored in the next reply on the same StackExchange post I linked above. I initially ignored it because I instinctively ignore any PHP code example that includes $GLOBALS… but in this case, it’s apparently the official answer on the matter. Boo.

The code I ended up putting into wp-config.php looks a bit different though:

if (WP_DEBUG) {
  $GLOBALS[‘wp_filter’] = [
    ‘enable_wp_debug_mode_checks’ => [
      10 => [[
        ‘accepted_args’ => 0,
        ‘function’ => function() {
          error_reporting(E_ALL & ~E_DEPRECATED);
          ini_set(‘display_errors’, ‘on’);
          return false;
        },
      ]],
    ],
  ];
}

I’m not sure why the StackExchange poster put the error_reporting() call outside the conditional. I also found I needed to specifically set ini_set('display_errors', 'on'); because returning false from this function causes the rest of wp_debug_mode() not to execute — which we want, but we need to make sure to replicate any of the rest of its functionality that we do need. I probably should add the bit that doesn’t output errors on REST/AJAX calls, but I’ll worry about that when it becomes an issue. I don’t use either of those very often. (Of course the WP admin itself uses AJAX all the time.)

A few reasons why you should keep your old domain name indefinitely, even if you’ve changed your business name and never plan to go back

What follows is a slight reworking of an email I just sent to a client. I think it’s broadly useful enough that it deserves to be shared publicly.

Here’s the scenario: The client’s family business operated for many years under her father’s name, but when he retired and she took over, she changed the name. In the several years since, she has built a strong reputation for the new identity, and she was wondering if it was finally time to let the old domain name lapse.

Short answer: No!

Here’s the longer answer, which also addresses the natural follow-up question: Why?

First, “google” your old domain name. Just go to Google, type the old domain name in the search bar, and see what comes up. You may be surprised how many websites out there still have links to URLs with your old domain.

Assuming you kept the old domain configured as an alias when you built your new website, if you keep the domain, those old links will still work, and redirect to the current site. If you were to let the domain lapse, those links would stop working. (Whether or not anyone is actually clicking those links is another matter, of course. If you have Google Analytics or other site stats, check your referrers for some insight on that.)

The same goes for email. Some people may still have old business cards, or for other reasons might still try sending email to those old addresses. If you have them configured to forward (which, again, you should have done when you initially made the switch), then you’ll still get those messages.

Another thing to keep in mind: if you let the old domain lapse, someone else will be able to register it, and could put it to nefarious use — phishing, scams, or just generally sleazy content. I have seen it happen. Even the best case scenario — no one registering it — will result in it loading a “this domain is available” placeholder page with the domain registrar, which may give people the impression you’ve gone out of business.

Given the relatively low annual cost of a domain registration, my recommendation is that you should keep your old domains registered for as long as you’re in business.

New album: Interference Waves (2022 Singles, Outtakes & Allsorts)

It’s another new album! This isn’t really “new” music, though. It’s a compilation of tracks I recorded this year that weren’t already included on one of the three (!) other albums or one EP that I already released this year. It’s all on Bandcamp now. And on YouTube too, actually.

2-hour music video project: “Auric Strands”

I’ve gotten on a bit of a roll with my Saturday music video projects lately, and I’m starting to get just the slightest iota of traction on my YouTube channel — mostly because of silly stunts I shamelessly promote in the comments on more popular YouTubers’ videos, but possibly people genuinely being interested in what I’m doing — and I’m sure that some if not most of the, as of today, 46 (!) subscribers I have are people I don’t know personally. (I always find it easier to get people who don’t know you to engage with artistic endeavors.)

Anyway… I wrapped up my last video project, and with holiday season in full swing, I knew I couldn’t devote (counts on fingers) 17 hours to this like I typically do, so this time I managed to pull it off, start-to-finish, in two hours. Here’s how it came together.

I had a couple of inspirations at work in this. First, my ongoing interest in the broad, nebulously-defined “Synthwave” subgenre of electronic music. For me, it just means instrumental electronic music with a steady beat and sounds that are evocative of the early- to mid-1980s. (1984 is the magical year for me, YMMV.) There’s a sub-subgenre called “Outrun” that generally features a more driving beat, whereas some Synthwave music tends towards ambient or at least “moody.” It’s named for Out Run, a popular car racing arcade game released by Sega in 1986. I never really played Out Run much, if at all, because at the time (I was in middle school) my parents bought me a computer instead of the latest video game console, and I didn’t have the endless supply of quarters (or transportation to the mall) that I would’ve needed to spend a lot of time in the arcade.

Now, things are different. For the past couple of months I’ve been playing a lot of retro games on my Anbernic RG353M, and last night, after burning myself out on Mother 3, I decided to give the arcade version of Out Run a try, mainly to pay attention to the music. I found it interesting, with some surprisingly jazzy chords, an unusually complex song structure, and of course the driving beat I expected.

Earlier yesterday, I had restrung my Precision Bass with some new La Bella gold flatwound strings, so this morning I wanted to play the bass, and more specifically, I wanted to record the bass. So I picked it up and just started noodling around to come up with a bass line that used a driving 8th note pulse. I knew I was kind of inventing a chord progression as I went, but at the moment I was just focused on the sound and the shape of the bass line. I knew I needed to play with a pick — something I rarely, but not never, do.

Once the bass line came together, I knew I had something good going. It was just after 10 AM, and I decided to see how much I could accomplish by noon. I wanted to write and record an entire song, and produce a video, but I wasn’t sure I could do it all in 2 hours.

tl;dr I did. (OK, this is already way too late for a tl;dr but… anyway, just keep reading now that you’re this far.)

I retreated to the “padded room” in the basement with my bass, my laptop, and my MIDI controller keyboard. At that point I had mentally sketched out a plan to record the bass first, and then drums in the form of LinnDrum samples, and then I would just see where inspiration took me from there.

After the drums were recorded, I layered on two more MIDI synth parts using Alchemy sounds in Logic (first a pad and next an arpeggiator). And then I switched over to my Yamaha Reface CP keyboard to lay down some melodies. The melody in the A section is played with a Rhodes electric piano sound, and the B section with a Hohner clavinet sound.

And that was it! I wrapped up recording by 11 AM, had the mix done in 20 minutes, and spent about another half hour putting together the video in Final Cut before posting it to YouTube at 12:05 PM, roughly 2 hours after I had first picked up my bass with only the vaguest idea of a song in my head. Here’s the end result:

Let’s talk a bit about the structure of the song, as it’s a bit unusual. The chord progression in the A section is a 6-bar loop. The keyboard part is just alternating between a G major chord and a D major chord, but with the bass changing the root under the chords, the progression is effectively:

Emin7 /// | % | Bmin7 /// | % | GMaj9 /// | D/F# /// :||

This is repeated 5 times at the beginning, and 4 times when it returns, then 2 times (I think… I’m writing this from memory!) at the end. Overall this section is essentially in E natural minor (or E Aeolian mode).

The last time the A section is repeated, transitioning into the B section, the bass line goes up to a B, instead of returning to E. The B section, a 4-bar loop with some syncopated chord changes, shifts the key to B natural minor (B Aeolian), introducing C# in the melody, where the A section used C natural. Here the keyboard is alternating between D major and B minor chords, with the bass playing A, F# and B, making the chord progression:

Bmin /// | % | D/A // D/F# | //// :||

Once the overall audio-visual production was done, I just needed to add some title screens. Which, of course, meant coming up with a title. My working title was the somewhat literal and boring “Golden Strings,” which got the point across but was a bit uninspired. So I scanned my brain for synonyms. Immediately the word “Auric” popped into my head. As in Auric Goldfinger. Perfect. But then I needed a synonym for “string” as well, and there I resorted to an online thesaurus. Not because I couldn’t think of another word on my own so much as I couldn’t think of a good one fast enough as my noon deadline was fast approaching. There amongst a bunch of real clunkers — twine, cord, ropethong, seriously? — I spotted the perfect choice: “strand.”

“Auric Strands” it is. It wasn’t hard, then, to find a font in my library that fit perfectly. (It’s Big Shoulders Inline Display, if you’re into that sort of thing.) And so the project was done, and just in time!

If you like the song, you can download it on Bandcamp. Thanks for listening! And watching! And reading!

Thanksgiving ruminations

It is Thanksgiving. It is a strange Thanksgiving — my first without my mom. Not the first I’ve ever not spent with my mom; for many of my adult years we would spend the holiday with my wife’s larger family, until that Thanksgiving 13 years ago that was the first without her mom.

It’s the first Thanksgiving I can’t spend with my mom, because she’s not here anymore. And it’s not something we’ve had the majority of a year to prepare ourselves for. She’s been gone for precisely 56 days. Enough time for it to become a familiar daily fact of life, but not enough for us to know what to do with the information, much less what to do with ourselves on a holiday we are used to spending together.

Thanksgiving has always been a rather strange holiday, the one most singularly centered around food. Particular types of food that, for the most part, we only consume as part of this annual ritual. It’s a strange ritual, too, reinforcing the simplistic, sanitized version of our (white America’s) history. But I suspect that my family is far from unusual in focusing very little on that history, one way or another, and instead turning towards the activities in the kitchen, accompanied by a soundtrack of parades, dog shows and football, emanating from the TV in the next room.

When we shifted to spending almost every Thanksgiving with my family — many years spent at holiday rentals on Lake Superior’s North Shore, just before heavy winter weather sets in and the trip would be more adventure than my parents would be willing to undertake — I became self-appointed head chef, attending to the turkey, the gravy, and a green bean casserole stubbornly made from scratch. It was my way of giving to those I love, but it was also my way of coping, as an introvert who often needs to flee for some alone time, even amongst close family.

Things in the kitchen always seem to culminate in a moment of peak chaos, as the preparation of all dishes is timed to coordinate with the turkey emerging from the oven. Gravy, by necessity, comes last, made from collected pan drippings as the turkey rests in its foil tent, awaiting the big show. But often there’s still a mad flurry of activity around potatoes, stuffing (which never lives up to its name in our House of Perpetual Fear of Food-borne Illness), and assorted vegetable side dishes. Things got even more chaotic when Sara and I became vegetarian — well, pescatarian, but we only eat seafood 0-5 times a month — and I needed to start making our vegetarian mushroom gravy in addition to everything else. (We don’t faff around with vegan loafs or anything like that. We just make sure that all of the sides that call for broth use vegetable broth, and we’re good. We’ll often have a small piece of turkey but honestly, for us Thanksgiving dinner has always been about the sides anyway!)

Sara has dubbed me the saucier for my (semi-)expertise in the realm of gravies and sauces, and it is true I do take great pride in delivering a perfectly smooth, rich and flavorful gravy to the table. I never forget the first time I made Thanksgiving gravy, in 1999, the year we hosted both of our sets of parents — all four of them, still alive and well — at our one-bedroom, garden level apartment in Uptown Minneapolis. I didn’t know what I was doing, and the gravy was a lumpy disaster I tried to salvage by running it through the blender.

Since that time, I truly did get the knack for making gravy. First, you’ve got to know your proportions: n tablespoons of fat, n tablespoons of flour, and n cups of liquid. For Thanksgiving, n = 4. Melt the fat (butter or skimmed from drippings), stir in the flour and mix until completely combined, and then gradually add the liquid (drippings or broth), about 1/4 cup at a time, constantly whisking until everything comes together before adding more. Along the way, “everything together” gradually changes from the consistency of cookie dough, to paste, to thick cream, to smooth liquid. Once all of the liquid is added, you keep it at a low boil and continue to stir constantly until it’s the desired thickness. Oh, and there’s another trick: in addition to adequate seasoning with salt and pepper, and other herbs if desired, around halfway through the final thickening process, also whisk in around a tablespoon of tomato paste, ketchup, or my personal favorite, Heinz chili sauce (which really is just a more “rustic” ketchup). It adds a bit of color, a slight tang, and helps the gravy to thicken up. Just be sure you whisk it in completely. But the key above all else is that bit in bold.

This year we weren’t sure what to do for Thanksgiving. We’re spending it with my dad, of course, but he’s not comfortable driving in the metro area anymore, so we’re going to him. We didn’t want to cause stress for him with a bunch of dirty dishes (even if we do all the clean-up) and many hours of chaos in his apartment kitchen in Rochester, so we decided to cook the whole meal a day ahead. (Although in the end it was only about half of the meal, and we’ll finish the rest in Rochester today.)

Cooking Thanksgiving dinner when you have an entire day wide open, and no need for everything to come together at once, is a transformative experience. Each dish could receive undivided attention. It also was a chance for some personal reflection, on a day when I really needed it. First up was my 100%-stubbornly-made-from-scratch green bean casserole. Next, I took a break to enjoy the unusually warm weather with a run while Sara made the apple pie.

Once I was back in the kitchen, it was time for my mushroom gravy, and I have to say, it is quite possibly the best mushroom gravy I have ever made. It’s amazing how good a gravy can be when you’re able to give it your undivided attention. I gently cooked about 6 oz of thin slices of mushroom — heavily seasoned with Trader Joe’s “Green Goddess” seasoning blend (mostly garlic and onion powders with dried spinach [!] and parsley, and lemon peel) — in a half a stick of butter until they were very soft and just starting to caramelize a bit on the edges, then I added 1/4 cup of dry white wine — a Sauvignon Blanc from New Zealand, my personal favorite — and let that cook off. Next up came 4 tbsp flour with a dash of McCormick “Poultry Seasoning” (which I think is nutmeg, sage, marjoram, thyme, maybe oregano?), followed by the gradual addition of 4 cups of vegetable broth, and of course later a dollop of Heinz chili sauce. It wasn’t anything radically different from what I always make, but sitting there stirring it gently and giving it my undivided attention for 20 or so minutes, really made a difference.

Yes, I said “sitting there.” By this point late in the afternoon, I was sitting on a stool at the stove, because my plantar fasciitis was acting up from a day on my feet. It’s something I’ve been struggling extensively with all year, after a day this past spring, when my anxiety over the return of my mom’s cancer, and our ambivalence over having urged my parents to sell their house — a constant source of stress in recent years — and move into an apartment, filled me with an impulse to organize my stacks of storage bins in our basement, and I spent an entire day standing in sock feet, shuttling bins up and down from the basement to our dining room. My left foot was destroyed after that day. It took months to get back to pain-free running, and I now need to wear heavy-duty Dr. Scholl’s insoles in all of my shoes, including an old pair of running shoes that have now become my indoor “slippers” that I need to have on my feet pretty much any time I’m not lying in bed.

It’s been a strange, difficult year, stacked on top of a string of strange, difficult years. My mom was initially diagnosed with stage 3 lung cancer at the beginning of 2019, and after a couple of months of brutally taxing proton beam treatments at the Mayo Clinic and lingering side effects (the treatments essentially fried her esophagus), she was mostly back to her old self by the end of the year. But then covid hit, and we only saw them in person twice after March 2020, even though they just lived 70 miles away. By the time we were all vaccinated in 2021, regular visits returned, but so did my mom’s cancer. Another brief round of proton beam treatments, plus a completely unrelated skin cancer on her nose, which required a skin graft from her forehead, and all of us learned just how weird and complicated the recovery process for something like that can be. She made it through that too, stronger and more determined than anyone — most of all herself — believed she could be. But 2022 began with more cancer, more treatments, and then eventually, no more treatments.

August and September were particularly difficult. She started to use a walker around the apartment, and after a couple of late-night falls, and her right lung filling with fluid (a symptom of the metastasized cancer), she spent her last 6 weeks in the hospital — most of those in Lake City, an hour away from home, because Mayo seems to need to shuttle off its long-term hospitalized patients to far-flung outposts rather than keeping them in Rochester, followed by 4 — in retrospect, mercifully brief — days in hospice in a mediocre nursing home (chosen because it was available and her insurance-approved hospital time had run out) a couple of miles from their apartment.

Rochester, Minnesota is an interesting place to observe the best and worst of the American medical system. In the Mayo Clinic it has world-class medical technology, and the best experts you will find anywhere. For almost any type of medical treatment you may need, there is scarcely any place in the world where better care is available. And yet, if you are a U.S. citizen trapped in our byzantine insurance and medical billing system, it can be an absolute nightmare. Fortunately for my mom, her working years came at a time when someone in a fairly low-level clerical office job could still make a career, retire after 30 years at a reasonably young age, and live out their days with a guaranteed pension and comprehensive health insurance coverage. Cancer didn’t bankrupt her family. Still, in the end, the best medical treatment in the world was only able to give her a couple of extra years. Eventually a day comes where the treatments end, the breathing ends, and those concerns float away with the wind.

But it’s difficult, as one left behind, to spend more than a few scattered moments reflecting on what was, and what was lost. There are arrangements to be made, bills to be paid, documents to get notarized, phone calls, letters, emails, files to organize, the pointless busy work of a society where every moment we’re drawing breath, and fair bit of time on either side of that, is a business opportunity for someone.

So where am I going with all of these ramblings? Do I have a point? Does any of it? Life is a series of moments, and the meaning is what we find in them for ourselves. Sometimes I just want to take a “time out” and try to set a few of these moments into something more permanent.

And what’s more permanent than a blog post?