
Teilen Sie:
Als ausgebildeter Schauspieler mit einer Dissertation in Standup-Comedy bin ich über die Meetup-Szene zur PHP-Entwicklung gekommen. Man findet mich, wenn ich über Technik spreche oder schreibe, oder wenn ich seltsame Platten aus meiner Vinylsammlung spiele oder kaufe.
Scrub Up! Reinigung Ihrer PHP-Anwendung mit PHPStan
In meiner Zeit als PHP-Entwickler hat sich die Art und Weise, wie wir Code schreiben und ausliefern, dramatisch verändert. In den frühen Symfony und Zend Framework Applikationen, die PHP-FIG nicht existierte und die Codierungsstandards im Ermessen desjenigen lagen, der sie schrieb. In den Jahren, in denen wir eine weit verbreitete Annahme von PSR-Standardswar das Angebot an soliden statischen Analysewerkzeugen eher lückenhaft. Das heißt, bis jetzt, mit der Veröffentlichung der Version 1.0 von PHPStan. Lassen Sie uns diesen Anlass feiern, indem wir einige der Funktionen durchgehen!
Kompilierte Sprachen, Ihr präemptiver Bug-Squasher
Einer der großen Vorteile der Verwendung einer kompilierten Sprache wie Java oder C# ist, dass die Kompilierzeit komplett versagt, wenn Ihr Code nicht Typesafeund Standards durchsetzt (obwohl ich das leicht sagen kann, da es nicht 2 Uhr morgens ist und ich gerade meinen zehnten Kaffee trinke). Mit PHP eine interpretierte Sprache ist, haben wir nicht denselben Luxus.
Wird als kompiliert interpretiert: CI + Tooling
Dank der schieren Menge an DevOps-Tools, die uns in der modernen Webentwicklung und statischen Analyse zur Verfügung stehen, ist es so wir haben die gleichen Werkzeuge zur Verfügung, aber mit anderen Mitteln. Da dies der Fall ist, kann ich nicht befürworten wie sehr Ich empfehle Ihnen, eine ähnliche Umgebung wie die, die ich Ihnen vorstelle, zu schaffen. Warum sollten Sie also diese Werkzeuge brauchen? Lassen Sie uns ein Beispiel betrachten.
Das Szenario
Es ist üblich, ein Thema zu wählen, das Spaß macht oder zu dem passt, worüber man schreibt, wenn es um Werkzeuge wie dieses geht. In diesem Artikel möchte ich Ihnen jedoch ein Szenario vorstellen, das mir persönlich immer wieder in Agenturen begegnet ist:
"Hilfe! Jemand anderes hat meine PHP-Anwendung gebaut und ich brauche jemanden, der sie rettet und die Wartung übernimmt, weil die Funktionen X/Y/Z gebaut werden müssen, aber die Funktionen A/B/C nicht einmal gut funktionieren!"
Die Übernahme der Codebasis/des Projekts eines anderen ist immer eine Lotterie. Wenn Sie es übernehmen, weil es neue Funktionen braucht und es bereits ein von technischen Schulden durchsetztes Chaos ist, wissen Sie, dass Sie das in Ordnung bringen müssen, bevor Sie etwas anderes anfassen. Noch schlimmer ist, dass viele dieser Projekte (meiner Erfahrung nach) dazu neigen, absolut keine Tests zur Selbstdokumentation des Codes zu haben. Betrachten Sie ein klassisches Beispiel, das ich immer wieder gesehen habe:
$someData = \MyNamespace\MyORM\MyRepository::findAllBySomething(SOMETHING);
foreach ($someData as $myEntity) {
$myEntity->doTheThing();
}
Sie haben diese Entitätsklasse oder die Repository-Methode nicht geschrieben. Sie haben keine Typehints, weil sie ursprünglich in PHP5.3 geschrieben wurden, oder der Entwickler hat keine verwendet. Es ist in Ordnung, wenn Ihr ORM ein Array mit denselben Entitäten zurückgibt, aber ein Fehler, ein Null-Ergebnis im Rückgabewert von findAllBySomething() und doTheThing() wird einen fatalen Fehler auslösen.
Es ist Zeit für die Einstellung von PHPStan darauf zu setzen.

