Passed
Pull Request — master (#258)
by Alejandro
04:00
created

DbUpdater   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Test Coverage

Coverage 97.3%

Importance

Changes 0
Metric Value
eloc 38
dl 0
loc 86
ccs 36
cts 37
cp 0.973
rs 10
c 0
b 0
f 0
wmc 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A extractDbFile() 0 13 2
A downloadDbFile() 0 12 2
A deleteTempFiles() 0 5 2
A copyNewDbFile() 0 6 2
A downloadFreshCopy() 0 9 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Shlinkio\Shlink\Common\IpGeolocation\GeoLite2;
5
6
use Fig\Http\Message\RequestMethodInterface as RequestMethod;
7
use GuzzleHttp\ClientInterface;
8
use GuzzleHttp\Exception\GuzzleException;
9
use GuzzleHttp\RequestOptions;
10
use PharData;
11
use Shlinkio\Shlink\Common\Exception\RuntimeException;
12
use Symfony\Component\Filesystem\Exception as FilesystemException;
13
use Symfony\Component\Filesystem\Filesystem;
14
use Throwable;
15
use function sprintf;
16
17
class DbUpdater implements DbUpdaterInterface
18
{
19
    private const DB_COMPRESSED_FILE = 'GeoLite2-City.tar.gz';
20
    private const DB_DECOMPRESSED_FILE = 'GeoLite2-City.mmdb';
21
22
    /**
23
     * @var ClientInterface
24
     */
25
    private $httpClient;
26
    /**
27
     * @var Filesystem
28
     */
29
    private $filesystem;
30
    /**
31
     * @var GeoLite2Options
32
     */
33
    private $options;
34
35 5
    public function __construct(ClientInterface $httpClient, Filesystem $filesystem, GeoLite2Options $options)
36
    {
37 5
        $this->httpClient = $httpClient;
38 5
        $this->filesystem = $filesystem;
39 5
        $this->options = $options;
40
    }
41
42
    /**
43
     * @throws RuntimeException
44
     */
45 5
    public function downloadFreshCopy(callable $handleProgress = null): void
46
    {
47 5
        $tempDir = $this->options->getTempDir();
48 5
        $compressedFile = sprintf('%s/%s', $tempDir, self::DB_COMPRESSED_FILE);
49
50 5
        $this->downloadDbFile($compressedFile, $handleProgress);
51 4
        $tempFullPath = $this->extractDbFile($compressedFile, $tempDir);
52 3
        $this->copyNewDbFile($tempFullPath);
53 1
        $this->deleteTempFiles([$compressedFile, $tempFullPath]);
54
    }
55
56 5
    private function downloadDbFile(string $dest, callable $handleProgress = null): void
57
    {
58
        try {
59 5
            $this->httpClient->request(RequestMethod::METHOD_GET, $this->options->getDownloadFrom(), [
60 5
                RequestOptions::SINK => $dest,
61 5
                RequestOptions::PROGRESS => $handleProgress,
62
            ]);
63 1
        } catch (Throwable | GuzzleException $e) {
64 1
            throw new RuntimeException(
65 1
                'An error occurred while trying to download a fresh copy of the GeoLite2 database',
66 1
                0,
67 1
                $e
68
            );
69
        }
70
    }
71
72 4
    private function extractDbFile(string $compressedFile, string $tempDir): string
73
    {
74
        try {
75 4
            $phar = new PharData($compressedFile);
76 3
            $internalPathToDb = sprintf('%s/%s', $phar->getBasename(), self::DB_DECOMPRESSED_FILE);
77 3
            $phar->extractTo($tempDir, $internalPathToDb, true);
78
79 3
            return sprintf('%s/%s', $tempDir, $internalPathToDb);
80 1
        } catch (Throwable $e) {
81 1
            throw new RuntimeException(
82 1
                sprintf('An error occurred while trying to extract the GeoLite2 database from %s', $compressedFile),
83 1
                0,
84 1
                $e
85
            );
86
        }
87
    }
88
89 3
    private function copyNewDbFile(string $from): void
90
    {
91
        try {
92 3
            $this->filesystem->copy($from, $this->options->getDbLocation(), true);
93 2
        } catch (FilesystemException\FileNotFoundException | FilesystemException\IOException $e) {
94 2
            throw new RuntimeException('An error occurred while trying to copy GeoLite2 db file to destination', 0, $e);
95
        }
96
    }
97
98 1
    private function deleteTempFiles(array $files): void
99
    {
100
        try {
101 1
            $this->filesystem->remove($files);
102
        } catch (FilesystemException\IOException $e) {
103
            // Ignore any error produced when trying to delete temp files
104
        }
105
    }
106
}
107