| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Number::MuPhone; |
|
2
|
1
|
|
|
1
|
|
78076
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
34
|
|
|
3
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
28
|
|
|
4
|
1
|
|
|
1
|
|
14
|
use v5.020; |
|
|
1
|
|
|
|
|
3
|
|
|
5
|
1
|
|
|
1
|
|
783
|
use Moo; |
|
|
1
|
|
|
|
|
12846
|
|
|
|
1
|
|
|
|
|
6
|
|
|
6
|
1
|
|
|
1
|
|
2177
|
use Types::Standard qw( Maybe Str ); |
|
|
1
|
|
|
|
|
80892
|
|
|
|
1
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
$Number::MuPhone::VERSION = '1.0'; |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $MUPHONE_BASE_DIR = $ENV{MUPHONE_BASE_DIR} || $ENV{HOME}.'/.muphone'; |
|
11
|
|
|
|
|
|
|
our $EXTENSION_REGEX = qr/(?:\*|extension|ext|x)/; |
|
12
|
|
|
|
|
|
|
our $DIAL_PAUSE = ',,,'; |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# if custom data module exists, load it, else use distribution default |
|
15
|
|
|
|
|
|
|
# (which will most likely be out of date) |
|
16
|
|
|
|
|
|
|
our $MUPHONE_DATA; |
|
17
|
|
|
|
|
|
|
my $data_module_path = "$MUPHONE_BASE_DIR/lib/NumberMuPhoneData.pm"; |
|
18
|
|
|
|
|
|
|
if (-f $data_module_path) { |
|
19
|
|
|
|
|
|
|
require $data_module_path; |
|
20
|
|
|
|
|
|
|
} |
|
21
|
|
|
|
|
|
|
else { |
|
22
|
|
|
|
|
|
|
require Number::MuPhone::Data; |
|
23
|
|
|
|
|
|
|
} |
|
24
|
|
|
|
|
|
|
# Let's import the var shortcut to save typing |
|
25
|
|
|
|
|
|
|
Number::MuPhone::Data->import('$MUPHONE_DATA'); |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
################################################################################ |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=head1 NAME |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
Number::MuPhone - parsing and using phone numbers in pure Perl |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
NOTE: this is a full rewrite and is not backwards compatible with earlier |
|
34
|
|
|
|
|
|
|
versions of this module. |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
Parse, validate (loosely in some cases) and display phone numbers as expected. |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
This has stripped down functionality compared to libphonenumber, but it is |
|
41
|
|
|
|
|
|
|
also Pure Perl (TM), is a bit simpler to use, and contains the core functionality |
|
42
|
|
|
|
|
|
|
needed by common use cases. |
|
43
|
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
If you have functionality requests, please let me know: |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
All number regexes are derived from the XML file supplied by: |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
https://github.com/google/libphonenumber/ |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head2 BASIC USAGE |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Instantiate an instance using one of the following syntaxes |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# single arg: E.123 formatted number, scalar shortcut |
|
56
|
|
|
|
|
|
|
my $num = Number::MuPhone->new('+1 203 503 1199'); |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# single arg: E.123 formatted number, hashref format |
|
59
|
|
|
|
|
|
|
my $num = Number::MuPhone->new({ |
|
60
|
|
|
|
|
|
|
number => '+1 203 503 1199' |
|
61
|
|
|
|
|
|
|
}); |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# double arg, number and country - number can be in local or E.123 format, scalar args |
|
64
|
|
|
|
|
|
|
my $num = Number::MuPhone->new('+1 203 503 1199','US"); |
|
65
|
|
|
|
|
|
|
my $num = Number::MuPhone->new('(203) 503-1199','US'); |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# double arg, number and country - number can be in local or E.123 format, hashref args |
|
68
|
|
|
|
|
|
|
my $num = Number::MuPhone->new({ |
|
69
|
|
|
|
|
|
|
number => '+1 203 503 1199' |
|
70
|
|
|
|
|
|
|
country => 'US', |
|
71
|
|
|
|
|
|
|
}); |
|
72
|
|
|
|
|
|
|
my $num = Number::MuPhone->new({ |
|
73
|
|
|
|
|
|
|
number => '(203) 503-1199' |
|
74
|
|
|
|
|
|
|
country => 'US', |
|
75
|
|
|
|
|
|
|
}); |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# after instantiation, check all is well before using the object |
|
78
|
|
|
|
|
|
|
if ($num->error) { |
|
79
|
|
|
|
|
|
|
# process the error |
|
80
|
|
|
|
|
|
|
} |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head2 KEEPING UP TO DATE WITH CHANGES... |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
The data used to validate and format the phone numbers comes fropm Google's libphonenumber: |
|
85
|
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
TODO: add URL |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
This distribution comes with a reasonably recent copy of the libphonenumber source XML, but |
|
89
|
|
|
|
|
|
|
you can also set up a cron to update your source data weekly, to ensure you don't have |
|
90
|
|
|
|
|
|
|
problems with new area codes as they get added (this happens probably more often than you think). |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
By default, MuPhone's update script (perl-muphone-build-data) will create a ~/.muphon |
|
93
|
|
|
|
|
|
|
directory and dump everything in there if you choose to update periodically (or when |
|
94
|
|
|
|
|
|
|
starting a Docker container, say) |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
If you want to store the data elsewhere, set the MUPHONE_BASE_DIR env var to specify |
|
97
|
|
|
|
|
|
|
where you want it stored. Wherever you store it, the directory must be writeable by |
|
98
|
|
|
|
|
|
|
the user. |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Currently, the extractor script only grabs the data we need, and removes spacing, to keep the size down. |
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
If you want to examine all available data, set $DEBUG=1 (add in padding) and set |
|
103
|
|
|
|
|
|
|
$STRIP_SUPERFLUOUS_DATA=0 in the script and run it again. |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
for the following, paths are relative to the ~/.muphone or $ENV{MUPHONE_BASE_DIR} dirs as appropriate |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
./etc/PhoneNumberMetadata.xml - the libphonenumber source XML file |
|
108
|
|
|
|
|
|
|
./lib/NumberMuPhoneData.pm - the generated Number::MuPhone::Data |
|
109
|
|
|
|
|
|
|
./t/check_data_module.t - a little sanity script that runs after creating the data file |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=head3 Initial run |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Optionally, set the MUPHONE_BASE_DIR environment variable to point to your config directory (must be writeable). |
|
114
|
|
|
|
|
|
|
Otherwise, ~/.muphone will get used (default). |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
As the user, run: |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
perl-muphone-build-data |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Confirm the tests pass and the files are created (if not error output, tests passed). |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head3 Set up the cron to run weekly to update the data |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# using default data dir |
|
125
|
|
|
|
|
|
|
0 5 * * 1 /usr/local/bin/perl-muphone-build-data |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# using user specific data dir |
|
128
|
|
|
|
|
|
|
0 5 * * 1 MUPHONE_BASE_DIR=/path/to/config /usr/local/bin/perl-muphone-build-data |
|
129
|
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=head1 PUBLIC ATTRIBUTES |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=cut |
|
134
|
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
around BUILDARGS => sub { |
|
136
|
|
|
|
|
|
|
my ( $orig, $class, @args ) = @_; |
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# args are probably a hashref - { number => $number, country => 'US' } |
|
139
|
|
|
|
|
|
|
# but can use a shortcut, if preferred |
|
140
|
|
|
|
|
|
|
# ($number, 'US') |
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
if (ref $args[0] ne 'HASH' and @args>2) { |
|
143
|
|
|
|
|
|
|
die "Bad args - must be a hashref of name args or (\$num,\$country_code)"; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
if (!ref $args[0]) { |
|
147
|
|
|
|
|
|
|
$args[0] = { number => $args[0] }; |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
$args[0]->{country} = pop @args |
|
150
|
|
|
|
|
|
|
if $args[1]; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
return $class->$orig(@args); |
|
154
|
|
|
|
|
|
|
}; |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub BUILD { |
|
157
|
17
|
|
|
17
|
0
|
1730
|
my ($self,$arg) = @_; |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# extract number and extension, determine countrycode from number, |
|
160
|
|
|
|
|
|
|
# strip off possible national/international dial prefix |
|
161
|
|
|
|
|
|
|
# and store attributes as needed |
|
162
|
17
|
|
|
|
|
37
|
$self->_process_raw_number; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
} |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=head2 number |
|
167
|
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
The raw number sent in at instantiation - not needed (outside of logging, maybe) |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=cut |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
has number => ( |
|
173
|
|
|
|
|
|
|
isa => Str, |
|
174
|
|
|
|
|
|
|
is => 'ro', |
|
175
|
|
|
|
|
|
|
required => 1, |
|
176
|
|
|
|
|
|
|
); |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head2 extension |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Extenstion number (digits only) |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=cut |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
has extension => ( |
|
185
|
|
|
|
|
|
|
is => 'rw', |
|
186
|
|
|
|
|
|
|
default => '' |
|
187
|
|
|
|
|
|
|
); |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head2 country |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
The 2 character country code sent in instantiation, or inferred from an E.123 number |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=cut |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# 2 char country code - either explicitly sent, to inferred from the number / config |
|
196
|
|
|
|
|
|
|
has country => ( |
|
197
|
|
|
|
|
|
|
isa => Maybe[Str], |
|
198
|
|
|
|
|
|
|
is => 'rw', |
|
199
|
|
|
|
|
|
|
lazy => 1, |
|
200
|
|
|
|
|
|
|
); |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head2 error |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
If the args don't point to a valid number at instantiation, this error will be set |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=cut |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
has error => ( |
|
209
|
|
|
|
|
|
|
isa => Str, |
|
210
|
|
|
|
|
|
|
is => 'rw', |
|
211
|
|
|
|
|
|
|
default => '', |
|
212
|
|
|
|
|
|
|
); |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
=head2 country_name |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
Full text name of country() |
|
217
|
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
=cut |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
has country_name => ( |
|
221
|
|
|
|
|
|
|
is => 'lazy', |
|
222
|
|
|
|
|
|
|
); |
|
223
|
|
|
|
|
|
|
sub _build_country_name { |
|
224
|
3
|
|
|
3
|
|
4396
|
my $self = shift; |
|
225
|
3
|
|
|
|
|
53
|
return $MUPHONE_DATA->{territories}->{ $self->country }->{TerritoryName}; |
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head2 country_code |
|
229
|
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
1-3 digit country code |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=cut |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
has country_code => ( |
|
235
|
|
|
|
|
|
|
is => 'lazy', |
|
236
|
|
|
|
|
|
|
); |
|
237
|
|
|
|
|
|
|
sub _build_country_code { |
|
238
|
6
|
|
|
6
|
|
1193
|
my $self = shift; |
|
239
|
6
|
|
|
|
|
106
|
return $MUPHONE_DATA->{territories}->{ $self->country }->{countryCode}; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head2 national_dial |
|
243
|
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
How you would dial this number within the country (including national dial code) |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=cut |
|
247
|
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
has national_dial => ( |
|
249
|
|
|
|
|
|
|
is => 'lazy', |
|
250
|
|
|
|
|
|
|
); |
|
251
|
|
|
|
|
|
|
sub _build_national_dial { |
|
252
|
4
|
|
|
4
|
|
32
|
my $self = shift; |
|
253
|
4
|
100
|
|
|
|
44
|
my $dial_prefix = $self->_national_prefix_optional_when_formatting |
|
254
|
|
|
|
|
|
|
? '' |
|
255
|
|
|
|
|
|
|
: $self->_national_dial_prefix; |
|
256
|
|
|
|
|
|
|
|
|
257
|
4
|
|
|
|
|
82
|
return $dial_prefix.$self->_cleaned_number.$self->_extension_dial; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=head2 national_display |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
Display this number in the national number format |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
=cut |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# How do you display the number when you're in the country? |
|
267
|
|
|
|
|
|
|
# this default should work for most countries |
|
268
|
|
|
|
|
|
|
has national_display => ( |
|
269
|
|
|
|
|
|
|
is => 'ro', |
|
270
|
|
|
|
|
|
|
lazy => 1, |
|
271
|
|
|
|
|
|
|
default => sub { |
|
272
|
|
|
|
|
|
|
my $self = shift; |
|
273
|
|
|
|
|
|
|
my $dial_prefix = $self->_national_prefix_optional_when_formatting |
|
274
|
|
|
|
|
|
|
? '' |
|
275
|
|
|
|
|
|
|
: $self->_national_dial_prefix; |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
return $dial_prefix.$self->_formatted_number.$self->_extension_display; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
); |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 national_display |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
Display this number in the international number format (E.123) |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=cut |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
has international_display => ( |
|
288
|
|
|
|
|
|
|
is => 'ro', |
|
289
|
|
|
|
|
|
|
lazy => 1, |
|
290
|
|
|
|
|
|
|
default => sub { |
|
291
|
|
|
|
|
|
|
my $self = shift; |
|
292
|
|
|
|
|
|
|
return '+'.$self->country_code.' '.$self->_formatted_number.$self->_extension_display; |
|
293
|
|
|
|
|
|
|
} |
|
294
|
|
|
|
|
|
|
); |
|
295
|
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=head2 e164 |
|
297
|
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
The number in E.164 format (+$COUNTRY_CODE$NUMBER[;ext=$EXTENSION]) |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
|
301
|
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
has e164 => ( |
|
303
|
|
|
|
|
|
|
is => 'lazy', |
|
304
|
|
|
|
|
|
|
); |
|
305
|
|
|
|
|
|
|
sub _build_e164 { |
|
306
|
3
|
|
|
3
|
|
1756
|
my $self = shift; |
|
307
|
3
|
100
|
|
|
|
18
|
my $ext = $self->extension |
|
308
|
|
|
|
|
|
|
? ";ext=".$self->extension |
|
309
|
|
|
|
|
|
|
: ''; |
|
310
|
3
|
|
|
|
|
50
|
return $self->e164_no_ext.$ext; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
=head2 e164_no_ext |
|
314
|
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
The number in E.164 format, but with no extension (+$COUNTRY_CODE$NUMBER) |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=cut |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
has e164_no_ext => ( |
|
320
|
|
|
|
|
|
|
is => 'lazy', |
|
321
|
|
|
|
|
|
|
); |
|
322
|
|
|
|
|
|
|
sub _build_e164_no_ext { |
|
323
|
3
|
|
|
3
|
|
1828
|
my $self = shift; |
|
324
|
3
|
|
|
|
|
50
|
return '+'.$self->country_code.$self->_cleaned_number; |
|
325
|
|
|
|
|
|
|
} |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# number with international and national dial codes, and all non digits removed |
|
328
|
|
|
|
|
|
|
has _cleaned_number => ( |
|
329
|
|
|
|
|
|
|
is => 'rw', |
|
330
|
|
|
|
|
|
|
default => '', |
|
331
|
|
|
|
|
|
|
); |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# basic validation of a number via this regex |
|
334
|
|
|
|
|
|
|
has _national_number_regex => ( |
|
335
|
|
|
|
|
|
|
is => 'lazy', |
|
336
|
|
|
|
|
|
|
); |
|
337
|
|
|
|
|
|
|
sub _build__national_number_regex { |
|
338
|
14
|
|
|
14
|
|
107
|
my $self = shift; |
|
339
|
14
|
|
|
|
|
212
|
my $regex_string = $MUPHONE_DATA->{territories}->{ $self->country }->{generalDesc}->{nationalNumberPattern}; |
|
340
|
14
|
|
|
|
|
234
|
return qr/^$regex_string$/; |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
# Display number without international or nation dial prefixes |
|
344
|
|
|
|
|
|
|
# built by _process_raw_number |
|
345
|
|
|
|
|
|
|
has _formatted_number => ( |
|
346
|
|
|
|
|
|
|
is => 'rw', |
|
347
|
|
|
|
|
|
|
); |
|
348
|
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# Boolean used to help determine how to display a number |
|
350
|
|
|
|
|
|
|
# built in sub _process_raw_number |
|
351
|
|
|
|
|
|
|
has _national_prefix_optional_when_formatting => ( |
|
352
|
|
|
|
|
|
|
is => 'rw', |
|
353
|
|
|
|
|
|
|
); |
|
354
|
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# add pause to extension to create dial |
|
356
|
|
|
|
|
|
|
has _extension_dial => ( |
|
357
|
|
|
|
|
|
|
is => 'lazy', |
|
358
|
|
|
|
|
|
|
); |
|
359
|
|
|
|
|
|
|
sub _build__extension_dial { |
|
360
|
4
|
|
|
4
|
|
31
|
my $self = shift; |
|
361
|
4
|
100
|
|
|
|
39
|
return $self->extension |
|
362
|
|
|
|
|
|
|
? $DIAL_PAUSE.$self->extension |
|
363
|
|
|
|
|
|
|
: ''; |
|
364
|
|
|
|
|
|
|
} |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
# prefix you dial when dialing the _cleaned_number within the country |
|
367
|
|
|
|
|
|
|
has _national_dial_prefix => ( |
|
368
|
|
|
|
|
|
|
is => 'lazy', |
|
369
|
|
|
|
|
|
|
); |
|
370
|
|
|
|
|
|
|
sub _build__national_dial_prefix { |
|
371
|
8
|
|
|
8
|
|
2792
|
my $self = shift; |
|
372
|
8
|
|
|
|
|
129
|
$MUPHONE_DATA->{territories}->{ $self->country }->{nationalPrefix}; |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# how to display the extension text + number (currently only in English) |
|
376
|
|
|
|
|
|
|
has _extension_display => ( |
|
377
|
|
|
|
|
|
|
is => 'lazy', |
|
378
|
|
|
|
|
|
|
); |
|
379
|
|
|
|
|
|
|
sub _build__extension_display { |
|
380
|
10
|
|
|
10
|
|
1303
|
my $self = shift; |
|
381
|
10
|
100
|
|
|
|
76
|
my $ext = |
|
382
|
|
|
|
|
|
|
return $self->extension |
|
383
|
|
|
|
|
|
|
? ' '.$self->_extension_text.' '.$self->extension |
|
384
|
|
|
|
|
|
|
: ''; |
|
385
|
|
|
|
|
|
|
} |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# text to display befor an extension |
|
388
|
|
|
|
|
|
|
has _extension_text => ( |
|
389
|
|
|
|
|
|
|
is => 'ro', |
|
390
|
|
|
|
|
|
|
default => 'ext', |
|
391
|
|
|
|
|
|
|
); |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# helper method to get the country for a number, country, or object |
|
394
|
|
|
|
|
|
|
sub _get_country_from { |
|
395
|
15
|
|
|
15
|
|
25
|
my ($self,$str_or_obj) = @_; |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# $str_or_arg should be |
|
398
|
|
|
|
|
|
|
# - Number::MuPhone instance |
|
399
|
|
|
|
|
|
|
# - E.123 formatted number |
|
400
|
|
|
|
|
|
|
# - 2 char country code |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# muphone num |
|
403
|
15
|
100
|
|
|
|
71
|
if (ref $str_or_obj eq 'Number::MuPhone') { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
404
|
8
|
|
|
|
|
191
|
return $str_or_obj->country; |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
# E.123 |
|
407
|
|
|
|
|
|
|
elsif ($str_or_obj =~ /^\s\+/) { |
|
408
|
0
|
|
|
|
|
0
|
my $num = Number::MuPhone->new($str_or_obj); |
|
409
|
0
|
|
|
|
|
0
|
return $num->country; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
# it should be a country |
|
412
|
|
|
|
|
|
|
elsif ( $str_or_obj =~ /^[A-Z]{2}$/ ) { |
|
413
|
7
|
|
|
|
|
18
|
return $str_or_obj; |
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
else { |
|
416
|
0
|
|
|
|
|
0
|
die "Not a country, E.123 num, or MuPhone object: $str_or_obj"; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
} |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
=head1 METHODS |
|
421
|
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=head2 dial_from |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
How to dial the number from the number/country sent in as an arg. eg |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
my $uk_num1 = Number::MuPhone->new({ country => 'GB', number => '01929 552699' }); |
|
427
|
|
|
|
|
|
|
my $uk_num2 = Number::MuPhone->new({ country => 'GB', number => '01929 552698' }); |
|
428
|
|
|
|
|
|
|
my $us_num = Number::MuPhone->new({ country => 'US', number => '203 503 1234' }); |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# these all have the same output (01929552699) |
|
431
|
|
|
|
|
|
|
my $dial_from_uk = $uk_num1->dial_from($uk_num2); |
|
432
|
|
|
|
|
|
|
my $dial_from_uk = $uk_num1->dial_from('GB'); |
|
433
|
|
|
|
|
|
|
my $dial_from_uk = $uk_num1->dial_from('+441929 552698'); |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# similarly, dialling the number from the US (011441929552699) |
|
436
|
|
|
|
|
|
|
my $dial_from_us = $uk_num1->dial_from($us_num); |
|
437
|
|
|
|
|
|
|
my $dial_from_us = $uk_num1->dial_from('US'); |
|
438
|
|
|
|
|
|
|
my $dial_from_us = $uk_num1->dial_from('+1 203 503 1234'); |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=cut |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
sub dial_from { |
|
443
|
7
|
|
|
7
|
1
|
2754
|
my ($self,$str_or_obj) = @_; |
|
444
|
7
|
|
33
|
|
|
19
|
$str_or_obj||=$self; |
|
445
|
7
|
|
|
|
|
15
|
my $from_country = $self->_get_country_from($str_or_obj); |
|
446
|
7
|
100
|
|
|
|
151
|
if ( $from_country eq $self->country ) { |
|
447
|
4
|
|
|
|
|
83
|
return $self->national_dial; |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
else { |
|
450
|
|
|
|
|
|
|
return $MUPHONE_DATA->{territories}->{ $from_country }->{internationalPrefix} |
|
451
|
3
|
|
|
|
|
65
|
.$self->country_code |
|
452
|
|
|
|
|
|
|
.$self->_cleaned_number; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head2 display_from |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
How to display the number for the number/country sent in as an arg. eg |
|
459
|
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
my $uk_num1 = Number::MuPhone->new({ country => 'GB', number => '01929 552699' }); |
|
461
|
|
|
|
|
|
|
my $uk_num2 = Number::MuPhone->new({ country => 'GB', number => '01929 552698' }); |
|
462
|
|
|
|
|
|
|
my $us_num = Number::MuPhone->new({ country => 'US', number => '203 503 1234' }); |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
# these all have the same output (01929 552699) |
|
465
|
|
|
|
|
|
|
my $display_from_uk = $uk_num1->display_from($uk_num2); |
|
466
|
|
|
|
|
|
|
my $display_from_uk = $uk_num1->display_from('GB'); |
|
467
|
|
|
|
|
|
|
my $display_from_uk = $uk_num1->display_from('+441929 552698'); |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# similarly, dialling the number from the US (01144 1929 552699) |
|
470
|
|
|
|
|
|
|
my $display_from_us = $uk_num1->display_from($us_num); |
|
471
|
|
|
|
|
|
|
my $display_from_us = $uk_num1->display_from('US'); |
|
472
|
|
|
|
|
|
|
my $display_from_us = $uk_num1->display_from('+1 203 503 1234'); |
|
473
|
|
|
|
|
|
|
|
|
474
|
|
|
|
|
|
|
=cut |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
sub display_from { |
|
477
|
8
|
|
|
8
|
1
|
2205
|
my ($self,$str_or_obj) = @_; |
|
478
|
8
|
|
33
|
|
|
20
|
$str_or_obj||=$self; |
|
479
|
8
|
|
|
|
|
19
|
my $from_country = $self->_get_country_from($str_or_obj); |
|
480
|
8
|
100
|
|
|
|
172
|
if ( $from_country eq $self->country ) { |
|
481
|
4
|
|
|
|
|
82
|
return $self->national_display; |
|
482
|
|
|
|
|
|
|
} |
|
483
|
|
|
|
|
|
|
else { |
|
484
|
|
|
|
|
|
|
# (DIAL PREFIX) (COUNTRY CODE) (FORMATTED NUMBER) [ (EXTENSION) ] |
|
485
|
|
|
|
|
|
|
return $MUPHONE_DATA->{territories}->{ $from_country }->{internationalPrefix} |
|
486
|
4
|
|
|
|
|
105
|
.$self->country_code.' ' |
|
487
|
|
|
|
|
|
|
.$self->_formatted_number.$self->_extension_display; |
|
488
|
|
|
|
|
|
|
} |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
# PRIVATE METHODS |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
# splits off optional extension, and cleans both up for storage |
|
495
|
|
|
|
|
|
|
# only place where we set error |
|
496
|
|
|
|
|
|
|
sub _process_raw_number { |
|
497
|
17
|
|
|
17
|
|
22
|
my $self = shift; |
|
498
|
|
|
|
|
|
|
|
|
499
|
17
|
|
|
|
|
123
|
my ($raw_num,$ext) = split $EXTENSION_REGEX, $self->number; |
|
500
|
17
|
|
100
|
|
|
73
|
$ext||=''; |
|
501
|
17
|
|
|
|
|
23
|
$ext =~ s/\D//g; |
|
502
|
17
|
|
|
|
|
43
|
$self->extension($ext); |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# if number begins with a '+' we can determine country from E.123 number |
|
505
|
17
|
100
|
|
|
|
285
|
if ($raw_num =~ /^\s*\+/) { |
|
|
|
50
|
|
|
|
|
|
|
506
|
4
|
|
|
|
|
13
|
$self->_process_from_e123($raw_num); |
|
507
|
|
|
|
|
|
|
} |
|
508
|
|
|
|
|
|
|
# if we have a country set, clean up raw number (ie, strip national dial code, if set) |
|
509
|
|
|
|
|
|
|
elsif (my $country = $self->country) { |
|
510
|
13
|
|
|
|
|
119
|
$raw_num =~ s/\D//g; |
|
511
|
13
|
|
|
|
|
37
|
my $national_prefix = $MUPHONE_DATA->{territories}->{ $country }->{nationalPrefix}; |
|
512
|
13
|
50
|
|
|
|
26
|
if ( defined $national_prefix ) { |
|
513
|
13
|
|
|
|
|
83
|
$raw_num =~ s/^$national_prefix//; |
|
514
|
|
|
|
|
|
|
} |
|
515
|
13
|
|
|
|
|
40
|
$self->_cleaned_number( $raw_num ); |
|
516
|
|
|
|
|
|
|
} |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
# if no country set by the time we get here, we need to set error and bail |
|
519
|
17
|
|
|
|
|
279
|
my $country = $self->country; |
|
520
|
17
|
50
|
|
|
|
101
|
unless ( $country ) { |
|
521
|
0
|
|
|
|
|
0
|
$self->error("Country not supplied, and I can't determine it from the number"); |
|
522
|
0
|
|
|
|
|
0
|
return; |
|
523
|
|
|
|
|
|
|
} |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# Number must match the national number pattern, if exists |
|
526
|
17
|
|
|
|
|
36
|
my $cleaned_num = $self->_cleaned_number; |
|
527
|
17
|
50
|
33
|
|
|
75
|
if ( $MUPHONE_DATA->{territories}->{ $country }->{generalDesc} |
|
528
|
|
|
|
|
|
|
&& $MUPHONE_DATA->{territories}->{ $country }->{generalDesc}->{nationalNumberPattern} ) { |
|
529
|
|
|
|
|
|
|
|
|
530
|
17
|
|
|
|
|
169
|
my $regex = qr/^(?:$MUPHONE_DATA->{territories}->{ $country }->{generalDesc}->{nationalNumberPattern})$/; |
|
531
|
17
|
100
|
|
|
|
102
|
unless ( $cleaned_num =~ $regex ) { |
|
532
|
3
|
|
|
|
|
59
|
$self->error("Number ($cleaned_num) is not valid for country ($country)"); |
|
533
|
3
|
|
|
|
|
108
|
return; |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
} |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
# confirm cleaned number is a valid number for the country |
|
538
|
14
|
50
|
|
|
|
249
|
unless ( $self->_cleaned_number =~ $self->_national_number_regex ) { |
|
539
|
0
|
|
|
|
|
0
|
$self->error("Number $raw_num is not valid for country ".$self->country); |
|
540
|
|
|
|
|
|
|
} |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
# don't create formatted number if we have an error |
|
543
|
14
|
50
|
|
|
|
236
|
$self->error and return; |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# if no number formats, just set to the cleaned number |
|
546
|
14
|
|
|
|
|
286
|
my $number_formats = $MUPHONE_DATA->{territories}->{ $self->country }->{availableFormats}->{numberFormat}; |
|
547
|
|
|
|
|
|
|
|
|
548
|
14
|
|
|
|
|
91
|
my $num = $self->_cleaned_number; |
|
549
|
14
|
|
|
|
|
18
|
my $national_prefix_optional=0; |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# iterate through the available formats until you get a match |
|
552
|
|
|
|
|
|
|
# (if not set, we default to cleaned number |
|
553
|
14
|
|
|
|
|
27
|
FORMAT: foreach my $format_hash (@$number_formats) { |
|
554
|
|
|
|
|
|
|
# not all countries have leading digit mappings |
|
555
|
49
|
50
|
|
|
|
105
|
if (my $leading_digits = $format_hash->{leadingDigits}) { |
|
556
|
49
|
100
|
|
|
|
740
|
next FORMAT unless ( $num =~ /^(?:$leading_digits)/ ); |
|
557
|
|
|
|
|
|
|
} |
|
558
|
|
|
|
|
|
|
|
|
559
|
14
|
|
|
|
|
197
|
my $pattern = qr/^$format_hash->{pattern}$/; |
|
560
|
14
|
50
|
|
|
|
90
|
next FORMAT unless ( $num =~ $pattern ); |
|
561
|
|
|
|
|
|
|
|
|
562
|
14
|
|
|
|
|
28
|
my $format = $format_hash->{format}; |
|
563
|
|
|
|
|
|
|
|
|
564
|
14
|
|
|
|
|
36
|
my $regex_statement = "\$num =~ s/$pattern/$format/;"; |
|
565
|
14
|
|
|
|
|
1339
|
eval $regex_statement; |
|
566
|
14
|
50
|
|
|
|
60
|
if ($@) { |
|
567
|
0
|
|
|
|
|
0
|
$self->error("Can't format number($num) with regex($regex_statement): $@"); |
|
568
|
0
|
|
|
|
|
0
|
last FORMAT; |
|
569
|
|
|
|
|
|
|
} |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
$national_prefix_optional = $format_hash->{nationalPrefixOptionalWhenFormatting} |
|
572
|
14
|
100
|
|
|
|
40
|
? 1 : 0; |
|
573
|
14
|
|
|
|
|
38
|
last FORMAT; |
|
574
|
|
|
|
|
|
|
} |
|
575
|
|
|
|
|
|
|
|
|
576
|
14
|
|
|
|
|
62
|
$self->_formatted_number($num); |
|
577
|
14
|
|
|
|
|
90
|
$self->_national_prefix_optional_when_formatting($national_prefix_optional); |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
# number starts with a + ? Great, we should be able to work it out. |
|
582
|
|
|
|
|
|
|
sub _process_from_e123 { |
|
583
|
4
|
|
|
4
|
|
7
|
my ($self,$num) = @_; |
|
584
|
|
|
|
|
|
|
|
|
585
|
4
|
|
|
|
|
20
|
$num =~ s/\D//g; |
|
586
|
|
|
|
|
|
|
|
|
587
|
4
|
|
|
|
|
7
|
my $countries = []; |
|
588
|
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# grab from country lookup - country code is 1-3 digits long |
|
590
|
4
|
|
|
|
|
9
|
my @prefixes = map { substr($num, 0, $_) } 1..3; |
|
|
12
|
|
|
|
|
28
|
|
|
591
|
4
|
|
|
|
|
9
|
PREFIX: foreach my $idd (@prefixes) { |
|
592
|
|
|
|
|
|
|
# we found a match |
|
593
|
6
|
100
|
|
|
|
20
|
if ($countries = $MUPHONE_DATA->{idd_codes}->{$idd}) { |
|
594
|
|
|
|
|
|
|
# so strip off the IDD from the number |
|
595
|
4
|
|
|
|
|
55
|
$num =~ s/^$idd//; |
|
596
|
4
|
|
|
|
|
11
|
last PREFIX; |
|
597
|
|
|
|
|
|
|
} |
|
598
|
|
|
|
|
|
|
} |
|
599
|
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
# now find out which country the number matches |
|
601
|
|
|
|
|
|
|
# (for IDD codes with multiple countries, this may not be correct, but should be |
|
602
|
|
|
|
|
|
|
# good enough for this use case - just don't rely on the country |
|
603
|
|
|
|
|
|
|
# TODO - maybe iterate through all regexes by number type to confirm validity? |
|
604
|
|
|
|
|
|
|
# generalDesc regex is too loose for (eg) US/CA |
|
605
|
|
|
|
|
|
|
# to implement this, we'd need to keep the various number type regexes around |
|
606
|
|
|
|
|
|
|
# Suggest look at adding in next update |
|
607
|
4
|
|
|
|
|
6
|
my $country; |
|
608
|
4
|
|
|
|
|
9
|
COUNTRY: foreach my $country (@$countries) { |
|
609
|
|
|
|
|
|
|
my $national_number_format_regex = $MUPHONE_DATA->{territories}->{$country}->{generalDesc} && $MUPHONE_DATA->{territories}->{$country}->{generalDesc}->{nationalNumberPattern} |
|
610
|
58
|
50
|
33
|
|
|
1209
|
? qr/^$MUPHONE_DATA->{territories}->{$country}->{generalDesc}->{nationalNumberPattern}$/ |
|
611
|
|
|
|
|
|
|
: ''; |
|
612
|
58
|
50
|
|
|
|
152
|
$national_number_format_regex |
|
613
|
|
|
|
|
|
|
or next COUNTRY; |
|
614
|
|
|
|
|
|
|
|
|
615
|
58
|
100
|
|
|
|
305
|
$num =~ $national_number_format_regex |
|
616
|
|
|
|
|
|
|
or next COUNTRY; |
|
617
|
|
|
|
|
|
|
|
|
618
|
6
|
|
|
|
|
131
|
$self->country($country); |
|
619
|
6
|
|
|
|
|
206
|
$self->_cleaned_number($num); |
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
} |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
1; |