From b4fda225c2dc29f302094d84cf4f38fd37e9e04e Mon Sep 17 00:00:00 2001 From: alexmerlin Date: Tue, 2 Dec 2025 11:10:51 +0200 Subject: [PATCH 1/3] Issue #50: Major refactoring Added option to increase memory limit Signed-off-by: alexmerlin --- README.md | 2 +- SECURITY.md | 23 +++----- composer.json | 7 +-- docs/book/v3/configuration.md | 2 +- docs/book/v3/manage-geolite2-database.md | 6 +- docs/book/v3/overview.md | 2 +- docs/book/v3/usage.md | 29 +--------- src/Client.php | 31 ++++++++++ src/Command/GeoIpCommand.php | 63 ++++++++++++++------- src/Data/CityData.php | 13 ++--- src/Data/ContinentData.php | 13 +---- src/Data/CountryData.php | 17 ++---- src/Data/LocationData.php | 29 +++------- src/Data/OrganizationData.php | 14 ++--- src/Extractor.php | 29 ++++++++++ src/Factory/GeoIpCommandFactory.php | 9 ++- src/FileSystem.php | 44 ++++++++++++++ src/Service/LocationService.php | 36 ++++++++++-- src/Service/LocationServiceInterface.php | 10 +++- test/Command/GeoIpCommandTest.php | 19 +++---- test/CommonTrait.php | 22 ++++++- test/Factory/GeoIpCommandFactoryTest.php | 6 +- test/Factory/LocationServiceFactoryTest.php | 3 +- test/Service/LocationServiceTest.php | 13 ++--- 24 files changed, 272 insertions(+), 170 deletions(-) create mode 100644 src/Client.php create mode 100644 src/Extractor.php create mode 100644 src/FileSystem.php diff --git a/README.md b/README.md index c471b67..9a836ad 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Documentation is available at: https://docs.dotkernel.org/dot-geoip/. ## Badges ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-geoip) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.10.0) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.11.0) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/network) diff --git a/SECURITY.md b/SECURITY.md index 3251d56..01c4aeb 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,14 +4,13 @@ | Version | Supported | PHP Version | |---------|--------------------|------------------------------------------------------------------------------------------------------------| -| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.10.0) | +| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.11.0) | | <= 2.x | :x: | | ## Reporting Potential Security Issues -If you have encountered a potential security vulnerability in this project, -please report it to us at . We will work with you to -verify the vulnerability and patch it. +If you have encountered a potential security vulnerability in this project, please report it to us at . +We will work with you to verify the vulnerability and patch it. When reporting issues, please provide the following information: @@ -19,20 +18,12 @@ When reporting issues, please provide the following information: - A description indicating how to reproduce the issue - A summary of the security vulnerability and impact -We request that you contact us via the email address above and give the -project contributors a chance to resolve the vulnerability and issue a new -release prior to any public exposure; this helps protect the project's -users, and provides them with a chance to upgrade and/or update in order to -protect their applications. - +We request that you contact us via the email address above and give the project contributors a chance to resolve the vulnerability and issue a new release prior to any public exposure; +this helps protect the project's users and provides them with a chance to upgrade and/or update to protect their applications. ## Policy If we verify a reported security vulnerability, our policy is: -- We will patch the current release branch, as well as the immediate prior minor - release branch. - -- After patching the release branches, we will immediately issue new security - fix releases for each patched release branch. - +- We will patch the current release branch, as well as the immediate prior minor release branch. +- After patching the release branches, we will immediately issue new security fix releases for each patched release branch. diff --git a/composer.json b/composer.json index 6458f4a..c51de70 100644 --- a/composer.json +++ b/composer.json @@ -23,12 +23,11 @@ }, "require": { "php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "ext-curl": "*", + "ext-zlib": "*", "dotkernel/dot-cli": "^3.5", "geoip2/geoip2": "^3.0", - "guzzlehttp/guzzle": "^7.8", - "laminas/laminas-filter": "^2.34", - "psr/container": "^1.1 || ^2.0.0", - "symfony/filesystem": "^7.0" + "psr/container": "^1.1 || ^2.0.0" }, "require-dev": { "laminas/laminas-coding-standard": "^3.0", diff --git a/docs/book/v3/configuration.md b/docs/book/v3/configuration.md index 7fcf73b..90096e4 100644 --- a/docs/book/v3/configuration.md +++ b/docs/book/v3/configuration.md @@ -11,5 +11,5 @@ Dot\GeoIP\ConfigProvider::class, Register the library's synchronizer command by adding the following line to your application's `config/autoload/cli.global.php` file under the `commands` array key: ```php -Dot\GeoIP\Command\GeoIpCommand::getDefaultName() => Dot\GeoIP\Command\GeoIpCommand::class, +\Dot\GeoIP\Command\GeoIpCommand::$defaultName => \Dot\GeoIP\Command\GeoIpCommand::class, ``` diff --git a/docs/book/v3/manage-geolite2-database.md b/docs/book/v3/manage-geolite2-database.md index baa857b..740434d 100644 --- a/docs/book/v3/manage-geolite2-database.md +++ b/docs/book/v3/manage-geolite2-database.md @@ -22,6 +22,10 @@ city: n/a -> 2021-07-01 02:09:20 country: n/a -> 2021-07-01 02:05:12 ``` -Get help for this command by running `php bin/cli.php help geoip:synchronize`. +Get help for this command by running: + +```shell +php bin/cli.php geoip:synchronize --help +``` > If you set up the synchronizer command as a cronjob, you can add the `-q|--quiet` option, and it will output data only if an error has occurred. diff --git a/docs/book/v3/overview.md b/docs/book/v3/overview.md index ecffac8..31de452 100644 --- a/docs/book/v3/overview.md +++ b/docs/book/v3/overview.md @@ -7,7 +7,7 @@ Dotkernel component to provide geographical details about an IP address. ## Badges ![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-geoip) -![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.10.0) +![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.11.0) [![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/issues) [![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/network) diff --git a/docs/book/v3/usage.md b/docs/book/v3/usage.md index c0d7a80..8166947 100644 --- a/docs/book/v3/usage.md +++ b/docs/book/v3/usage.md @@ -3,36 +3,13 @@ Below is an example implementation of using DotGeoip to retrieve information about an IP address. ```php -locationService = $locationService; + public function __construct( + private \Dot\GeoIP\Service\LocationServiceInterface $locationService + ) { } - /** - * @param string $ipAddress - * @return object - */ public function myMethod(string $ipAddress): object { return $this->locationService->getCountry($ipAddress); // Returns an instance of Dot\GeoIP\Data\CountryData diff --git a/src/Client.php b/src/Client.php new file mode 100644 index 0000000..d143569 --- /dev/null +++ b/src/Client.php @@ -0,0 +1,31 @@ + $fp, + CURLOPT_FAILONERROR => true, + ]); + + curl_exec($ch); + fclose($fp); + } +} diff --git a/src/Command/GeoIpCommand.php b/src/Command/GeoIpCommand.php index 4ece182..d2bbbb5 100644 --- a/src/Command/GeoIpCommand.php +++ b/src/Command/GeoIpCommand.php @@ -4,28 +4,27 @@ namespace Dot\GeoIP\Command; +use Dot\GeoIP\Client; +use Dot\GeoIP\Extractor; +use Dot\GeoIP\FileSystem; use Dot\GeoIP\Service\LocationService; use Dot\GeoIP\Service\LocationServiceInterface; use Exception; -use GuzzleHttp\Client; -use GuzzleHttp\Exception\GuzzleException; -use GuzzleHttp\RequestOptions; -use Laminas\Filter\Decompress; +use InvalidArgumentException; use MaxMind\Db\Reader\Metadata; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Filesystem\Filesystem; use function array_key_exists; use function array_keys; use function date; use function implode; +use function ini_set; +use function preg_match; use function sprintf; -use function str_replace; -use function trim; #[AsCommand( name: 'geoip:synchronize', @@ -35,11 +34,14 @@ class GeoIpCommand extends Command { /** @var string $defaultName */ public static $defaultName = 'geoip:synchronize'; + private FileSystem $fileSystem; public function __construct( protected LocationServiceInterface $locationService, ) { parent::__construct(self::$defaultName); + + $this->fileSystem = new FileSystem(); } public function configure(): void @@ -56,25 +58,32 @@ public function configure(): void implode(', ', array_keys(LocationService::DATABASES)) ), LocationService::DATABASE_ALL + ) + ->addOption( + 'memory-limit', + 'm', + InputOption::VALUE_OPTIONAL, + 'Memory limit for decompression. Examples: 256M, 1G', + '128M' ); } /** - * @throws GuzzleException * @throws Exception */ public function execute(InputInterface $input, OutputInterface $output): int { - $fileSystem = new Filesystem(); - if (! $fileSystem->exists($this->locationService->getConfig('targetDir'))) { - $fileSystem->mkdir($this->locationService->getConfig('targetDir')); - } + $memoryLimit = $input->getOption('memory-limit'); + $this->validateMemoryLimit($memoryLimit); + ini_set('memory_limit', $memoryLimit); + + $this->fileSystem->mkdir($this->locationService->getConfig('targetDir')); $database = $input->getOption('database') ?? LocationService::DATABASE_ALL; $databases = $this->identifyDatabases($database); foreach ($databases as $database) { - $sourcePath = $this->locationService->getDatabaseSource($database); - $targetPath = $this->locationService->getDatabasePath($database); + $tempFilePath = $this->locationService->getTempFilePath($database); + $realFilePath = $this->locationService->getRealFilePath($database); $oldVersion = 'n/a'; $oldMetadata = $this->locationService->getDatabaseMetadata($database); @@ -82,15 +91,11 @@ public function execute(InputInterface $input, OutputInterface $output): int $oldVersion = date('Y-m-d H:i:s', $oldMetadata->buildEpoch); } - $url = trim($this->locationService->getConfig('databases')[$database]['source']); - $url = str_replace('{year}', date('Y'), $url); - $url = str_replace('{month}', date('m'), $url); - (new Client())->get($url, [RequestOptions::SINK => $sourcePath]); + (new Client())->get($this->locationService->getDatabaseSourceUrl($database), $tempFilePath); - $content = (new Decompress())->getAdapter()->decompress($sourcePath); - $fileSystem->remove($targetPath); - $fileSystem->dumpFile($targetPath, $content); - $fileSystem->remove($sourcePath); + $this->fileSystem->remove($realFilePath); + (new Extractor())->extract($tempFilePath, $realFilePath); + $this->fileSystem->remove($tempFilePath); $newVersion = 'n/a'; $newMetadata = $this->locationService->getDatabaseMetadata($database); @@ -128,4 +133,18 @@ public function identifyDatabases(string $identifier): array return [$identifier]; } + + /** + * @throws InvalidArgumentException + */ + private function validateMemoryLimit(string $memoryLimit): void + { + if (preg_match('/^(\d+)([MG])$/', $memoryLimit) === 1) { + return; + } + + throw new InvalidArgumentException( + sprintf('Invalid memory limit: %s', $memoryLimit) + ); + } } diff --git a/src/Data/CityData.php b/src/Data/CityData.php index 779bf21..876a0ea 100644 --- a/src/Data/CityData.php +++ b/src/Data/CityData.php @@ -8,13 +8,10 @@ class CityData implements ArraySerializableInterface { - protected ?string $name; - protected ?string $error; - - public function __construct(?string $name = null, ?string $error = null) - { - $this->name = $name; - $this->error = $error; + public function __construct( + private ?string $name = null, + private ?string $error = null, + ) { } public function getName(): ?string @@ -25,6 +22,7 @@ public function getName(): ?string public function setName(?string $name): self { $this->name = $name; + return $this; } @@ -36,6 +34,7 @@ public function getError(): ?string public function setError(?string $error): self { $this->error = $error; + return $this; } diff --git a/src/Data/ContinentData.php b/src/Data/ContinentData.php index 539c1a4..13cca36 100644 --- a/src/Data/ContinentData.php +++ b/src/Data/ContinentData.php @@ -8,18 +8,11 @@ class ContinentData implements ArraySerializableInterface { - protected ?string $code; - protected ?string $name; - protected ?string $error; - public function __construct( - ?string $code = null, - ?string $name = null, - ?string $error = null + private ?string $code = null, + private ?string $name = null, + private ?string $error = null, ) { - $this->code = $code; - $this->name = $name; - $this->error = $error; } public function getCode(): ?string diff --git a/src/Data/CountryData.php b/src/Data/CountryData.php index 35e0234..3e0a76a 100644 --- a/src/Data/CountryData.php +++ b/src/Data/CountryData.php @@ -8,21 +8,12 @@ class CountryData implements ArraySerializableInterface { - protected ?bool $isEuMember; - protected ?string $isoCode; - protected ?string $name; - protected ?string $error; - public function __construct( - ?bool $isEuMember = false, - ?string $isoCode = null, - ?string $name = null, - ?string $error = null + private ?bool $isEuMember = false, + private ?string $isoCode = null, + private ?string $name = null, + private ?string $error = null, ) { - $this->isEuMember = $isEuMember; - $this->isoCode = $isoCode; - $this->name = $name; - $this->error = $error; } public function getIsEuMember(): ?bool diff --git a/src/Data/LocationData.php b/src/Data/LocationData.php index 381ab73..4feb5bf 100644 --- a/src/Data/LocationData.php +++ b/src/Data/LocationData.php @@ -8,30 +8,15 @@ class LocationData implements ArraySerializableInterface { - protected ?ContinentData $continent; - protected ?CountryData $country; - protected ?CityData $city; - protected ?OrganizationData $organization; - protected ?float $latitude; - protected ?float $longitude; - protected ?string $timeZone; - public function __construct( - ?ContinentData $continent = null, - ?CountryData $country = null, - ?CityData $city = null, - ?OrganizationData $organization = null, - ?float $latitude = null, - ?float $longitude = null, - ?string $timeZone = null + private ?ContinentData $continent = null, + private ?CountryData $country = null, + private ?CityData $city = null, + private ?OrganizationData $organization = null, + private ?float $latitude = null, + private ?float $longitude = null, + private ?string $timeZone = null, ) { - $this->continent = $continent; - $this->country = $country; - $this->city = $city; - $this->organization = $organization; - $this->latitude = $latitude; - $this->longitude = $longitude; - $this->timeZone = $timeZone; } public function getContinent(): ?ContinentData diff --git a/src/Data/OrganizationData.php b/src/Data/OrganizationData.php index 7970c2b..48182cd 100644 --- a/src/Data/OrganizationData.php +++ b/src/Data/OrganizationData.php @@ -8,18 +8,11 @@ class OrganizationData implements ArraySerializableInterface { - protected ?int $asn; - protected ?string $name; - protected ?string $error; - public function __construct( - ?int $asn = null, - ?string $name = null, - ?string $error = null + private ?int $asn = null, + private ?string $name = null, + private ?string $error = null, ) { - $this->asn = $asn; - $this->name = $name; - $this->error = $error; } public function getAsn(): ?int @@ -54,6 +47,7 @@ public function getError(): ?string public function setError(?string $error): self { $this->error = $error; + return $this; } diff --git a/src/Extractor.php b/src/Extractor.php new file mode 100644 index 0000000..45e43a6 --- /dev/null +++ b/src/Extractor.php @@ -0,0 +1,29 @@ +has(LocationServiceInterface::class) - ? $container->get(LocationServiceInterface::class) - : null; - if (! $locationService instanceof LocationServiceInterface) { + if (! $container->has(LocationServiceInterface::class)) { throw new Exception(self::MESSAGE_MISSING_LOCATION_SERVICE); } - return new GeoIpCommand($locationService); + return new GeoIpCommand( + $container->get(LocationServiceInterface::class) + ); } } diff --git a/src/FileSystem.php b/src/FileSystem.php new file mode 100644 index 0000000..1baf7d3 --- /dev/null +++ b/src/FileSystem.php @@ -0,0 +1,44 @@ +exists($path)) { + return; + } + + mkdir($path, $mode, true); + } + + public function remove(string $path): void + { + if (! $this->exists($path)) { + return; + } + + if (is_file($path)) { + unlink($path); + } + + if (is_dir($path)) { + rmdir($path); + } + } +} diff --git a/src/Service/LocationService.php b/src/Service/LocationService.php index 1990b83..9cb392a 100644 --- a/src/Service/LocationService.php +++ b/src/Service/LocationService.php @@ -16,15 +16,19 @@ use MaxMind\Db\Reader\Metadata; use Throwable; +use function array_key_exists; use function array_pop; use function basename; +use function date; use function explode; use function file_exists; use function filter_var; use function implode; use function rtrim; use function sprintf; +use function str_replace; use function sys_get_temp_dir; +use function trim; use const FILTER_FLAG_IPV4; use const FILTER_FLAG_IPV6; @@ -121,7 +125,7 @@ public function setAsnReader(Reader $asnReader): self public function databaseExists(string $database): bool { - $path = $this->getDatabasePath($database); + $path = $this->getRealFilePath($database); return file_exists($path); } @@ -178,14 +182,16 @@ public function getDatabaseMetadata(string $database): ?Metadata } } - public function getDatabasePath(string $database): string + public function getRealFilePath(string $database): string { return sprintf('%s/%s.mmdb', rtrim($this->config['targetDir'], '/'), $database); } - public function getDatabaseSource(string $database): string + public function getTempFilePath(string $database): string { - return sprintf('%s/%s', sys_get_temp_dir(), basename($this->config['databases'][$database]['source'])); + return $this->parsePlaceholders( + sprintf('%s/%s', sys_get_temp_dir(), basename($this->config['databases'][$database]['source'])) + ); } /** @@ -194,10 +200,22 @@ public function getDatabaseSource(string $database): string public function getDatabaseReader(string $database): Reader { return new Reader( - $this->getDatabasePath($database) + $this->getRealFilePath($database) ); } + /** + * @throws Exception + */ + public function getDatabaseSourceUrl(string $database): string + { + if (! array_key_exists($database, $this->config['databases'])) { + throw new Exception('Database source URL not found'); + } + + return $this->parsePlaceholders($this->config['databases'][$database]['source']); + } + /** * @throws Exception * @throws AddressNotFoundException @@ -263,4 +281,12 @@ public function obfuscateIpAddress(string $ipAddress): string return implode($separator, $parts); } + + private function parsePlaceholders(string $subject): string + { + $subject = str_replace('{year}', date('Y'), $subject); + $subject = str_replace('{month}', date('m'), $subject); + + return trim($subject); + } } diff --git a/src/Service/LocationServiceInterface.php b/src/Service/LocationServiceInterface.php index e8d4261..975c13c 100644 --- a/src/Service/LocationServiceInterface.php +++ b/src/Service/LocationServiceInterface.php @@ -9,6 +9,7 @@ use Dot\GeoIP\Data\CountryData; use Dot\GeoIP\Data\LocationData; use Dot\GeoIP\Data\OrganizationData; +use Exception; use GeoIp2\Database\Reader; use MaxMind\Db\Reader\Metadata; @@ -28,12 +29,17 @@ public function getCity(string $ipAddress): CityData; public function getDatabaseMetadata(string $database): ?Metadata; - public function getDatabasePath(string $database): string; + public function getRealFilePath(string $database): string; - public function getDatabaseSource(string $database): string; + public function getTempFilePath(string $database): string; public function getDatabaseReader(string $database): Reader; + /** + * @throws Exception + */ + public function getDatabaseSourceUrl(string $database): string; + public function getLocation(string $ipAddress): LocationData; public function getOrganization(string $ipAddress): OrganizationData; diff --git a/test/Command/GeoIpCommandTest.php b/test/Command/GeoIpCommandTest.php index 361c908..28fe954 100644 --- a/test/Command/GeoIpCommandTest.php +++ b/test/Command/GeoIpCommandTest.php @@ -9,8 +9,6 @@ use Dot\GeoIP\Service\LocationServiceInterface; use DotTest\GeoIP\CommonTrait; use Exception; -use GuzzleHttp\Exception\GuzzleException; -use MaxMind\Db\Reader\InvalidDatabaseException; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -33,29 +31,28 @@ public function testCreateCommand(): void } /** - * @throws GuzzleException - * @throws InvalidDatabaseException|\PHPUnit\Framework\MockObject\Exception + * @throws \PHPUnit\Framework\MockObject\Exception + * @throws Exception */ public function testExecuteCommandWillCreateDatabaseFile(): void { $input = $this->createMock(InputInterface::class); $output = $this->createMock(OutputInterface::class); - $input->expects($this->once()) - ->method('getOption') - ->with('database') - ->willReturn( - LocationService::DATABASE_ASN - ); + $input->method('getOption')->willReturnMap([ + ['memory-limit', '256M'], + ['database', LocationService::DATABASE_ASN], + ]); $locationService = new LocationService($this->getConfig()); $command = new GeoIpCommand($locationService); $command->execute($input, $output); - $this->assertFileExists($locationService->getDatabasePath(LocationService::DATABASE_ASN)); + $this->assertFileExists($locationService->getRealFilePath(LocationService::DATABASE_ASN)); } /** * @throws \PHPUnit\Framework\MockObject\Exception + * @throws Exception */ public function testWillIdentifyValidDatabases(): void { diff --git a/test/CommonTrait.php b/test/CommonTrait.php index 29e80fd..042aa59 100644 --- a/test/CommonTrait.php +++ b/test/CommonTrait.php @@ -8,6 +8,7 @@ use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStreamDirectory; +use function date; use function file_get_contents; use function sprintf; @@ -66,17 +67,32 @@ protected function getConfig(): array protected function generateConfig(string $targetDir): array { + $year = date('Y'); + $month = date('m'); + return [ 'targetDir' => $targetDir, 'databases' => [ LocationService::DATABASE_ASN => [ - 'source' => 'https://download.db-ip.com/free/dbip-asn-lite-{year}-{month}.mmdb.gz', + 'source' => sprintf( + 'https://download.db-ip.com/free/dbip-asn-lite-%d-%d.mmdb.gz', + $year, + $month + ), ], LocationService::DATABASE_CITY => [ - 'source' => 'https://download.db-ip.com/free/dbip-city-lite-{year}-{month}.mmdb.gz', + 'source' => sprintf( + 'https://download.db-ip.com/free/dbip-city-lite-%d-%d.mmdb.gz', + $year, + $month + ), ], LocationService::DATABASE_COUNTRY => [ - 'source' => 'https://download.db-ip.com/free/dbip-country-lite-{year}-{month}.mmdb.gz', + 'source' => sprintf( + 'https://download.db-ip.com/free/dbip-country-lite-%d-%d.mmdb.gz', + $year, + $month + ), ], ], ]; diff --git a/test/Factory/GeoIpCommandFactoryTest.php b/test/Factory/GeoIpCommandFactoryTest.php index 3cd3daa..6db9dbd 100644 --- a/test/Factory/GeoIpCommandFactoryTest.php +++ b/test/Factory/GeoIpCommandFactoryTest.php @@ -21,7 +21,8 @@ class GeoIpCommandFactoryTest extends TestCase /** * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface|Exception + * @throws Exception + * @throws NotFoundExceptionInterface */ public function testFactoryWillNotCreateCommandWithoutLocationService(): void { @@ -38,7 +39,8 @@ public function testFactoryWillNotCreateCommandWithoutLocationService(): void /** * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface|Exception + * @throws Exception + * @throws NotFoundExceptionInterface */ public function testFactoryWillCreateCommand(): void { diff --git a/test/Factory/LocationServiceFactoryTest.php b/test/Factory/LocationServiceFactoryTest.php index 04a5285..6630cc3 100644 --- a/test/Factory/LocationServiceFactoryTest.php +++ b/test/Factory/LocationServiceFactoryTest.php @@ -19,7 +19,8 @@ class LocationServiceFactoryTest extends TestCase /** * @throws ContainerExceptionInterface - * @throws NotFoundExceptionInterface|Exception + * @throws Exception + * @throws NotFoundExceptionInterface */ public function testCreateService(): void { diff --git a/test/Service/LocationServiceTest.php b/test/Service/LocationServiceTest.php index d250e26..b281782 100644 --- a/test/Service/LocationServiceTest.php +++ b/test/Service/LocationServiceTest.php @@ -30,7 +30,8 @@ class LocationServiceTest extends TestCase use CommonTrait; /** - * @throws InvalidDatabaseException|\PHPUnit\Framework\MockObject\Exception + * @throws InvalidDatabaseException + * @throws \PHPUnit\Framework\MockObject\Exception */ public function testAccessors(): void { @@ -192,20 +193,17 @@ public function testGetDatabaseMetadataForInvalidDatabase(): void public function testGetDatabasePath(): void { $locationService = new LocationService($this->getConfig()); - $path = $locationService->getDatabasePath(LocationService::DATABASE_ASN); + $path = $locationService->getRealFilePath(LocationService::DATABASE_ASN); $this->assertSame( sprintf('%s/%s.mmdb', $this->getConfig()['targetDir'], LocationService::DATABASE_ASN), $path ); } - /** - * @throws InvalidDatabaseException - */ public function testGetDatabaseSource(): void { $locationService = new LocationService($this->getConfig()); - $source = $locationService->getDatabaseSource(LocationService::DATABASE_ASN); + $source = $locationService->getTempFilePath(LocationService::DATABASE_ASN); $this->assertSame( sprintf( '%s/%s', @@ -246,7 +244,8 @@ public function testGetDatabaseReaderThrowsExceptionOnInvalidDatabase(): void /** * @throws AddressNotFoundException - * @throws InvalidDatabaseException|\PHPUnit\Framework\MockObject\Exception + * @throws InvalidDatabaseException + * @throws \PHPUnit\Framework\MockObject\Exception */ public function testGetLocation(): void { From 8f0651fcf6afebc60bcce549e72520212df036dd Mon Sep 17 00:00:00 2001 From: alexmerlin Date: Tue, 2 Dec 2025 11:25:55 +0200 Subject: [PATCH 2/3] Issue #50: Major refactoring Added option to increase memory limit Signed-off-by: alexmerlin --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c51de70..c4cff38 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "mikey179/vfsstream": "^1.6.7", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", - "phpunit/phpunit": "^10.2" + "phpunit/phpunit": "^10.5.59" }, "autoload": { "psr-4": { From b60716a86b88c7ea266901a2d84dde39a322665a Mon Sep 17 00:00:00 2001 From: alexmerlin Date: Tue, 2 Dec 2025 11:54:54 +0200 Subject: [PATCH 3/3] Issue #50: Major refactoring Added option to increase memory limit Signed-off-by: alexmerlin --- docs/book/v3/manage-geolite2-database.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/book/v3/manage-geolite2-database.md b/docs/book/v3/manage-geolite2-database.md index 740434d..8230574 100644 --- a/docs/book/v3/manage-geolite2-database.md +++ b/docs/book/v3/manage-geolite2-database.md @@ -29,3 +29,14 @@ php bin/cli.php geoip:synchronize --help ``` > If you set up the synchronizer command as a cronjob, you can add the `-q|--quiet` option, and it will output data only if an error has occurred. + +## Memory limit + +By default, the synchronizer command will use up to 128MB of memory. +If it happens to need more memory, you can increase the memory limit by providing the `memory-limit` option when calling the command: + +```shell +php bin/cli.php geoip:synchronize --memory-limit 256M +``` + +You can specify the memory limit in the following formats: `128M`, `1G`, `1024M`, etc.