Skip to content

Commit

Permalink
Merge pull request #21 from ipinfo/uman/fixes
Browse files Browse the repository at this point in the history
Various updates & fixes for a v2.0.0
  • Loading branch information
UmanShahzad committed Nov 13, 2020
2 parents bde9ce0 + 9cae63a commit d933b19
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 204 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
build
composer.lock
vendor
.phpunit.result.cache
18 changes: 1 addition & 17 deletions .travis.yml
Expand Up @@ -2,11 +2,9 @@ dist: trusty
language: php

php:
- 7.0
- 7.1
- 7.2
- 7.3
- hhvm
- 7.4

# This triggers builds to run on the new TravisCI infrastructure.
# See: http://docs.travis-ci.com/user/workers/container-based-infrastructure/
Expand All @@ -17,23 +15,9 @@ cache:
directories:
- $HOME/.composer/cache

matrix:
include:
- php: 7.0
env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest"'
allow_failures:
- php: hhvm

before_script:
- travis_retry composer update ${COMPOSER_FLAGS} --no-interaction --prefer-dist

script:
- vendor/bin/phpcs --standard=psr2 src/
- vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover

after_script:
- |
if [[ "$TRAVIS_PHP_VERSION" != 'hhvm' && "$TRAVIS_PHP_VERSION" = '7.0' ]]; then
wget https://scrutinizer-ci.com/ocular.phar
php ocular.phar code-coverage:upload --format=php-clover coverage.clover
fi
14 changes: 14 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,14 @@
# CHANGELOG

### 2.0.0 (November 2020)

- A `guzzle_opts` option is supported in the settings, which allows full Guzzle
option overrides.
- A `timeout` option is supported in the settings, which is the request timeout
value, and defaults to 2 seconds.
**BREAKING**: this was previously unconfigurable and was 0 seconds,
i.e. infinite timeout.
- The `buildHeaders` method on the main `IPinfo` client is now private.
**BREAKING**: this will no longer be available for use from the client.
- Only non-EOL PHP 7 versions are supported. In particular, PHP 7.2 and above
are all supported and tested in the CI.
10 changes: 10 additions & 0 deletions README.md
Expand Up @@ -154,6 +154,16 @@ It's possible to use a custom cache by creating a child class of the [CacheInter
>>> $client = new IPinfo($access_token, $settings);
```

### Overriding HTTP Client options

The IPinfo client constructor accepts a `timeout` key which is the request
timeout in seconds.

For full flexibility, a `guzzle_opts` key is accepted which accepts an
associative array which is described in [Guzzle Request Options](https://docs.guzzlephp.org/en/stable/request-options.html).
Options set here will override any custom settings set by the IPinfo client
internally in case of conflict, including headers.

### Internationalization

When looking up an IP address, the response object includes a `Details->country_name` attribute which includes the country name based on American English. It is possible to return the country name in other languages by setting the `countries_file` keyword argument when creating the `IPinfo` object.
Expand Down
8 changes: 7 additions & 1 deletion composer.json
Expand Up @@ -13,6 +13,12 @@
"email": "jameshtimmins@gmail.com",
"homepage": "https://github.com/jhtimmins",
"role": "Developer"
},
{
"name": "Uman Shahzad",
"email": "uman@mslm.io",
"homepage": "https://github.com/UmanShahzad",
"role": "Developer"
}
],
"require": {
Expand Down Expand Up @@ -42,7 +48,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "2.0-dev"
}
},
"config": {
Expand Down
38 changes: 14 additions & 24 deletions phpunit.xml.dist
@@ -1,26 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="ipinfo Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<logging>
<log type="tap" target="build/report.tap"/>
<log type="junit" target="build/report.junit.xml"/>
</logging>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" backupGlobals="false" backupStaticAttributes="false" colors="true" verbose="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">src/</directory>
</include>
</coverage>
<testsuites>
<testsuite name="ipinfo Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
</phpunit>
15 changes: 15 additions & 0 deletions psalm.xml
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<psalm
errorLevel="8"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>
82 changes: 48 additions & 34 deletions src/IPinfo.php
Expand Up @@ -18,6 +18,7 @@ class IPinfo
const COUNTRIES_FILE_DEFAULT = __DIR__ . '/countries.json';
const REQUEST_TYPE_GET = 'GET';
const STATUS_CODE_QUOTA_EXCEEDED = 429;
const REQUEST_TIMEOUT_DEFAULT = 2; // seconds

public $access_token;
public $cache;
Expand All @@ -27,7 +28,20 @@ class IPinfo
public function __construct($access_token = null, $settings = [])
{
$this->access_token = $access_token;
$this->http_client = new Client(['http_errors' => false]);

/*
Support a timeout first-class, then a `guzzle_opts` key that can
override anything.
*/
$guzzle_opts = [
'http_errors' => false,
'headers' => $this->buildHeaders(),
'timeout' => $settings['timeout'] ?? self::REQUEST_TIMEOUT_DEFAULT
];
if (isset($settings['guzzle_opts'])) {
$guzzle_opts = array_merge($guzzle_opts, $settings['guzzle_opts']);
}
$this->http_client = new Client($guzzle_opts);

$countries_file = $settings['countries_file'] ?? self::COUNTRIES_FILE_DEFAULT;
$this->countries = $this->readCountryNames($countries_file);
Expand All @@ -50,7 +64,6 @@ public function __construct($access_token = null, $settings = [])
public function getDetails($ip_address = null)
{
$response_details = $this->getRequestDetails((string) $ip_address);

return $this->formatDetailsObject($response_details);
}

Expand Down Expand Up @@ -84,56 +97,57 @@ public function formatDetailsObject($details = [])
*/
public function getRequestDetails(string $ip_address)
{
if (!$this->cache->has($ip_address)) {
$url = self::API_URL;
if ($ip_address) {
$url .= "/$ip_address";
}

try {
$response = $this->http_client->request(
self::REQUEST_TYPE_GET,
$url,
$this->buildHeaders()
);
} catch (GuzzleException $e) {
throw new IPinfoException($e->getMessage());
} catch (Exception $e) {
throw new IPinfoException($e->getMessage());
}

if ($response->getStatusCode() == self::STATUS_CODE_QUOTA_EXCEEDED) {
throw new IPinfoException('IPinfo request quota exceeded.');
} elseif ($response->getStatusCode() >= 400) {
throw new IPinfoException('Exception: ' . json_encode([
if ($this->cache->has($ip_address)) {
return $this->cache->get($ip_address);
}

$url = self::API_URL;
if ($ip_address) {
$url .= "/$ip_address";
}

try {
$response = $this->http_client->request(
self::REQUEST_TYPE_GET,
$url
);
} catch (GuzzleException $e) {
throw new IPinfoException($e->getMessage());
} catch (Exception $e) {
throw new IPinfoException($e->getMessage());
}

if ($response->getStatusCode() == self::STATUS_CODE_QUOTA_EXCEEDED) {
throw new IPinfoException('IPinfo request quota exceeded.');
} elseif ($response->getStatusCode() >= 400) {
throw new IPinfoException('Exception: ' . json_encode([
'status' => $response->getStatusCode(),
'reason' => $response->getReasonPhrase(),
]));
}

$raw_details = json_decode($response->getBody(), true);
$this->cache->set($ip_address, $raw_details);
]));
}

return $this->cache->get($ip_address);
$raw_details = json_decode($response->getBody(), true);
$this->cache->set($ip_address, $raw_details);

return $raw_details;
}

/**
* Build headers for API request.
* @return array Headers for API request.
*/
public function buildHeaders()
private function buildHeaders()
{
$headers = [
'user-agent' => 'IPinfoClient/PHP/1.0',
'accept' => 'application/json',
'user-agent' => 'IPinfoClient/PHP/2.0',
'accept' => 'application/json',
];

if ($this->access_token) {
$headers['authorization'] = "Bearer {$this->access_token}";
}

return ['headers' => $headers];
return $headers;
}

/**
Expand Down

0 comments on commit d933b19

Please sign in to comment.