getBytesFromDecimal($m[1], $numBytes); } } else { if (preg_match('/^0[Xx]0*([0-9A-Fa-f]+)$/', $value, $m)) { return $this->getBytesFromHexadecimal($m[1], $numBytes); } if (preg_match('/^0+([0-7]*)$/', $value, $m)) { return $this->getBytesFromOctal($m[1], $numBytes); } if (preg_match('/^[1-9][0-9]*$/', $value)) { return $this->getBytesFromDecimal($value, $numBytes); } } // Not a valid number return null; } /** * @return int */ protected function getMaxSignedInt() { return PHP_INT_MAX; } /** * @param string $value never zero-length, never extra leading zeroes * @param int $numBytes * * @return int[]|null */ private function getBytesFromBits($value, $numBytes) { $valueLength = strlen($value); if ($valueLength > $numBytes << 3) { // overflow return null; } $remainderBits = $valueLength % 8; if ($remainderBits !== 0) { $value = str_pad($value, $valueLength + 8 - $remainderBits, '0', STR_PAD_LEFT); } $bytes = array_map('bindec', str_split($value, 8)); /** @var int[] $bytes */ return array_pad($bytes, -$numBytes, 0); } /** * @param string $value may be zero-length, never extra leading zeroes * @param int $numBytes * * @return int[]|null */ private function getBytesFromOctal($value, $numBytes) { if ($value === '') { return array_fill(0, $numBytes, 0); } $bits = implode( '', array_map( function ($octalDigit) { return str_pad(decbin((int) octdec($octalDigit)), 3, '0', STR_PAD_LEFT); }, str_split($value, 1) ) ); $bits = ltrim($bits, '0'); return $bits === '' ? array_fill(0, $numBytes, 0) : self::getBytesFromBits($bits, $numBytes); } /** * @param string $value never zero-length, never extra leading zeroes * @param int $numBytes * * @return int[]|null */ private function getBytesFromDecimal($value, $numBytes) { $valueLength = strlen($value); $maxSignedIntLength = strlen((string) $this->getMaxSignedInt()); if ($valueLength < $maxSignedIntLength) { return $this->getBytesFromBits(decbin((int) $value), $numBytes); } // Divide by two, so that we have 1 less bit $carry = 0; $halfValue = ltrim( implode( '', array_map( function ($digit) use (&$carry) { $number = $carry + (int) $digit; $carry = ($number % 2) * 10; return (string) $number >> 1; }, str_split($value, 1) ) ), '0' ); $halfValueBytes = $this->getBytesFromDecimal($halfValue, $numBytes); if ($halfValueBytes === null) { return null; } $carry = $carry === 0 ? 0 : 1; $result = array_fill(0, $numBytes, 0); for ($index = $numBytes - 1; $index >= 0; $index--) { $byte = $carry + ($halfValueBytes[$index] << 1); if ($byte <= 0xFF) { $carry = 0; } else { $carry = ($byte & ~0xFF) >> 8; $byte -= 0x100; } $result[$index] = $byte; } if ($carry !== 0) { // Overflow return null; } return $result; } /** * @param string $value never zero-length, never extra leading zeroes * @param int $numBytes * * @return int[]|null */ private function getBytesFromHexadecimal($value, $numBytes) { $valueLength = strlen($value); if ($valueLength > $numBytes << 1) { // overflow return null; } $value = str_pad($value, $valueLength + $valueLength % 2, '0', STR_PAD_LEFT); $bytes = array_map('hexdec', str_split($value, 2)); /** @var int[] $bytes */ return array_pad($bytes, -$numBytes, 0); } }