malico/mobile-cm-php
A PHP client for the MobileCM API that lets you manage mobile content and campaigns from your application. Provides simple, framework-agnostic methods for authentication and API requests, making it easy to integrate MobileCM services into Laravel or any PHP project.
This is a plain PHP library, not a Laravel package — there is no service provider, no facade, no config file to publish. Plan for a few minutes of manual wiring.
composer require malico/mobile-cm-php then open vendor/malico/mobile-cm-php/src/ and the README before writing any code. The package is small (single-purpose prefix lookup), so reading the whole source takes a minute and tells you the real class name, method signature, and return shape (string network name vs. enum vs. object). Treat the README as possibly stale and the source as the source of truth.--ignore-platform-reqs workaround the earlier guidance suggested is far less likely to be needed. Still check composer.json for the actual php and illuminate/* constraints: "tested for php8 and php7" is a release-note claim, not a guarantee your 8.3/8.4 runtime is covered. Verify it installs cleanly on your interpreter before designing around it.Mobile::getNetwork('6XXXXXXXX') (confirm the exact call from source) run against a handful of known MTN, Orange, Nexttel and Camtel numbers. Write down the return value for unknown/invalid input — null, false, an empty string, or an exception changes everything downstream.network column, so you can route SMS/OTP traffic or segment customers. Wrap the call in your own small service class rather than scattering the vendor class through controllers — you want one seam to swap it out.Wrap it, don't call it directly. Bind a thin adapter in a service provider so the vendor class appears in exactly one file:
// app/Services/NetworkResolver.php
class NetworkResolver
{
public function for(string $phone): ?string
{
return Mobile::getNetwork($this->normalize($phone)); // verify method name
}
}
This is your insurance policy: the library is small and thinly maintained, and swapping to a hand-rolled prefix table later becomes a one-file change.
Normalise before lookup. Strip +237, leading 00237, spaces, dashes and parentheses first. The library almost certainly expects a bare local number (9 digits starting 6), and throwing +237 6XX XX XX XX at it will silently return "unknown" rather than erroring — the worst failure mode.
Model it as a stored attribute. Resolve once at write time, not on every read:
protected static function booted(): void
{
static::saving(function ($user) {
if ($user->isDirty('phone')) {
$user->phone_network = app(NetworkResolver::class)->for($user->phone);
}
});
}
Backfill existing rows with a queued command and chunkById, since the lookup is per-row and cheap but you don't want it in a migration.
Validation rule. A Rule object that fails on null network is a clean way to reject invalid numbers at registration, and gives you one place to add an allowlist if you only support certain carriers:
Validator::extend('cm_mobile', fn ($attr, $value) => app(NetworkResolver::class)->for($value) !== null);
Cache the mapping. The result is a pure function of the prefix, so cache per-prefix (Cache::remember("cm_net:{$prefix}", 86400, ...)) if you resolve in bulk. Per-phone caching is wasteful — key on the first 2–3 digits.
Config-driven allowlist. Put your supported networks in config/telecom.php and let callers ask "is this a supported network?" instead of comparing magic strings throughout the codebase. This is also where you put your prefix table if you end up replacing the package.
Integration tips. Use it inside an onboarding/OTP job to pick the SMS gateway credential set per carrier. If you're doing CSV imports, resolve in a LazyCollection pipeline so a 50k-row file doesn't balloon memory, and log unmatched numbers to a side table for manual review rather than failing the import.
null for both "invalid format" and "unrecognised prefix", you can't distinguish user error from a stale table — log the raw input alongside the result so debugging is possible.Mobile:: resolves out of the box; either use the fully-qualified class or alias it. If the class is static-only with a private constructor, you cannot mock it in tests — hence the adapter.+237 variants, plus garbage) in a data provider. This is your regression suite, and it's also exactly what you'd reuse if you rewrite the lookup yourself — run it once more after the 1.3.1 bump to confirm nothing shifted.composer install will erase. If you need new ranges, copy the table into your own config now rather than patching vendor/. Constrain to ^1.3 in composer.json so an untested 1.4 bump can't silently change the lookup.How can I help you explore Laravel packages today?