PHP-Dateien in andere Scripts einbinden

Damit eine gewisse Skalierbarkeit von Webanwendungen gewährleistet werden kann, werden Dateien mit globalen Einstellungen und Funktionen gern in eine eigene PHP-Datei ausgelagert. Dies erspart Arbeitsaufwand bei Änderungen (Änderungen müssen nur in 1 Datei durchgeführt werden statt in allen) und senkt auch die Dateigröße, wobei erstgenannter grund wesnetlich bedeutsamer ist. Für das Einfügen dieser ausgelagerten Dateien bietet PHP mehrere Möglichkeiten an – diese möchte ich in diesem Beitrag vorstellen.

Auch dieser Beitrag entstand durch eine Anregung in den Themenvorschlägen – diesmal ein Vorschlag von workerholic.

Es gibt 4 Varianten per PHP eine externe Datei in ein anderes Script zu laden. Diese bieten jeweils eine leicht unterschiedliche Funktionsweise.

1. include und require
Beide Funktionen laden eine als Parameter übergebene Datei. Der einzige Unterschied liegt in der Fehlerbehandlung. Während include() bei Fehlern wie einer fehlenden Datei lediglich einen Fehler vom Typ E_WARNING auslöst, bricht require bei Fehlern mit einem Fatal Error (E_ERROR) das Script sofort ab. PHP.net bringt es auf den Punkt:

Mit anderen Worten, verwenden Sie require(), wenn Sie möchten, dass eine fehlende Datei die Ausführung ihres Skripts beendet. include() verhält sich anders, ihr Skript wird weiterhin ausgeführt.

2. include_once und require_once
Für diese beiden gilt das gleiche wie bei 1., jedoch mit einem Unterschied: Bei größeren Projekten kann es leicht passieren, dass man durch mehrere Auslagerungen von Programmkomplexen oder durch eine unübersichtliche Anwendungsarchitektur eine Ressourcen-Datei mehrmals lädt. include() und require() brechen dann sofort ab, weil sie merken, dass die Funktionen schon einmal deklariert wurden (das wäre das gleiche, wie wenn man 2 Funktionen mit dem gleichen Namen in dieselbe Datei schreibt).
include_once und require_once verhindern solche Fehler aber, indem die übergebenen Dateien nur einmalig geladen werden (wie der Name schon sagt: once). Das erhöht durchaus den Komfort bei Großprojekten, da nicht penibel darauf geachtet werden muss, ob eine Datei über 4 Ecken schon einmal eingefügt wurde.
Die Frage ist nur: Zu welchem Preis erreicht man dieses Plus an Komfort?

Ich habe die 4 Varianten durch ein Testscript laufen lassen, in dem erst eine Ressourcen-Datei eingefügt wurde und anschließend eine darin enthaltene Funktion aufgerufen wurde.

<?php
include("datei.php"); //oder eben die anderen 3 Varianten
funktion(); //diese Funktion wird in datei.php implementiert
?>

Zum Test habe ich eine Bibliothek von 18 kB Größe genutzt. Das Ergebnis sieht folgendermaßen aus:

Datei Gesamtlaufzeit durchschnittliche Laufzeit pro Durchlauf Verhältnis zur schnellsten Variante
result_include.php 23.643998 s 236.440 ms 100 %
result_require_once.php 23.714100 s 237.141 ms 100 %
result_include_once.php 23.754157 s 237.542 ms 100 %
result_require.php 23.774186 s 237.742 ms 101 % (+ 1%)

Es wird deutlich, dass sich die Varianten kaum unterscheiden in der Geschwindigkeit. Der zusätzliche Komfort bei den *_once-Varianten kostet also keinerlei Performance.

Meine Empfehlung geht deshalb in Richtung require_once, weil sie den besagten Zusatzkomfort bietet (sich also gegen die Gefahr der Mehrfachinklusion wendet) und außerdem abbricht, wenn die Datei nicht gefunden wird. Denn eigentlich möchte ich das entsprechende Script ja mit dem Ziel laden, dass ich später mit den darin enthaltenen Funktionen weiterarbeiten kann. Sind diese nicht da, ist das oft nicht sinnvoll möglich.

Wie immer könnt ihr euch die Quelltexte und die Benchmarks herunterladen.

Jan hat 152 Beiträge geschrieben

20 Kommentare zu “PHP-Dateien in andere Scripts einbinden

  1. Jan sagt:

    __autoload() hat doch damit gar nichts zu tun, denn damit allein kann ich keine externen Dateien einbinden.
    In der __autoload() wird ja meistens auch einfach mit require_once die nötige Klassenimplementierung geladen.

  2. IcyT sagt:

    Danke für den Test, das hatte mich auch schon lange mal interessiert, wie groß (bzw. klein) da die Unterschiede sind.
    Ich selbst benutze auch immer require_once.

  3. Flor1an sagt:

    Ich kann deine Empfehlung nicht teilen! Denn wer *_once() nutzt um „mögliche“ doppelte Einbindung von Skripten vorbeugen möchte der hat einfach nur ein schlechtes Softwaredesign. Es sollte ohne Probleme möglich sein das ganze so zu verarbeiten dass Skripte nur einmal eingebunden werden! Nur in ganz seltenen Fällen sollte man *_once() Funktionen nutzen und zwar wenn es sich einfach nicht vermeiden lässt. Im Normalfall lässt es sich allerdings vermeiden.

  4. Jan sagt:

    Natürlich lässt es sich vermeiden. Es lässt sich auch vermeiden mit dem Auto gegen einen Baum zu fahren, allerdings baut man trotzdem Airbags in Autos ein.

    Gegen eine zusätzliche Sicherheit ohne Performancenachteil kann ich jedenfalls nix sagen.

  5. kaki sagt:

    @Flor1an
    Das mag bei kleinen Projekten sicherlich der Fall sein. Aber wenn an einem Großprojekt mit mehreren (ich würde schon sagen ab 6-10) Programmieren über mehrere „Generationen“ in verschiedenen Teamkonstellationen gearbeitet wird, dann bist du einfach froh wenn du in deinem Modul require_once(„klasse.die.vor.5monaten.jemand.geschrieben.hat.php“) machen kannst, ohne dir gedanken machen zu müssen ob IRGENDWO jemand anderes die in seinem Modul auch schon benutzt hat.

  6. madmufflon sagt:

    sorry, falsch ausgedrückt.
    war ein vorschlag für ein damit verwandtes thema. ich wollte vorschlagen mal zu testen, wieviel performance es kostet autoload einzusetzen, gegenüber einfachen requires (bzw. includes)

    ganz nebenbei: require_once kann auch mal zu problemen führen, wenn man nicht aufpasst. dann hat man weiter oben innerhalb einer methode schonmal irgend eine konfigurationsdatei aufgerufen und dann funktioniert das programm nicht, weil man require_once verwendet hat. deswegen finde ich eigentlich es lässt sich keine pauschale aussage treffen, man muss immer schauen, ob man in methoden oder auserhalb arbeitet, usw.
    ich persönlich verwende normalerweise require, aber es ist schonmal gut zu wissen, dass es rein performancetechnisch keinen unterschied macht

  7. manuel sagt:

    also dieser Test ist nicht gut!
    Schau dir ZendFramework oder Solar an, dann weisst du wie sauberes und gutes Design funktioniert und dort kann dann wie von madmufflon angemerkt wurde mit __autoload ohne Probleme jede benötigte Klasse per include einfügen!

    Wer das nicht glaubt bitte mal folgendes anschauen, dazu gibts auch noch nen Video ka wo… hier nur die Slides:
    http://talks.php.net/show/drupal08

  8. Jan sagt:

    Hab mir die Slides mal angesehen, kann da aber nichts erkennen, was an einem Framework (das immer Overhead bedeutet, es sei denn es ist genau für EINE Anwendung geschrieben) schneller sein soll.

    Zumal OOP und Performance sowieso zwei Dinge sind, die sich nur schwer vereinbaren lassen.

    Und drittens: Ich sehe auf den Slides nur die Include-Hierarchien der verschiedenen Frameworks. Diese nutzen natürlich genauso die oben beschriebenen 4 Varianten, Dateien einzufügen.

    Und: Wo gehts in meinem Beitrag um sauberes Design/Architektur? Es geht um einen Vergleich verschiedener PHP-Funktionen.

    Also ich nehme Kritik gern an, aber bitte nochmal konkretisieren.

  9. Stephan sagt:

    @kaki

    Grundsätzlich kann ich dir nur beipflichten, aber defakto ist es ohne größeren Aufwand unter Verwendung entsprechender Projektverwaltungssoftware problemlos möglich, die Entstehung derartiger Szenarien zu unterbinden.

    Wenn der Entwicklungsprozess aus den geschilderten Gründen darunter leidet, dass eine Hand nicht weiß, was die andere macht, kann von einem effizienten Schaffungsprozess nicht mehr die Rede sein und dem Projektverantwortlichen nur Versagen vorgeworfen werden.

    Zweifelsohne beseitzen beide Funktionen Daseinsberechtigung, der Vorzug der einen dient aber unter eben jenen geschilderten Umständen nicht der Problemlösung sondern vielmehr der Verschleierung.

    Letztlich resultiert aus diesem Disput eine Grundsatzdiskussion, welche sich grundsätzlich nicht oder nur schwer auf eine konstruktive Art und Weise führen lässt.

  10. amun ra sagt:

    Rasmus Lerdorf:

    „Try to avoid using include_once and require_once if possible. You are much better off using a straight include or require call, because the *_once() calls are very slow under an opcode cache. Sometimes there is no way around using these calls, but recognize that each one costs you an extra open() syscall and hash look up.“

  11. Radhad sagt:

    Ich finde der Test mit einem „inlcude“ sagt doch überhaupt garnichts aus! Bei den *_once-Funktionen wird geprüft, ob die Datei schon vorhanden ist – wie willst du das Nachstellen, wenn du nur eine einzige Datei einbindest? Wenn es 10 mal dieselbe wäre, die ca. 10 Funktionen verwendet, ok, das wäre repräsentativer als nur mit einem einzigen include!

  12. Jan sagt:

    @Radhad: Es ging darum, aufzuzeigen, ob ein Performanceunterschied zwischen den besagten 4 Funktionen besteht. Mein Ziel war nicht, die Funktionalität der once-Funktionen durch die ohne once nachzustellen.

  13. amun ra sagt:

    Ich verweise nochmal auf mein Kommentar von heute.
    Rasmus Lerdorf ist der Erfinder von PHP
    und auf seiner Homepage sagt er explizit,
    das man die *_once calls vermeiden sollte.

    Ich halte deinen Benchmark für nicht aussagekräftig!

  14. Jan sagt:

    Denke das hier ist der Grund, warum ich keine Einbußen feststellen konnte:
    „Is there a real cost to do require once more then once? YES, there is a real path call that has to be done, and this is cached – so the real cost is on a miss – when the include path setting is incorrect – it’s traumatic, terrible. The cost is when the server config isn’t correct – when the config is great it’s not a problem.“

    Das habe ich gerade gefunden unter http://www.nowpublic.com/tech-biz/rasmus-lerdorf-simple-hard-drupalcon-2008-key-note

    Kannst Du bitte noch Deine Quelle angeben, amun ra?

  15. alex sagt:

    ich habe schon ein paar mal gelesen, dass man die _once varianten vermeiden sollte, wenn man opcode-cacher benutzt… also xcache, APC etc. da es dann erhebliche performanceeinbußen geben kann.
    habe grad keine quelle und keine benchmarks parat, wollts nur mal erwähnt haben 🙂

  16. Marcel sagt:

    Ich will mal kurz das Thema nochmal aufgreifen, obwohl aschon etwas älter.

    @madmufflon
    Der Leistungsunterschied zwischen einem:

    include ‚meine_klasse.php‘;
    und:
    __autoload();
    wird schon vorhanden sein, das ergibt sich schon aus der Beschreibung der autoload() Funktion. Weil diese erst in letzter Instanz nachschaut ob eine File mit dem Namen der Klasse vorhanden ist.
    Solche extra Schritte Kosten natürlich Zeit. Einzisgter Vorteil den ich da gerade sehe ist, das wenn eine Klasse nicht benötigt/aufgerufen wird, diese auch nicht geladen werden muß. Aber das läßt sich mit einem ordentlichen Framework auch vermeiden, wie ja in den Kommentaren schon bemerkt wurde.

    @all
    Das es ein Leistungsunterschied zwischen z.B. include und z.B. include_once geben könnte, kann man sich auch vorstellen, denn bei once muß php erst schauen ob die Datei schonmal geladen wurde (Mehrarbeit). Im obrigen Beitrag war der Unterschied aber nicht meßbar.

    Einsetzen würde ich aufrufe mit _once eigentlich nur wenn ich fremde Progs anpassen müsste wo man kaum noch ein Durchblick sieht. Weil manche bekommen es fertig manche includes nur unter bestimmten Bedingungen durchzuführen.

  17. blubb sagt:

    Ich weiß ja nicht wie ihr denkt aber wenn ihr so genau auf Performance schaut dann nehmt asp mit c++.

    Und wenn man PHP Scripte schreibt sollte man die sowieso funktionaler schreiben als andere Sprachsyntax schon alleine wegen der Sicherheit. Wer nun bei einer funktion wie include oder require auf dem Speed achtet der ist selbst schuld. Man sollte schauen das man bei anderen Sachen Performance reinbringt z.b bei Schleifen Abfrage und sow eiter

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>