NAME
GDPR::IAB::TCFv2::Validator - declarative compliance checks for TC strings
SYNOPSIS
use GDPR::IAB::TCFv2::Validator;
my $validator = GDPR::IAB::TCFv2::Validator->new(
vendor_id => 284,
consent_purpose_ids => [ 1, 3, 9 ],
legitimate_interest_purpose_ids => [ 10 ],
flexible_purpose_ids => [ 10 ],
verify_disclosed_vendors => 1,
min_tcf_policy_version => 5,
strict_legal_basis => 1,
);
# Fail-fast: stops at the first failing rule.
my $tc_string = '...';
my $result = $validator->validate($tc_string);
# Accumulate every failure for richer error reporting.
$result = $validator->validate_all($tc_string);
if ($result) {
# All rules passed.
}
else {
warn "Compliance failed:\n$result\n"; # stringification = reasons
for my $reason ( $result->reasons ) {
warn "$reason";
}
}
DESCRIPTION
GDPR::IAB::TCFv2::Validator is a small rule engine that turns a static "compliance policy" — required purposes, expected vendor, optional disclosed-vendors check — into a single validate / validate_all call against a TC string (or a pre-parsed GDPR::IAB::TCFv2 object).
Each rule produces a human-readable reason on failure; reasons are collected on a GDPR::IAB::TCFv2::Validator::Result object that overloads boolean and string contexts so it drops into typical error-handling idioms (if (!$result), print "$result\n") without ceremony.
CONSTRUCTOR
new
my $v = GDPR::IAB::TCFv2::Validator->new( %args );
Recognized keys:
vendor_id— the vendor whose access is being validated. Optional in the constructor (can be supplied per call viavalidate(..., vendor_id => N)) but one of the two must be set orvalidate/validate_allwillcroakwith"missing vendor_id".consent_purpose_ids— arrayref of purpose IDs that must have vendor consent. Validated via "is_vendor_consent_allowed" in GDPR::IAB::TCFv2.legitimate_interest_purpose_ids— arrayref of purpose IDs that must have vendor legitimate-interest. Validated via "is_vendor_legitimate_interest_allowed" in GDPR::IAB::TCFv2. The IAB spec forbids LI for Purpose 1 always, and for Purposes 3-6 in TCF v2.2+; those are enforced by the underlying parser and surface here as failures.flexible_purpose_ids— arrayref of purpose IDs that are flexible per the vendor's GVL declaration (the basis can flip if a publisher restriction is present in the TC string). The default basis is derived structurally from the other two lists:If the purpose ID also appears in
consent_purpose_ids, the default basis is consent.If the purpose ID also appears in
legitimate_interest_purpose_ids, the default basis is legitimate interest.
A purpose listed in
flexible_purpose_idsmust also appear in exactly one of the other two lists, or the constructorcroaks. Validated via "is_vendor_allowed_for_flexible_purpose" in GDPR::IAB::TCFv2.verify_disclosed_vendors— boolean. When true, the validator inspects the TC string's Disclosed Vendors segment: if the segment is present, the vendor must appear there or the rule fails with"vendor N not disclosed"(ReasonVendorNotDisclosed). An absent segment is never averify_disclosed_vendorsfailure on its own — that case is owned by the mandatory-segment rules below.Setting
min_tcf_policy_versionto 5 or higher impliesverify_disclosed_vendors(it is auto-enabled), mirroring the Golib-gdprvalidator.Independently of
verify_disclosed_vendors, an absent Disclosed Vendors segment is mandatory (failing with ReasonMissingDisclosedVendors) when either:the TC string was created on/after the TCF v2.3 deadline (date-based,
2026-02-28); orthe TC string's own policy version is
>= 5andmin_tcf_policy_versionis>= 5. A policy-2/4 string under a policy-5 floor fails the floor (ReasonPolicyVersionTooLow) rather than the missing-segment rule.
Otherwise an absent segment is silently ignored (matches legacy behavior).
strict_legal_basis— boolean. Passed through to the underlyingis_vendor_*_allowedcalls (as thestrictnamed argument) so invalid purpose IDs causecroakinstead of a silent failure. Defaults to0.
METHODS
validate
my $result = $validator->validate( $tc_string_or_object, %overrides );
Runs the configured rules against $tc_string_or_object. Stops at the first failing rule (fail-fast mode) and returns a GDPR::IAB::TCFv2::Validator::Result carrying that one reason.
%overrides can replace the constructor values for vendor_id, strict_legal_basis, verify_disclosed_vendors, min_tcf_policy_version, cmp_validator, consent_purpose_ids, legitimate_interest_purpose_ids, and flexible_purpose_ids for this call only.
The list overrides (consent_purpose_ids, legitimate_interest_purpose_ids, flexible_purpose_ids) do not re-validate coherence — orphan entries (a flexible pid that isn't also in one of the basis lists) are silently dropped at runtime rather than fatal. This makes per-call overrides forgiving for callers that generate their lists dynamically; the constructor remains strict for the static policy.
$tc_string_or_object may be either a raw consent string or a pre-parsed GDPR::IAB::TCFv2 object — handy when the same TC string is being validated against multiple policies.
validate_all
Identical to "validate" but runs every rule and accumulates all failures into the result. Use when you want a complete error report rather than the first failure.
SEE ALSO
GDPR::IAB::TCFv2::Validator::Result for the result-object API, including the bool / "" overloads and the $\-aware stringification.
GDPR::IAB::TCFv2 for the underlying parser and the is_vendor_*_allowed family of methods this validator is built on.