Der Hoster kündigt die Abschaltung alter PHP-Versionen an, ein Plugin verlangt mindestens 8.1, oder die Konsole meldet eine andere Version als die Website. In allen drei Fällen brauchst du zuerst eine verlässliche Antwort auf die Frage, was gerade läuft. Die Funktion PHP phpversion liefert sie in einer Zeile, und darauf lässt sich alles Weitere aufbauen.
Fangen wir mit dem Fall an, in dem du nur die Zahl sehen willst.
Die schnelle Antwort mit PHP phpversion
Lege eine Datei mit diesem Inhalt an, lade sie hoch und rufe sie im Browser auf. Danach lösche sie wieder.
<?php
echo 'PHP-Version: ' . phpversion(); /* PHP-Version: 8.3.4 */
echo PHP_VERSION; /* 8.3.4 */
echo PHP_VERSION_ID; /* 80304 */
Das Löschen ist kein Formalismus. Eine dauerhaft erreichbare Datei, die deine genaue Version verrät, erspart einem Angreifer die halbe Vorarbeit. Er muss dann nicht mehr raten, welche bekannten Lücken bei dir greifen könnten.
Wer WordPress nutzt, findet die Version auch ohne eigene Datei: im Adminbereich unter Werkzeuge und Website-Zustand steht sie im Reiter Bericht. Bei den meisten Hostern zeigt das Kundenmenü sie ebenfalls an, allerdings manchmal nur die Standardversion des Servers und nicht die, die deine Domain tatsächlich verwendet. Der Dreizeiler oben ist deshalb die verlässlichere Auskunft.
PHP phpversion und PHP_VERSION im Vergleich
Beide liefern denselben String. Der Unterschied liegt woanders.
| | phpversion() | PHP_VERSION |
| Art | Funktion | Konstante |
| Rückgabe | String, etwa 8.3.4 | derselbe String |
| Kann mehr | ja, mit Parameter auch Extension-Versionen | nein |
| Nutzbar in | normalem Code | auch dort, wo kein Funktionsaufruf erlaubt ist |
Für die reine Versionsabfrage kannst du also frei zwischen PHP phpversion und der Konstante wählen. Die Konstante hat einen kleinen Vorteil bei Konstanten-Ausdrücken und Attributen, weil dort kein Funktionsaufruf stehen darf. Mehr zu diesem Unterschied steht im Tutorial zu PHP Konstanten.
Die Version einer Extension abfragen
Der eigentliche Mehrwert der Funktion steckt im Parameter. Mit einem Extension-Namen liefert PHP phpversion deren Version statt der von PHP selbst.
<?php
$gd = phpversion('gd');
if ($gd === false) {
echo 'GD ist nicht geladen.';
} else {
echo 'GD-Version: ' . $gd;
}
/* Nur Existenz pruefen, ohne Versionsangabe: */
var_dump(extension_loaded('curl')); /* bool(true) */
Achte auf den Rückgabewert false. Er bedeutet, dass die Extension nicht geladen ist, und nicht etwa, dass sie keine Version meldet. In einer echo-Ausgabe wird daraus ein leerer String, und dann sieht es so aus, als wäre die Abfrage fehlgeschlagen. Prüfe deshalb mit === gegen false, bevor du den Wert ausgibst.
Wenn du nur wissen willst, ob eine Extension vorhanden ist, nimm extension_loaded(). Das ist deutlicher zu lesen und bringt keinen zweideutigen Rückgabewert mit. PHP phpversion lohnt sich hier nur, wenn dich die Versionsnummer der Extension wirklich interessiert.
Versionen richtig vergleichen
Sobald dein Code auf die Version reagieren soll, reicht die Ausgabe von PHP phpversion nicht mehr. Du brauchst einen Vergleich, und dafür gibt es zwei saubere Wege.
<?php
if (version_compare(PHP_VERSION, '8.1.0', '>=')) {
echo 'Enums sind verfuegbar.';
}
/* Schneller, ohne String-Parsing: */
if (PHP_VERSION_ID >= 80100) {
echo 'Enums sind verfuegbar.';
}
PHP_VERSION_ID ist eine Ganzzahl nach der Formel Major mal 10000 plus Minor mal 100 plus Release. Aus 8.3.4 wird also 80304. Der Vergleich ist dadurch ein simpler Zahlenvergleich und entsprechend schnell. Was der Konstante fehlt, sind Suffixe: eine Vorabversion wie 8.4.0-RC2 lässt sich damit nicht von der finalen 8.4.0 unterscheiden.
version_compare() kann genau das. Die Funktion versteht Zusatzbezeichnungen wie dev, alpha, beta und RC und sortiert sie in der richtigen Reihenfolge vor die finale Version. Für Bibliotheken, die auch auf Vorabversionen laufen sollen, ist sie die richtige Wahl.
Warum ein String-Vergleich hier bricht
Der naheliegende Weg ist der falsche, und der Fehler fällt erst auf, wenn eine zweistellige Minor-Version erscheint.
<?php
var_dump('8.9' >= '8.10');
/* bool(true), obwohl 8.10 die neuere Version ist */
var_dump(version_compare('8.9', '8.10', '>='));
/* bool(false), korrekt */
PHP vergleicht die beiden Strings zeichenweise. An der dritten Stelle steht einmal eine 9 und einmal eine 1, und 9 ist grösser. Dass die 1 zu einer 10 gehört, kann ein Zeichenvergleich nicht wissen. Solange nur einstellige Minor-Versionen im Umlauf sind, fällt das nie auf. Genau deshalb ist es eine Falle mit langer Zündschnur.
Mindestversion prüfen, ohne dass das Skript vorher stirbt
Eine Versionsprüfung nützt nichts, wenn PHP die Datei gar nicht erst laden kann. Enthalten deine Dateien moderne Sprachmittel wie Enums oder Konstruktor-Promotion, scheitert schon das Parsen auf einer alten Version, und der Nutzer sieht einen Parse Error statt deiner Meldung.
Der Ausweg ist eine kleine Startdatei, die bewusst in alter Syntax geschrieben ist und erst danach den Rest einbindet.
<?php
/* bootstrap.php, absichtlich ohne moderne Sprachmittel */
if (version_compare(PHP_VERSION, '8.1.0', '<')) {
header('Content-Type: text/plain; charset=utf-8');
echo 'Dieses Programm braucht mindestens PHP 8.1.0. ';
echo 'Gefunden wurde ' . PHP_VERSION . '.';
exit(1);
}
require __DIR__ . '/anwendung.php';
Die Datei darf keine Typdeklarationen, keine Pfeilfunktionen und keine Attribute enthalten. Sie ist die einzige Stelle im Projekt, an der sich alte Syntax lohnt.
Warum CLI und Webserver verschiedene Versionen melden
Auf der Konsole meldet php -v eine Version, die Website eine andere. Das ist kein Fehler, sondern der Normalfall auf Servern mit mehreren installierten PHP-Versionen. Die Kommandozeile nutzt die Version, die im Systempfad liegt, während der Webserver über FPM oder ein Modul eine eigene Installation mit eigener Konfiguration anspricht.
<?php
echo 'Version: ' . PHP_VERSION . "\n";
echo 'Schnittstelle: ' . php_sapi_name() . "\n";
echo 'Geladene php.ini: ' . (php_ini_loaded_file() ?: 'keine') . "\n";
/*
Version: 8.3.4
Schnittstelle: fpm-fcgi
Geladene php.ini: /etc/php/8.3/fpm/php.ini
*/
Diese drei Zeilen klären die meisten Missverständnisse dieser Art, weil PHP phpversion allein nur die halbe Auskunft gibt. php_sapi_name() nennt die Schnittstelle, unter der das Skript läuft, und php_ini_loaded_file() zeigt, welche Konfiguration dabei tatsächlich gilt. Wer häufig zwischen Umgebungen wechselt, kennt das Problem aus der Praxis; das Tutorial zu PHP mit Docker zeigt, wie sich solche Unterschiede von vornherein vermeiden lassen.
Der folgende Ablauf zeigt, welcher Weg zu welcher Frage passt.
flowchart TD
A[Version gebraucht] --> B{Wofuer?}
B -->|Nur anzeigen| C[phpversion ausgeben]
B -->|Im Code entscheiden| D{Feature oder Version?}
D -->|Feature| E[function_exists pruefen]
D -->|Version| F{Genauigkeit?}
F -->|Schnell| G[PHP_VERSION_ID vergleichen]
F -->|Mit Suffixen| H[version_compare nutzen]
Besser gegen Fähigkeiten prüfen als gegen Nummern
So nützlich PHP phpversion ist, eine Versionsnummer bleibt ein Umweg. Was dich interessiert, ist selten die Zahl, sondern fast immer die Frage, ob eine bestimmte Funktion existiert. Frag also direkt danach.
<?php
if (function_exists('json_validate')) {
$gueltig = json_validate($eingabe);
} else {
json_decode($eingabe);
$gueltig = json_last_error() === JSON_ERROR_NONE;
}
Der Unterschied wird auf Distributionen wie Debian oder RHEL sichtbar. Dort werden Funktionen und Sicherheitskorrekturen regelmässig in ältere Pakete zurückportiert, ohne dass sich die gemeldete Versionsnummer entsprechend ändert. Eine Prüfung auf 8.3 schliesst dann Systeme aus, auf denen dein Code problemlos liefe. Neben function_exists() gibt es class_exists(), method_exists() und defined() für denselben Zweck.
Für Sprachsyntax funktioniert dieser Weg allerdings nicht. Ob Enums verfügbar sind, lässt sich nicht abfragen, ohne den Code zu laden, der sie benutzt. Dort bleibt die Versionsprüfung im Bootstrap die richtige Lösung.
Welche Version sollte überhaupt laufen?
Wenn du die Zahl vor dir hast, stellt sich die Anschlussfrage. Eine PHP-Version bekommt zwei Jahre lang normale Pflege und danach ein weiteres Jahr nur noch Sicherheitskorrekturen. Was älter ist, bekommt gar nichts mehr, auch keine Korrekturen für bekannt gewordene Lücken. Die aktuellen Termine stehen auf php.net/supported-versions.php.
Praktisch heisst das: alles unterhalb von 8.1 gehört abgelöst, unabhängig davon, ob die Seite noch läuft. Der Sprung auf eine neuere Version bringt dabei meist auch spürbar mehr Tempo, weil zwischen 7.4 und 8.x grössere Arbeiten am Interpreter lagen.
Eine Kuriosität am Rande, weil die Frage regelmässig auftaucht: eine PHP-Version 6 gibt es nicht. Sie war jahrelang in Entwicklung, scheiterte an der geplanten Unicode-Umstellung und wurde nie veröffentlicht. Um Verwechslungen mit der vorhandenen Literatur zu vermeiden, ging es von 5.6 direkt auf 7.0.
Vor einem Versionswechsel lohnt ein Testlauf auf einer Kopie. Rechne mit Meldungen aus Bibliotheken, die länger nicht aktualisiert wurden, und prüfe vorher mit PHP phpversion, welche Extensions in welcher Version geladen sind.
Die Versionsnummer nicht nach aussen geben
PHP hängt standardmässig einen Header an jede Antwort, der die genaue Version nennt:
<?php
/* Typische Antwortheader mit expose_php = On:
X-Powered-By: PHP/8.3.4
Abschalten in der php.ini:
expose_php = Off
*/
Das ist kein akutes Sicherheitsloch, aber eine unnötige Information. Wer eine bekannte Lücke ausnutzen will, sucht gezielt nach Servern mit passender Version. Schalte expose_php ab, wenn du Zugriff auf die php.ini hast. Wichtiger bleibt trotzdem, keine selbstgebaute Datei mit Versionsausgabe im Web-Root liegen zu lassen. Wie sich eine solche Ausgabe absichern lässt, steht ausführlich im Tutorial zu phpinfo() in PHP, das die vollständige Konfigurationsausgabe behandelt.
Fazit zu PHP phpversion
PHP phpversion beantwortet zwei verschiedene Fragen. Ohne Parameter nennt die Funktion die laufende PHP-Version, mit Parameter die einer geladenen Extension. Gleichwertig zur ersten Variante ist die Konstante PHP_VERSION.
Beim Vergleichen führt kein Weg an version_compare() oder PHP_VERSION_ID vorbei. Ein direkter String-Vergleich liefert falsche Ergebnisse, sobald zweistellige Minor-Versionen im Spiel sind, und dieser Fehler zeigt sich erst Jahre später. Wo es um ein einzelnes Feature geht, ist function_exists() ohnehin die bessere Frage als jede Versionsnummer.