Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Push Meldungen
 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

Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains und...
https://www.Hosterplus.de
Webspace-Mieten.d...
Webhosting mit Plesk aus Erfurt: Vier Webspace-Pak...
https://www.Webspace-Mieten.de
 
 
 

PHP stream_get_meta_data(): Metadaten von Streams lesen

Sie befinden sich: Home > Php Tutorial > PHP stream_get_meta_data():...

PHP stream_get_meta_data(): Metadaten von Streams lesen
Veröffentlicht am:
08.11.2026
Hits / Besucher:
11
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Die Importfunktion bekommt ihre Daten mal aus einer hochgeladenen Datei, mal aus einem gzip-Archiv, mal aus einem Puffer in php://temp und mal direkt von einer URL. Jede Quelle verhält sich ein wenig anders, und das Skript soll das erkennen, bevor es liest. Ob ein Stream zurückspringen kann, zeigt das Tutorial zu fseek(), wie man bei Socket-Verbindungen Zeitlimits setzt, das Tutorial zu stream_socket_client(). Hier geht es um das ganze Array, das PHP stream_get_meta_data() liefert: welche Schlüssel es gibt, welcher Stream welche davon kennt und was sie wirklich bedeuten.

Illustration zum Tutorial: PHP stream_get_meta_data(): Metadaten von Streams lesen

Die Messungen zu PHP stream_get_meta_data() liefen unter Windows und Linux mit PHP 8.4, die HTTP-Beispiele gegen einen lokalen Webserver.

Was PHP stream_get_meta_data() über einen Stream verrät

Ein Stream ist in PHP alles, woraus sich lesen oder worin sich schreiben lässt: eine Datei, ein Speicherpuffer, eine Netzwerkverbindung, ein gepacktes Archiv. Den Zugang stellt ein Wrapper her, etwa plainfile für Dateien oder http für Webadressen. PHP stream_get_meta_data() nimmt einen offenen Stream entgegen und gibt ein Array zurück, das Herkunft, Zustand, Puffer und Fähigkeiten beschreibt. Manche Wrapper legen eigene Daten dazu.

Die Schlüssel lassen sich in vier Gruppen ordnen:

flowchart TD
    A[stream_get_meta_data] --> B[Herkunft]
    A --> C[Zustand]
    A --> D[Puffer und Fähigkeiten]
    A --> E[Zusatzdaten je Wrapper]
    B --> B1[wrapper_type, stream_type]
    B --> B2[mode, uri]
    C --> C1[timed_out, blocked, eof]
    D --> D1[unread_bytes, seekable]
    E --> E1[wrapper_data bei HTTP]
    E --> E2[crypto bei TLS]
    E --> E3["mediatype bei data://"]

Die Signatur im Original: Parameter und Rückgabewert

PHP stream_get_meta_data() hat genau einen Parameter:

stream_get_meta_data(resource $stream): array


/* stream - a stream created by fopen(),
fsockopen(), stream_socket_client()
return - an array with the metadata of the
stream; the list of keys depends
on the wrapper:
timed_out - true if the stream timed out
while waiting for data on the
last call to fread() or fgets()
blocked - true if the stream is in
blocking io mode
eof - true if the stream has reached
end-of-file
unread_bytes - bytes currently contained in
the read buffer of php
stream_type - a label describing the
implementation of the stream
wrapper_type - the protocol wrapper layered
over the stream, e.g. http
wrapper_data - data of the wrapper, for http
the response headers
mode - the type of access required
seekable - whether the current stream
can be seeked
uri - the name of the file or url
associated with the stream.
Streams are the way php handles a file, a
remote resource or compressed data
(compress.zlib, ftp, http) with a common
set of functions. */

Einen Fehlerwert kennt PHP stream_get_meta_data() nicht. Ein bereits geschlossener Stream wirft TypeError: stream_get_meta_data(): supplied resource is not a valid stream resource, und ein gescheitertes fopen() endet mit must be of type resource, false given. Das Ergebnis von fopen() gehört deshalb vorher geprüft. Welche Schlüssel im Array stehen, hängt vom Wrapper ab, so steht es auch im Kommentar.

Alle Schlüssel im Überblick

Für eine gewöhnliche Datei liefert PHP stream_get_meta_data() neun Schlüssel. Ausgegeben mit var_export() statt print_r(), damit false nicht als leere Stelle und true nicht als 1 erscheint:

<?php

$h = fopen(__DIR__ . '/daten.txt', 'rb');
foreach (stream_get_meta_data($h) as $k => $v) {
echo $k, ': ', var_export($v, true), PHP_EOL;
}
/* timed_out: false
blocked: true
eof: false
wrapper_type: 'plainfile'
stream_type: 'STDIO'
mode: 'rb'
unread_bytes: 0
seekable: true
uri: '/var/www/daten.txt' */

