Dieser Artikel bietet Ihnen eine detaillierte Einführung (Codebeispiel) über den PHP-Ausgabepuffer. Ich hoffe, dass er für Freunde hilfreich ist.
PHP-Ausgabepuffer:
Puffer: ist eigentlich ein Speicheradressraum. Es dient zur Speicherung von Bereichen für die Datenübertragung zwischen Geräten mit unsynchronisierten Geschwindigkeiten oder Geräten mit unterschiedlichen Prioritäten. Durch Puffern kann die Wartezeit für die Interaktion zwischen Prozessen verkürzt werden, sodass der Betriebsprozess des schnellen Geräts nicht unterbrochen wird, wenn Daten von einem langsamen Gerät gelesen werden.
Der Ausgabestream von PHP enthält viel Inhalt normalerweise Text, den Entwickler von PHP ausgeben möchten. Die meisten dieser Texte werden mit der Funktion echo oder printf() ausgegeben. Jede Funktion, die Inhalte ausgibt, verwendet den Ausgabepuffer
Dies bezieht sich auf ein normales PHP-Skript. Wenn Sie eine PHP-Erweiterung entwickeln, kann die verwendete Funktion (C-Funktion) direkt ausgeben und in die SAPI-Pufferschicht schreiben, ohne die Ausgabepufferschicht zu durchlaufen (wir können sie in die PHP-Quelldatei schreiben). main /php_output.h Erfahren Sie mehr über die API-Dokumentation für diese C-Funktionen)
2> Der Ausgabepuffer ist nicht die einzige Ebene, die zum Puffern der Ausgabe verwendet wird, sondern nur eine von vielen Ebenen Die Schicht unterscheidet sich von der verwendeten. Im Zusammenhang mit SAPI (Web oder CLI) können unterschiedliche SAPIs unterschiedliche Verhaltensweisen aufweisen
Allgemein bekannt als „Ausgabepuffer“
3> Ausgabepuffer in SAPI, das sind die Schichten in PHP, die puffern, wenn die Ausgabebytes PHP verlassen und in die unteren Schichten der Computerarchitektur gelangen (Terminalpuffer, Fast-CGI-Puffer). , Webserver-Puffer, Betriebssystem-Puffer, TCP/IP-Stack-Puffer usw.).
Die SAPI der PHP-CLI wird auch als Befehlszeilenschnittstelle bezeichnet. Sie setzt die Option „output_buffer“ in der php.ini-Konfiguration auf 0, was bedeutet, dass der Standard-PHP-Ausgabepuffer deaktiviert ist In der CLI wird der auszugebende Inhalt standardmäßig direkt an die SAPI-Schicht übergeben, es sei denn, Sie rufen die Klassenfunktion ob_() manuell auf. In der CLI wird der Wert von implicit_flush ebenfalls auf 1 gesetzt,Hinweis: Wir verwechseln oft die Rolle von implicit_flush mit PHP. Der Quellcode sagt alles: Wenn implicit_flush auf on (Wert 1) gesetzt ist, wird jede Ausgabe, sobald sie in den SAPI-Puffer geschrieben wird, geleert (flush, was bedeutet). Schreiben Sie die Daten in die untere Schicht, und der Puffer wird gelöscht)
Das heißt, wenn Daten an die CLI-SAPI gesendet werden, überträgt die CLI-SAPI die Daten sofort an die nächste Schicht, normalerweise die Standardausgabe Pipe, write() und fflush() sind dafür verantwortlich
Standard-PHP-Ausgabepuffer
: Wenn Sie eine andere SAPI als die CLI verwenden, wie z. B. PHP-FPM, Die folgenden drei pufferbezogenen Funktionen werden verwendet php.ini-Konfigurationsoptionen
implicit_flushoutput_handler Erstens können Sie die Funktion ini_set() nicht zum Ändern der Werte verwenden dieser Optionen zur Laufzeit, da diese Werte beim Start des PHP-Programms geändert werden. Sie werden vor der Ausführung eines Skripts analysiert, sodass Sie den Wert zur Laufzeit mit ini_set() ändern können, er wird jedoch nicht wirksam . Wir können ihre Werte nur ändern, indem wir die php.ini-Datei bearbeiten oder die Option -d beim Ausführen des PHP-Programms verwenden.
Standardmäßig setzt die PHP-Distribution den Ausgabepuffer im Abschnitt php.ini auf 4096 Wörter, sofern dieser Wert vorhanden ist auf EIN eingestellt ist, beträgt die Standardgröße des Ausgabepuffers 16 KB
Die Verwendung eines Puffers für Ausgabeinhalte in einer Webanwendungsumgebung ist gut für die Leistung
:
Die Standardeinstellung von 4 KB ist ein geeigneter Wert Dass Sie zunächst 4096 ASCLL-Zeichen schreiben, dann mit der darunter liegenden SAPI-Schicht kommunizieren und die Nachricht Byte für Byte über den Socket in einer Webanwendungsumgebung übertragen können. Der Weg ist nicht gut für die Leistung, ein besserer Weg ist die Übertragung des gesamten Inhalts auf einmal an den Server übertragen oder zumindest Stück für Stück übertragen. Je geringer die Anzahl der Datenaustausche zwischen den Schichten ist, desto besser ist die Leistung, die immer beibehalten werden sollte. Die Ausgabepuffer sind verfügbar und PHP ist für die Übertragung ihres Inhalts verantwortlich an den Endbenutzer, nachdem die Anfrage abgeschlossen ist. Der Entwickler muss nichts tun
Implicit_flush ist standardmäßig deaktiviert, sodass das Schreiben neuer Daten nicht aktualisiert wird. Für das FastCGI-Protokoll besteht der Aktualisierungsvorgang darin, nach jedem Schreibvorgang ein FastCGI-Array-Paket zu senden Datenpaket Es wäre besser, wenn der Puffer voll wäre. Wenn Sie den SAPI-Puffer manuell aktualisieren müssen, verwenden Sie die Funktion „flush()“. Wenn Sie eine Aktualisierung schreiben möchten, können Sie „implicit_flush“ festlegen oder die Funktion „ob_implicit_flush()“ aufrufen.
Empfohlene Konfiguration:
„output_buffering=4096“
implicit_flush = Off/no
Um die Größe des Ausgabepuffers zu ändern, stellen Sie sicher, dass der verwendete Wert ein Vielfaches von 4/8 ist, was 32/64-Bit-Betriebssystemen entspricht.
Output_handler ist ein Rückruffunktion, die sein kann Ändern Sie den Inhalt im Puffer, bevor Sie den Puffer aktualisieren
Der Inhalt im Puffer wird an die Rückruffunktion Ihrer Wahl übergeben (nur eine kann verwendet werden), um eine Inhaltskonvertierung durchzuführen. Wenn Sie also den Inhalt erhalten möchten, den PHP dem Webserver und dem Benutzer zur Verfügung stellt, können Sie den Ausgabepuffer-Rückruf verwenden. Die Ausgabe bezieht sich auf: Nachrichtenkopf, Nachrichtenkopf. Der HTTP-Header ist auch Teil der Ausgabepufferschicht
Nachrichtenheader und Nachrichtentext :
Tatsächlich kann jede PHP-Funktion, die sich auf die Ausgabe des Nachrichtenheaders bezieht (header(), setcookie( ) , session_start()) verwenden alle die interne Funktion sapi_header_op, die den Inhalt nur in den Nachrichten-Header-Puffer schreibt. Wenn wir die Funktion printf() verwenden, wird der Inhalt zuerst in den Ausgabepuffer geschrieben (möglicherweise mehrere). Wenn der Inhalt des Ausgaberingbereichs gesendet werden muss, sendet PHP zuerst den Nachrichtenheader und dann den Nachrichtentext . Um alles in PHP zu verwalten, können Sie, wenn Sie es selbst tun möchten, nur den Ausgabepuffer deaktivieren
Benutzerausgabepuffer:
Wenn Sie die standardmäßige PHP-Ausgabepufferebene verwenden möchten , einfach Die CLI kann nicht verwendet werden, da sie diese Ebene deaktiviert hat
/*launched via php -d output_buffering=32 -d implicit_flush=1 * */ echo str_repeat('a',31); sleep(3); echo 'b'; sleep(3); echo 'c'; ?>
Die Standardausgabepuffergröße ist auf 32 Bytes eingestellt. Wenn das Programm ausgeführt wird, schreibt es zuerst 31 Bytes, schläft dann und schreibt dann 1 Byte. Dieses Byte füllt den Puffer, es aktualisiert sich sofort und übergibt die darin enthaltenen Daten an den Puffer der SAPI-Schicht. Da implicit_flush auf 1 gesetzt ist, wird auch der Puffer der SAPI-Schicht sofort aktualisiert Also aa...b ausgeben, dann schlafen und dann ein Byte ausgeben. Zu diesem Zeitpunkt hat der Puffer 31 leere Bytes, aber das Skript wird ausgeführt, sodass der Puffer, der dieses Byte enthält, sofort aktualisiert und ausgegeben wird auf dem Bildschirm. c
Benutzerausgabepuffer: Erstellt durch ob_start(), können wir mehrere solcher Puffer erstellen (bis der Speicher erschöpft ist) und jeder neue Puffer wird gestapelt Jedes Mal, wenn der vorherige Puffer gefüllt ist oder überläuft, wird ein Aktualisierungsvorgang durchgeführt, und dann werden die Daten an den nächsten Puffer übergeben
function callback($buffer) { // replace all the apples with oranges return ucfirst($buffer); } function callback1($buffer) { // replace all the apples with oranges static $a=0; return $a++.'-'.$buffer."\n"; } ob_start('callback1',10); ob_start("callback",3); echo "fo"; sleep(2); echo 'o'; sleep(2); echo "barbazz"; sleep(2); echo "hello";
Nach dem First-in-last-out-Prinzip des Im Stack wird jede Ausgabe zuerst in Puffer 2 gespeichert. Die Größe von Puffer 2 beträgt 3 Byte. Wenn also die erste Echo-Anweisung die Zeichenfolge „fo“ ausgibt, wird sie zuerst in Puffer 2 gespeichert, wobei ein Zeichen fehlt. Wenn die zweite Echo-Anweisung „o“ ausgibt, ist Puffer 2 voll , also wird es gespült. Die Rückruffunktion von ob_start() wird vor dem Aktualisieren aufgerufen. Diese Funktion wandelt den ersten Buchstaben des Puffers in Großbuchstaben um, sodass die Ausgabe „Foo“ ist, dann wird sie in Puffer 1 gespeichert und die dritte Ausgabe ist „barbazz“. Es wird immer noch zuerst in Puffer 2 abgelegt. Puffer 2 ist übergelaufen, daher wird die durch den Aufruf der Rückruffunktion erhaltene Struktur „Barbazz“ übergeben und dann an Puffer 1 übergeben Die Zeichen von „FooBarbazz“ werden in Puffer 1 gespeichert. Puffer 1 wird aktualisiert. Ebenso wird die Rückruffunktion von ob_start() zuerst aufgerufen. Die Rückruffunktion von Puffer 1 fügt das Modell zur Zeichenfolgensignatur hinzu ist '0-FooBarbazz',
Die letzte Echo-Anweisung gibt eine Zeichenfolge „Hallo“ aus, die länger als drei Zeichen ist, sodass Puffer 2 ausgelöst wird, da das Skript zu diesem Zeitpunkt ausgeführt wird, also ist es so Außerdem wird Puffer 1 sofort geleert und erhält „1-Hello“.
Daher ist die Verwendung der Echo-Funktion eine so einfache Sache, aber sie ist auch kompliziert, wenn es um Puffer und Leistung geht. Achten Sie daher auf die Größe des verwendeten Ausgabeinhalts echo. Wenn sich die Pufferkonfiguration vom Ausgabeinhalt unterscheidet, ist die Leistung besser. Wenn die Pufferkonfiguration kleiner als der Ausgabeinhalt ist, muss der Ausgabeinhalt in der Anwendung segmentiert werden.
Ausgabepuffermechanismus: Hauptsächlich, weil die gesamte Pufferschicht nach 5.4 neu geschrieben wurde. Wenn wir unsere eigene PECL-Erweiterung entwickeln, können wir unsere eigene Ausgabepuffer-Rückrufmethode deklarieren Kann unterschieden werden von anderen PECL-Erweiterungen, um Konflikte zu vermeiden
输出缓冲区的陷阱:
有些PHP的内部函数也使用了输出缓冲区,它们会叠加到其他的缓冲区上,这些函数会填满自己的缓冲区然后刷新,或者返回里面的内容,比如print_r()、higglight_file()和highlight_file::handle()都是此类,所以不应该在输出缓冲区的回调函数中使用这些函数,这样会导致未定义的错误
同样的道理,当PHP执行echo、print时,也不会立即通过tcp输出到浏览器,而时将数据先写入PHP的默认缓冲区,我们可以理解PHP有一套自己的输出缓冲机制,在传送给系统缓存之前建立一个新的队列,数据经过该队列,当一个PHP缓冲区写满以及脚本执行逻辑需要输出时,脚本会把里面的数据传输给SAPI浏览器
echo/print->php输出缓冲区->SAPI缓冲区->TCP缓冲区->浏览器缓冲区->浏览器展示
ob_flush()和flush()区别:
ob_flush():把数据从php的缓冲区中释放出来
flush():把不再缓冲区中的或者说是被释放出来的数据发送到浏览器,严格来讲, 这个只有在PHP做为apache的Module(handler或者filter)安装的时候, 才有实际作用. 它是刷新WebServer(可以认为特指apache)的缓冲区.
在nginx中ob_flush和flush两个都失效:
解决办法: 发现在nginx的配置中,有如下的设置
fastcgi_buffer_size 128k;
fastcgi_buffers 8 128k;
Nginx会缓冲PHP输出的信息,当达到128k时才会将缓冲区的数据发送给客户端,那么我们首先需要将这个缓冲区调小
比如:
fastcgi_buffer_size 4k;
fastcgi_buffers 8 4k;
并且,必须禁用gzip
gzip off;
然后,在php中,在ob_flush
和flush
前,输出一段达到4k的内容,例如:
echo
str_repeat
(‘ ‘, 1024*4);
到此,PHP就可以正常通过ob_flush
和flush
逐行输出需要的内容了。
输出缓冲区实践
1> 通过ob_start()函数手动处理PHP缓冲区机制,这样即便输出内容超过配置参数大小,也不会把数据传输给浏览器,ob_start()将PHP缓冲区空间设置到足够大,只有脚本执行结束后或调用ob_end_flush()函数,才会把数据发送给浏览器
for($i=0;$i<10;$i++){ echo $i.'<br/>'; sleep($i+1); }
执行之后不会每隔几秒就有输出,知道脚本循环结束后,才会一次性输出,这是因为数据量太小,输出缓冲区没有写满
2>当修改output_buffering=0
for($i=0;$i<10;$i++){ echo $i.'<br/>'; flush(); sleep($i+1); }
因为缓冲区的容量设置为0,禁用PHP缓冲区机制,这是我们在浏览器看到断断续续输出,而不必等到脚本执行完毕才看到输出,这是因为数据没有在缓存中停留
3>我们把参数修改为output_buffering=4096,输出数据大于一个缓冲区,不调用ob_start()函数
首先先输出一个4k的内容记下来加本来输出的内容:
for($i=0;$i<10;$i++){ echo echo str_repeat(' ', 1024*4*8).$i<br/>; sleep($i); }
发现可以HTTP连接未关闭,可以看到间断输出,尽管启用了PHP输出缓冲区机制,但是也不是一次性输出,这还是因为PHP缓冲区空间不够,每写满一个缓冲区,数据就会发送到浏览器。
4>参照上例子,这次我们调用ob_start()
ob_start(); //开启PHP缓冲区 for($i=0;$i<10;$i++){ echo echo str_repeat(' ', 1024*4*8).$i<br/>; sleep($i); }
等到服务端脚本全部处理完,响应结束才会看到完整的输出,在输出前浏览器会一直保持空白,这是因为,PHP一旦调用了ob_start()会将PHP缓冲区扩展到足够大,知道ob_end_flush函数调用或者脚本运行结束才发送PHP缓冲区中的数据到客户端浏览器
可以通过tcpdump命令监控TCP的报文,来观察一下使用ob_start()和没有使用它的区别
总结:ob_start激活output_buffering机制,一旦激活,脚本不再直接输出给浏览器,而是先暂时写入PHP缓冲区
PHP默认开启out_buffering机制,通过调用ob_start函数把output_buffering值扩展到足够大,也可以通过$chunk_size来指定output_buffering的值,$chunk_size默认值是0,表示直到脚本运行结束后,PHP缓冲区中的数据才会发送到浏览器,若设置了$chunk_size的大小,则只要缓冲区达到这个值,就会发送给浏览器你
可以通过指定output_callback参数来处理PHP缓冲区的数据,比如ob_gzhandler()将缓冲区中的数据压缩后传送给浏览器,ob_get_contents()是获取一份PHP缓冲区中的数据拷贝
ob_start(); echo date('Y-m-d h:i:s'); $output=ob_get_contents(); ob_end_flush(); echo '<!output>'.$output;
后者是从ob_get_contents取的缓冲区的内容
ob_end_flush()与ob_end_clean(0这两个函数都会关闭输出缓冲,区别是前者只是把PHP缓冲中的数据发生给客户端浏览器,而后者将PHP缓冲区中的数据删掉,但不发送给客户端,前者调用之后数据依然存在,ob_get_contents()依然可以获取PHP缓冲区中的数据拷贝
输出缓冲与静态页面:
大家都知道静态页面的加载速度快,不用请求数据库,以下就是生成静态页面的脚本代码
echo str_pad('',1024);//使缓冲区溢出 ob_start();//打开缓冲区 $content=ob_get_contents();//获取缓冲区内容 $f=fopen('./index.html','w'); fwrite($f,$content);//写入到文件 fclose($f); ob_end_clean()清空并关闭缓冲区
在一些模板引擎和页面文件缓冲中ob_start()函数被使用,如 WP、Drupal、Smarty,例如:
在Wp经常在主题目录header.php看到类似的代码:
只有一个flush(),它的所在的位置就是告诉浏览器那一部分的缓存需要更新,即页面头部以上部分需缓存
以及Wp的部分代码,可以看到,在缓冲区开启时,加入自己的回调方法
内容压缩输出:就是把输出到客户端浏览器的内容进行压缩
好处:降低客户端对服务器出口带宽的占用,提升带宽的利用率。降低Web服务器(如Nginx、Apache、Tomcat等)处理文本时引入的开销,用户端可以减少网络传输延时对用户体验的影响,降低浏览器加载页面内容时占用的内存,有利于改善浏览器稳定性
ob_start('ob_gzhandler');//使用gz格式压缩输出 print'my contents'; ob_end_flush();
之压缩当前脚本与缓冲区,对其他脚本没有影响,PHP还提供另外一种压缩方式,在php.ini修改
zlib_output_compression=On
这样输出时所有页面都以zlib的压缩方式输出,但是两者混用是毫无意义的,只会额外消耗CPU性能,让它压缩已经压缩好的内容,但是基于实践,使用PHP压缩效果并不是十分理想,通常做法是放在Web服务器,比如Apache启用defate、Nginx使用gzip的方式都比PHP端压缩效果好得多
输出缓冲允许第三方库和应用框架(Laravel、TP等)开发者完全控制它们自己输出的内容。比如把他们放在一个全局缓冲区中处理,对于任何输出流的内容(如数据压缩)和任何HTTP消息头、PHP都以正确的书序发送,使用输出缓冲区能够有效地节省带宽,比如图片、字体、CSS、JS等前端内容,特别是限制前端框架也越来越来,让它使用户反应速度更快,从而有效提高系统性能
Das obige ist der detaillierte Inhalt vonDetaillierte Einführung in den PHP-Ausgabepuffer (Codebeispiel). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!