download_database()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 16
nc 3
nop 1
dl 0
loc 31
rs 9.7333
c 0
b 0
f 0
1
<?php
2
/**
3
 * The MaxMind database service class file.
4
 *
5
 */
6
7
defined( 'ABSPATH' ) || exit;
8
9
/**
10
 * The service class responsible for interacting with MaxMind databases.
11
 *
12
 * @since 1.0.19
13
 */
14
class GetPaid_MaxMind_Database_Service {
15
16
	/**
17
	 * The name of the MaxMind database to utilize.
18
	 */
19
	const DATABASE = 'GeoLite2-Country';
20
21
	/**
22
	 * The extension for the MaxMind database.
23
	 */
24
	const DATABASE_EXTENSION = '.mmdb';
25
26
	/**
27
	 * A prefix for the MaxMind database filename.
28
	 *
29
	 * @var string
30
	 */
31
	private $database_prefix;
32
33
	/**
34
	 * Class constructor.
35
	 *
36
	 * @param string|null $database_prefix A prefix for the MaxMind database filename.
37
	 */
38
	public function __construct( $database_prefix ) {
39
		$this->database_prefix = $database_prefix;
40
	}
41
42
	/**
43
	 * Fetches the path that the database should be stored.
44
	 *
45
	 * @return string The local database path.
46
	 */
47
	public function get_database_path() {
48
		$uploads_dir = wp_upload_dir();
49
50
		$database_path = trailingslashit( $uploads_dir['basedir'] ) . 'invoicing/';
51
		if ( ! empty( $this->database_prefix ) ) {
52
			$database_path .= $this->database_prefix . '-';
53
		}
54
		$database_path .= self::DATABASE . self::DATABASE_EXTENSION;
55
56
		// Filter the geolocation database storage path.
57
		return apply_filters( 'getpaid_maxmind_geolocation_database_path', $database_path );
58
	}
59
60
	/**
61
	 * Fetches the database from the MaxMind service.
62
	 *
63
	 * @param string $license_key The license key to be used when downloading the database.
64
	 * @return string|WP_Error The path to the database file or an error if invalid.
65
	 */
66
	public function download_database( $license_key ) {
67
68
		$download_uri = add_query_arg(
69
			array(
70
				'edition_id'  => self::DATABASE,
71
				'license_key' => urlencode( wpinv_clean( $license_key ) ),
72
				'suffix'      => 'tar.gz',
73
			),
74
			'https://download.maxmind.com/app/geoip_download'
75
		);
76
77
		// Needed for the download_url call right below.
78
		require_once ABSPATH . 'wp-admin/includes/file.php';
79
80
		$tmp_archive_path = download_url( esc_url_raw( $download_uri ) );
81
82
		if ( is_wp_error( $tmp_archive_path ) ) {
83
			// Transform the error into something more informative.
84
			$error_data = $tmp_archive_path->get_error_data();
85
			if ( isset( $error_data['code'] ) && $error_data['code'] == 401 ) {
86
				return new WP_Error(
87
					'getpaid_maxmind_geolocation_database_license_key',
88
					__( 'The MaxMind license key is invalid. If you have recently created this key, you may need to wait for it to become active.', 'invoicing' )
89
				);
90
			}
91
92
			return new WP_Error( 'getpaid_maxmind_geolocation_database_download', __( 'Failed to download the MaxMind database.', 'invoicing' ) );
93
		}
94
95
		// Extract the database from the archive.
96
		return $this->extract_downloaded_database( $tmp_archive_path );
0 ignored issues
show
Bug introduced by
$tmp_archive_path of type WP_Error is incompatible with the type string expected by parameter $tmp_archive_path of GetPaid_MaxMind_Database...t_downloaded_database(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

96
		return $this->extract_downloaded_database( /** @scrutinizer ignore-type */ $tmp_archive_path );
Loading history...
97
98
	}
99
100
	/**
101
	 * Extracts the downloaded database.
102
	 *
103
	 * @param string $tmp_archive_path The database archive path.
104
	 * @return string|WP_Error The path to the database file or an error if invalid.
105
	 */
106
	protected function extract_downloaded_database( $tmp_archive_path ) {
107
108
		// Extract the database from the archive.
109
		$tmp_database_path = '';
110
111
		try {
112
113
			$file              = new PharData( $tmp_archive_path );
114
			$tmp_database_path = trailingslashit( dirname( $tmp_archive_path ) ) . trailingslashit( $file->current()->getFilename() ) . self::DATABASE . self::DATABASE_EXTENSION;
115
116
			$file->extractTo(
117
				dirname( $tmp_archive_path ),
118
				trailingslashit( $file->current()->getFilename() ) . self::DATABASE . self::DATABASE_EXTENSION,
119
				true
120
			);
121
122
		} catch ( Exception $exception ) {
123
			return new WP_Error( 'invoicing_maxmind_geolocation_database_archive', $exception->getMessage() );
124
		} finally {
125
			// Remove the archive since we only care about a single file in it.
126
			unlink( $tmp_archive_path );
127
		}
128
129
		return $tmp_database_path;
130
	}
131
132
	/**
133
	 * Fetches the ISO country code associated with an IP address.
134
	 *
135
	 * @param string $ip_address The IP address to find the country code for.
136
	 * @return string The country code for the IP address, or empty if not found.
137
	 */
138
	public function get_iso_country_code_for_ip( $ip_address ) {
139
		$country_code = '';
140
141
		if ( ! class_exists( 'MaxMind\Db\Reader' ) ) {
142
			return $country_code;
143
		}
144
145
		$database_path = $this->get_database_path();
146
		if ( ! file_exists( $database_path ) ) {
147
			return $country_code;
148
		}
149
150
		try {
151
			$reader = new MaxMind\Db\Reader( $database_path );
152
			$data   = $reader->get( $ip_address );
153
154
			if ( isset( $data['country']['iso_code'] ) ) {
155
				$country_code = $data['country']['iso_code'];
156
			}
157
158
			$reader->close();
159
		} catch ( Exception $e ) {
160
			wpinv_error_log( $e->getMessage(), 'SOURCE: MaxMind GeoLocation' );
161
		}
162
163
		return $country_code;
164
	}
165
166
}
167