Der Pfad hinter uri ist ein Beispiel, PHP gibt ihn so zurück, wie er an fopen() ging. Ein relativer Pfad bleibt also relativ. Die Bedeutung der Schlüssel im Einzelnen:

Schlüssel Typ Bedeutung
timed_out bool das letzte Lesen hat das Zeitlimit erreicht
blocked bool Lesen wartet, bis Daten da sind
eof bool das Ende wurde zuletzt erreicht
wrapper_type string Wrapper, etwa plainfile, PHP, http
stream_type string Umsetzung darunter, etwa STDIO, MEMORY, ZLIB
mode string Zugriffsmodus, meist der an fopen() übergebene
unread_bytes int Bytes im Lesepuffer von PHP
seekable bool der Stream kann springen
uri string Dateiname oder URL beim Öffnen
wrapper_data je nach Wrapper bei HTTP die Antwort-Header als Array
crypto array nur bei TLS: Protokoll und Verfahren

Welche Streams welche Schlüssel liefern

Die neun Schlüssel der Datei sind keine feste Zusage. Im Test lieferte PHP stream_get_meta_data() bei mehreren Stream-Typen weniger Einträge, andere brachten zusätzliche mit. Unter Windows und Linux waren die Ergebnisse gleich:

Stream wrapper_type / stream_type Besonderheit
php://memory PHP / MEMORY geöffnet mit 'r+', gemeldet als w+b
php://temp PHP / TEMP kein timed_out, blocked, eof
data:// RFC2397 / RFC2397 zusätzlich mediatype und base64, dafür kein timed_out, blocked, eof
tmpfile() plainfile / STDIO uri ist der echte Pfad der Temp-Datei
compress.zlib:// ZLIB / ZLIB alle neun Schlüssel
gzopen() fehlt / ZLIB kein wrapper_type, kein uri
popen(), Prozess-Pipe fehlt / STDIO kein wrapper_type, kein uri, nicht suchbar
opendir() plainfile / dir auch ein Verzeichnis ist ein Stream, ohne uri
php://stdin PHP / STDIO aus einer Pipe nicht suchbar, aus einer umgeleiteten Datei suchbar
TCP-Socket fehlt / tcp_socket/ssl heißt auch ohne Verschlüsselung so
HTTP über fopen() http / tcp_socket/ssl zusätzlich wrapper_data, bei HTTPS auch crypto
eigener Wrapper user-space / user-space wrapper_data ist das Objekt der Wrapper-Klasse

Bei mode kennen die Speicherpuffer php://memory und php://temp nur drei Werte. Ein Modus mit a erscheint als a+b, einer mit w oder + als w+b, r, rb und c als rb. So wurde im Test aus 'r+' und 'c+' der Wert w+b.

Wer die Schlüssel ohne Prüfung liest, bekommt bei php://temp die Warnung Undefined array key "eof". Der Null-Koaleszenz-Operator mit einem sinnvollen Ersatz verhindert das:

<?php

$t = fopen('php://temp', 'w+b');
$meta = stream_get_meta_data($t);

var_dump(array_key_exists('eof', $meta));
/* bool(false) */

$ende = $meta['eof'] ?? feof($t);
$wrapper = $meta['wrapper_type'] ?? '-';

wrapper_data: HTTP-Header und Weiterleitungen

Beim HTTP-Wrapper legt PHP stream_get_meta_data() unter wrapper_data die Antwort-Header als Array ab, eine Zeile je Header, die Statuszeile zuerst. Nach einer Weiterleitung stehen dort mehrere Antworten. Im Test antwortete der Server auf die erste Adresse mit 301 und auf das Ziel mit 200:

<?php

function letzter_status(array $kopf): ?int
{
$status = null;
$muster = '#^HTTP/\S+ (\d{3})#';
foreach ($kopf as $zeile) {
if (preg_match($muster, $zeile, $t)) {
$status = (int) $t[1];
}
}
return $status;
}

$h = fopen('http://example.com/alt', 'r');
$meta = stream_get_meta_data($h);
foreach ($meta['wrapper_data'] as $zeile) {
if (str_starts_with($zeile, 'HTTP/')) {
echo $zeile, PHP_EOL;
}
}
/* HTTP/1.1 301 Moved Permanently
HTTP/1.1 200 OK */

echo $meta['uri'], PHP_EOL;
/* http://example.com/alt (nicht das Ziel) */
echo letzter_status($meta['wrapper_data']);
/* 200 */

Die Header beider Antworten stehen hintereinander in wrapper_data, im Test 24 Zeilen. Die neue Adresse findet sich nur im Location-Header dazwischen, denn uri bleibt die Adresse, die an fopen() ging. Für den Statuscode des Inhalts zählt die letzte Statuszeile, nicht wrapper_data[0]. Mit der Kontextoption 'follow_location' => 0 blieb es bei der 301-Antwort. Überraschend war 'max_redirects' => 1: Damit folgte PHP gar keiner Weiterleitung und brach mit Redirection limit reached, aborting ab. Wie man eine Kette von Weiterleitungen auswertet, ohne den Inhalt zu laden, zeigt das Tutorial zu get_headers().

Bei einem Fehlerstatus liefert fopen() normalerweise nur false und die Warnung HTTP request failed! HTTP/1.1 404 Not Found. Mit ignore_errors kommt trotzdem ein Stream zurück, samt Headern:

<?php

$ctx = stream_context_create([
'http' => ['ignore_errors' => true],
]);
$h = fopen($url, 'r', false, $ctx);
$kopf = stream_get_meta_data($h)['wrapper_data'];
echo letzter_status($kopf);
/* 404 */

$http_response_header und http_get_last_response_headers()

Dieselben Header gibt es noch auf zwei anderen Wegen. Die Variable $http_response_header füllt PHP nach jeder HTTP-Anfrage automatisch, und seit PHP 8.4 liefert http_get_last_response_headers() sie als Funktion. Im Test waren alle drei Quellen nach fopen() identisch, auch nach der Weiterleitung. Beide Alternativen enthielten die Header sogar nach einem gescheiterten fopen() mit 404, wo es gar keinen Stream gibt. Der Unterschied liegt im Geltungsbereich:

<?php

function lade(string $url): void
{
$h = fopen($url, 'r');
var_dump(isset($http_response_header));
fclose($h);
}

lade('http://example.com/');
/* bool(true) */
var_dump(isset($http_response_header));
/* bool(false) */
var_dump(is_array(http_get_last_response_headers()));
/* bool(true) */

Die Variable entsteht nur im Geltungsbereich des Aufrufs, hier also in der Funktion, die fopen() aufruft. Die neue Funktion gilt überall und wird mit http_clear_last_response_headers() auf null zurückgesetzt. Seit PHP 8.5 gilt die Variable als veraltet: Im Test meldete PHP 8.5.8 schon beim Kompilieren Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead, auch für Code, der nie ausgeführt wird. Bei einem offenen Stream ist wrapper_data deshalb die direkteste Quelle, sonst http_get_last_response_headers().

unread_bytes: was im Puffer von PHP liegt

PHP liest nicht Byte für Byte, sondern in Blöcken zu 8192 Bytes. Was davon noch nicht beim Skript angekommen ist, meldet PHP stream_get_meta_data() unter unread_bytes:

<?php

$h = fopen(__DIR__ . '/gross.txt', 'rb');
fgets($h);
echo stream_get_meta_data($h)['unread_bytes'];
/* 8190 */

$h = fopen(__DIR__ . '/gross.txt', 'rb');
stream_set_chunk_size($h, 1024);
fgets($h);
echo stream_get_meta_data($h)['unread_bytes'];
/* 1022 */

Die Datei hatte 10.000 Zeilen mit je einem x, also 20.000 Bytes. Nach der ersten Zeile mit zwei Bytes lagen 8190 Bytes im Puffer, mit der Blockgröße 1024 waren es 1022. Der Wert ist eine Momentaufnahme des Puffers. Er kann dem Rest der Datei entsprechen, mit der Blockgröße 65536 lagen hier alle übrigen 19.998 Bytes im Puffer, verlassen kann man sich darauf aber nicht. Bei HTTP zeigte sich das deutlich: Direkt nach dem Öffnen einer Datei mit 5651 Bytes meldete Linux 5651 und Windows 3715, je nachdem, wie viel beim ersten Lesen vom Netz kam. Das Handbuch rät deshalb, den Wert nicht für Programmlogik zu verwenden.

Mit stream_set_read_buffer($h, 0) lässt sich der Puffer abschalten. Bei einer Datei mit 100 Bytes meldete unread_bytes nach fread($h, 3) dann 0 statt 97. fgets() puffert trotzdem, bis das Zeilenende gefunden ist. Eine verbreitete Sorge betrifft stream_select(): Daten im PHP-Puffer seien dafür unsichtbar. Im Test meldete stream_select() einen Socket mit fünf Bytes Rest im Puffer sofort als lesbar, die Sorge trifft auf PHP also nicht zu.

eof und feof(): letzter Stand gegen Nachprüfen

