in GitLab CI bündeln
Code, der nicht getestet und nicht analysiert wird, akkumuliert stille Fehler. PHPUnit, PHPStan, PHPCS und composer audit als parallele GitLab-CI-Jobs gebündelt liefern nach jedem Commit Rückmeldung über Tests, Typsicherheit, Stil und bekannte Sicherheitslücken – automatisch und ohne manuellen Aufwand.
Inhaltsverzeichnis
- 1. Warum alle Checks in die Pipeline gehören
- 2. PHPUnit in GitLab CI integrieren
- 3. PHPStan: statische Analyse im Pipeline-Job
- 4. PHPCS und Coding-Standard-Checks
- 5. Sicherheitschecks: composer audit und mehr
- 6. Parallele Ausführung aller Test-Jobs
- 7. Test-Berichte in GitLab sichtbar machen
- 8. QS-Werkzeuge im Vergleich
- 9. Magento-spezifische Besonderheiten
- 10. Zusammenfassung
- 11. FAQ
1. Warum alle Checks in die Pipeline gehören
Code-Qualitätswerkzeuge, die nur lokal und nur von gewissenhaften Entwicklern ausgeführt werden, sind keine Qualitätssicherung – sie sind eine Hoffnung. PHPUnit, PHPStan, PHPCS und composer audit haben nur dann Wert als Prozess-Absicherung, wenn sie automatisch bei jedem Commit und jedem Merge Request ausgeführt werden. Ohne diese Automatisierung entsteht unvermeidlich Drift: ein Entwickler läuft PHPStan nicht, ein anderer vergisst den Code-Style, und das Ergebnis ist eine Codebasis, die mit steigendem Druck immer inkonsistenter wird.
GitLab CI ist der richtige Ort für diese Automatisierung. Die Test-Jobs laufen parallel, liefern Ergebnisse in GitLab-Merge-Requests als Pass/Fail-Marker und verhindern, dass Code, der Checks nicht besteht, auf den main-Branch gemergt werden kann. Das kostet initial Konfigurationsaufwand, zahlt sich aber in jeder Codebase aus, die länger als ein Quartal aktiv entwickelt wird. Besonders in Magento-Projekten mit intensiver Modul-Entwicklung ist diese Absicherung kein Luxus, sondern Betriebshygiene.
2. PHPUnit in GitLab CI integrieren
PHPUnit in GitLab CI zu betreiben ist konzeptuell einfach, erfordert in Magento-Projekten aber einige Vorkehrungen. Magento's Test-Framework ist auf Integration Tests ausgelegt, die eine laufende Datenbank und Redis-Instanz benötigen. Unit Tests hingegen sind vollständig isoliert und laufen ohne externe Abhängigkeiten – sie sind der erste und wichtigste Test-Typ für selbst geschriebene Module und ViewModels. Die Empfehlung: Unit Tests als eigenen Job in der Test-Stage ausführen, Integration Tests nur auf Staging oder in dafür vorbereiteten Services-Jobs.
GitLab bietet mit services in der Pipeline-Konfiguration die Möglichkeit, MySQL und Redis als Seicar-Container neben dem Test-Job zu starten. Das ermöglicht auch Integration Tests direkt in der Pipeline. Für Magento-Integration-Tests ist zusätzlich eine install-config-files.php nötig, die die Testdatenbank konfiguriert. Diese Komplexität ist für die meisten Projekte zu hoch – Unit Tests mit vollständiger Isolation sind der pragmatischere Ausgangspunkt.
# .gitlab-ci.yml — parallel quality assurance jobs in test stage
test:phpunit:
stage: test
image: php:8.4-cli
variables:
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.cache/composer"
cache:
key:
files: [composer.lock]
paths: [.cache/composer/, vendor/]
policy: pull
before_script:
- apt-get update -qq &&
apt-get install -y -qq git unzip libzip-dev
- docker-php-ext-install zip pdo_mysql
- composer install --no-dev --prefer-dist --no-interaction
# Install test dependencies separately
- composer require --dev
phpunit/phpunit:^11
--no-interaction --no-update
- composer update phpunit/phpunit --no-interaction
script:
- ./vendor/bin/phpunit
--testsuite Unit
--log-junit junit-unit.xml
--coverage-text
artifacts:
when: always
reports:
junit: junit-unit.xml
expire_in: 1 week
only:
- merge_requests
- main
- tags
test:phpstan:
stage: test
image: php:8.4-cli
cache:
key:
files: [composer.lock]
paths: [.cache/composer/, vendor/]
policy: pull
script:
# Run PHPStan at level 6 for Magento modules
- ./vendor/bin/phpstan analyse
app/code/
--level=6
--error-format=gitlab
--no-progress
> phpstan-report.json || true
- ./vendor/bin/phpstan analyse
app/code/
--level=6
--no-progress
artifacts:
when: always
paths: [phpstan-report.json]
expire_in: 1 week
only:
- merge_requests
- main
3. PHPStan: statische Analyse im Pipeline-Job
PHPStan findet Fehler, die Tests nicht finden: falsche Typen, nicht existierende Methoden, undefinierte Eigenschaften und logische Inkonsistenzen, die erst zur Laufzeit explodieren. Für Magento-Projekte ist PHPStan auf Level 6–8 sinnvoll, je nach Reife der Codebasis. Level 0 prüft nur offensichtliche Fehler, Level 8 erzwingt vollständige Typ-Annotationen. Die pragmatische Strategie: mit Level 4 beginnen, alle bestehenden Fehler mit einer Baseline ausblenden, und dann bei jeder Änderung sicherstellen, dass keine neuen Fehler entstehen.
Für Magento ist das PHPStan-Plugin bitExpert/phpstan-magento unerlässlich: es kennt Magento-spezifische Patterns wie ObjectManager::get(), Factory-Klassen und die Proxy-Generierung und verhindert tausende Falsch-Positive. Ohne dieses Plugin erzeugt PHPStan in jedem Magento-Projekt so viele False-Positives, dass das Tool nicht sinnvoll nutzbar ist. Die Konfiguration gehört in eine phpstan.neon-Datei im Projektroot, die vom GitLab-CI-Job automatisch aufgenommen wird.
4. PHPCS und Coding-Standard-Checks
PHPCS (PHP_CodeSniffer) prüft, ob der Code dem definierten Coding-Standard entspricht. Für Magento-Projekte ist das Magento2-Standard aus dem Paket magento/magento-coding-standard der richtige Ausgangspunkt. Dieser Standard umfasst PSR-12, Magento-spezifische Regeln für Kommentare, String-Interpolation, Datenbankabfragen und die Verwendung von Dependeny-Injection statt ObjectManager. In GitLab CI läuft PHPCS als eigenständiger Job parallel zu PHPStan und PHPUnit.
Wichtig: PHPCS als Pipeline-Blocker ist sinnvoll, aber erst wenn die gesamte Codebasis konform ist. Wer PHPCS in eine bestehende Codebasis einführt, nutzt besser --report=diff und phpcbf zum automatischen Korrigieren, bevor er die Pipeline-Checks aktiviert. Der GitLab-CI-Job für PHPCS lässt sich mit allow_failure: true zunächst als Warnung konfigurieren und später auf allow_failure: false umstellen, wenn die Codebasis bereinigt ist.
5. Sicherheitschecks: composer audit und mehr
Sicherheitschecks in der Pipeline sind kein Nice-to-have, sondern für jeden produktiven E-Commerce-Shop Pflicht. Der erste und einfachste Check ist composer audit: Composer 2.4+ hat diesen Befehl eingebaut und prüft alle installierten Pakete gegen die Sicherheitsdatenbank von packagist.org. Wenn eine bekannte CVE für eine installierte Version vorliegt, schlägt der Befehl mit Exit-Code 1 fehl – was den Pipeline-Job zum Scheitern bringt und das Team informiert.
Für tiefgehendere Sicherheitsanalysen eignet sich local-php-security-checker, das ebenfalls die Symfony-Security-Datenbank abfragt und im JSON-Format ausgibt. GitLab Ultimate bietet darüber hinaus eingebaute SAST (Static Application Security Testing) und Dependency Scanning als fertige Template-Jobs. Für Projekte ohne GitLab Ultimate ist die Kombination aus composer audit und einem manuell konfigurierten local-php-security-checker-Job ausreichend und kostenlos.
# Security and code style jobs running in parallel
test:phpcs:
stage: test
image: php:8.4-cli
cache:
key:
files: [composer.lock]
paths: [.cache/composer/, vendor/]
policy: pull
script:
# Run PHPCS with Magento2 standard on custom code only
- ./vendor/bin/phpcs
--standard=Magento2
--extensions=php
--ignore=*/vendor/*,*/generated/*
app/code/
allow_failure: false
only:
- merge_requests
- main
test:security:
stage: test
image: php:8.4-cli
cache:
key:
files: [composer.lock]
paths: [.cache/composer/]
policy: pull
script:
# Audit all installed packages for known CVEs
- composer audit --no-dev --format=json > security-audit.json
|| (cat security-audit.json && exit 1)
# Additional: check for abandoned packages
- composer outdated --no-dev --format=json |
php -r "
\$data = json_decode(file_get_contents('php://stdin'), true);
\$abandoned = array_filter(\$data['installed'] ?? [],
fn(\$p) => \$p['abandoned'] ?? false);
foreach(\$abandoned as \$p) {
echo 'ABANDONED: ' . \$p['name'] . PHP_EOL;
}"
artifacts:
when: always
paths: [security-audit.json]
expire_in: 1 month
only:
- merge_requests
- main
- schedules
6. Parallele Ausführung aller Test-Jobs
Der entscheidende Performance-Vorteil einer sauber konfigurierten Test-Stage ist die parallele Ausführung. PHPUnit, PHPStan, PHPCS und composer audit haben keine Abhängigkeiten untereinander – sie können alle gleichzeitig auf verschiedenen GitLab-Runnern ausgeführt werden. Wenn jeder Job einzeln zwei Minuten dauert, läuft die gesamte Test-Stage bei paralleler Ausführung trotzdem in zwei Minuten – nicht in acht. Das macht den Unterschied zwischen einer Pipeline, die das Team akzeptiert, und einer, die als zu langsam übersprungen wird.
Für die parallele Ausführung müssen alle Jobs in derselben Stage definiert sein und unabhängige Caches verwenden. Der Build-Job in der vorherigen Stage produziert das vendor/-Verzeichnis als Artefakt oder via Cache; alle Test-Jobs nehmen diesen Cache mit policy: pull (nur lesen, nicht schreiben) ab. So wird der Cache nicht durch parallele Schreibzugriffe korrumpiert, und alle Test-Jobs starten gleichzeitig, sobald die Build-Stage abgeschlossen ist.
7. Test-Berichte in GitLab sichtbar machen
GitLab bietet native Unterstützung für JUnit-XML-Berichte: wenn ein Test-Job artifacts.reports.junit definiert, erscheinen die Test-Ergebnisse direkt im Merge-Request als übersichtliche Tabelle. Jeder fehlgeschlagene Test wird mit seiner Fehlermeldung angezeigt, ohne dass der Reviewer in die Job-Logs schauen muss. PHPUnit generiert diese Berichte mit dem Flag --log-junit junit.xml; PHPStan kann mit --error-format=junit ebenfalls JUnit-XML ausgeben.
Für Code-Coverage-Berichte bietet GitLab ab der Coverage-Konfiguration in den Projekt-Einstellungen: mit einem Regex-Pattern wie /Lines:\s+(\d+\.\d+)%/ extrahiert GitLab den Coverage-Wert aus dem PHPUnit-Text-Report und zeigt ihn als Badge im Repository an. Das motiviert Teams, den Coverage-Wert über die Zeit zu steigern, ohne dass ein externes Tool wie Coveralls oder Codecov nötig wäre.
8. QS-Werkzeuge im Vergleich
Die vier zentralen Qualitätswerkzeuge decken unterschiedliche Fehlerklassen ab und ergänzen sich gegenseitig. Keines kann das andere vollständig ersetzen.
| Werkzeug | Fehlerklasse | Magento-Plugin nötig? | GitLab-Report-Format |
|---|---|---|---|
| PHPUnit | Laufzeit-Fehler, Logik-Bugs, Regressionen | Nein | JUnit XML |
| PHPStan | Typfehler, undefinierte Methoden, Logikfehler | Ja (bitExpert/phpstan-magento) | JUnit XML / GitLab JSON |
| PHPCS | Stil-Verletzungen, Standard-Abweichungen | Ja (magento/magento-coding-standard) | Text / Checkstyle XML |
| composer audit | Bekannte CVEs in Abhängigkeiten | Nein | JSON / Text |
| GitLab SAST (Ultimate) | Sicherheitslücken im eigenen Code | Nein (eingebaut) | GitLab Security Dashboard |
9. Magento-spezifische Besonderheiten
Magento-Projekte haben spezifische Anforderungen, die Standard-PHP-QS-Konfigurationen nicht abdecken. Die wichtigste ist die Unterscheidung zwischen eigenem Code (app/code/) und Magento-Core- und Drittanbieter-Code (vendor/). PHPStan, PHPCS und PHPUnit sollen ausschließlich den eigenen Code prüfen. Falsch konfigurierte Tools, die auch vendor/ analysieren, erzeugen tausende irrelevante Fehler und machen die Ausgabe unbrauchbar.
Ein zweiter Magento-spezifischer Aspekt ist die Generated-Code-Abhängigkeit. Viele Magento-Klassen – Factories, Proxies, Interceptors – existieren erst nach setup:di:compile. PHPStan benötigt diese generierten Klassen, um korrekte Typ-Analysen durchzuführen. Das bedeutet: der PHPStan-Job muss entweder den generierten Code als Artefakt vom Build-Job erhalten oder selbst ein setup:di:compile mit einer Stub-Datenbank ausführen. Die einfachere Lösung ist es, dem PHPStan-Job die generated/-Artefakte aus dem Build-Stage zur Verfügung zu stellen.
10. Zusammenfassung
PHPUnit, PHPStan, PHPCS und composer audit in GitLab CI zu bündeln ist keine Einzel-Maßnahme, sondern ein System. Jedes Werkzeug deckt eine andere Fehlerklasse ab, und nur in Kombination bieten sie eine belastbare Qualitätssicherung. Die parallele Ausführung in der Test-Stage hält die Pipeline schnell; das JUnit-Reporting macht Fehler direkt im Merge-Request sichtbar; die Magento-spezifischen Plugins verhindern False-Positives. Das Ergebnis ist ein Prozess, der schlechten Code systematisch verhindert, statt ihn in Code-Reviews zu jagen.
Der pragmatische Startpunkt für bestehende Projekte: zuerst composer audit aktivieren (sofortiger Mehrwert, null Fehlalarme), dann PHPCS mit allow_failure: true einführen und die Codebasis schrittweise bereinigen, dann PHPStan mit Baseline und schrittweise steigendem Level, schließlich PHPUnit für eigene Module. Dieser schrittweise Ansatz verhindert, dass das Team von hundert Fehlern erschlagen wird und die QS-Jobs deaktiviert.
PHP-Qualitätssicherung in GitLab CI — Das Wichtigste auf einen Blick
Parallel ausführen
PHPUnit, PHPStan, PHPCS und composer audit in derselben Test-Stage – alle parallel, unabhängig, in unter 3 Minuten.
Nur eigenen Code prüfen
app/code/ analysieren, vendor/ und generated/ explizit ausschließen – sonst tausende Fehlalarme.
Magento-Plugins
bitExpert/phpstan-magento und magento/magento-coding-standard sind Pflicht für sinnvolle Analysen.
Schrittweise einführen
composer audit zuerst, dann PHPCS, PHPStan mit Baseline, zuletzt PHPUnit – Fehler nie auf einmal.