Localiser une IP en php [Résolu]

Signaler
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020
-
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020
-
Bonjour,
Je trouve des réponses sur https://forums.commentcamarche.net mais elles ne sont pas récentes; j'en ai essayé quelques unes : soit ça ne fonctionne pas du tout, soit le nombre d'interrogations est limité, ...
Bref, comment obtenir le pays et si possible la ville correspondants à une adresse IP ?
Merci

32 réponses

Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Bonjour,

Tu peux utiliser les bases GeoIP mises à disposition par Maxmind.

Pour les télécharger il faut s'inscrire sur le site de Maxmind :
https://dev.maxmind.com/geoip/geoip2/geolite2/

Pour les utiliser tu as une lib officielle, tout est expliqué avec des exemples :
https://github.com/maxmind/GeoIP2-php

Un rapide exemple fonctionnel, à adapter :
(j'utilise les ternaires de PHP7, à transformer en ternaire classique sur les anciennes versions)
<?php
    // dépendances
    require_once 'vendor/autoload.php';
    use GeoIp2\Database\Reader;

    // choix de la base, GeoLite2-City contient les villes (City)
    // sinon Geolite2-Country.mmdb suffit pour les pays
    $reader = new Reader('GeoLite2-City.mmdb');

    // choix d'une IP
    $record = $reader->city("195.248.251.112");

    print(
        // country->names['code-pays'], nom localisé
        ($record->country->names['fr'] ?? "Pays introuvable") .
        '<br/>' .
        // idem, peut retourner NULL, j'anticipe avec une ternaire
        ($record->city->names['fr'] ?? "Ville introuvable")
    );
?>


Résultat :
France
Paris
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Merci pour ta réponse,
J'avais trouvé un tuto ici http://www.finalclap.com/faq/8-php-geolocalisation-ip
Je suis bloqué parce qu'il est écrit "extraire le zip téléchargé pour récupérer le fichier GeoLiteCity.dat"
j'ai téléchargé et décompressé GeoLite2-City mais le fichier que je trouve s'appelle "GeoLite2-City.mmdb"
où dois-je le mettre pour que ton exemple fonctionne ?
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Comme tu veux, il faudra simplement adapter
new Reader('GeoLite2-City.mmdb');
pour indiquer l'emplacement du fichier.

Dans mon exemple c'était dans le même dossier, sinon tu devras indiquer le chemin (relatif ou absolu, c'est toi qui décide !).
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Et je te déconseille fortement de continuer le tuto, qui n'est plus à jour.
Il était conçu pour une ancienne version de GeoIP.
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

OK, j'ai mis la DB dans le même répertoire que mon script php qui va l'utiliser.
C'est sympa de vouloir m'aider.

En plus de Databse/Reader de quels autres fichiers ai-je besoin du répertoire src ?
Dans ton script je ne comprends pas vendor/autoload.php
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Toutes les instructions d'installation sont sur la page github. Ils disent vraiment quel fichier inclure et comment.

https://github.com/maxmind/GeoIP2-php#readme

Moi j'ai suivi l'étape "Composer", donc dans le dossier du projet :
composer require geoip2/geoip2:~2.0

Si tu n'as pas composer je te renvois (encore) aux explications du github pour savoir comment l'installer.

Tu peux aussi suivre l'autre solution et utiliser
geoip2.phar
, à récupérer sur leur github (les explications, encore :P).
Il te suffira alors de remplacer :
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;

par :
require 'geoip2.phar';
use GeoIp2\Database\Reader;


Mes explications s'arrêtent ici, tu as vraiment tout ce qu'il faut pour réussir à utiliser GeoIP. Je te renvois également à l'image que j'ai posté plus haut, et qui est simplement un screen de mon dossier de test.

(et il faut lire le github :P)
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

J'ai téléchargé Composer-setup.exe pour essayer d'installer Composer.
L'installeur me demande un fichier php.exe
Je pense que je vais laisser tomber.
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
bonjour, moi, sur mon site, pour les adresses ipv4, je retrouve l'info via la variable globale $_SERVER:
[GEOIP_CITY] =>
[GEOIP_COUNTRY_NAME] =>
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

