62 votes

Comment rediriger STDOUT vers un fichier en PHP?

Le code ci-dessous fonctionne presque, mais ce n'est pas vraiment ce que je voulais :

ob_start();
echo 'xxx';
$contents = ob_get_contents();
ob_end_clean();
file_put_contents($file,$contents);

Y a-t-il un moyen plus naturel ?

2 votes

Vous devriez vraiment accepter une réponse ici. Bas Peters vous a donné la solution parfaite.

1 votes

Le code d'exemple redirige la SORTIE vers un fichier, pas vers STDOUT, la solution de Bas semble fonctionner uniquement parce que les environnements CLI (et dans une moindre mesure CGI) utilisent ces flux de manière interchangeable. Le module PHP Apache ne le fait pas.

132voto

Bas Peters Points 779

Il est possible d'écrire directement STDOUT dans un fichier en PHP, ce qui est beaucoup plus facile et plus direct que d'utiliser un tampon de sortie.

Faites ceci au tout début de votre script:

fclose(STDIN);
fclose(STDOUT);
fclose(STDERR);
$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('application.log', 'wb');
$STDERR = fopen('error.log', 'wb');

Pourquoi au tout début me direz-vous? Aucun descripteur de fichier ne devrait être ouvert à ce moment, car lorsque vous fermez les descripteurs de fichier d'entrée, de sortie et d'erreur standard, les trois premiers nouveaux descripteurs deviendront les NOUVEAUX descripteurs de fichier d'entrée, de sortie et d'erreur.

Dans mon exemple, j'ai redirigé l'entrée standard vers /dev/null et les descripteurs de sortie et d'erreur vers des fichiers journaux. C'est une pratique courante lors de la création d'un script daemon en PHP.

Pour écrire dans le fichier application.log, cela suffirait:

echo "Bonjour le monde\n";

Pour écrire dans le fichier error.log, il faudrait faire:

fwrite($STDERR, "Quelque chose s'est mal passé\n"); 

Veuillez noter que lorsque vous changez les descripteurs d'entrée, de sortie et d'erreur, les constantes PHP intégrées STDIN, STDOUT et STDERR deviendront inutilisables. PHP ne mettra pas à jour ces constantes avec les nouveaux descripteurs et il n'est pas autorisé à redéfinir ces constantes (elles sont appelées constantes pour une raison après tout).

0 votes

Savez-vous où cette fonctionnalité est documentée ?

2 votes

Cette fonctionnalité est documentée sur php.net

8 votes

C'est le comportement de base Unix (c'est aussi pourquoi il n'est pas portable sur Windows). Unix attribue le plus petit descripteur de fichier disponible à un fichier. Étant donné que les fcloses ferment les descripteurs 0, 1 et 2, ils deviennent disponibles pour les 3 ouvertures suivantes. De plus, Unix est extraordinaire par sa simplicité.

22voto

user340140 Points 204

Voici une façon de détourner la SORTIE qui semble être le problème d'origine

$ob_file = fopen('test.txt','w');

function ob_file_callback($buffer)
{
  global $ob_file;
  fwrite($ob_file,$buffer);
}

ob_start('ob_file_callback');

plus d'infos ici :

http://my.opera.com/zomg/blog/2007/10/03/how-to-easily-redirect-php-output-to-a-file

8 votes

Si vous utilisez PHP 5.3 ou une version plus récente, je vous suggère d'utiliser une fonction de fermeture sans avoir besoin que $ob_file soit global : $ob_file_callback = function($buffer) use ($ob_file) { fwrite($ob_file, $buffer); }; ob_start($ob_file_callback);

0 votes

Cela fonctionne pour moi et devrait être la réponse acceptée à mon avis

0 votes

@user340140 Si vous savez comment rediriger stdin, veuillez partager.

10voto

Aucune des réponses n'a fonctionné pour mon cas particulier où j'avais besoin d'une façon multiplateforme de rediriger la sortie dès qu'elle était affichée afin de suivre les journaux avec tail -f log.txt ou une autre application de visualisation de journaux. J'ai trouvé la solution suivante:

$logFp = fopen('log.txt', 'w');

ob_start(function($buffer) use($logFp){
    fwrite($logFp, $buffer);
}, 1); //remarquez l'utilisation de chunk_size == 1

echo "première sortie\n";
sleep(10)
echo "deuxième sortie\n";

ob_end_clean();

Je n'ai remarqué aucun problème de performance, mais si vous en voyez, vous pouvez modifier chunk_size pour de plus grandes valeurs.

Maintenant, suivez simplement le fichier journal avec tail -f:

tail -f log.txt

0 votes

"ob_start(function($buffer) use($logFp){" Woa, pourriez-vous expliquer cette magie ? Depuis quand peut-on passer des fonctions en tant qu'arguments ? Qu'est-ce que ce 'use' ? Depuis quand est-ce une syntaxe PHP valide ?

8voto

chaos Points 69029

Non, l'empilement de sortie est aussi bon que possible. Bien que ce soit légèrement plus agréable de simplement faire

ob_start();
echo 'xxx';
$contents = ob_get_flush();
file_put_contents($file,$contents);

4 votes

Ne ob_get_flush() envoie-t-il pas également le contenu du tampon au navigateur ? Peut-être ob_get_clean() serait-il mieux.

2 votes

Y a-t-il un moyen d'obtenir également STDERR?

7voto

Utiliser le module eio pecl eio est très facile, vous pouvez également capturer des erreurs internes de PHP, var_dump, echo, etc. Dans ce code, vous trouverez quelques exemples de différentes situations.

$fdout = fopen('/tmp/stdout.log', 'wb');
$fderr = fopen('/tmp/stderr.log', 'wb');

eio_dup2($fdout, STDOUT);
eio_dup2($fderr, STDERR);
eio_event_loop();

fclose($fdout);
fclose($fderr);

// exemples de sortie
echo "message à stdout\n";

$v2dump = array(10, "graphinux");
var_dump($v2dump);

// erreur/avertissement interne PHP
$div0 = 10/0;

// messages d'erreurs utilisateur
fwrite(STDERR, "erreur contrôlée par l'utilisateur\n");

L'appel à eio_event_loop est utilisé pour s'assurer que les requêtes eio précédentes ont été traitées. Si vous avez besoin d'ajouter des journaux, lors de l'appel à fopen, utilisez le mode 'ab' au lieu de 'wb'.

L'installation du module eio est très facile (http://php.net/manual/es/eio.installation.php). J'ai testé cet exemple avec la version 1.2.6 du module eio.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X