From fb61ef070ad1859dcf3fc8395d94b0904c463cbd Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 27 May 2024 16:54:12 +0200 Subject: [PATCH] Add native types to public API This changeset adds native types to the public API as discussed in #219. Once merged, I'm planning to add PHPStan in a follow-up PR which would take advantage of these types. Builds on top of #222, #223 and reactphp/cache#60 --- src/Config/Config.php | 9 +++++--- src/Config/HostsFile.php | 26 ++++++++++++++---------- src/Model/Record.php | 12 +++++------ src/Query/CachingExecutor.php | 5 +++-- src/Query/CoopExecutor.php | 3 ++- src/Query/ExecutorInterface.php | 7 +++++-- src/Query/FallbackExecutor.php | 3 ++- src/Query/HostsFileExecutor.php | 3 ++- src/Query/RetryExecutor.php | 2 +- src/Query/SelectiveTransportExecutor.php | 3 ++- src/Query/TcpTransportExecutor.php | 6 +++--- src/Query/TimeoutExecutor.php | 8 ++++++-- src/Query/UdpTransportExecutor.php | 5 +++-- src/Resolver/Factory.php | 8 ++++---- src/Resolver/Resolver.php | 5 +++-- src/Resolver/ResolverInterface.php | 13 +++++++----- 16 files changed, 71 insertions(+), 47 deletions(-) diff --git a/src/Config/Config.php b/src/Config/Config.php index 0b19e9af..c30bb26a 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -26,7 +26,7 @@ final class Config * @return self * @codeCoverageIgnore */ - public static function loadSystemConfigBlocking() + public static function loadSystemConfigBlocking(): self { // Use WMIC output on Windows if (DIRECTORY_SEPARATOR === '\\') { @@ -71,7 +71,7 @@ public static function loadSystemConfigBlocking() * @return self * @throws RuntimeException if the path can not be loaded (does not exist) */ - public static function loadResolvConfBlocking($path = null) + public static function loadResolvConfBlocking(?string $path = null): self { if ($path === null) { $path = '/etc/resolv.conf'; @@ -122,7 +122,7 @@ public static function loadResolvConfBlocking($path = null) * @return self * @link https://ss64.com/nt/wmic.html */ - public static function loadWmicBlocking($command = null) + public static function loadWmicBlocking(?string $command = null): self { $contents = shell_exec($command === null ? 'wmic NICCONFIG get "DNSServerSearchOrder" /format:CSV' : $command); preg_match_all('/(?<=[{;,"])([\da-f.:]{4,})(?=[};,"])/i', $contents, $matches); @@ -133,5 +133,8 @@ public static function loadWmicBlocking($command = null) return $config; } + /** + * @var array + */ public $nameservers = []; } diff --git a/src/Config/HostsFile.php b/src/Config/HostsFile.php index 1333102d..30e05aec 100644 --- a/src/Config/HostsFile.php +++ b/src/Config/HostsFile.php @@ -24,10 +24,9 @@ class HostsFile /** * Returns the default path for the hosts file on this system * - * @return string * @codeCoverageIgnore */ - public static function getDefaultPath() + public static function getDefaultPath(): string { // use static path for all Unix-based systems if (DIRECTORY_SEPARATOR !== '\\') { @@ -59,7 +58,7 @@ public static function getDefaultPath() * @return self * @throws RuntimeException if the path can not be loaded (does not exist) */ - public static function loadFromPathBlocking($path = null) + public static function loadFromPathBlocking(?string $path = null): self { if ($path === null) { $path = self::getDefaultPath(); @@ -73,6 +72,9 @@ public static function loadFromPathBlocking($path = null) return new self($contents); } + /** + * @var string + */ private $contents; /** @@ -80,9 +82,13 @@ public static function loadFromPathBlocking($path = null) * * @param string $contents */ - public function __construct($contents) + public function __construct(string $contents) { - // remove all comments from the contents + /** + * remove all comments from the contents + * + * @var string $contents + */ $contents = preg_replace('/[ \t]*#.*/', '', strtolower($contents)); $this->contents = $contents; @@ -92,9 +98,9 @@ public function __construct($contents) * Returns all IPs for the given hostname * * @param string $name - * @return string[] + * @return iterable */ - public function getIpsForHost($name) + public function getIpsForHost(string $name): iterable { $name = strtolower($name); @@ -113,17 +119,15 @@ public function getIpsForHost($name) } } } - - return $ips; } /** * Returns all hostnames for the given IPv4 or IPv6 address * * @param string $ip - * @return string[] + * @return iterable */ - public function getHostsForIp($ip) + public function getHostsForIp(string $ip): iterable { // check binary representation of IP to avoid string case and short notation $ip = @inet_pton($ip); diff --git a/src/Model/Record.php b/src/Model/Record.php index c20403f5..d399001f 100644 --- a/src/Model/Record.php +++ b/src/Model/Record.php @@ -131,18 +131,18 @@ final class Record * considered a BC break. See the format definition of known types above * for more details. * - * @var string|string[]|array + * @var string|array */ public $data; /** * @param string $name - * @param int $type - * @param int $class - * @param int $ttl - * @param string|string[]|array $data + * @param int $type + * @param int $class + * @param int $ttl + * @param string|array $data */ - public function __construct($name, $type, $class, $ttl, $data) + public function __construct(string $name, int $type, int $class, int $ttl, $data) { $this->name = $name; $this->type = $type; diff --git a/src/Query/CachingExecutor.php b/src/Query/CachingExecutor.php index 03d56c95..4ca90dd2 100644 --- a/src/Query/CachingExecutor.php +++ b/src/Query/CachingExecutor.php @@ -5,6 +5,7 @@ use React\Cache\CacheInterface; use React\Dns\Model\Message; use React\Promise\Promise; +use React\Promise\PromiseInterface; final class CachingExecutor implements ExecutorInterface { @@ -24,7 +25,7 @@ public function __construct(ExecutorInterface $executor, CacheInterface $cache) $this->cache = $cache; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $id = $query->name . ':' . $query->type . ':' . $query->class; @@ -65,7 +66,7 @@ function (Message $message) use ($id) { * @return int * @internal */ - public function ttl(Message $message) + public function ttl(Message $message): int { // select TTL from answers (should all be the same), use smallest value if available // @link https://tools.ietf.org/html/rfc2181#section-5.2 diff --git a/src/Query/CoopExecutor.php b/src/Query/CoopExecutor.php index 3ce41691..ec7dd216 100644 --- a/src/Query/CoopExecutor.php +++ b/src/Query/CoopExecutor.php @@ -3,6 +3,7 @@ namespace React\Dns\Query; use React\Promise\Promise; +use React\Promise\PromiseInterface; /** * Cooperatively resolves hosts via the given base executor to ensure same query is not run concurrently @@ -45,7 +46,7 @@ public function __construct(ExecutorInterface $base) $this->executor = $base; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $key = $this->serializeQueryToIdentity($query); if (isset($this->pending[$key])) { diff --git a/src/Query/ExecutorInterface.php b/src/Query/ExecutorInterface.php index 0bc3945f..3e99fb72 100644 --- a/src/Query/ExecutorInterface.php +++ b/src/Query/ExecutorInterface.php @@ -2,6 +2,9 @@ namespace React\Dns\Query; +use React\Dns\Model\Message; +use React\Promise\PromiseInterface; + interface ExecutorInterface { /** @@ -36,8 +39,8 @@ interface ExecutorInterface * ``` * * @param Query $query - * @return \React\Promise\PromiseInterface<\React\Dns\Model\Message> + * @return PromiseInterface * resolves with response message on success or rejects with an Exception on error */ - public function query(Query $query); + public function query(Query $query): PromiseInterface; } diff --git a/src/Query/FallbackExecutor.php b/src/Query/FallbackExecutor.php index 6999e1b8..4d876ee3 100644 --- a/src/Query/FallbackExecutor.php +++ b/src/Query/FallbackExecutor.php @@ -3,6 +3,7 @@ namespace React\Dns\Query; use React\Promise\Promise; +use React\Promise\PromiseInterface; final class FallbackExecutor implements ExecutorInterface { @@ -15,7 +16,7 @@ public function __construct(ExecutorInterface $executor, ExecutorInterface $fall $this->fallback = $fallback; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $cancelled = false; $promise = $this->executor->query($query); diff --git a/src/Query/HostsFileExecutor.php b/src/Query/HostsFileExecutor.php index 590f8abe..a7c2d981 100644 --- a/src/Query/HostsFileExecutor.php +++ b/src/Query/HostsFileExecutor.php @@ -5,6 +5,7 @@ use React\Dns\Config\HostsFile; use React\Dns\Model\Message; use React\Dns\Model\Record; +use React\Promise\PromiseInterface; use function React\Promise\resolve; /** @@ -25,7 +26,7 @@ public function __construct(HostsFile $hosts, ExecutorInterface $fallback) $this->fallback = $fallback; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { if ($query->class === Message::CLASS_IN && ($query->type === Message::TYPE_A || $query->type === Message::TYPE_AAAA)) { // forward lookup for type A or AAAA diff --git a/src/Query/RetryExecutor.php b/src/Query/RetryExecutor.php index e68dcc61..196750a5 100644 --- a/src/Query/RetryExecutor.php +++ b/src/Query/RetryExecutor.php @@ -16,7 +16,7 @@ public function __construct(ExecutorInterface $executor, $retries = 2) $this->retries = $retries; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { return $this->tryQuery($query, $this->retries); } diff --git a/src/Query/SelectiveTransportExecutor.php b/src/Query/SelectiveTransportExecutor.php index b76632fd..214249bd 100644 --- a/src/Query/SelectiveTransportExecutor.php +++ b/src/Query/SelectiveTransportExecutor.php @@ -3,6 +3,7 @@ namespace React\Dns\Query; use React\Promise\Promise; +use React\Promise\PromiseInterface; /** * Send DNS queries over a UDP or TCP/IP stream transport. @@ -61,7 +62,7 @@ public function __construct(ExecutorInterface $datagramExecutor, ExecutorInterfa $this->streamExecutor = $streamExecutor; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $pending = $this->datagramExecutor->query($query); diff --git a/src/Query/TcpTransportExecutor.php b/src/Query/TcpTransportExecutor.php index d940da61..2ba249b4 100644 --- a/src/Query/TcpTransportExecutor.php +++ b/src/Query/TcpTransportExecutor.php @@ -7,7 +7,9 @@ use React\Dns\Protocol\Parser; use React\EventLoop\Loop; use React\EventLoop\LoopInterface; +use React\EventLoop\TimerInterface; use React\Promise\Deferred; +use React\Promise\PromiseInterface; use function React\Promise\reject; /** @@ -153,7 +155,7 @@ public function __construct($nameserver, LoopInterface $loop = null) $this->dumper = new BinaryDumper(); } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $request = Message::createRequestForQuery($query); @@ -328,8 +330,6 @@ public function handleRead() /** * @internal - * @param string $reason - * @param int $code */ public function closeError($reason, $code = 0) { diff --git a/src/Query/TimeoutExecutor.php b/src/Query/TimeoutExecutor.php index d6a63211..3a7a031f 100644 --- a/src/Query/TimeoutExecutor.php +++ b/src/Query/TimeoutExecutor.php @@ -5,6 +5,7 @@ use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\Promise\Promise; +use React\Promise\PromiseInterface; final class TimeoutExecutor implements ExecutorInterface { @@ -12,14 +13,17 @@ final class TimeoutExecutor implements ExecutorInterface private $loop; private $timeout; - public function __construct(ExecutorInterface $executor, $timeout, LoopInterface $loop = null) + /** + * @param float|int $timeout + */ + public function __construct(ExecutorInterface $executor, $timeout, ?LoopInterface $loop = null) { $this->executor = $executor; $this->loop = $loop ?: Loop::get(); $this->timeout = $timeout; } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $promise = $this->executor->query($query); diff --git a/src/Query/UdpTransportExecutor.php b/src/Query/UdpTransportExecutor.php index 31290284..148e13bf 100644 --- a/src/Query/UdpTransportExecutor.php +++ b/src/Query/UdpTransportExecutor.php @@ -8,6 +8,7 @@ use React\EventLoop\Loop; use React\EventLoop\LoopInterface; use React\Promise\Deferred; +use React\Promise\PromiseInterface; use function React\Promise\reject; /** @@ -99,7 +100,7 @@ final class UdpTransportExecutor implements ExecutorInterface * @param string $nameserver * @param ?LoopInterface $loop */ - public function __construct($nameserver, LoopInterface $loop = null) + public function __construct(string $nameserver, ?LoopInterface $loop = null) { if (\strpos($nameserver, '[') === false && \substr_count($nameserver, ':') >= 2 && \strpos($nameserver, '://') === false) { // several colons, but not enclosed in square brackets => enclose IPv6 address in square brackets @@ -117,7 +118,7 @@ public function __construct($nameserver, LoopInterface $loop = null) $this->dumper = new BinaryDumper(); } - public function query(Query $query) + public function query(Query $query): PromiseInterface { $request = Message::createRequestForQuery($query); diff --git a/src/Resolver/Factory.php b/src/Resolver/Factory.php index 5fe608cb..758e7caf 100644 --- a/src/Resolver/Factory.php +++ b/src/Resolver/Factory.php @@ -32,11 +32,11 @@ final class Factory * * @param Config|string $config DNS Config object (recommended) or single nameserver address * @param ?LoopInterface $loop - * @return \React\Dns\Resolver\ResolverInterface + * @return ResolverInterface * @throws \InvalidArgumentException for invalid DNS server address * @throws \UnderflowException when given DNS Config object has an empty list of nameservers */ - public function create($config, LoopInterface $loop = null) + public function create($config, LoopInterface $loop = null): ResolverInterface { $executor = $this->decorateHostsFileExecutor($this->createExecutor($config, $loop ?: Loop::get())); @@ -55,11 +55,11 @@ public function create($config, LoopInterface $loop = null) * @param Config|string $config DNS Config object (recommended) or single nameserver address * @param ?LoopInterface $loop * @param ?CacheInterface $cache - * @return \React\Dns\Resolver\ResolverInterface + * @return ResolverInterface * @throws \InvalidArgumentException for invalid DNS server address * @throws \UnderflowException when given DNS Config object has an empty list of nameservers */ - public function createCached($config, LoopInterface $loop = null, CacheInterface $cache = null) + public function createCached($config, LoopInterface $loop = null, CacheInterface $cache = null): ResolverInterface { // default to keeping maximum of 256 responses in cache unless explicitly given if (!($cache instanceof CacheInterface)) { diff --git a/src/Resolver/Resolver.php b/src/Resolver/Resolver.php index 6108b3da..b655080f 100644 --- a/src/Resolver/Resolver.php +++ b/src/Resolver/Resolver.php @@ -6,6 +6,7 @@ use React\Dns\Query\ExecutorInterface; use React\Dns\Query\Query; use React\Dns\RecordNotFoundException; +use React\Promise\PromiseInterface; /** * @see ResolverInterface for the base interface @@ -19,14 +20,14 @@ public function __construct(ExecutorInterface $executor) $this->executor = $executor; } - public function resolve($domain) + public function resolve(string $domain): PromiseInterface { return $this->resolveAll($domain, Message::TYPE_A)->then(function (array $ips) { return $ips[array_rand($ips)]; }); } - public function resolveAll($domain, $type) + public function resolveAll(string $domain, int $type): PromiseInterface { $query = new Query($domain, $type, Message::CLASS_IN); diff --git a/src/Resolver/ResolverInterface.php b/src/Resolver/ResolverInterface.php index 555a1cb1..3698c38a 100644 --- a/src/Resolver/ResolverInterface.php +++ b/src/Resolver/ResolverInterface.php @@ -2,6 +2,8 @@ namespace React\Dns\Resolver; +use React\Promise\PromiseInterface; + interface ResolverInterface { /** @@ -39,10 +41,10 @@ interface ResolverInterface * ``` * * @param string $domain - * @return \React\Promise\PromiseInterface + * @return PromiseInterface * resolves with a single IP address on success or rejects with an Exception on error. */ - public function resolve($domain); + public function resolve(string $domain): PromiseInterface; /** * Resolves all record values for the given $domain name and query $type. @@ -86,9 +88,10 @@ public function resolve($domain); * $promise->cancel(); * ``` * - * @param string $domain - * @return \React\Promise\PromiseInterface + * @param string $domain domain to resolve + * @param int $type query type, see Message::TYPE_* constants + * @return PromiseInterface> * Resolves with all record values on success or rejects with an Exception on error. */ - public function resolveAll($domain, $type); + public function resolveAll(string $domain, int $type): PromiseInterface; }