WordCamp Leipzig Opening Remarks

This weekend I attended WordCamp Leipzig 2025, and WordCamp Leipzig is already a special one. It is super affordable, even for a WordCamp with just 9€ per ticket, this year tickets were in such high demand, that organizers sent out e-mails to ensure that people who bought a ticket were able to attend or pass on their ticket to people who didn’t get a ticket.

But that’s not all. Last year I got to know Manuela Grunert, or as she calls herself Yuri. We play Minecraft together and she’s blind, with about 2% of her eyesight remaining. And yes, 2% is legally blind in Germany, she’s still able to see colors and schematics. And if she’s close to things she can still read.

Since I’m always curious and had never had the chance to talk to a blind person before, I asked her if I could ask a few questions. She agreed. A few weeks later, I asked if she might want to share her insights with a broader group, perhaps at a WordPress Meetup, and that idea eventually grew into applying for WordCamp Leipzig.

The first talks by blind people at a German WordCamp

Her brother Mathias Grunert, who happens to be a blind developer, also applied, and both were chosen to speak in Leipzig. So we had not just one, but two talks by blind people in Leipzig.

Yuri and her audience at WordCamp

Yuri was very nervous, and I opted to be her MC, since I had thrown her in at the deep end. Both talks were great and – without any exaggeration or intended pun – eye-opening. It’s a whole different story when you hear from an accessibility expert about what you should do in theory, versus witnessing firsthand what happens if you don’t.

Yuri’s talk was about how she, as a user, uses the internet with a smartphone. And her opening questions, which we developed together, had the expected results and effect..

The audience of roughly 60 people was asked three questions and were told to lower their hand as soon as their answer was “No.” The first question was: “Who here has created or maintained a website?” – as expected, all hands remained up. The second question was: “Who checked it for accessibility?” – about 10 people lowered their hands. This was a little less than I expected, which was a positive surprise. But the third question had the expected result and impact: “And who of you did so on a smartphone?” – If I counted correctly, only 7 hands remained raised; everyone else, including myself, lowered their hands.

Yuri then presented her talk and hands-on examples using VoiceOver from her iPhone. For some, the VoiceOver was too fast; others appreciated seeing her actual personal experience. One audience member commented that some blind people listen to VoiceOver at up to 1,000 words per minute. For reference, you’ll probably read this article at about 200–240 words per minute.

The blind insighter – the second talk

Mathias had a different point of view – or point of experience? – as a developer he showed crafted examples that he came across over the years. My personal highlights in his talk: he presented his slides from memory, so the first roughly 10 minutes were done literally and figuratively blind. And during his hands-on session he did a few examples immersing us into his world, turning the screen black. For the given examples, none of us correctly guessed what VoiceOver tried to tell us.

Mathias announced that he would share these examples. As soon as I have the link, I will add it here.

My personal blind experience

Since I invited them, I also took care of getting them from the main station to the hotel, to the venue, and back. I also supported them during the WordCamp when needed.

For me, it was a humbling experience. Things that are a minor inconvenience for us, sighted people, can be a real struggle for Yuri, Mathias, and other visually impaired people.

A tram/subway/train without accurate announcements? For us, it’s annoying. For them? They might leave the train too early or miss their exit.

A steep staircase? A loose handrail? A narrow ceiling without marking? For them way more likely to stumble or hit their heads.

A fast food restaurant with fancy names for their products, their menu animated on a TV and no handout version? For us, it’s mostly no issue-at worst, mildly annoying. They have to ask what’s on the menu.

I go home with a better understanding of their struggles in life and am impressed by what they achieve without senses that we take for granted.

Others also welcomed them with open arms into the community, and they have already been asked to speak at other WordCamps. So there is hope that this will not be their last appearance at WordCamps. And maybe it will encourage others to follow their example.

Call for speakers and organizers

We learned a lot from both of them, and in my humble opinion, witnessing it firsthand is extremely valuable-more valuable than I had imagined. A11y experts can only share theoretical knowledge. While we still need this, understanding why is a whole different story.

Impaired speakers

