1<?php 2 3namespace GeoIp2\Database; 4 5use GeoIp2\Exception\AddressNotFoundException; 6use GeoIp2\ProviderInterface; 7use MaxMind\Db\Reader as DbReader; 8use 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 */ 34class 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