[ Excel_Tipe29 へ戻る ]     ( Coloring  by  SyntaxHighlighter )

Tips29: JavaScript で電話番号(局番)の検証/編集    [ ソースファイル ダウンロード ]  [ PHP 用 テストツール ]

  [ JavaScript with JSON  ( AreaCode4JSON.json ) ]   [ JavaScript ]   [ JAVA ]   [ PHP ]   [ Perl ]   [ Python ]   [ Ruby ]   [ VBA / VB6 ]   [ VB.net ]


  ( Source Code : PHP   Ver 1.00 )    動作確認環境 [ Eclipse / PHP 5.6.39 ]

<?php

//-【 PHP 移植版 TelephoneCheck.php (Ver 1.00 , 翻訳テーブル:2022/7/1 版) 】------------------------------
//
//  電話番号のハイフン検証/編集関数
//  ( ValidateTelephone / FormatTelephone / GetTypeTelephone / PhoneNumber )
//
// 【 移植元ソース:番号翻訳テーブル 内包版 TelephoneCheck.js 】
//
//   Tips29: JavaScriptで電話番号(局番)のハイフン検証/編集
//   http://addinbox.sakura.ne.jp/Excel_Tips29.htm
//
//   Author : AddinBox(角田) http://addinbox.sakura.ne.jp/
//
//   β  1.00 , 2019/ 2/14 (JavaScript版 初版)
//   β  4.40 , 2019/ 3/27 (JavaScript版)
//   β  4.40 , 2019/ 3/27 (PHP 移植)
//   - - - - - - - - - - - -
//   Ver 1.00 , 2019/ 4/ 4 (PHP 移植, 番号翻訳テーブル:2019/4/1 版)
//   Ver 1.00 , 2019/ 5/11 (PHP 移植, 番号翻訳テーブル:2019/5/7 版)
//   Ver 1.00 , 2020/ 7/20 (PHP 移植, 番号翻訳テーブル:2020/6/1 版)
//   Ver 1.00 , 2022/ 8/ 2 (PHP 移植, 番号翻訳テーブル:2022/7/1 版)
//
//   *1 固定電話番号は番号翻訳テーブルによって、全国の市外局番を忠実に判定します。
//      下記で翻訳テーブルの仕組みを確認できます。
//          [ 電話番号翻訳テーブル 翻訳シュミレーター ]
//            http://addinbox.sakura.ne.jp/Excel_Tips29.htm#S2_2
//-------------------------------------------------------------------------------
//   【 サポートしている電話番号 】
//       固定電話       10桁(先頭 0) [ 0ABCDE-FGHJ ]
//       携帯電話       11桁(先頭 070/080/090) [ 0x0-CDEF-GHJK , C:1-9 ] 補:060は実施未定
//       着信課金       10桁(先頭 0120) [ 0120-DEF-GHJ ]
//       着信課金       11桁(先頭 0800) [ 0800-DEF-GHJK ]
//       IP電話       11桁(先頭 050)  [ 050-CDEF-GHJK , C:1-9 ]
//       M2M         11桁(先頭 020x) [ 020-CDE-FGHJK , C:1-3,5-9 ]
//       ポケベル       11桁(先頭 0204) [ 020-4DE-FGHJK ]
//       FMC         11桁(先頭 0600) [ 0600-DEF-GHJK ]
//       情報料代理徴収 10桁(先頭 0990) [ 0990-DEF-GHJ ]
//       全国統一番号   10桁(先頭 0570) [ 0570-DEF-GHJ ]
//-------------------------------------------------------------------------------

