قرینه از
https://github.com/matomo-org/matomo.git
synced 2025-08-22 15:07:44 +00:00

* [Coding Style] Enable rule PSR12.Files.FileHeader * Apply CS * Replace Piwik with Matomo in file headers * Unify file headers (position, no. of lines, https links) * Rebuild dist files * Apply CS * Fix system test that relies on line numbers in a file that had the file header updated --------- Co-authored-by: Stefan Giehl <stefan@matomo.org>
229 خطوط
8.5 KiB
PHP
229 خطوط
8.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*/
|
|
|
|
namespace Piwik\Plugins\GeoIp2\Commands;
|
|
|
|
use Piwik\Development;
|
|
use Piwik\Http;
|
|
use Piwik\Plugin\ConsoleCommand;
|
|
use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2\Php;
|
|
use Piwik\Plugins\UserCountry\LocationProvider;
|
|
|
|
/**
|
|
* This command can be used to update the list of regions and their names that Matomo knows about.
|
|
* A list of iso regions is fetched from the iso-codes project. This list will then be used to update the regions array
|
|
* located in data/isoRegionNames
|
|
* - new regions will be added as current
|
|
* - changed names will be updated (adding the previous name as alternate name)
|
|
* - removed regions will be kept, but marked as not current
|
|
*
|
|
* Additionally, this command can be used to add regions that are returned by DB IP GeoIP database
|
|
* As the DBIP Lite database only contains region names, but no region codes, we try to map the returned name to a known
|
|
* region. As DBIP in some cases returns names, that differ from the official region name, Matomo would be unable to
|
|
* store those regions. To provide a better mapping this command allows to provide the --db-ip-csv option.
|
|
* This option should provide the path to the DB-IP city lite database in CSV format. In addition, the paid DB-IP city
|
|
* (mmdb) database should be configured in Matomo as location provider.
|
|
* The command will then iterate through all IP ranges defined in the CSV database and query a look-up using the
|
|
* location provider. The returned region iso code and region name is then compared with those included in the regions
|
|
* array. Missing regions will be added (as not current), mismatching names will be added as alternate names.
|
|
* This will ensure that regions returned by the lite database should be mapped correctly.
|
|
* Attention: Using this option will take a couple of hours to process.
|
|
*/
|
|
class UpdateRegionCodes extends ConsoleCommand
|
|
{
|
|
public $source = 'https://salsa.debian.org/iso-codes-team/iso-codes/-/raw/main/data/iso_3166-2.json';
|
|
|
|
protected function configure()
|
|
{
|
|
$this->setName('usercountry:update-region-codes');
|
|
$this->setDescription("Updates the ISO region names");
|
|
$this->addOptionalValueOption('db-ip-csv', null, 'Uses the provided DB IP CSV database to iterate over all included IP ranges.');
|
|
}
|
|
|
|
public function isEnabled()
|
|
{
|
|
return Development::isEnabled();
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
protected function doExecute(): int
|
|
{
|
|
$output = $this->getOutput();
|
|
$input = $this->getInput();
|
|
|
|
$regionsFile = __DIR__ . '/../data/isoRegionNames.php';
|
|
|
|
$output->setDecorated(true);
|
|
|
|
$output->writeln('Starting region codes update');
|
|
|
|
$output->write('Fetching region codes from ' . $this->source);
|
|
|
|
try {
|
|
$newContent = Http::sendHttpRequest($this->source, 1000);
|
|
} catch (\Exception $e) {
|
|
$output->writeln(' <fg=red>X (Fetching content failed)</>');
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$regionData = json_decode($newContent, true);
|
|
|
|
if (empty($regionData)) {
|
|
$output->writeln(' <fg=red>X (Content could not be parsed)</>');
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$output->writeln(' <fg=green>✓</>');
|
|
|
|
$newRegions = [];
|
|
foreach ($regionData['3166-2'] as $region) {
|
|
list($countryCode, $regionCode) = explode('-', $region['code']);
|
|
$newRegions[$countryCode][$regionCode] = [
|
|
'name' => $region['name'],
|
|
'altNames' => [],
|
|
'current' => true
|
|
];
|
|
}
|
|
|
|
|
|
ksort($newRegions);
|
|
|
|
$currentRegions = include $regionsFile;
|
|
|
|
foreach ($currentRegions as $countryCode => $regions) {
|
|
foreach ($regions as $regionCode => $regionData) {
|
|
if (isset($newRegions[$countryCode][$regionCode])) {
|
|
$newRegions[$countryCode][$regionCode]['altNames'] = $regionData['altNames'];
|
|
|
|
if (
|
|
$newRegions[$countryCode][$regionCode]['name'] !== $regionData['name']
|
|
&& !in_array($regionData['name'], $newRegions[$countryCode][$regionCode]['altNames'])
|
|
) {
|
|
$newRegions[$countryCode][$regionCode]['altNames'][] = $regionData['name'];
|
|
}
|
|
|
|
if (($key = array_search($newRegions[$countryCode][$regionCode]['name'], $newRegions[$countryCode][$regionCode]['altNames'])) !== false) {
|
|
unset($newRegions[$countryCode][$regionCode]['altNames'][$key]);
|
|
$newRegions[$countryCode][$regionCode]['altNames'] = array_values($newRegions[$countryCode][$regionCode]['altNames']);
|
|
}
|
|
} else {
|
|
$newRegions[$countryCode][$regionCode] = $regionData;
|
|
$newRegions[$countryCode][$regionCode]['current'] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
$dbIpCsvFile = $input->getOption('db-ip-csv');
|
|
|
|
if (!empty($dbIpCsvFile)) {
|
|
$this->enrichWithDbIpRegions($dbIpCsvFile, $newRegions);
|
|
}
|
|
|
|
if (json_encode($newRegions) === json_encode($currentRegions)) {
|
|
$output->writeln('');
|
|
$output->writeln('Everything already up to date <fg=green>✓</>');
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
$content = <<<CONTENT
|
|
<?php
|
|
// The below list contains all ISO region codes and names known to Matomo
|
|
// Format:
|
|
// <CountryCode> => [
|
|
// <RegionCode> => [
|
|
// 'name' => <CurrentISOName>
|
|
// 'altNames' => [
|
|
// // list of previous names or names used by GeoIP providers like db-ip
|
|
// ],
|
|
// 'current' => <bool> indicating if the iso code is currently used
|
|
// ]
|
|
// ]
|
|
return
|
|
CONTENT;
|
|
|
|
$content .= var_export($newRegions, true) . ';';
|
|
|
|
file_put_contents($regionsFile, $content);
|
|
|
|
$output->writeln('File successfully updated <fg=green>✓</>');
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
private function enrichWithDbIpRegions(string $dbIpCsvFile, array &$regions)
|
|
{
|
|
$output = $this->getOutput();
|
|
$output->writeln('Start looking through GeoIP database for missing region names');
|
|
|
|
$php = new Php();
|
|
|
|
$supportedInfo = $php->getSupportedLocationInfo();
|
|
|
|
if (empty($supportedInfo[LocationProvider::REGION_CODE_KEY])) {
|
|
$output->writeln(' <fg=red>X Region codes not supported by currently used GeoIP database. Skipping.</>');
|
|
return;
|
|
}
|
|
|
|
$output->writeln('Iterating through all IPv4 addresses...');
|
|
|
|
$this->initProgressBar(6396645);
|
|
|
|
$handle = fopen($dbIpCsvFile, 'r');
|
|
|
|
while (!feof($handle)) {
|
|
$csv = str_getcsv(fgets($handle));
|
|
$ip = $csv[0] ?? '';
|
|
|
|
$this->advanceProgressBar();
|
|
|
|
if (empty($ip)) {
|
|
continue;
|
|
}
|
|
|
|
$location = $php->getLocation(['ip' => $ip]);
|
|
|
|
$countryCode = $location[LocationProvider::COUNTRY_CODE_KEY] ?? null;
|
|
$regionCode = $location[LocationProvider::REGION_CODE_KEY] ?? null;
|
|
$regionName = $location[LocationProvider::REGION_NAME_KEY] ?? null;
|
|
|
|
if (empty($countryCode) || empty($regionCode) || empty($regionName)) {
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($countryCode, $regions)) {
|
|
continue;
|
|
}
|
|
|
|
if (!array_key_exists($regionCode, $regions[$countryCode])) {
|
|
$output->writeln('');
|
|
$output->writeln("Adding missing region $regionName ($regionCode) for country $countryCode <fg=green>✓</>");
|
|
$regions[$countryCode][$regionCode] = [
|
|
'name' => $regionName,
|
|
'altNames' => [],
|
|
'current' => false,
|
|
];
|
|
} else {
|
|
if (
|
|
$regionName !== $regions[$countryCode][$regionCode]['name']
|
|
&& !in_array($regionName, $regions[$countryCode][$regionCode]['altNames'])
|
|
) {
|
|
$output->writeln('');
|
|
$output->writeln("Adding alternate region name $regionName to region {$regions[$countryCode][$regionCode]['name']} ($regionCode) for country $countryCode <fg=green>✓</>");
|
|
$regions[$countryCode][$regionCode]['altNames'][] = $regionName;
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose($handle);
|
|
$this->finishProgressBar();
|
|
}
|
|
}
|