begin process at 2012 05 27 22:25:22
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Fichier / Disque

 > COMPTEUR DE VISITES SUR FICHIER

COMPTEUR DE VISITES SUR FICHIER


 Information sur la source

Note :
9 / 10 - par 1 personne
9,00 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Fichier / Disque Classé sous :ip, compteur visite, sprintf, fichier, session Niveau :Débutant Date de création :11/09/2011 Date de mise à jour :12/09/2011 19:55:31 Vu :2 693

Auteur : pierreSabatier

Ecrire un message privé
Commentaire sur cette source (19)
Ajouter un commentaire et/ou une note

 Description

Bonjour tout le monde,

Je poste un petit compteur de visites. Il s'agit de trois fonctions.
- add_visitor lit et réécrit sur l'enregistrement précédent et le cas échéant, à la fin du fichier.
- get_server_ip récupère l'ip (sans la vérifier)
- visite ne compte qu'une seule visite par session par utilisateur.

Grâce aux fonctions de formatage de texte, sprintf, fscanf, on arrive à de bons résultats, très rapidement.

Source

  • <?php
  • define('EXIT_SUCCESS', 0);
  • define('EXIT_FAILURE', 1);
  • function get_server_ip(){
  • if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  • $ipString = $_SERVER['HTTP_X_FORWARDED_FOR'];
  • $addr = explode(',',$ipString);
  • $ip = trim($addr[sizeof($addr)-1]);
  • }
  • elseif(isset($_SERVER['HTTP_CLIENT_IP'])){
  • $ip = $_SERVER['HTTP_CLIENT_IP'];
  • }
  • else{
  • $ip = $_SERVER['REMOTE_ADDR'];
  • }
  • return $ip;
  • }
  • function add_visitor($visitor, $date, $file = 'compteur.txt'){
  • if (FALSE === ($pt = fopen($file,'r+'))) {
  • return EXIT_FAILURE;
  • }
  • if (!flock($pt, LOCK_EX)) {
  • return EXIT_FAILURE;
  • }
  • $n = 1;
  • while(3 === fscanf($pt, "%s\t%d\t%d\n", $ip,$nb_visite,$last_date)) {
  • if ($ip === $visitor) {
  • $str_len = strlen($visitor) + 15; // 3 (tab et chariot) + 4 (nombre) + 8 (date)
  • $n += $nb_visite;
  • // rembobiner au début de l'enregistrement concerné
  • fseek($pt, - $str_len, SEEK_CUR);
  • break;
  • }
  • }
  • // écrire soit à la fin du fichier, soit sur l'enregistrement précédent
  • $entry = sprintf("%s\t%04d\t%d\n", $visitor, $n, $date);
  • fwrite($pt, $entry);
  • flock($pt, LOCK_UN);
  • fclose($pt);
  • return EXIT_SUCCESS;
  • }
  • function visite(){
  • session_start();
  • if (!array_key_exists('welcome',$_SESSION)) {
  • $_SESSION['welcome'] = TRUE;
  • $ip = get_server_ip();
  • // vérifier si $ip est une IP (V4 ou V6)
  • if (FALSE === filter_var($ip, FILTER_VALIDATE_IP)) {
  • return EXIT_FAILURE;
  • }
  • add_visitor($ip,date('Ymd'));
  • }
  • else {
  • $_SESSION['welcome'] = FALSE;
  • }
  • return EXIT_SUCCESS;
  • }
<?php
define('EXIT_SUCCESS', 0);
define('EXIT_FAILURE', 1);

function get_server_ip(){
	if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) { 
		$ipString = $_SERVER['HTTP_X_FORWARDED_FOR']; 
		$addr = explode(',',$ipString); 
		$ip = trim($addr[sizeof($addr)-1]);
	} 
	elseif(isset($_SERVER['HTTP_CLIENT_IP'])){ 
		$ip = $_SERVER['HTTP_CLIENT_IP'];
	}
	else{ 
		$ip = $_SERVER['REMOTE_ADDR'];
	} 
	return $ip;
}
function add_visitor($visitor, $date, $file = 'compteur.txt'){
	if (FALSE === ($pt = fopen($file,'r+'))) {
		return EXIT_FAILURE;
	}
	if (!flock($pt, LOCK_EX)) {
		return EXIT_FAILURE;
	}
	$n = 1;
	while(3 === fscanf($pt, "%s\t%d\t%d\n", $ip,$nb_visite,$last_date)) {
		if ($ip === $visitor) {
			$str_len = strlen($visitor) + 15; // 3 (tab et chariot) + 4 (nombre) + 8 (date)
			$n += $nb_visite;
			// rembobiner au début de l'enregistrement concerné
			fseek($pt, - $str_len, SEEK_CUR);
			break;
		}
	}
	// écrire soit à la fin du fichier, soit sur l'enregistrement précédent
	$entry = sprintf("%s\t%04d\t%d\n", $visitor, $n, $date);
	fwrite($pt, $entry);
	flock($pt, LOCK_UN);
	fclose($pt);
	return EXIT_SUCCESS;
}

