Inloggen via Passkeys toegevoegd!

Passkeys zijn een veilige manier van inloggen die ons hopelijk eindelijk gaat verlossen van die vreselijk onhandige wachtwoorden. Naar aanleiding van een stuk in cT-magazine heb ik Passkeys als tweede optie toegevoegd voor het inloggen op deze site. Het voordeel van het gebruik van WordPress is dat daar al een plugin voor is: WebAuthn. Nooit gedacht dat WordPress nog eens een voordeel zou zijn op veiligheidsgebied 😃

Today I learned…

Last weekend, I decided to update this website: the WordPress version, the plugins and the PHP version on the server itself. That last update had an interesting consequence: the OpenID plugin broke (which I didn’t notice until I got a nightly warning email).

The (kindly) supplied error message and stacktrace pointed quite straightforward to the cause of the problem: ” Uncaught ValueError: Unknown format specifier ” ” … #0 /home/xxx/domains/erikroos.org/public_html/wordpress/wp-content/plugins/wp-openid-selector/wp-openid-selector.php(63): printf()”.

I decided to take the hard route and try to fix the problem by hand. On the indicated line I found a printf statement with a %1 placeholder in it. As always, Stackoverflow gave the solution almost immediately: in PHP 8, a placeholder must always be terminated with a $, and optionally a format specifier like “s” (for string). So I replaced all the %1’s with %1$1 and, simple as that, OpenID worked again, allowing me to log in again (which can be quite handy).

When looking at the plugin code, I noticed the use of a function “named” __. Some googling showed that this is a built-in WordPress function for translating the given string using the current locale. It is somewhat similar to PHP’s own gettext() function, which can be aliased using a single _. And I thought Python was strange for using dunders (double underscores) in function names!

Over het verdwijnen van streektalen

Zoals we deze week hebben kunnen lezen, verdwijnen de streektalen in het Nederlandse taalgebied in rap tempo. Ik zie (en vooral: hoor) dat al langer gebeuren en vind dat, hoewel het logisch en onvermijdelijk is, jammer.

Streektalen vertellen ons veel over de geschiedenis van een land. In Nederland zie je bijvoorbeeld Friese, Frankische en Saksische talen en mengvormen waar deze volkeren op elkaar gebotst zijn. Aan (streek)taalgrenzen kun je ook nu nog zien hoe 1500 jaar geleden de Grote Volksverhuizing verlopen is. Dat is toch fascinerend!

Ik noemde de mengvormen al, ook een interessant fenomeen. Zo vormen streektalen een continuüm dat landsgrenzen, geschiedkundig een vrij recente uitvinding, overschrijdt. Zo kun je in het Noorden, van west naar oost, van het Fries, via het Westerkwartiers (een Saksisch-Friese mengtaal) en het Gronings naar het Oostfries aan de andere kant van de grens komen. Prachtig om te zien hoe men vroeger over grenzen heen met elkaar kon communiceren in de eigen taal, zonder op een vreemde taal zoals het Engels terug te hoeven grijpen.

Ten laatste valt mij op dat streektalen soms schitterende woorden en uitdrukkingen kennen, die nu met hen dreigen te verdwijnen. In het Gronings is iets niet mooi maar schier, of zelf aibels schier, en als je doodgaat dan sterf je niet maar “kom je oet de tied”. Wat een filosofische gedachte zit daarachter!

Troost is dat al dit moois goed gedocumenteerd is en daardoor niet echt verloren zal gaan. Maar het leven is er straks wel uit, er komt niets nieuws meer bij. Zonde. En tevens het voorland van het Nederlands: ik denk dat dat, met het oprukkende Engels, over 100 jaar in dezelfde positie verkeert. We hebben hier dan een dialect van het Engels, met een lichte Nederlandse tongval en enkele resterende woorden.

