Maison  >  Article  >  développement back-end  >  Introduction détaillée au tampon de sortie php (exemple de code)

Introduction détaillée au tampon de sortie php (exemple de code)

不言
不言avant
2019-03-01 11:59:452667parcourir

Cet article vous apporte une introduction détaillée (exemple de code) sur le tampon de sortie PHP. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer. J'espère qu'il vous sera utile.

PHP Output Buffer :
Buffer : est en fait un espace d'adressage mémoire. Il est utilisé pour stocker des zones de transmission de données entre des appareils avec des vitesses non synchronisées ou des appareils avec des priorités différentes. La mise en mémoire tampon peut réduire le temps d'attente pour l'interaction entre les processus, de sorte que lorsque les données sont lues à partir d'un appareil lent, le processus de fonctionnement de l'appareil rapide ne sera pas interrompu

Le flux de sortie de PHP contient beaucoup de contenu. généralement du texte que les développeurs souhaitent que PHP génère. La plupart de ces textes sont générés à l'aide de la fonction echo ou printf()

1> Toute fonction qui génère du contenu utilisera le tampon de sortie

Il s'agit d'un script PHP normal. Si vous développez une extension PHP, la fonction (fonction C) utilisée peut directement sortir et écrire dans la couche tampon SAPI sans passer par la couche tampon de sortie (nous pouvons l'écrire dans le fichier source PHP). main /php_output.h Découvrez la documentation de l'API pour ces fonctions C)

2> Le tampon de sortie n'est pas la seule couche utilisée pour tamponner la sortie, c'est en fait juste une des nombreuses couches Le comportement du tampon de sortie. la couche est différente de celle utilisée. Concernant SAPI (Web ou CLI), différents SAPI peuvent avoir des comportements différents

Communément appelés « tampons de sortie »

3> tampons de sortie dans SAPI, ce sont des couches en PHP qui tamponnent lorsque les octets de sortie quittent PHP et entrent dans les couches inférieures de l'architecture informatique. Des zones continueront d'apparaître (tampon terminal (tampon terminal), tampon fast-cgi. , tampon du serveur Web, tampon du système d'exploitation, tampon de pile TCP/IP, etc.).

La SAPI de PHP CLI est un peu spéciale. La CLI est également appelée interface de ligne de commande. Elle forcera l'option output_buffer dans la configuration php.ini à 0, ce qui signifie que le tampon de sortie PHP par défaut est désactivé, donc dans la CLI, par défaut vous Le contenu à sortir sera transmis directement à la couche SAPI, sauf si vous appelez manuellement la fonction de classe ob_(), et dans la CLI, la valeur de implicit_flush sera également définie sur 1,

Remarque : Nous confondons souvent le rôle de implicit_flush et PHP. Le code source dit tout : lorsque implicit_flush est défini sur on (valeur 1), dès qu'une sortie est écrite dans le tampon SAPI, elle sera vidée (flush, c'est-à-dire écrivez les données dans la couche inférieure et le tampon sera clair)
C'est-à-dire que lorsque des données sont envoyées à la CLI SAPI, la CLI SAPI transférera immédiatement les données vers sa couche suivante, généralement la sortie standard pipe, write() et fflush(). La fonction est responsable de cela

Tampon de sortie PHP par défaut

 : Si vous utilisez un SAPI différent de la CLI, tel que PHP-FPM, les trois fonctions suivantes liées au tampon seront utilisées Options de configuration de php.ini output_buffering implicit_flush
output_handler
Tout d'abord, vous ne pouvez pas utiliser la fonction ini_set() pour modifier les valeurs​​ de ces options au moment de l'exécution, car ces valeurs seront modifiées au démarrage du programme PHP. Elles sont analysées avant d'exécuter un script, vous pouvez donc utiliser ini_set() pour modifier la valeur au moment de l'exécution, mais cela ne prendra pas effet. . Nous ne pouvons modifier leurs valeurs qu'en éditant le fichier php.ini ou en utilisant l'option -d lors de l'exécution du programme PHP
Par défaut, la distribution PHP définira output_buffering à 4096 mots dans la section php.ini, si sa valeur est réglé sur ON, la taille du tampon de sortie par défaut est de 16 Ko

L'utilisation d'un tampon pour le contenu de sortie dans un environnement d'application Web est bonne pour les performances
 : Le paramètre 4K par défaut est une valeur appropriée, ce qui signifie que vous pouvez d'abord écrire 4096 caractères ASCLL, puis communiquer avec la couche SAPI ci-dessous et transmettre le message octet par octet via le Socket dans un environnement d'application Web. La méthode n'est pas bonne pour les performances, une meilleure méthode consiste à transférer tout le contenu. au serveur en une seule fois, ou au moins les transférer morceau par morceau. Moins il y a d'échanges de données entre couches, meilleures sont les performances, qui doivent toujours être maintenues. Les tampons de sortie sont disponibles et PHP se chargera de transmettre leur contenu. à l'utilisateur final une fois la demande terminée. Le développeur n'a rien à faire

