J’ai été récemment ammené à parser un nombre important de fichiers de log compressés avec Gzip.

Je voulais traiter chaque fichier à la volée, ne pas avoir à écrire chaque fichier décompressé. Pour ce dernier point, rien de plus simple, il suffit d’utiliser les options “-cd” de gzip qui permettent de décompresser et de rediriger vers la sortie standard.

Voici ce à quoi je voulais arriver :
user@hostname$ gzip -cd mon_fichier_de_log.gz | ./parser.php

Concernant le parser PHP, j’ai utilisé les streams qui permettent notament de récupérer ce qui est écrit sur la sortie standard (comme ça tombre bien, n’est ce pas :) ).

La version simple

1. Le traitement du flux



<?php 
$input 
fopen('php://stdin''r'); // ouverture du flux
while (!feof($input)) {
    
$line fgets($input); // lecture ligne par ligne
    
parse($line); // ma fonction pour parser 
}
fclose($input); // fermeture du flux

2. Rendre le script php exécutable

Définir le bang dans le script PHP :

#!/usr/bin/php
<?php


Fixer l’attribut exécutable :
user@hostname$ chmod +x parser.php

3. L’exécution

user@hostname$ gzip -cd mon_fichier_de_log.gz | ./parser.php

Une version plus complète

CI-dessous une version plus structurée, facilement extensible dans laquelle il est possible d’appliquer plusieurs traitement.
<?php

interface ICommandBuilder {
    public function 
addCommand(ICommand $command);
}

interface ICommand {
    public function 
run($str);
    public function 
finalize();
}

/**
 * Read input on stdin and call a list of ICommand on each readed line.
 */
class Phpipe implements ICommandBuilder {
    
    
/**
     * Worker list, all workers are called for each readed line.
     * @var array of ICommand object
     */
    
protected $worker = array();
    
    public function 
__contruct() {}
    
    
/**
     * Append a new worker
     * @param ICommand $command
     * @return self
     */
    
public function addCommand(ICommand $command) {
        
array_push($this->worker$command);
        return 
$this;
    }
    
    
/**
     * Read stdin until feof
     */
    
public function read() {
        
        
$input fopen('php://stdin''r');
        
$exclude = array();
        
        while (!
feof($input)) {
            
$line fgets($input);
            foreach(
$this->worker as $worker) {
                
$worker->run($line);
            }
        }
        
        
fclose($input);
        
$this->finalize();
    }
    
    
/**
     * Call all ICommand::finalize
     */
    
protected function finalize() {
        foreach(
$this->worker as $worker) {
            
$worker->finalize();
        }
    }
    
}

/**
 * ICommand Null pattern
 */
class LineNull implements ICommand {
    public function 
run($str) {
        return;
    }
    public function 
finalize() {
        return;
    }

/**
 * ICommand Set line number and echo line.
 */
class LineEcho implements ICommand {
    
    public function 
run($str) {
        !isset(
$this->i) ?  $this->null;
        echo ++
$this->i': '$str;
    }
    
    public function 
finalize() {
        echo 
"\n";
    }
    

/**
 * ICommand Echo each line reversed.
 */
class LineReverse implements ICommand {
    
    public function 
run($str) {
        echo 
strrev(trim($str))."\n";
    }
    
    public function 
finalize() {}
}

/**
 * ICommand Append each line in a buffer. Buffer is echoed on finalieze() call. 
 */
class LineBuffer implements ICommand {
    
    public 
$buffer '';
    
    public function 
run($str) {
        
$this->buffer .= $str;
    }
    
    public function 
finalize() {
        echo 
$this->buffer;
    }
}

$worker = new LineEcho;

$phpipe = new Phpipe;
$phpipe->addCommand(new LineNull)
    ->
addCommand($worker)
    ->
addCommand(new LineReverse)
    ->
addCommand(new LineBuffer);
$phpipe->read();

echo "terminated\n";

Pour aller plus loin

  • Permettre la sélection du flux en entrée
  • Utiliser les stream de filtrage
, , ,
Trackback

no comment untill now

Add your comment now