Spijtig maar onvermijdelijk. Maar om het proces te vertragen heb ik, als IT’er, nog wel een initiatief bedacht: ik ga bezig om in Python een geheel automatisch vertaalprogramma Nederlands-Gronings te maken, de Grunnificator. Ik houd jullie op de hoogte van de voortgang en hoop hem over een tijdje online te kunnen zetten Tot die tijd: kop d’rveur

Today I learned: hoe verhelp ik het nare, gierende geluid van een Philips-scheerapparaat

Al sinds mijn achttiende scheer ik mezelf elke dag trouw. Na een korte periode van nat scheren met mijn opa’s oude Gilette uit de jaren 1950 (en na afloop met zo’n blokje aluin over mijn gemartelde velletje heen), ben ik al gauw overgestapt op elektrisch scheren. Het werd een Philishave en dat is het altijd gebleven, want het beviel nou eenmaal goed. Tot mijn jongste scheerapparaat, eentje uit de 5000-serie, een hoog, gierend geluid begon te maken tijdens het scheren. Ik dacht dat er iets aanliep in een van de scheerkoppen en begon maar eens met het hele apparaat schoon te maken. Azijn tegen de kalk, wasbezine tegen de vettigheid: het apparaat werd schoner dan ooit maar het piepende geluid bleef. Bijzonder irritant! Tot ik ineens een ingeving kreeg en een druppeltje olie (lijnzaad geloof ik) bij de aandrijving van de onderste scheerkop in goot. Opgelost! Hemelse stilte tijdens het scheren! Dus: herken je dit probleem, een druppeltje olie is je redding en je Philishave kan nog jaren mee.

Drupje olie bij de onderste scheerkop, et voilà

Wat is er met Speeltuinzoeker gebeurd?

Geregeld krijg ik nog de vraag hoe het met mijn speeltuinen-website gaat. Het antwoord is: hij bestaat nog, maar eerlijk gezegd doe ik er weinig meer mee. Waarom dan? Het concept bleek toch niet zo aan te slaan als ik had gehoopt. Het was moeilijk om een “community” van ouders van de grond te krijgen die informatie over speeltuinen met elkaar gingen delen. Facebook-advertenties, in combinatie met winacties, zijn een goede manier om (tijdelijke) activiteit te genereren, maar zodra je ze stopzet, houdt dat ook op. Waarschijnlijk had ik er met heel veel tijd energie wel een levendige site van kunnen maken, maar die beide factoren zijn bij een vader van drie jonge kinderen, met een fulltime baan, niet bepaald voorradig. Het was in ieder geval een leerzame ervaring en ik heb de domeinnaam nog 🙂 De hosting vindt nu echter hier, op erikroos.org, plaats. Wie weet wordt Speeltuinzoeker nog eens (her)ontdekt en gaat het nog een grootse toekomst tegemoet!

Zie www.speeltuinzoeker.nl oftewel erikroos.org/speeltuinzoeker

Sorting algorithms III – 9 years later

And so I’m back! I haven’t been here for quite some time, and a lot has happened in the meantime. I’ve worked as a Customer Support Manager at Eezeebee (a small software company for fashion B2B’s and B2C’s) for quite some time, but recently I made a career change by becoming something I never thought I’d become (mostly because my father was one): a teacher! I’ve recently begun as an IT teacher at the Hanzehogeschool (University of Applied Sciences). It’s hard work but it’s fun and it’s rewarding. And it brought me back to that old passion o’mine: sorting algorithms 🙂 One of the courses I teach, together with the esteemed Bas Heijne, is Algorithms and Data Structures, wherein sorting is used as an example case for developing efficient algorithms. Reading back on what I wrote 9 years ago, it seems to me that the infamous H Sort is nothing more than Selection Sort (although the keeping-in-memory part could be a small improvement). But… the other day I thought of yet another sorting algorithm: Index Sort, or maybe Hash Sort. It has to do with storing items in an array using their value (or a hash of it) as the index. Theoretically, this could give you a Big O of N (N times O(1)). I’m still developing and testing it using good old Java, and the results seem promising, but I have no final results yet. So, again, stay tuned for more on Sorting!

Today I learned…

PHP has a nice library to work with zip archives: ZipArchive. However, you cannot open zip files that are hosted on some website. First you have to download the zip file, and then you can open it using ZipArchive. An example:

$file = 'http://www.somesite.com/file.zip';
file_put_contents('file.zip', fopen($file, 'r'));
$zip = new ZipArchive();
if ($zip->open('file.zip') === true) {
    for ($i = 0; $i < $zip->numFiles; $i++) { 
         $stat = $zip->statIndex($i);
         if (strpos($stat['name'], 'partoffilename') !== false) {
             $fileContents = $zip->getFromIndex($i);
             // Do something here...
         }
    }
}

Also note something interesting here: if you don’t know the exact name of the file inside the zip archive that you want to open, you cannot use getFromName(), so you have to take another approach. In the above example, I iterate through the files in the archive using an index, checking every file for the part of the filename that I do know. When there’s a match, I can use the current index and getFromIndex() to open the file.

Today I learned…

In PHP, if you sort -or better: do not sort- an array, using a custom function that happens to return 0 every time, the resulting order is undefined as opposed to the original order as you might expect.

So, if you do something like this and X is never set:

usort($arr_content['content']['results'], function($a, $b) {
    if (isset($a['X']) && isset($b['X'])) {
       if ($a['X'] == $b['X']) {
            return 0;
        }
        if ($a['X'] < $b['X']) {
            return -1;
        }
        return 1;
    }
    if (isset($a['X'])) {
        return -1;
    }
    if (isset($b['X'])) {
        return 1;
    }
    return 0; // this is returned for every element since $a['X'] and $b['X'] are never set
 });

you end up with an array that’s sorted in an unexpected way, in my case even perfectly reversed!

I had to work around this “feature” of PHP by checking the number of undefined X’es in the array and skipping the usort() of their number is equal to the number of elements in the array.

Sorting algorithms II

As posted a few days ago, I experimented a bit with a new sorting algorithm called H Sort. I built a simple testing framework in Java to see how well it performs compared to traditional Q Sort.

First I steadily increased the number of runs. I noticed that the average time it takes to do a sorting run decreases with the total number of runs. At first this seemed strange because the runs are unconnected, a new array of random numbers generated each run. Then I realized that this effect is probably due to more processing power and cores being allocated to the process when the number of runs increases. The average sorting times of the two algorithms begin to settle at 1,000 runs and become really stable at 100,000 runs.

  • Times: 100 Orderedness: 0% Size: 100 Average time Q: 99,32 micros Average time H: 152,88 micros Factor H vs. Q: 1,54
  • Times: 1000 Orderedness: 0% Size: 100 Average time Q: 9,40 micros Average time H: 37,18 micros Factor H vs. Q: 3,95
  • Times: 10000 Orderedness: 0% Size: 100 Average time Q: 8,92 micros Average time H: 35,15 micros Factor H vs. Q: 3,94
  • Times: 50000 Orderedness: 0% Size: 100 Average time Q: 8,81 micros Average time H: 33,52 micros Factor H vs. Q: 3,81
  • Times: 100000 Orderedness: 0% Size: 100 Average time Q: 8,72 micros Average time H: 32,94 micros Factor H vs. Q: 3,78
  • Times: 500000 Orderedness: 0% Size: 100 Average time Q: 8,87 micros Average time H: 33,52 micros Factor H vs. Q: 3,78
  • Times: 1000000 Orderedness: 0% Size: 100 Average time Q: 8,77 micros Average time H: 33,15 micros Factor H vs. Q: 3,78
