Magento 使用 geoip2 插件做用户IP定位功能

一. 安装 geoip2 库,并检查ip

第一步:安装 geoip2 库

composer require geoip2/geoip2:~2.0

 

第二步:下载 GeoLite2 数据库
需要注册账户,注册成功后就可以直接下载 GeoLite2 数据库
第三步:调用 ip 检测方法

$reader = new Reader('/var/geoip/GeoLite2-Country.mmdb'); //替换成自己的数据库文件路径
$record = $reader->city('128.101.101.101');
print($record->country->isoCode);

 

 

二. 更新 geoip2 数据库

下载用到的 API
https://www.geodbase-update.com/api/v1/edition
需要注册账户的 License key
直接上代碼:

<?php

namespace xxxx\xxxxxx\Helper;

use DirectoryIterator;
use Exception;
use FilesystemIterator;
use PharData;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
use ZipArchive;

/**
* Class Client
* @package tronovav\GeoIP2Update
*/
class Client
{
const ARCHIVE_GZ = 'tar.gz';
const ARCHIVE_ZIP = 'zip';

/**
* @var string Your account’s actual license key on www.maxmind.com
* @link https://support.maxmind.com/account-faq/license-keys/where-do-i-find-my-license-key/
*/
public $license_key;

/**
* @var string Your account’s actual "geodbase_update_key" on www.geodbase-update.com
* @link https://www.geodbase-update.com
*/
public $geodbase_update_key;

/**
* @var string[] Database editions list to update.
* @link https://www.maxmind.com/en/accounts/current/geoip/downloads/
*/
public $editions;

/**
* @var string Destination directory. Directory where your copies of databases are stored. Your old databases will be updated there.
*/
public $dir;

protected $_editionVersions = array();
protected $_baseUrlApi = 'https://www.geodbase-update.com/api/v1/edition';
protected $_updated = array();
protected $_errors = array();
protected $_errorUpdateEditions = array();
protected $_lastModifiedStorageFileName = 'VERSION.txt';
protected $_client = 1;
protected $_client_version = '2.3.1';

public function __construct(array $params)
{
$this->setConfParams($params);
$thisClass = new ReflectionClass($this);
foreach ($params as $key => $value)
if ($thisClass->hasProperty($key) && $thisClass->getProperty($key)->isPublic()) {
$this->$key = $value;
} else {
$this->_errors[] = "The \"{$key}\" parameter does not exist. Just remove it from the options. See https://www.geodbase-update.com";
}
}

/**
* Update info.
* @return array
*/
public function updated()
{
return $this->_updated;
}

/**
* Update errors.
* @return array
*/
public function errors()
{
return array_merge($this->_errors, array_values($this->_errorUpdateEditions));
}

/**
* Database update launcher.
* @throws Exception
*/
public function run()
{
if (!$this->validate()) {
return;
}

$this->updateEdition((string)$this->editions);

}

protected function setConfParams(&$params)
{
if (array_key_exists('geoipConfFile', $params)) {
if(is_file($params['geoipConfFile']) && is_readable($params['geoipConfFile'])) {
$confParams = array();
foreach (file($params['geoipConfFile']) as $line) {
$confString = trim($line);
if (preg_match('/^\s*(?P<name>LicenseKey|EditionIDs)\s+(?P<value>([\w-]+\s*)+)$/', $confString, $matches)) {
$confParams[$matches['name']] = $matches['name'] === 'EditionIDs'
? array_values(array_filter(explode(' ', $matches['value']), function ($val) {
return trim($val);
}))
: trim($matches['value']);
}
}
$this->license_key = !empty($confParams['LicenseKey']) ? $confParams['LicenseKey'] : $this->license_key;
$this->editions = !empty($confParams['EditionIDs']) ? $confParams['EditionIDs'] : $this->editions;
} else {
$this->_errors[] = 'The geoipConfFile parameter was specified, but the file itself is missing or unreadable. See https://www.geodbase-update.com';
}
unset($params['geoipConfFile']);
}
}

/**
* @return bool
*/
protected function validate()
{
if (!empty($this->_errors)) {
return false;
}

switch (true) {
case empty($this->dir):
$this->_errors[] = 'Destination directory not specified. See documentation at https://www.geodbase-update.com';
break;
case !is_dir($this->dir):
$this->_errors[] = "The destination directory \"{$this->dir}\" does not exist. See documentation at https://www.geodbase-update.com";
break;
case !is_writable($this->dir):
$this->_errors[] = "The destination directory \"{$this->dir}\" is not writable. See documentation at https://www.geodbase-update.com";
}

if (empty($this->license_key)) {
$this->_errors[] = 'You must specify your Maxmind "license_key". See documentation at https://www.geodbase-update.com';
}

if (empty($this->editions)) {
$this->_errors[] = "No GeoIP revision names are specified for the update. See documentation at https://www.geodbase-update.com";
}

if (!empty($this->_errors)) {
return false;
}

return true;
}

/**
* @param string $editionId
* @throws Exception
*/
protected function updateEdition(string $editionId)
{
$remoteEditionData = $this->getRemoteEditionData($editionId);

if (!empty($this->_errorUpdateEditions[$editionId])) {
return;
}

if ($remoteEditionData['ext'] === self::ARCHIVE_ZIP && !class_exists('\ZipArchive')) {
$this->_errorUpdateEditions[$editionId] = "PHP zip extension is required to update csv databases. See https://www.php.net/manual/en/zip.installation.php to install zip php extension.";
return;
}

$remoteActualVersion = date_create($remoteEditionData['version']);

$localEditionData = is_file($this->getEditionDirectory($editionId) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName) ?
file_get_contents($this->getEditionDirectory($editionId) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName) : '';

$currentVersion = date_create_from_format('Y-m-d\TH:i:sP',$localEditionData) ?: 0;

$this->_editionVersions[$editionId] = [
!empty($currentVersion) ? $currentVersion->format('c') : 0,
$remoteActualVersion->format('c')
];

if (empty($currentVersion) || $currentVersion != $remoteActualVersion) {

$this->download($remoteEditionData);
if (!empty($this->_errorUpdateEditions[$editionId])) {
return;
}

$this->extract($remoteEditionData);
if (!empty($this->_errorUpdateEditions[$editionId])) {
return;
}

$this->_updated[] = "$editionId has been updated.";
} else {
$this->_updated[] = "$editionId does not need to be updated.";
}
}

/**
* @param $editionId
* @return string
*/
protected function getEditionDirectory($editionId): string
{
return $this->dir . DIRECTORY_SEPARATOR . $editionId;
}

/**
* @param string $editionId
* @return array
*/
protected function getRemoteEditionData(string $editionId): array
{
$ch = curl_init(trim($this->_baseUrlApi,'/').'/'.'data'.'?'. http_build_query(array(
'id' => $editionId,
)));
curl_setopt_array(
$ch,
[
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json',
'X-Api-Key: '.$this->geodbase_update_key
],
CURLOPT_POSTFIELDS => json_encode(
[
'maxmind_key' =>$this->license_key,
'client' => $this->_client,
'client_version' => $this->_client_version
]
)
]
);

$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if(empty($httpCode)){
$this->_errorUpdateEditions[$editionId] = "The remote server is not available.";
return [];
}

$resultArray = json_decode($result,true);

if($httpCode !== 200){
$this->_errorUpdateEditions[$editionId] = $resultArray['data']['message'] ?: $resultArray['data']['name'];
return [];
}
return $resultArray['data'];
}

