<?php
    # Time-based One-Time Password algorithm (RFC 6238)
    
function calc_totp(
        
string $secretkey,
        
int $codelen 6,
        
int $epoch 0,
        
int $timestep 30,
        ?
int $timestamp null,
        
$hashfunc "sha1"
    
): string {
        if (
$timestamp === null) {
            
$timestamp time();
        }

        
assert(is_int($epoch));
        
assert(is_int($timestep));
        
assert(is_int($timestamp));

        return 
calc_hotp(
            
$secretkey,
            
pack("N*"0$timestamp $epoch - ($timestamp $epoch) % $timestep),
            
$codelen,
            
$hashfunc
        
);
    }

    
# HMAC-based One-Time Password algorithm (RFC 4226)
    
function calc_hotp(
        
string $secretkey,
        
string $counter,
        
int $codelen 6,
        
$hashfunc "sha1"
    
): string {
        
assert(is_string($secretkey));
        
assert(is_string($counter));
        
assert(is_int($codelen) && <= $codelen && $codelen <= 9);

        
$hash hash_hmac($hashfunc$counter$secretkeytrue);

        
$offset ord(substr($hash, -1)) & 0xf;
        
$val unpack("N*"substr($hash$offset4))[1] & 0x7fffffff;

        return 
str_pad((string)($val % (10 ** $codelen)), $codelen"0"STR_PAD_LEFT);
    }