Having found the optimal number of runs, I then varied the ‘orderedness’ of the generated sort lists. Orderedness corresponds to the chance that the number at index i is equal to i. If not, it is a random number. So an array with 0% orderedness is a completely random array, whereas one with 100% orderedness goes like 1, 2, 3, …, 100. I read that Q Sort doesn’t perform well on already quite ordered lists. I can verify that, except that when comparing to H Sort that moment is reached only around 98% orderedness, which is an almost completely ordered array. So no great advantage of H Sort so far…
  • Times: 100000 Orderedness: 0% Size: 100 Average time Q: 8,88 micros Average time H: 33,60 micros Factor H vs. Q: 3,78
  • Times: 100000 Orderedness: 10% Size: 100 Average time Q: 8,70 micros Average time H: 33,43 micros Factor H vs. Q: 3,84
  • Times: 100000 Orderedness: 20% Size: 100 Average time Q: 8,50 micros Average time H: 33,31 micros Factor H vs. Q: 3,92
  • Times: 100000 Orderedness: 30% Size: 100 Average time Q: 8,28 micros Average time H: 33,07 micros Factor H vs. Q: 4,00
  • Times: 100000 Orderedness: 40% Size: 100 Average time Q: 8,00 micros Average time H: 32,45 micros Factor H vs. Q: 4,05
  • Times: 100000 Orderedness: 50% Size: 100 Average time Q: 7,76 micros Average time H: 31,75 micros Factor H vs. Q: 4,09
  • Times: 100000 Orderedness: 60% Size: 100 Average time Q: 7,52 micros Average time H: 31,05 micros Factor H vs. Q: 4,13
  • Times: 100000 Orderedness: 70% Size: 100 Average time Q: 7,29 micros Average time H: 30,12 micros Factor H vs. Q: 4,13
  • Times: 100000 Orderedness: 80% Size: 100 Average time Q: 7,06 micros Average time H: 28,45 micros Factor H vs. Q: 4,03
  • Times: 100000 Orderedness: 90% Size: 100 Average time Q: 7,07 micros Average time H: 23,88 micros Factor H vs. Q: 3,38
  • Times: 100000 Orderedness: 95% Size: 100 Average time Q: 7,42 micros Average time H: 16,93 micros Factor H vs. Q: 2,28
  • Times: 100000 Orderedness: 98% Size: 100 Average time Q: 8,15 micros Average time H: 8,85 micros Factor H vs. Q: 1,09
  • Times: 100000 Orderedness: 99% Size: 100 Average time Q: 8,55 micros Average time H: 5,05 micros Factor H vs. Q: 0,59
  • Times: 100000 Orderedness: 100% Size: 100 Average time Q: 9,08 micros Average time H: 0,50 micros Factor H vs. Q: 0,06
Maybe better luck for H Sort when varying the array size? Indeed, H sort performs almost as well as Q Sort on arrays of size… 10. When increasing the array size, Q Sort quickly takes the lead. And then we run into the limitations of my Core i7 2.8 GHz laptop: sorting arrays of 10,000 numbers for 100,000 times took so much time I eventually gave up and killed the process.
  • Times: 100000 Orderedness: 50% Size: 10 Average time Q: 0,48 micros Average time H: 0,51 micros Factor H vs. Q: 1,08
  • Times: 100000 Orderedness: 50% Size: 100 Average time Q: 7,74 micros Average time H: 31,75 micros Factor H vs. Q: 4,10
  • Times: 100000 Orderedness: 50% Size: 500 Average time Q: 58,33 micros Average time H: 359,31 micros Factor H vs. Q: 6,16
  • Times: 100000 Orderedness: 50% Size: 1000 Average time Q: 127,35 micros Average time H: 1323,50 micros Factor H vs. Q: 10,39

All in all we can say that H Sort is not a very good algorithm, which in no useful situation is able to outperform good old Q Sort. It was fun developing and testing it, but I’ll leave it at this. The project can be found on GitHub for those who want to try themselves and maybe improve the algorithm.