Implicit_flush est désactivé par défaut, de sorte que les nouvelles écritures de données ne rafraîchissent pas SAPI Pour le protocole FastCGI, l'opération d'actualisation consiste à envoyer un paquet de tableau FastCGI après chaque écriture si le paquet de tableau FastCGI est envoyé avant l'envoi. paquet de données Il serait préférable que le tampon soit plein. Si vous devez actualiser manuellement le tampon SAPI, utilisez la fonction flush(). Si vous souhaitez écrire une actualisation, vous pouvez définir implicit_flush ou appeler la fonction ob_implicit_flush()
Configuration recommandée :
output_buffering=4096 implicit_flush = Off/no
Pour modifier la taille du tampon de sortie, assurez-vous que la valeur utilisée est un multiple de 4/8, qui sont respectivement des systèmes d'exploitation 32/64 bits

output_handler est un fonction de rappel, qui peut être Modifier le contenu du tampon avant de rafraîchir le tampon

Le contenu du tampon sera transmis à la fonction de rappel que vous choisissez (une seule peut être utilisé) pour effectuer la conversion de contenu, donc si vous souhaitez obtenir le contenu que PHP donne au serveur Web et à l'utilisateur, vous pouvez utiliser le rappel du tampon de sortie. La sortie fait référence à : en-tête de message, en-tête de message. L'en-tête du message HTTP fait également partie de la couche tampon de sortie


En-tête du message et corps du message  : En fait, toute fonction PHP liée à la sortie de l'en-tête du message (header(), setcookie () , session_start()) utilisent tous la fonction interne sapi_header_op, qui écrira uniquement le contenu dans le tampon d'en-tête du message. Lorsque nous utilisons la fonction printf(), le contenu sera d'abord écrit dans le tampon de sortie (éventuellement plusieurs). Lorsque le contenu de la zone d'anneau de sortie doit être envoyé, PHP enverra d'abord l'en-tête du message, puis enverra le corps du message. . Afin de tout gérer en PHP, si vous souhaitez le faire vous-même, vous pouvez uniquement désactiver le tampon de sortie

Tampon de sortie utilisateur :

Si vous souhaitez utiliser la couche tampon de sortie PHP par défaut , juste La CLI ne peut pas être utilisée car elle a désactivé cette couche

/*launched via php -d output_buffering=32 -d implicit_flush=1 
 * */
echo str_repeat('a',31);
sleep(3);
echo 'b';
sleep(3);
echo 'c';
?>
La taille du tampon de sortie par défaut est définie sur 32 octets. Le programme écrira d'abord 31 octets, puis dormira, puis écrira 1 octet. cet octet remplit le tampon, il se videra immédiatement et transmettra les données à l'intérieur au tampon de la couche SAPI. Parce que implicit_flush est défini sur 1, le tampon de la couche SAPI sera également vidé vers la couche suivante, donc la sortie. aa...b, puis dormir, puis générer un octet. À ce moment, le tampon a 31 octets vides, mais le script est exécuté, donc le tampon contenant cet octet sera également actualisé immédiatement, ce sera donc une sortie d'écran. c

