Skip to content

Commit

Permalink
Merge pull request #10 from hostinger/feat/update-php-dig
Browse files Browse the repository at this point in the history
feat: Partially refactor and update php-dig to PHP8
  • Loading branch information
pauliustHostinger authored Apr 5, 2023
2 parents 98945d2 + 4064cc8 commit 33ca1f8
Show file tree
Hide file tree
Showing 26 changed files with 510 additions and 384 deletions.
15 changes: 0 additions & 15 deletions .codeclimate.yml

This file was deleted.

5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,4 @@ composer.phar
.DS_Store
/.idea
composer.lock

# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
# composer.lock
.phpunit.result.cache
22 changes: 0 additions & 22 deletions .travis.yml

This file was deleted.

23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,28 @@
It should drastically decrease time to get dns records, and lower failure errors like `dns_get_record(): A temporary server error occurred.`

## Installation
Install the latest version with

For now this package is not on [Packgist](https://packagist.org/), so you need to add it to your composer.json manually
```json
{
"repositories": [
{
"type": "vcs",
"url": "[email protected]:hostinger/php-dig.git"
}
]
}
```

Install the latest version with
```console
$ composer require hostinger/php-dig
```

## Usage

```php
$client = new Hostinger\DigClient();
$client = new \Hostinger\Dig\Client();
$result = $client->getRecord('hostinger.com', DNS_MX);
```

Expand All @@ -28,8 +40,9 @@ Package checks if it can run `exec` in server environment, otherwise it will fal

### DigClient implements LoggerAwareInterface
You can set [logger](https://github.com/Seldaek/monolog/) to debug / log package activity

```php
$client = new Hostinger\DigClient();
$client = new \Hostinger\Dig\Client();
$logger = new \Monolog\Logger\Logger('App');
$logger->pushHandler(new StreamHandler('path/to/your.log'));
$client->setLogger($logger);
Expand All @@ -39,7 +52,7 @@ $client->setLogger($logger);

### Requirements

- php-dig client works with PHP 5.6 or above.
- php-dig client works with PHP 8.0 or above.

### Submitting bugs and feature requests

Expand All @@ -48,4 +61,4 @@ Bugs and feature request are tracked on [GitHub](https://github.com/hostinger/ph

## Sources
- [Stack overflow question What would cause checkdnsrr() or dns_get_record() to take too long?](http://stackoverflow.com/questions/14065946/what-would-cause-checkdnsrr-or-dns-get-record-to-take-too-long)
- [Reddit thread: dns_get_record suddenly running very slowly](https://www.reddit.com/r/PHP/comments/2k3ns7/dns_get_record_suddenly_running_very_slowly/)
- [Reddit thread: dns_get_record suddenly running very slowly](https://www.reddit.com/r/PHP/comments/2k3ns7/dns_get_record_suddenly_running_very_slowly/)
15 changes: 6 additions & 9 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,21 @@
],
"license": "MIT",
"require": {
"php": ">=5.6",
"psr/log": "^1.0"
"php": ">=8.0",
"psr/log": "^3.0"
},
"require-dev": {
"phpunit/phpunit": "^5.6"
"phpunit/phpunit": "^9"
},
"autoload": {
"psr-4": {
"Hostinger\\": "src/Hostinger"
"Hostinger\\Dig\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Hostinger\\": "src/Hostinger"
},
"classmap": [
"tests/"
]
"Hostinger\\Dig\\Tests\\": "tests/"
}
},
"scripts": {
"test": "./vendor/bin/phpunit"
Expand Down
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
parameters:
level: 6
paths:
- src
- tests
19 changes: 3 additions & 16 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<phpunit bootstrap="./tests/bootstrap.php" colors="true">
<testsuites>
<testsuite>
<directory suffix="Test.php">./tests</directory>
<testsuite name="Hostinger PHP Dig">
<directory suffix="Test.php">tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
</phpunit>
163 changes: 163 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<?php

declare(strict_types=1);

namespace Hostinger\Dig;

use Closure;
use ErrorException;
use Hostinger\Dig\RecordType\RecordType;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\NullLogger;

class Client implements LoggerAwareInterface
{
use LoggerAwareTrait;

protected Closure $fallback;

/**
* @param Closure|null $customFallback Fallback to use when dig fails. Should take two arguments: domain and type.
*/
public function __construct(Closure $customFallback = null)
{
$this->logger = new NullLogger();
$this->fallback = $customFallback ?? Closure::fromCallable([$this, 'defaultFallback']);
}

/**
* @param Closure $fallback Fallback to use when dig fails. Should take two arguments: domain and type.
*/
public function setFallback(Closure $fallback): void
{
$this->fallback = $fallback;
}

/**
* Resets fallback to default dns_get_record() call.
* @return void
*/
public function resetFallback(): void
{
$this->fallback = Closure::fromCallable([$this, 'defaultFallback']);
}

/**
* @param string $domain
* @param int $type One of the DNS_* constants
* @param string $dnsProvider
* @param int $timeout
* @return array
*/
public function getRecord(string $domain, int $type, string $dnsProvider = '8.8.8.8', int $timeout = 2): array
{
$recordTypeFactory = new RecordTypeFactory();
$recordType = $recordTypeFactory->make($type);
if (is_null($recordType)) {
$this->logger->warning('Unsupported DNS type', [
'domain' => $domain,
'type' => RecordTypeFactory::dnsTypeToName($type),
]);
return ($this->fallback)($domain, $type);
}

$execState = $this->execEnabled();
if ($execState !== true) {
$this->logger->warning('EXEC disabled', [
'domain' => $domain,
'type' => $recordType->getType(),
'error' => $execState,
]);
return ($this->fallback)($domain, $type);
}

$this->logger->debug('execute dig', ['domain' => $domain, 'type' => $recordType->getType()]);

return $this->executeDig($domain, $recordType, $dnsProvider, $timeout);
}

/**
* Use custom error handler to convert dns_get_record() errors to exceptions.
* It throws a warning when the DNS query fails.
* @see http://stackoverflow.com/a/1241751/2728507
* @see http://php.net/manual/en/function.restore-error-handler.php
* @param string $domain
* @param int $type
* @return array
* @throws ErrorException
*/
protected function defaultFallback(string $domain, int $type): array
{
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
// error was suppressed with the @-operator
if (0 === error_reporting()) {
return false;
}

throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});

try {
return dns_get_record($domain, $type);
} catch (ErrorException $errorException) {
$this->logger->critical('dns_get_record() query failed', [
'domain' => $domain,
'type' => RecordTypeFactory::dnsTypeToName($type),
'error' => $errorException->getMessage(),
]);
} finally {
restore_error_handler();
}

return [];
}

/**
* Check if system allowed to execute commands. Return true or string with error message
* @return bool|string
*/
protected function execEnabled(): bool|string
{
if (!function_exists('exec')) {
return 'missing_function_exec';
}

$disabled = explode(',', ini_get('disable_functions'));
if (in_array('exec', $disabled)) {
return 'disabled_function_exec';
}

$lines = [];
$command = 'dig -v 2>&1 | grep "not found" ';
exec($command, $lines);
if (!empty($lines)) {
return 'dig not found';
}

return true;
}

protected function executeDig(
string $domain,
RecordType $recordType,
string $dnsProvider = '8.8.8.8',
int $timeout = 2
): ?array {
$dnsType = strtoupper($recordType->getType());
$command = sprintf(
'dig @%s +noall +answer +time=%u %s %s',
escapeshellarg($dnsProvider),
$timeout,
escapeshellarg($dnsType),
escapeshellarg($domain)
);

exec($command, $output, $returnCode);
if ($returnCode !== 0 || empty($output)) {
return null;
}

return $recordType->transform($output);
}
}
Loading

0 comments on commit 33ca1f8

Please sign in to comment.