Ihre Strategie kennen
Es ist zwar einfach zu sagen: "Benutzen Sie PHPStan", aber wenn Sie eine ältere oder technisch hochverschuldete Anwendung haben, brauchen Sie eine Strategie, anstatt einfach nur Dinge zu tun und zu sehen, was passiert. Zunächst einmal sollten Sie sich mit den Regelebenen vertraut machen.
Regel-Ebenen
PHPStan ist so aufgebaut, dass es mit bestimmten Regelebenen läuft, die von 0-9 nummeriert sind:
Basisprüfungen, unbekannte Klassen, unbekannte Funktionen, unbekannte Methoden, die auf
$thisfalsche Anzahl von Argumenten, die an diese Methoden und Funktionen übergeben werden, immer undefinierte Variablenmöglicherweise undefinierte Variablen, unbekannte magische Methoden und Eigenschaften von Klassen mit
__callund__getunbekannte Methoden, die auf alle Ausdrücke geprüft werden (nicht nur
$this), Validierung von PHPDocsRückgabetypen, den Eigenschaften zugewiesene Typen
grundlegende Prüfung auf toten Code - immer falsch
instanceofund andere Typprüfungen, toteelseVerzweigungen, unerreichbarer Code nach der Rückkehr; usw.Überprüfung der Arten von Argumenten, die an Methoden und Funktionen übergeben werden
fehlende typehints melden
teilweise falsche Unionstypen melden - wenn man eine Methode aufruft, die nur auf einigen Typen in einem Unionstyp existiert, beginnt Level 7 dies zu melden; andere möglicherweise falsche Situationen
Bericht über den Aufruf von Methoden und den Zugriff auf Eigenschaften bei nullbaren Typen
seien Sie streng mit dem
mixedTyp - die einzige erlaubte Operation, die Sie damit durchführen können, ist die Übergabe an eine anderemixed
Aus diesem Grund ist Ihre Strategie so wichtig. Wenn Sie ein Altprojekt haben, das von jemand anderem geschrieben wurde, und Sie den PHPStan-Taskrunner auf Stufe 9 starten, könnten Sie von den Ergebnissen, die er liefert, überwältigt sein. Alles ist kaputt! Zum Refactoring würde ich Folgendes vorschlagen:
Setzen Sie sich für jede Stufe Meilensteine, und beginnen Sie klein.
Die langfristigen Investitionen werden sich irgendwann auszahlen (wir werden uns in Kürze mit den Pipelines befassen), aber legen Sie die Obergrenze fest, bis zu der Sie bereit sind zu gehen, wenn Sie die "Behebung der Tech-Schulden" unter Ihrer eigenen "Definition von erledigt" einstufen
Ein gutes De-facto-Ziel für ein Legacy-Projekt ist es, die Regelstufe 6 zu erreichen. An diesem Punkt kann Ihre Codebasis wahrscheinlich von einem Zustand "gefährlich" zu "korrekt" übergehen. Damit wäre Regelstufe 6 Ihre Basislinie).
Das ist super wichtig: Stellen Sie sicher, dass Sie Zeit zuweisen (Sprints, aufgeschlüsselte Jira-Tickets für die Masochisten), um zu beheben. was PHPStan auf jeder Regelebene anzeigt. Tech Debt zu beheben ist nicht einfach in vielen Fällen nicht einfach, und Sie haben keine Ahnung, welche Art von Fehlern in der Geschäftslogik in Ihrer Anwendung auftreten könnten.
Stellen Sie beim Festlegen der inkrementellen Ziele für Regelebenen sicher, dass Sie Ihre Pipeline einrichten, bevor Sie Änderungen festschreiben, damit Sie beim Refactoring keine neuen Codegerüche einführen. Beim Einrichten Ihrer Pipeline müssen Sie Folgendes festlegen Ihre Baselinefestlegen, wozu wir gleich kommen werden.
Pipeline
In der Welt von DevOps gibt es eine überwältigende Anzahl von Tooling-Optionen, um Ihre Probleme zu lösen. In diesem Fall biete ich nur einen Ansatz an, der jedoch weniger komplex ist als andere verfügbare Optionen. Sobald Sie Ihre Strategie festgelegt haben, ist es an der Zeit, Ihre Pipeline so einzurichten, dass wir keinen neuen Code übertragen, der nicht zuvor PHPStan durchlaufen hat.
Verteidigungsschranken: lokal vs. serverseitig
Ich führe gerne Werkzeuge ein, um die Möglichkeit von Single-Points-of-Failure zu eliminieren, und als Ergebnis dieses Zynismus empfehle ich dringend, dass Sie Ihre statische Analyse sowohl auf den Rechnern der lokalen Entwickler als auch Server-seitige CI-Prüfungen in Ihrem Repository.
Lokales
Komponist + PHPStan
Als Erstes müssen Sie PHPStan in Ihrem Projekt installieren. Wir verwenden composer verwenden, wobei wir davon ausgehen, dass Ihr Legacy-Code hoffentlich eine Paketverwaltung verwendet. Wenn nicht, können Sie composer installieren und verwenden composer init um ein neues Projekt zu erstellen.
Um PHPStan zu installieren, führen Sie Folgendes aus:
Wir fügen --dev hinzu, da wir es für die Produktion nicht benötigen (theoretisch!).
Konfiguration: Festlegung der Ausgangssituation
Dies ist eine sehr nützliche Funktion von PHPStan. Ihre Baseline legt den "Nullpunkt" Ihrer Anwendung fest, so dass alle aktuellen Fehler, die innerhalb der von Ihnen gewählten Regelebene existieren, ignoriert werden bis Sie sich entscheiden, sich mit ihnen zu befassenaber gleichzeitig eine Regelebene für alle neu vorgenommenen Änderungen durchsetzen können. Ein vernünftiger Ansatz, wie er in der Strategie beschrieben wird, wäre die Festlegung einer Basislinie auf Regelebene 6:
Der gesamte neue Code, der in das Projekt einfließt, muss der Regelstufe 6 entsprechen.
Anschließend können Sie die in Ihren strategischen Zielen festgelegten technischen Verschuldungsziele für die unteren Ebenen festlegen.
Um Ihre Baseline zu erstellen, führen Sie Folgendes aus:
Sie haben nun Ihre Basiskonfiguration in der angegebenen Datei festgelegt (phpstan.neon), die eine detaillierte Übersicht der Fehler pro Datei speichert.
Jetzt wollen Sie, dass PHPStan Übertragungen an Ihr Projektarchiv verhindert bevor sie in Ihre Quelle hochgeladen werden können. Hierfür verwenden wir Git-Hooks.
Git-Haken
Irgendwie habe ich Jahre gebraucht, um zu erkennen, dass Git tatsächlich standardmäßig Hooks in ein neues Git-Repository auf git init. Sie können mehr über git hooks hier lesen. Wir werden den pre-commit Haken. Solange Sie noch keine Hooks in Ihrem Projekt angefasst haben, können Sie den Pre-Commit-Hook aktivieren, indem Sie ihn umbenennen - führen Sie dies im Stammverzeichnis Ihres Projekts aus:
Öffnen Sie nun die Datei, löschen Sie den Inhalt und kopieren Sie den folgenden Text hinein:
Jetzt haben Sie aktiviert pre-commitaktiviert haben, wird PHPStan vor jedem Commit ausgelöst und analysiert anhand der Baseline auf alle neuen Dateien, die in der Git-Übertragung geändert wurden. Kein stinkender Commit-Code mehr!
Wenn Sie also den Befehlszeilen-Trigger anpassen möchten, wenn Sie eine Stufe aufsteigen, ändern Sie die analysisResult=$(vendor/bin/phpstan analyse $gitDiffFiles) Zeilenargumente.
Server-seitig
Je mehr Schutz Sie für Ihren Code bieten können, desto besser. Die serverseitige Ausführung von PHPStan nach einem Push an Ihren Code als Teil Ihrer kontinuierlichen Integration ist ein ein Muss. Für dieses Beispiel werden wir Github Actions verwenden, aber denken Sie daran, dass Sie dies mit dem gleichen Funktionsumfang auch in CircleCI, Bitbucket Pipelines, Gitlab CI/CD, oder Jenkins. Hier ist ein Beispiel für einen auf Github eingerichteten Aktions-Workflow, der Ihren Code mit einer Ubuntu Container:
---
name: build
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
name: Build example
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.0
extensions: json, mbstring
coverage: pcov
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run PHPStan
run: vendor/bin/phpstan analyse .Der Befehl unter "Run PHPStan" kann nach Ihren Anforderungen konfiguriert werden, genauso wie Sie den Befehl konfigurieren können, wenn Sie PHPStan lokal ausführen. Ich habe diesen Arbeitsablauf so geschrieben, dass PHPStan standardmäßig auf allen Dateien im Projekt ausgeführt wird (dieser Arbeitsablauf wurde noch nicht abgefeuert composer noch nicht ausgelöst, daher entfällt der unnötige und ineffiziente Schritt der Ausführung in Ihrem vendor Ordner), so dass ich hier empfehlen würde, eine Konfiguration zu verwenden, die die Regelebene des gesamten Projekts festlegt.
Ihr Legacy-Projekt verfügt nun über eine Strategie zur Bereinigung Ihres Codes und über Pipelines, die verhindern, dass neue Fehler in Commits auftauchen, während gleichzeitig eine Analyse gegen die Baseline für den gesamten vorhandenen Code durchgeführt wird. Diese Art von Setup gibt Ihnen weitaus mehr Vertrauen in das Projekt und liefert gleichzeitig Erkenntnisse darüber, in welchen Bereichen wahrscheinlich Refactoring erforderlich ist, um technische Schulden zu beseitigen.
Zu guter Letzt: Statische Analyse vs. Tests
Ich sage das laut, vor allem für die Leute hinten im Raum: PHPStan und jedes andere statische Analysewerkzeug ist kein Ersatz für Ihre Tests! Ich würde die Verwendung so formulieren, dass eine Testsuite und PHPStan sich gegenseitig ergänzen bei der Beurteilung der Qualität Ihres Codes.
Es ist ein Irrglaube zu glauben, dass Sie keine oder nur eine geringe Notwendigkeit für eine Testsuite haben. Das Wichtigste dabei ist, dass die statische Analyse Ihre Domänenlogik nicht testen kann. Auch wenn dies eine offensichtliche Aussage zu sein scheint, ist es erwähnenswert, dass sie verwirrend sein kann, da PHPStan kann die Notwendigkeit bestimmter Tests beseitigen kann. Ein Beispiel hierfür wäre ein instanceOf Test, der besagt, dass eine zu erstellende Klasse das Endergebnis eines Prozesses ist. PHPStan kann dieses Erfordernis beseitigen, da es die Analyse liefert, die notwendig ist, um diesen potenziellen Fehler zu beseitigen, aber es weiß nicht Ihre Domänenlogik, die vorher benötigt wird, nicht kennen - das ist, was Sie tun testen müssen.
Und denken Sie daran: Es gibt Alternativen!
Haben Sie es ausprobiert? Nicht so begeistert? Jeder hat seine Vorlieben, und während ich mein Loblied auf Ondřej für seine Arbeit an PHPStan loben möchte, ist es erwähnenswert, dass es mehrere andere Tools gibt, die entweder die gleiche Aufgabe erfüllen oder in Verbindung mit PHPStan verwendet werden können:
Danke
Ein besonderer Dank geht an Ondřej Mirtes für seine Ratschläge und seine harte Arbeit bei der Veröffentlichung dieses großartigen Tools.
Teilen Sie:
Als ausgebildeter Schauspieler mit einer Dissertation in Standup-Comedy bin ich über die Meetup-Szene zur PHP-Entwicklung gekommen. Man findet mich, wenn ich über Technik spreche oder schreibe, oder wenn ich seltsame Platten aus meiner Vinylsammlung spiele oder kaufe.