// 番号翻訳テーブル(2022/7/1 版)
//   固定電話の全国の局番(市外局番+市内局番)および携帯電話(070/080/090)等の局番を
//   収録しているJSONファイル(AreaCode4JSON.json)を必要最小限の内容に編集しています。
//   完全版のフォーマットは [ http://addinbox.sakura.ne.jp/Excel_Tips29.htm ] を参照の事。
//   尚、このPHP用データはJSONファイル作成ツールで一緒に作成していますので、
//  「配列データ」の同一性/正確性は保障します。
//
$AreaCodeArray = [
      [0,1,22,41,42,54,69,70,81,114],          [0,-3,2,4,7,10,15,19,20,21],                      [-103,0,0,-4,-4,-4,3,0,0,0],
      [0,0,-4,-4,-4,-4,-4,-5,0,0],             [0,0,0,-4,-4,-4,-4,5,-4,6],                       [0,0,-5,0,-5,-4,-4,-5,-4,0],
      [0,0,-5,-4,-4,-4,-4,-5,-5,0],            [0,0,-4,-4,-4,8,9,0,0,0],                         [0,0,-4,-4,-4,0,-5,-5,0,0],
      [0,0,-4,-4,-4,-4,-5,0,0,0],              [0,0,-4,-4,11,12,13,-4,14,0],                     [0,0,-4,-4,-4,-4,-4,-5,-3,-4],
      [0,-3,-4,-4,-4,-4,-4,-3,-5,-4],          [0,0,-4,-4,-5,0,-4,-4,0,0],                       [0,0,-4,-4,-4,-4,-5,-5,-4,0],
      [0,0,-4,16,17,18,-4,-4,0,0],             [0,0,-5,0,-5,-5,-4,0,-4,-4],                      [0,0,-4,-4,-4,-4,-4,0,-5,0],
      [0,0,-4,-4,-5,-5,-5,0,-5,0],             [0,0,-4,-4,-4,-4,-4,-3,-4,-4],                    [0,0,-4,-4,-4,-4,-4,-4,-3,0],
      [0,-4,-4,-4,-4,-4,-3,-4,-4,-3],          [130,0,23,25,26,27,32,34,36,39],                  [-4,0,-3,24,-4,-4,-4,-3,-4,-4],
      [-3,0,-4,-4,-3,-3,-3,-3,-3,-3],          [0,0,0,-4,-4,-4,-3,-4,-4,0],                      [-4,-4,-4,-4,-4,-3,-4,-4,-4,-3],
      [-4,0,-3,-3,-4,28,29,30,31,-4],          [-3,-3,-3,-3,-3,-3,-3,-4,-4,-3],                  [-3,0,0,-4,-4,-4,-4,-4,-4,-4],
      [-3,-3,-4,-4,-4,-3,-3,-3,-3,-3],         [-3,0,-4,-4,-4,-4,-4,-4,-4,-4],                   [-4,-4,-3,-4,33,-4,-4,-4,-4,-4],
      [-3,0,-4,-4,-4,-4,-3,-3,-3,0],           [-4,0,-3,-3,-4,0,-4,-4,35,-4],                    [0,0,-4,-4,0,-4,-4,-4,-3,-3],
      [-4,0,-4,37,-4,-4,-3,-4,-4,38],          [-3,0,-4,-3,-3,-4,-4,0,-4,-4],                    [-3,0,0,0,0,-3,-4,-4,-4,-4],
      [0,-4,-3,40,-4,-4,-4,-4,-3,-4],          [-3,0,-4,-4,-4,-3,-3,0,-3,0],                     [0,0,0,-2,-2,-2,-2,0,0,0],
      [0,0,43,47,-3,-3,48,49,51,52],           [-2,0,44,-3,-3,-3,-3,-3,45,46],                   [-3,0,-4,-4,-4,-4,-4,-4,-4,-4],
      [-3,-3,-4,-4,-3,-3,-3,-4,-4,0],          [-2,-3,-2,-2,-2,-2,-2,-3,-3,-2],                  [0,0,-3,-3,-3,0,-4,0,-4,-4],
      [-4,0,-3,-4,-3,-4,-4,-4,-3,0],           [50,-2,0,-3,-3,-4,-4,-3,-4,-4],                   [-2,0,-4,-4,-4,-4,-4,-4,-4,-2],
      [-4,0,-3,0,-3,-3,-3,-3,-3,-3],           [0,0,-3,-4,-4,-4,0,0,0,53],                       [0,0,-5,0,-5,0,-5,0,-5,0],
      [131,0,-3,55,59,60,61,62,64,65],         [0,-4,-4,-4,-3,-3,-4,-4,-4,56],                   [0,0,-3,0,0,0,57,58,-3,0],
      [0,-4,-4,-4,-3,-3,-3,-3,-3,-3],          [0,0,0,-3,-4,0,0,-4,0,0],                         [0,0,-3,-3,-4,-4,-3,-4,-4,-3],
      [-4,-4,-3,-4,-4,-4,-4,-4,-4,-3],         [0,-4,-4,-4,-4,-4,-4,-4,-4,-4],                   [-110,0,-4,-4,-4,-4,63,-4,-4,0],
      [0,0,-4,-4,-4,-4,-4,-4,0,-5],            [0,-4,-3,-3,-4,-4,-4,-4,0,0],                     [0,0,-3,-3,-4,-4,-4,66,-4,68],
      [0,0,-4,-4,-4,0,0,-4,-4,67],             [0,0,-5,-5,-5,-5,-5,-4,-4,-5],                    [0,0,-4,-4,-4,-4,-4,-4,-4,-3],
      [132,0,0,0,-2,0,-2,-2,0,0],              [133,0,71,72,73,-3,75,76,-3,77],                  [0,-4,-3,-3,-3,-4,-3,-3,-3,-3],
      [0,0,0,0,-3,-4,-4,-4,-4,-4],             [-4,0,-4,-4,-4,-4,74,-4,-4,-4],                   [0,0,0,-4,-4,-4,-4,0,-5,0],
      [0,-4,-3,-4,-3,-4,-4,-4,-4,0],           [-4,-4,-4,-4,-4,-3,-4,0,-4,-4],                   [-4,-4,-3,-3,78,79,80,-4,-4,-4],
      [0,0,-3,-3,-3,-3,-4,-4,-4,-3],           [-3,0,-4,-4,-4,-3,-3,-4,-4,-3],                   [-3,0,-4,-4,-4,-4,-3,-3,-4,-4],
      [134,0,82,87,94,96,98,111,112,113],      [-4,0,-3,-4,83,-3,-4,-4,-3,84],                   [-3,0,-3,-3,-4,-4,-4,-4,-4,-3],
      [-3,0,85,-4,86,-4,-3,-4,-4,0],           [-4,-3,-3,-3,-3,-3,-3,-3,-3,-3],                  [-4,-3,-3,-3,-4,-4,0,0,0,0],
      [0,0,-3,-4,-4,-4,88,89,91,92],           [-3,0,-4,-4,-4,-4,-4,-4,-4,-4],                   [0,0,-4,-4,-4,-4,90,-3,-3,0],
      [-4,0,-4,-4,-4,-4,-3,-3,-3,-4],          [0,0,-4,-4,-4,-4,0,-5,-5,0],                      [-3,0,-3,-3,-3,-3,93,-3,-3,-3],
      [0,0,-5,-3,0,-5,-3,-5,-5,0],             [0,0,0,0,0,-4,-4,95,-4,-3],                       [0,0,-4,-4,-4,-4,-4,-5,-4,0],
      [0,97,-4,-4,-4,-4,-4,-4,-4,-4],          [0,0,-5,0,-5,0,0,0,0,0],                          [0,0,-3,99,-3,101,103,105,108,109],
      [0,0,-4,-4,-4,-4,100,-4,-4,0],           [0,0,-3,-3,-3,0,-4,0,-3,0],                       [0,0,-3,0,-4,102,-4,-4,0,0],
      [-4,0,-3,0,-4,0,0,0,-4,0],               [-3,0,-4,-4,-4,-4,-4,-4,-4,104],                  [-4,-3,-4,-4,-4,-4,-4,-3,-3,-4],
      [0,0,106,107,-4,-4,-4,-4,-4,-4],         [0,0,-3,-3,-3,-4,-3,-4,-3,0],                     [0,0,0,-4,-4,-4,-4,-3,-3,0],
      [-3,0,-4,-4,-4,-4,-4,-4,-4,-3],          [-3,0,-4,-4,-3,-3,-4,-4,-4,110],                  [0,0,-4,-4,0,-3,0,-3,0,-3],
      [0,0,0,0,0,-4,0,-4,-3,-4],               [-4,0,0,-4,-4,-4,-3,-4,-3,-4],                    [0,0,-4,-4,-4,-4,-4,-4,-4,-3],
      [135,0,115,116,117,118,119,120,121,123], [-4,0,-3,-3,-3,-3,-3,-3,-3,-3],                   [-4,0,-3,-3,-3,-3,-3,-3,-3,-3],
      [-4,0,-4,-4,-4,0,-4,-4,-4,-4],           [-4,0,-4,0,-4,-4,-4,-4,-3,-4],                    [0,0,-3,-3,-4,-4,-4,-4,-4,-4],
      [0,0,-4,-4,-4,-3,0,-4,-4,-4],            [122,0,-4,-4,-4,-4,-4,-4,-3,-3],                  [0,0,-5,-4,-4,-4,-4,-4,-4,-4],
      [-109,124,-3,125,128,-4,129,-4,-3,0],    [0,0,-5,-5,0,0,0,0,0,0],                          [0,0,-4,126,127,-4,-4,-4,-4,0],
      [0,-3,-4,-4,-4,-4,-4,-4,-4,-4],          [-4,-4,0,-3,0,-3,0,-3,0,-4],                      [-3,0,-4,-4,-4,-4,-4,-3,-3,-4],
      [0,0,-4,-4,-4,-4,-4,-4,-4,-5],           [0,-106,-106,-106,-107,-106,-106,-106,-106,-106], [0,-105,-105,-105,-105,-105,-105,-105,-105,-105],
      [-108,0,0,0,0,0,0,0,0,0],                [0,-102,-102,-102,-102,-102,-102,-102,-102,-102], [-104,-102,-102,-102,-102,-102,-102,-102,-102,-102],
      [0,-102,-102,-102,-102,-102,-102,-102,-102,-102]
];