function visite(){
	session_start();
	if (!array_key_exists('welcome',$_SESSION)) {
		$_SESSION['welcome'] = TRUE;
		$ip = get_server_ip();
		// vérifier si $ip est une IP (V4 ou V6)
		if (FALSE === filter_var($ip, FILTER_VALIDATE_IP)) {
			return EXIT_FAILURE;
		}
		add_visitor($ip,date('Ymd'));
	}
	else {
		$_SESSION['welcome'] = FALSE;
	}
	return EXIT_SUCCESS;
}

 Conclusion

Comme je l'ai lu, ça peut convenir à des sites à faible trafic. Encore que, vu ce que fais la fonction visite() j'aimerais bien qu'on m'explique... S'il faut ouvrir une connexion à des SGBD, envoyer un INSERT OR UPDATE, ça fait bien ramer un gros site, non ?

N'étant pas un grand expert en sécurité, je vous propose de venir critiquer mon nouveau script.


 Historique

11 septembre 2011 15:26:52 :
La variable $str_len n'était pas vraiment affectée au bon endroit.
12 septembre 2011 19:55:31 :
Les modifs : - verrou sur le fichier - contrôle de l'ip dans la fonction visite()

 Sources du même auteur

[CRON] INSERT ON DUPLICATE KEY UPDATE
Source avec Zip TESTS UNITAIRES

 Sources de la même categorie

SUPPRESSION AUTOMATIQUE DES FICHIERS RAW ORPHELINS APRÈS UN ... par malokaff
UPLOADER DES FICHIER DANSUN DOSSIER, SÉCURISÉ AVEC MOT DE PA... par raizzo
FONCTION RECURSIVE DE DOSSIERS par tefa24600
Source avec Zip CONVERSION DES UNITÉS INFORMATIQUE (O, KIO, MIO, GIO, TIO...... par genetApt151
TROUVER LES DOUBLONS DANS UNE LISTE DE FICHIERS par soobook

 Sources en rapport avec celle ci

CLASSE DE GESTION DE "VARIABLES GLOBALES D'ENVIRONNEMENT" par pifou25
UPLOADER DES FICHIER DANSUN DOSSIER, SÉCURISÉ AVEC MOT DE PA... par raizzo
Source avec Zip Source avec une capture CREATION DE COMPTE AVEC CRYPTAGE ET ESPACE DE CONNEXION SEC... par bm1982
Source avec Zip COMPTEURS SIMPLE/IP/COOKIES par edotheking
Source avec Zip CLASSE "REMPLAÇANT" LES SESSIONS PHP (AVEC VÉRIFICATION DE L... par thomvaill

Commentaires et avis

Commentaire de pierreSabatier le 11/09/2011 18:33:23

Bonjour tout le monde,

le code à proprement parler, s'arrête ligne 44. La suite est une batterie de 4 tests.

On l'utilise comme ça, en début de script :
<?php
visite();
?>

Vous avez peut-être compris que la variable $_SESSION['welcome'] est à TRUE à la première visite, puis à FALSE. Utile pour ceux qui veulent afficher un texte dynamique sur cet événement (l'entrée sur le site)

Allez, j'attends vos réactions !

Commentaire de cod57 le 11/09/2011 19:27:12

bonjour

j'ai une une erreur already ... donc j'ai mis session_start()
en début de fichier
il faut activer assert donc
// Activation des assertions et mise en mode discret
assert_options(ASSERT_ACTIVE, 1);
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_QUIET_EVAL, 1);

// Création d'un gestionnaire d'assertions
function my_assert_handler($file, $line, $code)
{
    echo "<hr>Échec de l'assertion :
        File '$file'<br />
        Line '$line'<br />
        Code '$code'<br /><hr />";
}

// Configuration de la méthode de callback
assert_options(ASSERT_CALLBACK, 'my_assert_handler');

// Utilisation d'une assertion qui va échouer
assert('mysql_query("")');

Commentaire de pierreSabatier le 11/09/2011 19:51:16

COD57 > session_start() doit toujours être appelé avant le moindre affichage (même un espace, ou un retour chariot), c'est la source de ton erreur.

Je n'ai pas fait de zip, mais il faut voir deux fichiers distincts :
- le premier s'arrête ligne 44, il s'agit du fichier de fonction que j'ai appelé "visite.php",
- le second est un fichier de test, que j'ai placé dans le même répertoire (je l'ai appelé "visite_test.php")

Pour les assert, oui, tu les configures bien. C'est plus propre que mon WARNING.

Et qu'en penses-tu ?

Commentaire de cod57 le 11/09/2011 20:02:07

Oui en effet j'avais un echo qui trainé (pas vu, scuzy) ... ça à l'air de bien fonctionner maintenant ...
Oui ton code peut être une alternative pour se passer de mysql

A++  

Commentaire de pierreSabatier le 11/09/2011 20:08:24

Super si ça fonctionne.

Mon code ne remplacera cependant jamais un SGBD. En effet, MySql fait de bonnes choses et ce que j'ai écrit reste très simple.

Mais merci, je prend ça pour un encouragement.

A+

Commentaire de cod57 le 11/09/2011 20:32:14 9/10

@ on code ne remplacera cependant jamais un SGBD.
Mais desfois on a besoin d'un code simple et rapide et ton exemple est plus complet qu'une simple incrémentation ...

Commentaire de pierreSabatier le 11/09/2011 20:42:59

Si tu fais référence à ce script : http://www.phpcs.com/codes/COMPTEUR-VISITE-FICHIER-TXT-CODE-MINIMAL_53389.aspx , oui, mon exemple est plus complet, et plus utile je crois.

Commentaire de X_Cli le 12/09/2011 10:29:30

Bonjour,
Votre code contient des bugs de type race condition (je vous laisse faire une recherche google pour voir de quoi il s'agit, vous les trouverez facilement par vous même, après coup), ainsi qu'une faille de sécurité pouvant mener à un déni de service, puisque le champ $visitor n'est pas limité en terme de taille, et qu'il peut, si j'ai bien compris le fonctionnement de votre application, être manipulé à volonté par l'attaquant puisqu'on utilise l'entête HTTP_X_FORWARDED_FOR pour l'alimenter. Vous pouvez me répliquer que les contrôles sur les valeurs peuvent être fait en dehors de vos fonctions, et qu'ils sont à la charge de l'utilisateur de votre librairie, mais à ce moment, un exemple serait le bienvenue afin d'éviter que des débutants fassent "add_visitor(get_ip(), " ...
Bon courage pour les corrections,
Cordialement,
X_Cli

Commentaire de pierreSabatier le 12/09/2011 11:21:13

Bonjour X_CLI,

Je vais poster une modification de mon code très rapidement.

Commentaire de cod57 le 12/09/2011 15:02:08

bonjour
@X_Cli pour une attaque de type race il faudrait pouvoir écrire du contenu dans la variable qui reste coté serveur ($_SERVER) inaccessible ? Si tu penses que ça peut passer ?
Une solution de solution serait un fake un .tmp aleatoire mais je vois pas l'attaque race la dedans peut tu m'expliquer ton idée

++

Commentaire de X_Cli le 12/09/2011 15:47:11

Bonjour cod57,
Note bien que je parle de bug de type race condition et non d'attaque, contrairement à l'attaque sur entete HTTP.
Une race condition intervient notamment si plusieurs connexions arrivent simultanément depuis le même client : plusieurs entrées peuvent être ajoutées pour son IP, ou alors l'incrémentation, si l'IP a déjà été enregistrée peut n'être incrémentée qu'une fois pour deux visites. Il faut un mécanisme de lock pour l'accès au fichier.
Le web n'étant pas spécialement friand de lock (du moins dans les scripts php), ce genre de compteur est mieux s'il est réalisé avec un processus résidant qui recoit des appels (genre une trame UDP) par visite, et met à jour à interval régulier un fichier de compteurs.

Commentaire de pierreSabatier le 12/09/2011 16:08:16

@X_CLI > Le corps de add_visitor devrait être comme ça ?

function add_visitor($visitor, $date, $file = 'compteur.txt'){
$pt = fopen($file,'r+');
if (!flock($pt, LOCK_EX)) {
return EXIT_FAILURE;
}
[...]
fwrite($pt, $entry);
flock($pt, LOCK_UN);
fclose($pt);
return EXIT_SUCCESS;
}

Commentaire de cod57 le 12/09/2011 16:39:02

@X_CLI
'condition intervient notamment si plusieurs connexions arrivent simultanément' === DOS,DDOS,udpflood ... selon moi un utilisateur lambda ne fait pas ça.
Autrement oui pour le lock, c'est d'ailleurs conseillé.
++

Commentaire de X_Cli le 12/09/2011 17:25:29

@pierreSabatier: pour un site à petite affluence, ce genre de fix peut marcher. Pour un site à plus grande affluence, on pourrait imaginer un fichier de lock par IP.
Il manque toujours le contrôle de l'IP (tu peux utiliser par exemple la regexp que j'aimis dans mon source pour valider les IPv4, reste à faire celle pour les IPv6), car sans cela il y a un risque de déni de service (par remplissage de l'espace disque).

@cod57 : ou alors un "site qui rame" et un utilisateur qui fait "Rafrachir" et les différentes connexions arrivent simultanément. Tu serais surpris du nombre de gens qui, lorsque leur réseau domestique WIFI rame, martèlent avec frénésie (3 ou 4 fois par seconde) le bouton de rafraichissement de la page (quand ils ne restent pas appuyés sur F5/Ctrl+R). Il y a des dizaines de bonnes raisons que plusieurs connexions se fassent simultanément qui ne sont pas des attaques, mais des connexion légitimes.

Commentaire de cod57 le 12/09/2011 18:09:23

@X_Cli en fait tu as raison, on ne sait jamais comment le site est utilisé ou visité.
''F5 grrrr ... un javascript pour la dévalider''
... je plaisante ++

Commentaire de arta le 19/09/2011 06:24:47

Bonjour tous

Désolé mais je vois pas la balise de fermeture ?> ???

Commentaire de pierreSabatier le 19/09/2011 08:12:43

Bonjour ARTA, tu ne la vois pas car il n'y en a pas.

Je te renvoie à l'un des premiers cours de php : http://www.php.net/manual/fr/language.basic-syntax.instruction-separation.php . La balise fermante est facultative voire gênante sur les fichiers de fonctions comme celui-ci.

Commentaire de jptilly le 19/09/2011 17:59:40

Bonjour,

fopen sans fclose si flock return EXIT_FAILURE;

Dans ce cas c'est pas grave, on est en PHP avec un timeout sur l'exécution et flock avec cet argument bloque jusqu'au SUCCESS
C'est le timeout qui va terminer le script et fermer le fichier si flock fail.

Commentaire de jptilly le 19/09/2011 18:08:08

function add_visitor($visitor, $date, $file = 'compteur.txt')
{ $ret= EXIT_FAILURE;
  if (TRUE === ($pt = fopen($file,'r+')))
  { if (flock($pt, LOCK_EX))
    {
       [...]

      flock($pt, LOCK_UN);
      $ret= EXIT_SUCCESS;
    }
    fclose($pt);
  }
  return($ret);
}

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

php : lire fichier txt, comparer heure et choix liens [ par drominik ] voila,j'ai dans un fichier txt, l'ip du visiteur et l'heure de sa visite,et j'aimerais a chaque visiteur pouvoir aller lire le fichier txt, et compare IP/Fichier.txt: Probleme FOPEN [ par skurvy ] Voila mon problem:&lt;?$ip= $_SERVER["REMOTE_ADDR"];// Adresse ip$b= "&lt;br&gt;";// pour nouvelle ligne$chat = file("http://".$ip."/chat.txt"); //ouv Problème avec les variables de session [ par banzaichico ] Bonjour à tous,g un pti prob avec mes variables de session:quand je fais : FICHIER 1 :session_start();$theme= "mal";session_register('theme'):FICHIER Fichier de session [ par mfaraday ] Salut !Je pose une question toute théorique sur les fichiers de session.Je crée ma session avec session_startje détruit mes variables avec session_uns Session bizarroïde [ par malik7934 ] Hello,Je ne connais pas trop les sessions et j'ai un problème:J'ai un fichier pwd.php qui crée une image avec un text 'pwd':&lt;? session_start();func SVP ! verification si l'ip a changé ! [ par chico200987 ] Alors voici mon code : _______________________________________________________________________ &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transi session et inclussion de fichier [ par jackrichard ] bonjour a tousvoila j'ai un probl&#232;me avec une session et une inclusion de fichercode de index.php&lt;?session_start();include ("connnection base Require vs Include, Fonctions de BdD et de Session... [ par Tartuffe ] Hello.1/ j'ai un fichier 'conf' qui contient tous le nécessaire pour les accès BdD2/ dans chaque page je fais un require dudit 'conf' et mes 'DBLink' Sécurité par adresse IP et Sécurité par objet Session [ par dadystar ] Bonjour,j'aimerais savoir la diff&#233;rence entre une s&#233;curit&#233; par adresse IP et celle par objet session.MerciCe que je sais c'est que je n Session et no-ip [ par rockclimber ] Bonjour, voila peu de temps que mon site de commerce est en ligne mais avec une ip dynamique (no-ip), en interne les sessions fonctionnent parfaitemen


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,530 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales