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