Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Myanmar Phone Number Php Laravel Package

cyberwings/myanmar-phone-number-php

A PHP package for working with Myanmar phone numbers: validate, normalize, format, and parse local and international numbers. Helps Laravel and PHP apps handle user input, clean stored values, and display consistent Myanmar mobile numbers.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a direct PHP port of mm_phonenumber and gives you four things for Myanmar mobile numbers: validation, operator lookup, length/sanitization normalization, and network type (2G/3G/4G/5G) lookup. It has no dependencies beyond PHP, so it drops into anything from Laravel to a legacy script.

Install:

composer require cyberwings/myanmar-phone-number-php

If Laravel package discovery is wired up, the service provider and facade register automatically; otherwise add them manually to config/app.php:

'providers' => [
    CyberWings\MyanmarPhoneNumber\MyanmarPhoneNumberServiceProvider::class,
],
'aliases' => [
    'MyanmarPhone' => CyberWings\MyanmarPhoneNumber\Facades\MyanmarPhoneNumber::class,
],

First use case — normalizing + validating an input field:

use CyberWings\MyanmarPhoneNumber\MyanmarPhoneNumber;

$raw = ' +95 9 771 234 567 ';

if (! MyanmarPhoneNumber::isValid($raw)) {
    // reject
}

$canonical = MyanmarPhoneNumber::sanitize($raw);       // e.g. "09771234567"
$operator  = MyanmarPhoneNumber::getOperatorName($raw); // e.g. "Telenor"
$network   = MyanmarPhoneNumber::getNetworkType($raw);  // e.g. "4G"

Where to look first:

  1. README.md on the repo — the method names mirror the JS library (isValidMMPhoneNumber, getOperatorName, sanitize, getNetworkType), so read it to confirm the exact PHP signatures before you build abstractions on top.
  2. src/ — the operator/prefix map is almost certainly a static array; that's the file you'll edit when you need to fix or extend coverage.
  3. tests/ — grab test fixtures from here; they are the fastest way to see which prefix formats the package actually accepts.

Most common first integration: validate the phone number on a registration or login form, store the sanitized 09xxxxxxxxx version in the DB, and use the operator name to route SMS through the cheapest gateway.

Implementation Patterns

1. Wrap it in a custom validation rule (don't inline isValid in controllers)

// app/Rules/MyanmarPhone.php
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use CyberWings\MyanmarPhoneNumber\MyanmarPhoneNumber;

class MyanmarPhone implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (! is_string($value) || ! MyanmarPhoneNumber::isValid($value)) {
            $fail('The :attribute must be a valid Myanmar mobile number.');
        }
    }
}

Then 'phone' => ['required', new MyanmarPhone] and you get consistent error messages across the app.

2. Normalize on write with a model mutator

Always store the canonical form. Raw input from Myanmar users arrives in half a dozen shapes:

  • 09 771 234 567
  • +959771234567
  • 959771234567
  • 09771234567
  • +95 (0)9 771 234 567
public function setPhoneAttribute($value): void
{
    $this->attributes['phone'] = MyanmarPhoneNumber::sanitize($value);
}

Do the sanitize on the model, not the request, so imports, API endpoints, and artisan commands all get the same normalization for free.

3. Operator-based SMS routing

This is the main business reason to pull in a package like this — Myanmar operators have different gateway coverage and rates.

class SmsRouter
{
    public function gatewayFor(string $phone): SmsGateway
    {
        return match (MyanmarPhoneNumber::getOperatorName($phone)) {
            'MPT'      => app(MptGateway::class),
            'Telenor'  => app(TelenorGateway::class),
            'Ooredoo'  => app(OoredooGateway::class),
            'Mytel'    => app(MytelGateway::class),
            default    => app(FallbackGateway::class),
        };
    }
}

Wrap this in an interface (SmsRouterContract) and bind it in a service provider — the operator list will change over time and you don't want to edit controllers every time.

4. Bulk cleanup job for imported CSVs

Because the package is dependency-free and fast, it's a good fit for chunked processing of customer lists:

Customer::whereNull('phone_normalized')
    ->chunkById(500, function ($customers) {
        foreach ($customers as $customer) {
            if (MyanmarPhoneNumber::isValid($customer->phone_raw)) {
                $customer->update([
                    'phone_normalized'  => MyanmarPhoneNumber::sanitize($customer->phone_raw),
                    'phone_operator'    => MyanmarPhoneNumber::getOperatorName($customer->phone_raw),
                ]);
            }
        }
    });

Persist sanitize() output and getOperatorName() as columns — recomputing on every request is wasteful and makes segmenting users by operator trivial.

5. Pre-flight check before SMS dispatch

Before you burn gateway credits, check the network type. A 2G-only number might not be a good candidate for MMS/rich content:

if (MyanmarPhoneNumber::getNetworkType($phone) === '2G') {
    $this->sendPlainSms($phone);
} else {
    $this->sendRichMessage($phone);
}

Treat the return value defensively — if the prefix is unknown to the package, you'll likely get null or an empty string, so always have a default branch.

6. Thin service wrapper for testability

Wrap the static facade in your own injectable class so tests can mock it without touching the static state:

class PhoneInspector
{
    public function isValid(string $raw): bool
    {
        return MyanmarPhoneNumber::isValid($raw);
    }
    public function operator(string $raw): ?string
    {
        return MyanmarPhoneNumber::getOperatorName($raw) ?: null;
    }
}

Then $this->mock(PhoneInspector::class) in feature tests.

Gotchas and Tips

The package is a thin data-driven port — coverage is only as good as the prefix table. Myanmar's numbering plan changes (Mytel launched in 2018, Telenor's assets changed hands, new blocks get allocated). If isValid() rejects something a real user just received from the operator, open the operator map file and add the prefix. Don't file a bug and wait — the maintainer activity on this package is essentially zero.

Only mobile numbers, not landlines. 01-xxxxxxx Yangon numbers and similar fixed-line formats are not what this validates. If your app accepts both, gate the landline path yourself:

$isLandline = str_starts_with($normalized, '01');

Sanitize BEFORE validating. Users pasting from Google Contacts or WhatsApp send +95 (0)9 771 234 567 with spaces and parentheses. Depending on the implementation order, isValid() may reject strings sanitize() would have cleaned up. Best practice:

$normalized = MyanmarPhoneNumber::sanitize($raw);
if (MyanmarPhoneNumber::isValid($normalized)) { ... }

Watch out for the 0 after +95. International Myanmar format is +95 9xxxxxxxx, not +95 09xxxxxxxx. Some users (and some old phone exports) include the extra 0. Check what sanitize() returns for both inputs and make sure they collapse to the same value — if they don't, add your own pre-strip:

$normalized = preg_replace('/^\+?95\s*0?/', '+95', $raw);

Operator names are strings, not enums. Match case-insensitively, and always have a default/fallback. Don't switch on 'Telenor' if you can't guarantee trailing whitespace isn't being sent back — normalize with trim() and strcasecmp() in your router.

Don't branch critical logic on getNetworkType() alone. If the package doesn't recognize a prefix, it will likely return null. Guard it:

$network = MyanmarPhoneNumber::getNetworkType($phone) ?? 'unknown';

and don't let an unknown number fail silently through a match without a default arm.

Testing tip: don't hit the network. The package is pure static logic. Grab a handful of fixtures from the repo's tests/ folder (MPT, Telenor, Ooredoo, Mytel, invalid) and pin them as data providers in your own test suite. This insulates you from the package's fixtures ever drifting.

Cache the sanitized/operator values on the record. Operators and prefixes do change, but so rarely that storing phone_operator at registration time and only recomputing on a schedule (weekly job) gives you the best of both worlds — fast reads and fresh data.

Facade aliasing quirk. If package discovery is not configured (or you're on an older Laravel), the MyanmarPhone facade won't be registered and you'll get a "target class does not exist" error. Bind manually to the singleton in AppServiceProvider::register():

$this->app->singleton('myanmar-phone', fn () => new \CyberWings\MyanmarPhoneNumber\MyanmarPhoneNumber());

Extension point: if you need to support international numbers or landlines alongside mobile, subclass the package's main class and override the operator table rather than forking the repo — you'll get upstream updates (if any ever come) without merge pain.

When to reach for something else: this library is fine for validation and operator classification, but it is not a lookup service — it doesn't do HLR, E.164 canonicalization beyond Myanmar, or portability checks. If you need those, layer libphonenumber-for-php on top or replace this package entirely for international traffic. Use this one specifically when Myanmar-only + zero-dependency is the requirement.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel