Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Artfiles.de
Bietet Serviceorientierte...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains...
https://www.Hosterplus.de
 
 
 

PHP Output Buffering: ob_start, ob_get_clean und Templates

Sie befinden sich: Home > Php Tutorial > PHP Output Buffering: ob_st...

PHP Output Buffering: ob_start, ob_get_clean und Templates
Eintrag am:
20.07.2026
Hits / Besucher:
16
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Nicht immer soll die Ausgabe eines PHP-Skripts sofort beim Browser landen. Manchmal will man sie erst sammeln, verändern oder komplett verwerfen, bevor auch nur ein Byte das Skript verlässt. Genau dafür gibt es Output Buffering. Dieser Artikel erklärt die wichtigsten ob_*-Funktionen im Zusammenspiel, baut damit in wenigen Zeilen eine eigene Template-Engine und zeigt, wie sich der bekannte "headers already sent"-Fehler damit aus dem Weg räumen lässt.

Was ist PHP Output Buffering?

Output Buffering in PHP ist die Fähigkeit, alle Ausgaben eines Skripts erst einmal in einem Output-Puffer im Speicher zu sammeln, bevor sie an den Browser gesendet werden. Statt jedes echo und jedes HTML-Stück sofort an den Webserver weiterzuleiten, sammelt das laufende Skript den Output zwischenzeitlich als String im Puffer, der später ausgelesen, transformiert oder verworfen werden kann. Output-Puffer können sogar verschachtelt arbeiten, was später noch wichtig wird. Diese kleine Mechanik ist die Grundlage für eigene Template-Engines, für das Lösen des berühmten "headers already sent"-Fehlers und für die GZIP-Komprimierung im laufenden Request: Wenn der Output erst nachträglich rausgeht, kann der Header-Code vorher noch greifen.

Illustration zum Tutorial: PHP Output Buffering: ob_start, ob_get_clean und Templates

Bevor ob_start, ob_get_clean und eine kompakte Template-Engine an die Reihe kommen, ein kurzer Blick auf den Funktionsvergleich der wichtigsten ob_*-Aufrufe.

Dieses Tutorial zeigt die wichtigsten ob_*-Funktionen im Vergleich, baut eine kompakte Template-Engine in wenigen Zeilen und behandelt verschachtelte Puffer mit ob_get_level(). Am Ende ist klar, wann sich Output Buffering lohnt und wann es nur unnötigen Speicherverbrauch verursacht.

Die wichtigsten ob_*-Funktionen für PHP Output Buffering im Vergleich

Es gibt vier Funktionen, die immer wieder zusammen auftauchen. Sie sehen sich ähnlich, machen aber unterschiedliche Dinge. Wer den Unterschied einmal verstanden hat, schreibt deutlich klarere Buffer-Logik.

FunktionLiefert Inhalt?Beendet Buffer?
ob_get_contents()JaNein, Buffer bleibt aktiv
ob_get_clean()JaJa
ob_end_flush()Nein, gibt Inhalt direkt ausJa
ob_end_clean()Nein, verwirft InhaltJa

Mit diesem Vier-Pack kommt man bei 99 Prozent aller PHP Output Buffering-Use-Cases aus. ob_start() startet den Puffer, eine der vier Funktionen oben beendet oder liest ihn. Daneben gibt es noch ob_get_level() zur Inspektion und ob_implicit_flush() für eine seltene Spezialsituation, in der man sofort flushen will. Wer PHP Output Buffering täglich nutzt, hat diese Tabelle nach kurzer Zeit im Kopf und kombiniert die Funktionen ohne Nachschlagen.

Erste Schritte mit PHP Output Buffering: ob_start und ob_get_clean

Das einfachste PHP Output Buffering-Beispiel sammelt zwei echo-Ausgaben in einem String. Die Variable enthält am Ende genau das, was sonst sofort an den Browser gegangen wäre.

<?php

ob_start();
echo 'Hallo PHP';
echo PHP_EOL . 'mit Output Buffering';
$inhalt = ob_get_clean();