Der Schlüssel eof aus PHP stream_get_meta_data() und die Funktion feof() klingen gleich, verhalten sich aber nicht gleich. Bei einer Datei mit 24 Bytes meldeten nach fread($h, 24) beide noch false, erst der nächste Leseversuch setzte beide auf true. Bei einem Socket zeigte sich der Unterschied:

<?php

[$a, $b] = stream_socket_pair(
STREAM_PF_UNIX,
STREAM_SOCK_STREAM,
STREAM_IPPROTO_IP
);
fwrite($a, 'abc');
fclose($a);

echo fread($b, 10);
/* abc */
var_dump(stream_get_meta_data($b)['eof']);
/* bool(false) */
var_dump(feof($b));
/* bool(true) */
var_dump(stream_get_meta_data($b)['eof']);
/* bool(true) */

Unter Windows braucht das Beispiel STREAM_PF_INET statt STREAM_PF_UNIX, das Ergebnis war dasselbe. Der Schlüssel eof zeigt den zuletzt bekannten Stand. feof() fragt nach, merkt, dass die Gegenseite geschlossen hat, und aktualisiert dabei auch den Schlüssel.

timed_out, blocked, seekable und crypto in Kürze

Liefert fgets() bei einer Netzwerkverbindung false, kann die Gegenseite getrennt haben oder einfach zu langsam gewesen sein. timed_out unterscheidet beides:

<?php

stream_set_timeout($s, 1);
$zeile = fgets($s);
$meta = stream_get_meta_data($s);
if ($zeile === false && $meta['timed_out']) {
echo 'Zeitlimit erreicht';
} elseif ($zeile === false && feof($s)) {
echo 'Verbindung geschlossen';
}

Gegen den lokalen Webserver ohne Anfrage kam nach genau einer Sekunde false, timed_out stand auf true und blieb es bis zum nächsten Lesen. Welche Zeitlimits es bei Verbindungen gibt, erklärt das Tutorial zu stream_socket_client(). blocked wechselte nach stream_set_blocking($s, false) auf false. seekable beantwortet die Frage, ob fseek() und rewind() funktionieren, Einzelheiten dazu stehen im Tutorial zu fseek(). Bei verschlüsselten Verbindungen über https:// oder ssl:// stand crypto im Test unter Linux als erster Schlüssel im Array, mit dem Protokoll TLSv1.3 und 256 Bit. Bei http:// fehlt er ganz, daran erkennt man eine unverschlüsselte Verbindung, nicht am stream_type. Was dort sonst steht, zeigt das Tutorial zu stream_socket_enable_crypto().

Praxis: Stream-Diagnose mit PHP stream_get_meta_data()

Für Logs und Fehlermeldungen genügt oft eine Zeile, die sagt, um was für einen Stream es sich handelt. Die Funktion arbeitet mit allen Typen aus der Tabelle, weil sie fehlende Schlüssel abfängt:

<?php

function stream_info($h): string
{
$m = stream_get_meta_data($h);
return sprintf(
'%s/%s %s%s',
$m['wrapper_type'] ?? '-',
$m['stream_type'],
$m['mode'],
$m['seekable'] ? ' seekable' : ''
);
}

echo stream_info(gzopen('log.gz', 'r'));
/* -/ZLIB r seekable */

Für eine Datei ergab sie plainfile/STDIO rb seekable, für php://temp PHP/TEMP w+b seekable, für einen HTTP-Stream http/tcp_socket/ssl r und für fopen('php://stdin', 'r') aus einer Pipe PHP/STDIO r, die Konstante STDIN meldet dort rb. Dasselbe Array steckt hinter vielen Bibliotheken. Das PSR-7-Interface StreamInterface schreibt für getMetadata() vor, dass die Schlüssel denen von PHP stream_get_meta_data() entsprechen. Bibliotheken wie Guzzle setzen dieses Interface um. Wer einen eigenen Wrapper mit stream_wrapper_register() anmeldet, findet in wrapper_type und stream_type den Wert user-space und in wrapper_data die Instanz der eigenen Klasse.

Fazit

PHP stream_get_meta_data() beschreibt einen offenen Stream in einem Array, aber nicht jeder Stream liefert dieselben Schlüssel. php://temp kennt kein eof, gzopen() und Prozess-Pipes kein wrapper_type, HTTPS bringt crypto mit. Ein Zugriff mit ?? hält den Code frei von Warnungen.

Bei HTTP stehen in wrapper_data die Header aller Antworten einer Weiterleitungskette, der Statuscode des Inhalts ist die letzte Statuszeile, und uri bleibt die Ausgangsadresse. unread_bytes und eof sind Momentaufnahmen. Wer das Ende einer Verbindung sicher erkennen will, fragt mit feof() nach, und für ein Zeitlimit gibt timed_out die Antwort.

 

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.