diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 14:58:55 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 14:58:55 -0300 |
| commit | 64764fb9fac0227f8e0443e7c7519d73aec275b9 (patch) | |
| tree | cb4fc56eeb6ecce53854f9c530609cf01ab3349f /src/Address.php | |
| download | php-64764fb9fac0227f8e0443e7c7519d73aec275b9.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'src/Address.php')
| -rw-r--r-- | src/Address.php | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/Address.php b/src/Address.php new file mode 100644 index 0000000..8b970aa --- /dev/null +++ b/src/Address.php @@ -0,0 +1,133 @@ +<?php + +declare(strict_types=1); + +namespace Cepis; + +/** + * A CEP and everything the database knows about it. + * + * **Property names are the API's own JSON keys, in Portuguese** — + * `logradouro`, `bairro`, `localidade`. Everything else in this library is + * English; the data fields are the one deliberate exception, so that what you + * read in the JSON at https://cep.is/01001000 is exactly what you read on the + * object, with no translation layer and no field-by-field mapping to keep in + * your head. The values are Portuguese either way — they come from the + * Correios database. + * + * Fields the database does not have for that CEP come back as empty strings, + * never `null` — that is what the API returns. + * + * {@see Address::$geo} is the exception: it is `null` when there is no + * coordinate. There the difference is real, because the API **omits** the + * whole field in that case (about 43% of CEPs) rather than sending null. + * + * {@see Address::$raw} holds the response exactly as it arrived, + * uninterpreted, so a field the API grows before this library is updated is + * not lost. + */ +final class Address +{ + /** + * The JSON keys mapped onto properties, in the API's own order. + */ + public const FIELDS = [ + 'cep', 'logradouro', 'complemento', 'unidade', 'bairro', 'localidade', + 'uf', 'estado', 'regiao', 'ibge', 'gia', 'ddd', 'siafi', + ]; + + /** + * @param array<string, mixed> $raw + */ + public function __construct( + public readonly string $cep = '', + public readonly string $logradouro = '', + public readonly string $complemento = '', + public readonly string $unidade = '', + public readonly string $bairro = '', + public readonly string $localidade = '', + public readonly string $uf = '', + public readonly string $estado = '', + public readonly string $regiao = '', + public readonly string $ibge = '', + public readonly string $gia = '', + public readonly string $ddd = '', + public readonly string $siafi = '', + public readonly ?Geo $geo = null, + public readonly array $raw = [], + ) { + } + + /** + * Build from the decoded response. + * + * Unknown fields are ignored during construction but preserved in + * {@see Address::$raw}. A new field in the API therefore cannot break this + * library. + * + * @param array<string, mixed> $data + */ + public static function fromArray(array $data): self + { + $values = []; + foreach (self::FIELDS as $name) { + $value = $data[$name] ?? ''; + $values[$name] = is_scalar($value) ? (string) $value : ''; + } + + return new self( + cep: $values['cep'], + logradouro: $values['logradouro'], + complemento: $values['complemento'], + unidade: $values['unidade'], + bairro: $values['bairro'], + localidade: $values['localidade'], + uf: $values['uf'], + estado: $values['estado'], + regiao: $values['regiao'], + ibge: $values['ibge'], + gia: $values['gia'], + ddd: $values['ddd'], + siafi: $values['siafi'], + geo: Geo::fromArray($data['geo'] ?? null), + raw: $data, + ); + } + + /** + * Read any field of the raw response, including one the API grew after + * this release, or `null` when it is absent. + */ + public function field(string $name): ?string + { + $value = $this->raw[$name] ?? null; + + return is_scalar($value) ? (string) $value : null; + } + + /** + * The CEP with its mask: `01001-000`. + * + * The API always returns it unmasked; formatting is presentation, and it + * lives here so it does not become a repeated `sprintf` in every project + * that uses this. + */ + public function formattedCep(): string + { + return substr($this->cep, 0, 5) . '-' . substr($this->cep, 5, 3); + } + + /** + * @return array<string, mixed> + */ + public function toArray(): array + { + $out = []; + foreach (self::FIELDS as $name) { + $out[$name] = $this->{$name}; + } + $out['geo'] = $this->geo?->toArray(); + + return $out; + } +} |