/* Variable enthaelt jetzt die zwei echoed Zeilen */
echo strlen($inhalt) . ' Zeichen abgefangen';

Was beim normalen Skript-Lauf direkt an den Browser geht, landet hier in $inhalt. Mit dem String lässt sich anschließend alles machen: in eine Datei schreiben, durch eine Suchen-Ersetzen-Logik laufen lassen, in eine Variable für ein Layout packen oder einfach verwerfen. Genau dieses Capture-Pattern ist die Basis für Template-Engines, für das Generieren von Mail-HTML und für alle Fälle, in denen PHP Output Buffering die Kontrolle über den Output-Strom übernehmen soll.

Eine Template-Engine mit PHP Output Buffering in wenigen Zeilen

Mit ob_start(), einem include und ob_get_clean() entsteht in 10 Zeilen Code eine simple Template-Engine. Sie nimmt einen View-Namen und ein Daten-Array, rendert das Template und gibt das fertige HTML als String zurück. Diese Mini-Engine zeigt eindrucksvoll, wie mächtig PHP Output Buffering im Alltag wirklich ist.

<?php

function render(string $view, array $daten = []): string
{
extract($daten, EXTR_SKIP);
ob_start();
include __DIR__ . '/views/' . $view . '.php';
return ob_get_clean();
}

/* views/hallo.php enthaelt z.B.:
<h1>Hallo <?= htmlspecialchars($name) ?></h1> */

$html = render('hallo', ['name' => 'Mia']);
echo $html;

Drei Dinge passieren in der Funktion. Erstens packt extract() die Schlüssel aus dem Daten-Array in lokale Variablen, sodass das Template $name direkt nutzen kann. Zweitens startet ob_start() einen Puffer, der die HTML-Ausgabe des Templates sammelt. Drittens liefert ob_get_clean() den kompletten String zurück, ohne ihn an den Browser zu senden. Damit kann der Aufrufer das Ergebnis weiterverarbeiten, in ein Layout packen oder mit anderen Komponenten kombinieren. Genau dieses Pattern findet sich auch in vielen großen Frameworks, wo PHP Output Buffering der unsichtbare Helfer hinter der Kulisse ist.

Das Header-Problem lösen

Der gefürchtete Fehler "Cannot modify header information - headers already sent" entsteht, wenn ein Skript header() oder setcookie() aufruft, nachdem schon ein Byte Output rausgegangen ist. Das kann ein vergessenes Leerzeichen vor <?php sein, eine Notice oder ein zu frühes echo. Mit einem Output-Puffer am Skriptanfang sammelt PHP alles im Speicher, sodass später trotzdem noch HTTP-Header gesetzt werden können.

<?php

ob_start();

/* Hier laufen Includes, Funktionen, Templates die Output erzeugen */
require __DIR__ . '/header.php';

/* Spaeter koennen wir trotzdem noch Header setzen */
header('Content-Type: text/html; charset=UTF-8');
header('X-Powered-By: PHP-Space');

/* Am Ende rausschicken */
ob_end_flush();

Diese Variante ist ein praktischer Notnagel für Bestandscode, dessen Output-Reihenfolge sich nicht ohne weiteres umbauen lässt. Bei Neuentwicklungen ist es sauberer, alle header()-Aufrufe vor jeden Output zu legen und ohne globalen Puffer auszukommen. Aber als Rettungsmittel für schwer wartbaren Legacy-Code spart ob_start() an dieser Stelle viele Stunden Debugging.

GZIP-Kompression mit PHP Output Buffering und ob_gzhandler

ob_gzhandler ist ein eingebauter Output-Handler, der den gepufferten Inhalt automatisch GZIP-komprimiert und den passenden Content-Encoding-Header setzt, falls der Browser das unterstützt. Aktiviert wird er als zweiter Parameter beim ob_start().

<?php

