*/ private $traits; /** * @param string $external_id Site-scoped id. * @param string $email Email address. * @param string $phone Phone number. * @param array $traits Profile attributes. */ private function __construct( string $external_id, string $email, string $phone, array $traits ) { $this->external_id = $external_id; $this->email = $email; $this->phone = $phone; $this->traits = $traits; } /** * Build from loose input, keeping only what is well-formed. * * @param array $raw Keys: external_id, email, phone, traits. * @return self */ public static function from( array $raw ): self { $email = isset( $raw['email'] ) && is_scalar( $raw['email'] ) ? sanitize_email( (string) $raw['email'] ) : ''; // A malformed address is worse than none: it would be attached to the profile and then match // nothing, so the person stays split across two identities forever. if ( '' !== $email && ! is_email( $email ) ) { $email = ''; } return new self( isset( $raw['external_id'] ) && is_scalar( $raw['external_id'] ) ? sanitize_text_field( (string) $raw['external_id'] ) : '', $email, isset( $raw['phone'] ) && is_scalar( $raw['phone'] ) ? self::normalize_phone( (string) $raw['phone'] ) : '', isset( $raw['traits'] ) && is_array( $raw['traits'] ) ? $raw['traits'] : array() ); } /** * An identity with nothing in it, for calls that only carry an anonymous id. * * @return self */ public static function anonymous(): self { return new self( '', '', '', array() ); } /** * Whether the collector would reject a payload built from this. * * @return bool */ public function is_empty(): bool { return '' === $this->external_id && '' === $this->email && '' === $this->phone; } /** * Profile attributes. * * @return array */ public function traits(): array { return $this->traits; } /** * The identity fields of a payload, with empty values omitted rather than sent blank. * * @return array */ public function to_array(): array { return array_filter( array( 'external_id' => $this->external_id, 'email' => $this->email, 'phone' => $this->phone, ), static function ( $value ) { return '' !== $value; } ); } /** * Keep digits and a single leading plus. * * The collector does the real parsing, with the property's calling code to fall back on; this only * strips the spaces, dashes and parentheses that would otherwise make the same number look like * two different people. * * @param string $phone Raw number. * @return string */ private static function normalize_phone( string $phone ): string { $plus = 0 === strpos( trim( $phone ), '+' ); $digits = preg_replace( '/[^0-9]/', '', $phone ); $digits = is_string( $digits ) ? $digits : ''; if ( strlen( $digits ) < 5 ) { return ''; } return $plus ? '+' . $digits : $digits; } }