Why you should not put theme code in plugins

I regularly maintain about 40 WordPress websites. Updating plugins and themes can cause issue, but updating core usually goes without any issue. Not so this week on a new website I updated for the first time.

After the core update I got the “There has been a critical error on this website” dialog and the backend was broken. Fortunately I created a fresh backup just before the update. But as the website was not hosted on one of our servers and the FTP account was not showing all files, I first had to use the Web-FTP function from the hoster to be able to download the backup file. With a local copy of the website, the debugging session could start.

Customizing theme icons

Let’s take a look at the code that caused the issue. With debugging mode enabled, I was able to find this error message:

PHP Fatal error:  Uncaught Error: Class 'TwentyTwenty_SVG_Icons' not found in ...

This error was caused by the following lines (code simplified for the example):

function add_custom_twentytwenty_icons() {
	TwentyTwenty_SVG_Icons::$ui_icons['hamburger'] = '<svg><!-- ... --></svg>';
add_action( 'after_setup_theme', 'add_custom_twentytwenty_icons' );

A simple code to add a custom icon to the TwentyTwenty “ui” SVG icons group. This has been done in an action after_setup_theme which looks reasonable. But there was something essential missing. The check, if the class does exists. So how could it be done better?

Fixing the issue

There are several ways in how we can fix this specific issue. Let’s take a look at them to find a good solution.

Check for existence of the class

As the error indicated, the code fails, as the class does not exist and therefore the property cannot be extended. So why don’t we just check that first:

function add_custom_twentytwenty_icons() {
	if ( class_exists( 'TwentyTwenty_SVG_Icons' ) ) {
		TwentyTwenty_SVG_Icons::$ui_icons['hamburger'] = '<svg><!-- ... --></svg>';
add_action( 'after_setup_theme', 'add_custom_twentytwenty_icons' );

This would work. Now we are sure that we only change the property of the class, if it exists. But there are some issues with this solution. What if the class name changes? What if the property name changes? In those cases, we either get another fatal error or a warning and broken code at least.

Overwriting the pluggable class

Guess what? This specific class of TwentyTwenty is pluggable. So if you want to change the behavior of the class, you could simply just declare the class in your own code before it is loaded by TwentyTwenty itself.

The only downside with this: if you only want to add an icon, duplicating the whole class might be a bit of an overkill. You would have to make sure that your cloned class keeps in sync with the original class on future updated of TwentyTwenty.

Use a filter!

Yes, you read that correctly, there is a filter to alter the SVG icons available in TwentyTwenty we could just use to add our own icon:

 * Filters Twenty Twenty's array of icons.
 * The dynamic portion of the hook name, `$group`, refers to
 * the name of the group of icons, either "ui" or "social".
 * @since Twenty Twenty 1.5
 * @param array $arr Array of icons.
$arr = apply_filters( "twentytwenty_svg_icons_{$group}", $arr );

In our case, we wanted to add an icon to the “ui” group, so the new function could look as simple as this:

function add_custom_twentytwenty_icons( $icons ) {
	$icons['hamburger'] = '<svg><!-- ... --></svg>';

	return $icons;
add_action( 'twentytwenty_svg_icons_ui', 'add_custom_twentytwenty_icons' );

This is the best solution. The only downside with this is that this callback would be call for every icon that needs to be printed to the page. Maybe this is why the “clever solution” that caused the error was trying to add the icon just once to the class property.

Why was the original solution causing an error?

But my motivation for this blog post was not only to show that specific error and how it could be solved. It’s about why it became an error. The original code was not stored in a child theme, but in a mu-plugin!

The project was using a “vendor-wp-base-theme” as a child theme to TwentyTwenty. Inside of this child theme, not a wohle lot was happening. In addition, there was a mu-plugin “vendor-wp-base-theme-plugin” which used a PHP class adding widgets, enqueuing styles and scripts, adding some ACF filters and using the code from above to add this one SVG icon.

But why is this an issue? Why would the after_setup_theme action fire but the files from the theme would not have been loaded? The issue lies within the wp-settings.php file. This file will fist load all mu-plugin file, then all plugin files and finally all files from “active and valid themes”, so from both the parent and the child theme, before firing the action:

foreach ( wp_get_active_and_valid_themes() as $theme ) {
	if ( file_exists( $theme . '/functions.php' ) ) {
		include $theme . '/functions.php';
unset( $theme );

 * Fires after the theme is loaded.
 * @since 3.0.0
do_action( 'after_setup_theme' );

So how can we still get the issue? Let’s take a look at the wp_get_active_and_valid_themes function and what it’s (not) doing:

function wp_get_active_and_valid_themes() {
	global $pagenow;

	$themes = array();

	if ( wp_installing() && 'wp-activate.php' !== $pagenow ) {
		return $themes;
	// ...

This function is checking, if WordPress is “installing”, which will also be true, if the core needs to upgrade the database after an update using the wp-admin/upgrade.php file. In this case, the function will return the empty themes array, which will therefore not load the functions.php files from both the parent and child theme and will still fire the after_setup_theme action causing the issue.

Conclusion: never do themes stuff in plugins!

There is a good reason why you should put code to customize a theme into a child theme and code to customize a plugin into another (mu-)plugin. This will prevent issues like the one demonstrated in the code from this blog post. Running theme code in plugins (and vice versa) can very easily cause issues like this, which might be hard to debug, especially when they only occur in rare cases (like a core database update).

If you really have to put code into a place where it would typically does not belong to, always check, if the thing you want to modify exists. And if the original code offers hooks, please always use them. They have been added just for those reasons. Even if you think you are clever and can save some function calls by directly manipulating PHP class properties (or similar things), don’t do that. Others will be very thankful that your code will not break their site with a usually uncritical task like a WordPress major core update. 😉

Creating SVG sprites in combination with wordpress/scripts

In one of my advent calendar blog posts in 2016 I wrote (in German) about how to create a SVG sprite with SVG symbols using a Gulp script. With Gutenberg becoming a major part of WordPress I have used the wordpress/scripts package a lot lately. I a recent project I wanted to create such a SVG symbols sprite again, but I didn’t liked to use Gulp additionally with wordpress/scripts, so I searched for an alternative.

SVG Spritemap Webpack Plugin

After some search I’ve found the svg-spritemap-webpack-plugin which looked quite promising. It’s using webpack (which is also included in wordpress/scripts) and can be used like this:

Read more →

Hide the download button for audio and video blocks

Last week I was asked for help on a website project. In this project, the website embeds some audio files into the page. This is a great feature of WordPress making it so easy to embed media files. But some people dislike, that on some browsers it’s do easy to download those files.

The “download” button in Chrome

In the Chrome browsers, it’s simpler than in any other browsers. By default, any audio and video tag would add a “download” button to the options (the three dots, next to the controls). In this particular project, the button should be removed, to make it at least a little harder to download a file and not “encouraging” anyone to do so.

Hiding the button using the “controlList” attribute

Chrome is the only browser with such a button (I know of) and forunately it also has an easy way to disable it: the controlList attribute. This attribute can have different/multiple values and one of them is the nodownload value.

You may now this, that you can simply add this attribute manually to the block using the “Edit as HTML” view. But as soon as you switch back to the “Edit visually” view, the block will be defect and you can only choose to either recover it (which removes the attribute) or convert it to HTML.

Adding for a feature request in Gutenberg

As this issue was probably not new, I searched for existing tickets and found two. The first one was asking to add a toggle to show the download button. This one referred to a second ticket about the general idea of disabling the download button, also by introducing a toggle.

In this second ticket some good arguments were made against such a toggle. As only Chrome has this button, such a toggle would only “work” in Chrome. And it would not even really work. Because even the attribute will not prevent downloads.

If a audio or video file is embedded with an audio or video HTML tag using a file from the media library, it can be downloaded. It will actually be “downloaded” automatically, once the media is played.

Silently removing the button with a plugin

So even though you cannot prevent the download, you might still want to hide the button in this case you can filter the block rendering and add the attribute there. In the simplest form, it will look like this:

function hide_download_buttons_on_embeds_render_block( $block_content, $block ) {
	if ( 'core/audio' === $block['blockName'] ) {
		$block_content = str_replace(
			'<audio ',
			'<audio controlsList="nodownload" ',
	if ( 'core/video' === $block['blockName'] ) {
		$block_content = str_replace(
			'<video ',
			'<video controlsList="nodownload" ',

	return $block_content;
add_filter( 'render_block', 'hide_download_buttons_on_embeds_render_block', 10, 2 );

You might have to write a bit more code, if the HTML tags in your WordPress installation already uses some other attribute values.


While it’s not possible to prevent downloads of audio and video files from the media library in such a way, it might still be something you want to add to your page.

If you do want to allow downloads – let’s say for episodes of a podcast – it’s much better to actively add a download button using the “File” block. This will not only present a consistent button in browser, it will also make it a lot easier, obvious and accessible to download the media file.

As always, the solution from this blog post can be found as a GIST where you can download the solution as a ZIP file and install it as a plugin.

Accessibility is not a feature

The yearly Global Accessibility Awareness Day is still some weeks away – this year it be celebrated on 20 May 2021 – but some recent discussions made be pick this topic for my next blog post.

It’s not just a number, it’s people!

In a recent discussion on twitter regarding a corona contact tracing app, the importance of an accessible app was discussed once more. I can’t tell you how many time I’ve read statements like these:

Read more →

Repairing the WordPress database

Earlier this week one website was not responding and showing an error that the connection to the database was not possible. Such an error is usually a sign, that the credentials are invalid. But no changes have been made to either the credentials or the configuration file. After activating the WP_DEBUG mode I found out that the database was corrupt.

Repairing the database with the WP-CLI

My first approach was using the WP-CLI. There you have an option to optimize the tables, which can also sometimes repair errors:

wp db optimize

This command is usually been used when large amounts of rows where deleted but the table size was not reduced. But it will not always also fix issues with the tables. Fortunately there is another command for this task:

Read more →

Show comments of private posts in the comments widget

In September 2017 I wrote a blog post on how to show tags from private posts in the tags widget. Three weeks ago I got a comment (on the German blog post) if something similar would be possible for comments from private posts. In this posts I would like to present you a simple solution to that comment.

Filter the comments query arguments

By default the comments widget will only show five latest approved comments from public posts. This is how the query arguments look like:

Read more →

Get detailed download stats for plugins

Recently I was checking the downloads of a plugin in the WordPress.org plugin directory. In the “Advanced View” of a plugin you will find a “Downloads Per Day” graph with the numbers from the past 267 days. Let’s take a look at the stats for the poplar Antispam Bee plugin:

Downloads per day for Antispam Bee

When you hover over the graph, you will find the date an number of downloads of that day. But I wanted to know the summary of the downloads in the first few weeks after the latest release (the spike in the graph) and writing down all numbers manually to add them up would have been a bit too much work. So how could I get those numbers more easily?

Read more →

Better security for WordPress with secure server headers

There are many options to make a WordPress website more secure. For some of those options you might use additional plugins. But there is an easy way to increase the security by setting only some server settings. But before I go into details, let’s find out how to get a current security status of your site.

Mozilla Observatory

An excellent tool to test your website for security is the Mozilla Observatory tool. This tool scans not only the headers you server is sending, it will also scan TLS and SSH settings and will use some third-party tools to give you a brief overview. A result might look like this, before you adjust any settings:

Read more →

Debugging memory usage

Last week, an issue was reported for a site. It only occurred sometimes and the result was an Error 500. Activating the WP_DEBUG mode I was quickly able to find out that the memory limit was hit. The only fix was an increase of this limit, as the functionality on the pages used quite a lot memory and an optimization was not easy to find.

In those cases it would be nice to be able to find out how much you have to increase the limit. So how much would a page load usually take. There are several ways to find this out.

The danegerous way

One simple solution would be to decrease the memory limit until you always get a memory limit error. Then you slightly increase the limit until you only get the error some times. This is the value the actual usage would likely be. Then you add some more to that limit, so you don’t run into it again.

Read more →

Set the debug level using error_reporting

Everyone knows that. You work on a website and you run into issues. Sometime you even get a critical error like this:

But in some cases, you don’t get the error on every single request. Then you have to activate the debug mode and log all errors.

Activating the debug mode

To activate the debug mode for WordPress you simply have to set some constants in your wp-config.php file. These are the defaults:

Read more →