/* Pruefen ob Browser GZIP unterstuetzt, dann Handler aktivieren */
if (str_contains($_SERVER['HTTP_ACCEPT_ENCODING'] ?? '', 'gzip')) {
ob_start('ob_gzhandler');
}

echo str_repeat('Hallo PHP, ', 1000);
/* Wird komprimiert ausgeliefert; Browser bekommt Content-Encoding: gzip */

Bei großen HTML-Seiten kann GZIP die übertragene Datenmenge auf 20 bis 30 Prozent des Originals drücken. In der Praxis ist es meist sauberer, die Komprimierung beim Webserver via mod_deflate (Apache) oder gzip on (Nginx) zu aktivieren, weil dort die Konfiguration zentral und ressourcensparender liegt. Wenn das aber nicht möglich ist, etwa auf eingeschränktem Shared Hosting, bleibt ob_gzhandler als praktische Notlösung.

Verschachtelte Buffer und ob_get_level

Output Buffer arbeiten als Stack: jeder neue ob_start()-Aufruf legt einen neuen Puffer obenauf, jeder ob_*end*-Aufruf nimmt nur den obersten weg. Das ist nützlich, wenn ein Template-Renderer innerhalb eines anderen Templates eingesetzt wird, kann aber verwirren, wenn man die Tiefe nicht im Blick hat.

<?php

ob_start();
echo "Aeusserer Buffer\n";

ob_start();
echo "Innerer Buffer\n";
echo 'Buffer-Tiefe: ' . ob_get_level() . PHP_EOL;
$innerer = ob_get_clean();

echo "Inhalt zurueck im aeusseren: $innerer";
$gesamter = ob_get_clean();

echo $gesamter;
/* Ausgabe:
Aeusserer Buffer
Inhalt zurueck im aeusseren: Innerer Buffer
Buffer-Tiefe: 2 */

Der ob_get_clean()-Aufruf greift immer auf den obersten Puffer zu. Mit ob_get_level() lässt sich prüfen, wie viele Puffer aktuell aktiv sind. Wer eine Library schreibt, die selbst Puffer benutzt, sollte zur Sicherheit die Anfangs-Tiefe merken und beim Verlassen nur die selbst gestarteten Puffer wieder schließen. So bleibt der Aufrufer vor unerwarteten Seiteneffekten geschützt.

flowchart TD
    A[ob_start] --> B[Buffer aktiv]
    B --> C[Ausgaben sammeln]
    C --> D{Wie weiter?}
    D -->|holen und Ende| E[ob_get_clean]
    D -->|ausgeben und Ende| F[ob_end_flush]
    D -->|verwerfen| G[ob_end_clean]
    D -->|nur holen| H[ob_get_contents]

Unterschied zwischen ob_flush() und ob_end_flush()

In der Praxis werden ob_flush() und ob_end_flush() häufig verwechselt, obwohl sie sich an einem entscheidenden Punkt unterscheiden. ob_flush() schickt den aktuellen Pufferinhalt an die nächste Schicht (entweder den nächsten Puffer oder den Browser), lässt den Puffer aber aktiv und leer zurück. ob_end_flush() macht dasselbe und schliesst den Puffer anschließend. Für Streaming-Szenarien, in denen schon während des Skript-Laufs Daten beim Browser ankommen sollen, ist genau dieser Unterschied entscheidend.

<?php

ob_start();
echo 'Erster Teil';
ob_flush(); /* raus, Puffer bleibt aktiv */
flush(); /* Webserver-Puffer ebenfalls leeren */

echo 'Zweiter Teil';
ob_end_flush(); /* raus, Puffer schliesst */

ob_flush() ohne ein nachfolgendes flush() reicht meist nicht, weil der Webserver (Apache/Nginx) seinen eigenen Puffer mitbringt. Erst die Kombination beider Aufrufe sorgt dafür, dass die Bytes wirklich beim Browser landen. Ein typischer Anwendungsfall ist ein langsamer Report, der dem Nutzer Zeile für Zeile zurückgeben soll, statt am Ende eine grosse Antwort zu liefern.

