Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
declare(strict_types=1);
4
 
5
namespace GeoIp2\Database;
6
 
7
use GeoIp2\Exception\AddressNotFoundException;
8
use GeoIp2\Model\AbstractModel;
9
use GeoIp2\Model\AnonymousIp;
10
use GeoIp2\Model\Asn;
11
use GeoIp2\Model\City;
12
use GeoIp2\Model\ConnectionType;
13
use GeoIp2\Model\Country;
14
use GeoIp2\Model\Domain;
15
use GeoIp2\Model\Enterprise;
16
use GeoIp2\Model\Isp;
17
use GeoIp2\ProviderInterface;
18
use MaxMind\Db\Reader as DbReader;
19
use MaxMind\Db\Reader\InvalidDatabaseException;
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
    /**
48
     * @var DbReader
49
     */
50
    private $dbReader;
51
 
52
    /**
53
     * @var string
54
     */
55
    private $dbType;
56
 
57
    /**
58
     * @var array<string>
59
     */
60
    private $locales;
61
 
62
    /**
63
     * Constructor.
64
     *
65
     * @param string $filename the path to the GeoIP2 database file
66
     * @param array  $locales  list of locale codes to use in name property
67
     *                         from most preferred to least preferred
68
     *
69
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
70
     *                                                     is corrupt or invalid
71
     */
72
    public function __construct(
73
        string $filename,
74
        array $locales = ['en']
75
    ) {
76
        $this->dbReader = new DbReader($filename);
77
        $this->dbType = $this->dbReader->metadata()->databaseType;
78
        $this->locales = $locales;
79
    }
80
 
81
    /**
82
     * This method returns a GeoIP2 City model.
83
     *
84
     * @param string $ipAddress an IPv4 or IPv6 address as a string
85
     *
86
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
87
     *                                                     not in the database
88
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
89
     *                                                     is corrupt or invalid
90
     */
91
    public function city(string $ipAddress): City
92
    {
93
        // @phpstan-ignore-next-line
94
        return $this->modelFor(City::class, 'City', $ipAddress);
95
    }
96
 
97
    /**
98
     * This method returns a GeoIP2 Country model.
99
     *
100
     * @param string $ipAddress an IPv4 or IPv6 address as a string
101
     *
102
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
103
     *                                                     not in the database
104
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
105
     *                                                     is corrupt or invalid
106
     */
107
    public function country(string $ipAddress): Country
108
    {
109
        // @phpstan-ignore-next-line
110
        return $this->modelFor(Country::class, 'Country', $ipAddress);
111
    }
112
 
113
    /**
114
     * This method returns a GeoIP2 Anonymous IP model.
115
     *
116
     * @param string $ipAddress an IPv4 or IPv6 address as a string
117
     *
118
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
119
     *                                                     not in the database
120
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
121
     *                                                     is corrupt or invalid
122
     */
123
    public function anonymousIp(string $ipAddress): AnonymousIp
124
    {
125
        // @phpstan-ignore-next-line
126
        return $this->flatModelFor(
127
            AnonymousIp::class,
128
            'GeoIP2-Anonymous-IP',
129
            $ipAddress
130
        );
131
    }
132
 
133
    /**
134
     * This method returns a GeoLite2 ASN model.
135
     *
136
     * @param string $ipAddress an IPv4 or IPv6 address as a string
137
     *
138
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
139
     *                                                     not in the database
140
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
141
     *                                                     is corrupt or invalid
142
     */
143
    public function asn(string $ipAddress): Asn
144
    {
145
        // @phpstan-ignore-next-line
146
        return $this->flatModelFor(
147
            Asn::class,
148
            'GeoLite2-ASN',
149
            $ipAddress
150
        );
151
    }
152
 
153
    /**
154
     * This method returns a GeoIP2 Connection Type model.
155
     *
156
     * @param string $ipAddress an IPv4 or IPv6 address as a string
157
     *
158
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
159
     *                                                     not in the database
160
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
161
     *                                                     is corrupt or invalid
162
     */
163
    public function connectionType(string $ipAddress): ConnectionType
164
    {
165
        // @phpstan-ignore-next-line
166
        return $this->flatModelFor(
167
            ConnectionType::class,
168
            'GeoIP2-Connection-Type',
169
            $ipAddress
170
        );
171
    }
172
 