//-------------------------------------------------------------------------------
// 電話番号のフォーマット&検証 ( PhoneNumber )
//    --- ValidateTelephone/FormatTelephone の簡易インターフェース ---
//
// TelCode -- 半角文字列[ 0-9, -, (, ) ]
//            固定電話/携帯電話などの[ハイフン/括弧]編集 or 未編集電話番号
//
// Action  -- "V"        : 電話番号の検証
//            "F1"〜"F6" : 電話番号の区切り編集( 2桁目が EditType の値 )
//
// 返却値
//    Action= "V"        -- 0: 検証エラー , 1:検証OK , 2:検証OK(電話番号が数字のみ)
//                            (Boolean 変換すると 0⇒False, 1/2⇒True になります)
//
//          = "F1"〜"F6" -- 電話番号が正しい場合: EditType(2桁目)に従って編集した電話番号
//                          電話番号が誤っている場合: "Phone Error"
//
//          上記以外     -- "" (Boolean変換でFalse)で返ります。
//
//-------------------------------------------------------------------------------
function PhoneNumber($TelCode = "", $Action = "") {

    // FormatTelephone/ValidateTelephone の返却値
    $Result = InitTelephone("Value");

    $strAction = "";

    $patternAction = "/^(([Ff][1-6])|[Vv])$/";  // "F1"〜"F6" or "V"

    if (is_string($Action)) {
        if (preg_match($patternAction, $Action) == true) {
            $strAction = mb_strtoupper($Action);
        } else {
            return "";
        }
    } else {
        return "";
    }

    if (! (is_string($TelCode))) {
        return "";
    }

    if ($strAction == "V") {
        $Result = ValidateTelephone($TelCode);
        switch ($Result["ErrId"]) {
            case 0:
                return 1;  // OK(Boolean変換でTrue)
                break;
            case 8:
                return 2;  // OK (電話番号が数字のみ, Boolean変換でTrue)
                break;
            default:
                return 0;  // エラー(Boolean変換でFalse)
        }
    } else {
        $Result = FormatTelephone($TelCode, (int)(mb_substr($strAction, 1, 1)) );
        if ($Result["ErrId"] == 0) {
            return $Result["TelEdit"];
        } else {
            return "Phone Error";
        }
    }
}


//-------------------------------------------------------------------------------
// 返却値を受け取る変数に連想配列の初期設定 ( InitTelephone )
//
// Target -- "Value" or "Type" (左記以外 "Value" 扱い)
//
// 返却値 -- Target = "Value" の場合、下記の連想配列
//              ["TelType" => -1, "EditType" => 9, "TelEdit" => "", "ErrId" => 0]
//           Target = "Type" の場合、下記の連想配列
//              ["TelType" => -1, "SizeAll" => 0, "Size1" => 0, "Size2" => 0, "Size3" => 0, "ErrId" => 0]
//-------------------------------------------------------------------------------
function InitTelephone($Target = "Value") {
    $Target2 = "";

    if (is_string($Target)) {
        if (mb_strtoupper($Target) == "VALUE") {
            $Target2 = "Value";
        } else if (mb_strtoupper($Target) == "TYPE") {
            $Target2 = "Type";
        } else {
            $Target2 = "Value";
        }
    } else {
        $Target2 = "Value";
    }

    if ($Target2 == "Value") {
        return ["TelType" => -1, "EditType" => 9, "TelEdit" => "", "ErrId" => 0];
    } else {
        return ["TelType" => -1, "SizeAll" => 0, "Size1" => 0, "Size2" => 0, "Size3" => 0, "ErrId" => 0];
    }
}


//-------------------------------------------------------------------------------
// 電話番号の検証 ( ValidateTelephone )
//
// TelCode -- 半角文字列[ 0-9, -, (, ) ]
//            固定電話/携帯電話などの[ハイフン/括弧]編集
//
// 返却値  -- [TelType, EditType, TelEdit, ErrId] の連想配列
//   TelType   1:固定電話(10桁), 2:携帯電話(070/080/090,11桁), 3:着信課金(0120,10桁),
//             4:着信課金(0800,11桁), 5:IP電話(050,11桁), 6:M2M(020x,11桁,x≠4),
//             7:ポケベル(0204,11桁), 8:FMC(0600,11桁), 9:情報料徴収(0990,10桁),
//             10:統一番号(0570,10桁), -1:other
//
//   EditType  1or4: 0AB-CDE-FGHJ, 2or5: 0AB(CDE)FGHJ, 3or6: (0AB)CDE-FGHJ, 9:other
//             携帯電話のみ、[3-4-4 桁]で区切る⇒1〜3 , [3-3-5 桁]で区切る⇒4〜6
//
//   TelEdit   TelType/EditType で編集し直された電話番号(正しい編集結果)
//               ErrId=0/1/3/5 : 同じパターンで編集し直された電話番号
//                 〃 =8       : ハイフン編集した電話番号
//                 〃 =2       : TelCode のまま(未使用局番なので正誤は判断しない)
//                 〃 =4/6/9   : 空文字
//
//   ErrId     0:OK, 1:区切り位置不正, 2:未使用局番(固定電話), 3:市内局番1桁目が[0 or 1](固定電話),
//             4:引数不正([数字,ハイフン,括弧]以外がある or 編集パターン不正 or '0'始まりでない or 桁数不足),
//             5:引数不正(局番タイプに応じた桁数と不一致),
//             6:その他のエラー, 8:OK(数字のみ指定), 9:引数未定義(TelCode)
//-------------------------------------------------------------------------------
function ValidateTelephone($TelCode = "") {

    // 返却値
    $Result = InitTelephone("Value");

    // FormatTelephoneの返却値
    $ResultFormat = InitTelephone("Value");
    // 携帯電話(3-3-5桁区切り編集の再取得用)
    $ResultFormat2 = InitTelephone("Value");

    $strTelCode = "";    // PHP5では引数に型指定(string)が無いのでチェック&変換する
    $TelNumber = "";    // ハイフン,括弧除去(数字のみ)
    $EditType = "";

    // 厳密なチェックは FormatTelephone の結果と比較して行なうので
    // 下記のパターンチェックでは桁数については曖昧で構わない('0'始まりのみチェックする)
    $pattern0 = "/^0\d+$/";            // 0123456789
    $pattern1 = "/^0\d+-\d+-\d+$/";    // 012-345-6789
    $pattern2 = "/^0\d+\(\d+\)\d+$/";  // 012(345)6789
    $pattern3 = "/^\(0\d+\)\d+-\d+$/"; // (012)345-6789


    // PHP5 では約20億を超える数値は int ではなく float になる
    // 電話番号を数値で指定すると int を超える
    if (is_string($TelCode)) {
        $strTelCode = $TelCode;
    } elseif (is_float($TelCode) || is_int($TelCode)) {
        $strTelCode = (string)$TelCode;    // 数値の場合、先頭'0'が取れるので次の判定でエラーで返る
    } else {
        $Result["ErrId"] = 9;
        return $Result;
    }

    // 引数(TelCode)省略の場合、既定値(空文字)により以下でエラー判定される
    if (preg_match($pattern0, $strTelCode) == true) {  // (数字のみ)  0123456789
        // 数字のみ指定 ⇒ 編集のみ行って比較検証は無し
        $ResultFormat = FormatTelephone($strTelCode, 1);  // ハイフン編集で固定
        $Result["TelType"] = $ResultFormat["TelType"];
        $Result["EditType"] = 1;
        $Result["TelEdit"] = $ResultFormat["TelEdit"];
        // ValidateTephone用エラーコードに差替え
        switch ($ResultFormat["ErrId"]) {
            case 0:
                $Result["ErrId"] = 8;  // OK(数字のみ指定)
                break;
            case 1:
                $Result["ErrId"] = 2;  // 未使用局番(固定電話番号)
                break;
            case 2:
                $Result["ErrId"] = 3;  // 市内局番1桁目が[0 or 1](固定電話)
                break;
            case 3:
                $Result["ErrId"] = 4;  // 桁数不足(数字が10桁未満)
                break;
            case 4:
                $Result["ErrId"] = 5;  // 局番タイプに応じた桁数と不一致
                break;
            default:
                $Result["ErrId"] = 6;  // その他のエラー
        }
        return $Result;
    } elseif (preg_match($pattern1, $strTelCode) == true) {  // EditType:1  012-345-6789
        $EditType = 1;
        $Result["EditType"] = 1;
    } elseif (preg_match($pattern2, $strTelCode) == true) {  // EditType:2 012(345)6789
        $EditType = 2;
        $Result["EditType"] = 2;
    } elseif (preg_match($pattern3, $strTelCode) == true) {  // EditType:3 (012)345-6789
        $EditType = 3;
        $Result["EditType"] = 3;
    } else {
        // [数字,ハイフン,括弧]以外がある or 編集パターン不正 or '0'始まりでない
        $Result["ErrId"] = 4;
        $Result["TelEdit"] = $strTelCode;
        return $Result;
    }

    // FormatTelephone 用に[ハイフン,括弧]を除去して「数字のみ」にする
    $TelNumber = preg_replace('/(-)|(\()|(\))/', "", $strTelCode);

    $ResultFormat = FormatTelephone($TelNumber, $EditType);
    // FormatTelephone で編集し直した結果と比較して検証する
    // ErrId で返るのは
    //    0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話)
    //    3:引数不正(TelCode: 数字が10桁未満),
    //    4:引数不正(TelCode: 局番タイプの桁数と不一致)
    // 下記状態は既にチェック済なので、その値が返ることは無い
    //    3:引数不正(TelCode: ['0'始まりの数字]以外)
    //    6:引数不正(EditType)
    //    9:引数未定義(TelCode/EditType)

    $Result["TelType"] = $ResultFormat["TelType"];
    $Result["TelEdit"] = $ResultFormat["TelEdit"];
    // ValidateTephone用エラーコードに差替え
    switch ($ResultFormat["ErrId"]) {
        case 0:  // FormatTelephoneでOK ⇒ 正しい電話番号
            if ($strTelCode === $ResultFormat["TelEdit"]) {
                $Result["ErrId"] = 0;    // OK (区切り位置も正しい)
            } else {
                if ($ResultFormat["TelType"] == 2) {   // 携帯電話(070/080/090)
                    // 携帯電話の場合、別の区切り方[3-3-5 桁]で再チェック
                    $ResultFormat2 = FormatTelephone($TelNumber, $EditType + 3);
                    if ($strTelCode === $ResultFormat2["TelEdit"]) {
                        // OK (3-3-5桁で一致)
                        $Result["ErrId"] = 0;  // OK
                        $Result["TelEdit"] = $ResultFormat2["TelEdit"];
                        $Result["EditType"] = $EditType + 3;
                    } else {
                        $Result["ErrId"] = 1;  // 区切り位置不正
                    }
                } else {
                    // 携帯電話 以外
                    $Result["ErrId"] = 1;  // 区切り位置不正
                }
            }
            break;
        case 1:
            $Result["ErrId"] = 2;  // 未使用局番(固定電話番号)
            $Result["TelEdit"] = TelCode;  // ResultFormat.TelEdit(=TelNumber)なので入力値に差し替える
            break;
        case 2:
            $Result["ErrId"] = 3;  // 市内局番1桁目が[0 or 1](固定電話)
            break;
        case 3:
            $Result["ErrId"] = 4;  // 桁数不足(数字が10桁未満)
            break;
        case 4:
            $Result["ErrId"] = 5;  // 局番タイプに応じた桁数と不一致
            break;
        default:
            $Result["ErrId"] = 6;  // その他のエラー
    }
    return $Result;
}


//-------------------------------------------------------------------------------
// 電話番号のハイフン編集 ( FormatTelephone )
//
// TelCode  -- 半角数字文字列[ 0-9 ], 10 or 11桁(先頭'0'固定)
//             固定電話/携帯電話などの電話番号
//
// EditType -- 数値
//             1or4: 0AB-CDE-FGHJ , 2or5: 0AB(CDE)FGHJ , 3or6: (0AB)CDE-FGHJ
//             携帯電話のみ、[3-4-4 桁]で区切る⇒1〜3 , [3-3-5 桁]で区切る⇒4〜6
//
// 返却値 -- [TelType, EditType, TelEdit, ErrId] の連想配列
//   TelType   1:固定電話(10桁), 2:携帯電話(070/080/090,11桁), 3:着信課金(0120,10桁),
//             4:着信課金(0800,11桁), 5:IP電話(050,11桁), 6:M2M(020x,11桁,x≠4),
//             7:ポケベル(0204,11桁), 8:FMC(0600,11桁), 9:情報料徴収(0990,10桁),
//             10:統一番号(0570,10桁), -1:other
//
//   EditType  1or4: 0AB-CDE-FGHJ, 2or5: 0AB(CDE)FGHJ, 3or6: (0AB)CDE-FGHJ, 9:other
//
//   TelEdit   編集された電話番号
//               ErrId=0/2/4 : 編集された電話番号
//                〃  =1     : TelCode の内容のまま
//                〃  =3/6/9 : 空文字
//
//   ErrId     0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話),
//             3:引数不正(TelCode: ['0'始まりの数字]以外 or 10桁未満),
//             4:引数不正(TelCode: 局番タイプに応じた桁数と不一致)
//             6:引数不正(EditType), 9:引数未定義(TelCode/EditType)
//-------------------------------------------------------------------------------
function FormatTelephone($TelCode = "", $EditType = 0) {

    // 返却値
    $Result = InitTelephone("Value");

    // GetTypeTelephone 返却値
    $ResultType = InitTelephone("Type");

    $strTelCode = "";    // PHP5では引数に型指定(string)が無いのでチェック&変換する
    $intEditType = 0;    // PHP5では引数に型指定(int)が無いのでチェック&変換する

    $TelPattern  = "/^0\d{9,}$/";     // '0'始まりの数字文字列(10桁以上)
    $EditTypePattern = "/^[1-6]$/";   // '1' 〜 '6'(1文字)

    // PHP5 では約20億を超える数値は int ではなく float になる
    // 電話番号を数値で指定すると int を超える
    if (is_string($TelCode)) {
        $strTelCode = $TelCode;
    } elseif (is_float($TelCode) || is_int($TelCode)) {
        $strTelCode = (string)$TelCode;    // 数値の場合、先頭'0'が取れるので次の判定でエラーで返る
    } else {
        $Result["ErrId"] = 9;
        return $Result;
    }

    // 引数(TelCode)省略の場合、既定値(空文字)により以下でエラー判定される
    // ['0'始まりの数字文字列(10桁以上)]のチェック
    // (番号翻訳(GetTypeTelephone)後に完全な桁数チェックを行なう。ここでは10桁未満のみNG)
    if (preg_match($TelPattern, $strTelCode) == false) {
        $Result["ErrId"] = 3;
        return $Result;
    }


    // 引数(EditType)省略の場合、既定値(0)により以下でエラー判定される
    // EditTypeのチェック
    if (is_string($EditType)) {
        if (preg_match($EditTypePattern, $EditType) == true) {
            $intEditType = (int)$EditType;
        } else {
            $Result["ErrId"] = 6;
            return $Result;
        }
    } else if (is_int($EditType)) {
        if ($EditType >= 1 && $EditType <= 6) {
            $intEditType = $EditType;
        } else {
            $Result["ErrId"] = 6;
            return $Result;
        }
    } else {
        $Result["ErrId"] = 6;
        return $Result;
    }
    $Result["EditType"] = $intEditType;


    $ResultType = GetTypeTelephone($strTelCode);  // 番号翻訳して電話番号の種別と桁区切り情報を取得する
    // ErrId で返るのは
    //    0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話)
    // 下記状態は既にチェック済なので、その値が返ることは無い
    //    3:電話番号種別が不明(桁数不足で番号翻訳未了)← 10桁以上なので未了は無い
    //    8:引数不正(TelCode: ['0'始まりの数字]以外)
    //    9:引数未定義(TelCode)

    $Result["TelType"] = $ResultType["TelType"];
    $Result["ErrId"] = $ResultType["ErrId"];
    if ($ResultType["ErrId"] == 1) {
        // 未使用局番(固定電話)
        $Result["TelEdit"] = $strTelCode;
    } else {
        // 0:OK  or  2:市内局番1桁目が[0 or 1](固定電話)
        if ($ResultType["TelType"] == 2 && $intEditType >= 4) {
            // 携帯電話(070/080/090)で[3-3-5 桁区切り, 0x0-CDE-FGHJK]を指定された場合
            $Result["TelEdit"] = InsertSeparator($strTelCode, $intEditType, 3, 3);
        } else {
            // 携帯電話(3-4-4桁区切り)を含め、他は返却値で区切りパターンが得られる
            // 固定電話では[市外局番桁数/市内局番桁数/加入者番号桁数(4)]が得られる
            if ($ResultType["Size2"] == 0) {
                // 固定電話で、市内局番なし [ 0ABCDE-FGHJ ] (現在、この地域は存在しない)
                $Result["TelEdit"]  = mb_substr($strTelCode, 0, 6) . "-" . mb_substr($strTelCode, 6);
            } else {
                $Result["TelEdit"] = InsertSeparator($strTelCode, $intEditType, $ResultType["Size1"], $ResultType["Size2"]);
            }
        }

        if (mb_strlen($strTelCode) != $ResultType["SizeAll"]) {
            $Result["ErrId"] = 4;  // 桁数エラー(局番タイプに応じた桁数と不一致)
        }
    }

    return $Result;
}