/**
* @param array $remoteEditionData
*/
protected function download($remoteEditionData)
{
$ch = curl_init(
trim($this->_baseUrlApi,'/').'/'.'download'.'?'. http_build_query(
['request_id' => $remoteEditionData['request_id']]
)
);
$fh = fopen($this->getArchiveFile($remoteEditionData), 'wb');
curl_setopt_array($ch, array(
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'X-Api-Key: '.$this->geodbase_update_key,
),
CURLOPT_FILE => $fh,
));
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
fclose($fh);
if ($response === false || $httpCode !== 200){
if(is_file($this->getArchiveFile($remoteEditionData)))
unlink($this->getArchiveFile($remoteEditionData));
$this->_errorUpdateEditions[$remoteEditionData['id']] = "Download error: ($httpCode)" . curl_error($ch);
}
}

protected function getArchiveFile($remoteEditionData)
{
return $this->dir . DIRECTORY_SEPARATOR . $remoteEditionData['id'] . '.' . $remoteEditionData['ext'];
}

/**
* @param array $remoteEditionData
*/
protected function extract(array $remoteEditionData)
{
switch ($remoteEditionData['ext']) {
case self::ARCHIVE_GZ:
if (!in_array('phar', stream_get_wrappers(), true)) {
stream_wrapper_restore('phar');
}
$phar = new PharData($this->getArchiveFile($remoteEditionData));
$phar->extractTo($this->dir, null, true);
break;
case self::ARCHIVE_ZIP:
$zip = new ZipArchive;
$zip->open($this->getArchiveFile($remoteEditionData));
$zip->extractTo($this->dir);
$zip->close();
break;
}

unlink($this->getArchiveFile($remoteEditionData));

if (!is_dir($this->getEditionDirectory($remoteEditionData['id']))) {
mkdir($this->getEditionDirectory($remoteEditionData['id']));
}

$directories = new DirectoryIterator($this->dir);
foreach ($directories as $directory) {
/* @var DirectoryIterator $directory */
if ($directory->isDir() && preg_match('/^' . $remoteEditionData['id'] . '[_\d]+$/i', $directory->getBasename())) {
$newEditionDirectory = new DirectoryIterator($directory->getPathname());
foreach ($newEditionDirectory as $item)
if ($item->isFile()) {
rename($item->getPathname(), $this->getEditionDirectory($remoteEditionData['id']) . DIRECTORY_SEPARATOR . $item->getBasename());
}
file_put_contents($this->getEditionDirectory($remoteEditionData['id']) . DIRECTORY_SEPARATOR . $this->_lastModifiedStorageFileName, $remoteEditionData['version']);
$this->deleteDirectory($directory->getPathname());
break;
}
}
$this->copyFiles($remoteEditionData);
}