et pour les ipv6 ?
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272 >
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

GeoIP2 gère les adresses IPv6, il te suffira de lui donner une IPv6
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

J'essaye d'essayer avec GeoIP2.phar
J'ai créé un répertoire GeoIP à la racine dans lequel j'ai mis la DB, geoip2.phar
ainsi que les répertoires Database, Exception, ...
et donc puisqu'il est à la racine

use /GeoIp\Database\Reader;

mais il n'aime pas le '/'
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Pourquoi '/' ici ? Je n'en ai pas mis dans mon exemple.

Je te renvois à mon premier message, qui contient un exemple fonctionnel (fonctionnel = que j'ai installé et testé sur mon serveur).
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Pour ton envie d'abandonner... c'est toi qui voit. Moi je te dirai simplement que tu étais proche de la fin, il faut trouver php.exe dans les dossiers de ton environnement de développement et lancer composer.phar en console.

Comme je suis sympa je t'ai même illustré ça avec un gif.

Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Il n'y a pas de fichier php.exe sur mon PC.
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272 >
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Et il fonctionne comment PHP sur ton environnement local ? Évidemment que oui, il y a un fichier php.exe. Il fait partie de PHP :p.

Pense à activer l'affichage des extensions de fichiers :


Si tu as besoin d'aide il faudra me dire le nom de ton serveur local (Wamp ? UwAmp ? EasyPHP ? XAMPP ? ...).
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272 >
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020

Autre méthode, ouvre simplement le gestionnaire des tâches, onglet "Détails" et fait un clic droit sur php.exe.
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Je n'ai pas de serveur local.
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Cette fois c'est à toi de t'adapter, je ne peux pas te proposer toutes les solutions possibles sans même savoir comment tu peux tester ton code PHP. Je précise par ailleurs que n'importe quel tuto PHP à destination des débutants te dira d'installer un simple serveur Wamp pour tester ton code facilement.

Donc soit tu installes un serveur local pour coller à mes instructions, soit tu fait comme je t'ai dit plus haut, avec
geoip2.phar
à mettre simplement dans le même dossier.
<?php
    // dépendances
    require 'geoip2.phar';
    use GeoIp2\Database\Reader;

    // choix de la base, GeoLite2-City contient les villes (City)
    // sinon Geolite2-Country.mmdb suffit pour les pays
    $reader = new Reader('GeoLite2-City.mmdb');


En faisant comme je t'ai dit, avec le code que je t'ai donné dans mon premier message, tu n'as aucune raison d'être bloqué.
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Je trouve un peu lourd de devoir installer un serveur local pour pouvoir installer Composer pour pouvoir utiliser autoload.php pour pouvoir obtenir la localisation d'une adresse IP.

Il me semble plus simple d'utiliser geoip2.phar mais je dois mettre aussi dans le même répertoire Database\Reader et GeoLite2-City.mmdb et peut-être aussi d'autres fichiers.
En d'autres termes pour chaque script php où je veux localiser l'IP je devrais duplicier tous ces fichiers dans le même répertoire.
N'est-il pas possible de mettre une seule fois ce fichiers dans un répertoire commun afin de pouvoir les invoquer à partir de n'importe quel script php ?
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
pourquoi dois-tu mettre Database\Reader et GeoLite2-City.mmdb dans le même répertoire?
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020
>
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020

Parce que, comme je l'ai écrit avant, j'ai créé un répertoire GeoIP à la racine dans lequel j'ai mis la DB, geoip2.phar
ainsi que les répertoires Database, Exception, ... et donc puisqu'il est à la racine

use /GeoIp\Database\Reader;

mais il n'aime pas le '/'

Je viens cependant de comprendre que pour atteindre la racine en php il ne faut pas utiliser '/' comme en html mais $_SERVER['DOCUMENT_ROOT'], ce problème devrait donc être réglé.
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Une possibilité serait de mettre un script php ainsi que la DB dans un répertoire distinct
d'appeler ce script en lui passant l'IP comme paramètre
et qu'il retourne comme paramètre par exemple la ville et le pays.

Dans un répertoire GeoIP situé à la racine je mets la DB, le reader, ... et un script php que j'appelle LocateIP
<?php
session_start();
require 'geoip2.phar';
use GeoIp2\Database\Reader;
$reader = new Reader('/GeoIP/GeoLite2-City.mmdb');
$result = json_decode(file_get_contents('http://ip-api.io/json/'.$_SESSION['LocateIP']),true);
extract($result);
$_SESSION['LocateIP'] = $city."/".$region_name."/".$country_name;
?>

Pour l'appeler à partir de n'importe quel répertoire
session_start();
$_SESSION['LocateIP'] = l'IP à localiser
$output = shell_exec ( '\GeoIP/LocateIP.php' );
$_SESSION['LocateIP'] contient le nom de la ville, région, pays

Problème : Warning: shell_exec() has been disabled for security reasons
Je n'ai pas de disable_functions dans php.ini !
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
ton code est bizarre, tu ne fais rien avec $reader.
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Ai-je oublié quelque chose ?

require $_SERVER['DOCUMENT_ROOT'].'GeoIP/geoip2.phar';
use $_SERVER['DOCUMENT_ROOT'].GeoIP\Database\Reader;
$reader = new Reader($_SERVER['DOCUMENT_ROOT'].'GeoIP/GeoLite2-City.mmdb');
$result = json_decode(file_get_contents('http://ip-api.io/json/'.$IP),true);
extract($result);
$Location = $city."/".$region_name."/".$country_name;
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
je ne comprends pas ce que tu essaies de réaliser. as-tu lu la suggestion en #1?
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

bonjour,
En effet je ne sais plus d'où ça sort, reset.
Le répertoire GeoIP situé à la racine de mon espace web contient en principe les répertoires et fichiers nécessaires pour utiliser GeoIP2.
Je rencontre toujours des difficultés pour l'utiliser à partir d'un autre répertoire; voici mon code
<?php
// dépendances GeoIP
require $_SERVER['DOCUMENT_ROOT'].'/GeoIP/geoip2.phar';
use GeoIP\Database\Reader;
$reader = new Reader('GeoLite2-City.mmdb');
...
$record = $reader->city($IP); // $IP contient l'IP que je veux localiser
$Location = ($record->city->names['fr'] ?? "Ville introuvable")."/".($record->country->names['fr'] ?? "Pays introuvable");

Fatal error: Uncaught Error: Class 'GeoIP\Database\Reader' not found

Comment expliquer à Use que GeoIP se trouve à la racine ?
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
je pense que tu n'as pas compris à quoi sert
use
.
as-tu essayé de l'utiliser exactement comme en #1?
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Use sert à définir un alias pour un Namespace.
Puisque mon répertoire s'appelle GeoIP et non GeoIP2 j'avais remplacé
use GeoIP2\Database\Reader;
par
use GeoIP\Database\Reader;

Comme en #1
use GeoIp2\Database\Reader;

Fatal error: Uncaught InvalidArgumentException: The file "GeoLite2-City.mmdb" does not exist or is not readable. in phar:///customers/e/a/4/rudyv.be/httpd.www/GeoIP/geoip2.phar/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php:52 Stack trace: #0 phar:///customers/e/a/4/rudyv.be/httpd.www/GeoIP/geoip2.phar/src/Database/Reader.php(54): MaxMind\Db\Reader->__construct('GeoLite2-City.m...') #1 /customers/e/a/4/rudyv.be/httpd.www/Aquarium/RVRT/RVRTactivityGeoIP.php(6): GeoIp2\Database\Reader->__construct('GeoLite2-City.m...') #2 {main} thrown in phar:///customers/e/a/4/rudyv.be/httpd.www/GeoIP/geoip2.phar/vendor/maxmind-db/reader/src/MaxMind/Db/Reader.php on line 52

GeoLite2-City.mmdb se trouve pourtant bien dans le répertoire GeoIP qui est à la racine.
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
Un Namespace n'a rien à voir avec un répertoire.
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
as-tu essayé de donner le nom complet pour le fichier mmdb?
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Merci, c'était ça, mon code complet
require $_SERVER['DOCUMENT_ROOT'].'/GeoIP/geoip2.phar';
use GeoIp2\Database\Reader;
$reader = new Reader($_SERVER['DOCUMENT_ROOT'].'/GeoIP/GeoLite2-City.mmdb');
...
$record = $reader->city(trim($IP));
$Location = ($record->city->names['fr'] ?? "Ville?")."/".($record->country->names['fr'] ?? "Pays?");

Il fonctionne presque, je n'ai pas toujours la ville.

Un défaut est que la variable $IP ne contient pas toujours une IP; dans ce cas j'ai une erreur "Uncaught InvalidArgumentException".
Est-il possible que $reader->city(trim($IP)) donne un code d'erreur dans ce cas ?
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

J'ai trouvé filter_var($IP, FILTER_VALIDATE_IP)
Un tout grand merci à yg_be et Pyraah
Messages postés
3546
Date d'inscription
lundi 16 juillet 2007
Statut
Contributeur sécurité
Dernière intervention
5 juillet 2020
1 272
Merci à yg_be d'avoir pris le relais, j'ai été peu disponible ces derniers jours.

Pour la ville... j'avais précisé dans mon code que ça pouvait renvoyer NULL.

Pour l'IP tu peux d'abord vérifier
'REMOTE_ADDR'
. Si c'est vide, vérifie
'HTTP_X_FORWARDED_FOR'
.

Dans mon code j'ai chaîné ça dans une suite de ternaires :
$ip = $_SERVER['REMOTE_ADDR'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? FALSE;

Je te renvois au timecode suivant, Grafikart explique assez bien les ternaires de PHP7 : https://youtu.be/obr7pO7_myI?t=540

Bye :).
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Bonjour,
J'ai été amené à localiser un IP parce que je constatais une forte utilisation d'un de mes scripts php; j'ai ajouté $_SERVER['REMOTE_ADDR'] pour obtenir l'IP : Washington !
Depuis j'ai ajouté un Captcha au script, problème résolu.

J'ai toutefois gardé l'affichage de l'IP, il est plus pratique d'obtenir directement la localisation plutôt que l'IP, une grand précision n'est pas requise, ça fonctionne très bien comme cela.

Le seul petit problème est qu'au moment de l'affichage je ne suis plus en connexion avec l'utilisateur, j'ai seulement une zone IP dans une DB, et cette zone ne contient pas toujours une IP; dans ce cas $reader->city(trim($IP)) donne "Uncaught InvalidArgumentException" et le script s'arrête.
J'ai alors utilisé filter_var($IP, FILTER_VALIDATE_IP) avant de soumettre la variable à $reader
mais je suis tombé sur un enregistrement où la zone IP contenait "0.0.0.0" qui est une IP syntaxiquement valide, qui passe donc à travers le filtre, mais qui plante le reader avec un message du style "IP not found in GeoLite2-City.mmdb".

Ma question est donc : comment empêcher reader de se planter s'il n'aime pas l'IP qu'on lui passe ?
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
Messages postés
762
Date d'inscription
mercredi 4 août 2010
Statut
Membre
Dernière intervention
6 juillet 2020

Bonjour,
Non, je ne connais pas.
Si j'ai bien compris il faut lancer (throw) l'exception dans le module qui va la produire, à savoir Reader.
Je ne vois pas comment je pourrais m'en servir pour ne pas avoir "Uncatched exception".
Si Reader ne donne pas de réponse mon script peut très bien continuer.

Pour le moment je n'appelle simplement pas le reader si l'IP = "0.0.0.0" ou si filter_var($IP, FILTER_VALIDATE_IP) = false.
Messages postés
11513
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 juillet 2020
659
le message t'indique qu'il s'agit d'une exception non attrapée. tu dois attraper l'exception, pas la lancer.