Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/profile-builder/admin/admin-functions.php
+++ b/profile-builder/admin/admin-functions.php
@@ -849,3 +849,74 @@
<?php
}
add_action( 'admin_footer', 'wppb_output_deactivation_popup' );
+
+
+/**
+ * Check if current screen is a Profile Builder admin page.
+ *
+ * @return bool
+ */
+function wppb_is_profile_builder_admin_page() {
+
+ if ( ! is_admin() )
+ return false;
+
+ $screen = get_current_screen();
+
+ if ( ! $screen )
+ return false;
+
+ $pb_pages = array(
+ 'profile-builder',
+ 'profile-builder-dashboard',
+ 'profile-builder-basic-info',
+ 'profile-builder-general-settings',
+ 'profile-builder-add-ons',
+ 'manage-fields',
+ );
+
+ foreach ( $pb_pages as $page ) {
+
+ if ( strpos( $screen->id, $page ) !== false )
+ return true;
+
+ }
+
+ if ( ! empty( $screen->post_type ) ) {
+ $pb_cpt_pages = array( 'wppb-ul-cpt', 'wppb-rf-cpt', 'wppb-epf-cpt' );
+
+ if ( in_array( $screen->post_type, $pb_cpt_pages, true ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Output the popup markup used for documentation links from the admin.
+ *
+ * @return void
+ */
+function wppb_output_docs_link_popup() {
+
+ if ( ! wppb_is_profile_builder_admin_page() )
+ return;
+
+ ?>
+ <div id="wppb-docs-link-popup" class="wppb-docs-link-popup" title="<?php echo esc_attr__( 'Need Help?', 'profile-builder' ); ?>" style="display:none;">
+ <div class="wppb-docs-link-popup-content">
+ <img src="<?php echo esc_url( WPPB_PLUGIN_URL . 'assets/images/pb-logo.svg' ); ?>" alt="<?php esc_attr_e( 'Profile Builder', 'profile-builder' ); ?>" width="44" height="44" class="wppb-docs-link-popup-logo">
+ <div>
+ <p class="wppb-docs-link-popup-description"><?php esc_html_e( 'If you need a hand with this setting, you can check the documentation or open a support ticket on WordPress.org.', 'profile-builder' ); ?></p>
+ <p class="wppb-docs-link-popup-description"><?php esc_html_e( 'We will do our best to help you figure it out.', 'profile-builder' ); ?></p>
+ </div>
+ </div>
+ <div class="wppb-docs-link-popup-actions cozmoslabs-wrap">
+ <a href="#" target="_blank" rel="noopener noreferrer" class="button button-primary wppb-docs-link-popup-open-docs"><?php esc_html_e( 'View Documentation', 'profile-builder' ); ?></a>
+ <a href="https://wordpress.org/support/plugin/profile-builder/#new-topic-0" target="_blank" rel="noopener noreferrer" class="button button-primary wppb-docs-link-popup-open-wporg"><?php esc_html_e( 'Open Support Ticket', 'profile-builder' ); ?></a>
+ </div>
+ </div>
+ <?php
+}
+add_action( 'admin_footer', 'wppb_output_docs_link_popup' );
--- a/profile-builder/admin/manage-fields.php
+++ b/profile-builder/admin/manage-fields.php
@@ -107,6 +107,7 @@
// $manage_field_types['optgroups']['standard']['options'][] = 'Radio';
$manage_field_types['optgroups']['standard']['options'][] = 'HTML';
$manage_field_types['optgroups']['standard']['options'][] = 'Upload';
+ $manage_field_types['optgroups']['standard']['options'][] = 'International Telephone Input';
// $manage_field_types['optgroups']['standard']['options'][] = 'Avatar';
$manage_field_types['optgroups']['advanced']['options'][] = 'Phone';
@@ -153,6 +154,7 @@
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Select (Multiple)', 'disabled' => true );
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'HTML', 'disabled' => true );
$manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'Upload', 'disabled' => true );
+ $manage_field_types['optgroups']['standard']['options'][] = array( 'field_name' => 'International Telephone Input', 'disabled' => true );
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Phone', 'disabled' => true );
$manage_field_types['optgroups']['advanced']['options'][] = array( 'field_name' => 'Select (Country)', 'disabled' => true );
@@ -323,8 +325,13 @@
array( 'type' => 'text', 'slug' => 'number-step-value', 'title' => __( 'Number Step Value', 'profile-builder' ), 'description' => __( "Step value 1 to allow only integers, 0.1 to allow integers and numbers with 1 decimal", 'profile-builder' ) .'<br>'. __( "To allow multiple decimals use for eg. 0.01 (for 2 deciamls) and so on", 'profile-builder' ) .'<br>'. __( "You can also use step value to specify the legal number intervals (eg. step value 2 will allow only -4, -2, 0, 2 and so on)", 'profile-builder' ) .'<br>'. __( "Leave it empty for no restriction", 'profile-builder' ) ),
array( 'type' => 'select', 'slug' => 'required', 'title' => __( 'Required', 'profile-builder' ), 'options' => array( 'No', 'Yes' ), 'default' => 'No', 'description' => __( 'Whether the field is required or not', 'profile-builder' ) ),
array( 'type' => 'select', 'slug' => 'overwrite-existing', 'title' => __( 'Overwrite Existing', 'profile-builder' ), 'options' => array( 'No', 'Yes' ), 'default' => 'No', 'description' => __( "Selecting 'Yes' will add the field to the list, but will overwrite any other field in the database that has the same meta-name<br/>Use this at your own risk", 'profile-builder' ) ),
+ array( 'type' => 'text', 'slug' => 'initial-country', 'title' => __( 'Initial Country', 'profile-builder' ), 'description' => __( "Set the initial country for the phone field. Use 'auto' to detect it automatically by IP, or enter one ISO 3166-1 alpha-2 country code (e.g. ro, jp, us).", 'profile-builder' ), 'default' => 'auto' ),
+ array( 'type' => 'text', 'slug' => 'preferred-countries', 'title' => __( 'Preferred Countries', 'profile-builder' ), 'description' => __( "Set preferred countries using ISO 3166-1 alpha-2 codes (e.g. ro, jp, us), separated by commas.", 'profile-builder' ), 'default' => '' ),
+ array( 'type' => 'text', 'slug' => 'excluded-countries', 'title' => __( 'Excluded Countries', 'profile-builder' ), 'description' => __( "Exclude countries using ISO 3166-1 alpha-2 codes (e.g. af, ru, us), separated by commas.", 'profile-builder' ), 'default' => '' ),
+ array( 'type' => 'checkbox', 'slug' => 'national-mode', 'title' => __( 'National Mode', 'profile-builder' ), 'options' => array( '%'.__('Yes','profile-builder').'%'.'yes' ), 'description' => __( "Enable National Mode to enter numbers in local format (without country prefix). Disable it to use International Mode (with country prefix).", 'profile-builder' ) ),
+ array( 'type' => 'checkbox', 'slug' => 'hide-flags', 'title' => __( 'Hide Flags', 'profile-builder' ), 'options' => array( '%'.__('Yes','profile-builder').'%'.'yes' ), 'description' => __( "Enable to hide country flags in the phone field.", 'profile-builder' ) ),
- // Added the new option for the map field type, that allows to customize the POIs load type.
+ // Added the new option for the map field type, that allows to customize the POIs load type.
array(
'type' => 'select',
'slug' => 'map-pins-load-type',
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/CountryCodeSource.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/CountryCodeSource.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * Country code source from number
+ */
+enum CountryCodeSource: int
+{
+ /**
+ * The country_code is derived based on a phone number with a leading "+", e.g. the French
+ * number "+33 1 42 68 53 00".
+ */
+ case FROM_NUMBER_WITH_PLUS_SIGN = 0;
+ /**
+ * The country_code is derived based on a phone number with a leading IDD, e.g. the French
+ * number "011 33 1 42 68 53 00", as it is dialled from US.
+ */
+ case FROM_NUMBER_WITH_IDD = 1;
+ /**
+ * The country_code is derived based on a phone number without a leading "+", e.g. the French
+ * number "33 1 42 68 53 00" when defaultCountry is supplied as France.
+ */
+ case FROM_NUMBER_WITHOUT_PLUS_SIGN = 2;
+ /**
+ * The country_code is derived NOT based on the phone number itself, but from the defaultCountry
+ * parameter provided in the parsing function by the clients. This happens mostly for numbers
+ * written in the national format (without country code). For example, this would be set when
+ * parsing the French number "01 42 68 53 00", when defaultCountry is supplied as France.
+ */
+ case FROM_DEFAULT_COUNTRY = 3;
+
+ case UNSPECIFIED = 4;
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/CountryCodeToRegionCodeMap.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/CountryCodeToRegionCodeMap.php
@@ -0,0 +1,269 @@
+<?php
+
+/**
+ * libphonenumber-for-php-lite data file
+ * This file has been @generated from libphonenumber data
+ * Do not modify!
+ * @internal
+ */
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * @internal
+ */
+class CountryCodeToRegionCodeMap
+{
+ /**
+ * A mapping from a country code to the region codes which denote the
+ * country/region represented by that country code. In the case of multiple
+ * countries sharing a calling code, such as the NANPA countries, the one
+ * indicated with "isMainCountryForCode" in the metadata should be first.
+ * @var array<int,string[]>
+ */
+ public const COUNTRY_CODE_TO_REGION_CODE_MAP = [
+ 1 => [
+ 'US',
+ 'AG',
+ 'AI',
+ 'AS',
+ 'BB',
+ 'BM',
+ 'BS',
+ 'CA',
+ 'DM',
+ 'DO',
+ 'GD',
+ 'GU',
+ 'JM',
+ 'KN',
+ 'KY',
+ 'LC',
+ 'MP',
+ 'MS',
+ 'PR',
+ 'SX',
+ 'TC',
+ 'TT',
+ 'VC',
+ 'VG',
+ 'VI',
+ ],
+ 7 => ['RU', 'KZ'],
+ 20 => ['EG'],
+ 27 => ['ZA'],
+ 30 => ['GR'],
+ 31 => ['NL'],
+ 32 => ['BE'],
+ 33 => ['FR'],
+ 34 => ['ES'],
+ 36 => ['HU'],
+ 39 => ['IT', 'VA'],
+ 40 => ['RO'],
+ 41 => ['CH'],
+ 43 => ['AT'],
+ 44 => ['GB', 'GG', 'IM', 'JE'],
+ 45 => ['DK'],
+ 46 => ['SE'],
+ 47 => ['NO', 'SJ'],
+ 48 => ['PL'],
+ 49 => ['DE'],
+ 51 => ['PE'],
+ 52 => ['MX'],
+ 53 => ['CU'],
+ 54 => ['AR'],
+ 55 => ['BR'],
+ 56 => ['CL'],
+ 57 => ['CO'],
+ 58 => ['VE'],
+ 60 => ['MY'],
+ 61 => ['AU', 'CC', 'CX'],
+ 62 => ['ID'],
+ 63 => ['PH'],
+ 64 => ['NZ'],
+ 65 => ['SG'],
+ 66 => ['TH'],
+ 81 => ['JP'],
+ 82 => ['KR'],
+ 84 => ['VN'],
+ 86 => ['CN'],
+ 90 => ['TR'],
+ 91 => ['IN'],
+ 92 => ['PK'],
+ 93 => ['AF'],
+ 94 => ['LK'],
+ 95 => ['MM'],
+ 98 => ['IR'],
+ 211 => ['SS'],
+ 212 => ['MA', 'EH'],
+ 213 => ['DZ'],
+ 216 => ['TN'],
+ 218 => ['LY'],
+ 220 => ['GM'],
+ 221 => ['SN'],
+ 222 => ['MR'],
+ 223 => ['ML'],
+ 224 => ['GN'],
+ 225 => ['CI'],
+ 226 => ['BF'],
+ 227 => ['NE'],
+ 228 => ['TG'],
+ 229 => ['BJ'],
+ 230 => ['MU'],
+ 231 => ['LR'],
+ 232 => ['SL'],
+ 233 => ['GH'],
+ 234 => ['NG'],
+ 235 => ['TD'],
+ 236 => ['CF'],
+ 237 => ['CM'],
+ 238 => ['CV'],
+ 239 => ['ST'],
+ 240 => ['GQ'],
+ 241 => ['GA'],
+ 242 => ['CG'],
+ 243 => ['CD'],
+ 244 => ['AO'],
+ 245 => ['GW'],
+ 246 => ['IO'],
+ 247 => ['AC'],
+ 248 => ['SC'],
+ 249 => ['SD'],
+ 250 => ['RW'],
+ 251 => ['ET'],
+ 252 => ['SO'],
+ 253 => ['DJ'],
+ 254 => ['KE'],
+ 255 => ['TZ'],
+ 256 => ['UG'],
+ 257 => ['BI'],
+ 258 => ['MZ'],
+ 260 => ['ZM'],
+ 261 => ['MG'],
+ 262 => ['RE', 'YT'],
+ 263 => ['ZW'],
+ 264 => ['NA'],
+ 265 => ['MW'],
+ 266 => ['LS'],
+ 267 => ['BW'],
+ 268 => ['SZ'],
+ 269 => ['KM'],
+ 290 => ['SH', 'TA'],
+ 291 => ['ER'],
+ 297 => ['AW'],
+ 298 => ['FO'],
+ 299 => ['GL'],
+ 350 => ['GI'],
+ 351 => ['PT'],
+ 352 => ['LU'],
+ 353 => ['IE'],
+ 354 => ['IS'],
+ 355 => ['AL'],
+ 356 => ['MT'],
+ 357 => ['CY'],
+ 358 => ['FI', 'AX'],
+ 359 => ['BG'],
+ 370 => ['LT'],
+ 371 => ['LV'],
+ 372 => ['EE'],
+ 373 => ['MD'],
+ 374 => ['AM'],
+ 375 => ['BY'],
+ 376 => ['AD'],
+ 377 => ['MC'],
+ 378 => ['SM'],
+ 380 => ['UA'],
+ 381 => ['RS'],
+ 382 => ['ME'],
+ 383 => ['XK'],
+ 385 => ['HR'],
+ 386 => ['SI'],
+ 387 => ['BA'],
+ 389 => ['MK'],
+ 420 => ['CZ'],
+ 421 => ['SK'],
+ 423 => ['LI'],
+ 500 => ['FK'],
+ 501 => ['BZ'],
+ 502 => ['GT'],
+ 503 => ['SV'],
+ 504 => ['HN'],
+ 505 => ['NI'],
+ 506 => ['CR'],
+ 507 => ['PA'],
+ 508 => ['PM'],
+ 509 => ['HT'],
+ 590 => ['GP', 'BL', 'MF'],
+ 591 => ['BO'],
+ 592 => ['GY'],
+ 593 => ['EC'],
+ 594 => ['GF'],
+ 595 => ['PY'],
+ 596 => ['MQ'],
+ 597 => ['SR'],
+ 598 => ['UY'],
+ 599 => ['CW', 'BQ'],
+ 670 => ['TL'],
+ 672 => ['NF'],
+ 673 => ['BN'],
+ 674 => ['NR'],
+ 675 => ['PG'],
+ 676 => ['TO'],
+ 677 => ['SB'],
+ 678 => ['VU'],
+ 679 => ['FJ'],
+ 680 => ['PW'],
+ 681 => ['WF'],
+ 682 => ['CK'],
+ 683 => ['NU'],
+ 685 => ['WS'],
+ 686 => ['KI'],
+ 687 => ['NC'],
+ 688 => ['TV'],
+ 689 => ['PF'],
+ 690 => ['TK'],
+ 691 => ['FM'],
+ 692 => ['MH'],
+ 800 => ['001'],
+ 808 => ['001'],
+ 850 => ['KP'],
+ 852 => ['HK'],
+ 853 => ['MO'],
+ 855 => ['KH'],
+ 856 => ['LA'],
+ 870 => ['001'],
+ 878 => ['001'],
+ 880 => ['BD'],
+ 881 => ['001'],
+ 882 => ['001'],
+ 883 => ['001'],
+ 886 => ['TW'],
+ 888 => ['001'],
+ 960 => ['MV'],
+ 961 => ['LB'],
+ 962 => ['JO'],
+ 963 => ['SY'],
+ 964 => ['IQ'],
+ 965 => ['KW'],
+ 966 => ['SA'],
+ 967 => ['YE'],
+ 968 => ['OM'],
+ 970 => ['PS'],
+ 971 => ['AE'],
+ 972 => ['IL'],
+ 973 => ['BH'],
+ 974 => ['QA'],
+ 975 => ['BT'],
+ 976 => ['MN'],
+ 977 => ['NP'],
+ 979 => ['001'],
+ 992 => ['TJ'],
+ 993 => ['TM'],
+ 994 => ['AZ'],
+ 995 => ['GE'],
+ 996 => ['KG'],
+ 998 => ['UZ'],
+ ];
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MatchType.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MatchType.php
@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * Types of phone number matches
+ * See detailed description beside the isNumberMatch() method
+ */
+enum MatchType: int
+{
+ case NOT_A_NUMBER = 0;
+ case NO_MATCH = 1;
+ case SHORT_NSN_MATCH = 2;
+ case NSN_MATCH = 3;
+ case EXACT_MATCH = 4;
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/Matcher.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/Matcher.php
@@ -0,0 +1,151 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * Matcher for various regex matching
+ *
+ * Note that this is NOT the same as google's java PhoneNumberMatcher class.
+ * This class is a minimal port of java's built-in matcher class, whereas PhoneNumberMatcher
+ * is designed to recognize phone numbers embedded in any text.
+ *
+ * @internal
+ */
+class Matcher
+{
+ protected string $pattern;
+
+ protected string $subject = '';
+
+ /**
+ * @var array<int,mixed>
+ */
+ protected array $groups = [];
+
+ private int $searchIndex = 0;
+
+ public function __construct(string $pattern, string $subject)
+ {
+ $this->pattern = str_replace('/', '/', $pattern);
+ $this->subject = $subject;
+ }
+
+ protected function doMatch(string $type = 'find', int $offset = 0): bool
+ {
+ $final_pattern = '(?:' . $this->pattern . ')';
+ switch ($type) {
+ case 'matches':
+ $final_pattern = '^' . $final_pattern . '$';
+ break;
+ case 'lookingAt':
+ $final_pattern = '^' . $final_pattern;
+ break;
+ case 'find':
+ default:
+ // no changes
+ break;
+ }
+ $final_pattern = '/' . $final_pattern . '/ui';
+
+ $search = mb_substr($this->subject, $offset);
+
+ $result = preg_match($final_pattern, $search, $groups, PREG_OFFSET_CAPTURE);
+
+ if ($result === 1) {
+ // Expand $groups into $this->groups, but being multi-byte aware
+
+ $positions = [];
+
+ foreach ($groups as $group) {
+ $positions[] = [
+ $group[0],
+ $offset + mb_strlen(substr($search, 0, $group[1])),
+ ];
+ }
+
+ $this->groups = $positions;
+ }
+
+ return ($result === 1);
+ }
+
+ public function matches(): bool
+ {
+ return $this->doMatch('matches');
+ }
+
+ public function lookingAt(): bool
+ {
+ return $this->doMatch('lookingAt');
+ }
+
+ public function find(?int $offset = null): bool
+ {
+ if ($offset === null) {
+ $offset = $this->searchIndex;
+ }
+
+ // Increment search index for the next time we call this
+ $this->searchIndex++;
+ return $this->doMatch('find', $offset);
+ }
+
+ public function groupCount(): ?int
+ {
+ if ($this->groups === []) {
+ return null;
+ }
+
+ return count($this->groups) - 1;
+ }
+
+ public function group(?int $group = null): ?string
+ {
+ if ($group === null) {
+ $group = 0;
+ }
+ return $this->groups[$group][0] ?? null;
+ }
+
+ public function end(?int $group = null): ?int
+ {
+ if ($group === null) {
+ $group = 0;
+ }
+ if (!isset($this->groups[$group])) {
+ return null;
+ }
+ return $this->groups[$group][1] + mb_strlen($this->groups[$group][0]);
+ }
+
+ public function start(?int $group = null): mixed
+ {
+ if ($group === null) {
+ $group = 0;
+ }
+ if (!isset($this->groups[$group])) {
+ return null;
+ }
+
+ return $this->groups[$group][1];
+ }
+
+ public function replaceFirst(string $replacement): string
+ {
+ return preg_replace('/' . $this->pattern . '/x', $replacement, $this->subject, 1);
+ }
+
+ public function replaceAll(string $replacement): string
+ {
+ return preg_replace('/' . $this->pattern . '/x', $replacement, $this->subject);
+ }
+
+ public function reset(string $input = ''): static
+ {
+ $this->subject = $input;
+
+ return $this;
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MatcherAPIInterface.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MatcherAPIInterface.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * Interface MatcherAPIInterface
+ *
+ * Internal phonenumber matching API used to isolate the underlying implementation of the
+ * matcher and allow different implementations to be swapped in easily.
+ *
+ * @package libphonenumber
+ * @internal
+ */
+interface MatcherAPIInterface
+{
+ /**
+ * Returns whether the given national number (a string containing only decimal digits) matches
+ * the national number pattern defined in the given {@code PhoneNumberDesc} message.
+ */
+ public function matchNationalNumber(string $number, PhoneNumberDesc $numberDesc, bool $allowPrefixMatch): bool;
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MetadataSourceInterface.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MetadataSourceInterface.php
@@ -0,0 +1,18 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+interface MetadataSourceInterface
+{
+ /**
+ * Gets phone metadata for a region.
+ */
+ public function getMetadataForRegion(string $regionCode): PhoneMetadata;
+
+ /**
+ * Gets phone metadata for a non-geographical region.
+ */
+ public function getMetadataForNonGeographicalRegion(int $countryCallingCode): PhoneMetadata;
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MultiFileMetadataSourceImpl.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/MultiFileMetadataSourceImpl.php
@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+use RuntimeException;
+
+/**
+ * @internal
+ */
+class MultiFileMetadataSourceImpl implements MetadataSourceInterface
+{
+ /**
+ * A mapping from a region code to the PhoneMetadata for that region.
+ * @var PhoneMetadata[]
+ */
+ protected array $regionToMetadataMap = [];
+
+ /**
+ * A mapping from a country calling code for a non-geographical entity to the PhoneMetadata for
+ * that country calling code. Examples of the country calling codes include 800 (International
+ * Toll Free Service) and 808 (International Shared Cost Service).
+ * @var PhoneMetadata[]
+ */
+ protected array $countryCodeToNonGeographicalMetadataMap = [];
+
+ /**
+ * @param string $currentFilePrefix The prefix of the metadata class names from which region data is loaded
+ */
+ public function __construct(
+ protected readonly string $currentFilePrefix = __NAMESPACE__ . 'dataPhoneNumberMetadata_'
+ ) {}
+
+ public function getMetadataForRegion(string $regionCode): PhoneMetadata
+ {
+ $regionCode = strtoupper($regionCode);
+
+ if (!isset($this->regionToMetadataMap[$regionCode])) {
+ // The regionCode here will be valid and won't be '001', so we don't need to worry about
+ // what to pass in for the country calling code.
+ $this->loadMetadataFromFile($this->currentFilePrefix, $regionCode, 0);
+ }
+
+ return $this->regionToMetadataMap[$regionCode];
+ }
+
+ public function getMetadataForNonGeographicalRegion(int $countryCallingCode): PhoneMetadata
+ {
+ if (!isset($this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode])) {
+ $this->loadMetadataFromFile($this->currentFilePrefix, PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY, $countryCallingCode);
+ }
+
+ return $this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode];
+ }
+
+ /**
+ * @throws RuntimeException
+ */
+ public function loadMetadataFromFile(string $filePrefix, string $regionCode, int $countryCallingCode): void
+ {
+ $regionCode = strtoupper($regionCode);
+
+ $isNonGeoRegion = PhoneNumberUtil::REGION_CODE_FOR_NON_GEO_ENTITY === $regionCode;
+
+ $class = $filePrefix . ($isNonGeoRegion ? $countryCallingCode : ucfirst($regionCode));
+
+ if (!class_exists($class)) {
+ throw new RuntimeException('missing metadata: ' . $class);
+ }
+
+ $metadata = new $class();
+
+ if (!$metadata instanceof PhoneMetadata) {
+ throw new RuntimeException('invalid metadata: ' . $class);
+ }
+
+ if ($isNonGeoRegion) {
+ $this->countryCodeToNonGeographicalMetadataMap[$countryCallingCode] = $metadata;
+ } else {
+ $this->regionToMetadataMap[$regionCode] = $metadata;
+ }
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/NumberFormat.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/NumberFormat.php
@@ -0,0 +1,183 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * Number Format
+ * @internal
+ */
+class NumberFormat
+{
+ protected string $pattern = '';
+ protected bool $hasPattern = false;
+ protected string $format = '';
+ protected bool $hasFormat = false;
+ /**
+ * @var array<int,string>
+ */
+ protected array $leadingDigitsPattern = [];
+ protected string $nationalPrefixFormattingRule = '';
+ protected bool $hasNationalPrefixFormattingRule = false;
+ protected bool $nationalPrefixOptionalWhenFormatting = false;
+ protected bool $hasNationalPrefixOptionalWhenFormatting = false;
+ protected string $domesticCarrierCodeFormattingRule = '';
+ protected bool $hasDomesticCarrierCodeFormattingRule = false;
+
+ public function hasPattern(): bool
+ {
+ return $this->hasPattern;
+ }
+
+ public function getPattern(): string
+ {
+ return $this->pattern;
+ }
+
+ public function setPattern(string $value): static
+ {
+ $this->hasPattern = true;
+ $this->pattern = $value;
+
+ return $this;
+ }
+
+ public function hasNationalPrefixOptionalWhenFormatting(): bool
+ {
+ return $this->hasNationalPrefixOptionalWhenFormatting;
+ }
+
+ public function getNationalPrefixOptionalWhenFormatting(): bool
+ {
+ return $this->nationalPrefixOptionalWhenFormatting;
+ }
+
+ public function setNationalPrefixOptionalWhenFormatting(bool $nationalPrefixOptionalWhenFormatting): static
+ {
+ $this->hasNationalPrefixOptionalWhenFormatting = true;
+ $this->nationalPrefixOptionalWhenFormatting = $nationalPrefixOptionalWhenFormatting;
+
+ return $this;
+ }
+
+ public function hasFormat(): bool
+ {
+ return $this->hasFormat;
+ }
+
+ public function getFormat(): string
+ {
+ return $this->format;
+ }
+
+ public function setFormat(string $value): static
+ {
+ $this->hasFormat = true;
+ $this->format = $value;
+
+ return $this;
+ }
+
+ /**
+ * @return array<int,string>
+ */
+ public function leadingDigitPatterns(): array
+ {
+ return $this->leadingDigitsPattern;
+ }
+
+ public function leadingDigitsPatternSize(): int
+ {
+ return count($this->leadingDigitsPattern);
+ }
+
+ public function getLeadingDigitsPattern(int $index): string
+ {
+ return $this->leadingDigitsPattern[$index];
+ }
+
+ /**
+ * @param array<int,string> $patterns
+ */
+ public function setLeadingDigitsPattern(array $patterns): static
+ {
+ $this->leadingDigitsPattern = $patterns;
+ return $this;
+ }
+
+ public function addLeadingDigitsPattern(string $value): static
+ {
+ $this->leadingDigitsPattern[] = $value;
+
+ return $this;
+ }
+
+ public function hasNationalPrefixFormattingRule(): bool
+ {
+ return $this->hasNationalPrefixFormattingRule;
+ }
+
+ public function getNationalPrefixFormattingRule(): string
+ {
+ return $this->nationalPrefixFormattingRule;
+ }
+
+ public function setNationalPrefixFormattingRule(string $value): static
+ {
+ $this->hasNationalPrefixFormattingRule = true;
+ $this->nationalPrefixFormattingRule = $value;
+
+ return $this;
+ }
+
+ public function clearNationalPrefixFormattingRule(): static
+ {
+ $this->nationalPrefixFormattingRule = '';
+
+ return $this;
+ }
+
+ public function hasDomesticCarrierCodeFormattingRule(): bool
+ {
+ return $this->hasDomesticCarrierCodeFormattingRule;
+ }
+
+ public function getDomesticCarrierCodeFormattingRule(): string
+ {
+ return $this->domesticCarrierCodeFormattingRule;
+ }
+
+ public function setDomesticCarrierCodeFormattingRule(string $value): static
+ {
+ $this->hasDomesticCarrierCodeFormattingRule = true;
+ $this->domesticCarrierCodeFormattingRule = $value;
+
+ return $this;
+ }
+
+ public function mergeFrom(NumberFormat $other): static
+ {
+ if ($other->hasPattern()) {
+ $this->setPattern($other->getPattern());
+ }
+ if ($other->hasFormat()) {
+ $this->setFormat($other->getFormat());
+ }
+ $leadingDigitsPatternSize = $other->leadingDigitsPatternSize();
+ for ($i = 0; $i < $leadingDigitsPatternSize; $i++) {
+ $this->addLeadingDigitsPattern($other->getLeadingDigitsPattern($i));
+ }
+ if ($other->hasNationalPrefixFormattingRule()) {
+ $this->setNationalPrefixFormattingRule($other->getNationalPrefixFormattingRule());
+ }
+ if ($other->hasDomesticCarrierCodeFormattingRule()) {
+ $this->setDomesticCarrierCodeFormattingRule($other->getDomesticCarrierCodeFormattingRule());
+ }
+ if ($other->hasNationalPrefixOptionalWhenFormatting()) {
+ $this->setNationalPrefixOptionalWhenFormatting($other->getNationalPrefixOptionalWhenFormatting());
+ }
+
+ return $this;
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/NumberParseException.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/NumberParseException.php
@@ -0,0 +1,64 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+use Exception;
+use Stringable;
+use Throwable;
+
+/**
+ * Generic exception class for errors encountered when parsing phone numbers.
+ */
+class NumberParseException extends Exception implements Stringable
+{
+ /**
+ * The country code supplied did not belong to a supported country or non-geographical entity.
+ */
+ public const INVALID_COUNTRY_CODE = 0;
+ /**
+ * This indicates the string passed is not a valid number. Either the string had less than 3
+ * digits in it or had an invalid phone-context parameter. More specifically, the number failed
+ * to match the regular expression VALID_PHONE_NUMBER, RFC3966_GLOBAL_NUMBER_DIGITS, or
+ * RFC3966_DOMAINNAME in PhoneNumberUtil
+ */
+ public const NOT_A_NUMBER = 1;
+ /**
+ * This indicates the string started with an international dialing prefix, but after this was
+ * stripped from the number, had less digits than any valid phone number (including country
+ * code) could have.
+ */
+ public const TOO_SHORT_AFTER_IDD = 2;
+ /**
+ * This indicates the string, after any country code has been stripped, had less digits than any
+ * valid phone number could have.
+ */
+ public const TOO_SHORT_NSN = 3;
+ /**
+ * This indicates the string had more digits than any valid phone number could have.
+ */
+ public const TOO_LONG = 4;
+
+ protected int $errorType;
+
+ public function __construct(int $errorType, string $message, ?Throwable $previous = null)
+ {
+ parent::__construct($message, $errorType, $previous);
+ $this->message = $message;
+ $this->errorType = $errorType;
+ }
+
+ /**
+ * Returns the error type of the exception that has been thrown.
+ */
+ public function getErrorType(): int
+ {
+ return $this->errorType;
+ }
+
+ public function __toString(): string
+ {
+ return 'Error type: ' . $this->errorType . '. ' . $this->message;
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneMetadata.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneMetadata.php
@@ -0,0 +1,305 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+use function count;
+
+/**
+ * Class PhoneMetadata
+ * @package libphonenumber
+ * @internal Used internally, and can change at any time
+ */
+class PhoneMetadata
+{
+ /**
+ * @var string|null
+ */
+ protected const ID = null;
+ /**
+ * @var int|null
+ */
+ protected const COUNTRY_CODE = null;
+ /**
+ * @var string|null
+ */
+ protected const LEADING_DIGITS = null;
+ /**
+ * @var string|null
+ */
+ protected const NATIONAL_PREFIX = null;
+ protected ?string $nationalPrefixForParsing = null;
+ protected ?string $internationalPrefix = null;
+ protected ?string $preferredInternationalPrefix = null;
+ protected ?string $nationalPrefixTransformRule = null;
+ protected ?string $preferredExtnPrefix = null;
+ protected bool $mainCountryForCode = false;
+ protected bool $mobileNumberPortableRegion = false;
+ protected ?PhoneNumberDesc $generalDesc = null;
+ protected ?PhoneNumberDesc $mobile = null;
+ protected ?PhoneNumberDesc $premiumRate = null;
+ protected ?PhoneNumberDesc $fixedLine = null;
+ protected bool $sameMobileAndFixedLinePattern = false;
+ /**
+ * @var NumberFormat[]
+ */
+ protected array $numberFormat = [];
+ protected ?PhoneNumberDesc $tollFree = null;
+ protected ?PhoneNumberDesc $sharedCost = null;
+ protected ?PhoneNumberDesc $personalNumber = null;
+ protected ?PhoneNumberDesc $voip = null;
+ protected ?PhoneNumberDesc $pager = null;
+ protected ?PhoneNumberDesc $uan = null;
+ protected ?PhoneNumberDesc $emergency = null;
+ protected ?PhoneNumberDesc $voicemail = null;
+ protected ?PhoneNumberDesc $short_code = null;
+ protected ?PhoneNumberDesc $standard_rate = null;
+ protected ?PhoneNumberDesc $carrierSpecific = null;
+ protected ?PhoneNumberDesc $smsServices = null;
+ protected ?PhoneNumberDesc $noInternationalDialling = null;
+ /**
+ * @var NumberFormat[]
+ */
+ protected array $intlNumberFormat = [];
+
+ public function isMainCountryForCode(): bool
+ {
+ return $this->mainCountryForCode;
+ }
+
+ public function getMainCountryForCode(): bool
+ {
+ return $this->mainCountryForCode;
+ }
+
+ public function numberFormatSize(): int
+ {
+ return count($this->numberFormat);
+ }
+
+ public function getNumberFormat(int $index): NumberFormat
+ {
+ return $this->numberFormat[$index];
+ }
+
+ public function intlNumberFormatSize(): int
+ {
+ return count($this->intlNumberFormat);
+ }
+
+ public function getIntlNumberFormat(int $index): NumberFormat
+ {
+ return $this->intlNumberFormat[$index];
+ }
+
+ public function hasGeneralDesc(): bool
+ {
+ return $this->generalDesc !== null;
+ }
+
+ public function getGeneralDesc(): ?PhoneNumberDesc
+ {
+ return $this->generalDesc;
+ }
+
+ public function hasFixedLine(): bool
+ {
+ return $this->fixedLine !== null;
+ }
+
+ public function getFixedLine(): ?PhoneNumberDesc
+ {
+ return $this->fixedLine;
+ }
+
+ public function hasMobile(): bool
+ {
+ return $this->mobile !== null;
+ }
+
+ public function getMobile(): ?PhoneNumberDesc
+ {
+ return $this->mobile;
+ }
+
+ public function getTollFree(): ?PhoneNumberDesc
+ {
+ return $this->tollFree;
+ }
+
+ public function getPremiumRate(): ?PhoneNumberDesc
+ {
+ return $this->premiumRate;
+ }
+
+ public function getSharedCost(): ?PhoneNumberDesc
+ {
+ return $this->sharedCost;
+ }
+
+ public function getPersonalNumber(): ?PhoneNumberDesc
+ {
+ return $this->personalNumber;
+ }
+
+ public function getVoip(): ?PhoneNumberDesc
+ {
+ return $this->voip;
+ }
+
+ public function getPager(): ?PhoneNumberDesc
+ {
+ return $this->pager;
+ }
+
+ public function getUan(): ?PhoneNumberDesc
+ {
+ return $this->uan;
+ }
+
+ public function hasEmergency(): bool
+ {
+ return $this->emergency !== null;
+ }
+
+ public function getEmergency(): ?PhoneNumberDesc
+ {
+ return $this->emergency;
+ }
+
+ public function getVoicemail(): ?PhoneNumberDesc
+ {
+ return $this->voicemail;
+ }
+
+ public function getShortCode(): ?PhoneNumberDesc
+ {
+ return $this->short_code;
+ }
+
+
+ public function getStandardRate(): ?PhoneNumberDesc
+ {
+ return $this->standard_rate;
+ }
+
+ public function getCarrierSpecific(): ?PhoneNumberDesc
+ {
+ return $this->carrierSpecific;
+ }
+
+ public function getSmsServices(): ?PhoneNumberDesc
+ {
+ return $this->smsServices;
+ }
+
+ public function getNoInternationalDialling(): ?PhoneNumberDesc
+ {
+ return $this->noInternationalDialling;
+ }
+
+
+ public function getId(): ?string
+ {
+ return static::ID;
+ }
+
+ public function getCountryCode(): ?int
+ {
+ return static::COUNTRY_CODE;
+ }
+
+ public function getInternationalPrefix(): ?string
+ {
+ return $this->internationalPrefix;
+ }
+
+
+ public function hasPreferredInternationalPrefix(): bool
+ {
+ return ($this->preferredInternationalPrefix !== null);
+ }
+
+ public function getPreferredInternationalPrefix(): ?string
+ {
+ return $this->preferredInternationalPrefix;
+ }
+
+ public function hasNationalPrefix(): bool
+ {
+ return static::NATIONAL_PREFIX !== null;
+ }
+
+ public function getNationalPrefix(): ?string
+ {
+ return static::NATIONAL_PREFIX;
+ }
+
+ public function hasPreferredExtnPrefix(): bool
+ {
+ return $this->preferredExtnPrefix !== null;
+ }
+
+ public function getPreferredExtnPrefix(): ?string
+ {
+ return $this->preferredExtnPrefix;
+ }
+
+ public function hasNationalPrefixForParsing(): bool
+ {
+ return $this->nationalPrefixForParsing !== null;
+ }
+
+ public function getNationalPrefixForParsing(): ?string
+ {
+ return $this->nationalPrefixForParsing;
+ }
+
+ public function getNationalPrefixTransformRule(): ?string
+ {
+ return $this->nationalPrefixTransformRule;
+ }
+
+ public function getSameMobileAndFixedLinePattern(): bool
+ {
+ return $this->sameMobileAndFixedLinePattern;
+ }
+
+ /**
+ * @return NumberFormat[]
+ */
+ public function numberFormats(): array
+ {
+ return $this->numberFormat;
+ }
+
+ /**
+ * @return NumberFormat[]
+ */
+ public function intlNumberFormats(): array
+ {
+ return $this->intlNumberFormat;
+ }
+
+ public function hasLeadingDigits(): bool
+ {
+ return static::LEADING_DIGITS !== null;
+ }
+
+ public function getLeadingDigits(): ?string
+ {
+ return static::LEADING_DIGITS;
+ }
+
+ public function isMobileNumberPortableRegion(): bool
+ {
+ return $this->mobileNumberPortableRegion;
+ }
+
+ public function setInternationalPrefix(string $value): static
+ {
+ $this->internationalPrefix = $value;
+ return $this;
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumber.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumber.php
@@ -0,0 +1,409 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+use Serializable;
+
+/**
+ * It is not recommended to create PhoneNumber objects directly, instead you should
+ * use PhoneNumberUtil::parse() to parse the number and return a PhoneNumber object
+ * @no-named-arguments
+ */
+class PhoneNumber implements Serializable
+{
+ /**
+ * The country calling code for this number, as defined by the International Telecommunication Union
+ * (ITU). For example, this would be 1 for NANPA countries, and 33 for France.
+ */
+ protected ?int $countryCode = null;
+ /**
+ * National (significant) Number is defined in International Telecommunication Union (ITU)
+ * Recommendation E.164. It is a language/country-neutral representation of a phone number at a
+ * country level. For countries which have the concept of an "area code" or "national destination
+ * code", this is included in the National (significant) Number. Although the ITU says the maximum
+ * length should be 15, we have found longer numbers in some countries e.g. Germany.
+ *
+ * Note that the National (significant) Number does not contain the National(trunk) prefix.
+ */
+ protected ?string $nationalNumber = null;
+ /**
+ * Extension is not standardized in ITU recommendations, except for being defined as a series of
+ * numbers with a maximum length of 40 digits. It is defined as a string here to accommodate for the
+ * possible use of a leading zero in the extension (organizations have complete freedom to do so,
+ * as there is no standard defined). However, only ASCII digits should be stored here.
+ */
+ protected ?string $extension = null;
+ /**
+ * In some countries, the national (significant) number starts with one or more "0"s without this
+ * being a national prefix or trunk code of some kind. For example, the leading zero in the national
+ * (significant) number of an Italian phone number indicates the number is a fixed-line number.
+ * There have been plans to migrate fixed-line numbers to start with the digit two since December
+ * 2000, but it has not happened yet. See http://en.wikipedia.org/wiki/%2B39 for more details.
+ *
+ * These fields can be safely ignored (there is no need to set them) for most countries. Some
+ * limited number of countries behave like Italy - for these cases, if the leading zero(s) of a
+ * number would be retained even when dialling internationally, set this flag to true, and also
+ * set the number of leading zeros.
+ *
+ * Clients who use the parsing functionality of the i18n phone number libraries
+ * will have these fields set if necessary automatically.
+ */
+ protected ?bool $italianLeadingZero = null;
+ /**
+ * This field is used to store the raw input string containing phone numbers before it was
+ * canonicalized by the library. For example, it could be used to store alphanumerical numbers
+ * such as "1-800-GOOG-411".
+ */
+ protected ?string $rawInput = null;
+ /**
+ * The source from which the country_code is derived. This is not set in the general parsing method,
+ * but in the method that parses and keeps raw_input. New fields could be added upon request.
+ */
+ protected ?CountryCodeSource $countryCodeSource = CountryCodeSource::UNSPECIFIED;
+ /**
+ * The carrier selection code that is preferred when calling this phone number domestically. This
+ * also includes codes that need to be dialed in some countries when calling from landlines to
+ * mobiles or vice versa. For example, in Columbia, a "3" needs to be dialed before the phone number
+ * itself when calling from a mobile phone to a domestic landline phone and vice versa.
+ *
+ * Note this is the "preferred" code, which means other codes may work as well.
+ */
+ protected ?string $preferredDomesticCarrierCode = null;
+ /**
+ * Whether this phone number has a number of leading zeros set.
+ */
+ protected bool $hasNumberOfLeadingZeros = false;
+ /**
+ * The number of leading zeros of this phone number.
+ */
+ protected int $numberOfLeadingZeros = 1;
+
+ public function clear(): static
+ {
+ $this->clearCountryCode();
+ $this->clearNationalNumber();
+ $this->clearExtension();
+ $this->clearItalianLeadingZero();
+ $this->clearNumberOfLeadingZeros();
+ $this->clearRawInput();
+ $this->clearCountryCodeSource();
+ $this->clearPreferredDomesticCarrierCode();
+ return $this;
+ }
+
+ public function clearCountryCode(): static
+ {
+ $this->countryCode = null;
+ return $this;
+ }
+
+ public function clearNationalNumber(): static
+ {
+ $this->nationalNumber = null;
+ return $this;
+ }
+
+ public function clearExtension(): static
+ {
+ $this->extension = null;
+ return $this;
+ }
+
+ public function clearItalianLeadingZero(): static
+ {
+ $this->italianLeadingZero = null;
+ return $this;
+ }
+
+ public function clearNumberOfLeadingZeros(): static
+ {
+ $this->hasNumberOfLeadingZeros = false;
+ $this->numberOfLeadingZeros = 1;
+ return $this;
+ }
+
+ public function clearRawInput(): static
+ {
+ $this->rawInput = null;
+ return $this;
+ }
+
+ public function clearCountryCodeSource(): static
+ {
+ $this->countryCodeSource = CountryCodeSource::UNSPECIFIED;
+ return $this;
+ }
+
+ public function clearPreferredDomesticCarrierCode(): static
+ {
+ $this->preferredDomesticCarrierCode = null;
+ return $this;
+ }
+
+ /**
+ * Merges the information from another phone number into this phone number.
+ */
+ public function mergeFrom(PhoneNumber $other): static
+ {
+ if ($other->hasCountryCode()) {
+ $this->setCountryCode($other->getCountryCode());
+ }
+ if ($other->hasNationalNumber()) {
+ $this->setNationalNumber($other->getNationalNumber());
+ }
+ if ($other->hasExtension()) {
+ $this->setExtension($other->getExtension());
+ }
+ if ($other->hasItalianLeadingZero()) {
+ $this->setItalianLeadingZero($other->isItalianLeadingZero());
+ }
+ if ($other->hasNumberOfLeadingZeros()) {
+ $this->setNumberOfLeadingZeros($other->getNumberOfLeadingZeros());
+ }
+ if ($other->hasRawInput()) {
+ $this->setRawInput($other->getRawInput());
+ }
+ if ($other->hasCountryCodeSource()) {
+ $this->setCountryCodeSource($other->getCountryCodeSource());
+ }
+ if ($other->hasPreferredDomesticCarrierCode()) {
+ $this->setPreferredDomesticCarrierCode($other->getPreferredDomesticCarrierCode());
+ }
+ return $this;
+ }
+
+ public function hasCountryCode(): bool
+ {
+ return $this->countryCode !== null;
+ }
+
+ public function getCountryCode(): ?int
+ {
+ return $this->countryCode;
+ }
+
+ public function setCountryCode(int $value): static
+ {
+ $this->countryCode = $value;
+ return $this;
+ }
+
+ public function hasNationalNumber(): bool
+ {
+ return $this->nationalNumber !== null;
+ }
+
+ public function getNationalNumber(): ?string
+ {
+ return $this->nationalNumber;
+ }
+
+ public function setNationalNumber(string $value): static
+ {
+ $this->nationalNumber = $value;
+ return $this;
+ }
+
+ public function hasExtension(): bool
+ {
+ return isset($this->extension) && $this->extension !== '';
+ }
+
+ public function getExtension(): ?string
+ {
+ return $this->extension;
+ }
+
+ public function setExtension(string $value): static
+ {
+ $this->extension = $value;
+ return $this;
+ }
+
+ public function hasItalianLeadingZero(): bool
+ {
+ return isset($this->italianLeadingZero);
+ }
+
+ public function setItalianLeadingZero(bool $value): static
+ {
+ $this->italianLeadingZero = $value;
+ return $this;
+ }
+
+ /**
+ * Returns whether this phone number uses an italian leading zero.
+ *
+ * @return bool|null True if it uses an italian leading zero, false it it does not, null if not set.
+ */
+ public function isItalianLeadingZero(): ?bool
+ {
+ return $this->italianLeadingZero ?? null;
+ }
+
+ public function hasNumberOfLeadingZeros(): bool
+ {
+ return $this->hasNumberOfLeadingZeros;
+ }
+
+ public function getNumberOfLeadingZeros(): int
+ {
+ return $this->numberOfLeadingZeros;
+ }
+
+ public function setNumberOfLeadingZeros(int $value): static
+ {
+ $this->hasNumberOfLeadingZeros = true;
+ $this->numberOfLeadingZeros = $value;
+ return $this;
+ }
+
+ public function hasRawInput(): bool
+ {
+ return isset($this->rawInput);
+ }
+
+ public function getRawInput(): ?string
+ {
+ return $this->rawInput;
+ }
+
+ public function setRawInput(string $value): static
+ {
+ $this->rawInput = $value;
+ return $this;
+ }
+
+ public function hasCountryCodeSource(): bool
+ {
+ return $this->countryCodeSource !== CountryCodeSource::UNSPECIFIED;
+ }
+
+ public function getCountryCodeSource(): ?CountryCodeSource
+ {
+ return $this->countryCodeSource;
+ }
+
+ public function setCountryCodeSource(CountryCodeSource $value): static
+ {
+ $this->countryCodeSource = $value;
+ return $this;
+ }
+
+ public function hasPreferredDomesticCarrierCode(): bool
+ {
+ return isset($this->preferredDomesticCarrierCode);
+ }
+
+ public function getPreferredDomesticCarrierCode(): ?string
+ {
+ return $this->preferredDomesticCarrierCode;
+ }
+
+ public function setPreferredDomesticCarrierCode(string $value): static
+ {
+ $this->preferredDomesticCarrierCode = $value;
+ return $this;
+ }
+
+ /**
+ * Returns whether this phone number is equal to another.
+ *
+ * @param PhoneNumber $other The phone number to compare.
+ *
+ * @return bool True if the phone numbers are equal, false otherwise.
+ */
+ public function equals(PhoneNumber $other): bool
+ {
+ if ($this === $other) {
+ return true;
+ }
+
+ return $this->countryCode === $other->countryCode
+ && $this->nationalNumber === $other->nationalNumber
+ && $this->extension === $other->extension
+ && $this->italianLeadingZero === $other->italianLeadingZero
+ && $this->numberOfLeadingZeros === $other->numberOfLeadingZeros
+ && $this->rawInput === $other->rawInput
+ && $this->countryCodeSource === $other->countryCodeSource
+ && $this->preferredDomesticCarrierCode === $other->preferredDomesticCarrierCode;
+ }
+
+ /**
+ * Returns a string representation of this phone number.
+ */
+ public function __toString(): string
+ {
+ $outputString = 'Country Code: ' . $this->countryCode;
+ $outputString .= ' National Number: ' . $this->nationalNumber;
+ if ($this->hasItalianLeadingZero()) {
+ $outputString .= ' Leading Zero(s): true';
+ }
+ if ($this->hasNumberOfLeadingZeros()) {
+ $outputString .= ' Number of leading zeros: ' . $this->numberOfLeadingZeros;
+ }
+ if ($this->hasExtension()) {
+ $outputString .= ' Extension: ' . $this->extension;
+ }
+ if ($this->hasCountryCodeSource()) {
+ $outputString .= ' Country Code Source: ' . $this->countryCodeSource->name;
+ }
+ if ($this->hasPreferredDomesticCarrierCode()) {
+ $outputString .= ' Preferred Domestic Carrier Code: ' . $this->preferredDomesticCarrierCode;
+ }
+ return $outputString;
+ }
+
+ public function serialize(): ?string
+ {
+ return serialize($this->__serialize());
+ }
+
+ public function __serialize(): array
+ {
+ return [
+ $this->countryCode,
+ $this->nationalNumber,
+ $this->extension,
+ $this->italianLeadingZero,
+ $this->numberOfLeadingZeros,
+ $this->rawInput,
+ $this->countryCodeSource,
+ $this->preferredDomesticCarrierCode,
+ ];
+ }
+
+ public function unserialize($data): void
+ {
+ $this->__unserialize(unserialize($data, ['allowed_classes' => [__CLASS__]]));
+ }
+
+ /**
+ * @param array{int,string,string,bool|null,int,string|null,CountryCodeSource|null,string|null} $data
+ */
+ public function __unserialize(array $data): void
+ {
+ [
+ $this->countryCode,
+ $this->nationalNumber,
+ $this->extension,
+ $this->italianLeadingZero,
+ $this->numberOfLeadingZeros,
+ $this->rawInput,
+ $countryCodeSource,
+ $this->preferredDomesticCarrierCode
+ ] = $data;
+
+ // BC layer to allow this method to unserialize "old" phonenumbers
+ if (is_int($countryCodeSource)) {
+ $countryCodeSource = CountryCodeSource::from($countryCodeSource);
+ }
+ $this->countryCodeSource = $countryCodeSource;
+
+ if ($this->numberOfLeadingZeros > 1) {
+ $this->hasNumberOfLeadingZeros = true;
+ }
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumberDesc.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumberDesc.php
@@ -0,0 +1,140 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * Phone Number Description
+ * @internal
+ */
+class PhoneNumberDesc
+{
+ protected bool $hasNationalNumberPattern = false;
+ protected string $nationalNumberPattern = '';
+ protected bool $hasExampleNumber = false;
+ protected string $exampleNumber = '';
+ /**
+ * @var int[]
+ */
+ protected array $possibleLength = [];
+ /**
+ * @var int[]
+ */
+ protected array $possibleLengthLocalOnly = [];
+
+ /**
+ * @return int[]
+ */
+ public function getPossibleLength(): array
+ {
+ return $this->possibleLength;
+ }
+
+ /**
+ * @param int[] $possibleLength
+ */
+ public function setPossibleLength(array $possibleLength): static
+ {
+ $this->possibleLength = $possibleLength;
+ return $this;
+ }
+
+ public function addPossibleLength(int $possibleLength): void
+ {
+ if (!in_array($possibleLength, $this->possibleLength, true)) {
+ $this->possibleLength[] = $possibleLength;
+ }
+ }
+
+ public function clearPossibleLength(): void
+ {
+ $this->possibleLength = [];
+ }
+
+ /**
+ * @return int[]
+ */
+ public function getPossibleLengthLocalOnly(): array
+ {
+ return $this->possibleLengthLocalOnly;
+ }
+
+ /**
+ * @param int[] $possibleLengthLocalOnly
+ */
+ public function setPossibleLengthLocalOnly(array $possibleLengthLocalOnly): static
+ {
+ $this->possibleLengthLocalOnly = $possibleLengthLocalOnly;
+
+ return $this;
+ }
+
+ public function addPossibleLengthLocalOnly(int $possibleLengthLocalOnly): void
+ {
+ if (!in_array($possibleLengthLocalOnly, $this->possibleLengthLocalOnly, true)) {
+ $this->possibleLengthLocalOnly[] = $possibleLengthLocalOnly;
+ }
+ }
+
+ public function clearPossibleLengthLocalOnly(): void
+ {
+ $this->possibleLengthLocalOnly = [];
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasNationalNumberPattern(): bool
+ {
+ return $this->hasNationalNumberPattern;
+ }
+
+ public function getNationalNumberPattern(): string
+ {
+ return $this->nationalNumberPattern;
+ }
+
+ public function setNationalNumberPattern(string $value): static
+ {
+ $this->hasNationalNumberPattern = true;
+ $this->nationalNumberPattern = $value;
+
+ return $this;
+ }
+
+ public function hasExampleNumber(): bool
+ {
+ return $this->hasExampleNumber;
+ }
+
+ public function getExampleNumber(): string
+ {
+ return $this->exampleNumber;
+ }
+
+ public function setExampleNumber(string $value): static
+ {
+ $this->hasExampleNumber = true;
+ $this->exampleNumber = $value;
+
+ return $this;
+ }
+
+ private static self $emptyObject;
+
+ /**
+ * Used for metadata as a shortcut to an empty object
+ * Use the same object to reduce load further
+ * @internal
+ */
+ public static function empty(): self
+ {
+ if (!isset(self::$emptyObject)) {
+ self::$emptyObject = (new self())
+ ->setPossibleLength([-1]);
+ }
+
+ return self::$emptyObject;
+ }
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumberFormat.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumberFormat.php
@@ -0,0 +1,25 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+/**
+ * INTERNATIONAL and NATIONAL formats are consistent with the definition in ITU-T Recommendation
+ * E123. For example, the number of the Google Switzerland office will be written as
+ * "+41 44 668 1800" in INTERNATIONAL format, and as "044 668 1800" in NATIONAL format.
+ * E164 format is as per INTERNATIONAL format but with no formatting applied, e.g.
+ * "+41446681800". RFC3966 is as per INTERNATIONAL format, but with all spaces and other
+ * separating symbols replaced with a hyphen, and with any phone number extension appended with
+ * ";ext=". It also will have a prefix of "tel:" added, e.g. "tel:+41-44-668-1800".
+ *
+ * Note: If you are considering storing the number in a neutral format, you are highly advised to
+ * use the PhoneNumber class.
+ */
+enum PhoneNumberFormat: int
+{
+ case E164 = 0;
+ case INTERNATIONAL = 1;
+ case NATIONAL = 2;
+ case RFC3966 = 3;
+}
--- a/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumberMatch.php
+++ b/profile-builder/assets/lib/libphonenumber-for-php-lite/src/PhoneNumberMatch.php
@@ -0,0 +1,86 @@
+<?php
+
+declare(strict_types=1);
+
+namespace libphonenumber;
+
+use InvalidArgumentException;
+use Stringable;
+
+use function mb_strlen;
+
+/**
+ * @no-named-arguments
+ */
+class PhoneNumberMatch implements Stringable
+{
+ /**
+ * The start index into the text.
+ */
+ private int $start;
+
+ /**
+ * The raw substring matched.
+ */
+ private string $rawString;
+
+ /**
+ * The matched phone number.
+ */
+ private PhoneNumber $number;
+
+ /**
+ * Creates a new match
+ *
+ * @param int $start The start index into the target text
+ * @param string $rawString The matched substring of the target text
+ * @param PhoneNumber $number The matched phone number
+ */
+ public function __construct(int $start, string $rawString, PhoneNumber $number)
+ {
+ if ($start < 0) {
+ throw new InvalidArgumentException('Start index must be >= 0.');
+ }
+
+ $this->start = $start;
+ $this->rawString = $rawString;
+ $this->number = $number;
+ }
+
+ /**
+ * Returns the phone number matched by the receiver.
+ */
+ public function number(): PhoneNumber
+ {
+ return $this->number;
+ }
+
+ /**
+ * Returns the start index of the matched phone number within the searched text.
+ */
+ public function start(): int
+ {
+ return $this->start;
+ }
+
+ /**
+ * Returns the exclusive end index of the matched phone number within the searched text.
+ */
+ public function end(): int
+ {
+ return $this->start + mb_strlen($this->rawString);
+ }
+
+ /**
+ * Returns the raw string matched as a phone numb