Skip to content

Commit d1ff53b

Browse files
authored
Merge pull request #51 from dotkernel/issue-50
Issue #50: Major refactoring & Added option to increase memory limit
2 parents 7094783 + b60716a commit d1ff53b

24 files changed

+284
-171
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Documentation is available at: https://docs.dotkernel.org/dot-geoip/.
1111
## Badges
1212

1313
![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-geoip)
14-
![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.10.0)
14+
![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.11.0)
1515

1616
[![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/issues)
1717
[![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/network)

SECURITY.md

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,26 @@
44

55
| Version | Supported | PHP Version |
66
|---------|--------------------|------------------------------------------------------------------------------------------------------------|
7-
| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.10.0) |
7+
| 3.x | :white_check_mark: | ![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.11.0) |
88
| <= 2.x | :x: | |
99

1010
## Reporting Potential Security Issues
1111

12-
If you have encountered a potential security vulnerability in this project,
13-
please report it to us at <[email protected]>. We will work with you to
14-
verify the vulnerability and patch it.
12+
If you have encountered a potential security vulnerability in this project, please report it to us at <[email protected]>.
13+
We will work with you to verify the vulnerability and patch it.
1514

1615
When reporting issues, please provide the following information:
1716

1817
- Component(s) affected
1918
- A description indicating how to reproduce the issue
2019
- A summary of the security vulnerability and impact
2120

22-
We request that you contact us via the email address above and give the
23-
project contributors a chance to resolve the vulnerability and issue a new
24-
release prior to any public exposure; this helps protect the project's
25-
users, and provides them with a chance to upgrade and/or update in order to
26-
protect their applications.
27-
21+
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;
22+
this helps protect the project's users and provides them with a chance to upgrade and/or update to protect their applications.
2823

2924
## Policy
3025

3126
If we verify a reported security vulnerability, our policy is:
3227

33-
- We will patch the current release branch, as well as the immediate prior minor
34-
release branch.
35-
36-
- After patching the release branches, we will immediately issue new security
37-
fix releases for each patched release branch.
38-
28+
- We will patch the current release branch, as well as the immediate prior minor release branch.
29+
- After patching the release branches, we will immediately issue new security fix releases for each patched release branch.

composer.json

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,18 @@
2323
},
2424
"require": {
2525
"php": "~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
26+
"ext-curl": "*",
27+
"ext-zlib": "*",
2628
"dotkernel/dot-cli": "^3.5",
2729
"geoip2/geoip2": "^3.0",
28-
"guzzlehttp/guzzle": "^7.8",
29-
"laminas/laminas-filter": "^2.34",
30-
"psr/container": "^1.1 || ^2.0.0",
31-
"symfony/filesystem": "^7.0"
30+
"psr/container": "^1.1 || ^2.0.0"
3231
},
3332
"require-dev": {
3433
"laminas/laminas-coding-standard": "^3.0",
3534
"mikey179/vfsstream": "^1.6.7",
3635
"phpstan/phpstan": "^2.1",
3736
"phpstan/phpstan-phpunit": "^2.0",
38-
"phpunit/phpunit": "^10.2"
37+
"phpunit/phpunit": "^10.5.59"
3938
},
4039
"autoload": {
4140
"psr-4": {

docs/book/v3/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ Dot\GeoIP\ConfigProvider::class,
1111
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:
1212

1313
```php
14-
Dot\GeoIP\Command\GeoIpCommand::getDefaultName() => Dot\GeoIP\Command\GeoIpCommand::class,
14+
\Dot\GeoIP\Command\GeoIpCommand::$defaultName => \Dot\GeoIP\Command\GeoIpCommand::class,
1515
```

docs/book/v3/manage-geolite2-database.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@ city: n/a -> 2021-07-01 02:09:20
2222
country: n/a -> 2021-07-01 02:05:12
2323
```
2424

25-
Get help for this command by running `php bin/cli.php help geoip:synchronize`.
25+
Get help for this command by running:
26+
27+
```shell
28+
php bin/cli.php geoip:synchronize --help
29+
```
2630

2731
> 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.
32+
33+
## Memory limit
34+
35+
By default, the synchronizer command will use up to 128MB of memory.
36+
If it happens to need more memory, you can increase the memory limit by providing the `memory-limit` option when calling the command:
37+
38+
```shell
39+
php bin/cli.php geoip:synchronize --memory-limit 256M
40+
```
41+
42+
You can specify the memory limit in the following formats: `128M`, `1G`, `1024M`, etc.

docs/book/v3/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Dotkernel component to provide geographical details about an IP address.
77
## Badges
88

99
![OSS Lifecycle](https://img.shields.io/osslifecycle/dotkernel/dot-geoip)
10-
![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.10.0)
10+
![PHP from Packagist (specify version)](https://img.shields.io/packagist/php-v/dotkernel/dot-geoip/3.11.0)
1111

1212
[![GitHub issues](https://img.shields.io/github/issues/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/issues)
1313
[![GitHub forks](https://img.shields.io/github/forks/dotkernel/dot-geoip)](https://github.com/dotkernel/dot-geoip/network)

docs/book/v3/usage.md

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,13 @@
33
Below is an example implementation of using DotGeoip to retrieve information about an IP address.
44

55
```php
6-
<?php
7-
8-
declare(strict_types=1);
9-
10-
namespace Api\Example\Service;
11-
12-
use Dot\GeoIP\Service\LocationServiceInterface;
13-
use Throwable;
14-
15-
/**
16-
* Class ExampleService
17-
* @package Api\Example\Service
18-
*/
196
class ExampleService
207
{
21-
protected LocationServiceInterface $locationService;
22-
23-
/**
24-
* ExampleService constructor.
25-
* @param LocationServiceInterface $locationService
26-
*/
27-
public function __construct(LocationServiceInterface $locationService)
28-
{
29-
$this->locationService = $locationService;
8+
public function __construct(
9+
private \Dot\GeoIP\Service\LocationServiceInterface $locationService
10+
) {
3011
}
3112

32-
/**
33-
* @param string $ipAddress
34-
* @return object
35-
*/
3613
public function myMethod(string $ipAddress): object
3714
{
3815
return $this->locationService->getCountry($ipAddress); // Returns an instance of Dot\GeoIP\Data\CountryData

src/Client.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Dot\GeoIP;
6+
7+
use function curl_exec;
8+
use function curl_init;
9+
use function curl_setopt_array;
10+
use function fclose;
11+
use function fopen;
12+
13+
use const CURLOPT_FAILONERROR;
14+
use const CURLOPT_FILE;
15+
16+
final readonly class Client
17+
{
18+
public function get(string $url, string $target): void
19+
{
20+
$fp = fopen($target, 'wb');
21+
$ch = curl_init($url);
22+
23+
curl_setopt_array($ch, [
24+
CURLOPT_FILE => $fp,
25+
CURLOPT_FAILONERROR => true,
26+
]);
27+
28+
curl_exec($ch);
29+
fclose($fp);
30+
}
31+
}

src/Command/GeoIpCommand.php

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,27 @@
44

55
namespace Dot\GeoIP\Command;
66

7+
use Dot\GeoIP\Client;
8+
use Dot\GeoIP\Extractor;
9+
use Dot\GeoIP\FileSystem;
710
use Dot\GeoIP\Service\LocationService;
811
use Dot\GeoIP\Service\LocationServiceInterface;
912
use Exception;
10-
use GuzzleHttp\Client;
11-
use GuzzleHttp\Exception\GuzzleException;
12-
use GuzzleHttp\RequestOptions;
13-
use Laminas\Filter\Decompress;
13+
use InvalidArgumentException;
1414
use MaxMind\Db\Reader\Metadata;
1515
use Symfony\Component\Console\Attribute\AsCommand;
1616
use Symfony\Component\Console\Command\Command;
1717
use Symfony\Component\Console\Input\InputInterface;
1818
use Symfony\Component\Console\Input\InputOption;
1919
use Symfony\Component\Console\Output\OutputInterface;
20-
use Symfony\Component\Filesystem\Filesystem;
2120

2221
use function array_key_exists;
2322
use function array_keys;
2423
use function date;
2524
use function implode;
25+
use function ini_set;
26+
use function preg_match;
2627
use function sprintf;
27-
use function str_replace;
28-
use function trim;
2928

3029
#[AsCommand(
3130
name: 'geoip:synchronize',
@@ -35,11 +34,14 @@ class GeoIpCommand extends Command
3534
{
3635
/** @var string $defaultName */
3736
public static $defaultName = 'geoip:synchronize';
37+
private FileSystem $fileSystem;
3838

3939
public function __construct(
4040
protected LocationServiceInterface $locationService,
4141
) {
4242
parent::__construct(self::$defaultName);
43+
44+
$this->fileSystem = new FileSystem();
4345
}
4446

4547
public function configure(): void
@@ -56,41 +58,44 @@ public function configure(): void
5658
implode(', ', array_keys(LocationService::DATABASES))
5759
),
5860
LocationService::DATABASE_ALL
61+
)
62+
->addOption(
63+
'memory-limit',
64+
'm',
65+
InputOption::VALUE_OPTIONAL,
66+
'Memory limit for decompression. Examples: 256M, 1G',
67+
'128M'
5968
);
6069
}
6170

6271
/**
63-
* @throws GuzzleException
6472
* @throws Exception
6573
*/
6674
public function execute(InputInterface $input, OutputInterface $output): int
6775
{
68-
$fileSystem = new Filesystem();
69-
if (! $fileSystem->exists($this->locationService->getConfig('targetDir'))) {
70-
$fileSystem->mkdir($this->locationService->getConfig('targetDir'));
71-
}
76+
$memoryLimit = $input->getOption('memory-limit');
77+
$this->validateMemoryLimit($memoryLimit);
78+
ini_set('memory_limit', $memoryLimit);
79+
80+
$this->fileSystem->mkdir($this->locationService->getConfig('targetDir'));
7281

7382
$database = $input->getOption('database') ?? LocationService::DATABASE_ALL;
7483
$databases = $this->identifyDatabases($database);
7584
foreach ($databases as $database) {
76-
$sourcePath = $this->locationService->getDatabaseSource($database);
77-
$targetPath = $this->locationService->getDatabasePath($database);
85+
$tempFilePath = $this->locationService->getTempFilePath($database);
86+
$realFilePath = $this->locationService->getRealFilePath($database);
7887

7988
$oldVersion = 'n/a';
8089
$oldMetadata = $this->locationService->getDatabaseMetadata($database);
8190
if ($oldMetadata instanceof Metadata) {
8291
$oldVersion = date('Y-m-d H:i:s', $oldMetadata->buildEpoch);
8392
}
8493

85-
$url = trim($this->locationService->getConfig('databases')[$database]['source']);
86-
$url = str_replace('{year}', date('Y'), $url);
87-
$url = str_replace('{month}', date('m'), $url);
88-
(new Client())->get($url, [RequestOptions::SINK => $sourcePath]);
94+
(new Client())->get($this->locationService->getDatabaseSourceUrl($database), $tempFilePath);
8995

90-
$content = (new Decompress())->getAdapter()->decompress($sourcePath);
91-
$fileSystem->remove($targetPath);
92-
$fileSystem->dumpFile($targetPath, $content);
93-
$fileSystem->remove($sourcePath);
96+
$this->fileSystem->remove($realFilePath);
97+
(new Extractor())->extract($tempFilePath, $realFilePath);
98+
$this->fileSystem->remove($tempFilePath);
9499

95100
$newVersion = 'n/a';
96101
$newMetadata = $this->locationService->getDatabaseMetadata($database);
@@ -128,4 +133,18 @@ public function identifyDatabases(string $identifier): array
128133

129134
return [$identifier];
130135
}
136+
137+
/**
138+
* @throws InvalidArgumentException
139+
*/
140+
private function validateMemoryLimit(string $memoryLimit): void
141+
{
142+
if (preg_match('/^(\d+)([MG])$/', $memoryLimit) === 1) {
143+
return;
144+
}
145+
146+
throw new InvalidArgumentException(
147+
sprintf('Invalid memory limit: %s', $memoryLimit)
148+
);
149+
}
131150
}

src/Data/CityData.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88

99
class CityData implements ArraySerializableInterface
1010
{
11-
protected ?string $name;
12-
protected ?string $error;
13-
14-
public function __construct(?string $name = null, ?string $error = null)
15-
{
16-
$this->name = $name;
17-
$this->error = $error;
11+
public function __construct(
12+
private ?string $name = null,
13+
private ?string $error = null,
14+
) {
1815
}
1916

2017
public function getName(): ?string
@@ -25,6 +22,7 @@ public function getName(): ?string
2522
public function setName(?string $name): self
2623
{
2724
$this->name = $name;
25+
2826
return $this;
2927
}
3028

@@ -36,6 +34,7 @@ public function getError(): ?string
3634
public function setError(?string $error): self
3735
{
3836
$this->error = $error;
37+
3938
return $this;
4039
}
4140

0 commit comments

Comments
 (0)