claudsonm/cep-promise-php
Resolve Brazilian postal codes (CEP) asynchronously with a promise-based API, querying multiple providers and returning normalized address data. Ideal for Laravel and PHP apps that need reliable, fallback-ready CEP lookups.
CepPromise is a Brazilian CEP (postal code) lookup library. Install via Composer:
composer require cep-promise/cep-promise
Requirements as of v1.2.0: PHP 7.4–8.1 and Guzzle 7.2.0+. If your project is still on PHP 7.3 or Guzzle 6.x, you must upgrade before installing.
The first thing to look at is the CepPromise::fetch entry point and its return object (CepPromiseResponse), since everything flows through it. A basic lookup:
use CepPromise\CepPromise;
$result = CepPromise::fetch('01310-100');
echo $result->getCity(); // São Paulo
echo $result->getState(); // SP
New in v1.2.0 — restrict which providers run by passing an optional second argument with provider class names:
use CepPromise\CepPromise;
use CepPromise\Providers\ViaCep;
use CepPromise\Providers\Correios;
$result = CepPromise::fetch('01310-100', [ViaCep::class, Correios::class]);
That's the first use case to try: a simple fetch call, then constrain providers once you know which ones your network/environment can reach.
Wrap lookups in a service class. Don't call CepPromise::fetch directly from controllers. Build a small CepService that handles exceptions, caching, and provider selection:
class CepService
{
public function lookup(string $cep): ?CepPromiseResponse
{
try {
return Cache::remember("cep:{$cep}", 86400, fn () => CepPromise::fetch($cep));
} catch (CepPromiseException $e) {
Log::warning('CEP lookup failed', ['cep' => $cep, 'errors' => $e->toArray()]);
return null;
}
}
}
Provider pinning for reliability. The new second parameter of fetch is the cleanest way to enforce an allow-list (e.g. only providers that honor your rate limits or your company's outbound firewall). Keep the list in config/cep.php and inject it into your service so you can tune it per environment without touching code.
Aggregate error inspection. Because the library tries multiple providers, a single failure isn't representative. After a caught CepPromiseException, use toArray() to see which providers failed. The v1.2.0 fix ensures this method no longer blows up when an error entry isn't a CepPromiseProviderException (previously a hard toArray() crash), so it's now safe to log all errors uniformly.
Caching and throttling. Real-world usage hits provider rate limits fast. Cache results (CEPs don't change) and add a retry/backoff layer around fetch.
Testing. v1.2.0 mocks all HTTP calls and reaches 100% coverage internally — mirror that in your app by faking Guzzle's handler for your CepService tests rather than hitting real endpoints.
composer.json constraints before bumping; on Laravel 6/7 with old Guzzle you'll be blocked. This is the biggest upgrade blocker.toArray() is now safe, but only since 1.2.0. If you rely on it in logging and pin an older version, guard against the case where $errors contains non-CepPromiseProviderException entries — it will throw.fetch parameter is optional. Omitting it keeps the default provider order. Passing [] (empty array) is a foot-gun — validate that the list isn't empty before calling.fetch is convenient but not mockable; wrap it for testability.CepPromiseException, dump toArray(), and cross-reference the failing provider class names against the ones you passed in — the new fixed behavior makes this a one-liner.How can I help you explore Laravel packages today?