Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace GeoIp2\Database;
6
 
7
use GeoIp2\Exception\AddressNotFoundException;
8
use GeoIp2\Model\AnonymousIp;
9
use GeoIp2\Model\Asn;
10
use GeoIp2\Model\City;
11
use GeoIp2\Model\ConnectionType;
12
use GeoIp2\Model\Country;
13
use GeoIp2\Model\Domain;
14
use GeoIp2\Model\Enterprise;
15
use GeoIp2\Model\Isp;
16
use GeoIp2\ProviderInterface;
17
use MaxMind\Db\Reader as DbReader;
18
use MaxMind\Db\Reader\InvalidDatabaseException;
19
use MaxMind\Db\Reader\Metadata;
20
 
21
/**
22
 * Instances of this class provide a reader for the GeoIP2 database format.
23
 * IP addresses can be looked up using the database specific methods.
24
 *
25
 * ## Usage ##
26
 *
27
 * The basic API for this class is the same for every database. First, you
28
 * create a reader object, specifying a file name. You then call the method
29
 * corresponding to the specific database, passing it the IP address you want
30
 * to look up.
31
 *
32
 * If the request succeeds, the method call will return a model class for
33
 * the method you called. This model in turn contains multiple record classes,
34
 * each of which represents part of the data returned by the database. If
35
 * the database does not contain the requested information, the attributes
36
 * on the record class will have a `null` value.
37
 *
38
 * If the address is not in the database, an
39
 * {@link \GeoIp2\Exception\AddressNotFoundException} exception will be
40
 * thrown. If an invalid IP address is passed to one of the methods, a
41
 * SPL {@link \InvalidArgumentException} will be thrown. If the database is
42
 * corrupt or invalid, a {@link \MaxMind\Db\Reader\InvalidDatabaseException}
43
 * will be thrown.
44
 */
45
class Reader implements ProviderInterface
46
{
47
    private DbReader $dbReader;
48
 
49
    private string $dbType;
50
 
51
    /**
52
     * @var array<string>
53
     */
54
    private array $locales;
55
 
56
    /**
57
     * Constructor.
58
     *
59
     * @param string        $filename the path to the GeoIP2 database file
60
     * @param array<string> $locales  list of locale codes to use in name property
61
     *                                from most preferred to least preferred
62
     *
63
     * @throws InvalidDatabaseException if the database is corrupt or invalid
64
     */
65
    public function __construct(
66
        string $filename,
67
        array $locales = ['en']
68
    ) {
69
        $this->dbReader = new DbReader($filename);
70
        $this->dbType = $this->dbReader->metadata()->databaseType;
71
        $this->locales = $locales;
72
    }
73
 
74
    /**
75
     * This method returns a GeoIP2 City model.
76
     *
77
     * @param string $ipAddress an IPv4 or IPv6 address as a string
78
     *
79
     * @throws AddressNotFoundException if the address is not in the database
80
     * @throws InvalidDatabaseException if the database is corrupt or invalid
81
     */
82
    public function city(string $ipAddress): City
83
    {
84
        return $this->modelFor(City::class, 'City', $ipAddress);
85
    }
86
 
87
    /**
88
     * This method returns a GeoIP2 Country model.
89
     *
90
     * @param string $ipAddress an IPv4 or IPv6 address as a string
91
     *
92
     * @throws AddressNotFoundException if the address is not in the database
93
     * @throws InvalidDatabaseException if the database is corrupt or invalid
94
     */
95
    public function country(string $ipAddress): Country
96
    {
97
        return $this->modelFor(Country::class, 'Country', $ipAddress);
98
    }
99
 
100
    /**
101
     * This method returns a GeoIP2 Anonymous IP model.
102
     *
103
     * @param string $ipAddress an IPv4 or IPv6 address as a string
104
     *
105
     * @throws AddressNotFoundException if the address is not in the database
106
     * @throws InvalidDatabaseException if the database is corrupt or invalid
107
     */
108
    public function anonymousIp(string $ipAddress): AnonymousIp
109
    {
110
        return $this->flatModelFor(
111
            AnonymousIp::class,
112
            'GeoIP2-Anonymous-IP',
113
            $ipAddress
114
        );
115
    }
116
 
117
    /**
118
     * This method returns a GeoLite2 ASN model.
119
     *
120
     * @param string $ipAddress an IPv4 or IPv6 address as a string
121
     *
122
     * @throws AddressNotFoundException if the address is not in the database
123
     * @throws InvalidDatabaseException if the database is corrupt or invalid
124
     */
125
    public function asn(string $ipAddress): Asn
126
    {
127
        return $this->flatModelFor(
128
            Asn::class,
129
            'GeoLite2-ASN',
130
            $ipAddress
131
        );
132
    }
133
 
134
    /**
135
     * This method returns a GeoIP2 Connection Type model.
136
     *
137
     * @param string $ipAddress an IPv4 or IPv6 address as a string
138
     *
139
     * @throws AddressNotFoundException if the address is not in the database
140
     * @throws InvalidDatabaseException if the database is corrupt or invalid
141
     */
142
    public function connectionType(string $ipAddress): ConnectionType
143
    {
144
        return $this->flatModelFor(
145
            ConnectionType::class,
146
            'GeoIP2-Connection-Type',
147
            $ipAddress
148
        );
149
    }
150
 
151
    /**
152
     * This method returns a GeoIP2 Domain model.
153
     *
154
     * @param string $ipAddress an IPv4 or IPv6 address as a string
155
     *
156
     * @throws AddressNotFoundException if the address is not in the database
157
     * @throws InvalidDatabaseException if the database is corrupt or invalid
158
     */
159
    public function domain(string $ipAddress): Domain
160
    {
161
        return $this->flatModelFor(
162
            Domain::class,
163
            'GeoIP2-Domain',
164
            $ipAddress
165
        );
166
    }
167
 
168
    /**
169
     * This method returns a GeoIP2 Enterprise model.
170
     *
171
     * @param string $ipAddress an IPv4 or IPv6 address as a string
172
     *
173
     * @throws AddressNotFoundException if the address is not in the database
174
     * @throws InvalidDatabaseException if the database is corrupt or invalid
175
     */
176
    public function enterprise(string $ipAddress): Enterprise
177
    {
178
        return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress);
179
    }