function InsertSeparator($TelCode, $EditType, $Size1, $Size2) {
    $Result = "";
    $EditCode1 = mb_substr($TelCode, 0, $Size1);        // 1st section :固定電話では市外局番(先頭'0')
    $EditCode2 = mb_substr($TelCode, $Size1, $Size2);   // 2nd section :固定電話では市内局番
    $EditCode3 = mb_substr($TelCode, $Size1 + $Size2);  // 3rd section :固定電話では加入者番号(末尾まで取り出す)

    switch ($EditType) {
        case 1:  // 0AB-CDE-FGHJ
        case 4:
            $Result = $EditCode1 . "-" . $EditCode2 . "-" . $EditCode3;
            break;
        case 2:  // 0AB(CDE)FGHJ
        case 5:
            $Result = $EditCode1 . "(" . $EditCode2 . ")" . $EditCode3;
            break;
        case 3:  // (0AB)CDE-FGHJ
        case 6:
            $Result = "(" . $EditCode1 . ")" . $EditCode2 . "-" . $EditCode3;
    }
    return $Result;
}


//-------------------------------------------------------------------------------
// 電話番号タイプの取得 ( GetTypeTelephone )
//
// TelCode  -- 半角数字文字列(2桁以上, 先頭'0'固定)
//             固定電話/携帯電話などの電話番号(途中までの内容でも可)
//
// 返却値 -- [TelType, SizeAll, Size1, Size2, Size3, ErrId] の連想配列
//   TelType   1:固定電話(10桁), 2:携帯電話(070/080/090,11桁), 3:着信課金(0120,10桁),
//             4:着信課金(0800,11桁), 5:IP電話(050,11桁), 6:M2M(020x,11桁,x≠4),
//             7:ポケベル(0204,11桁), 8:FMC(0600,11桁), 9:情報料徴収(0990,10桁),
//             10:統一番号(0570,10桁), -1:other
//
//   SizeAll   ErrId=0/2 : TelType に応じた電話番号の桁数(10 or 11, Size1〜3の合計値)
//             ErrId=1/3/8/9 : 0
//
//   Size1-3   ErrId=0/2 : TelType に応じた電話番号の区切り桁数(Size1 には先頭'0'含む)
//             ErrId=1/3/8/9 : 0
//             (携帯電話(TelType=2)は[3-4-4 桁区切り]のみで返す)
//
//   ErrId     0:OK, 1:未使用局番(固定電話), 2:市内局番1桁目が[0 or 1](固定電話),
//             3:電話番号種別が不明(桁数不足で番号翻訳未了)
//             8:引数不正(TelCode: ['0'始まりの数字]以外), 9:引数未定義(TelCode)
//-------------------------------------------------------------------------------
function GetTypeTelephone($TelCode = "") {

    // 返却値
    $Result = InitTelephone("Type");

    global $AreaCodeArray;  // 番号翻訳テーブル

    $strTelCode = "";    // PHP5では引数に型指定(string)が無いのでチェック&変換する

    $AreaCodeLength = 0;
    $CityCodeLength = 0;
    $CityCode1 = "";      // 市内局番の1桁目
    $idx = 0;
    $cnt = 0;
    $num = 0;
    $LinkValue = 0;
    $TelType = 0;
    $TelPattern  = "/^0\d+$/";   // '0'始まりの数字文字列(桁数制限なし)

    // ( 1)固定電話       10桁 [ 桁区切りは番号翻訳結果に拠る ]
    // ( 2)携帯電話       11桁 [ 0x0-CDEF-GHJK , 070/080/090 ]
    // ( 3)着信課金       10桁 [ 0120-DEF-GHJ  ]
    // ( 4)着信課金       11桁 [ 0800-DEF-GHJK ]
    // ( 5)IP電話       11桁 [ 050-CDEF-GHJK ]
    // ( 6)M2M         11桁 [ 020-CDE-FGHJK ]
    // ( 7)ポケベル       11桁 [ 020-4DE-FGHJK ]
    // ( 8)FMC         11桁 [ 0600-DEF-GHJK ]
    // ( 9)情報料代理徴収 10桁 [ 0990-DEF-GHJ  ]
    // (10)全国統一番号   10桁 [ 0570-DEF-GHJ  ]
    //  SizeInfo [SizeAll, Size1, Size2, Size3]
    $SizeInfo = [
        [0,0,0,0],  [10,0,0,0], [11,3,4,4], [10,4,3,3], [11,4,3,4],
        [11,3,4,4], [11,3,3,5], [11,3,3,5], [11,4,3,4], [10,4,3,3], [10,4,3,3]
    ];

    // PHP5 では約20億を超える数値は int ではなく float になる
    // 電話番号を数値で指定すると int を超える
    if (is_string($TelCode)) {
        $strTelCode = $TelCode;
    } elseif (is_float($TelCode) || is_int($TelCode)) {
        $strTelCode = (string)$TelCode;    // 数値の場合、先頭'0'が取れるので次の判定でエラーで返る
    } else {
        $Result["ErrId"] = 9;
        return $Result;
    }

    // 引数(TelCode)省略の場合、既定値(空文字)により以下でエラー判定される
    // ['0'始まりの数字文字列(桁数制限なし)]のチェック
    if (preg_match($TelPattern, $strTelCode) == false) {
        $Result["ErrId"] = 8;
        return $Result;
    }

    // ※ 番号翻訳テーブルをソースコード内に直に収録しているので
    //    JSONファイルの読み込み処理は必要ない。

    // 番号翻訳テーブルから局番情報(市外局番の桁数 or [携帯番号 等]のTelType情報)を取得する。
    // [TelCode]の長さが[局番桁数 未満]も受け入れているので『番号翻訳 途中』でのループ終了(TelType= -1)もある。
    $idx = 0;
    $TelType = -1;
    $AreaCodeLength = 0;
    for ($cnt = 1; ($cnt <= 5) && ($cnt < mb_strlen($strTelCode)); $cnt++) {  // 番号翻訳テーブルはA〜Eコードの5段
        $num = (int)mb_substr($strTelCode, $cnt, 1);    // 電話番号の2桁目以降(1桁目は'0')を順次取り出す
        $LinkValue = $AreaCodeArray[$idx][$num];
        if ($LinkValue == 0) {   // 番号翻訳 終了(固定電話 未使用局番)
            $TelType = 1;
            $AreaCodeLength = 0;
            break;
        } elseif ($LinkValue < 0) {  // 番号翻訳 終了
            if ($LinkValue > -100) {
                // 固定電話の市外局番桁数(符号反転値) 実際の収録値[ -2 〜 -5 ]
                $TelType = 1;
                $AreaCodeLength = $LinkValue * (-1);  // 市外局番 桁数決定(先頭の "0" 含む)
            } else {
                // 携帯電話 等のTelType値(符号反転値 - 100) 実際の収録値[ -102 〜 -110 ]
                $TelType = ($LinkValue * (-1)) - 100;
            }
            break;
        } else {  // プラス値
            $idx = $LinkValue;  // 検索継続(収録値は NextIndex)
        }
    }

    // TelType に応じた桁区切りを設定する
    // 固定電話(TelType=1)では市外局番桁数(AreaCodeLength)に従って設定する
    // Size1 の値は[先頭'0'含む]
    $Result["TelType"] = $TelType;
    switch ($TelType) {
        case -1:
            // 電話番号種別が不明(桁数不足で番号翻訳未了)
            $Result["ErrId"] = 3;
            break;
        case 1:
            // 固定電話
            if ($AreaCodeLength == 0) {  // 未使用 局番
                $Result["ErrId"] = 1;
            } elseif ($AreaCodeLength == 6) {  // 市内局番なし [ 0ABCDE-FGHJ ] (現在、この地域は存在しない)
                $Result["ErrId"] = 0;
                $Result["SizeAll"] = 10;
                $Result["Size1"] = 6;
                $Result["Size2"] = 0;
                $Result["Size3"] = 4;
            } else {
                $CityCodeLength = 6 - $AreaCodeLength;  // 10 - AreaCodeLength - 4(加入者番号)
                if (mb_strlen($strTelCode) < ($AreaCodeLength + 1)) {
                    // [TelCode]が市内局番の桁位置までないので市内局番適否の判定不可⇒ OK で返す
                    $Result["ErrId"] = 0;
                } else {
                    // FormatTelephone から呼ばれる場合は 10桁以上なので必ず判定可能
                    $CityCode1 = mb_substr($strTelCode, $AreaCodeLength, 1);  // 市内局番 1桁目
                    if ($CityCode1 === "0" || $CityCode1 === "1") {
                        $Result["ErrId"] = 2;  // 市内局番1桁目に0/1は割り当てられない
                    } else {
                        $Result["ErrId"] = 0;  // OK
                    }
                }
                $Result["SizeAll"] = 10;
                $Result["Size1"] = $AreaCodeLength;
                $Result["Size2"] = $CityCodeLength;
                $Result["Size3"] = 4;
            }
            break;
        default:
            // TelType = 2〜10, SizeInfoテーブルから桁情報を設定する
            $Result["ErrId"] = 0;  // OK
            $Result["SizeAll"] = $SizeInfo[$TelType][0];
            $Result["Size1"]   = $SizeInfo[$TelType][1];
            $Result["Size2"]   = $SizeInfo[$TelType][2];
            $Result["Size3"]   = $SizeInfo[$TelType][3];
    }
    return $Result;
}

?>