value, $payload, true); return self::create($value, $type); } public static function create(string $value, HashType $type = HashType::sha256): self { return new self($value, $type); } /** * @param string $value hash value (binary, hex or base64 encoded) * @param HashType $type */ public function __construct(string $value, public readonly HashType $type = HashType::sha256) { $length = strlen($value); if ($length === $this->type->length()) { $value = base64_encode($value); } elseif ($length === $this->type->length() * 2 && ctype_xdigit($value)) { $value = base64_encode(hex2bin($value)); } elseif (strlen(base64_decode($value) ?: '') !== $this->type->length()) { throw new \LogicException('Invalid base64 encoded value', 1678620881); } $this->value = $value; } public function __toString(): string { return sprintf("'%s-%s'", $this->type->value, $this->value); } /** * Unquoted hash value, to be used like `integrity="sha256-..."` */ public function export(): string { return $this->type->value . '-' . $this->value; } public static function knows(string $value): bool { return preg_match(self::createParsingPattern(), $value) === 1; } public static function parse(string $value): self { if (preg_match(self::createParsingPattern(), $value, $matches) !== 1) { throw new \LogicException(sprintf('Parsing "%s" is not known', $value), 1678621397); } return new self($matches['value'], HashType::from($matches['type'])); } /** * Parses the unquoted SRI format used in HTML `integrity` attributes (e.g. `sha256-abc123==`), * as well as the quoted CSP format (e.g. `'sha256-abc123=='`). */ public static function fromString(string $value): self { $value = trim($value, "'"); $pattern = sprintf('/^(?P%s)-(?P.+)$/', implode('|', HashType::values())); if (preg_match($pattern, $value, $matches) !== 1) { throw new \LogicException(sprintf('Parsing "%s" is not known', $value), 1773012077); } return new self($matches['value'], HashType::from($matches['type'])); } private static function createParsingPattern(): string { $types = array_map(static fn(HashType $type): string => $type->value, HashType::cases()); return sprintf("/^'(?P%s)-(?P.+)'$/", implode('|', $types)); } public function compile(?FrontendInterface $cache = null): string { return (string)$this; } public function serialize(): string { return (string)$this; } }