Optimale Anwendung von include und file_exists

Vor allem bei PHP-Software, die auf (mehreren) Servern eingesetzt wird, die häufig aktualisiert wird und die Aktualisierung von unterschiedlichen Anwendern durchgeführt wird, kann es vorkommen, dass beim Includieren von Dateien nicht 100%ig sichergestellt ist, dass die einzubindende Datei existiert. Das Includieren einer nicht existenten Datei ist natürlich etwas, was keinesfalls vorkommen sollte. Anhand von Benchmarks zeigen wir Ihnen nachfolgend einen Tipp, wie sich die Problematik auch ohne Verwendung der relativ teuren Überprüfung mit file_exists() mit einfachen Mitteln lösen lässt.

Bei den ersten drei Beispielen existiert die zu includierende Datei. Bei den darauffolgenden zwei Beispielen tritt die beschriebene Problematik auf, dass die zu includierende Datei nicht vorhanden ist.

include() ohne file_exists()

Code:

<?php
for ($i = 0;$i < 10000;$i++) {
    include ("./beispiel.php");
}
?>

Erläuterung:
Eine vorhandene Datei namens beispiel.php wird innerhalb einer for()-Schleife 10.000mal includiert.

include() mit file_exists()

Code:

<?php
for ($i = 0;$i < 10000;$i++) {
    if (file_exists("./beispiel.php")) {
        include ("./beispiel.php");
    }
}
?>

Erläuterung:
Vor dem Includieren der beispiel.php-Datei wird deren Existenz mit file_exists() überprüft.

Erstellen, falls include fehlschlägt

Code:

<?php
for ($i = 0;$i < 10000;$i++) {
    if (!include ("./beispiel.php")) {
        touch("./beispiel.php");
    }
}
?>

Erläuterung:
Falls das Includieren der Beispiel-Datei fehlschlägt, wird versucht, diese mit touch() zu erstellen. Da die Datei bereits vorhanden ist, kommt die touch()-Anweisung nicht zum Tragen.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
include() ohne file_exists()0.27 [#/sec] (mean)3725.906 [ms] (mean)
17.4%
14.2%
include() mit file_exists()0.23 [#/sec] (mean)4344.531 [ms] (mean)
Erstellen, falls include fehlschlägt0.25 [#/sec] (mean)3949.375 [ms] (mean)
8.7%
9.1%

Das Script mit Verwendung von include() ohne vorherige Überprüfung mit file_exists() spart über 14% der Ausführzeit gegenüber dem Script mit der in diesem Fall überflüssigen Überprüfung mit file_exists(). Die in diesem Fall ebenfalls sinnlose Variante mit touch() ist noch deutlich schneller als das Script mit der Überprüfung via file_exists().

Fazit

Wenn fest davon ausgegangen werden kann, dass die zu includierende Datei vorhanden ist, sind sämtliche Überprüfungen überflüssig und kosten unnötig Zeit. Soweit ist das wenig überraschend. Betrachten wir nun jedoch Fälle, bei denen die zu includierende Datei nicht existiert.

include() mit file_exists()

Code:

<?php
for ($i = 0;$i < 10000;$i++) {
    if (file_exists("./beispiel.php")) {
        include ("./beispiel.php");
    }
}
?>

Erläuterung:
Die nicht vorhandene Datei beispiel.php würde includiert werden, würde die Überprüfung mit file_exists positiv ausfallen. Die Datei existiert jedoch nicht und wird folglich nicht includiert.

Erstellen, falls include fehlschlägt

Code:

<?php
for ($i = 0;$i < 10000;$i++) {
    if (!include ("./beispiel.php")) {
        touch("./beispiel.php");
    }
}
?>

Erläuterung:
In diesem Fall wird die Datei mittels touch() erstellt, sofern sie nicht includiert werden kann. Dies verursacht einen einmaligen Fehler, nämlich beim fehlgeschlagenen Includieren. Zudem funktioniert die Variante nur, wenn touch() gemäß Zugriffsrechten und open_basedir möglich ist. Im Unterschied zum vorangegangenen Script wird ab dem zweiten Durchlauf der for()-Schleife die beispiel.php-Datei includiert, sofern touch() erfolgreich ist.

Ergebnis

Requests per second (RPS)Time per Request (TPR)Vergleich
include() mit file_exists()0.46 [#/sec] (mean)2177.188 [ms] (mean)
70.4%
42.0%
Erstellen, falls include fehlschlägt0.27 [#/sec] (mean)3754.688 [ms] (mean)

Das Script mit Überprüfung via file_exists() ist deutlich schneller, da die zu includierende Datei nicht vorhanden ist, nicht erstellt wird und folglich auch nicht includiert wird.

Fazit

Das Erstellen einer leeren Datei wie in unserem Beispiel ist natürlich relativ sinnfrei. Wenn Sie die touch()-Variante jedoch dahingehend erweitern, dass die Datei einen für die weitere Verarbeitung relevanten Inhalt bekommt, kann sie sehr sinnvoll eingesetzt werden. Vor allem ist zu beachten, dass die Ausführzeit praktisch identisch ist mit der des Scripts mit „normalem” include() ohne Prüfung auf Vorhandensein. Es handelt sich also um eine elegante Lösung, um Abwärtskompatibilität oder kleine selbstinstallierende Erweiterungen an bestehenden PHP-Scripten herzustellen.

 

Anmerkung:
Die Tests wurden mit dem Apache HTTP server benchmarking tool ab auf einem im Februar 2017 nicht mehr dem Stand der Technik entsprechenden Server durchgeführt. Die RPS und TPR der getesteten PHP-Scripte sollten auf einem im Produktiv-Einsatz befindlichen Webserver deutlich besser sein.

Nächster Performance-Tipp