Manipuler des fichiers en PHP

J’ai besoin de lire le contenu de fichiers créés par une application tierce. Il faut :

  • lister les fichiers avec extension ‘log’ dans un répertoire ;
  • pour chaque fichier, lire chaque ligne ;
  • pour chaque ligne, transformer son contenu texte en un array de données ;
  • selon la valeur de certains éléments de la ligne, réaliser une action spécifique ;
  • une fois le fichier traité, en modifier l’extension (devient ‘fait’).

Pour cela, j’utilise un script PHP installé dans le répertoire /var/www/html/tests  d’un Raspberry Pi sur lequel j’ai installé apache et PHP (cf article Un serveur Web sur mon Raspberry Pi).

De quoi je pars ?

/var/www/html/tests  contient le fichier test-explode.php  et un sous répertoire data .

/var/www/html/tests/data  contient 3 fichiers dont le nom est sous la forme domoAL_20150910.log . Chacun de ces fichiers contient 1000 à 2000 lignes de type :

1441868688	!	Thu, 10 Sep 2015 07:04:48 GMT ! dev_0x05 !	2756	2	1462	1831	1931	0
1441868744	!	Thu, 10 Sep 2015 07:05:44 GMT ! dev_0x05 !	2761	2	1462	1825	1918	0
1441868799	!	Thu, 10 Sep 2015 07:06:39 GMT ! dev_0x05 !	2756	2	1462	1825	1950	0
1441868854	!	Thu, 10 Sep 2015 07:07:34 GMT ! dev_0x05 !	2760	2	1468	1831	1993	868
1441868910	!	Thu, 10 Sep 2015 07:08:30 GMT ! dev_0x05 !	2754	2	1468	1843	2037	0
1441868965	!	Thu, 10 Sep 2015 07:09:25 GMT ! dev_0x05 !	2762	2	1468	1856	2075	0

Lister les fichiers d’un répertoire

Objectif : lister les fichiers avec extension ‘log’ dans un répertoire.

Ma solution est fortement inspirée de cette page de php.net.

<section class="data">
<H3>Test list each file in a directory</h3>	

<?php
// http://php.net/manual/fr/function.scandir.php
echo "<br /><hr />" ;
$directory = '../tests/data';
$file_read = array( 'log' );

$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );

foreach ( $scanned_directory as $key => $value ) {

	$type = explode( '.', $value ); 
	$type = array_reverse( $type );
	if( !in_array( $type[0], $file_read ) ) {		// continue if not ".log"
		continue;
	}

	$lines = 0;
	$handle = fopen( $directory . DIRECTORY_SEPARATOR . $value, 'r' );

	while ( !feof( $handle ) ) {

		if ( is_bool( $handle ) ) {
			break;
		}

		$line = fgets( $handle );
		$lines++;
	}

	fclose( $handle );

	$result['lines_html'][] = array ( $directory . '/' . $value ,  $lines ) ; 
	$result['lines_count'] = $result['lines_count'] + $lines;
	$result['files_count'] = $result['files_count'] + 1;
	
}
var_dump( $result ) ;
echo "<br /><hr />" ;

?>

</section>

ce script affiche un array sur ma page (que j’ai mis en forme à la main) :

array(3) { 
	["lines_html"]=> array(3) {
		[0]=> array(2) {
			[0]=> string(33) "../tests/data/domoAL_20150910.log" 
			[1]=> int(1561) 
		} 
		[1]=> array(2) { 
			[0]=> string(33) "../tests/data/domoAL_20150913.log" 
			[1]=> int(1560) 
		} 
		[2]=> array(2) { 
			[0]=> string(33) "../tests/data/domoAL_20150914.log" 
			[1]=> int(1560) 
		} 
	} 
	["lines_count"]=> int(4681) 
	["files_count"]=> int(3) 
}

Le script trouve donc bien les 3 fichiers et le nombre de ligne de chaque fichier.

Lire les lignes d’un fichier

Objectif : pour chaque fichier, lire chaque ligne.

Créer un array avec la liste des fichiers

Je crée une fonction qui génère un array avec le nom de chaque fichier du répertoire.

<?php
// http://php.net/manual/fr/function.scandir.php

// list all log files in the ../tests/data directory in an array
$list_files = al_list_files_in_dir( '../tests/data', array( 'log' ) ) ;
echo "<br />liste des fichiers<hr />" ;
var_dump( $list_files ) ;
echo "<br /><hr />" ;

function al_list_files_in_dir( $directory, $file_read ) {

	$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );
	
	foreach ( $scanned_directory as $key => $value ) {
	
		$type = explode( '.', $value ); 
		$type = array_reverse( $type );
		if( !in_array( $type[0], $file_read ) ) {		// continue if not ".log"
			continue;
		}

		$result[] = $directory . '/' . $value ; 
	}
	
	return $result ;
	
}

?>

J’obtiens un array comme celui ci (mis en forme à la main) :

array(3) {
	[0]=> string(33) "../tests/data/domoAL_20150910.log" 
	[1]=> string(33) "../tests/data/domoAL_20150913.log" 
	[2]=> string(33) "../tests/data/domoAL_20150914.log" 
}

Dans chaque fichier, lire les lignes une à une

<H4>read each line of each log file</h4>	

<?php
foreach ( $list_files as $key => $value ) {
	
	$file_content = al_read_lines_in_file( $value ) ;
	echo "<br />contenu des fichiers<hr />" ;
	var_dump( $file_content ) ;
	echo "<br /><hr />" ;		
}

function al_read_lines_in_file( $file ) {
	
	$handle = fopen( $file, "r" );

	$count = 0 ;
	if ($handle) {
		
		while (($line = fgets($handle)) !== false) {
			$content[] = $line ;
		}
	
		fclose($handle);
		return $content ;
		
	} else {
		echo "can't open file \n" ;
	} 
	
}


?>

Affiche 3 gigantesques arrays de 1500 lignes chacun environ.

Mettre le contenu d’une ligne dans un array

Objectif : pour chaque ligne, transformer son contenu texte en un array de données.

Chaque ligne contient des données séparées soit par « ! », soit par une tabulation. Je veux la transformer en une série de données.

<H3>Store the content of each line in an array</h3>	
<?php

foreach ( $list_files as $key => $value ) {

	$file_content = al_read_lines_in_file( $value ) ;
	$line_count = 0 ;

	foreach ( $file_content as $contents => $content ) {
		
		$file_data[ $line_count ] = al_store_lines_in_array( $content ) ;
		$line_count++ ;
	}

	echo "<br />données" . $value . "<hr />" ;
	var_dump( $file_data ) ;
	echo "<br /><hr />" ;	
}



function al_store_lines_in_array( $text ) {

	$val = explode("!", $text);
	
	// split on tabulation
	$temp = preg_split( "/[\t]/", trim( $val[3] ) );

	// strip whitespace from the beginning and end of string.	
	$val[0] = trim( $val[0] );
	$val[1] = trim( $val[1] );
	$val[2] = trim( $val[2] );	
	$val[3] = trim( $val[3] );
	$val[4] = trim( $temp[0] );
	$val[5] = trim( $temp[1] );
	$val[6] = trim( $temp[2] );
	$val[7] = trim( $temp[3] );
	$val[8] = trim( $temp[4] );
	$val[9] = trim( $temp[5] );	

	return $val ;
}

?>

Pour le fichier ../tests/data/domoAL_20150910.log , ça me donne un array de 1560 arrays sous forme :

array(1560) { 
	[0]=> array(10) { 
		[0]=> string(10) "1441843202" 
		[1]=> string(29) "Thu, 10 Sep 2015 00:00:02 GMT" 
		[2]=> string(8) "dev_0x05" 
		[3]=> string(23) "2755	2	1531	1906	2168	0" 
		[4]=> string(4) "2755" 
		[5]=> string(1) "2" 
		[6]=> string(4) "1531" 
		[7]=> string(4) "1906" 
		[8]=> string(4) "2168" 
		[9]=> string(1) "0" 
	} 
	...
}

tout est sous la forme de texte (string). Il va falloir maintenant que je transforme la date « 1441843202 » et la dernière valeur (0 pour la ligne affiché ci-dessus) en données sur lesquelles je pourrai valider ou non une condition.

Mettre des conditions sur le contenu de l’array

Objectif : selon la valeur de certains éléments de la ligne, réaliser une action spécifique. Si le dernier élément de l’array n’est pas nul, l’afficher avec sa date. Si le temps écoulé depuis le dernier élément affiché est supérieur à 15 minutes, afficher la ligne même si sa dernière valeur est nulle.

conditions sur deux données

Je crée un array contenant des données comme celles qui doivent être produites par la lecture d’un des fichiers log. Je transforme les deux données sur lesquelles je veux pouvoir faire une condition en entiers avec la fonction intval() .

Pour le timestamp, je veux qu’il puisse être affiché de manière lisible : $date_humain = date ( « d/m/Y H:i:s » , $date_stamp ) ;  va transformer 1441843313 en 10/09/2015 02:01:53 .

Le script suivant lit le contenu de l’array de données puis :

  1. si la dernière valeur ($value[9] ) est nulle, met $save à faux, sauf si ça fait plus de 5 minutes que $save est faux.
  2. si $save est true, imprime les données

Vu l’array que j’ai construit pour cet essai, je devrais avoir 4 lignes imprimées et une non (celle qui correspond à la deuxième valeur de l’array, qui a un timestamp très proche du premier).

<H3>Test conditions sur éléments de l'array de données</h3>	

<?php

$data = array (
	0 => array(
		0 => "1441843202" ,
		1 => "Thu, 10 Sep 2015 00:00:02 GMT" ,
		2 => "dev_0x05" ,
		3 => "2755	2	1531	1906	2168	0" ,
		4 => "2755" ,
		5 => "2" ,
		6 => "1531" ,
		7 => "1906" ,
		8 => "2168" ,
		9 => "0" 	
	),
	1 => array(
		0 => "1441843258" ,
		1 => "Thu, 10 Sep 2015 00:00:58 GMT" ,
		2 => "dev_0x05" ,
		3 => "2761	2	1537	1906	2137	0" ,
		4 => "2761" ,
		5 => "2" ,
		6 => "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "0"
	),
	2 => array(
		0 => "1441843313" ,
		1 => "Thu, 10 Sep 2015 00:01:53 GMT" ,
		2 => "dev_0x05" ,
		3 => "2761	2	1537	1906	2137	0" ,
		4 =>  "2761" ,
		5 => "2" ,
		6 => "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "216"		
	),
	3 => array(
		0 => "1441849352" ,
		1 =>  "Thu, 10 Sep 2015 01:42:32 GMT" ,
		2 =>  "dev_0x05" ,
		3 =>  "2761	2	1537	1906	2137	0" ,
		4 =>  "2761" ,
		5 =>  "2" ,
		6 =>  "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "0"		
	),
	4 => array(
		0 => "1441851457" ,
		1 => "Thu, 10 Sep 2015 02:17:37 GMT" ,
		2 => "dev_0x05" ,
		3 => "2761	2	1537	1906	2137	0" ,
		4 => "2761" ,
		5 => "2" ,
		6 => "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "51"		
	)
) ;

// var_dump( $data ) ;
// echo "<hr /><br />" ;

echo "<hr />" ;	

$ref_time = 0000000 ;		

foreach ( $data as $key => $value ) {

	$date_stamp = intval( $value[0] ) ;
	$date_humain = date ( "d/m/Y H:i:s" , $date_stamp ) ;
	
	$time_lapsed = $date_stamp - $ref_time ; // temps écoulé depuis dernière ligne à imprimer
	
	$chaudiere = intval( $value[9] ) ;
	
	if ( $chaudiere == 0 ) {
		
		$save = false ; 
		
		if ( $time_lapsed > 300 ) {	// plus de 5 minutes
			
			$save = true ;
			$ref_time = $date_stamp ;
		}
		
		echo "<hr />" ;
		
	} else {
		
		$save = true ;
		
	}
	
	echo $key . " | \t" . $time_lapsed . " | \t" ; 

	if ( $save == true ) {
		
		echo $chaudiere . " | \t" . $save  . " | \t" . $date_stamp  . " | \t" . $date_humain ;
		
	}	else {
		
		echo "do not print" ;
		
	}
	
	echo "<hr />" ;
	
}


echo "<br />" ;	

?>

et le résultat est conforme à mes attentes :

tests en php de manipulation de tableau

Modifier l’extension d’un fichier

Objectif : une fois le fichier traité, en modifier l’extension (devient ‘fait’).

ici j’ai fait beaucoup usage de /var/log/apache2/error.log  !!! J’ai ainsi compris que j’obtenais une erreur « Permission denied » lorsque je voulais renommer.

Je n’ai jamais réussi à réaliser l’opération, même en la remplaçant par une copie puis une suppression du fichier initial… Puis, j’ai eu l’idée que c’est normal ! Heureusement que l’on ne peut pas supprimer ou renommer des fichiers au travers d’un navigateur internet…

j’ai donc créé un fichier test-rename.php (droits 740) dans le répertoire /home/jf/exec du Raspberry Pi. Dans le répertoire /home/jf/snd/data, j’ai placé les 3 fichiers à renommer. Et ça fonctionne !

#!/usr/bin/php

<?php
$directory = '/home/jf/snd/data';
$file_read = array( 'log' );

$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );


foreach ( $scanned_directory as $key => $value ) {

	$type = explode( '.', $value ); 
	// var_dump( $type ) ;
	
	$type = array_reverse( $type );
	if( !in_array( $type[0], $file_read ) ) {		// continue if not ".log"
		continue;
	}

	$old_name = $directory . DIRECTORY_SEPARATOR . $value ;
	$new_name = $directory . DIRECTORY_SEPARATOR . $type[1] . ".fait" ;
	
	$return_val = rename( $old_name, $new_name );
			
	if ( $return_val == 1 ) { 
	   echo "success : " . $old_name . " renamed " . $new_name . "\r\n"; 
	} else { 
	   echo "failed to rename " . $old_name . " to : " . $new_name . "\r\n" ; 
	} 		

}

?>

Ensuite dans la console de commande du Raspberry Pi :

cd /home/jf/exec
./test-rename.php

Et ça y est, la console affiche

success : /home/jf/snd/data/domoAL_20150910.log renamed /home/jf/snd/data/domoAL_20150910.fait
success : /home/jf/snd/data/domoAL_20150913.log renamed /home/jf/snd/data/domoAL_20150913.fait
success : /home/jf/snd/data/domoAL_20150914.log renamed /home/jf/snd/data/domoAL_20150914.fait

Et si je vérifie, les 3 fichiers ont bien été renommés.

Et maintenant ?

J’ai fait fonctionner les prototypes de code dont je vais avoir besoin. Je vais les utiliser pour faire échanger des données entre un Raspberry Pi (qui contient les fichiers log) et une box domotique qui va archiver et afficher les données.

Poster un Commentaire

avatar
  S’abonner  
Notifier de