Streaming und Server-Sent Events: Buffer komplett deaktivieren

Wer Server-Sent Events ausliefert oder einen grossen CSV-Download streamt, will gar keinen Puffer mehr im Weg haben. Sowohl die Skript-eigenen ob_*-Puffer als auch die output_buffering-Direktive aus der php.ini müssen vorher abgeräumt werden. Ein bewährtes Disable-Pattern sieht so aus.

<?php

/* alle aktiven Puffer schliessen */
while (ob_get_level() > 0) {
ob_end_flush();
}

header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no'); /* Nginx: kein Proxy-Puffer */

for ($i = 1; $i <= 5; $i++) {
echo "data: Tick $i\n\n";
flush();
sleep(1);
}

Die while-Schleife verhindert, dass ein global aktivierter Puffer aus der php.ini (etwa output_buffering = 4096) leise weiterläuft und dem Streaming alle Bytes zurückhält. X-Accel-Buffering: no weist Nginx an, die Antwort nicht selbst zu puffern. Ohne diesen Header würden Nutzer trotz flush() erst am Skript-Ende eine zusammenhängende Antwort sehen.

Aktive Output-Handler inspizieren

Bei verschachtelten Frameworks oder Bestandscode mit vielen Includes ist nicht immer klar, welche Handler aktuell auf dem Puffer-Stack laufen. ob_list_handlers() liefert ein Array mit den Namen aller aktiven Handler und hilft beim Debuggen.

<?php

ob_start('ob_gzhandler');
ob_start('meinLogger');

print_r(ob_list_handlers());
/* Array ( [0] => ob_gzhandler [1] => meinLogger ) */

Wer im Error-Log unerwartete Handler entdeckt, kann gezielt nach dem ob_start-Aufruf in den Includes suchen. Besonders nützlich ist die Funktion auch im Zusammenspiel mit der php.ini-Direktive output_buffering: Steht dort ein Handler-Name statt On oder einer Bytegrösse, taucht dieser hier ebenfalls auf.

Wann sollte ich Output Buffering nutzen, wann nicht?

Output Buffering ist immer dann sinnvoll, wenn HTML-Ausgabe als String weiterverarbeitet werden soll, bevor sie zum Browser geht. Klassische Fälle sind Template-Engines, das Sammeln von Mail-HTML für den anschließenden Versand, Suchen-Ersetzen über den fertigen Output, oder das Lösen von Header-Problemen in Bestandscode. Auch für das Erstellen statischer Snapshots, etwa beim Generieren einer Sitemap oder eines Cache-Files, ist ob_get_clean() ein guter Helfer.

Ungeeignet ist Output Buffering bei sehr großen Ausgaben, weil der gesamte Inhalt im Speicher gehalten wird. Eine 50-MB-Excel-Export-Funktion sollte den Output direkt in eine Datei oder einen Stream schreiben, statt erstmal alles im RAM zu sammeln. Auch globales Buffering über die php.ini-Direktive output_buffering = On ist mit Vorsicht zu genießen: Es kann unerwartetes Verhalten erzeugen, weil ein header()-Aufruf scheinbar funktioniert, obwohl er es ohne den globalen Puffer nicht hätte. Im Zweifel lokal mit ob_start() einsetzen, dort, wo der Code es wirklich braucht, und nicht global aktivieren.

Eigener Output-Handler als Callback

PHP Output Buffering kennt eine mächtige Erweiterung: ein eigener Output-Handler kann den gepufferten String vor der Ausgabe nochmal transformieren. Der Handler ist ein normales Callable, das den String entgegennimmt und einen veränderten String zurückgibt. Klassische Anwendung ist das Logging der Antwort, das Einfügen einer Debug-Toolbar oder das automatische Ersetzen von Token-Platzhaltern.

<?php

function meinLogger(string $output): string
{
file_put_contents(
__DIR__ . '/output.log',
date('Y-m-d H:i:s') . ' Bytes: ' . strlen($output) . PHP_EOL,
FILE_APPEND
);
/* Original-Output bleibt unveraendert */
return $output;
}

ob_start('meinLogger');
echo '<h1>Hallo Output Buffering</h1>';
echo '<p>Wird gleich an den Browser geschickt</p>';
ob_end_flush();
/* Logger-Funktion wird einmal beim ob_end_flush mit dem gesamten Inhalt aufgerufen */

Der Handler-Mechanismus ist bewusst flexibel gehalten. Wer einen eigenen Mini-Caching-Layer baut, kann den Handler so schreiben, dass er den Output zusätzlich in eine Datei legt. Wer eine Debug-Toolbar einblendet, kann am Ende des HTML-Body einen </body>-Tag suchen und davor das Toolbar-Markup einsetzen. Wichtig ist nur: Der Handler soll keine eigenen Echo-Aufrufe machen, sonst kommt der Output an unerwarteten Stellen heraus.

PHP Output Buffering und HTTP-Status-Codes

Ein häufiger Anwendungsfall ist die Kombination aus Output Buffering und einer 404-Seite. Sobald ein Skript merkt, dass die angefragte Ressource nicht existiert, will es einen 404-Status setzen, dann eine Fehlerseite ausgeben und nicht den halb gerenderten Content. Mit ob_clean() lässt sich der bisherige Output verwerfen und durch eine saubere Fehlerseite ersetzen.

<?php

ob_start();
echo '<html><body><h1>Lade Artikel ...</h1>';

$artikel = sucheArtikel($_GET['id'] ?? 0);

if ($artikel === null) {
ob_clean(); /* bereits gepuffertes verwerfen */
http_response_code(404);
include __DIR__ . '/views/404.php';
ob_end_flush();
exit;
}

echo '<p>' . htmlspecialchars($artikel->text) . '</p>';
echo '</body></html>';
ob_end_flush();

Der Trick mit ob_clean() und anschließendem Status-Code ist nur deshalb möglich, weil der Header noch nicht raus ist. Ohne PHP Output Buffering wäre der <h1> schon beim Browser angekommen und der Status-Code stünde auf 200 fest. So zeigt sich, dass Output Buffering nicht nur Komfort ist, sondern auch ein wichtiges Werkzeug für korrektes HTTP-Verhalten in dynamischen Seiten.

Performance-Hinweise zu PHP Output Buffering

Output Buffering hat einen kleinen, aber messbaren Speicher-Overhead. Jeder Puffer hält seinen Inhalt komplett im RAM, bis er geleert oder verworfen wird. Bei vielen kleinen Templates und einer typischen Webseite mit wenigen Kilobyte HTML ist das völlig egal. Bei Seiten mit mehreren Megabyte Inhalt, etwa bei serverseitig gerenderten Single-Page-Apps mit umfangreichen Datasets, kann sich der Speicherverbrauch summieren.

Eine gute Faustregel: Pro Request maximal zwei aktive Puffer, ausser bei sehr bewussten Pipeline-Konstruktionen. Wer mit ob_get_level() regelmäßig sieben oder acht offene Puffer sieht, hat irgendwo ein Leck und sollte die Library-Reihenfolge prüfen. Mit einem kurzen Helper lässt sich das überwachen.

<?php

register_shutdown_function(function () {
if (ob_get_level() > 2) {
error_log('Achtung: ' . ob_get_level() . ' offene Output-Buffer am Skript-Ende');
}
});

So bekommt man im Error-Log einen Hinweis, wenn ein Skript zu viele Puffer offen lässt. Das hilft besonders bei Bestandscode mit vielen Includes, in denen einzelne Komponenten ihre eigenen Puffer aufmachen, ohne sie sauber zu schließen.

PHP Output Buffering in Frameworks