/**
* @param $remoteEditionData
*/
public function copyFiles($remoteEditionData)
{
$directoryPath = $this->dir . DIRECTORY_SEPARATOR . $remoteEditionData['id'];
$sourceFile = $this->dir . DIRECTORY_SEPARATOR . $remoteEditionData['id'] . DIRECTORY_SEPARATOR . $remoteEditionData['id'] . '.mmdb';
$targetFile = $this->dir . DIRECTORY_SEPARATOR . $remoteEditionData['id'] . '.mmdb';
$content = file_get_contents($sourceFile);
file_put_contents($targetFile, $content);
$this->deleteDirectory($directoryPath);
}

/**
* @param string $directoryPath
*/
protected function deleteDirectory(string $directoryPath)
{
if (is_dir($directoryPath)) {
$directory = new RecursiveDirectoryIterator($directoryPath, FilesystemIterator::SKIP_DOTS);
$children = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($children as $child)
/* @var RecursiveDirectoryIterator $child */
$child->isDir() ? rmdir($child) : unlink($child);
rmdir($directoryPath);
}
}
}


//调用数据库更新
public function execute()
{
// 获取 License key
$licenseKey = $this->helperData->getLicenseKey();
// 指定下载目录
$downloadPath = $this->directoryList->getPath('var') . $this->helperData->getDownloadPath();
if (!is_dir($downloadPath)) {
mkdir($downloadPath, 0755, true);
}
$client = new Client(
[
'license_key' => $licenseKey,
'dir' => $downloadPath,
'editions' => 'GeoLite2-Country',
]
);
$client->run();
}

 

发表评论