173
    /**
174
     * This method returns a GeoIP2 Domain model.
175
     *
176
     * @param string $ipAddress an IPv4 or IPv6 address as a string
177
     *
178
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
179
     *                                                     not in the database
180
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
181
     *                                                     is corrupt or invalid
182
     */
183
    public function domain(string $ipAddress): Domain
184
    {
185
        // @phpstan-ignore-next-line
186
        return $this->flatModelFor(
187
            Domain::class,
188
            'GeoIP2-Domain',
189
            $ipAddress
190
        );
191
    }
192
 
193
    /**
194
     * This method returns a GeoIP2 Enterprise model.
195
     *
196
     * @param string $ipAddress an IPv4 or IPv6 address as a string
197
     *
198
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
199
     *                                                     not in the database
200
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
201
     *                                                     is corrupt or invalid
202
     */
203
    public function enterprise(string $ipAddress): Enterprise
204
    {
205
        // @phpstan-ignore-next-line
206
        return $this->modelFor(Enterprise::class, 'Enterprise', $ipAddress);
207
    }
208
 
209
    /**
210
     * This method returns a GeoIP2 ISP model.
211
     *
212
     * @param string $ipAddress an IPv4 or IPv6 address as a string
213
     *
214
     * @throws \GeoIp2\Exception\AddressNotFoundException  if the address is
215
     *                                                     not in the database
216
     * @throws \MaxMind\Db\Reader\InvalidDatabaseException if the database
217
     *                                                     is corrupt or invalid
218
     */
219
    public function isp(string $ipAddress): Isp
220
    {
221
        // @phpstan-ignore-next-line
222
        return $this->flatModelFor(
223
            Isp::class,
224
            'GeoIP2-ISP',
225
            $ipAddress
226
        );
227
    }
228
 
229
    private function modelFor(string $class, string $type, string $ipAddress): AbstractModel
230
    {
231
        [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
232
 
233
        $record['traits']['ip_address'] = $ipAddress;
234
        $record['traits']['prefix_len'] = $prefixLen;
235
 
236
        return new $class($record, $this->locales);
237
    }
238
 
239
    private function flatModelFor(string $class, string $type, string $ipAddress): AbstractModel
240
    {
241
        [$record, $prefixLen] = $this->getRecord($class, $type, $ipAddress);
242
 
243
        $record['ip_address'] = $ipAddress;
244
        $record['prefix_len'] = $prefixLen;
245
 
246
        return new $class($record);
247
    }
248
 
249
    private function getRecord(string $class, string $type, string $ipAddress): array
250
    {
251
        if (strpos($this->dbType, $type) === false) {
252
            $method = lcfirst((new \ReflectionClass($class))->getShortName());
253
 
254
            throw new \BadMethodCallException(
255
                "The $method method cannot be used to open a {$this->dbType} database"
256
            );
257
        }
258
        [$record, $prefixLen] = $this->dbReader->getWithPrefixLen($ipAddress);
259
        if ($record === null) {
260
            throw new AddressNotFoundException(
261
                "The address $ipAddress is not in the database."
262
            );
263
        }
264
        if (!\is_array($record)) {
265
            // This can happen on corrupt databases. Generally,
266
            // MaxMind\Db\Reader will throw a
267
            // MaxMind\Db\Reader\InvalidDatabaseException, but occasionally
268
            // the lookup may result in a record that looks valid but is not
269
            // an array. This mostly happens when the user is ignoring all
270
            // exceptions and the more frequent InvalidDatabaseException
271
            // exceptions go unnoticed.
272
            throw new InvalidDatabaseException(
273
                "Expected an array when looking up $ipAddress but received: "
274
                . \gettype($record)
275
            );
276
        }
277
 
278
        return [$record, $prefixLen];
279
    }
280
 
281
    /**
282
     * @throws \InvalidArgumentException if arguments are passed to the method
283
     * @throws \BadMethodCallException   if the database has been closed
284
     *
285
     * @return \MaxMind\Db\Reader\Metadata object for the database
286
     */
287
    public function metadata(): DbReader\Metadata
288
    {
289
        return $this->dbReader->metadata();
290
    }
291
 
292
    /**
293
     * Closes the GeoIP2 database and returns the resources to the system.
294
     */
295
    public function close(): void
296
    {
297
        $this->dbReader->close();
298
    }
299
}