So if YOU are visually impaired, blind, deaf, have color blindness, or any other restriction that makes it harder for you to take part in everyday life on the internet, reach out to your local community-whether it’s a WordPress meetup, WordCamp, or similar communities for Joomla, TYPO3, Drupal, etc.-and share your story.

Organizers

The same goes for organizers of events: accessibility is becoming more and more important. If you know anyone, encourage them to share their personal experience. You can also reach out to me or to the organizers of WordCamp Leipzig.

Thank you Everyone Else

Even though I did not cover the remaining talks at WordCamp Leipzig, I also want to thank everyone else: the organizers, the speakers, and all attendees.

Christoph giving Robert a crocheted personalized Wapuu with green shirt, glasses and straw hat.
And a special thank you to Robert, who initially kicked off WordCamp Leipzig. Have fun with your crocheted doppelganger.

Christoph holding his talk at WordCamp Vienna 2025

In 2024 I held a talk about improving the performance of a website with 500’000 attachments at WordCamps in Karlsruhe(Video) and in the Netherlands(Video). For Vienna in 2025 I applied this talk as a Workshop. Since I needed a similarly huge database, I needed to come up with a solution for it.

FakerPress, wp-cli-fixtures and other dead ends

My first idea was to use FakerPress to create a randomized/anonymous set of data. But knowing the sheer size I needed, I directly checked for a WP CLI command, FakerPress didn’t come with one, but using that I found wp-cli-fixtures, this looked promising. It looked like I could provide a small .yaml file, that would generate a dummy database for my participants. Well it didnt work.

  • 🚧 It didn’t work with PHP 8.4.
  • 💡 7.4 and switching to a fork, it worked
  • 🚧 When trying to generate a few dummy attachments, I saw it actually tried to download these, with a goal of 500k attachments it would have downloaded several TB in the workshop.
  • 💡 Using a 8×8 px jpg stored locally would create roughly 150mb data for the user, which looked good.
  • 💀 Trying to create 50k posts via fixtures took about 30 minutes

After seeing this, I had to switch the approach.

Using the project’s database

Using the real data was the next logical solution. In order to be able to share it, I will need to remove/replace any potentially personal or copyright protected content.

Step 1: Deleting any unnecessary table

Since I want to show how to improve the performance, mostly related to posts and attachments, we can remove custom tables by plugins that will not be part of the workshop.

So I deleted any table from the SEO plugins, contact form plugins, cookie consent, and custom plugins.

Step 2: Anonymizing users

With the help of AI and generator for random names, I created a simple WP CLI command to anonymize users which you can find on my Gist.

All names will be replaced by some random combinations of first names and cat and dog names, as well as create matching email addresses and clearing all passwords.

Step 3: Dropping the comments.

The comments won’t be relevant to the workshop, so we’ll truncate the tables. Originally I intended to use a similar CLI command as for the users, which would be a simple adjustment to the user CLI.

TRUNCATE wp_comments;
TRUNCATE wp_commentmeta;

Step 4: Cleaning wp_posts

We’ll drop all post types we don’t need and all revisions and all orphaned post meta.

DELETE FROM wp_posts WHERE post_type IN ( 'acf-field', 'acf-field-group', 'audiopodcast', 'ep-pointer', 'ep-synonym', 'oembed_cache', 'revision', 'wp_block', 'wpcf7_contact_form');

DELETE pm
FROM wp_postmeta pm
LEFT JOIN wp_posts wp ON wp.ID = pm.post_id
WHERE wp.ID IS NULL;

Step 5: Clearing taxonomies

Removing legacy taxonomies was part of the original talk, but replicating this for the workshop will come at the cost of a huge database. Due to time concerns, we will ignore this and remove all taxonomies but category.

-- 1. Delete from wp_term_relationships
DELETE tr
FROM wp_term_relationships tr
INNER JOIN wp_term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
WHERE tt.taxonomy IN (
    'wpmf-category', 'post_tag', 'brand', 'relevance', 'main_category'
)
   OR tt.taxonomy LIKE 'kf\_%';

