Download the PHP package singra/br-validation without Composer
On this page you can find all versions of the php package singra/br-validation. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download singra/br-validation
More information about singra/br-validation
Files in singra/br-validation
Package br-validation
Short Description Validate, format, generate and redact Brazilian documents. Pure PHP, zero production dependencies.
License MIT
Homepage https://github.com/singraworks/br-validation
Informations about the package br-validation
singra/br-validation
English · Português
Validate, format, generate and redact Brazilian documents.
Each document is a final readonly value object. Cpf::from() returns a Cpf,
and holding one is proof the check digits were verified — once, at the boundary,
rather than re-derived by every layer that touches it.
Pure PHP 8.3+. The require block is php and nothing else: no framework, no
HTTP client, no network lookups.
Quick start
Documents
| Class | Length | Validation | Extra accessors |
|---|---|---|---|
Documents\Cpf |
11 | mod-11 ×2 | fiscalRegion() |
Documents\Cnpj |
14 | mod-11 ×2 over ord − 48 |
root() branch() isHeadquarters() isAlphanumeric() |
Documents\Cep |
8 | no check digit — Correios allocation table | uf() |
API
Every document exposes the same surface.
__toString() and jsonSerialize() both return the raw value. Display is
always a deliberate call, so nothing bound to a CHAR(11) column receives
punctuation by accident.
Constructors are private — from(), tryFrom() and generate() are the only
ways to obtain an instance, and all three validate.
Cnpj
Numeric and alphanumeric CNPJs both go through one mod-11 routine. Since July
2026 (IN RFB nº 2.229/2024)
the root and branch may contain A–Z; each position contributes its ASCII value
minus 48, which leaves 0–9 at face value and reduces exactly to the pre-2026
arithmetic. Check digits stay numeric. Every CNPJ issued before the change
remains valid.
If your storage cannot hold letters, check isAlphanumeric() at your boundary
rather than reaching for a different validator.
Cep
CEP has no check digit — no arithmetic distinguishes a real one from an invented one. Validation is shape, plus membership of a block Correios actually allocated, plus rejection of uniform sequences, which fill Brazilian address tables as placeholders.
uf() is non-nullable: allocation is enforced at construction, so there is
always an answer. The trade is that the bundled table is load-bearing — a block
allocated in future would be rejected until the table catches up. This does not
promise the address exists.
Cpf
There is deliberately no Cpf::uf(). The ninth digit identifies the Receita
Federal region that issued the CPF, not where the holder lives, and eight of
the ten regions cover more than one state.
Input handling
Whitespace — including internal — plus ., - and / are removed. Everything
left must belong to the document's alphabet.
Packages that strip every non-digit accept those last two. This one does not: a
Cpf that can be built from arbitrary prose proves nothing about its input.
Leading zeros are never restored. A CPF read back out of an INT column
arrives ten characters long and is rejected. Repairing it belongs at the
boundary where the data was damaged, not here.
Errors
from() throws InvalidDocument, which extends InvalidArgumentException and
implements the BrValidationException marker. Its message is English, for logs;
reason is the machine-readable contract to build user-facing text from.
Reason |
Raised when | Documents |
|---|---|---|
WrongLength |
wrong number of characters after normalisation | all |
IllegalCharacter |
a character outside the alphabet survived normalisation | all |
RepeatedCharacters |
every character is the same | all |
InvalidCheckDigit |
check digits do not match the rest | Cpf Cnpj |
UnallocatedRange |
well formed, but issued by nobody | Cep |
RepeatedCharacters is a separate rule, not a redundant one: every uniform
sequence satisfies mod-11. 111.111.111-11 checks out perfectly.
Redaction
| Strategy | Cpf |
Cnpj |
Cep |
|---|---|---|---|
Default |
***.444.777-** |
**.222.333/0001-** |
01310-*** |
Head |
111.***.***-** |
11.***.***/****-** |
01310-*** |
Tail |
***.***.777-** |
**.***.***/0001-** |
*****-100 |
Full |
***.***.***-** |
**.***.***/****-** |
*****-*** |
Every strategy hides the check digits, and that is the point. Check digits
are derived from the rest of the document. Publishing ***.456.789-09 leaves
1,000 candidates for the hidden group, and the two visible check digits narrow
that to roughly 8. Brazilian government portals redact as ***.456.789-** for
exactly this reason.
CEP hides the suffix instead, that being what narrows a code to a street or a
single building. Head and Default coincide there, CEP having only two groups.
Generation
Uses PHP's core Random\Randomizer — the CSPRNG by default, seedable for
reproducible fixtures.
Cnpj::generate() is numeric by default, since that is still what almost every
system in the country holds. With alphanumeric: true at least one letter is
guaranteed, so the flag always means what it says. Generated CNPJs are head
office records (0001).
Cep::generate() draws uniformly across a state's real allocated space rather
than picking a block first, which matters for states split across blocks of very
different sizes.
Displaying data that does not validate
Production tables hold documents with bad check digits, entered years ago, that
still have to render on a screen or an invoice. mask() and redact() are
static, enforce shape, and deliberately do not consult the check digits.
The verbs carry the distinction: from() and isValid() verify; mask() and
redact() only arrange.
Not included
- RG. No national standard, 27 incompatible state formats, most uncheckable.
A validator returning
truefor an RG would be lying. - Inscrição Estadual. 27 distinct algorithms, comparable in size to everything here combined.
- Network lookups. No ViaCEP, no Receita, no PSR-18 dependency. Offline arithmetic and one small bundled table.
- Framework bridges. A Laravel rule is three lines around
isValid(); it does not need to live here.
Development
Coverage needs Xdebug or PCOV. The suite is built in four layers, each catching what the others structurally cannot:
- External vectors, cross-checked against an independently authored implementation of the same specifications.
- Seeded round-trip fuzzing —
isValid(generate()), andgenerate(uf: X)->uf() === X. - One targeted invalid per
Reason. - Mutation testing and architecture assertions.
Layer 1 is what makes the rest trustworthy. Fuzzing proves only that this package's generator and validator agree with each other, and they would agree perfectly while sharing a transposed weight vector.
New test vectors must be verified against an implementation other than this one. See CONTRIBUTING.md.
License
MIT. See LICENSE.