Completed
Pull Request — master (#340)
by Alejandro
05:57
created

DbUpdater   A

Complexity

Total Complexity 10

Size/Duplication

Total Lines 80
Duplicated Lines 0 %

Test Coverage

Coverage 97.3%

Importance

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

6 Methods

Rating   Name   Duplication   Size   Complexity  
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
A __construct() 0 5 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
    /** @var ClientInterface */
23
    private $httpClient;
24
    /** @var Filesystem */
25
    private $filesystem;
26
    /** @var GeoLite2Options */
27
    private $options;
28
29 5
    public function __construct(ClientInterface $httpClient, Filesystem $filesystem, GeoLite2Options $options)
30
    {
31 5
        $this->httpClient = $httpClient;
32 5
        $this->filesystem = $filesystem;
33 5
        $this->options = $options;
34
    }
35
36
    /**
37
     * @throws RuntimeException
38
     */
39 5
    public function downloadFreshCopy(callable $handleProgress = null): void
40
    {
41 5
        $tempDir = $this->options->getTempDir();
42 5
        $compressedFile = sprintf('%s/%s', $tempDir, self::DB_COMPRESSED_FILE);
43
44 5
        $this->downloadDbFile($compressedFile, $handleProgress);
45 4
        $tempFullPath = $this->extractDbFile($compressedFile, $tempDir);
46 3
        $this->copyNewDbFile($tempFullPath);
47 1
        $this->deleteTempFiles([$compressedFile, $tempFullPath]);
48
    }
49
50 5
    private function downloadDbFile(string $dest, callable $handleProgress = null): void
51
    {
52
        try {
53 5
            $this->httpClient->request(RequestMethod::METHOD_GET, $this->options->getDownloadFrom(), [
54 5
                RequestOptions::SINK => $dest,
55 5
                RequestOptions::PROGRESS => $handleProgress,
56
            ]);
57 1
        } catch (Throwable | GuzzleException $e) {
58 1
            throw new RuntimeException(
59 1
                'An error occurred while trying to download a fresh copy of the GeoLite2 database',
60 1
                0,
61 1
                $e
62
            );
63
        }
64
    }
65
66 4
    private function extractDbFile(string $compressedFile, string $tempDir): string
67
    {
68
        try {
69 4
            $phar = new PharData($compressedFile);
70 3
            $internalPathToDb = sprintf('%s/%s', $phar->getBasename(), self::DB_DECOMPRESSED_FILE);
71 3
            $phar->extractTo($tempDir, $internalPathToDb, true);
72
73 3
            return sprintf('%s/%s', $tempDir, $internalPathToDb);
74 1
        } catch (Throwable $e) {
75 1
            throw new RuntimeException(
76 1
                sprintf('An error occurred while trying to extract the GeoLite2 database from %s', $compressedFile),
77 1
                0,
78 1
                $e
79
            );
80
        }
81
    }
82
83 3
    private function copyNewDbFile(string $from): void
84
    {
85
        try {
86 3
            $this->filesystem->copy($from, $this->options->getDbLocation(), true);
87 2
        } catch (FilesystemException\FileNotFoundException | FilesystemException\IOException $e) {
88 2
            throw new RuntimeException('An error occurred while trying to copy GeoLite2 db file to destination', 0, $e);
89
        }
90
    }
91
92 1
    private function deleteTempFiles(array $files): void
93
    {
94
        try {
95 1
            $this->filesystem->remove($files);
96
        } catch (FilesystemException\IOException $e) {
97
            // Ignore any error produced when trying to delete temp files
98
        }
99
    }
100
}
101