Skip to content

Instantly share code, notes, and snippets.

@scravy
Created September 22, 2020 11:04
Show Gist options
  • Save scravy/f801ea4719c8fd2fa29de40f4f8296f1 to your computer and use it in GitHub Desktop.
Save scravy/f801ea4719c8fd2fa29de40f4f8296f1 to your computer and use it in GitHub Desktop.
<?php
class FlexBase {
private $spec = array();
private $bases = array();
private $checksum = 1;
private $num_digits;
private $pretty = FALSE;
private $base = 10;
private $alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
function __construct($spec) {
if (func_num_args() > 1) {
$spec = func_get_args();
}
foreach ($spec as $i => $s) {
list($min, $max) = $s;
$this->bases[$i] = $max - $min + 1;
$this->spec[$i] = $min;
}
$this->update_digits();
}
private function update_digits() {
$this->num_digits = strlen($this->max());
}
public function set_checksum($mod = 1) {
if ($mod < 1) {
return FALSE;
}
$this->checksum = $mod;
$this->update_digits();
}
public function set_pretty($pretty = TRUE) {
$this->pretty = !!$pretty;
}
public function set_base($base = 10) {
if ($base < 2 || $base > 36) {
return FALSE;
}
$this->base = $base;
$this->update_digits();
return TRUE;
}
public function set_alphabet($alphabet) {
if (strlen($alphabet) < 2 || strlen($alphabet) > 36) {
return FALSE;
}
$this->alphabet = $alphabet;
$this->set_base(strlen($alphabet));
return TRUE;
}
private function into_internal($data) {
$value = 0;
foreach ($this->spec as $i => $min) {
$value *= $this->bases[$i];
$d = $data[$i] ?? $min;
$value += ($d - $min) % $this->bases[$i];
}
if ($this->checksum > 1) {
$checksum = $value % $this->checksum;
$value *= $this->checksum;
$value += $checksum;
}
if ($this->base != 10) {
$value = strtr(
base_convert($value, 10, $this->base),
"0123456789abcdefghijklmnopqrstuvwxyz",
$this->alphabet
);
}
return $value;
}
public function into($data) {
if (func_num_args() != 1 && !is_array($data[0])) {
$data = func_get_args();
}
$value = $this->into_internal($data);
if ($this->pretty) {
$value = str_pad("$value", $this->num_digits, $this->alphabet[0], STR_PAD_LEFT);
$len = strlen($value);
$seglen = intdiv($len, 3);
$value = substr($value, 0, $seglen)
. '-' . substr($value, $seglen, $len - 2 * $seglen)
. '-' . substr($value, -$seglen);
}
return $value;
}
public function from($value) {
if (is_string($value)) {
$value = implode('', explode(' ', strtr($value, '_-', ' ')));
for ($i = 0; $i < strlen($value); $i += 1) {
if (strpos($this->alphabet, $value[$i]) === FALSE) {
return FALSE;
}
}
$value = strtr(
$value,
$this->alphabet,
"0123456789abcdefghijklmnopqrstuvwxyz"
);
$value = ltrim($value, '0');
if (empty($value)) {
$value = 0;
}
}
if ($this->base != 10) {
$value = base_convert($value, $this->base, 10);
}
$data = array();
$v = $value;
$embedded_checksum = 0;
if ($this->checksum > 1) {
$v = intdiv($value, $this->checksum);
$embedded_checksum = $value % $this->checksum;
$calculated_checksum = $v % $this->checksum;
if ($embedded_checksum != $calculated_checksum) {
return FALSE;
}
}
foreach (array_reverse($this->spec, TRUE) as $i => $min) {
array_unshift($data, $min + ($v % $this->bases[$i]));
$v = intdiv($v, $this->bases[$i]);
}
if ($v != 0) {
return FALSE;
}
return $data;
}
public function max() {
$data = array();
foreach ($this->spec as $i => $min) {
array_push($data, $this->bases[$i] + $min - 1);
}
return $this->into_internal($data);
}
public function num_digits() {
return $this->num_digits;
}
public function num_components() {
return count($this->spec);
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment