self::$maximumBodyBytes) { throw new HttpException(413, 'The request body is too large.'); } self::$rawBody = $raw; return $raw; } public static function queryString(string $name, int $maxBytes = 4096): string { $value = $_GET[$name] ?? null; if (!is_string($value)) { throw new HttpException(422, "Query parameter $name is required."); } return self::validatedString($value, $name, $maxBytes); } public static function queryInt( string $name, int $default, int $minimum, int $maximum, ): int { $value = $_GET[$name] ?? null; if ($value === null || $value === '') { return $default; } if ( !is_string($value) || filter_var($value, FILTER_VALIDATE_INT) === false ) { throw new HttpException(422, "Query parameter $name must be an integer."); } $integer = (int) $value; if ($integer < $minimum || $integer > $maximum) { throw new HttpException( 422, "Query parameter $name must be between $minimum and $maximum.", ); } return $integer; } public static function requiredQueryInt( string $name, int $minimum, int $maximum, ): int { $value = $_GET[$name] ?? null; if (!is_string($value) || $value === '') { throw new HttpException(422, "Query parameter $name is required."); } if (filter_var($value, FILTER_VALIDATE_INT) === false) { throw new HttpException(422, "Query parameter $name must be an integer."); } $integer = (int) $value; if ($integer < $minimum || $integer > $maximum) { throw new HttpException( 422, "Query parameter $name must be between $minimum and $maximum.", ); } return $integer; } public static function canonicalAddress(string $address): string { $address = strtolower(trim($address)); if (preg_match('/^[a-f0-9]{40}\.(clc|cltc)$/', $address) !== 1) { throw new HttpException(422, 'Enter a valid Contractless wallet address.'); } return $address; } public static function hash(string $hash, string $name): string { $hash = strtolower(trim($hash)); if (preg_match('/^[a-f0-9]{64}$/', $hash) !== 1) { throw new HttpException(422, "Field $name must be a 64-character hash."); } return $hash; } public static function signature(string $signature): string { $signature = strtolower(trim($signature)); if (preg_match('/^[a-f0-9]{1332}$/', $signature) !== 1) { throw new HttpException( 422, 'The signature must contain 1,332 hexadecimal characters.', ); } return $signature; } public static function transactionHex(string $transaction): string { $transaction = strtolower(trim($transaction)); if ( $transaction === '' || (strlen($transaction) % 2) !== 0 || !ctype_xdigit($transaction) ) { throw new HttpException( 422, 'The transaction must be complete hexadecimal data.', ); } return $transaction; } /** @return array */ public static function json(): array { $contentType = strtolower(trim(explode(';', $_SERVER['CONTENT_TYPE'] ?? '')[0])); if ($contentType !== 'application/json') { throw new HttpException(415, 'Content-Type must be application/json.'); } $raw = self::rawBody(); try { $body = json_decode($raw, true, flags: JSON_THROW_ON_ERROR); } catch (\JsonException) { throw new HttpException(400, 'The request body is not valid JSON.'); } if (!is_array($body)) { throw new HttpException(400, 'The request body must be a JSON object.'); } return $body; } /** @param array $body */ public static function bodyString( array $body, string $name, int $maxBytes = 4096, bool $trim = true, ): string { $value = $body[$name] ?? null; if (!is_string($value)) { throw new HttpException(422, "JSON field $name is required."); } return self::validatedString($value, $name, $maxBytes, $trim); } private static function validatedString( string $value, string $name, int $maxBytes, bool $trim = true, ): string { if ($trim) { $value = trim($value); } if ($value === '') { throw new HttpException(422, "Field $name cannot be empty."); } if (strlen($value) > $maxBytes) { throw new HttpException(422, "Field $name is too long."); } return $value; } }