PHP電話番号のフォーマット

国(都市)番号の形式での電話番号の自動フォーマットの問題があり、最初に既存のソリューションに目を向けました。
残念ながら、見つかったすべてのソリューションは、カスタム形式に適合する通常のラインに基づいており、それを超えると範囲が限定され、エラーが発生することが判明しました。

まず、見つかったソリューションの概要を説明します。 興味のない人のために、「電話番号の形式」という見出しまで下にスクロールすることをお勧めします。コードへのリンクを使用して番号を解析する私のバージョンは、すでにそこに表示されています。

全破壊的なプリミティブ

(解決策が見つかりました。下の鉱山)
私が最初に出会ったのは、次の計画のソリューションを提供するフォーラムおよびスクリプトバンク上のメッセージです。
<?
関数 phone_number( $ sPhone ){
$ sPhone = ereg_replace"[^ 0-9]"''$ sPhone );
ifstrlen$ sPhone )!= 10return (False);
$ sArea = substr$ sPhone0、3 );
$ sPrefix = substr$ sPhone3、3 );
$ sNumber = substr$ sPhone6、4 );
$ sPhone = "("$ sArea")"$ sPrefix「-」$ sNumber ;
return$ sPhone );
}
?>

電話番号をすばやく書式設定するための最も簡単なオプションの1つですが、そのようなソリューションはそれぞれ特定のローカルエリアの電話番号に焦点を当てており、問題の解決策ではありません。

sscanfを使用したフォーマット

(解決策が見つかりました。下の鉱山)
function formatPhone( $ phone ){
ifempty$ phone )) return "" ;
ifstrlen$ phone )== 7
sscanf$ phone"%3s%4s"$ prefix$ exchange );
else ifstrlen$ phone )== 10
sscanf$ phone「%3s%3s%4s」$ area$ prefix$ exchange );
else ifstrlen$ phone )> 10
ifsubstr$ phone0、1 )== '1' ){
sscanf$ phone「%1s%3s%3s%4s」$ country$ area$ prefix$ exchange );
}
その他 {
sscanf$ phone"%3s%3s%4s%s"$ area$ prefix$ exchange$ extension );
}
他に
return "unknown phone format:$ phone" ;
$ out = "" ;
$ out 。= isset$ country )? $国'''' ;
$ out 。= isset$ area )? '('$ area')''' ;
$ out 。= $プレフィックス'-'$ exchange ;
$ out 。= isset$ extension )? 'x'$拡張子'' ;
return $ out ;
}

単純な解決策にもかかわらず、この関数はすでに7、10、またはそれ以上の桁数の数字をフォーマットする方法を知っていますが、ロシアの後背地から数字を取得すると、チョークして誤った結果を出します。

symfony、lib /ヘルパー/ PhoneHelper.php、format_phone

(解決策が見つかりました。下の鉱山)
<?php
function format_phone( $ phone = ''$ convert = false$ trim = true
{
//電話番号を入力していない場合は、空を返します
ifempty$ phone )){
return '' ;
}

//文字や数字だけを保持する必要がない余分な文字を取り除きます
$ phone = preg_replace"/ [^ 0-9A-Za-z] /"""$ phone );

//文字を含む電話番号を同等の番号に変換しますか?
//サンプル:1-800-TERMINIX、1-800-FLOWERS、1-800-Petmeds
if$ convert == true ){
$ replace = array'2' => array'a''b''c' )、
'3' => 配列'd''e''f' )、
'4' => 配列'g''h''i' )、
'5' => 配列'j''k''l' )、
'6' => 配列'm''n''o' )、
'7' => 配列'p''q''r''s' )、
'8' => 配列't''u''v' )、 '9' => 配列'w''x''y''z' ));

//各文字を数字に置き換えます
//これはstr_replaceではなくstr_ireplaceで大文字と小文字を区別しないことに注意してください
foreach$ replace as $ digit => $ letters ){
$ phone = str_ireplace( $文字$数字$電話 );
}
}

// 11桁よりも長い数字がある場合、文字列を11桁に減らします
//これは、11文字に制限する場合にのみ実行されます
if$ trim == true && strlen$ phone )> 11 ){
$ phone = substr$ phone0、11 );
}

