» /rd/lib/Phone/Phone.php

<?

/*

  Common Phone Functions

  function format($ph)  # human formatted phone
  function clean($ph)   # COUNTRYCODE.PHONE

 */

class Phone

{
    
// Hardcoded. Sould not changes.
    // Function to generate: Phone_PrefixAdmin::spreadPerSites()
    
const CODE_TO_SITE_MAP = [
        
RDSite::SITE_ID_PHONEOWNER_US => [201,206,210,212,214,216,218,225,229,234,239,253,254,270,281,302,303,305,308,315,316,318,319,320,321,325,330,337,340,346,347,352,360,361,385,386,401,402,404,405,407,409,419,423,425,430,432,435,440,456,469,470,478,500,502,504,505,507,509,512,513,515,516,518,521,522,523,524,525,526,527,528,529,531,532,533,535,538,539,542,543,544,545,546,547,549,550,551,552,553,554,556,558,561,563,566,567,569,575,577,578,580,585,588,589,600,603,605,606,607,609,612,614,615,620,622,631,633,641,644,646,651,655,671,677,678,682,684,688,700,701,702,706,710,712,713,716,718,719,720,725,727,731,732,737,740,754,762,763,770,772,775,785,786,800,801,806,808,813,817,822,830,832,833,844,845,848,850,855,856,859,862,863,865,866,877,880,881,882,883,884,885,886,887,888,889,900,901,903,904,907,908,912,913,914,915,917,918,929,931,936,937,940,941,952,954,956,970,972,973,979,985],
        
RDSite::SITE_ID_PHONEID_US => [202,203,204,205,207,208,209,213,215,217,219,224,226,228,231,236,240,242,246,248,249,250,251,252,256,260,262,263,264,267,268,269,272,276,284,289,301,304,306,307,309,310,312,313,314,317,323,331,334,336,339,343,345,351,354,365,367,368,382,387,403,406,408,410,412,413,414,415,416,417,418,424,428,431,434,437,438,441,442,443,450,458,460,468,473,474,475,479,480,484,501,503,506,508,510,514,517,519,520,530,534,540,541,548,559,562,570,571,573,574,579,581,584,586,587,601,602,604,608,610,613,616,617,618,619,623,626,630,636,639,647,649,650,657,660,661,662,664,667,669,670,672,681,683,703,704,705,707,708,709,714,715,717,721,724,734,742,747,753,757,758,760,765,767,769,773,774,778,779,780,781,782,784,787,802,803,804,805,807,809,810,812,814,815,816,818,819,825,828,831,843,847,857,858,860,864,867,868,869,870,872,873,876,878,879,902,905,906,909,910,916,919,920,925,928,938,939,942,947,949,951,971,978,980,984,989],
    ];

    
/**
     * Returns formatted number. Makes some validation.
     * IMPORTANT! Phone::format(2010000000) => (201)
     * @param mixed $ph
     * @return string
     */
    
PUBLIC static function format($ph) { # human formatted phone
        
$ph=preg_replace("![^\d]!","",$ph);
        
$pl=strlen($ph);

        
$p3=substr($ph,0,3);
        if (
$p3=='011' $ph=substr($ph,3);
        if (
substr($ph,0,2)=='00'$ph=substr($ph,2);
        if (
$ph[0]=='0'$ph=substr($ph,1);
        
$pl=strlen($ph);
        if (
$pl<5) return ""// no junk phones
        
if ($pl<7) return substr($ph,0,-4)."-".substr($ph,-4);
        switch(
$pl) {
        case 
7: return substr($ph,0,3)."-".substr($ph,3);
        case 
8:
#        case 9: return substr($ph,0,-4)."-".substr($ph,-4);
        
case 10: return "(".substr($ph,0,3).") ".self::format(substr($ph,3));
        }

        return 
self::_international_phone_format($ph);
    }

    
/**
     * Formatted US phone, missing digits replaced with filler
     * ex: (617) 372-51xx or (617) 372-xxxx
     * @param mixed $phone_prefix string or int phone
     * @param string $filler character to mask digits in partially showing phone
     * @param string $format sprintf() format for phone output
     * @return string phone number representation
     */
    
static function us($phone_prefix$filler="*"$format=null) { # formatted US phone, missing digits replaced with $filler

        
if (!$format) {
            
$format RDSite::sitePhoneFormat();
        }
        
        
$phone_prefix self::clean($phone_prefix);

        if (!
$phone_prefix) {
            return 
"";
        }
        
$p substr($phone_prefix01)=='1' substr($phone_prefix1) : $phone_prefix;  # 10 digit US number

        
$l strlen($p);
        if (
$l == 3) {
            if (! 
$filler) {
                
//$ph = "($p)";
                
$ph sprintf($format$p"""");
                
$ph preg_replace('!([^\d])([^\d]+)!i'"$1"$ph); // remove multiple
            
} else {
                
//$ph = "($p) ".str_repeat($filler, 3)."-".str_repeat($filler, 4);
                
$ph sprintf($format$pstr_repeat($filler3), str_repeat($filler4));
            }
        } else {
            
$addon null;
            if (
$l>10) {
                
$addon substr($p10);
                
$p substr($p010);
            }
            
$ph sprintf($formatsubstr($p03), substr($p33), substr($p6)) . cs(" #%d"$addon??0);
            if (
$filler && $l<10) {
                return 
$ph.str_repeat($filler10-$l);
            }
        }
        
        return 
$ph;
    }

    
/**
     * 3-digit US phone prefix from $phone
     * @param string|int $phone
     * @return int
     * @example Phone::prefix3("1(800)1234-567");
     */
    
static function prefix3($phone) {
        
$phone self::clean($phone);
        return (int) (
floor($phone 10000000) - 1000);
    }

    
/**
     * 6-digit US phone prefix from $phone
     * @param string|int $phone
     * @return int
     * @example Phone::prefix6("1(800)1234-567");
     */
    
static function prefix6($phone) {
        
$phone self::clean($phone);
        return (int) (
floor($phone 10000) - 1000000);
    }

    
/*
        Reasons:
            rid    => XXX  - phone removal request
            fl     => XXX  - removed for specific Name (FL)
            pid+fl => XXX  - removed for profile_id, fl

    */
    
static function isForbidden($phone) { # [reason_hash] | null
        
$p Phone::clean($phone);
        if (
strlen($p)!=11)
            return;
        
#if ($r = M_ForbiddenPhones::isForbidden($p))
        #    return $r;
        
if ($r KRDB_Phone::isForbidden($p))
            return 
$r;
        
// null
    
}



    public static function 
mask_number($p$c '*'int $numberOfMask 4) {
        return 
substr($p0, -$numberOfMask) . str_repeat($c$numberOfMask);
    }

    
/**
     * @param string|null $source
     * @param int $numberOfMask
     * @param string $pattern
     * @param bool $startFromEnd
     * @return string
     */
    
public static function phoneMaskByDirection(?string $sourceint $numberOfMask 3string $pattern '*'bool $startFromEnd true): string
    
{
        
$strlen \mb_strlen($source);
        
$phoneResult '';

        for (
$len 0$len $strlen$len++) {
            if (
$startFromEnd) {
                
$phoneResult .= $len >= ($strlen $numberOfMask) ? preg_replace('/[\d]/'$pattern$source[$len]) : $source[$len];
            } else {
                
$phoneResult .= $len <= $numberOfMask preg_replace('/[\d]/'$pattern$source[$len]) : $source[$len];
            }
        }

        return 
$phoneResult;
    }

    
/**
     * use to store phones in database
     * trim prefixes: "+", "011", "00", remove non digits
     * ph = [prefix]countrycode-phone
     * @param string|int $ph
     * @return false|string|string[]|null
     */
    
PUBLIC static function clean($ph) { # COUNTRYCODE.PHONE
        
if (! $ph)
            return 
"";
        
$ph=trim($ph);
        if(!isset(
$ph[0]))
            return 
"";
        
$international = ($ph[0]=='+') || substr($ph,0,1)=='0';
        
$ph=preg_replace("![^\d]!","",$ph); //only numbers
        
if (substr($ph,0,2)=='00') return substr($ph,2);
        if (
substr($ph,0,3)=='011') return substr($ph,3);
        if (!empty(
$ph[0]) && $ph[0]=='0'$ph=substr($ph,1);

        if (! 
$international || "1" == substr($ph,0,1)) { //Non international or internation North America number
            
$len strlen($ph);
            if (
$len 10) {
                if (
"1" == substr($ph,0,1)) {
                    
$ph substr($ph011); // remove ext
                
} else {
                    
$ph substr($ph010); // remove ext
                
}
                
$len strlen($ph);
            }

            if (
$len == 10 && "1" != substr($ph,0,1)) {
                
$ph= (int) "1".$ph// US NUMBER
            
}
        }

        return 
$ph;
    }


    
// ================================================================================
    // INTERNAL

    
static function _international_phone_format($ph) {
        static 
$short_prefix=array(
                            
20=>1,27=>1,
                            
30=>1,31=>1,32=>1,33=>1,34=>1,36=>1,39=>1,
                            
40=>1,41=>1,43=>1,44=>1,45=>1,46=>1,47=>1,48=>1,49=>1,
                            
51=>1,52=>1,53=>1,54=>1,55=>1,56=>1,57=>1,58=>1,
                            
60=>1,61=>1,62=>1,63=>1,64=>1,65=>1,66=>1,
                            
76=>1,77=>1,
                            
81=>1,82=>1,84=>1,86=>1,
                            
90=>1,91=>1,92=>1,93=>1,94=>1,95=>1,98=>1,
                            );

        static 
$long_prefix=array(
                           
1242=>1,1246=>1,1264=>1,1268=>1,1284=>1,
                           
1340=>1,1345=>1,1441=>1,1473=>1,
                           
1649=>1,1664=>1,1670=>1,1671=>1,1684=>1,
                           
1721=>1,1758=>1,1767=>1,1784=>1,1787=>1,
                           
1808=>1,1809=>1,1829=>1,1849=>1,1868=>1,1869=>1,1876=>1,
                           
1939=>1,
                           
5399=>1,
                           
7840=>1,7940=>1,
                           
8810=>1,8811=>1,8812=>1,8813=>1,8816=>1,8817=>1,8818=>1,8819=>1);

        if (isset(
$short_prefix[$ph[0].$ph[1]]) )
            
$p=2;
        else
            
$p=3// no 4d code support
        
if ($p==&& $ph[0]=='7')
            
$p=1;
        if (
$p==&& $ph[0]=='1')
            
$p=1;

        if (isset(
$long_prefix[substr($ph,0,4)] ) )
            
$p=4;

        
$pref=substr($ph,0,$p);
        
$post=substr($ph,-4);
        
$ph=substr($ph,$p,-4);
        
$ph=substr($ph,0,-3)."-".substr($ph,-3);
        if (
$ph[0]=='-'$ph=substr($ph,1);
        if (
$pref=='1')
            return 
"$ph-$post";
        return 
"+$pref $ph-$post";
    }


        
/*
          'name' => 'Jennie Rodriguez',
          'uk' => '1106769-20239:M1Data/90',
          'bday' => '19800207',
          'ethnic' => 'Hispanic',
          'education' => 2,
          'occupation' => 2,
          'owner_rental' => 2,
          'email' => 'jennierodriguez6@hotmail.com',
          '_updated' => 1394238635,
          'loc' =>
                'street' => '428 e 137th st apt 4b',
                'zip' => 10454,
                'region' => 34,
                'city' => 11,
                'city_main' => 11,
          'id' => 1,
          'tp' => 'M1Data',
    */
    
static function p7_item($r$phone$mode "ph_listing"bool $isPrettyWrap false) {
        switch (
RDSite::siteID()) {
        case 
RDSite::SITE_ID_NEWENGLANDFACTS_COM:
            
$cssClass 'col small-24 medium-10 large-6 gray-text';
            
$cssClass2 'col small-24 medium-14 large-18';
            
$cssClass3 "r text";
            break;
        case 
RDSite::SITE_ID_PHONEOWNER_US:
            
$cssClass 'column small-24 medium-10 large-6 gray-text';
            
$cssClass2 'column small-24 medium-14 large-18';
            
$cssClass3 "row text";
            break;
        default:
            
$cssClass 'col-md-8 gray-text';
            
$cssClass2 'col-md-15';
            
$cssClass3 "row text";
        }
        
$l $r['loc']??[];

        
$info "";
        
$not_a_person false;
        switch (
$r['tp']) {
            case 
'business':
                
//PFL2 "Avvo", "BBB", "HealthGrades", "Martindale", "Trulia", "Vitals", "Zillow"
                
if ($e = ($r["info"]['title']??""))
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Title:</div><div class='$cssClass2'>$e</div></div>";
                if (
$e = ($r["info"]['organization']??"")) {
                    if (!
$r["name"]) {
                        
$r["name"] = $e;
                        
$not_a_person true;
                    } elseif (
$r["name"] != $e) {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Organization:</div><div class='$cssClass2'>$e</div></div>";
                    }
                }
                if (
$e = ($r["info"]['specialties']??[])) {
                    
$e array_slice($e010true);
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Specialties:</div><div class='$cssClass2'>".join(", "$e)."</div></div>";
                }
                if ((
$r['src']??"") == 'HealthGrades') {
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Type:</div><div class='$cssClass2'>Medical";
                            if (
$t = ($r["info"]["suffix"]??"")) {
                                if ((
$r['src']??"") == 'HealthGrades') {
                                    
$t_str Helper_MedicalAcronyms::short2long($t);
                                    
$info .= ", $tcs(" <span class='gray-text'>(%s)</span>"$t_str);
                                } else {
                                    
$info .= $t";
                                }
                            }
                    
$info .= "</div></div>";
                }
                if ((
$r['src']??"") == 'Avvo') {
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Type:</div><div class='$cssClass2'>Lawyers</div></div>";
                }

                break;
            case 
'Shopclub':
            case 
'USBusiness':
            case 
'Org':
            case 
'Allareacodes':
                if (!empty(
$r['email'])) {
                    if (
\RDSite::SITE_ID_PHONEOWNER_US != Phone::codeToSite($phone) || "light_report" == $mode || "prefix_listing" == $mode || "crosspage_address_phone" == $mode || "crosspage_org_phone" == $mode || "crosspage_profile_phone" == $mode || "org-phone" == $mode) {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Email:</div><div class='$cssClass2'>$r[email]</div></div>";
                    } else {
                        
//$href2 = "https://".C(".radaris")."/report/reverse_phone_lookup?phone=".substr($phone,1);
                        
$href2 Phone::reversePhoneLookupReportUrl($phone, !CD("is_radaris")?true:C(".radaris"));
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Email:</div><div class='$cssClass2'><a rel=\"nofollow\" href=\"$href2\" >$r[email]</a></div></div>";
                    }
                }
                if (
$e = ($r['title']??''))
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Title:</div><div class='$cssClass2'>$e</div></div>";

                if (
$e = ($r['organization']??'')) {
                    if (empty(
$r["name"])) {
                        
$r["name"] = $e;
                        
$not_a_person true;
                    } elseif (
$r["name"] != $e) {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Organization:</div><div class='$cssClass2'>$e</div></div>";
                    }
                }
                if (
$e = ($r['sic_name']??''))
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Specialties:</div><div class='$cssClass2'>$e</div></div>";
                if (
$e = ($r['industry']??'')){
                    if (
is_array($e) ) {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Industry:</div><div class='$cssClass2'>".cut(implode(", "$e), 180)."</div></div>";
                    } else {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Industry:</div><div class='$cssClass2'>".cut($e180)."</div></div>";
                    }
                }
                if (isset(
$r["services"])) {
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Services:</div><div class='$cssClass2'>".cut(implode(", "$r["services"]), 180)."</div></div>";
                }
                if (isset(
$r["categories"])) {
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Categories:</div><div class='$cssClass2'>".cut(implode(", "$r["categories"]), 180)."</div></div>";
                }
                if (isset(
$r["products"])) {
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Products:</div><div class='$cssClass2'>".cut(implode(", "$r["products"]), 180)."</div></div>";
                }
                
$sites= [];
                if (isset(
$r["sites"])) {

                    foreach(
$r["sites"] as $e){
                        
$site_url $e;
                        if (!
starts_with($site_url"http")) {
                            
$site_url "http://$site_url";
                        }
                        
$site_host preg_replace('`^www\.`i','',parse_url($site_urlPHP_URL_HOST));

                        if (
"prefix_listing" == $mode) {
                            
$sites[$site_host] = $site_host;
                        } else {
                            
$sites[$site_host] = '<a rel="nofollow" href="'.$site_url.'">'.$site_host.'</a>';
                        }

                    }

                }

                if (
$e = ($r["website"]??'')) {
                    
$site_url $e;
                    if (!
starts_with($site_url"http")) {
                        
$site_url "http://$site_url";
                    }
                    
$site_host preg_replace('`^www\.`i','',parse_url($site_urlPHP_URL_HOST));
                    if (
"prefix_listing" == $mode) {
                        
$sites[$site_host] = $site_host;
                    } else {
                        
$sites[$site_host] = "<a rel=\"nofollow\" href=\"$site_url\">".$site_host."</a>";
                    }
                }
                if(
$sites){
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Site:</div><div class='$cssClass2'>".join(', ',$sites)."</div></div>";
                }
                break;
            case 
'M1Data':
                if (!empty(
$r['email'])) {
                    if (
\RDSite::SITE_ID_PHONEOWNER_US != Phone::codeToSite($phone) || "light_report" == $mode || "prefix_listing" == $mode || "crosspage_address_phone" == $mode || "crosspage_org_phone" == $mode || "crosspage_profile_phone" == $mode) {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Email:</div><div class='$cssClass2'>$r[email]</div></div>";
                    } else {
                        
$href2 Phone::reversePhoneLookupReportUrl($phone, !CD("is_radaris")?true:C(".radaris"));
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Email:</div><div class='$cssClass2'><a rel=\"nofollow\" href=\"$href2\" >$r[email]</a></div></div>";
                    }
                }
                
#if (1 == $r['owner_rental'])
                #    $info .= "Homeowner";
                
if ($e = ($r['occupation']??"")) {
                    
$info .= "<div class='$cssClass3'><div class='$cssClass'>Occupation:</div><div class='$cssClass2'>".KRDB_PFL2_M1Data::$OCCUPATION[$e]."</div></div>";
                }
                if (
$e = ($r['education']??"")) {
                    if (
$e>0) {
                        
$info .= "<div class='$cssClass3'><div class='$cssClass'>Education:</div><div class='$cssClass2'>".KRDB_PFL2_M1Data::$EDUCATION[$e]."</div></div>";
                    }
                }
                if (
$e = ($r["bday"]??0)) {
                    
$r["bday"] = str_replace("X""0"$r["bday"]);
                }
            break;
        }

        if (!empty(
$r["name"]) && false !== strpos($r["name"], "&") || ($r['src']??"")=="BuildZoom" ) {
            
$not_a_person true;
        }

        
# $bday
        
$age = ($r['bday']??0) ? dt2age($r['bday']) : "";
        
$age preg_replace("![^\d]!"""$age);
        
$r['uk'] = preg_replace('!^(\d+-\d+)(-0:)!'"$1:"$r['uk']);
        if (
$not_a_person) {
            if(
$r['tp'] == 'Org'){

                if((
RDSite::siteID() == RDSite::SITE_ID_PHONEOWNER_COM) && $r['src']=="BuildZoom" && $r['score']>30  && $org_id fm('`^contractor:(\d+)`i'$r['uk'])){
                    
$lnk Contractor::url(['type'=>'contractor''id'=>$org_id]);
                    
$a "<a href=\""."https://".C(".homeflock")."$lnk\">$r[name]</a>";
                }elseif(!
RDSite::siteID() && "prefix_listing" != $mode && $org_id fm('`^org:(\d+)`i'$r['uk'])){
                    
$lnk Org::profileUrl($org_id$r['name']);
                    
$a "<a href=\""."https://".C(".business")."$lnk\">$r[name]</a>";
                }else{
                    
$a "<span class=\"bus\">$r[name]</span>";
                }

            }else{
                
$a "<span class=\"bus\">$r[name]</span>";
            }

        } else {
            if (
"prefix_listing" == $mode) {
                
$a $r['name'];
            } elseif (
"phone_profile" == $mode || "light_report" == $mode ) {
                list(
$fname_id$lname_id) = HB::explode("-"substr($r['uk'], 0strpos($r['uk'], ':')),2);
                
//30 Mar 2016 decided to lad to /people at phoneownre.com
                
$a = (!empty($r['no-pfl']) || empty($fname_id) || empty($lname_id))?($r['name']??""):'<a rel="nofollow" href="'.Phone::phonePeopleUrl($phoneHB::fl($fname_id,$lname_id), ($r['loc']??[])).'">' . ($r["name"]??"") .'</a>';
            } else {
                list(
$fname_id$lname_id) = HB::explode("-"substr($r['uk'], 0strpos($r['uk'], ':')),2);
                if (
$r['loc']['region'] ?? 0) {
                    if (
$r['loc']['city_main'] ?? 0) {
                        
$uk_loc $r['loc']['region']."-".$r['loc']['city_main'];
                    } else {
                        
$uk_loc $r['loc']['region'];
                    }
                } else {
                    
$uk_loc '-';
                }
                
$uk substr($r['uk'], 0strpos($r['uk'], ':')).":".$uk_loc.cs(":%d",$age);
                if(
RDSite::siteID() == RDSite::SITE_ID_CENTEDA_COM || RDSite::siteID() == RDSite::SITE_ID_PERSONTRUST_COM){
                    
$_url  '';
                    if(
$fname_id && $lname_id){
                        
$names Profile_Name::names([$fname_id,$lname_id]);
                        
$_url url("@search",['fname'=>$names[$fname_id], 'lname'=>$names[$lname_id]]);
                    }
                    
$a = (!empty($r['no-pfl']) || empty($fname_id) || empty($lname_id))?($r['name']??""):'<a rel="nofollow" href="'.$_url."\">" . ($r["name"]??"") . "</a>";
                }elseif(
RDSite::siteID() == RDSite::SITE_ID_REHOLD){
                    
//for checking profile in mantra on veripages
                    
include_once("/rd/vhosts/rdc/veripages.com/lib/Name/Directory.php");
                    include_once(
"/rd/vhosts/rdc/veripages.com/lib/Helper/Sitemap.php");
                    if(
$r['name']) {
                        list(
$fname,$mname$lname) =  Profile_Name::get_name($r['name']);
                        
$showUrlLink false;
                        if(!empty(
$fname_id) && !empty($lname_id)) {
                            
$fl HB::fl($fname_id$lname_id);
                            if (
\Helper_Sitemap::isCurrentSiteFL($fl\RDSite::SITE_ID_VERIPAGES_COM)) {
                                
$showUrlLink true;
                            }
                        }

                        
$a = ( $showUrlLink == false || empty($fl) || !empty($r['no-pfl']) || empty($fname_id) || empty($lname_id)) ? ($r['name'] ?? "") : '<a rel="nofollow" href="https://' C(".veripages") . "/name/$fname/$lname/\">" . ($r["name"] ?? "") . "</a>";
                    }
                }else{
                    
$a = (!empty($r['no-pfl']) || empty($fname_id) || empty($lname_id))?($r['name']??""):'<a rel="nofollow" href="https://'.C(".radaris")."/ng/search?flrc=$uk\">" . ($r["name"]??"") . "</a>";
                }
            }
        }

        if (
Debug::is_admin()) {
            if (!empty(
$r["_deleted"])) {
                if (!empty(
$r["_forbidden"])) {
                    
$a .= ' <span class="admin">forbidden: '.join(", "$r["_forbidden"]).'</span>';
                } else {
                    
$a .= ' <span class="admin">deleted</span>';
                }
            }
            
$info .= "<div class=\"admin\" style=\"font-size:10px;border:0;color:red\">$r[uk]</div>";
        }

        if (
"prefix_listing" == $mode || ($l && (!empty($l["po-box"]) || (!empty($l["street"]) && fm("!\bP\.? ?O\.? ?BOX\b(.*)!i"$l["street"]))))) {

            $st HB::locToPlainText($l);

        } elseif (
$l && (($l['street']??'') && (($l['city']??0) || ($l['city_main']??0)) && ($l['region']??0))) {
            if (
"phone_profile" == $mode || "light_report" == $mode) {
                
//30 Mar 2016 decided to lad to /people at phoneownre.com
                
if(RDSite::siteID() == RDSite::SITE_ID_CENTEDA_COM || RDSite::siteID() == RDSite::SITE_ID_PERSONTRUST_COM || RDSite::siteID() == RDSite::SITE_ID_RADARIS){
                    
$st '<span class="addr-cont">'.HB::locToPlainText($l).'</span>';
                }else{
                    if (
preg_match("!^(\d+)-(\d+):!iU"$r["uk"], $lfa)) {
                        
$st '<span class="addr-cont">'.HB::locToPlainText($l).'<a href="'.Phone::phoneAddressUrl($phoneHB::fl($lfa[1], $lfa[2]), $l).'" class="addr_link arrow" rel="nofollow">&gt;</a></span>';
                    } else {
                        
$st '<span class="addr-cont">'.HB::locToPlainText($l).'<a href="'.Phone::phoneAddressUrl($phone0$l).'" class="addr_link arrow" rel="nofollow">&gt;</a></span>';
                    }
                }
            } else {
                if (
preg_match("!^(\d+)-(\d+):!iU"$r["uk"], $lfa)) {
                    
$st HB::loc2str($lfalse$lfa[1], $lfa[2], ($isPrettyWrap HB_Legacy::LINK_TYPE_WHOLE_LINE true), false$isPrettyWrap);
                } else {
                    
$st HB::loc2str($lfalse00, ($isPrettyWrap HB_Legacy::LINK_TYPE_WHOLE_LINE true), false$isPrettyWrap);
                }
            }
        } else {
            
$st HB::locToPlainText($l);
//            $st = ucwords(strtolower($l['street']));
        
}

        return [ [
'class="names"'$a], $st$info ];

    }

    
// prefix8(details) within prefix7
    // when resolve_top_geo - resolved geo are provided instead of numbers
    
static function teaser7($prefix7$resolve_top_geo=true$only_full false) { # [prefix8 => details]
        
Profiler::in("Phone::teaser7");
        
$p substr(preg_replace("![^0-9]!"""$prefix7), 07);
        
$r i("dt-phone")->select($p, ['phone7' => $p'phones>'.($only_full?1:0)], "phone8, phones, records, urecords, top_geo1, top_geo2, top_geo3, updated, has");

        if (
$resolve_top_geo)  {
            
$rc = [];
            foreach (
$r as $row) {
                list(
$region$city) = self::_geo($row['top_geo1']);
                if (
$region && $region 256 && $city)
                    
$rc[]="$region-$city";
                list(
$region$city) = self::_geo($row['top_geo2']);
                if (
$region && $region 256 && $city)
                    
$rc[]="$region-$city";
                list(
$region$city) = self::_geo($row['top_geo3']);
                if (
$region && $region 256 && $city)
                    
$rc[]="$region-$city";
            }

            
$rc2cs $rc Geo_Go::go("cities", ["c" => join(",",$rc)]) : [];
            foreach (
$r as &$row) {
                list(
$region$city) = self::_geo($row['top_geo1']);
                
$row['top_geo1'] = $city $rc2cs["$region-$city"] : "";
                list(
$region$city) = self::_geo($row['top_geo2']);
                
$row['top_geo2'] = $city $rc2cs["$region-$city"] : "";
                list(
$region$city) = self::_geo($row['top_geo3']);
                
$row['top_geo3'] = $city $rc2cs["$region-$city"] : "";
            }
        }
        
Profiler::out();
        return 
$r;
    }



    
/**
     * Prefix8 - international phone prefix - 8+ digits (int) from "1 (234) 567-8XXXX" and from "1 (234) 567-89XXX"
     * use teaserResolveGeo call to resolve Geo
     * @param int $prefix8
     * @param bool $check_for_removed
     * @param DB_Parallel|null $DB_Parallel
     * @return array [ phone => [record1, record2, ...]]
     */
    
static function teaser8($prefix8$check_for_removed trueDB_Parallel &$DB_Parallel null) {
        
$r = [];
        
Profiler::in("Phone::teaser8");
        
$r_cnt 0;

        
$K KRDB::i("phone"substr($prefix808), $DB_Parallel);

        if (!
$DB_Parallel || $DB_Parallel->isComplete()) {
            
$teaser $K->items();
            if ((
$pl strlen($prefix8)) > 8) {
                foreach (
$teaser as $phone => $d) {
                    if (
substr($phone0$pl) == $prefix8) {
                        if (!empty(
$d["."]) && $check_for_removed) { // Check removed hashes. Only for 8+ phones to avoid heavy load.
                            
$to_check = [];
                            foreach (
$d["."] as $iid => $item) {
                                if (empty(
$item["_deleted"])) {
                                    
$tc = ["phone" => $phone];
                                    if (
$loc = ($item["loc"]??[])) {
                                        
$tc["loc"] = $loc;
                                    }
                                    if (
$uk = ($item["uk"]??"")) {
                                        
$tc["uk"] = $uk;
                                    }
                                    
$to_check[$iid] = $tc;
                                }
                            }

                            if (
$removed removal\Removal::filterRemoved($to_check$d["."], "mark")) {
                                
//Also save removals permanently
                                /* Permanent set may take a lot of time.
                                foreach ($removed as $iid => $tc) {
                                    $K[$phone][$iid]->_deleted = $d["."][$iid]["_deleted"];
                                    $K[$phone][$iid]->_forbidden = $d["."][$iid]["_forbidden"];
                                }
                                $K->save();
                                */
                            
}

                        } 
// Check removed hashes finished
                        
$r[$phone] = $d;
                    }
                }
                
$teaser $r;
            }
            
ksort($teaser);

            
$r = [];
            
$rc = [];
            foreach (
$teaser as $phone => $items_) {
                
$items = ($items_['.']??[]);
                if (
$items) {
                    foreach (
$items as $k => & $i) {
                        if (!empty(
$i['email'])) {
                            
$i['email'] = preg_replace("!^(.)(.+)\!(.+)!"'$1...@$3'$i['email']);
                        }
                        if (!empty(
$i['_deleted']) && !\Debug::is_admin()) {
                            unset(
$items[$k]);
                        }
                    }
                    if (
$items) {
                        
$r[$phone] = $items;
                    }
                }
            }
            
Profiler::out(['phones' => count($teaser)]);
        } else {
            
Profiler::out();
        }


        return 
$r;
    }

    static function 
_geo($geo) { # [$r, $c]
        
return [$geo >> 16$geo 65535];
    }

    static function 
_geoName($geo) { # name
        
list($r,$c) = self::_geo($geo);
        return 
Geo::name($r$c);
    }

    
// to use used on teaser8() data
    // add city_state fields to "$phone/$nn/loc/city_state"
    
static function teaserResolveGeo(array $teaser) { # ['phone' => [ r1, r2, r3 ]]
        
$RC = []; // [r-c => 1]
        
foreach ($teaser as $phone => $items) {
            foreach (
$items as $item) {
                
$loc $item['loc']??0;
                if (
$loc && ($loc['city']??0) && ($loc['region']??0)) {
                    
$RC[$loc['region'] . "-" $loc['city']] = 1;
                }
            }
        }
        if (! 
$RC)
            return 
$teaser;

        
Profiler::in_off("- resolve geo");
        
$rc2cs Geo_Go::go("cities", ["c" => join(","array_keys($RC))]);

        foreach (
$teaser as &$items) {
            foreach (
$items as &$item) {
                
$loc $item['loc']??0;
                if (
$loc && ($loc['city']??0) && ($loc['region']??0)) {
                    
$rc $loc['region'] . "-" $loc['city'];
                    
$item['loc']['city_state'] = $rc2cs[$rc];
                }
            }
            unset(
$item);
        }
        unset(
$items);
        
Profiler::out(['phones' => count($teaser), 'cities' => count($RC)]);
        return 
$teaser;
    }



    
/**
     * Build link to phone lookup report page
     * @param string $phone
     * @param mixed $absolute. if true - use C(".phones") domain, if string - use passed $absolute as domain
     * @return string
     */
    
static function reversePhoneLookupReportUrl($phone$absolute true) {
        
$phone Phone::clean($phone);
        if (
strlen($phone) > 11) {
            
$phone substr($phone011);
        }
        
$url "/report/reverse_phone_lookup?phone=$phone#findPhoneH";
        if (
$absolute) {
            if (
$absolute === true) {
                switch (
RDSite::siteID()) {
                    case 
RDSite::SITE_ID_PHONEID_US:
                        
$domain C(".phoneidus");
                        break;
                    case 
RDSite::SITE_ID_PHONEOWNER_US:
                        
$domain C(".phoneownerus");
                        break;
                    case 
RDSite::SITE_ID_PHONEOWNER_COM:
                        
$domain C(".phones");
                        break;
                    default:
                        
$phone_site_id Phone::codeToSite($phone);
                        if (
RDSite::SITE_ID_PHONEOWNER_US == $phone_site_id) {
                            
$domain C(".phoneownerus");
                        } elseif (
RDSite::SITE_ID_PHONEID_US == $phone_site_id) {
                            
$domain C(".phoneidus");
                        } else {
                            
$domain C(".phones");
                        }
                        break;
                }
            } else {
                
$domain $absolute;
            }
            
$url "https://".$domain.$url;
        }
        return 
$url;
    }

    
/**
     * Builds phone profile page link
     * @param string $phone
     * @param bool $absolute
     * @return string
     */
    
static function phonePrefixUrl($phone$absolute true) {
        
$ph self::clean($phone);
        if (
$ph[0] == "1") {
            
$ph substr($ph1);
        }

        
$url "/$ph";

        
$phone_site_id Phone::codeToSite($phone);
        if (
RDSite::siteID() == RDSite::SITE_ID_PHONEOWNER_US || RDSite::siteID() == RDSite::SITE_ID_PHONEID_US) {
            if (
RDSite::siteID() != $phone_site_id) {
                
$url "/ng$url";
            }
        }

        if (
$absolute) {
            if (
$absolute === true) {
                switch (
RDSite::siteID()) {
                    case 
RDSite::SITE_ID_PHONEID_US:
                        
$domain C(".phoneidus");
                        break;
                    case 
RDSite::SITE_ID_PHONEOWNER_US:
                        
$domain C(".phoneownerus");
                        break;
                    case 
RDSite::SITE_ID_PHONEOWNER_COM:
                        
$domain C(".phones");
                        break;
                    default:
                        
$phone_site_id Phone::codeToSite($phone);
                        if (
RDSite::SITE_ID_PHONEOWNER_US == $phone_site_id) {
                            
$domain C(".phoneownerus");
                        } elseif (
RDSite::SITE_ID_PHONEID_US == $phone_site_id) {
                            
$domain C(".phoneidus");
                        } else {
                            
$domain C(".phones");
                        }
                        break;
                }
            } else {
                
$domain $absolute;
            }
            
$url "https://".$domain.$url;
        }
        return 
$url;
    }


    
/**
     * Builds link to prefix page but with # fraction to jump to phone in the listing
     * @param string $phone
     * @param bool $absolute
     * @return string
     */
    
static function phoneAtPrefixPageUrl($phone$absolute true) {
        
$ph self::clean($phone);
        if (
$ph[0] == "1") {
            
$ph substr($ph1);
        }
        if (
10 != strlen($ph)) {
            
$url self::phonePrefixUrl($ph$absolute);
        } else {
            
$prefix substr($ph08);
            
$fraction substr($ph82);
            
$url self::phonePrefixUrl($prefix$absolute);
            
$url $url "#p$fraction";
        }
        return 
$url;
    }


    
/**
     * Builds phone profile page link
     * @param string $phone
     * @param bool $absolute
     * @return string
     */
    
static function phoneProfileUrl($phone$absolute true) {
        
$ph self::clean($phone);
        if (
strlen($ph) == 11 && $ph[0] == "1") {
            
$ph substr($ph1);
        }

        
$url "/phone/$ph";
        if (
$absolute) {
            if (
$absolute === true) {
                switch (
RDSite::siteID()) {
                    case 
RDSite::SITE_ID_PHONEID_US:
                        
$domain C(".phoneidus");
                        break;
                    case 
RDSite::SITE_ID_PHONEOWNER_US:
                        
$domain C(".phoneownerus");
                        break;
                    case 
RDSite::SITE_ID_PHONEOWNER_COM:
                        
$domain C(".phones");
                        break;
                    default:
                        
$phone_site_id Phone::codeToSite($phone);
                        if (
RDSite::SITE_ID_PHONEOWNER_US == $phone_site_id) {
                            
$domain C(".phoneownerus");
                        } elseif (
RDSite::SITE_ID_PHONEID_US == $phone_site_id) {
                            
$domain C(".phoneidus");
                        } else {
                            
$domain C(".phones");
                        }
                        break;
                }
            } else {
                
$domain $absolute;
            }
            
$url "https://".$domain.$url;
        }
        return 
$url;
    }

    
/**
     * Builds link to people page
     * @param string $phone
     * @param int $fl
     * @param array $loc SH_Loc
     * @param bool $absolute
     * @return type
     */
    
static function phonePeopleUrl($phone$fl$loc$absolute false) {
        
$ph self::clean($phone);
        if (
strlen($ph) == 11 && $ph[0] == "1") {
            
$ph substr($ph1);
        }
        
$fl = (int)$fl;
//        $loc = Geo::parseLoc($loc);
        
$a = !empty($loc["region"])?HB::locToPlainText($loc):"";

        
$p = ["p" => $ph"fl"=>$fl];
        if (
$a) {
            
$p["a"]=$a;
        }
        
$site_id RDSite::siteID();
        if(
$fl && ($site_id  == RDSite::SITE_ID_RADARIS  || $site_id == RDSite::SITE_ID_CENTEDA_COM  || $site_id == RDSite::SITE_ID_PERSONTRUST_COM) ){
            list(
$fid,$lid) = HB::fl($fl);
            
            if(
$fid && $lid){
                
$names Profile_Name::names([$fid,$lid]);
                if(
$site_id == RDSite::SITE_ID_CENTEDA_COM || $site_id == RDSite::SITE_ID_PERSONTRUST_COM){
                    
$url url("@search",['fname'=>$names[$fid], 'lname'=>$names[$lid]]);
                }else{
                    
$url "/p/$names[$fid]/$names[$lid]/";
                }
            }else{
                
$url '#';
            }
        }else{
            
$url url("/ng/people"$p);
        }

        return 
$url;
    }

    
/**
     * Builds link to address page
     * @param string $phone
     * @param int $fl
     * @param array $loc SH_Loc
     * @param bool $absolute
     * @return type
     */
    
static function phoneAddressUrl($phone$fl$loc$absolute false) {
        
$ph self::clean($phone);
        if (
strlen($ph) == 11 && $ph[0] == "1") {
            
$ph substr($ph1);
        }
        
$fl = (int)$fl;

//        $loc = Geo::parseLoc($loc);
        
$a = !empty($loc["region"])?HB::locToPlainText($loc):"";
        
$p = ["p" => $ph"a"=>$a];
        if (
$fl) {
            
$p["fl"] = $fl;
        }
        
$url url("/ng/address"$p);
        return 
$url;
    }

    
/**
     * Returns site ID for given phone/prefix
     * @param string $phone
     * @return int site ID
     */
    
static function codeToSite($phone) {
        
$site_id RDSite::SITE_ID_PHONEOWNER_US//default site for unmapped 3-digit prefixes

        
$prefix3 self::clean($phone);
        if (
$prefix3) {
            
$prefix3 substr($prefix301)=='1' substr($prefix313) : substr($prefix303);
        }

        foreach ([
RDSite::SITE_ID_PHONEOWNER_USRDSite::SITE_ID_PHONEID_US] as $sid) {
            if (
in_array($prefix3self::CODE_TO_SITE_MAP[$sid])) {
                
$site_id $sid;
                break;
            }
        }

        return 
$site_id;
    }


    
/**
     * Get list of phones interlinks.
     * @param int $phone_for
     * @param bool $check_for_removed
     * @return array
     * @throws Exception
     */
    
static function getInterlinks($phone_for$check_for_removed true) {
        
$phone Phone::clean($phone_for);
        if (
strlen($phone) != 11) {
            throw new 
Exception("Invalid phone number");
        }
        
$prefix substr($phone08);
        
$P KRDB::i("phone"$prefix);
        
$interlinks = ($P->interlinks[$phone]??0);
        if (
$interlinks && $check_for_removed) {
            
$to_check = [];
            foreach (
$interlinks as $phone => $interlink) {

                
$tc = ["phone" => $phone];
                if (
$loc $interlink["loc"]) {
                    
$tc["loc"] = $loc;
                }
                
                if (
$uk $interlink["uk"]) {
                    
$tc["uk"] = $uk;
                }
                
                
$to_check[$phone] = $tc;
            }

            if (
$removed removal\Removal::filterRemoved($to_check$interlinks"unset")) {
                
/* KRDB update may take a lot of time
                self::setInterlinks($phone_for, $interlinks, true);
                */
            
}

        }
        return 
$interlinks;
    }

    
/**
     * 
     * @param int $phone_for
     * @param array $intelinks
     * @param bool $save_immediately
     * @return array
     * @throws Exception
     */
    
static function setInterlinks($phone_for$intelinks$save_immediately=true) {
        
$phone Phone::clean($phone_for);
        if (
strlen($phone) != 11) {
            throw new 
Exception("Invalid phone number");
        }
        
$prefix substr($phone08);
        
$P KRDB::i("phone"$prefix);

        
$orig_intelinks $P->interlinks;
        
$orig_intelinks[$phone_for] = $intelinks;
        
$P->interlinks $orig_intelinks;

        if (
$save_immediately) {
            
$P->save(true);
        }

        return (
$P->interlinks[$phone]??0);
    }

}