-- 2. Delete from wp_term_taxonomy
DELETE FROM wp_term_taxonomy
WHERE taxonomy IN (
    'wpmf-category', 'post_tag', 'brand', 'relevance', 'main_category'
)
   OR taxonomy LIKE 'kf\_%';

-- 3. Delete from wp_termmeta (if you want to remove associated meta)
DELETE tm
FROM wp_termmeta tm
INNER JOIN wp_terms t ON tm.term_id = t.term_id
WHERE t.term_id IN (
    SELECT term_id FROM wp_term_taxonomy
    WHERE taxonomy IN (
        'wpmf-category', 'post_tag', 'brand', 'relevance', 'main_category'
    )
    OR taxonomy LIKE 'kf\_%'
);

-- 4. Delete from wp_terms
DELETE t
FROM wp_terms t
LEFT JOIN wp_term_taxonomy tt ON t.term_id = tt.term_id
WHERE tt.term_id IS NULL;

This cleared 1.8 million rows in total, decreasing the file size and most importantly, the expected import duration down by a significant amount.

Step 6: Masking the content

I basically needed a single post content that would contain all problems I needed to show, while it should still feel kind of natural. I decided to mask the titles by replace every word with a random word from lorem ipsum and published that to my gist(memo: upload it!) and cleaned up some further data. For the_content I went for deleting every single excerpt and every post content in order to file size and import time. These were later replaced with a generic dummy post by a plugin inside the project repository.

Step 7: A lot of manual clean up

These steps are too much to name every single of them.

  • Clear every email address found in the database
  • remove licenses, API keys and similar from wp_options
  • remove transients and other caches to reduce file size
  • and much more

In a follow-up to this, I will talk about the preparations of the workshop itself.

These are some additional resources for my Workshop in Vienna.

Update Query

INSERT INTO wp_custom_meta (post_id, post_hide_from_archive, _user_rate_mean)
SELECT 
    p.ID AS post_id,
    COALESCE(CAST(meta1.meta_value AS UNSIGNED), 0) AS post_hide_from_archive,
    COALESCE(CAST(meta2.meta_value AS FLOAT), 0) AS _user_rate_mean
FROM 
    wp_posts p
LEFT JOIN 
    wp_postmeta meta1 
    ON p.ID = meta1.post_id 
    AND meta1.meta_key = 'post_hide_from_archive'
LEFT JOIN 
    wp_postmeta meta2 
    ON p.ID = meta2.post_id 
    AND meta2.meta_key = '_user_rate_mean'
WHERE 
    p.post_type = 'post'
    AND p.post_status = 'publish'
GROUP BY 
    p.ID;

Recently when reviewing code I stumbled upon $var == "some string" in code, and that sparked the discussion wether you should have strict comparison even for strings.

<?php

// Don't spoil your self, have a guess what weird idea I had to achieve this.
// The solution start at line 42.
//
// Idea by: Christoph Daum
// https://christoph-daum.de

$value = get_customer();

echo 'Loose comparison is: ';
if ( $value == 'Hans Sampleman' ) {
	echo 'True' . PHP_EOL;
} else {
	echo 'False' . PHP_EOL;
}

echo 'Strict comparison is: ';
if ( $value === 'Hans Sampleman' ) {
	echo 'True' . PHP_EOL;
} else {
	echo 'False' . PHP_EOL;
}

echo 'Explicit comparison is: ';
if ( (string) $value === 'Hans Sampleman' ) {
	echo 'True' . PHP_EOL;
} else {
	echo 'False' . PHP_EOL;
}

/*
The code will result in:

Loose comparison is: True
Strict comparison is: False
Explicit comparison is: True
*/

I’ve created proof of concept to show that it actually might be beneficial to be strict, even for strings. While this PoC is crafted and very unlikely to happen in reality, it proofs that you want to be strict.

The magic happens in get_customer() which is missing in the excerpt by design. Feel free to take a quiz and try to guess what happens inside. Feel free to comment if you figured it out without spoiling it in the comments.

This is the complete code example.