*/ private $context; /** * @var bool */ private $opted_out; /** * Consent decision per category, as it stood at capture time. * * @var array */ private $consent; /** * @param string $anonymous_id Tracker anonymous id. * @param array $context S2S context object. * @param bool $opted_out Whether the visitor had opted out. * @param array $consent Consent decision per category. */ private function __construct( string $anonymous_id, array $context, bool $opted_out, array $consent ) { $this->anonymous_id = $anonymous_id; $this->context = $context; $this->opted_out = $opted_out; $this->consent = $consent; } /** * Freeze what a live context currently says. * * @param VisitorContextInterface $live The request's own context. * @param string[] $categories Consent categories worth remembering. * @return array Storable form. */ public static function capture( VisitorContextInterface $live, array $categories ): array { $consent = array(); foreach ( $categories as $category ) { $consent[ $category ] = $live->allows( $category ); } return array( 'anonymous_id' => (string) $live->anonymous_id(), 'context' => $live->to_array(), 'opted_out' => $live->has_opted_out(), 'consent' => $consent, ); } /** * Rebuild from its stored form. * * @param mixed $stored Stored value. * @return self|null Null when nothing usable was stored. */ public static function from_array( $stored ): ?self { if ( ! is_array( $stored ) ) { return null; } $context = isset( $stored['context'] ) && is_array( $stored['context'] ) ? $stored['context'] : array(); $consent = isset( $stored['consent'] ) && is_array( $stored['consent'] ) ? $stored['consent'] : array(); $normalised = array(); foreach ( $consent as $category => $allowed ) { $normalised[ (string) $category ] = (bool) $allowed; } return new self( isset( $stored['anonymous_id'] ) && is_scalar( $stored['anonymous_id'] ) ? (string) $stored['anonymous_id'] : '', $context, ! empty( $stored['opted_out'] ), $normalised ); } /** * The anonymous id captured at the time. * * @return string|null */ public function anonymous_id(): ?string { return '' !== $this->anonymous_id ? $this->anonymous_id : null; } /** * The context captured at the time. * * @return array */ public function to_array(): array { return $this->context; } /** * Whether the visitor had opted out. * * @return bool */ public function has_opted_out(): bool { return $this->opted_out; } /** * Whether a category was permitted at the time. * * A category nobody thought to record is treated as permitted, matching how a live context reads a * site with no consent banner: absence of a decision is not a refusal. * * @param string $category Consent category. * @return bool */ public function allows( string $category ): bool { return ! array_key_exists( $category, $this->consent ) || $this->consent[ $category ]; } }