180
 
181
    /**
182
     * This method returns a GeoIP2 ISP model.
183
     *
184
     * @param string $ipAddress an IPv4 or IPv6 address as a string
185
     *
186
     * @throws AddressNotFoundException if the address is not in the database
187
     * @throws InvalidDatabaseException if the database is corrupt or invalid
188
     */
189
    public function isp(string $ipAddress): Isp
190
    {
191
        return $this->flatModelFor(
192
            Isp::class,
193
            'GeoIP2-ISP',
194
            $ipAddress
195
        );
196
    }
197
 
198
    private function modelFor(string $class, string $type, string $ipAddress): object
199
    {
200
        [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
201
 
202
        $record['traits']['ip_address'] = $ipAddress;
203
        $record['traits']['prefix_len'] = $prefixLen;
204
 
205
        return new $class($record, $this->locales);
206
    }
207
 
208
    private function flatModelFor(string $class, string $type, string $ipAddress): object
209
    {
210
        [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
211
 
212
        $record['ip_address'] = $ipAddress;
213
        $record['prefix_len'] = $prefixLen;
214
 
215
        return new $class($record);
216
    }
217
 
218
    /**
219
     * @return array{0:array<string, mixed>, 1:int}
220
     */
221
    private function getRecord(string $class, string $type, string $ipAddress): array
222
    {
223
        if (!str_contains($this->dbType, $type)) {
224
            $method = lcfirst((new \ReflectionClass($class))->getShortName());
225
 
226
            throw new \BadMethodCallException(
227
                "The $method method cannot be used to open a {$this->dbType} database"
228
            );
229
        }
230
        [$record, $prefixLen] = $this->dbReader->getWithPrefixLen($ipAddress);
231
        if ($record === null) {
232
            throw new AddressNotFoundException(
233
                "The address $ipAddress is not in the database."
234
            );
235
        }
236
        if (!\is_array($record)) {
237
            // This can happen on corrupt databases. Generally,
238
            // MaxMind\Db\Reader will throw a
239
            // MaxMind\Db\Reader\InvalidDatabaseException, but occasionally
240
            // the lookup may result in a record that looks valid but is not
241
            // an array. This mostly happens when the user is ignoring all
242
            // exceptions and the more frequent InvalidDatabaseException
243
            // exceptions go unnoticed.
244
            throw new InvalidDatabaseException(
245
                "Expected an array when looking up $ipAddress but received: "
246
                . \gettype($record)
247
            );
248
        }
249
 
250
        return [$record, $prefixLen];
251
    }
252
 
253
    /**
254
     * @throws \InvalidArgumentException if arguments are passed to the method
255
     * @throws \BadMethodCallException   if the database has been closed
256
     *
257
     * @return Metadata object for the database
258
     */
259
    public function metadata(): Metadata
260
    {
261
        return $this->dbReader->metadata();
262
    }
263
 
264
    /**
265
     * Closes the GeoIP2 database and returns the resources to the system.
266
     */
267
    public function close(): void
268
    {
269
        $this->dbReader->close();
270
    }
271
}