Read UTC timestamp from NTP time server
Fri, 12 November 2021
NtpServer.php
<?php
/**
* NtpServer
* @example `$utcTimestamp = NtpServer::get();`
*/
class NtpServer
{
/**
* @var string[]
*/
public static array $servers = [
'time1.google.com',
'time2.google.com',
'time3.google.com',
'time4.google.com'
];
/**
* Get the time from an NTP server. The timestamp returned is always in UTC.
*
* @link https://en.wikipedia.org/wiki/Network_Time_Protocol
* @param array|null $servers
* @param int $secondsTimeout
* @param float $millisecondTimeout
* @return int|null
*/
public static function get(?array $servers = null, int $secondsTimeout = 1, float $millisecondTimeout = 0): ?int
{
if (null === $servers) {
$servers = self::$servers;
}
$time = null;
foreach ($servers as $server) {
$time = self::attempt($server, $secondsTimeout, $millisecondTimeout);
if ($time !== null) {
break;
}
}
return $time;
}
/**
* Attempt to get the time from an NTP server.
*
* @param string $server
* @param int $secondsTimeout
* @param float $millisecondTimeout
* @return int|mixed|null
*/
protected static function attempt(string $server, int $secondsTimeout = 1, float $millisecondTimeout = 0)
{
$socket = @stream_socket_client(
sprintf('udp://%s:123', $server),
$errorCode,
$errorMessage,
$millisecondTimeout
);
if (false === $socket || $errorCode !== 0) {
return null;
}
@stream_set_timeout($socket, $secondsTimeout, $millisecondTimeout);
if (false === @fwrite($socket, "\010\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")) {
return null;
}
if (false === $binary = @fread($socket, 48)) {
return null;
}
if (false === @fclose($socket)) {
return null;
}
if (false === $data = @unpack('N12', $binary)) {
return null;
}
return $data[9] - 2208988800;
}
}