Skip to main content

Finding Phone Numbers in Text

The PhoneNumberMatcher class finds and extracts phone numbers from text strings. This is useful for processing user-generated content, parsing documents, or extracting contact information.

Getting Started

1

Get PhoneNumberUtil Instance

$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
2

Create PhoneNumberMatcher

$text = "Hi, can you ring me at 1430 on 0117 496 0123. Thanks!";
$matcher = $phoneUtil->findNumbers($text, 'GB');
3

Iterate Through Matches

foreach ($matcher as $match) {
    $number = $match->number();
    $rawText = $match->rawString();
    
    echo "Found: $rawText\n";
    // Found: 0117 496 0123
}

Basic Usage

use libphonenumber\PhoneNumberUtil;

$phoneUtil = PhoneNumberUtil::getInstance();

$text = "Hi, can you ring me at 1430 on 0117 496 0123. Thanks!";

// Find all numbers in the text for GB region
$matcher = $phoneUtil->findNumbers($text, 'GB');

foreach ($matcher as $phoneNumberMatch) {
    $number = $phoneNumberMatch->number();
    
    // Display formatted number
    echo $phoneUtil->format(
        $number,
        \libphonenumber\PhoneNumberFormat::INTERNATIONAL
    );
    // Output: +44 117 496 0123
    
    // Get the raw text that was matched
    echo $phoneNumberMatch->rawString();
    // Output: 0117 496 0123
    
    // Get the position in the text
    echo "Found at position: " . $phoneNumberMatch->start();
    // Output: Found at position: 30
}

Understanding Leniency Levels

The Leniency parameter controls how strict the matching should be:
Most permissive - Matches anything that could possibly be a phone number:
use libphonenumber\Leniency\Possible;

$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();

$text = "Call 1234567890 or 123-456-7890";
$matcher = $phoneUtil->findNumbers(
    $text,
    'US',
    Possible::getInstance(),
    PHP_INT_MAX
);

// Will match both numbers even without proper formatting
foreach ($matcher as $match) {
    echo $match->rawString() . "\n";
}
Use when you want to catch all potential phone numbers, even if they might be false positives.

PhoneNumberMatch Object

Each match provides several useful methods:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$text = "Contact: +44 117 496 0123 (office)";
$matcher = $phoneUtil->findNumbers($text, 'GB');

foreach ($matcher as $match) {
    // Get the PhoneNumber object
    $number = $match->number();
    echo $phoneUtil->format(
        $number,
        \libphonenumber\PhoneNumberFormat::E164
    );
    // Output: +441174960123
    
    // Get the raw matched text
    echo $match->rawString();
    // Output: +44 117 496 0123
    
    // Get start position (0-indexed)
    echo $match->start();
    // Output: 9
    
    // Get end position
    echo $match->end();
    // Output: 26
    
    // Extract from original text
    $extracted = substr($text, $match->start(), $match->end() - $match->start());
    echo $extracted;
    // Output: +44 117 496 0123
}

Practical Examples

function extractContactInfo(string $emailBody, string $defaultRegion): array
{
    $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
    $matcher = $phoneUtil->findNumbers($emailBody, $defaultRegion);
    
    $contacts = [];
    
    foreach ($matcher as $match) {
        $number = $match->number();
        
        if ($phoneUtil->isValidNumber($number)) {
            $contacts[] = [
                'original' => $match->rawString(),
                'e164' => $phoneUtil->format(
                    $number,
                    \libphonenumber\PhoneNumberFormat::E164
                ),
                'international' => $phoneUtil->format(
                    $number,
                    \libphonenumber\PhoneNumberFormat::INTERNATIONAL
                ),
                'position' => $match->start(),
                'region' => $phoneUtil->getRegionCodeForNumber($number)
            ];
        }
    }
    
    return $contacts;
}

// Usage
$email = "Please call me at +1 (650) 253-0000 or +44 117 496 0123.";
$contacts = extractContactInfo($email, 'US');

foreach ($contacts as $contact) {
    echo "Found: {$contact['international']} from {$contact['region']}\n";
}

Controlling Max Tries

Limit the number of attempts to find numbers:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();

// Find at most 5 numbers
$matcher = $phoneUtil->findNumbers(
    $text,
    'US',
    \libphonenumber\Leniency\Valid::getInstance(),
    5  // maxTries
);

foreach ($matcher as $match) {
    // Process up to 5 matches
}
Set maxTries to prevent excessive processing on very long documents.

Handling Ambiguous Cases

Some text patterns may be ambiguous:
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();

// Could be a phone number or a time
$text = "Meet at 1430 or call 0117 496 0123";

$matcher = $phoneUtil->findNumbers($text, 'GB');

foreach ($matcher as $match) {
    $raw = $match->rawString();
    
    // "1430" might be matched as a phone number
    // Validate the match
    $number = $match->number();
    if ($phoneUtil->isValidNumber($number)) {
        echo "Valid: $raw\n";
    } else {
        echo "Possibly not a phone number: $raw\n";
    }
}
Always validate matched numbers, especially with more lenient matching modes.

Performance Considerations

// For very large documents, process in chunks
function extractPhoneNumbers(
    string $largeText,
    string $region,
    int $chunkSize = 10000
): array {
    $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
    $allNumbers = [];
    $chunks = str_split($largeText, $chunkSize);
    
    foreach ($chunks as $chunk) {
        $matcher = $phoneUtil->findNumbers($chunk, $region);
        
        foreach ($matcher as $match) {
            $number = $match->number();
            if ($phoneUtil->isValidNumber($number)) {
                $e164 = $phoneUtil->format(
                    $number,
                    \libphonenumber\PhoneNumberFormat::E164
                );
                $allNumbers[$e164] = true; // Use as set
            }
        }
    }
    
    return array_keys($allNumbers);
}
// Reuse the singleton instance
class PhoneNumberService
{
    private static ?PhoneNumberUtil $phoneUtil = null;
    
    public static function findNumbers(
        string $text,
        string $region
    ): iterable {
        if (self::$phoneUtil === null) {
            self::$phoneUtil = PhoneNumberUtil::getInstance();
        }
        
        return self::$phoneUtil->findNumbers($text, $region);
    }
}

Best Practices

Use Appropriate Leniency

Start with VALID leniency and adjust based on your false positive/negative tolerance

Always Validate

Even with strict leniency, validate matched numbers before using them

Set Max Tries

Limit matching attempts on large documents to prevent performance issues

Handle Duplicates

Use E164 format to identify and deduplicate matched numbers

Common Use Cases

Contact Extraction

Extract phone numbers from emails, documents, and web pages

Click-to-Call

Automatically linkify phone numbers in web content

Data Mining

Extract contact information from large document collections

Content Analysis

Analyze distribution of phone numbers by region or type

Next Steps

Parsing Numbers

Learn more about parsing phone numbers

Validating Numbers

Validate extracted phone numbers

PhoneNumberMatcher API

Complete API reference for phone number matching

Leniency Levels

Detailed explanation of leniency options

Build docs developers (and LLMs) love