Tampon de sortie utilisateur : créé via ob_start(), nous pouvons créer plusieurs de ces tampons (jusqu'à ce que la mémoire soit épuisée), ces tampons forment une structure de pile, chaque nouveau tampon Il sera empilé sur le tampon précédent. Chaque fois qu'il est rempli ou déborde, une opération de rafraîchissement sera effectuée, puis les données seront transmises au tampon suivant

  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";
selon l'ordre premier entré, dernier sorti de la pile In En principe, toute sortie sera d'abord stockée dans le tampon 2. La taille du tampon 2 est de 3 octets, donc lorsque la première instruction echo génère la chaîne 'fo', elle sera d'abord stockée dans le tampon 2, avec un caractère manquant. Lorsque la deuxième instruction echo génère 'o' , le tampon 2 est plein. , donc ça rougit. La fonction de rappel de ob_start() est appelée avant l'actualisation. Cette fonction convertit la première lettre du tampon en majuscule, donc la sortie est 'Foo', puis elle sera enregistrée dans le tampon 1, et la troisième sortie est 'barbazz' , elle sera toujours placée en premier dans le tampon 2. Cette chaîne a 7 octets. Le tampon 2 a débordé, elle est donc immédiatement actualisée en appelant la fonction de rappel, puis passée au tampon 1. , les dix. les caractères de 'FooBarbazz' sont enregistrés dans le tampon 1. Le tampon 1 sera actualisé De même, la fonction de rappel de ob_start() sera appelée en premier La fonction de rappel du tampon 1 ajoutera le modèle à la signature de chaîne, donc la première ligne. est '0-FooBarbazz',

La dernière instruction echo génère une chaîne 'hello', qui est supérieure à trois caractères, donc le tampon 2 sera déclenché, car le script est exécuté à ce moment, donc il est le tampon 1 sera également vidé immédiatement, obtenant « 1-Hello ».

Par conséquent, utiliser la fonction echo est une chose si simple, mais c'est également compliqué si cela implique des tampons et des performances, alors faites attention à la taille du contenu de sortie en utilisant echo Si la configuration du tampon est différente de la sortie Si le contenu est similaire, les performances seront meilleures Si la configuration du tampon est plus petite que le contenu de la sortie, le contenu de la sortie doit être segmenté dans l'application.

Mécanisme de tampon de sortie : principalement parce que toute la couche tampon a été réécrite après la version 5.4. Lorsque nous développons notre propre extension PECL, nous pouvons déclarer notre propre méthode de rappel du tampon de sortie, donc peut être distinguée. à partir d'autres extensions PECL pour éviter les conflits

输出缓冲区的陷阱

      有些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_flushflush前,输出一段达到4k的内容,例如:

            echo str_repeat(‘ ‘, 1024*4);

到此,PHP就可以正常通过ob_flushflush逐行输出需要的内容了。

输出缓冲区实践

    1> 通过ob_start()函数手动处理PHP缓冲区机制,这样即便输出内容超过配置参数大小,也不会把数据传输给浏览器,ob_start()将PHP缓冲区空间设置到足够大,只有脚本执行结束后或调用ob_end_flush()函数,才会把数据发送给浏览器 

for($i=0;$i<10;$i++){
echo $i.&#39;<br/>&#39;;
sleep($i+1);
}

 执行之后不会每隔几秒就有输出,知道脚本循环结束后,才会一次性输出,这是因为数据量太小,输出缓冲区没有写满

2>当修改output_buffering=0

for($i=0;$i<10;$i++){
echo $i.&#39;<br/>&#39;;
flush();
sleep($i+1);
}

 因为缓冲区的容量设置为0,禁用PHP缓冲区机制,这是我们在浏览器看到断断续续输出,而不必等到脚本执行完毕才看到输出,这是因为数据没有在缓存中停留

3>我们把参数修改为output_buffering=4096,输出数据大于一个缓冲区,不调用ob_start()函数

    首先先输出一个4k的内容记下来加本来输出的内容:   

for($i=0;$i<10;$i++){
echo   echo str_repeat(&#39; &#39;, 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(&#39; &#39;, 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(&#39;Y-m-d h:i:s&#39;);

$output=ob_get_contents();
ob_end_flush();
echo &#39;<!output>&#39;.$output;

 后者是从ob_get_contents取的缓冲区的内容

 ob_end_flush()与ob_end_clean(0这两个函数都会关闭输出缓冲,区别是前者只是把PHP缓冲中的数据发生给客户端浏览器,而后者将PHP缓冲区中的数据删掉,但不发送给客户端,前者调用之后数据依然存在,ob_get_contents()依然可以获取PHP缓冲区中的数据拷贝

输出缓冲与静态页面

     大家都知道静态页面的加载速度快,不用请求数据库,以下就是生成静态页面的脚本代码

echo str_pad(&#39;&#39;,1024);//使缓冲区溢出
ob_start();//打开缓冲区
$content=ob_get_contents();//获取缓冲区内容
$f=fopen(&#39;./index.html&#39;,&#39;w&#39;);
fwrite($f,$content);//写入到文件
fclose($f);
ob_end_clean()清空并关闭缓冲区

  在一些模板引擎和页面文件缓冲中ob_start()函数被使用,如 WP、Drupal、Smarty,例如:

在Wp经常在主题目录header.php看到类似的代码:

只有一个flush(),它的所在的位置就是告诉浏览器那一部分的缓存需要更新,即页面头部以上部分需缓存

以及Wp的部分代码,可以看到,在缓冲区开启时,加入自己的回调方法
 内容压缩输出:就是把输出到客户端浏览器的内容进行压缩

好处:降低客户端对服务器出口带宽的占用,提升带宽的利用率。降低Web服务器(如Nginx、Apache、Tomcat等)处理文本时引入的开销,用户端可以减少网络传输延时对用户体验的影响,降低浏览器加载页面内容时占用的内存,有利于改善浏览器稳定性 

ob_start(&#39;ob_gzhandler&#39;);//使用gz格式压缩输出
print&#39;my contents&#39;;
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等前端内容,特别是限制前端框架也越来越来,让它使用户反应速度更快,从而有效提高系统性能

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer