Kategorie: eproi pur

  • PHPUnit-Tests: Wenn ein roter Test ein Erfolg ist

    Vor einiger Zeit berichteten wir schon mal darüber, dass wir angefangen haben, PHPUnit-Tests zu verwenden. Inzwischen ist das fast schon ein Standard bei uns geworden – Zeit nochmal einen Einblick in etwas Spannendes zu geben…

    Am Anfang steht natürlich immer die Frage: Was teste ich und wie?

    Ein Test existiert schließlich nicht nur, damit am Ende alles schön grün aussieht. Vielmehr geht es darum, Schwachstellen, Edge‑Cases und unerwartete Situationen vor der Produktivsetzung abzufangen.

    Die Erwartungshaltung ist klar


    Ich schreibe einen Test, lasse ihn laufen und arbeite mich durch die Fehler, bis alles grün ist. Wenn ich gut bin, geht das schnell. Wenn ich schlecht bin oder Folgefehler auftreten, dauert es eben länger.

    Und natürlich bringt mir ein Testpaket nur etwas, wenn ich mich nicht selbst belüge. Also: keine „nur einfachen Tests“, sondern Dinge, die realistisch auftreten können. Ein paar Beispiele:

    • Nachname bleibt in einem Anmeldeformular unbelegt
    • Telefonnummern in verschiedenen Schreibweisen -> wichtig! Die Frage hier ist kann mein Setup damit umgehen
    • vielleicht eine nicht konforme Emailadresse
    • bei API Routen und Co. durchaus mal Testen mit gültigem Auth, ungültigem Auth und keinem Auth. Kommt bei ungültigem und keinen Auth z.B. ein passender 401 bzw. 403 zurück? (Hier gern auch ein Hinweis auf unseren Artikel zum HTTP 401 vs. 403)

    Gerade letzteres ist vielleicht etwas, auf das man nicht zwingend direkt kommt. Aber gerade solche Dinge sind auch wichtig, weil sie in der Realität ein Einfallstor liefern. Eine API Route die unautorisierte Zugriffe erlaubt und vielleicht sogar noch wilde Daten rausgibt – absolut undenkbar.

    Aber muss man dafür immer gleich eine neue Testreihe schreiben?

    Nicht unbedingt! Wir hatten zu Beginn der Entwicklung hier erst mal die API Route lokal ohne Auth hinterlegt. Das ist unsere modale Vorgehensweise: Schritt für Schritt und immer nach dem Credo Form follows function!

    Was wir dann gemacht haben? Naja nachdem wir soweit waren das alle Tests „grün“ waren haben wir die Auth-Middleware aktiviert. Und dann? Einfach die Tests nochmal ausgeführt.

    Natürlich waren alle Tests rot und das war auch erwartbar. Das war hier aber auch das Ziel – so konnten wir ohne neue Tests direkt sagen: Die Auth funktioniert, die Routen liefern korrekt ihr 401 zurück..

    Also ja, unsere Tests waren rot.
    Aber nein, das ist nicht schlimm.
    Es war erwartbar – und in diesem Moment genau das, was wir wollten.

  • Neues Kind in der eproi-Familie: Aeventus.de – für clevere Events

    Bisher stand eproi für maßgeschneiderte WordPress-Plugins und technisch raffinierte Programmierungen. Mit Aeventus präsentieren wir erstmals ein eigenständiges Softwareprodukt – browserbasiert, geräteunabhängig und bereit für den öffentlichen Auftritt.

    Aeventus – was ist das?

    Vielleicht hilft ein Blick auf den Namen, um zu verstehen, worum es geht. Aeventus ist eine Mischung aus dem lateinisch-italienischen „Avanti“ (Vorwärts!) und den englischen Begriffen „Event“ und „Us“. Es geht also um Veranstaltungen – und um uns. Um die Menschen, die planen, durchführen, mitwirken.

    Aeventus ist die Plattform, auf der man schnell und einfach Events einstellen kann – für sich selbst, für das eigene Unternehmen, für das eigene Team.

    Dabei steht nicht nur der Termin im Fokus, sondern die gesamte Dramaturgie: Wer baut vorher auf? Wer hält den Vortrag? Wer macht danach sauber? All das will zeitlich abgestimmt sein. Niemand möchte aufbauen, wenn das Event schon läuft – und die Reinigung bitte auch erst danach. Wenn irgendwo etwas klemmt, soll das schnell sichtbar werden.

    Genau hierbei setzt Aeventus an. Es lassen sich für Unternehmen rasch über z.B. Excel oder CSV Personen importieren – so muss man nicht alles händisch pflegen. Genauso der Import von bestehenden Events. Ob und was man dabei importieren möchte ist über geführte Formulare individuell einstellbar. So passt sich das Tool den eigenen Prozessen an und nicht umgedreht.

    Für wen ist Aeventus gedacht?

    Aeventus ist nicht auf eine bestimmte Branche oder Organisationsform festgelegt.

    Ob privat für eine größere Feier wie eine Hochzeit oder einen runden Geburtstag, in einer Wohngruppe zur Koordination gemeinsamer Mahlzeiten, bei einem Tag der offenen Tür in Schulen oder ganz klassisch für Firmenvorträge, Ausstellungen oder wechselnde Veranstaltungsreihen in Kultur- und Wissenschaftseinrichtungen – Aeventus passt sich an.

    Dabei ist es völlig in Ordnung, wenn nicht alle Elemente gebraucht werden: Vielleicht gibt es keinen Aufbau, weil der Raum bereits vorbereitet ist. Oder die Reinigung entfällt, weil sie extern organisiert wird. Aeventus bietet die Möglichkeit, solche Zeitslots vorzusehen – oder sie einfach wegzulassen. Das Tool passt sich dem Ablauf an, nicht umgekehrt.

    Weitergedacht…

    Aeventus ist längst noch nicht fertig – und das ist Absicht.
    Wir haben uns bewusst entschieden, mit einem sogenannten MVP (Minimum Viable Product) zu starten. Nicht, weil wir etwas Unfertiges auf den Markt bringen wollen oder nach dem Motto leben „reift beim Kunden“. Sondern weil wir so früh wie möglich praktische Erfahrungen und echtes Feedback sammeln möchten.

    Wir wollen nicht alles im Voraus durchplanen – nur um dann festzustellen, dass Nutzer:innen ganz andere Dinge brauchen oder etwas Essenzielles fehlt. Deshalb gibt es Aeventus schon jetzt. Und natürlich haben wir bereits viele Ideen für die nächsten Schritte:
    Von der Termin-Aussendung für gängige Mailclients wie Outlook & Co über PDF-Erstellung bis hin zur API-Ausgabe fertiger Events – vieles ist bereits in Planung.

    Jetzt ausprobieren

    Wir laden alle herzlich ein, Aeventus selbst auszuprobieren.
    Aktuell bieten wir eine zweimonatige kostenfreie Testphase an. Wer uns direkt unterstützen möchte, kann natürlich auch ohne Testphase loslegen .
    Unsere Pakete gibt es monatlich oder jährlich – mit einem Jahresvorteil von zwei Monaten.

    Jetzt direkt auf Aeventus.de vorbeischauen!

  • PHP – catch me if you can!

    In PHP werden natürlich auch Fehler behandelt. Wenn man kritische Fehler nicht abfängt, wandern sie weiter nach oben bis das Script irgendwann eben abbricht. Fast jeder Entwickler dürfte schon einmal eine fatal error message erhalten haben. Oder falls der Server nicht ganz so auskunftsfreudig ist – was natürlich auf Grund der Sicherheit einer Seite begrüßenswert ist – dann eben einfach nur einen 500 internal Server error.

    Beides trägt jetzt in der Regel in Produktivsystemen nicht gerade zur Begeisterung bei. Deswegen könnte der ein oder andere auch schon mal auf die Idee kommen um seinen Code einen try…catch Block zu bauen. Das bietet sich gerad bei potentiell sehr fehleranfälligen Programmpassagen an, hat aber manchmal noch zur Folge, dass der Fehler trotzdem auftritt. Das liegt daran das meistens dann eine solche Struktur genutzt wird:

    try
    {
       echo 50/0;
    }
    catch (Exception $e)
    {
       echo "Division durch null ist nicht erlaubt!";
    }
    

    Das Problem dabei: Nicht alles was auftauchen kann ist auch Exception. Eigentlich ist eine Exception eher sowas wie ein Spezialfall für einen Fehler der auftauchen kann. Deswegen sollte man in einem solchen Fall das ganze besser so aufbauen:

    try
    {
    echo 50/0;
    
    }
    catch (Throwable $e)
    {
       echo "Division durch null ist nicht erlaubt!";
    }

    Denn alles was auftreten könnte ist auf jeden Fall ein Throwable. Hier wird es jetzt korrekt abgefangen und das Programm steigt nicht mehr aus. Natürlich lässt sich das noch verfeinern und erhebt keinen Anspruch auf Vollständigkeit, kann aber in manchen Situationen die Rettung sein…

  • HTTP 403 vs. 401

    Bei HTTP-Statuscodes denkt jeder erst mal an den allseits bekannten 404 – nicht gefunden. Das dürfte in der Regel auch der sein, den man am häufigsten antrifft. Nämlich immer dann, wenn eine Webseite oder eine andere über einen Webserver ausgelieferte Ressource nicht angefunden wurde. Sei es aus einem Schreibfehler heraus oder weil die Ressource tatsächlich nicht oder nicht mehr existiert. Neben diesem sehr wichtigen Code gibt es aber noch zwei weitere bemerkenswerte und aus unserer Sicht vielleicht etwas unterschätzte Codes: 401 – Unauthorisiert und 403 – Verboten; bzw. auf englisch 401 – unauthorized und 403 – forbidden. Doch was sind die Unterschiede oder kann man beide einfach synonym verwenden?

    HTTP-Status-Was?

    HTTP-Status-Codes sind tatsächlich fester Bestandteil des HTT-Protokolls aber meistens bekommt man beim Surfen im Web nichts davon mit. Jede Anfrage wird mit einem solchen Code in der Antwort versehen. Da es jedoch in den eher unsichtbaren Meta-Daten steckt (übrigens nicht verwandt oder verschwägert mit dem großen Konzern ;)), bekommt man nicht sonderlich viel davon mit. Daher ist es wenig überraschend, dass der bekannteste und am häufigsten gesendete Code 200 ist. 200 bedeutet in dem Fall „OK“. Damit jetzt nicht hier alle 5 Klassen und deren Unterelemente vorgestellt werden müssen, verweisen wir für einen weiteren Quereinstieg in das Thema auf die entsprechende Seite im Wikipedia.

    HTTP-Codes 400er-Reihe

    Was man aber an Wissen aus dem bisherigen Text ableiten kann ist, dass es wohl eine 400er „Reihe“ geben muss – diese befasst sich mit den Client-Errors. Technisch gesehen also eher ein Statuscode des Browsers – obwohl auch diese Statuscodes i.d.R. eher vom Server gesendet werden. Im Allgemeinen bedeutet ein 400er Statuscode aber vor allem nicht, dass der Server ein grundlegendes Problem hat, sondern viel mehr, dass eben der Client irgendwas falsches in seine Anfrage gepackt hat. Das kann beim klassischen 404 eben sein, dass in der angefragten URL etwas nicht stimmt. Oder das die URL zwar an sich richtig ist, es die Ressource (also das Bild, das PDF, die Seite selbst) am Server nicht oder nicht mehr präsent ist. Insofern ist das wahrscheinlich ein bisschen hybrid zu sehen zwischen Server und Client. Was aber absolut klar ist: Irgendwas stimmt mit der Anfrage nicht!

    HTTP 401 – Unauthorized

    Ein eher unscheinbarer Code. Damit dieser zum Tragen kommt, muss man entweder in der Programmierung diesen explizit setzen (in PHP z.b. via header(„HTTP 401 – Unauthorized“);) oder sowas wie einen passwortgeschützten Ordner einrichten (im Apache via .htpwd Datei). Das Ergebnis ist jeweils dasselbe: Der Browser gibt keine schön designte Seite aus (es sei denn, man hat eigene Fehlerdokumente hinterlegt – aber dazu in einem anderen Post mehr). Doch was besagt dieser Code? Wie sich vermuten lässt aus den passwortgeschützten Verzeichnissen wollte der Anfrager auf eine Ressource zugreifen, die irgendeine Art Authorisierung erfordert – meistens ein Username/Passwort Gespann. Und dieses wurde entweder gar nicht mitgeliefert oder enthält einen Fehler (Schreibfehler, unbekannter Benutzername, Passwort falsch). Was genau nicht stimmt lässt sich hierüber leider nicht rausfinden. Wobei das aus Sicherheitsaspekten durchaus gut ist – aber dazu auch gleich nochmal mehr!

    HTTP 403 – Forbidden

    Einige werden jetzt sehr logisch rangehen und sagen „Hä? Das ist doch im Prinzip dasselbe!?“. Und wie immer ist die klassische Antwort darauf: Jain. Aber mal der Reihe nach. ein Code „Forbidden“ braucht technisch gesehen keine Logindaten. Es spielt bei dem Statuscode absolut keine Rolle ob jemand vielleicht sogar einen administrativen Zugang zu einem Bereich hat: Es ist schlicht und ergreifend verboten, zuzugreifen. Dieses Verboten ist jedoch recht „weich“, also es muss gar nicht zwingend sein, dass ein wirkliches Verbot vorliegt, es genügt z.B. schon, dass eine Ressource mit http statt https aufgerufen wurde und der Server so konfiguriert ist, dass http-Aufrufe eben verboten sind (kommt seltener zum Tragen, weil die meisten Server eine http-Anfrage stillschweigend auf https „upgraden“). Wie auch beim 401 wird hier keine weitere Erklärung mitgeliefert, warum das Verbot gilt. Auch das ist gut und auch dazu gleich nochmal mehr.

    Weil du unauthorized bist, ist der Zugriff verboten

    Jetzt könnte man ja sagen, man schenkt sich den einen oder den anderen http-Code weil jeder Zugriff, der ohne ein passendes Login daherkommt, gleichzeitig auch verboten ist. Und umgedreht? Ist jeder verbotene Zugriff auch gleich unauthorized? Leider nicht ganz. Rein technisch haben wir ja eben gerade schon beschrieben, dass bei 403 – Forbidden nicht zwingend vorher eine Username/Passwort Abfrage „schuld“ sein muss. Insofern gilt – rein technisch bzw. logisch gesprochen – zwar das eine, aber nicht das andere zwangsläufig. Natürlich kann man den Statuscode jeweils umschreiben und einen Server wie den Apache oder das eigene PHP-Programm so gestalten, dass eben nur einer von beiden Statuscodes zum Tragen kommt. Schließlich geht es ja nur darum, dem Client irgendwie etwas mitzuteilen, warum seine Anfrage zumindest gerade und mit den situativen Umständen nicht geht. Und es macht aus unserer Sicht manchmal sogar ein bisschen Sinn.

    Zum Hintergrund: Natürlich wissen auch diverse – auch schädliche – Bots und auch Angreifer den Unterschied zwischen den verschiedenen Statuscodes. Sende ich also ein 401 – Unauthorized, weiß der Client fast automatisch, dass es die Ressource prinzipiell gibt, es aber einen gesonderten Login oder eine andere Sicherungsmaßnahme bedarf um darauf zuzugreifen. Das ist für einen Angreifer durchaus schon eine lohnende Info. Denn am Anfang steht meist erst mal das Informationen sammeln zur jeweiligen Umgebung. Das heißt es geht noch gar nicht darum, direkt auf irgendwas zuzugreifen, sondern schlicht um einen Gesamteinblick.

  • Von Gitea nach Gitlab

    In der heutigen Zeit hat sich immer mehr durchgesetzt, Softwareprojekte mithilfe einer Versionierungssoftware zu entwickeln. Vorteile gibt es dabei tatsächlich nicht nur für Teams. Auch „Soloprojekte“ können von einer solchen Versionierung profitieren. Am Markt gibt es sehr verschiedene Systeme und dann stellt sich noch die Frage: Selbst hosten oder lieber einen Account bei einem größeren Anbieter nutzen? Ein Komplettüberblick für alle Varianten mit allen Vor- und Nachteilen kann es nicht geben. Wir wollen aber an dieser Stelle unseren Weg aufzeigen und vielleicht auch dokumentieren, warum der Anfang zwar gut war, bald aber nicht mehr so praktikabel.

    Ausgangssituation

    Aktuell gibt es bei uns nur „Soloprojekte“ also Projekte, bei denen ein Entwickler sich um die Programmierung innerhalb des Projektes kümmert. Perspektivisch könnte es aber auch passieren, dass so ein Projekt mit anderen zusammen bearbeitet wird – sei es Externen oder aber auch interne Mitarbeiter. Deswegen wurde zwischendurch beschlossen, für alle Projekte bereits eine Versionierung aufzusetzen. Auch mit dem Hintergrund, dass es immer mal wieder vorkommt, dass eine gewünschte Änderung dann doch nicht das passende für den Kunden ist. Das ist mit der Versionierung kein Problem: Richtig aufgesetzt kann man eine Änderung rasch zurücknehmen und bekommt einen sauberen Stand – ohne sich Gedanken machen zu müssen, ob man jetzt noch eine Codezeile vergessen haben könnte.

    Der Anfang: selbst gehostet oder bei einem Anbieter?

    Ok, der Wunsch war also da: Es sollte eine Versionierung eingeführt werden. Doch damit kamen erst mal die Fragen. Welches System passt überhaupt? Und danach: Wird ein großer Anbieter genutzt oder wird das auf einem eigenen Server selbst gehostet? Gleich vorweg: Wir haben uns fürs selbstgehostete Modell entschieden. Warum? Auch weil es ein bisschen mehr Kontrolle darüber liefert, wo die Daten liegen… Stichwort DSGVO und Co. Aber der Reihe nach.

    Schnell war hier klar es sollte etwas wie „git“ sein. Weithin bekannt und auch schon aus anderen Projekten und der Beschäftigung als Angestellter vertraut. Damit waren die Variationen schon mal eingeschränkt. Nach kurzer Recherche war dann auch klar, dass mit Gitea eine „Git-Version“ bereitsteht, die einfach zu installieren ist und damit perfekt geeignet für eine selbstgehostete Variante. Da auf dem passenden Server sonst nichts weiter laufen sollte an zusätzlichen Services haben wir also einen „nackten“ Server aufgesetzt, mit den notwendigen Dingen wie Apache, Datenbank und Co ausgestattet und Gitea darauf eingerichtet. Anschließend konnte nach Einrichtung der SSH Keys und eines Tortoise Git Systems auf dem eigenen Windowsrechner sofort mit dem Anlegen und Versionieren unserer Projekte begonnen werden.

    Was bietet Gitea?

    Kurz zusammengefasst: Gitea fußt auf der git-Software auf, ist aber bewusst etwas schlanker gehalten. Einige Randfunktionen bietet Gitea nicht, das war aber zu dem Zeitpunkt kein Problem. Es ging ja nur erst mal darum, etwas zu Versionieren. Und das kann Gitea genauso wie Gitlab oder andere Softwares dieser Art. Selbst mit Branches und Merges hat Gitea kein Problem. Wirklich? Naja nicht ganz. Bei den Branches gibt es schon die erste Einschränkung.

    Erste „Probleme“ mit Gitea…

    Branches kann Gitea aber bei den Merge-Requests geht nur Sekt oder Selters. Heißt konkret: Ein Branch kann mittels Merge-Requests nur in seiner Gesamtheit abgelehnt oder angenommen werden. Es ist nicht möglich, einige Änderungen in den Hauptpfad zu committen und andere zurückzuweisen. Wir haben das auch festgestellt, als auf einmal in einem Projekt vollkommen unvermittelt noch ein weiterer Externer Programmierer vom Auftraggeber zugelassen wurde und es auf einmal darum ging, die eigenen Änderungen mit den Änderungen des Kollegen so zusammen zu bekommen, dass am Ende alles erhalten blieb, was notwendig war. Wir haben das hier versucht über eben einen Branch, in dem erst mal die Änderungen des Anderen eingespielt wurden und dann sollte über einen Merge-Request das ganze vereinigt werden. Gitea zeigte auch brav alle Änderungen an. Aber eine Funktion, die die Übernahme bestimmter Änderungen zuließ während anderes nicht übernommen würde gab es nicht. Eben Sekt oder Selters. 😉

    Das war dann jedenfalls für uns der Moment, nochmal zu überdenken, ob ein Gitea wirklich weiterhelfen kann. Aber was gibt es für Optionen?

    Ein Seitenblick: Gitlab vs. Gitea

    Wie bereits erwähnt fußen mehrere Angebote auf git auf. Am bekanntesten neben Gitea dürfte Gitlab sein. Etwas mächtiger in den Funktionalitäten und etwas schwieriger aufzusetzen. Wir haben uns daran gewagt und werden in einem weiteren Post nochmal versuchen zusammenzufassen, auf was besonders geachtet werden sollte.

  • PHPUnit-Tests: Ersterfahrung

    PHPUnit-Tests gibt es schon seit über 20 Jahren. Bisher haben wir aber für unsere Entwicklungen eher auf „klassische“ Tests gesetzt. Heißt: Wir haben eine Software geschrieben und diese dann auf einem Testsystem „live“ ausprobiert. Um unsere Softwarequalität aber zu steigern und gleichzeitig Programmierstandards zu pflegen haben wir beschlossen in Zukunft bei der PHP-Entwicklung auch auf PHPUnit-Tests zu setzen. Ein erster Erfahrungseinblick.

    Ausgangssituation

    Wir hatten sowieso gerad ein neues Projekt am Start – diesmal wieder die Entwicklung eines WordPress-Plugins – und Ende letzten Jahres gerade auf der betterCode()-Konferenz für den Bereich PHP nochmal oder wieder von PHPUnit als „Test-Suite“ gehört. Da ja Stillstand bekanntlich den Tod bedeutet haben wir also mutig beschlossen, unsere Softwarequalität mithilfe dieses Tools einmal gehörig voranzubringen.

    Natürlich behalten wir auch unsere bisherige Testmodalität mit der Testinstallation bei. Schließlich soll ja auch das Aussehen und die Funktionalität im Browser getestet werden. Die PHPUnit-Tests werden wir aber zukünftig verstärkt einsetzen um bereits vorab generelle Tests aufzubauen, die einfach verhindern, dass wir schon mit ganz rudimentären Fehlern an den Start gehen, wie z.B. ein vergessenes Semikolon am Ende oder eine zu viel geöffnete oder geschlossene Klammer…

    Also wir hatten ein brandneues Projekt mit ohne irgendwas. Lokal haben wir einen Apache am Laufen und auch einen MariaDB-Server. Etwas googlen brachte uns dann noch den Hinweis, das es auch für WordPress eine Art „development“ Zweig gibt, mit dem man dann auch in PHPUnit testen können soll. Gesagt getan…

    Einrichten und erst mal Testen der Testumgebung

    Ja richtig: Bevor man sowas nutzen kann muss man das ja erst mal einrichten. Wir haben also erstmal das WordPress-Verzeichnis hingestellt und dann ging der Spaß auch schon los: Zum Einrichten hat uns das große Internet verraten, dass wir vermittels npm und grunt das ganze so aufsetzen können, dass es für unsere Unit-Tests läuft. Das war aber mal gar nicht so einfach. Es gab node und npm aber wir mussten zunächst die Pfade in die Umgebungsvariablen bringen bzw. verbessern, da node und npm ursprünglich über Visual Studio (manchmal entwickeln wir auch in C# ;))  auf das System kam und in dem Ordner noch nicht anständig wollte. nachdem wir das soweit hatten und auch alles sauber loslief, haben wir ins passende Plugin-Verzeichnis unser neues „Plugin“ kopiert und dann einen Ordner Tests angelegt.

    PHPUnit-Tests – jetzt aber!?

    Um erst mal warm zu werden haben wir erst mal ganz einfache Tests geschrieben. Wir wollten zunächst nur prüfen, ob unser Plugin aktiviert wurde, einen Menüeintrag im Adminmenu erzeugt und die Adminseite im Ansatz beim Aufruf der URL irgendwas an Inhalt erzeugt. Da kam aber schon die erste Frage in uns auf: Wie aktiviert man denn ein Plugin, wenn man gerade nicht die WordPress-typische Pluginseite vor der Nase hat? Etwas knifflig. Wir befragten also wieder das Internet und dort hieß es: Einfach in der Datenbank in den Optionen das Plugin unter den aktivierten Plugins mit eintragen. Ok – also auf die lokale DB mittels phpMyAdmin gegangen und: Die Datenbank ist ja leer? Moment – wir hatten doch vorhin mittels npm und grunt das ganze „installaliert“? Offenbar gereicht diese „Installation“ aber nicht soweit, dass auch eine DB ordentlich angelegt wird. Das Internet riet dazu, aus einer anderen frischen WordPress-Installation einfach die WordPress-eigenen Tabellen zu importieren. Gut also auch das erledigt.

    Jetzt aber zu den Tests? Noch nicht ganz. Unser geschriebener Test hatte als erstes Problem das die Ausgabe bereits erzeugt wurde, bevor das PHPUnit-Testgebilde seine Ausgabe gestartet hat. Ein klassisches Problem von Output vor den Headern. Aber auch sonst gab es haufenweise Probleme. Es genügte nicht die wp-load.php einzubinden und ggf. noch die wp-config. Es musste tatsächlich für alle benötigten Funktionen jeweils die passende wp-* Datei eingebunden werden in die Testklasse.

    Erstes Fazit

    Naja so ganz zufrieden sind wir mit den Bedingungen noch nicht. Eigentlich war nicht der Plan, alle Dateien „manuell“ einzubinden nur um dann einen Test fahren zu können. Der Plan war, eine Art WordPress-Instanz zu haben, in der man dann vielleicht noch mittels einfacher Hooks oder ähnlichem die benötigten Seiten und Eigenschaften aufrufen kann. Das hat bisher noch nicht geklappt. Aber vielleicht finden wir auch dafür demnächst nochmal eine etwas charmantere Konfiguration. Wir lernen ja noch… Und ach ja: Die Tests sind am Ende erfolgreich verlaufen. 😉

  • WordPress Posts und Taxonomies

    WordPress: wp_set_object_terms und zwei Kuriositäten aus der Praxis

    WordPress bietet für die programmatische Zuweisung von Taxonomien zu einem Post — etwa einem Bild — eine praktische Funktion:

    wp_set_object_terms()

    So hilfreich sie ist, so hat sie doch ein paar Tücken.
    Zum einen prüft die Funktion nicht, ob zwischen dem Post und dem Term bereits eine Relation besteht. Das ist bekannt und wird auch in der WordPress‑Entwicklerdokumentation beschrieben.

    In der Praxis gibt es jedoch zwei weitere Kuriositäten, über die man leicht stolpert.

    1. Mehrere Terms nacheinander setzen? Vorsicht.

    Wenn man versucht, mehrere Terms nacheinander einzeln zuzuweisen — also nicht als Array, sondern in mehreren Funktionsaufrufen — landet am Ende nur eine einzige Relation in der Datenbank.
    Hat man drei Terms, verschwinden zwei davon einfach.

    Lösung


    Alle Terms in ein Array packen und dieses Array an wp_set_object_terms() übergeben.
    Nur dann werden alle Relationen korrekt berücksichtigt.

    2. Typisierung der Term‑IDs ist entscheidend

    Ein weiteres Problem betrifft die Typisierung der übergebenen Werte.
    Wenn man Term‑IDs nicht explizit typisiert, kann es passieren, dass WordPress einige Werte als Strings interpretiert — mit entsprechend unerwarteten Ergebnissen.

    Beispiel

    (int)$term_id

    oder beim Befüllen eines Arrays:

    array_push($term_ids, (int)$term_id);

    Ohne diese Typisierung kann WordPress intern durcheinanderkommen, weil nicht sauber geprüft wird, was tatsächlich in $term_id steckt.

    Fazit

    Mit diesen beiden Vorsichtsmaßnahmen — Terms immer als Array übergeben und IDs sauber typisieren — verhält sich wp_set_object_terms() so, wie man es erwarten würde.

    Dass WordPress an dieser Stelle so wenig prüft, ist allerdings bemerkenswert.


    Vielleicht ergibt sich irgendwann die Gelegenheit, auszuprobieren, was passiert, wenn man der Funktion bewusst „unsaubere“ Strings übergibt…

  • ASP.Net Core und Authentication

    ASP.NET Core, Blazor und der verschwundene „Authenticated User“

    Wir arbeiten aktuell an einer Eigenentwicklung auf Basis von C# ASP.NET Core und Blazor.
    Im Zuge dieser Entwicklung haben wir natürlich auch die vom Framework bereitgestellte Login‑Mechanik implementiert. Und wie es bei .NET‑Updates manchmal ist: Nach einem Update funktionierte plötzlich nichts mehr so, wie es sollte.

    Der Login selbst lief scheinbar korrekt durch. Im Konsolenlog erschien „User logged in“, aber in den nachfolgenden Bereichen — dort, wo Daten basierend auf dem aktuellen Benutzer geladen werden sollten — meldete das System plötzlich, dass kein „authenticated user“ vorhanden sei.
    Merkwürdig.

    Parallel dazu sorgten verschiedene Rücksetzungen von NuGet‑Paketen dafür, dass das Projekt in der Visual Studio 2022 Preview gar nicht mehr starten wollte. Die Lösung dafür war simpel: Projekt in der regulären Visual Studio 2022 Version öffnen. Die Preview war ursprünglich installiert worden, als MAUI, Blazor und .NET 8 gerade frisch integriert wurden — inzwischen aber nicht mehr notwendig.

    Der eigentliche Fehler blieb jedoch bestehen:
    Seite aufrufen → Weiterleitung zum Login → erfolgreicher Login → zurück zur ursprünglichen Seite → Benutzer nicht authentifiziert → keine Daten.

    Die Anwendung ist eine hybride Blazor‑App, bestehend aus einem serverseitigen und einem clientseitigen Teil. Beide müssen — damit die Authentifizierung sauber funktioniert — den passenden AuthenticationStateProvider implementieren. Das war auch der Fall.

    Allerdings hatte sich im Update‑Chaos unbemerkt ein Scope verändert:

    builder.Services.AddScoped<AuthenticationStateProvider, PersistingRevalidatingAuthenticationStateProvider>();

    war plötzlich zu

    builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();

    geworden.

    Offenbar vertragen sich beide Varianten nicht besonders gut — zumindest nicht so, dass beide Seiten einer hybriden Blazor‑App zuverlässig mitbekommen, dass ein Benutzer erfolgreich eingeloggt wurde.
    Nach der Rückumstellung funktionierte die Authentifizierung sofort wieder.

    Woher die Änderung ursprünglich kam, können wir nicht sagen — und haben an der Stelle auch nicht weiter nachgeforscht. Wahrscheinlich hätte auch die umgekehrte Variante (beidseitige Umstellung auf IdentityRevalidatingAuthenticationStateProvider) funktioniert.

    Wichtig war: Der Scope musste auf beiden Seiten konsistent sein.
    Danach lief alles wieder stabil.