//ここで電話番号のフォーマットを実行します
ifstrlen$ phone )== 7 ){
return preg_replace"/([0-9a-zA-Z] {3})([0-9a-zA-Z] {4})/""$ 1- $ 2"$ phone );
} elseifstrlen$ phone )== 10 ){
return preg_replace"/([0-9a-zA-Z] {3})([0-9a-zA-Z] {3})([0-9a-zA-Z] {4})/""($ 1)$ 2- $ 3"$ phone );
} elseifstrlen$ phone )== 11 ){
return preg_replace"/([0-9a-zA-Z] {1})([0-9a-zA-Z] {3})([0-9a-zA-Z] {3})([0 -9a-zA-Z] {4})/ "" $ 1($ 2)$ 3- $ 4 "$ phone );
}

//長さが7、10、または11桁でない場合は元の電話番号を返します
$電話を 返す ;
}
?>

この関数では、XXX-XXXX、(XXX)XXX-XXXX、およびX(XXX)XXX-XXXXでの書式設定だけでなく、数字で書かれた数字の変換も可能です。 長さが7、10、および11文字の数値をフォーマットする際の制限された機能は適合しません。

電話番号の形式

Wikiの記事は、すべての数字をすばやくフォーマットするための単純で便利なパターンがないことを示しています。 ドメインゾーンと同様に国コードが登録され、都市コードは各国の良心に残ります。

つまり、国コードで始まる通話のルーティングはマスクされます。特定の国に送信された通話は、地域、都市、地区などのコードに従ってそのルートをさらに中断します。 最後のリンクが特定の電話/ファックス機に転送するまで、左端の数字から始まります。 この問題は、国内の都市コードが統一された分野横断的な標準化に適していないという事実によってさらに複雑になっています。 最悪の場合、数値を適切にフォーマットするには、国と都市のコードで2次元配列を使用する必要があります。

実際、すべてがそれほど怖くはありませんでした。 各国では、すべての都市コードを2つの部分に分けることができます。ほとんどの部分の長さは同じで、残りはすべてです。 これは、比較時にコード列挙領域を大幅に削減するのに十分です。 つまり フォームの国ごとにデータの配列を作成できます。
<?
$データ =配列(
'国コード' =>配列(
'name' => 'Country name'//便宜上。 使用されません。
'cityCodeLength' =>この国のregular_city_code_length、
'exceptions' =>配列(exception_city_codes)、

);
?>
次に、データを前処理し、検索エリアを絞り込むフィールド、 exceptions_maxおよびexceptions_min-それぞれ都市の例外の最大および最小コード長を追加します。 また、都市コードが0で始まる国を考慮する必要があります。この「機能」をzeroHackフィールドに反映します。 例として:
<?
$データ =配列(
'886' =>配列(
'名前' => '台湾'
'cityCodeLength' => 1
'zeroHack' => false
'exceptions' =>配列( 89、90、91、92、93、96、60、70、94、95 )、
'exceptions_max' => 2
'exceptions_min' => 2
)、
);
?>
その後、上記のソリューションからコードの適切なセクションを取り、フォーマット機能を作成します。
<?
関数の電話番号( $ phone = ''$ convert = true$ trim = true
{
グローバル $ phoneCodes ; //ほんの一例です! 実装するときは、グローバル変数を取り除きます。
ifempty$ phone )){
return '' ;
}
//数字の先頭にある「プラス」に関する情報を保存して、余分なゴミをクリーニングします
$ phone = trim$ phone );
$ plus =( $ phone [ 0 ] == '+' );
$ phone = preg_replace"/ [^ 0-9A-Za-z] /"""$ phone );
$ OriginalPhone = $ phone ;

//文字番号をデジタルに変換します
if$ convert == true &&! is_numeric$ phone )){
$ replace = array'2' => array'a''b''c' )、
'3' => 配列'd''e''f' )、
'4' => 配列'g''h''i' )、
'5' => 配列'j''k''l' )、
'6' => 配列'm''n''o' )、
'7' => 配列'p''q''r''s' )、
'8' => 配列't''u''v' )、
'9' => 配列'w''x''y''z' ));

foreach$ replace as $ digit => $ letters ){
$ phone = str_ireplace( $文字$数字$電話 );
}
}

//数値の先頭の00を+に置き換えます
ifsubstr$ phone0、2 )== "00"
{
$ phone = substr$ phone2strlen$ phone-2 );
$ plus = true ;
}

//電話が7文字より長い場合、国の検索を開始します
ifstrlen$ phone )> 7
foreach$ phoneCodes as $ countryCode => $ data
{
$ codeLen = strlen$ countryCode );
ifsubstr$ phone0$ codeLen )== $ countryCode
{
//国が見つかるとすぐに、都市コードのレベルに電話を切ります
$ phone = substr$ phone$ codeLenstrlen$ phone )- $ codeLen );
$ zero = false ;
//都市コードのゼロを確認します
if$ data [ 'zeroHack' ] && $ phone [ 0 ] == '0'
{
$ zero = true ;
$ phone = substr$ phone1strlen$ phone-1 );
}