Auch wenn die meisten modernen PHP-Frameworks wie Laravel, Symfony oder Slim ihre eigenen Response-Objekte mitbringen, steckt unter der Haube fast immer Output Buffering. Eine View-Datei in Laravel wird zum Beispiel von einer Engine wie Blade gerendert, die ihrerseits ob_start einsetzt, damit der gerenderte Inhalt als String über den Response-Builder weiter reisen kann. Diese Schicht wird vor dem Entwickler verborgen, ändert aber nichts daran, dass die Mechanik im Kern auf PHP Output Buffering basiert.

Wer ein eigenes Mini-Framework schreibt, bekommt mit den vorgestellten Patterns alles, was er für einen sauberen Request-Response-Zyklus braucht. Eine Render-Funktion erzeugt das HTML als String. Ein Layout-Wrapper packt es in ein gemeinsames Head- und Footer-Template. Eine Middleware sammelt den Output am Ende ein und gibt ihn an die HTTP-Schicht weiter. Wer diese drei Schichten beherrscht, hat ein gut wartbares Setup ohne große Abhängigkeiten.

Auch für Tests ist Output Buffering ein verlässliches Werkzeug. Im PHPUnit-Test lässt sich expectOutputString() nutzen, was intern ebenfalls einen Output-Puffer aufmacht und mit dem erwarteten String vergleicht. Wer komplexere Prüfungen braucht, kann selbst ob_start() aufrufen, das Verhalten provozieren und mit ob_get_clean() den gesammelten Output gegen erwartete Strukturen vergleichen. Das macht selbst Templates testbar, ohne dass eine echte HTTP-Antwort geschickt werden muss.

Sichere Reihenfolge der ob_-Aufrufe

Eine Sache, die in der Praxis immer wieder für Ärger sorgt, ist die Reihenfolge der ob_-Aufrufe. Wer einmal ob_start() aufruft, muss später genau einmal mit ob_end_flush(), ob_end_clean() oder ob_get_clean() schließen. Wer den Aufruf vergisst, hat einen offenen Puffer bis zum Skript-Ende, was bei verschachteltem Code zu Überraschungen führt. Wer doppelt schließt, bekommt eine Notice mit dem Hinweis, dass der Buffer-Stack leer ist.

Eine bewährte Konvention: pro Funktion oder Methode genau einen eigenen Puffer aufmachen und auch wieder schließen. So bleibt die Buffer-Bilanz immer ausgeglichen und der Code lässt sich gut lesen. Wer in einer Schleife Output sammelt, sollte den Puffer ausserhalb der Schleife aufmachen und dort schließen, nicht innerhalb. Das spart einen schnellen Overhead und macht die Logik klarer. Wer das einmal verinnerlicht hat, baut mit PHP Output Buffering robuste Capture-Patterns, die auch in komplexeren Anwendungen sauber funktionieren.

Das gilt besonders in Long-Running-Skripten, etwa in Cronjobs oder Worker-Loops. Ein vergessener Puffer dort kann über tausende von Iterationen Daten ansammeln und den Speicher unbemerkt voll laufen lassen. Eine kurze Routine, die nach jedem Iterationsschritt prüft, ob der Puffer-Level wieder auf dem Ausgangswert ist, schützt vor solchen Bugs. Genau diese Disziplin macht den Unterschied zwischen einem Demo-Skript und Production-Code, der monatelang ohne Aufsicht läuft.

Fazit

Das PHP Output Buffering ist eines der unauffälligsten, aber mächtigsten Werkzeuge der Sprache. Mit ob_start() beginnt der Puffer, mit ob_get_clean() wird er als String abgeholt und beendet, mit ob_end_flush() und ob_end_clean() gibt es zwei weitere Varianten zum Schließen. Auf dieser Basis entstehen kompakte Template-Engines, Notlösungen für Header-Probleme und gut komprimierte HTTP-Antworten. Wer das Stack-Verhalten verschachtelter Puffer und den Speicherverbrauch im Blick behält, hat mit den ob_*-Funktionen ein zuverlässiges Werkzeug für Templating und Output-Kontrolle in PHP.

 

Tags:

 

 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.