$ cityCode = NULL;
//最初に例外都市と比較します
if$ data [ 'exceptions_max' ]!= 0
for$ cityCodeLen = $ data [ 'exceptions_max' ]; $ cityCodeLen > = $ data [ 'exceptions_min' ]; $ cityCodeLen-
ifin_arrayintvalsubstr$ phone0$ cityCodeLen ))、 $ data [ 'exceptions' ]))
{
$ cityCode =( $ zero"0""" )。 substr$ phone0$ cityCodeLen );
$ phone = substr$ phone$ cityCodeLenstrlen$ phone )- $ cityCodeLen );
休憩 ;
}
//例外が発生した場合、デフォルトの長さに従って都市コードを切り取ります
ifis_null$ cityCode ))
{
$ cityCode = substr$ phone0$ data [ 'cityCodeLength' ]);
$ phone = substr$ phone$ data [ 'cityCodeLength' ]、 strlen$ phone )- $ data [ 'cityCodeLength' ]);
}
//結果を返します
return$ plus"+""" )。 $ countryCode'('$ cityCode')' .phoneBlocks( $ phone );
}
}
//国と都市コードなしで結果を返します
return$ plus"+""" ).phoneBlocks( $ phone );
}

//この関数は、桁数のパリティに応じて、任意の数値をXX-XX -...またはXXX-XX-XX -...の形式の文字列に変換します
function phoneBlocks( $ number ){
$ add = '' ;
ifstrlen$ number )% 2
{
$ add = $ number [ 0 ];
$ add 。=( strlen$ number )<= 5? "-": "");
$ number = substr$ number1strlen$ number-1 );
}
$ addを 返します。 implode"-" 、str_split( $ number2 ));
}

//テスト
エコー電話( "+38(044)226-22-04" )。 "<br />" ;
エコー電話( "0038(044)226-22-04" )。 "<br />" ;
エコー電話( "+79263874814" )。 "<br />" ;
エコー電話( "4816145" )。 "<br />" ;
エコー電話( "+44(0)870 770 5370" )。 "<br />" ;
エコー電話( "0044(0)870 770 5370" )。 "<br />" ;
エコー電話( "+436764505509" )。 "<br />" ;
エコー電話( "(+ 38-048)784-15-46" )。 "<br />" ;
エコー電話( "(38-057)706-34-03" )。 "<br />" ;
エコー電話( "+38(044)244 12 01" )。 "<br />" ;
?>

ここで、 global $ phoneCodes; -すべての国の情報を含む同じ配列。

出力します
+380(44)226-22-04<br/>+380(44)226-22-04<br/>+7(926)387-48-14<br/>481-61-45<br/>+44(0870)770-53-70<br/>+44(0870)770-53-70<br/>+43(6764)50-55-09<br/>380(4878)415-46<br/>380(5770)634-03<br/>+380(44)244-12-01

この関数はタスクを完全に解決します。
この機能の欠点の中で、都市コードはあるが国コードはない場合、電話番号を処理するだけでなく、最適化するために低速セクションの分析がないことに注意する必要があります(この場合、 phoneBlocksでブロックを打つか、上記のソリューションのいずれかを使用します)。 実装で使用する場合、グローバル変数をパラメーター内のリンクに置き換える必要があります。また、 phoneBlocks関数が担当する出力形式を調整または置き換えることもできます。

最も興味深い

サイトからの情報を使用する:
http://www.mtt.ru/info/def/index.wbp
http://www.hella.ru/code/codeuro.htm
http://www.scross.ru/guide/phone-global/
例外都市、 zeroHackフラグ、モバイルネットワークコードなど、代表されるすべての国のデータを収集しました。 コードはここからダウンロードできます

性能

最も悲観的な予想に反して、コードは2秒未満で10,000の数値を満たします。

準備中のUPD修正:
  1. 特定の国で採用されているフォーマットパターンのサポート(数字を表示するための「現地で受け入れられた」標準)。
  2. 番号をフォーマットする国を示すフラグを追加します。
  3. 出力形式を示すパラメーターを追加します(個人的な好みや例外の場合)。
  4. 非ラテン文字番号のサポート
  5. セル番号を定義し、角かっこをスペースに置き換える
UPD: https://github.com/mrXCray/PhoneCodesに投稿されたサーバーからアーカイブが消え、まもなく上記の修正内容とボーナスの更新が行われます。

Source: https://habr.com/ru/post/J102352/


All Articles